diff --git a/.github/workflows/dsh-ci.yml b/.github/workflows/dsh-ci.yml new file mode 100644 index 000000000..20ec3bf36 --- /dev/null +++ b/.github/workflows/dsh-ci.yml @@ -0,0 +1,149 @@ +name: DSH plugins CI + +# Verifies the DeepSeek Harness plugin workspace in dsh/. The jobs match the +# workspace's former CI in omdsh-dev/dsh-mnemon; every command runs from dsh/. + +on: + push: + branches: [master] + paths: + - 'dsh/**' + - '.github/workflows/dsh-ci.yml' + pull_request: + branches: [master] + paths: + - 'dsh/**' + - '.github/workflows/dsh-ci.yml' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +defaults: + run: + working-directory: dsh + +jobs: + verify-windows-workspace: + name: Verify Windows workspace paths (Node ${{ matrix.node }}) + runs-on: windows-latest + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + node: ['22.19.0', '24'] + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Set up Node.js + uses: actions/setup-node@v6 + with: + node-version: ${{ matrix.node }} + package-manager-cache: false + + - name: Install pnpm + run: npm install --global pnpm@10.13.1 + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Test workspace identities and platform paths + run: pnpm exec vitest run tests/workspace-storage.spec.ts tests/client-platform-boundary.spec.ts tests/async-subagent-memory.spec.ts + + - name: Test desktop CLI process ownership and hidden native launches + run: >- + pnpm exec vitest run tests/host-process.spec.ts tests/version-updates.spec.ts + plugins/dsh-mnemon-source-memory-spaces/tests/npm-native-cli.spec.ts + plugins/dsh-mnemon-source-memory-spaces/tests/runner.spec.ts + plugins/dsh-mnemon-source-memory-spaces/tests/process.spec.ts + + verify-source: + name: Verify source (Node 22.19) + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Check out repository + uses: actions/checkout@v6 + with: + fetch-depth: 2 + + - name: Set up Node.js + uses: actions/setup-node@v6 + with: + node-version: 22.19.0 + package-manager-cache: false + + - name: Install pnpm + run: npm install --global pnpm@10.13.1 + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Validate release intent + if: github.event_name == 'pull_request' + env: + CHANGESET_BASE_REVISION: ${{ github.event.pull_request.base.sha }} + CHANGESET_REVISION: ${{ github.sha }} + run: pnpm run release:intent + + - name: Type-check integrated source + run: pnpm run typecheck + + - name: Check documentation links and public plugin catalog + run: pnpm run verify:docs + + - name: Verify deterministic root build + run: pnpm run verify:build + + - name: Build public plugin SDK entries + run: >- + pnpm --workspace-concurrency=2 + --filter dsh-mnemon-source-memory-spaces + --filter dsh-mnemon-source-documents + --filter dsh-mnemon-source-runtime + --filter dsh-mnemon-strategy-default-three-tier + --parallel run build + + - name: Test integrated source + run: pnpm run test + + - name: Verify root package + run: pnpm run verify:package + + - name: Set up Node.js 20 for runtime compatibility + uses: actions/setup-node@v6 + with: + node-version: 20 + package-manager-cache: false + + - name: Import every public entry on Node.js 20 + run: node scripts/verify-public-entries.mjs + + verify-artifacts: + name: Verify packed plugins (Node 24) + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Set up Node.js + uses: actions/setup-node@v6 + with: + node-version: 24 + package-manager-cache: false + + - name: Install pnpm + run: npm install --global pnpm@10.13.1 + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Verify packed and independently installed plugins + env: + MNEMON_PLUGIN_VERIFY_CONCURRENCY: '4' + run: pnpm run verify:plugins diff --git a/AGENTS.md b/AGENTS.md index 517ece409..dfff24afc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,6 +32,18 @@ - Preserve independent replay, crash, authorization, and race oracles while compressing fixtures and shared setup. +## DeepSeek Harness Workspace + +- `dsh/` is the DeepSeek Harness plugin workspace: TypeScript, pnpm, MIT. It is + not part of the Go module; `dsh/go.mod` keeps it out of the module zip, and + Go code does not belong there. +- Work inside `dsh/` follows `dsh/CONTRIBUTING.md`: install with + `pnpm install --frozen-lockfile`, verify with `pnpm run verify`, and keep its + bilingual documentation and changeset rules. +- `.github/workflows/dsh-ci.yml` runs the workspace's CI from `dsh/`. Files in + `dsh/.github/` are inactive until the workspace's release lane and repository + rules move to the root. + ## Commit Discipline - Prefer small, logical commits. Split unrelated work instead of committing a diff --git a/README.md b/README.md index 28a2b8f53..24f2ba6e3 100644 --- a/README.md +++ b/README.md @@ -282,7 +282,7 @@ hooks. ### [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) -DeepSeek Harness (DSH) integrates through the [dsh-mnemon](https://github.com/omdsh-dev/dsh-mnemon) plugin, which layers DSH's runtime memory, managed project documents, and Mnemon's long-term memory spaces into one supervised three-tier memory system. +DeepSeek Harness (DSH) integrates through the [dsh-mnemon](dsh/README.md) plugin, developed in [`dsh/`](dsh/) of this repository, which layers DSH's runtime memory, managed project documents, and Mnemon's long-term memory spaces into one supervised three-tier memory system. With `mnemon` installed on the host (see [Install](#install)), add the plugin and restart your DSH Web profile: @@ -291,14 +291,12 @@ dsh plugin --profile web add dsh-mnemon dsh --profile web ``` -The Mnemon repository is also a direct GitHub installation source. Unreleased -plugin builds can still be installed from the dedicated repository, and local -development checkouts use an absolute path: +The Mnemon repository is also a direct GitHub installation source, and a local +checkout of the plugin workspace installs by its absolute path: ```bash dsh plugin --profile web add github:mnemon-dev/mnemon -dsh plugin --profile web add "github:omdsh-dev/dsh-mnemon" -dsh plugin --profile web add "link:/absolute/path/to/dsh-mnemon" +dsh plugin --profile web add "link:/absolute/path/to/mnemon/dsh" ``` New installations from the Mnemon repository resolve the `latest` npm release @@ -541,6 +539,10 @@ make help # show all targets See [Development and Deployment](docs/DEPLOYMENT.md) for Docker, Compose, Ollama embedding, and release setup. +The DeepSeek Harness plugin workspace in [`dsh/`](dsh/) is a separate pnpm +workspace (TypeScript). Its setup, checks and release process are in +[dsh/CONTRIBUTING.md](dsh/CONTRIBUTING.md). + ## Documentation - [Agency Preview](docs/AGENCY.md) — maturity boundary, Pi setup, operating model, completion semantics, and optional peers @@ -575,6 +577,9 @@ Copyright 2026 Grivn and Mnemon contributors. [Apache-2.0](LICENSE) +The DeepSeek Harness plugin workspace in [`dsh/`](dsh/) keeps its own +[MIT license](dsh/LICENSE). + The bracketed copyright example near the end of `LICENSE` is part of Apache 2.0's standard application appendix; this section carries the project's actual copyright notice. diff --git a/docs/zh/README.md b/docs/zh/README.md index 5b161ce85..3ba66c93b 100644 --- a/docs/zh/README.md +++ b/docs/zh/README.md @@ -252,7 +252,7 @@ mnemon setup --target hermes --yes ### [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) -DeepSeek Harness(DSH)通过 [dsh-mnemon](https://github.com/omdsh-dev/dsh-mnemon) 插件集成:该插件把 DSH 的运行时热记忆、受管项目档案与 Mnemon 长期记忆体组织成一套受监督的三层记忆系统。 +DeepSeek Harness(DSH)通过 [dsh-mnemon](../../dsh/README.zh-CN.md) 插件集成,插件在本仓库的 [`dsh/`](../../dsh/) 中开发:该插件把 DSH 的运行时热记忆、受管项目档案与 Mnemon 长期记忆体组织成一套受监督的三层记忆系统。 宿主机安装好 `mnemon`(见[安装](#安装))后,安装插件并重启 DSH Web profile: @@ -261,13 +261,11 @@ dsh plugin --profile web add dsh-mnemon dsh --profile web ``` -Mnemon 主仓库也可以直接作为 GitHub 安装源。未发布到 npm 的插件版本仍可从 -独立仓库安装;本地开发检出使用绝对路径: +Mnemon 主仓库也可以直接作为 GitHub 安装源;插件工作区的本地检出使用绝对路径安装: ```bash dsh plugin --profile web add github:mnemon-dev/mnemon -dsh plugin --profile web add "github:omdsh-dev/dsh-mnemon" -dsh plugin --profile web add "link:/absolute/path/to/dsh-mnemon" +dsh plugin --profile web add "link:/absolute/path/to/mnemon/dsh" ``` 从 Mnemon 主仓库执行全新安装时,会解析 npm 上标记为 `latest` 的 @@ -465,6 +463,9 @@ make help # 显示所有目标 **可选**:[Ollama](https://ollama.ai) 或 OpenAI 兼容的嵌入服务器 +[`dsh/`](../../dsh/) 中的 DeepSeek Harness 插件工作区是独立的 pnpm 工作区(TypeScript), +安装、检查和发布流程见 [dsh/CONTRIBUTING.zh-CN.md](../../dsh/CONTRIBUTING.zh-CN.md)。 + ## 文档 - [Agency Preview 指南](AGENCY.md) — 成熟度边界、Pi 设置、View → Intent → Receipt 与可选 peer 协作 @@ -489,5 +490,7 @@ Copyright 2026 Grivn and Mnemon contributors. [Apache-2.0](../../LICENSE) +[`dsh/`](../../dsh/) 中的 DeepSeek Harness 插件工作区保留自己的 [MIT 许可证](../../dsh/LICENSE)。 + `LICENSE` 末尾带方括号的版权示例属于 Apache 2.0 标准许可证的应用附录; 本节所列内容才是本项目的实际版权声明。 diff --git a/dsh/.changeset/README.md b/dsh/.changeset/README.md new file mode 100644 index 000000000..08e68b95d --- /dev/null +++ b/dsh/.changeset/README.md @@ -0,0 +1,17 @@ +# Release intents + +Every pull request that changes a published artifact adds a changeset with +`pnpm changeset`. Select only packages whose own artifact or published metadata +changes. Test-only, documentation-site and CI-only changes may use an empty +changeset when an explicit no-release record helps review. + +Packages use independent versions. `dsh-mnemon` remains the aggregate Starter: +Changesets bumps it whenever an exact plugin dependency moves, and its published +manifest records the fully tested plugin composition. Run `pnpm release:version` +in a dedicated release pull request; it applies pending intents, synchronizes +the aggregate and fixture pins plus generated version metadata, and updates the +lockfile. + +The npm workflow compares the frozen release revision with the preceding +release revision, packs only packages whose versions advanced, publishes plugin +dependency layers with bounded concurrency, and publishes the Starter last. diff --git a/dsh/.changeset/config.json b/dsh/.changeset/config.json new file mode 100644 index 000000000..9c25c1265 --- /dev/null +++ b/dsh/.changeset/config.json @@ -0,0 +1,15 @@ +{ + "changelog": false, + "commit": false, + "fixed": [], + "linked": [], + "access": "public", + "baseBranch": "main", + "updateInternalDependencies": "patch", + "bumpVersionsWithWorkspaceProtocolOnly": false, + "ignore": [], + "___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": { + "onlyUpdatePeerDependentsWhenOutOfRange": true, + "updateInternalDependents": "out-of-range" + } +} diff --git a/dsh/.github/ISSUE_TEMPLATE/bug_report.yml b/dsh/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000..0648f1695 --- /dev/null +++ b/dsh/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,149 @@ +name: Bug 报告 / Bug report +description: 报告异常现象、复现步骤和环境;日志及修复建议可选。 / Describe the problem, reproduction steps and environment; logs and fixes are optional. +title: "[Bug]: " +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + 请填写现象、预期结果、复现步骤和环境。信息不足时机器人会提示补充,模板检查不会自动关闭 Issue。 + Describe the problem, expected outcome, reproduction steps and environment. Missing information prompts a request for details; template checks do not close issues. + + 日志、截图和对照结果可直接放在复现步骤中。下方的证据、冒烟测试、代码引用和补丁草案均为选填,无需重复内容或先提供修复方案。 + Logs, screenshots and control results may go directly in the reproduction steps. Separate evidence, smoke tests, code references and patch proposals are optional; no duplication or fix is required. + + 通过 CLI/API 提交时可使用相同段落标题组织正文。类型填写为“Bug 报告 / Bug report”时,机器人会补齐 `bug` 标签,无需提交者拥有标签权限。 + CLI/API reports may use the same section headings. The bot adds the `bug` label when the type is “Bug 报告 / Bug report”; reporters do not need label permissions. + + 提交前请先搜索确认没有重复 / Search for existing reports before submitting: [Issues](https://github.com/mnemon-dev/mnemon/issues?q=is%3Aissue) + + 不要粘贴 token、密钥、私有记忆内容或未脱敏日志。安全漏洞请按 [SECURITY.md](../../SECURITY.md) 私下报告。 + Do not post tokens, credentials, private memory, or unredacted logs. Report security vulnerabilities privately through [SECURITY.md](../../SECURITY.md). + - type: checkboxes + id: duplicate-check + attributes: + label: 提交前查重 / Duplicate check + description: 请搜索 open 和 closed Issue。 / Search both open and closed issues. + options: + - label: 我已搜索 open/closed Issue,确认没有重复。 / I searched open and closed issues and found no duplicate. + required: true + - type: dropdown + id: area + attributes: + label: 涉及区域 / Affected area + description: 选择问题主要涉及的组件或边界。 / Select the component or boundary most closely related to the problem. + options: + - 安装、更新或发布 / Installation, update, or release + - Host 激活或 Headless / Host activation or Headless + - 运行时记忆 / Runtime Memory + - 项目档案 / Project Documents + - 记忆空间或 Provider / Memory Spaces or Providers + - 子 Agent 或 Agent 工作流 / Subagent or Agent workflow + - Web UI 或对话交互 / Web UI or conversation interaction + - 设置、存储或安全 / Settings, storage, or security + - CLI、RPC、命令或工具 / CLI, RPC, commands, or tools + - 文档 / Documentation + - 其他或不确定 / Other or unsure + validations: + required: true + - type: dropdown + id: issue-type + attributes: + label: Issue 类型 / Issue type + description: 本表单仅用于可复现的 Bug;功能请求或问题请使用另一个表单。 / This form is only for reproducible bugs; use the other form for requests or questions. + options: + - Bug 报告 / Bug report + validations: + required: true + - type: textarea + id: summary + attributes: + label: 摘要 / Summary + description: 用一两句话描述实际发生的问题。 / Describe the observed problem in one or two sentences. + placeholder: 出了什么问题?影响了哪些操作? / What went wrong, and which operations are affected? + validations: + required: true + - type: textarea + id: expected-outcome + attributes: + label: 预期结果 / Expected outcome + description: 描述正确行为以及可以验证的结果。 / Describe the correct behavior and an observable result. + placeholder: 你期望发生什么? / What did you expect to happen? + validations: + required: true + - type: textarea + id: details-reproduction + attributes: + label: 详情或复现步骤 / Details or reproduction steps + description: 提供最小复现步骤和实际结果;报错和日志(包括 JSON 或普通文本)可直接写在这里。 / Include minimal steps and observed results; errors and logs, including JSON or plain text, may go here. + placeholder: | + 1. 配置 / Configure ... + 2. 启动 / Start ... + 3. 调用或点击 / Invoke or click ... + 4. 观察到 / Observe ... + validations: + required: true + - type: textarea + id: environment + attributes: + label: 环境信息 / Environment + description: 请提供 DSH、dsh-mnemon、Mnemon、Node.js、操作系统、浏览器以及相关 Provider/配置,并先脱敏。 / Provide DSH, dsh-mnemon, Mnemon, Node.js, OS, browser, and relevant redacted Provider or configuration details. + placeholder: | + - DSH 版本 / DSH version: + - dsh-mnemon 版本或 commit / dsh-mnemon version or commit: + - Mnemon 版本 / Mnemon version: + - Node.js / pnpm: + - 操作系统或浏览器 / OS or browser: + - Provider 或相关配置(已脱敏)/ Provider or related configuration (redacted): + validations: + required: true + - type: textarea + id: bug-evidence + attributes: + label: Bug 证据 / Bug evidence + description: 选填。复现步骤已有证据时无需重复;支持普通文本、任意语言代码块、图片或附件。 / Optional. Do not repeat evidence already in the steps; plain text, any code-block language, images or attachments are accepted. + placeholder: 可粘贴脱敏报错、日志、截图或附件,也可留空。 / Add redacted errors, logs, screenshots or attachments, or leave blank. + validations: + required: false + - type: textarea + id: smoke-test + attributes: + label: 冒烟测试 / Smoke test + description: 选填。补充实际执行的验证、观察结果或对照实验;未执行可留空。 / Optional. Add checks you actually ran, observed results or controls; leave blank if none were run. + placeholder: | + - 测试环境 / Test environment: + - 执行命令或操作 / Commands or actions: + - 实际结果 / Observed result: + - 对照版本或配置 / Control version or configuration: + validations: + required: false + - type: textarea + id: code-reference + attributes: + label: 引用代码 / Code references + description: 选填。知道相关代码位置时,可提供路径、函数或固定 commit 链接。 / Optional. If you know the relevant code, add paths, functions or links pinned to a commit. + placeholder: | + src/host/subagent.ts:870 + tests/subagent.spec.ts + validations: + required: false + - type: textarea + id: patch + attributes: + label: 补丁草案 / Patch proposal + description: 选填。已有修复思路时可以补充;无需为提交 Bug 先编写补丁。 / Optional. Share a fix if you have one; a patch is not required to report a bug. + placeholder: | + ```diff + --- a/src/example.ts + +++ b/src/example.ts + @@ ... @@ + ``` + validations: + required: false + - type: textarea + id: additional-context + attributes: + label: 补充信息 / Additional context + description: 补充相关 Issue、PR、最小复现仓库或其他已脱敏上下文。 / Add related issues, pull requests, a minimal reproduction, or other redacted context. + validations: + required: false diff --git a/dsh/.github/ISSUE_TEMPLATE/config.yml b/dsh/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..3ba13e0ce --- /dev/null +++ b/dsh/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/dsh/.github/ISSUE_TEMPLATE/standard_issue.yml b/dsh/.github/ISSUE_TEMPLATE/standard_issue.yml new file mode 100644 index 000000000..f7e77f391 --- /dev/null +++ b/dsh/.github/ISSUE_TEMPLATE/standard_issue.yml @@ -0,0 +1,105 @@ +name: 功能请求或问题 / Request or question +description: 请求功能、更新文档或提出项目问题。 / Request a feature or documentation update, or ask a project question. +title: "[Issue]: " +body: + - type: markdown + attributes: + value: | + 请填写请求、预期结果、使用场景和相关环境。信息不足时机器人会提示补充,模板检查不会自动关闭 Issue。Bug 请使用“Bug 报告 / Bug report”表单。 + Describe the request, expected outcome, use case and relevant environment. Missing information prompts a request for details; template checks do not close issues. Use the “Bug 报告 / Bug report” form for bugs. + + 通过 CLI/API 提交时可使用相同段落标题组织正文;补充信息可直接编辑原 Issue,机器人会更新已有提示。 + CLI/API reports may use the same section headings. Edit the original issue to add information; the bot updates its existing reminder. + + 提交前请先搜索确认没有重复 / Search for existing issues before submitting: [Issues](https://github.com/mnemon-dev/mnemon/issues?q=is%3Aissue) + + 不要公开提交漏洞、token、密钥、私有记忆内容或未脱敏日志。安全问题请按 [SECURITY.md](../../SECURITY.md) 私下报告。 + Do not post vulnerabilities, tokens, credentials, private memory, or unredacted logs. Report security concerns privately through [SECURITY.md](../../SECURITY.md). + - type: checkboxes + id: duplicate-check + attributes: + label: 提交前查重 / Duplicate check + description: 请搜索 open 和 closed Issue。 / Search both open and closed issues. + options: + - label: 我已搜索 open/closed Issue,确认没有重复。 / I searched open and closed issues and found no duplicate. + required: true + - type: dropdown + id: area + attributes: + label: 涉及区域 / Affected area + description: 选择请求或问题主要涉及的组件或边界。 / Select the component or boundary most closely related to the request or question. + options: + - 安装、更新或发布 / Installation, update, or release + - Host 激活或 Headless / Host activation or Headless + - 运行时记忆 / Runtime Memory + - 项目档案 / Project Documents + - 记忆空间或 Provider / Memory Spaces or Providers + - 子 Agent 或 Agent 工作流 / Subagent or Agent workflow + - Web UI 或对话交互 / Web UI or conversation interaction + - 设置、存储或安全 / Settings, storage, or security + - CLI、RPC、命令或工具 / CLI, RPC, commands, or tools + - 文档 / Documentation + - 其他或不确定 / Other or unsure + validations: + required: true + - type: dropdown + id: issue-type + attributes: + label: Issue 类型 / Issue type + description: 选择最接近的分类。 / Select the closest category. + options: + - 功能请求 / Feature request + - 增强或优化 / Enhancement or optimization + - 文档 / Documentation + - 问题 / Question + - 其他 / Other + validations: + required: true + - type: textarea + id: summary + attributes: + label: 摘要 / Summary + description: 用一两句话描述问题或请求。 / Describe the question or request in one or two sentences. + placeholder: 希望解决什么问题或增加什么能力? / What problem should be solved or capability added? + validations: + required: true + - type: textarea + id: expected-outcome + attributes: + label: 预期结果 / Expected outcome + description: 描述完成后的可观察行为和验收结果。 / Describe the observable behavior and acceptance result after completion. + placeholder: 怎样才算完成? / What would count as complete? + validations: + required: true + - type: textarea + id: details-reproduction + attributes: + label: 详情或复现步骤 / Details or reproduction steps + description: 说明使用场景、现有行为、约束和替代方案;若涉及问题,请给出复现步骤。 / Explain the use case, current behavior, constraints, and alternatives; include reproduction steps when relevant. + placeholder: | + - 使用场景 / Use case: + - 当前限制 / Current limitation: + - 建议方案 / Proposed approach: + - 不在范围内的内容 / Out of scope: + validations: + required: true + - type: textarea + id: environment + attributes: + label: 环境信息 / Environment + description: 请提供 DSH、dsh-mnemon、Mnemon、操作系统及相关配置;与环境无关时填写 N/A。 / Provide DSH, dsh-mnemon, Mnemon, OS, and relevant configuration details, or enter N/A when environment-independent. + placeholder: | + - DSH 版本 / DSH version: + - dsh-mnemon 版本或 commit / dsh-mnemon version or commit: + - Mnemon 版本 / Mnemon version: + - 操作系统或浏览器 / OS or browser: + - 相关配置(已脱敏)/ Related configuration (redacted): + validations: + required: true + - type: textarea + id: additional-context + attributes: + label: 补充信息 / Additional context + description: 补充截图、日志、设计草图、相关 Issue/PR 或其他上下文。 / Add screenshots, logs, design sketches, related issues or pull requests, or other context. + validations: + required: false diff --git a/dsh/.github/README.md b/dsh/.github/README.md new file mode 100644 index 000000000..4de266d9b --- /dev/null +++ b/dsh/.github/README.md @@ -0,0 +1,16 @@ +# dsh/.github + +GitHub reads workflows, issue forms and pull request templates only from the +repository root. The workspace's CI runs from +[`.github/workflows/dsh-ci.yml`](../../.github/workflows/dsh-ci.yml). + +The files kept here are not active. The publish workflow, the pull request +rules, the issue workflows and their forms stay here, next to the tests that +read them, until the release lane and the repository rules for this workspace +move to the root. + +GitHub 只从仓库根目录读取工作流、Issue 表单和 PR 模板。本工作区的 CI 由 +[`.github/workflows/dsh-ci.yml`](../../.github/workflows/dsh-ci.yml) 运行。 + +这里保留的文件目前不生效。发布工作流、PR 规范、Issue 工作流和表单暂时放在这里, +和读取它们的测试放在一起,等本工作区的发布流程和仓库规范迁到根目录时再一起移动。 diff --git a/dsh/.github/pull_request_template.md b/dsh/.github/pull_request_template.md new file mode 100644 index 000000000..88a280b46 --- /dev/null +++ b/dsh/.github/pull_request_template.md @@ -0,0 +1,106 @@ +> 提交 PR 前请阅读[贡献指南](../CONTRIBUTING.zh-CN.md)、[Contributing Guide](../CONTRIBUTING.md)与[开发和验证指南](../docs/zh-CN/development/README.md) / [Development and Verification Guide](../docs/en/development/README.md)。 +> Before opening a PR, read the [Contributing Guide](../CONTRIBUTING.md), [贡献指南](../CONTRIBUTING.zh-CN.md), and the [Development and Verification Guide](../docs/en/development/README.md) / [开发和验证指南](../docs/zh-CN/development/README.md). +> PR 标题和提交信息必须使用 Conventional Commits(`type(scope): subject`),且不得包含 emoji。 / PR titles and commits must use Conventional Commits (`type(scope): subject`) and must not contain emoji. +> 本仓库接受 Bug 修复、兼容性适配、现有能力增强、性能或体验优化和维护类 PR。全新能力、Provider、持久化格式或安全边界变更必须先提 Issue 并获得维护者确认。 / This repository accepts bug fixes, compatibility work, improvements to existing capabilities, performance or UX optimization, and maintenance. New capabilities, Providers, persistence formats, or security-boundary changes require prior maintainer approval in an Issue. +> 外部贡献者的仅文档类 PR 不直接接受;请先提 Issue 讨论。维护者的发布说明与文档维护不受此限制。 / Documentation-only PRs from external contributors are not accepted directly; open an Issue first. Maintainer release notes and documentation maintenance are exempt. + +## 摘要 / Summary + + + +## 关联 Issue 或背景 / Related Issue or Context + + + +## 涉及区域 / Affected Areas + + + +- [ ] Host 激活、Headless 或 bundle / Host activation, Headless, or bundle +- [ ] 运行时记忆 / Runtime Memory +- [ ] 项目档案 / Project Documents +- [ ] 记忆空间或 Provider / Memory Spaces or Providers +- [ ] 子 Agent 或 Agent 工作流 / Subagent or Agent workflow +- [ ] Web UI 或对话交互 / Web UI or conversation interaction +- [ ] 设置、存储或安全 / Settings, storage, or security +- [ ] CLI、RPC、命令或工具 / CLI, RPC, commands, or tools +- [ ] 安装、更新或发布 / Installation, update, or release +- [ ] 测试、构建或文档 / Tests, build, or documentation +- [ ] 其他(请说明)/ Other (explain below) + +## PR 类型 / PR Type + + + +- [ ] 面向用户的功能或行为变更 / User-facing feature or behavior change +- [ ] Bug 修复 / Bug fix +- [ ] 增强或优化 / Enhancement or optimization +- [ ] 兼容性适配 / Compatibility change +- [ ] 维护或重构 / Maintenance or refactor +- [ ] 测试或构建 / Tests or build + +## 最新代码确认 / Latest Codebase Confirmation + +- [ ] 我已基于最新 `main` 分支开发,或在提交前已 rebase 或合并最新 `main`。 / I developed from the latest `main`, or rebased or merged the latest `main` before submitting. + +同步命令 / Sync command: + + + +## AI 编码披露 / AI Coding Disclosure + + + +- [ ] 完全 AI 编码:全部编程改动由 AI 产出,并由贡献者接受和审查。 / Fully AI-coded: AI produced all programming changes, which the contributor accepted and reviewed. +- [ ] 部分 AI 辅助:AI 帮助编写或修改了部分内容。 / Partially AI-assisted: AI helped write or modify part of the change. +- [ ] 未使用 AI 编码辅助。 / No AI coding assistance was used. + +使用的 AI 模型 / AI model used: + + + +使用的编码 Agent 工具 / Coding Agent tool used: + + + +## 仓库规范检查 / Repository Rules + + + +- [ ] 未修改 DSH 官方源码,未让 tsconfig 指向 DSH 源码 checkout,仅使用正式的 `@deepseek-ai/*` NPM 契约。 / I did not modify DSH source or point tsconfig at a DSH source checkout, and used only published `@deepseek-ai/*` NPM contracts. +- [ ] Client 与 Host 边界仍以浏览器安全的 `src/host/protocol.ts` 为准,没有在两侧重复定义 wire DTO。 / The Client and Host boundary still uses browser-safe `src/host/protocol.ts` as the source for wire DTOs. +- [ ] 持久化格式、RPC 权限、路径或凭据处理的变更包含兼容或拒绝路径、安全分析和相应测试。 / Changes to persistence formats, RPC authority, paths, or credentials include compatibility or rejection paths, security analysis, and tests. +- [ ] 没有提交 token、密钥、私有记忆、未脱敏日志或生成的 `lib/` 文件。 / I did not commit tokens, credentials, private memory, unredacted logs, or generated `lib/` files. +- [ ] 用户可见文案和长期文档已同步维护中文与英文版本,命令、配置键和路径保持一致。 / User-facing copy and long-lived documentation are synchronized in Chinese and English, with matching commands, configuration keys, and paths. +- [ ] 会改变发布制品或其元数据的 PR 已添加 changeset;仅测试、CI 或站点文档变更可不添加。 / A PR that changes a published artifact or its metadata includes a changeset; test-only, CI-only, and site-documentation-only changes may omit one. +- [ ] 新增和修改的代码、注释、文档、提交信息不含 emoji。 / New and modified code, comments, documentation, and commits contain no emoji. + +## 兼容性与数据安全 / Compatibility and Data Safety + + + +## 本地验证 / Local Validation + +执行的命令 / Commands run: + +```bash +pnpm run verify +``` + +结果摘要 / Result summary: + + + +## 用户可见变更证据 / Local Feature Evidence + + + +证据 / Evidence: diff --git a/dsh/.github/release-notes/v0.5.0-rc.1.md b/dsh/.github/release-notes/v0.5.0-rc.1.md new file mode 100644 index 000000000..c777ea178 --- /dev/null +++ b/dsh/.github/release-notes/v0.5.0-rc.1.md @@ -0,0 +1,28 @@ +## v0.5.0-rc.1 · Composable Memory Plugins + +This release candidate keeps the v0.4 Sidebar experience and upgrades the implementation to a composable, View-based memory system. Existing configuration, three-tier workflows, storage formats, and ordinary UI remain compatible. The only new user-facing controls are three disabled-by-default switches under **Settings → Memory System → Memory enhancements**. + +### What ships + +- One `dsh-mnemon` Starter plus sixteen independently published, version-locked packages: three Sources, one default Strategy, three optional Strategy enhancements, and nine Memory Space Providers. +- A narrow `ctx.mnemonMemory` runtime boundary, public `dsh-mnemon/extension-sdk`, isolated `dsh-mnemon/testing` fixtures, Fiber-owned registration, immutable turn Views, Source-local grants, and bounded execution. +- Optional scoped composition, light-context projection, and current-turn auto-capture guidance. These extensions compose through distinct slots and remain disabled unless the user enables them. +- Independent package, tarball, Node 20/22/24, Windows, Headless, copied-data upgrade, and real `v0.4.7 → v0.5.0-rc.1` Registry upgrade gates. + +### Community requests + +- [#110](https://github.com/omdsh-dev/dsh-mnemon/issues/110): auto-capture now offers an opt-in current-turn recording policy. It is not an unsupervised background writer, so the broader request remains open. +- [#40](https://github.com/omdsh-dev/dsh-mnemon/issues/40): light-context provides a bounded projection policy. Revisioned incremental injection remains an upstream-dependent open item. +- [#77](https://github.com/omdsh-dev/dsh-mnemon/issues/77): scoped composition generalizes the existing global/project memory use case with ordered Sources and a writable subset. + +### Install + +This prerelease is available on the npm `rc` channel and does not replace `latest`: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.0-rc.1 +``` + +Use `headless` instead of `web` for a Headless profile. Back up the effective memory root, Documents, DSH profile/configuration, and separately managed Provider data before trying a prerelease. + +Full compatibility boundaries, rollback instructions, and extension limitations are documented in the [English release notes](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.0-rc.1/docs/en/releases/v0.5.0-rc.1.md) and [中文发布说明](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.0-rc.1/docs/zh-CN/releases/v0.5.0-rc.1.md). diff --git a/dsh/.github/release-notes/v0.5.0.md b/dsh/.github/release-notes/v0.5.0.md new file mode 100644 index 000000000..9d9137472 --- /dev/null +++ b/dsh/.github/release-notes/v0.5.0.md @@ -0,0 +1,30 @@ +## v0.5.0 · Composable Memory Plugins + +v0.5.0 keeps the familiar v0.4 Sidebar experience while making the implementation composable through independently published Source, Strategy, and Provider plugins. Existing configuration, three-tier workflows, storage formats, and ordinary UI remain compatible. The only new user-facing controls are three disabled-by-default switches under **Settings → Memory System → Memory enhancements**. + +### What ships + +- One `dsh-mnemon` Starter plus sixteen independently published, version-locked packages: three Sources, one default Strategy, three optional Strategy enhancements, and nine Memory Space Providers. +- A narrow `ctx.mnemonMemory` runtime boundary, the public `dsh-mnemon/extension-sdk`, isolated `dsh-mnemon/testing` fixtures, Fiber-owned registration, immutable turn Views, Source-local grants, and bounded execution. +- Optional scoped composition, light-context projection, and current-turn auto-capture guidance. These extensions compose through distinct slots and remain disabled unless the user enables them. +- Independent package, tarball, Node 20/22/24, Windows, Headless, copied-data upgrade, and real `v0.4.7 → v0.5.0` Registry upgrade gates. + +The runtime behavior and persisted formats are unchanged from `v0.5.0-rc.1`; the stable promotion additionally generalizes and verifies the release pipeline for the npm `latest` channel. + +### Community requests + +- [#110](https://github.com/omdsh-dev/dsh-mnemon/issues/110): auto-capture provides an opt-in current-turn recording policy. It does not add an unsupervised background writer. +- [#40](https://github.com/omdsh-dev/dsh-mnemon/issues/40): light-context provides a bounded projection policy. Revisioned incremental injection remains an upstream-dependent open item. +- [#77](https://github.com/omdsh-dev/dsh-mnemon/issues/77): scoped composition generalizes the global/project memory use case with ordered Sources and a writable subset. + +### Install or upgrade + +`v0.5.0` is the npm `latest` release: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.0 +``` + +Use `headless` instead of `web` for a Headless profile, then restart DSH. Back up the effective memory root, Documents, DSH profile/configuration, and separately managed Provider data before upgrading. + +Full compatibility boundaries, rollback instructions, and extension limits are documented in the [English release notes](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.0/docs/en/releases/v0.5.0.md) and [中文发布说明](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.0/docs/zh-CN/releases/v0.5.0.md). diff --git a/dsh/.github/release-notes/v0.5.1.md b/dsh/.github/release-notes/v0.5.1.md new file mode 100644 index 000000000..6a043c808 --- /dev/null +++ b/dsh/.github/release-notes/v0.5.1.md @@ -0,0 +1,25 @@ +## v0.5.1 · Desktop and Turn Reliability + +v0.5.1 is a coordinated patch release for the `dsh-mnemon` Starter and all sixteen official Source, Strategy, and Provider packages. It fixes two user-visible failures on the stable DSH 0.1.2-rc.1 line without changing configuration or persisted data. + +### Fixes + +- [#176](https://github.com/omdsh-dev/dsh-mnemon/issues/176): mount the Memory System launcher through the released Desktop Sidebar's actual nested parent. Settings no longer fall back to a read-only state after an `insertBefore` `NotFoundError`. +- [#172](https://github.com/omdsh-dev/dsh-mnemon/issues/172): abort and settle an in-flight Idle Review before the next ordinary turn binds the same Agent runtime. The new turn no longer fails with `Mnemon runtime is already pinned`. + +### Verification and delivery + +- Both regressions were reproduced and corrected in isolated real DSH 0.1.2-rc.1 WebUI profiles, with before/after screenshots and focused regression tests in [PR #177](https://github.com/omdsh-dev/dsh-mnemon/pull/177) and [PR #178](https://github.com/omdsh-dev/dsh-mnemon/pull/178). +- PR CI now keeps two complementary heavy jobs and verifies independent packed plugins through a bounded four-worker queue. The measured critical path fell from 6m14s to 3m01s while retaining integrated source, public-entry, standalone package, external-consumer, and real DSH checks ([PR #179](https://github.com/omdsh-dev/dsh-mnemon/pull/179)). + +### Install or upgrade + +`v0.5.1` is published on the npm `latest` channel: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.1 +``` + +Use `headless` instead of `web` for a Headless profile, then restart DSH. No storage migration is required. Back up the effective memory root, Documents, DSH profile/configuration, and separately managed Provider data before upgrading. + +Full compatibility and rollback details are in the [English release notes](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.1/docs/en/releases/v0.5.1.md) and [中文发布说明](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.1/docs/zh-CN/releases/v0.5.1.md). diff --git a/dsh/.github/release-notes/v0.5.10.md b/dsh/.github/release-notes/v0.5.10.md new file mode 100644 index 000000000..becded53a --- /dev/null +++ b/dsh/.github/release-notes/v0.5.10.md @@ -0,0 +1,25 @@ +## v0.5.10 · Exact Memory Writes and Bounded Automatic Review + +- [#256](https://github.com/omdsh-dev/dsh-mnemon/pull/256): prefer a unique exact Runtime entry for replace/remove before substring matching. Short entries such as `X` can be changed alongside `EGO_LINUX_CHROME`. +- [#234](https://github.com/omdsh-dev/dsh-mnemon/pull/234): create the original OpenViking content, verify indexing and exact readback, and return scoped file receipts for archive compensation. Original contributor commits remain in the merged history. +- [#257](https://github.com/omdsh-dev/dsh-mnemon/pull/257): add an independent automatic-review switch, cadence and attempt limits, bounded default spawn context, and committed-write receipts after failure. Pause automatic review before child creation with the incompatible Agent Teams composition. + +### Packages and upgrade + +New publications: **dsh-mnemon 0.5.10**, **OpenViking Provider 0.5.5**, **Memory Spaces Source 0.5.8**, and **Runtime Source 0.5.7**. The remaining thirteen plugins keep their existing versions. + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.10 +``` + +Restart DSH and reload the browser; use `headless` for that Profile. The review budget resets when the parent Agent unloads or the Host restarts. The Agent Teams guard pauses automatic review; upstream Team policy and manual delegation are not repaired. + +### 中文说明 + +本版修复 Runtime 短条目编辑与删除、OpenViking 原文写入和回执验证,并限制自动审阅的频率、次数和默认上下文。自动审阅增加独立开关;遇到不兼容的 Agent Teams 组合时,在创建子 Agent 前暂停。审阅失败后保留已提交写入的回执,方便核对。 + +主包发布 0.5.10,OpenViking Provider 发布 0.5.5,Memory Spaces Source 发布 0.5.8,Runtime Source 发布 0.5.7。升级后重启 DSH。审阅预算随父 Agent 卸载或 Host 重启而重置;Team 兼容处理不代表上游策略或手动委派已修复。 + +The combined implementation passed 1,162 root tests plus plugin suites, real CLI/WebUI checks, real OpenViking storage checks and independent plugin composition. Before/after screenshots remain in the linked reports. Model responses and embeddings were deterministic local fixtures. The release workflow verifies versioned artifacts, Registry composition and a real upgrade before creating this Release. + +See the [English release notes](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.10/docs/en/releases/v0.5.10.md) and [中文发布说明](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.10/docs/zh-CN/releases/v0.5.10.md). diff --git a/dsh/.github/release-notes/v0.5.11.md b/dsh/.github/release-notes/v0.5.11.md new file mode 100644 index 000000000..69afbc381 --- /dev/null +++ b/dsh/.github/release-notes/v0.5.11.md @@ -0,0 +1,21 @@ +## v0.5.11 · Quiet Windows Git Branch Detection + +[#260](https://github.com/omdsh-dev/dsh-mnemon/pull/260) fixes [#259](https://github.com/omdsh-dev/dsh-mnemon/issues/259): Runtime Git branch probes hide their Windows console, preventing brief terminal windows in GUI hosts. Branch filtering, the two-second timeout and failure fallback are unchanged. + +### Packages and upgrade + +New publications: **dsh-mnemon 0.5.11** and **Runtime Source 0.5.8**. The other fifteen plugins retain their versions; configuration and memory data need no migration. + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.11 +``` + +Restart DSH and reload the browser; use `headless` for that Profile. Mnemon CLI remains independently maintained. + +### 中文说明 + +本版修复 Windows GUI 宿主查询 Git 分支时短暂弹出终端窗口的问题。Runtime 查询增加隐藏窗口选项,保留分支过滤、两秒超时和失败回退。主包发布 0.5.11,Runtime Source 发布 0.5.8,其余十五个插件不变,无需迁移配置或已有记忆。升级后重启 DSH 并刷新浏览器。 + +Four real Windows trials observed 104–105 visible windows per baseline trial and zero after the fix. The implementation passed 59 Runtime tests, 1,162 root tests, real CLI/macOS WebUI checks and independent plugin composition. [Before/after evidence](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.11/docs/pr-assets/issue-259-windows-git-console/README.md) distinguishes Windows process observation from macOS WebUI validation. The release workflow checks versioned artifacts, Registry composition and an actual upgrade before creating this Release. + +See [English release notes](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.11/docs/en/releases/v0.5.11.md) and [中文发布说明](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.11/docs/zh-CN/releases/v0.5.11.md). diff --git a/dsh/.github/release-notes/v0.5.12.md b/dsh/.github/release-notes/v0.5.12.md new file mode 100644 index 000000000..13826b321 --- /dev/null +++ b/dsh/.github/release-notes/v0.5.12.md @@ -0,0 +1,23 @@ +## v0.5.12 · DSH RC and Alpha Compatibility + +[#263](https://github.com/omdsh-dev/dsh-mnemon/pull/263) fixes [#261](https://github.com/omdsh-dev/dsh-mnemon/issues/261): the turn memory bar and save action now register correctly on DSH 0.1.6-alpha.2 while retaining 0.1.5-rc.2 compatibility. Sidebar and Settings use the public main-session binding; explicitly scoped views keep their own session and workspace authority. + +### Packages and upgrade + +Only **dsh-mnemon 0.5.12** is newly published. The sixteen companion plugins retain their versions. No configuration or stored-memory migration is required. + +The Client now requires **DSH 0.1.5-rc.1 or the supported newer cohorts**: the declared range is `^0.1.5-rc.1 || ^0.1.6-alpha.2`, with RC2 and alpha.2 explicitly verified. Older hosts without the public UI Session service must keep their previously verified Mnemon release. + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.12 +``` + +Restart DSH and reload the browser; use `headless` for that Profile. Mnemon CLI remains independently maintained. + +### 中文说明 + +本版修复 DSH 0.1.6-alpha.2 中回合记忆条和“存入记忆”入口无法挂载的问题,同时兼容已验证的 0.1.5-rc.2。Sidebar 与设置页使用公开主会话 binding,Builtin 等显式范围界面保留各自会话和工作区权限。仅发布主包 0.5.12,十六个配套插件不变,无需迁移数据。Client 兼容下限为 DSH 0.1.5-rc.1;缺少公开 UI Session 服务的旧宿主应继续使用此前验证过的 Mnemon 版本。升级后重启 DSH 并刷新浏览器。 + +The implementation passed 1,183 root tests, actual Mnemon CLI checks, real RC2/alpha.2 WebUI operations and independent plugin composition. The [before/after evidence](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.12/docs/pr-assets/issue-261-dsh-slots/README.md) includes peer-plugin coexistence, workspace isolation and original screenshots. Models use an explicit loopback fixture; tools, storage and browser operations are real. The release workflow rechecks versioned artifacts, full Registry composition and an actual v0.4.7 upgrade before creating this Release. + +See [English release notes](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.12/docs/en/releases/v0.5.12.md) and [中文发布说明](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.12/docs/zh-CN/releases/v0.5.12.md). diff --git a/dsh/.github/release-notes/v0.5.13.md b/dsh/.github/release-notes/v0.5.13.md new file mode 100644 index 000000000..5127e514d --- /dev/null +++ b/dsh/.github/release-notes/v0.5.13.md @@ -0,0 +1,33 @@ +## v0.5.13 · Workspace, Builtin and DSH 0.1.7 Compatibility + +- [#268](https://github.com/omdsh-dev/dsh-mnemon/pull/268) rejects Windows workspace paths below regular files before computing storage identities. Valid identities are preserved; existing storage is not moved or deleted. +- [#269](https://github.com/omdsh-dev/dsh-mnemon/pull/269) hides only the owning conversation's width handles while Builtin memory is open. The composer remains usable, and returning to Chat restores width dragging. +- [#270](https://github.com/omdsh-dev/dsh-mnemon/pull/270) supports DSH 0.1.7-alpha.1 live Config settings, icon exports and Session message sources, while retaining older hosts and historical Mnemon message deduplication. Profile settings keep revision checks, explicit choices and native expressions. + +### Packages and upgrade + +Only **dsh-mnemon advances to 0.5.13**; all sixteen companion plugins retain their versions. Compatibility evidence covers unchanged public DSH 0.1.5-rc.2, 0.1.6-alpha.2 and 0.1.7-alpha.1 packages. The Client floor remains 0.1.5-rc.1; Mnemon CLI remains independently maintained. + +Back up the DSH profile configuration, legacy settings and relevant Mnemon data before upgrading. + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.13 +``` + +Restart DSH and reload the browser; use `headless` for that Profile. If DSH first imports an original `settings.yaml`, Mnemon's supplemental UI/View recovery waits for the next cold Host start and can require a second complete stop/start. A browser refresh is insufficient. Recovery preserves `settings.yaml.imported` and requires the canonical `mnemon` root with the exact owning profile namespace; it does not guess custom roots or foreign suffixes. + +Downgrades need the matching configuration backup and manual reconciliation of later changes. New settings have no automatic reverse export. Runtime, Documents and Memory Spaces data formats are unchanged. + +### 中文说明 + +本版包含三项修复:Windows 工作区路径如果位于普通文件之下,会在计算存储标识前被拒绝,合法标识与已有存储保持不变;Builtin 记忆页仅隐藏所属对话的宽度手柄,常驻输入框仍可用,返回 Chat 后恢复拖拽;适配 DSH 0.1.7-alpha.1 的实时 Config 设置、图标与 Session 消息来源,同时保留旧宿主兼容、历史消息去重、设置修订检查、当前显式选择和原生表达式。 + +仅主包升级至 0.5.13,十六个配套插件版本不变。兼容证据覆盖未修改的公开 DSH 0.1.5-rc.2、0.1.6-alpha.2 和 0.1.7-alpha.1,Client 下限仍为 0.1.5-rc.1,Mnemon CLI 独立维护。升级前备份 Profile 配置、旧设置及相关数据,升级后重启 DSH 并刷新浏览器。首次由 DSH 导入原始 `settings.yaml` 后,Mnemon 补充 UI/View 恢复会延后到下一次冷启动,可能需要第二次完整停止并启动 Host;仅刷新页面不够。 + +恢复保留 `settings.yaml.imported` 原件,只接受标准 `mnemon` 根 Entry 与所属 Profile 的精确命名空间,不猜测自定义根或其他 Profile 的后缀。降级需要对应配置备份,并手动核对后续更改;没有自动逆向导出。Runtime、Documents 和 Memory Spaces 数据格式不变。 + +The implementation evidence covers [native Windows paths](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.13/docs/pr-assets/issue-266-workspace-ancestor/README.md), [Builtin interaction](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.13/docs/pr-assets/issue-265-builtin-width-handles/README.md), and [DSH settings, recovery and Sessions](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.13/docs/pr-assets/issue-267-settings-migration/README.md). Model responses use an explicit loopback fixture without an external model API key; tools, storage and browser operations are real. 发布证据覆盖原生 Windows 路径、Builtin 交互及 DSH 设置、恢复与 Session;模型使用本地夹具,工具、存储和浏览器操作真实执行。 + +The release workflow must verify the versioned artifacts, full Registry composition and an actual v0.4.7 upgrade before creating this Release. 发布工作流须完成版本化制品、完整 Registry 组合及 v0.4.7 真实升级检查,之后才创建此 Release。 + +See [English release notes](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.13/docs/en/releases/v0.5.13.md) and [中文发布说明](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.13/docs/zh-CN/releases/v0.5.13.md). diff --git a/dsh/.github/release-notes/v0.5.14.md b/dsh/.github/release-notes/v0.5.14.md new file mode 100644 index 000000000..bc30d7dfe --- /dev/null +++ b/dsh/.github/release-notes/v0.5.14.md @@ -0,0 +1,31 @@ +## v0.5.14 · Desktop Loading, OpenViking User Keys and Agent Teams Review + +- [#277](https://github.com/omdsh-dev/dsh-mnemon/pull/277) fixes Desktop profile bundle loading on older Cosmokit hosts, preserving live settings on newer hosts and existing component switches and namespaces. +- [#276](https://github.com/omdsh-dev/dsh-mnemon/pull/276) adds scoped OpenViking discovery with an explicitly configured user-key owner. It validates the data API without Admin access and retains the prior configuration when a replacement is rejected. +- [#278](https://github.com/omdsh-dev/dsh-mnemon/pull/278) supports guarded idle review with the verified DSH/Agent Teams 0.1.7-rc.1 combination and preserves complete live-user decisions in bounded checkpoints. Reviewer Team delegation stays denied; parent Team tools remain usable. + +### Packages and upgrade + +**dsh-mnemon 0.5.14**, **dsh-mnemon-provider-openviking 0.5.6**, and **dsh-mnemon-source-memory-spaces 0.5.9** form the updated composition. The other fourteen companion packages retain their versions. Mnemon CLI remains independently maintained; real CLI checks use 0.2.9. + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.14 +``` + +Back up the DSH profile and relevant Mnemon data before upgrading, then restart DSH and reload the browser. Use `headless` for that Profile. Client compatibility still starts at DSH 0.1.5-rc.1; complete profiles need Node `^22.19.0 || >=24.0.0`. + +**Agent Teams defaults to `idleReview.agentTeams: pause`.** Enable `scoped` explicitly with the verified DSH/Teams 0.1.7-rc.1 combination. Older DSH 0.1.5-rc.2 plus Teams 0.1.5-alpha.2 can still reject the reviewer with `TEAM_NOT_MEMBER`; no automatic enablement or policy bypass is added. Storage formats are unchanged. Downgrades require reconciling the optional new settings with the backed-up profile. + +### 中文说明 + +本版修复旧版 Cosmokit 宿主上的 Desktop Profile bundle 加载;新增 OpenViking User Key 所属用户配置,通过限定范围的数据 API 完成无需 Admin 权限的发现,并保留失败替换前的配置;支持已验证 DSH/Agent Teams 0.1.7-rc.1 组合中的受限空闲审查,完整保留有界检查点内的真实用户决定,禁止审查器进行 Team 委派,同时保留父级 Team 工具。 + +主包升级至 **0.5.14**,OpenViking Provider 升级至 **0.5.6**,Memory Spaces Source 升级至 **0.5.9**,其余十四个插件版本不变。升级前备份 Profile 与相关数据,升级后重启 DSH 并刷新浏览器。**Agent Teams 默认仍为 `pause`**,确认使用已验证的 0.1.7-rc.1 组合后手动选择 `scoped`。旧版 0.1.5-rc.2/Teams alpha.2 仍可能出现 `TEAM_NOT_MEMBER`。存储格式不变,降级时结合备份核对新增可选设置。 + +### Evidence + +[Desktop loading and cold restart](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.14/docs/pr-assets/issue-274-profile-generation/README.md), [OpenViking discovery](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.14/docs/pr-assets/issue-271-user-key/README.md), and [guarded Team review](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.14/docs/pr-assets/issue-275-agent-teams/README.md) retain before/after evidence and real WebUI screenshots. Provider/model replies use explicit loopback fixtures; browser, DSH tools, storage and CLI operations are real. These checks do not certify live cloud-account behavior or model quality. 证据保留原始故障、修复结果及真实 WebUI 截图;Provider/模型回复使用本地夹具,不代表真实云账户或在线模型质量验收。 + +The release workflow verifies versioned artifacts, all independent plugins, the full pinned Registry composition and an actual v0.4.7 upgrade before creating this Release. 发布工作流先验证版本化制品、独立插件、完整 Registry 组合和 v0.4.7 真实升级,再创建此 Release。 + +[English release notes](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.14/docs/en/releases/v0.5.14.md) · [中文发布说明](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.14/docs/zh-CN/releases/v0.5.14.md) diff --git a/dsh/.github/release-notes/v0.5.15.md b/dsh/.github/release-notes/v0.5.15.md new file mode 100644 index 000000000..ea5471ea4 --- /dev/null +++ b/dsh/.github/release-notes/v0.5.15.md @@ -0,0 +1,31 @@ +## v0.5.15 · USER Tool Writes, Runtime Metadata and Skin Hooks + +- [#282](https://github.com/omdsh-dev/dsh-mnemon/pull/282) restores USER add, replace and remove calls when the model supplies `branches: []`. Non-empty USER branches remain rejected; MEMORY branch behavior is unchanged. +- [#283](https://github.com/omdsh-dev/dsh-mnemon/pull/283) includes recorded importance and creation/update ages in Runtime model projections. Default Strategy guidance acknowledges the context budget without changing stored content, exact matching or capacity accounting. +- [#284](https://github.com/omdsh-dev/dsh-mnemon/pull/284) provides stable skin selectors and supported background variables for Sidebar and Builtin, with a bilingual skin development guide. Default backgrounds and body-portaled dialogs retain their existing behavior. + +### Packages and upgrade + +**dsh-mnemon 0.5.15**, **dsh-mnemon-source-runtime 0.5.9**, and **dsh-mnemon-strategy-default-three-tier 0.5.6** form the updated composition. The other fourteen companion packages retain their versions. Mnemon CLI remains independently maintained; local checks use official CLI 0.2.9. + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.15 +``` + +Back up the DSH profile and relevant Mnemon data, then restart DSH and reload the browser after upgrading. Use `headless` for that Profile. Client compatibility still starts at DSH 0.1.5-rc.1; complete profiles need Node `^22.19.0 || >=24.0.0`. + +No storage migration is required. Scoped, Light Context and Auto Capture remain composable. Existing skins must explicitly adopt the Mnemon hooks to customize transparency; the official theme stays opaque. + +### 中文说明 + +本版修复 USER 工具写入被空 `branches: []` 占位参数拒绝的问题,保留非空 USER 分支校验和 MEMORY 分支语义;让 Runtime 模型投影展示重要性与按天计算的创建、更新时间,并明确快照受上下文预算限制;提供稳定的皮肤选择器与背景变量,并补充中英文开发手册。 + +主包升级至 **0.5.15**,Runtime Source 升级至 **0.5.9**,默认三层 Strategy 升级至 **0.5.6**,其余十四个插件版本不变。升级前备份 Profile 与相关数据,升级后重启 DSH 并刷新浏览器。存储格式、精确匹配与容量计算不变,无需数据迁移。现有皮肤需显式接入背景覆盖接口,官方主题保持不透明,弹窗保持独立样式。 + +### Evidence + +[USER tool writes](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.15/docs/pr-assets/issue-281-user-branches/README.md), [Runtime projections](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.15/docs/pr-assets/issue-280-runtime-metadata/README.md), and [skin integration](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.15/docs/pr-assets/issue-273-skin-hooks/README.md) retain regression results and real WebUI comparisons. Model decisions use loopback fixtures; DSH tools, storage, browser and CLI operations are real. These checks do not certify live-model quality or Windows WebUI acceptance. 证据包含回归结果和真实 WebUI 对比;模型决策使用回环夹具,不代表在线模型质量或 Windows WebUI 已验收。 + +The release workflow verifies the merged workspace, independent plugins, frozen artifacts, the complete Registry combination and an actual v0.4.7 upgrade before creating this Release. 发布工作流先验证合并后的工作区、独立插件、冻结制品、完整 Registry 组合和 v0.4.7 真实升级,再创建此 Release。 + +[English release notes](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.15/docs/en/releases/v0.5.15.md) · [中文发布说明](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.15/docs/zh-CN/releases/v0.5.15.md) diff --git a/dsh/.github/release-notes/v0.5.16.md b/dsh/.github/release-notes/v0.5.16.md new file mode 100644 index 000000000..dbaf31471 --- /dev/null +++ b/dsh/.github/release-notes/v0.5.16.md @@ -0,0 +1,39 @@ +## v0.5.16 · Chinese Metadata, Native Sidebar and Source Scrolling + +- [#286](https://github.com/omdsh-dev/dsh-mnemon/pull/286) adds Chinese root-plugin metadata with English manifest fallback, checks packed locale resources and includes locale-only changes in release validation. The contributor's original commit [`d16d017`](https://github.com/omdsh-dev/dsh-mnemon/commit/d16d017aa5bb0c3bbbdb40dc5a95554266b6522a) is preserved. +- [#287](https://github.com/omdsh-dev/dsh-mnemon/pull/287) uses DSH's native Sidebar and main panel, fixes switching between Plugins and Memory System, and retains Source state on return. Native styling, collapsed navigation and the tested Maid Atelier skin keep the page header accessible. +- [#288](https://github.com/omdsh-dev/dsh-mnemon/pull/288) aligns Source frames and scrolling with Status, retains the Memory Spaces title/tab header, and reveals Related headings and close buttons below it without scrolling other panels. + +### Packages and upgrade + +**dsh-mnemon 0.5.16** and **dsh-mnemon-source-memory-spaces 0.5.10** form the updated composition; the other fifteen companion packages retain their versions. Upgrade both for the complete fix. The Starter pins all seventeen packages. + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.16 +``` + +Back up the Profile and relevant data, then restart DSH and reload the browser after upgrading. Use `headless` for that Profile. The Client floor remains DSH 0.1.5-rc.1; complete profiles require Node `^22.19.0 || >=24.0.0`. + +No persistence migration is required. The Source scroll callbacks are optional; older Hosts remain compatible but require manual scrolling. Mnemon CLI is independently maintained. + +Published DSH 0.1.7-rc.2 still shows `cordis:group` as Off while its children run; clicking the container's switch can return `unknown-plugin`. This release does not patch DSH or install a replacement plugin-management adapter. + +### 中文说明 + +本版为 DSH 中的主插件增加中文元数据,保留英文回退和贡献者原始提交,并补齐 locale 制品与发布校验;将记忆系统接入原生 Sidebar 和主面板,修复面板切换、皮肤下标题可见性与返回时的 Source 状态保留;让 Source 页面框架和滚动对齐状态页,并确保关联记忆标题与关闭按钮显示在固定头部下方。 + +Root 升级至 **0.5.16**,Memory Spaces Source 升级至 **0.5.10**,其余十五个插件版本不变。完整修复需两者一起升级,Starter 已固定全部十七个包。升级前备份 Profile 与相关数据,升级后重启 DSH 并刷新浏览器。无需持久化迁移;新增滚动回调可选,旧 Host 可继续工作但需手动滚动。 + +官方 DSH 0.1.7-rc.2 的 `cordis:group` 显示 Off、开关返回 `unknown-plugin` 问题仍存在。本版不包含运行时 DSH 补丁或替代插件管理服务的适配器。 + +### Evidence / 验证证据 + +- [Chinese metadata / 中文元数据](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.16/docs/pr-assets/pr-286-plugin-metadata/README.md) +- [Native Sidebar and skins / 原生 Sidebar 与皮肤](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.16/docs/pr-assets/sidebar-native-20260926/README.md) +- [Source frames and Related controls / Source 框架与关联操作](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.16/docs/pr-assets/source-page-frames-20260926/README.md) + +The versioned seventeen-artifact composition passed real Flash WebUI and official CLI acceptance, including Runtime and Documents archival, Native recall, Sidebar/page-frame behavior and cold-restart persistence. Thirteen real Flash requests returned HTTP 200. Root verification passed 1,421 tests; all sixteen independent plugin repositories, the external consumer and v0.4.7 upgrade passed. [Release evidence](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.16/docs/pr-assets/release-v0.5.16/README.md) records screenshots, artifact identities, local concurrency reruns and scope limits. + +版本化的全部十七个制品已通过真实 Flash WebUI 与官方 CLI 验收,包含 Runtime 和 Documents 归档、Native 召回、Sidebar/页面框架及冷重启数据保留。十三次真实 Flash 请求全部返回 HTTP 200;Root 测试 1,421 项通过,十六个独立插件仓库、外部消费者和 v0.4.7 升级均通过。[中文验收记录](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.16/docs/pr-assets/release-v0.5.16/README.zh-CN.md)包含截图、制品标识、本机并发重跑情况与验证范围。 + +[English release notes](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.16/docs/en/releases/v0.5.16.md) · [中文发布说明](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.16/docs/zh-CN/releases/v0.5.16.md) diff --git a/dsh/.github/release-notes/v0.5.17.md b/dsh/.github/release-notes/v0.5.17.md new file mode 100644 index 000000000..49ea6c220 --- /dev/null +++ b/dsh/.github/release-notes/v0.5.17.md @@ -0,0 +1,40 @@ +## v0.5.17 · Composable Strategies and Component Settings + +This release includes thirteen merged PRs since v0.5.16 (#290–#295 and #297–#303). The Starter pins **18 packages**, including the new **General strategy 0.5.1**. + +- Choose **Layered** or **General** strategy; standard selection, projection and capture enhancements can follow either. Component switches coordinate their dependencies, and conversations continue without memory if no main strategy runs. +- Configure Mnemon under **Plugins → dsh-mnemon**. Components own their settings and Status cards; controls follow DSH's interface, with immediate switches, Apply for typed values, and explicit Default/Custom data directories. +- **Mnemon CLI is optional**: it is required only by Mnemon Native. Other ready Providers can serve Memory Spaces independently. +- Preserve **可组合记忆 (dsh-mnemon)**, use the thin brain sidebar icon, and describe layered memory as the default composition. + +- Fix General strategy recall/related receipts rejected by DSH when optional evidence fields are absent; discovered and reproduced during live Flash acceptance. + +### Upgrade + +**DSH 0.1.7-rc.2 is the supported baseline. Keep v0.5.16 on older hosts.** Back up your Profile and data, install the Starter, then restart DSH and reload the browser: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.17 +``` + +Use `headless` for that Profile. The five Strategy packages require `dsh-mnemon ^0.5.17` for the new View extension SDK. Memory storage formats are unchanged; saved component choices migrate into the DSH Profile patch. Changing the data directory does not move existing data. + +The upstream `cordis:group` display/toggle issue remains documented in [#649](https://github.com/dsh-external/issues/issues/649); this release does not patch DSH. + +### 中文说明 + +本版汇总 v0.5.16 后合入的 **13 个 PR**,Starter 固定 **18 个包**,包括首次发布的通用策略 0.5.1。 + +- 可选择分层策略或通用策略,增强可随主策略组合,组件依赖与开关联动;没有主策略时对话可以不带记忆继续。 +- 配置集中到插件中的 dsh-mnemon 页面,组件拥有独立设置页和状态卡片;界面对齐 DSH,数据目录明确区分默认与自定义。 +- Mnemon CLI 仅为 Mnemon Native 所需,其他已就绪的 Provider 可独立服务记忆空间。 +- 保留“可组合记忆 (dsh-mnemon)”名称和细线大脑图标,将分层记忆描述为默认组合。 + +- 修复真实 Flash 验收中发现的通用策略 recall/related 回执问题:证据缺少可选字段时,DSH 不再拒绝返回值。 + +**本版支持 DSH 0.1.7-rc.2,旧宿主请保留 v0.5.16。** 升级前备份 Profile 和数据,升级后重启 DSH 并刷新浏览器。五个策略包要求主包 `^0.5.17`;存储格式不变,旧组件选择会迁移至 Profile patch,修改数据目录不会搬移数据。上游 cordis:group 显示问题仍存在。 + +### Details and evidence / 详情与证据 + +- [Full package matrix and all merged PRs](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.17/docs/en/releases/v0.5.17.md) · [完整包版本与 PR 清单](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.17/docs/zh-CN/releases/v0.5.17.md) +- [Versioned release acceptance](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.17/docs/pr-assets/release-v0.5.17/README.md) · [版本化验收记录](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.17/docs/pr-assets/release-v0.5.17/README.zh-CN.md) diff --git a/dsh/.github/release-notes/v0.5.18.md b/dsh/.github/release-notes/v0.5.18.md new file mode 100644 index 000000000..099c0d8a2 --- /dev/null +++ b/dsh/.github/release-notes/v0.5.18.md @@ -0,0 +1,32 @@ +## v0.5.18 · Immediate Activation and Quiet Desktop CLI + +- **Install → Enable now works in the official WebUI.** Fix missing Source/Strategy packages with isolated Desktop or pnpm installs, and register Web RPC correctly when the shared Connection is already running. No DSH source changes are needed. (#308) +- **Avoid npm launcher's extra Windows console window.** Run the verified native Mnemon executable directly for operations and version checks while preserving npm update ownership. (#305, #306) + +Changed packages: **dsh-mnemon 0.5.18** and **dsh-mnemon-source-memory-spaces 0.5.12**. The other 16 component versions remain unchanged. + +### Upgrade + +Supports **DSH 0.1.7-rc.2**. Install `dsh-mnemon@0.5.18` through **Add plugin** in the official WebUI, or run: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.18 +``` + +Use `headless` for that profile. If the host already loaded an older version, restart DSH once and reload the browser after upgrading. Fresh installation and activation of the new version do not require a restart. Memory formats, component choices and other plugin dependency routes are preserved. Keep the new Starter readiness component enabled. + +The existing upstream `cordis:group` display issue remains documented; use the bundle or core switch instead of the container row. + +### 中文说明 + +- **官方 WebUI 安装后可立即启用。** 修复 Desktop/pnpm 隔离依赖下 Source/Strategy 无法导入,以及已运行的 Connection 下 Web RPC 不可用的问题;全部修复位于 dsh-mnemon。 (#308) +- **避免 npm 启动器额外弹出 Windows 控制台。** 记忆操作与版本检查直接调用经过验证的 Mnemon Native 可执行文件,更新仍由 npm 管理。 (#305、#306) + +本次仅升级 **主包 0.5.18** 和 **Memory Spaces 0.5.12**,其余 16 个组件版本不变。 + +支持 **DSH 0.1.7-rc.2**。已加载旧版的实例升级后需重启一次 DSH 并刷新浏览器;新版首次安装和启用无需重启。记忆格式、组件选择和其他插件依赖路径保留。新增 Starter 就绪组件应保持启用。上游 cordis:group 显示问题仍存在,请使用 bundle 或核心开关。 + +### Details and evidence / 详情与证据 + +- [Release notes](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.18/docs/en/releases/v0.5.18.md) · [发布说明](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.18/docs/zh-CN/releases/v0.5.18.md) +- [Versioned acceptance](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.18/docs/pr-assets/release-v0.5.18/README.md) · [版本化验收](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.18/docs/pr-assets/release-v0.5.18/README.zh-CN.md) diff --git a/dsh/.github/release-notes/v0.5.19.md b/dsh/.github/release-notes/v0.5.19.md new file mode 100644 index 000000000..6f6f12362 --- /dev/null +++ b/dsh/.github/release-notes/v0.5.19.md @@ -0,0 +1,33 @@ +## v0.5.19 · DSH 0.2, Desktop Memory and a Guided First Install + +- **DSH 0.2.0-rc.1 support.** DSH checks every `@deepseek-ai/dsh*` peer range before installing a plugin and at startup, and refused earlier releases on 0.2. The Starter and the Memory Spaces Source now accept `^0.1.7-rc.2 || ^0.2.0-rc.1`. (#311) +- **Manage memory in DSH Desktop.** Desktop windows (`dsh-app://app/`) now use Mnemon's local channels, so Runtime memory, Memory Spaces and the plugin settings are editable with default settings. Pages opened from another device stay read only and say why. Status also names the Mnemon CLI version on a fresh install. (#310, #311) +- **Consistent memory interactions.** One Save to memory dialog with a receipt, a turn memory bar whose items open where they live, and DSH's own inputs, selectors and modal layer. (#307) +- **A guide from an empty machine to the first memory**, for the web, the desktop app and the command line, with screenshots and recordings from real DSH 0.2. (#307, #311) + +Changed packages: **dsh-mnemon 0.5.19**, **dsh-mnemon-source-runtime 0.5.11**, **dsh-mnemon-source-documents 0.5.8**, **dsh-mnemon-source-memory-spaces 0.5.13** and **dsh-mnemon-provider-mnemon-native 0.5.7**. The other 13 component versions remain unchanged. + +### Upgrade + +Supports **DSH 0.1.7-rc.2** (npm `latest`) and **DSH 0.2.0-rc.1** (npm `next`). Install `dsh-mnemon@0.5.19` through **Add plugin**, then **Enable now**, or run: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.19 +``` + +Include the version for the first 24 hours: pnpm 11 skips versions younger than a day and would install the previous release, which DSH 0.2 refuses. Update dsh-mnemon before moving a profile to DSH 0.2. Desktop users update from the app's Plugins page; no `remoteAccess` change is needed. If the host already loaded an older version, restart DSH once and reload the browser. No data migration. + +### 中文说明 + +- **支持 DSH 0.2.0-rc.1。** DSH 在安装插件前和启动时检查 `@deepseek-ai/dsh*` peer 范围,此前版本在 0.2 上会被拒绝。Starter 与记忆空间 Source 现在接受 `^0.1.7-rc.2 || ^0.2.0-rc.1`。(#311) +- **桌面版可以管理记忆。** 桌面窗口(`dsh-app://app/`)改用 Mnemon 本地通道,运行时记忆、记忆空间与插件设置以默认配置即可编辑;从其他设备打开的页面保持只读并写明原因。全新安装后状态页会写出 Mnemon CLI 版本。(#310、#311) +- **记忆交互一致。** 统一的“存入记忆”对话框与回执,回合记忆栏条目直达所在位置,使用 DSH 自己的输入框、选择器与弹窗层级。(#307) +- **从空白电脑到第一条记忆的安装指南**,覆盖网页、桌面版与命令行,配有真实 DSH 0.2 的截图与录屏。(#307、#311) + +发布后 24 小时内请安装 `dsh-mnemon@0.5.19`(pnpm 11 不会选用发布不足一天的版本)。升级到 DSH 0.2 前先更新 dsh-mnemon;桌面版在应用的插件页中更新,无需修改 `remoteAccess`。已加载旧版的实例升级后重启一次 DSH 并刷新浏览器。无需迁移数据。 + +### Details and evidence / 详情与证据 + +- [Release notes](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.19/docs/en/releases/v0.5.19.md) · [发布说明](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.19/docs/zh-CN/releases/v0.5.19.md) +- [Install and start](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.19/docs/en/guides/installation.md) · [安装与启动](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.19/docs/zh-CN/guides/installation.md) +- [Versioned acceptance](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.19/docs/pr-assets/release-v0.5.19/README.md) · [版本化验收](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.19/docs/pr-assets/release-v0.5.19/README.zh-CN.md) diff --git a/dsh/.github/release-notes/v0.5.2.md b/dsh/.github/release-notes/v0.5.2.md new file mode 100644 index 000000000..db6fa3d90 --- /dev/null +++ b/dsh/.github/release-notes/v0.5.2.md @@ -0,0 +1,30 @@ +## v0.5.2 · Atomic Disable and Independent Releases + +v0.5.2 fixes the v0.5 Starter lifecycle regression reported in [#185](https://github.com/omdsh-dev/dsh-mnemon/issues/185) and completes the one-time transition to independently versioned Source, Strategy, and Provider packages. + +### Fix + +- The existing `id: mnemon` profile Entry is again the atomic switch for the complete Starter. Setting `disabled: true` now disables the Core/Host, all three bundled Sources, the default Strategy, and all three optional Strategy enhancements together. +- DSH no longer fails startup with four Entries pending on `mnemonMemory` when a profile retained the disabled state from v0.4. Existing Entry ids and configuration targets remain compatible, and disabling the Starter does not uninstall packages or delete memory data. + +### Package release model + +- This release publishes the Starter and all sixteen official plugins once at `0.5.2` because the independent-version transition changes each package's published dependency metadata. +- Future releases select only packages whose own artifacts changed, plus `dsh-mnemon` whenever its exact tested composition changes. Selected plugin dependency layers publish with bounded concurrency, and the Starter publishes last. + +### Verification and delivery + +- [PR #186](https://github.com/omdsh-dev/dsh-mnemon/pull/186) reproduces the exact disabled-profile failure on DSH 0.1.2-rc.1 and Node.js 22.23.2, then verifies real Headless and WebUI startup with all eight Mnemon Entries disabled and no `mnemon_*` tool leakage. +- [PR #181](https://github.com/omdsh-dev/dsh-mnemon/pull/181) adds Changesets-backed release intent, changed-package validation, frozen selected artifacts, dependency-layer publication, and Registry read-back checks. + +### Install or upgrade + +`v0.5.2` is published on the npm `latest` channel: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.2 +``` + +Use `headless` instead of `web` for a Headless profile, then restart DSH. No storage migration is required. Back up the effective memory root, Documents, DSH profile/configuration, and separately managed Provider data before upgrading. + +Full compatibility and rollback details are in the [English release notes](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.2/docs/en/releases/v0.5.2.md) and [中文发布说明](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.2/docs/zh-CN/releases/v0.5.2.md). diff --git a/dsh/.github/release-notes/v0.5.20.md b/dsh/.github/release-notes/v0.5.20.md new file mode 100644 index 000000000..05a3bdc6b --- /dev/null +++ b/dsh/.github/release-notes/v0.5.20.md @@ -0,0 +1,29 @@ +## v0.5.20 · No Separate Starter Switch + +- **The Starter's readiness is part of its component group.** 0.5.18 and 0.5.19 listed `dsh-mnemon/starter` on the Plugins page, and turning it off kept every component waiting for `mnemonStarterReady`: `dsh web` started without the Memory System and the desktop app failed its start. The group now prepares dependency resolution itself (`dsh-mnemon/bundle`), so the row is gone, **Enable now** still needs no restart, and a stuck profile recovers after the update and a restart. (#314) +- **Recovery after updates.** The installation guide covers errors after an update without a restart, the desktop app's recovery page, and pnpm's one-day window falling back more than one release. (#313, #315) + +Changed package: **dsh-mnemon 0.5.20**. The 17 pinned component versions are unchanged. + +### Upgrade + +Supports **DSH 0.1.7-rc.2** (npm `latest`) and **DSH 0.2.0-rc.1** (npm `next`). Install `dsh-mnemon@0.5.20` through **Add plugin**, then **Enable now**, or run: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.20 +``` + +Include the version for the first 24 hours: pnpm 11 skips versions younger than a day, and until 17:31 UTC on 2026-09-29 it falls back to 0.5.17, which DSH 0.2 refuses. Existing installations run `dsh plugin --profile web add dsh-mnemon@0.5.20` for those 24 hours (pnpm keeps `update` and `update --latest` on the installed version until then), `dsh plugin --profile web update dsh-mnemon` afterwards, and restart DSH; until the restart, an update from 0.5.18 or 0.5.19 reports `mnemon-bundle (dsh-mnemon/bundle): pending (waiting for service: mnemonStarterReady)`. Desktop users update from the app's Plugins page, then quit and reopen it. No data migration. `dsh web --dump-config-schema` no longer lists Mnemon's component schemas. + +### 中文说明 + +- **Starter 的就绪步骤并入组件组。** 0.5.18 与 0.5.19 在插件页列出 `dsh-mnemon/starter`,关闭它会让全部组件一直等待 `mnemonStarterReady`:`dsh web` 启动后没有记忆系统,桌面版则启动失败。组件组现在自己准备依赖解析(`dsh-mnemon/bundle`),因此那一行已经去掉,“立即启用”仍无需重启,卡住的 profile 更新并重启后即可恢复。(#314) +- **更新后的恢复说明。** 安装指南补充了更新后不重启出现的报错、桌面版的插件恢复页,以及 pnpm 一天期限可能退回不止一个版本的情况。(#313、#315) + +发布后 24 小时内请安装 `dsh-mnemon@0.5.20`:pnpm 11 不会选用发布不足一天的版本,在北京时间 2026-09-30 01:31 之前会退回 0.5.17,DSH 0.2 会拒绝它。已有安装在这 24 小时内运行 `dsh plugin --profile web add dsh-mnemon@0.5.20`(在此之前 pnpm 的 `update` 与 `update --latest` 都会停留在已安装的版本),之后运行 `dsh plugin --profile web update dsh-mnemon`,然后重启 DSH;重启前,从 0.5.18 或 0.5.19 更新会提示 `mnemon-bundle (dsh-mnemon/bundle): pending (waiting for service: mnemonStarterReady)`。桌面版在插件页中更新后完全退出并重新打开。无需迁移数据。`dsh web --dump-config-schema` 不再列出 Mnemon 各组件的 schema。 + +### Details and evidence / 详情与证据 + +- [Release notes](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.20/docs/en/releases/v0.5.20.md) · [发布说明](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.20/docs/zh-CN/releases/v0.5.20.md) +- [Versioned acceptance](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.20/docs/pr-assets/release-v0.5.20/README.md) · [版本化验收](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.20/docs/pr-assets/release-v0.5.20/README.zh-CN.md) +- [Cold start and upgrades](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.20/docs/pr-assets/starter-group-upgrade/README.md) · [冷启动与升级](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.20/docs/pr-assets/starter-group-upgrade/README.zh-CN.md) diff --git a/dsh/.github/release-notes/v0.5.21.md b/dsh/.github/release-notes/v0.5.21.md new file mode 100644 index 000000000..283875245 --- /dev/null +++ b/dsh/.github/release-notes/v0.5.21.md @@ -0,0 +1,33 @@ +## v0.5.21 · Large Native Spaces and Leaner Hot Memory + +- **Large Native spaces work again.** Mnemon Native reads a whole Memory Space for its contents list, its graph and the exact-content check that runs before runtime memory archives at capacity. Every Mnemon command stopped at 2 MiB of output, but a space of about 1,000 insights returns 3–9 MB. Those reads failed, working memory could no longer make room, and the error suggested installing Mnemon. Whole-space reads now accept up to 128 MiB, every other command keeps 2 MiB, and a failure names its cause. (#321) +- **Idle review writes one layer.** Idle review no longer copies project records into hot memory. It considers a Document first, and MEMORY.md takes only a compact rule the user stated that no Document or entry covers. One pass writes a Document or a working-memory change, never both. **Write runtime memory** under Idle review on the Layered strategy's page limits review to Documents. (#322) +- **The fallback sidebar entry matches DSH's rows.** In layouts without DSH's native panel seat, the Memory System entry now uses DSH's own panel-row style. (#323) + +Changed packages: **dsh-mnemon 0.5.21**, **dsh-mnemon-source-memory-spaces 0.5.14** and **dsh-mnemon-provider-mnemon-native 0.5.8**. The other 15 pinned component versions are unchanged. + +### Upgrade + +Supports **DSH 0.2.0-rc.2** (npm `latest` and `next`) and **DSH 0.1.7-rc.2**. Install `dsh-mnemon@0.5.21` through **Add plugin**, then **Enable now**, or run: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.21 +``` + +Include the version for the first 24 hours: pnpm 11 skips versions younger than a day, so a plain install or update picks 0.5.20 until then. Existing installations run `dsh plugin --profile web add dsh-mnemon@0.5.21` for those 24 hours and `dsh plugin --profile web update dsh-mnemon` afterwards, then restart DSH. Desktop users update from the app's Plugins page, then quit and reopen it. No data migration: existing MEMORY.md entries stay, and `idleReview.runtimeMemory` defaults to on. + +### 中文说明 + +- **大型 Native 空间恢复可用。**Mnemon Native 会在内容列表、图谱,以及运行时记忆满容量归档前的逐字比对中读取整个记忆空间。此前每条 Mnemon 命令的输出上限都是 2 MiB,而约 1,000 条记忆的空间会返回 3–9 MB。这些读取因此失败,工作记忆无法腾出空间,报错还提示去安装 Mnemon。现在整库读取的上限为 128 MiB,其余命令仍为 2 MiB,失败时会写明原因。(#321) +- **空闲审查每轮只写一层。**空闲审查不再把项目记录复制进热记忆。它先考虑项目档案;MEMORY.md 只收用户明确说过、且项目档案和已有条目都未涵盖的简短规则。每轮只写项目档案或工作记忆之一。分层策略页面的空闲审查中新增**写入运行时记忆**开关,关闭后审查只创建项目档案。(#322) +- **兜底侧栏入口与 DSH 原生行一致。**在没有 DSH 原生面板席位的布局中,“记忆系统”入口现在使用 DSH 自身面板行的样式。(#323) + +变更的包:**dsh-mnemon 0.5.21**、**dsh-mnemon-source-memory-spaces 0.5.14** 与 **dsh-mnemon-provider-mnemon-native 0.5.8**;其余 15 个锁定的组件版本不变。支持 **DSH 0.2.0-rc.2**(npm `latest` 与 `next`)和 **DSH 0.1.7-rc.2**。 + +发布后 24 小时内请安装 `dsh-mnemon@0.5.21`:pnpm 11 不会选用发布不足一天的版本,在此之前普通安装或更新会得到 0.5.20。已有安装在这 24 小时内运行 `dsh plugin --profile web add dsh-mnemon@0.5.21`,之后运行 `dsh plugin --profile web update dsh-mnemon`,然后重启 DSH。桌面版在插件页中更新后完全退出并重新打开。无需迁移数据:已有的 MEMORY.md 条目保持不变,`idleReview.runtimeMemory` 默认开启。 + +### Details and evidence / 详情与证据 + +- [Release notes](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.21/docs/en/releases/v0.5.21.md) · [发布说明](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.21/docs/zh-CN/releases/v0.5.21.md) +- [Versioned acceptance](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.21/docs/pr-assets/release-v0.5.21/README.md) · [版本化验收](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.21/docs/pr-assets/release-v0.5.21/README.zh-CN.md) +- Fix records / 修复记录: [#320](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.21/docs/pr-assets/issue-320-native-store-dumps/README.md) · [#319](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.21/docs/pr-assets/issue-319-review-layers/README.md) · [#318](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.21/docs/pr-assets/issue-318-sidebar-entry/README.md) diff --git a/dsh/.github/release-notes/v0.5.22.md b/dsh/.github/release-notes/v0.5.22.md new file mode 100644 index 000000000..8c079756c --- /dev/null +++ b/dsh/.github/release-notes/v0.5.22.md @@ -0,0 +1,33 @@ +## v0.5.22 · In-App Updates, Conversation Tabs and Ollama Subagents + +- **Check versions → Update works in the desktop app.** The update now goes through DSH's own plugin installer, as `dsh plugin add dsh-mnemon@` does: it uses the Profile's package manager, which in the desktop app is the app's bundled pnpm, installs the exact checked version, lets DSH check the DSH versions the release supports, and restores the Profile when an install fails, showing pnpm's error line and DSH's log. The Memory System page DSH loads after the update reopens Check versions with the result, and until DSH restarts a notice above every page names the installed version and the one that still runs. (#325) +- **The Memory System works in a conversation tab whose Agent is not loaded.** For a conversation DSH showed but had not resumed, such as one whose Agent preset left the profile, Status waited for a workspace and Project Documents failed with `memory Source is unavailable in the requested management scope`; with workspace or centralized storage, runtime memory and Memory Spaces used the directory DSH started from. The Host now finds the workspace in DSH's workspace registry, and task Agents archive a full Documents set and choose a new space's Provider. (#326) +- **Memory subagents recover when a chat template requires a user query.** On Ollama 0.33 with Qwen3.x, idle review and other memory subagents failed with `no user query found in messages` once tool results pushed their prompt out of the context window. When a server refuses a subagent request this way, Mnemon adds a short user turn and retries the step at once; servers that never refuse, and the main conversation, see exactly what they saw before. (#328, issue #327) + +Changed package: **dsh-mnemon 0.5.22**. The Starter's 17 pinned component versions are unchanged. + +### Upgrade + +Supports **DSH 0.2.0-rc.2** (npm `latest` and `next`) and **DSH 0.1.7-rc.2**. Install `dsh-mnemon@0.5.22` through **Add plugin**, then **Enable now**, or run: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.22 +``` + +Include the version for the first 24 hours: pnpm 11 skips versions younger than a day, so a plain install or update picks 0.5.21 until then. Existing installations run `dsh plugin --profile web add dsh-mnemon@0.5.22` for those 24 hours and `dsh plugin --profile web update dsh-mnemon` afterwards, then restart DSH. In the desktop app, remove dsh-mnemon on the Plugins page and add `dsh-mnemon@0.5.22` again once, then quit and reopen the app; memory data stays, and from 0.5.22 on **Check versions → Update** installs later releases. No data migration. + +### 中文说明 + +- **检查版本 → 更新在桌面版中可用。**更新现在通过 DSH 自己的插件安装器完成,效果与 `dsh plugin add dsh-mnemon@<版本>` 相同:使用 Profile 自己的包管理器(桌面版中即应用自带的 pnpm),安装检查到的精确版本,由 DSH 检查该版本支持的 DSH 版本;安装失败时恢复 Profile,并显示 pnpm 的错误行与 DSH 的日志。更新后 DSH 载入的新记忆系统页面会重新打开检查版本并显示结果;DSH 重启之前,每个页面顶部都会提示已安装的版本与仍在运行的版本。(#325) +- **会话的 Agent 未加载时,会话标签页中的记忆系统也能使用。**对于 DSH 已显示但尚未恢复的会话(例如所用 Agent 预设已不在 profile 中),此前状态页一直等待工作区,项目档案报 `memory Source is unavailable in the requested management scope`;使用工作区或集中存储时,运行时记忆和记忆空间用的是 DSH 启动时所在的目录。现在 Host 通过 DSH 的工作区注册表找到工作区,并由任务 Agent 在项目档案已满时归档、为新记忆空间选择 Provider。(#326) +- **聊天模板要求用户查询时,记忆子代理可以自行恢复。**在 Ollama 0.33 上使用 Qwen3.x 时,工具结果把任务提示挤出上下文窗口后,后台审查等记忆子代理会报 `no user query found in messages`。现在服务端以这种方式拒绝子代理的请求时,Mnemon 会追加一条简短的用户消息并立即重试这一步;从不拒绝的服务端和主会话,收到的请求与之前完全相同。(#328,issue #327) + +变更的包:**dsh-mnemon 0.5.22**;Starter 锁定的 17 个组件版本不变。支持 **DSH 0.2.0-rc.2**(npm `latest` 与 `next`)和 **DSH 0.1.7-rc.2**。 + +发布后 24 小时内请安装 `dsh-mnemon@0.5.22`:pnpm 11 不会选用发布不足一天的版本,在此之前普通安装或更新会得到 0.5.21。已有安装在这 24 小时内运行 `dsh plugin --profile web add dsh-mnemon@0.5.22`,之后运行 `dsh plugin --profile web update dsh-mnemon`,然后重启 DSH。桌面版请在插件页移除 dsh-mnemon 后重新添加 `dsh-mnemon@0.5.22`,再完全退出并重新打开应用;记忆数据会保留,从 0.5.22 起可在**检查版本 → 更新**中安装之后的版本。无需迁移数据。 + +### Details and evidence / 详情与证据 + +- [Release notes](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.22/docs/en/releases/v0.5.22.md) · [发布说明](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.22/docs/zh-CN/releases/v0.5.22.md) +- [Versioned acceptance](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.22/docs/pr-assets/release-v0.5.22/README.md) · [版本化验收](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.22/docs/pr-assets/release-v0.5.22/README.zh-CN.md) +- Fix records / 修复记录: [#325](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.22/docs/pr-assets/version-update-through-dsh/README.md) · [#326](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.22/docs/pr-assets/conversation-tab-workspace/README.md) · [#327](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.22/docs/pr-assets/issue-327-subagent-user-turn/README.md) diff --git a/dsh/.github/release-notes/v0.5.23.md b/dsh/.github/release-notes/v0.5.23.md new file mode 100644 index 000000000..1884edced --- /dev/null +++ b/dsh/.github/release-notes/v0.5.23.md @@ -0,0 +1,26 @@ +## v0.5.23: Faster memory operations / 记忆操作性能优化 + +Publishes [#330](https://github.com/omdsh-dev/dsh-mnemon/pull/330): linear Runtime compaction, fewer Document excerpts, indexed space discovery and pinned-space resolution, single-pass recall statistics, cheaper batch preparation and stable bounded Holographic ranking. Result order, permissions, storage formats and UI flows are preserved. + +发布 [#330](https://github.com/omdsh-dev/dsh-mnemon/pull/330):Runtime 线性压缩、按返回结果生成 Document 摘要、空间发现与固定空间解析索引、单次遍历的召回统计、更低成本的批量准备,以及 Holographic 稳定有界排名。结果顺序、权限、存储格式与 UI 流程保持不变。 + +The Starter advances to **0.5.23**, Runtime to **0.5.12**, Documents to **0.5.9**, Memory Spaces to **0.5.15** and Holographic to **0.5.6**. The other 13 component versions are unchanged. + +Starter 升至 **0.5.23**,Runtime 升至 **0.5.12**,Documents 升至 **0.5.9**,Memory Spaces 升至 **0.5.15**,Holographic 升至 **0.5.6**;其余 13 个组件版本不变。 + +Measured N=1/10/100/1000 workloads are documented in the audit. At N=1000, local Runtime compaction falls from 41.347 ms to 4.047 ms, and local multi-space recall orchestration from 254.008 ms to 9.834 ms. These synthetic measurements exclude remote Provider and LLM latency. + +审计记录 N=1/10/100/1000 的测试。在 N=1000 时,本地 Runtime 压缩从 41.347 ms 降至 4.047 ms,多路召回本地编排从 254.008 ms 降至 9.834 ms;合成测试不包含远程 Provider 和 LLM 延迟。 + +From v0.5.22, use **Memory System → Status → Check versions → Update**, then restart DSH. Or install the exact version: + +从 v0.5.22 可使用**记忆系统 → 状态 → 检查版本 → 更新**,然后重启 DSH;也可安装精确版本: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.23 +``` + +No memory migration is required. 无需迁移已有记忆。 + +- [English release notes](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.23/docs/en/releases/v0.5.23.md) · [中文发布说明](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.23/docs/zh-CN/releases/v0.5.23.md) +- [Algorithm audit and raw measurements](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.23/docs/pr-assets/algorithm-performance-20261003/README.md) · [算法审计与原始指标](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.23/docs/pr-assets/algorithm-performance-20261003/README.zh-CN.md) diff --git a/dsh/.github/release-notes/v0.5.24.md b/dsh/.github/release-notes/v0.5.24.md new file mode 100644 index 000000000..32d50e495 --- /dev/null +++ b/dsh/.github/release-notes/v0.5.24.md @@ -0,0 +1,27 @@ +## v0.5.24: Complete entity counts and related memories on demand / 完整的实体计数与按需查找的相关记忆 + +Publishes [#332](https://github.com/omdsh-dev/dsh-mnemon/pull/332). The Entities page counts and lists every memory that carries an entity: the count is the number of those memories, the list holds every entity, and selecting one lists exactly those memories at once. Below them, **Find related memories** runs recall for the entity only when asked for, and stays open for the next entities until hidden. A Provider whose `list()` stops short of a space can supply the optional `entityIndex`. + +发布 [#332](https://github.com/omdsh-dev/dsh-mnemon/pull/332)。实体页统计并列出带有某个实体的全部记忆:计数就是这些记忆的条数,列表包含全部实体,选中后立即正好列出这些记忆。其下的**查找相关记忆**只在需要时运行召回,展开后切换到其他实体时保持展开,直到收起。`list()` 取不全整个空间的 Provider 可以提供可选的 `entityIndex`。 + +The Starter advances to **0.5.24** and Memory Spaces to **0.5.16**. The other 16 component versions are unchanged. + +Starter 升至 **0.5.24**,Memory Spaces 升至 **0.5.16**;其余 16 个组件版本不变。 + +On synthetic Native spaces, an entity counted 22 now lists 22 instead of 20 that included 3 without it, and the first memory shows 91–99 ms after a click instead of 400–1,891 ms. With 4 × 500 memories, a selection shows every carrying memory after 7–10 ms instead of 20 results after 1.5–1.7 s. + +在合成的 Native 空间上,计数 22 的实体现在列出 22 条,而不是混有 3 条不带该实体的 20 条;点击后第一条记忆在 91–99 毫秒出现,原先为 400–1,891 毫秒。在 4 × 500 条记忆上,选中实体 7–10 毫秒后显示带有它的全部记忆,原先 1.5–1.7 秒后才显示 20 条结果。 + +From v0.5.22, use **Memory System → Status → Check versions → Update**, then restart DSH. Or install the exact version: + +从 v0.5.22 可使用**记忆系统 → 状态 → 检查版本 → 更新**,然后重启 DSH;也可安装精确版本: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.24 +``` + +No memory migration is required. 无需迁移已有记忆。 + +- [English release notes](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.24/docs/en/releases/v0.5.24.md) · [中文发布说明](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.24/docs/zh-CN/releases/v0.5.24.md) +- [Entities page record](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.24/docs/pr-assets/entities-complete-index/README.md) · [实体页记录](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.24/docs/pr-assets/entities-complete-index/README.zh-CN.md) +- [Release acceptance](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.24/docs/pr-assets/release-v0.5.24/README.md) · [发布验收](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.24/docs/pr-assets/release-v0.5.24/README.zh-CN.md) diff --git a/dsh/.github/release-notes/v0.5.3.md b/dsh/.github/release-notes/v0.5.3.md new file mode 100644 index 000000000..bb4d84178 --- /dev/null +++ b/dsh/.github/release-notes/v0.5.3.md @@ -0,0 +1,32 @@ +## v0.5.3 · Actionable Version Maintenance + +v0.5.3 makes status-page version maintenance clearer, improves Host execution ownership, and refreshes the documentation shipped with all official packages. + +### Changes + +- Guide Mnemon CLI installation and migration through `@mnemon-dev/mnemon` on npm. Distinguish missing, unreadable, outdated, current, locally newer, and restart-pending states, while keeping installation guidance available offline. +- Verify npm ownership before updating the active CLI; share discovery with Native execution and support verified Windows npm launchers. Homebrew and Go updates remain available. +- Expand the dsh-mnemon card to maintain Source, Strategy, and Provider packages. Show installed versions, Starter pins, and Profile ownership; update independently installed packages in their owning Profile while preserving Starter dependencies and source links. +- Enforce read-only configuration and existing management authorization, serialize updates, verify installed versions, and retain restart reminders. +- Clarify Host turn/maintenance cleanup and Source presentation ownership, fix compact-header overlap, and refresh bilingual guides and published READMEs. + +Implementation: [#190](https://github.com/omdsh-dev/dsh-mnemon/pull/190), [#191](https://github.com/omdsh-dev/dsh-mnemon/pull/191), [#192](https://github.com/omdsh-dev/dsh-mnemon/pull/192). + +### Packages and upgrade + +The Starter and all sixteen official plugins advance to `0.5.3` because each has changed published content. Independent versioning remains in effect; unchanged packages will not be republished in future releases merely because the Starter changes. + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.3 +``` + +Use `headless` for a Headless Profile, then restart DSH. Mnemon CLI remains a separate installation: + +```sh +npm install --global @mnemon-dev/mnemon@latest +mnemon --version +``` + +No storage migration is required. Existing profiles and data remain compatible; rollback uses `dsh-mnemon@0.5.2` in the same Profile. The release workflow verifies frozen artifacts, npm read-back, a clean complete install, and a real Registry upgrade from v0.4.7 before creating this release. + +See the [English release notes](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.3/docs/en/releases/v0.5.3.md), [中文发布说明](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.3/docs/zh-CN/releases/v0.5.3.md), and [real status-page evidence](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.3/docs/pr-assets/status-version-maintenance/README.md) for compatibility, validation limits, and screenshots. diff --git a/dsh/.github/release-notes/v0.5.4.md b/dsh/.github/release-notes/v0.5.4.md new file mode 100644 index 000000000..38979295b --- /dev/null +++ b/dsh/.github/release-notes/v0.5.4.md @@ -0,0 +1,26 @@ +## v0.5.4 · Memory Space Terminology + +v0.5.4 standardizes “memory space” / “记忆空间” throughout the product and codebase, and keeps longer labels usable on narrow screens. + +### Changes + +- Align Chinese and English UI, accessibility labels, Agent guidance, command output, SDK terminology, published READMEs, guides, and editable diagrams. +- Add canonical `MemorySpace` models and service names while preserving deprecated aliases, existing Provider contexts and subclasses, tool/RPC fields, and stored data formats. User-authored names remain unchanged. +- Wrap narrow directory actions, Provider metadata, and storage badges; adjust navigation spacing for longer labels. +- Refresh bilingual desktop and narrow-screen UI evidence with eighteen original screenshots and recorded layout measurements. + +Implementation: [#194](https://github.com/omdsh-dev/dsh-mnemon/pull/194). + +### Packages and upgrade + +The Starter and all sixteen official plugins advance to `0.5.4` because each has changed code or published documentation. The Starter pins the full tested composition; independent package versioning remains in effect. + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.4 +``` + +Use `headless` for a Headless Profile, then restart DSH. Mnemon CLI remains separately installed and maintained through `@mnemon-dev/mnemon` on npm. + +No storage migration is required. Existing IDs, Provider connections, authorization, and data remain compatible. Rollback uses `dsh-mnemon@0.5.3` in the same Profile; separately updated plugins must be restored separately. The workflow verifies frozen artifacts, npm read-back, a clean complete install, and a real Registry upgrade from v0.4.7 before creating this release. + +See the [English release notes](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.4/docs/en/releases/v0.5.4.md), [中文发布说明](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.4/docs/zh-CN/releases/v0.5.4.md), and [bilingual UI evidence](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.4/docs/assets/memory-space-terminology/README.md) for compatibility, screenshots, and validation limits. The screenshots predate versioning and show the v0.5.3 capture base with the changes now released in v0.5.4. diff --git a/dsh/.github/release-notes/v0.5.5.md b/dsh/.github/release-notes/v0.5.5.md new file mode 100644 index 000000000..1591e013a --- /dev/null +++ b/dsh/.github/release-notes/v0.5.5.md @@ -0,0 +1,32 @@ +## v0.5.5 · Remote RPC Routing and Light Media + +v0.5.5 fixes authenticated remote WebUI calls that could receive HTTP 403, and refreshes the memory-space documentation with Light screenshots and recordings. + +### Changes + +- Route remote status, Source catalog, settings, View, activation and management calls through DSH's namespaced API Gateway; preserve local loopback channels and handle remote-desktop Host-ownership flags. +- Preserve DSH authentication and pairing. Gateway writes, ZIP operations, View mutations and settings mutations require `remoteAccess: trusted-host`; without it, readable settings report `writable: false`. Existing write restrictions remain active. +- Refresh the packaged bilingual READMEs and current guides with 60 Light JPEGs and two continuous demos, captured after importing a Mnemon Pack into disposable storage. Preserve original v0.5.4 capture labels and all historical media hashes. +- Update deployment guidance to distinguish browser authentication from Mnemon's explicit remote-management grant. + +Implementation: [#197](https://github.com/omdsh-dev/dsh-mnemon/pull/197), [#196](https://github.com/omdsh-dev/dsh-mnemon/pull/196). + +### Packages and upgrade + +Only `dsh-mnemon` advances to **0.5.5**. All sixteen official plugins remain pinned at **0.5.4** and are not republished. + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.5 +``` + +Restart DSH and reload the browser. Use `headless` for that Profile. Mnemon CLI remains independently maintained through `@mnemon-dev/mnemon` on npm. + +No memory-data migration is required. Rollback uses `dsh-mnemon@0.5.4` in the same Profile. The release workflow verifies the frozen Starter, the full mixed-version npm composition and a real Registry upgrade from v0.4.7 before creating this release. + +### 中文说明 + +本版修复远程页面已认证但状态、目录或设置请求仍返回 HTTP 403 的问题,改用 DSH API Gateway,同时保留本地回环通道。远程管理仍需显式 `remoteAccess: trusted-host` 授权,未授权的设置快照保持只读。 + +中英文文档采用导入备份后采集的 60 张浅色截图和两段连续演示,真实保留 v0.5.4 采集版本与历史媒体来源。本次仅发布主包 0.5.5,十六个子包保持 0.5.4;重启 DSH 并刷新浏览器即可使用新路由,无需迁移记忆数据。 + +See the [English release notes](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.5/docs/en/releases/v0.5.5.md), [中文发布说明](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.5/docs/zh-CN/releases/v0.5.5.md), and [Light media gallery](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.5/docs/assets/webui-v0.5.4/README.md) for compatibility, provenance and validation limits. diff --git a/dsh/.github/release-notes/v0.5.6.md b/dsh/.github/release-notes/v0.5.6.md new file mode 100644 index 000000000..403c1be57 --- /dev/null +++ b/dsh/.github/release-notes/v0.5.6.md @@ -0,0 +1,33 @@ +## v0.5.6 · Built-in Workspace Storage and Memory Reliability + +v0.5.6 adds centralized workspace isolation directly to Core/Host and ships the memory reliability fixes merged since v0.5.5. + +### Changes + +- Select Centralized workspaces in Settings → Memory system. Runtime, Documents, Memory Spaces and state share an isolated directory derived from the canonical workspace path. Customize the central root, optionally retain global USER.md, and use the same routing in Sidebar, Builtin and Headless. +- Apply Runtime capacity archival consistently across tools, Actions, child Agents and browser management. Preserve distinct but similar Native facts, validate import receipts and decode subprocess UTF-8 correctly. +- Protect existing Documents during idle review and remove forget from autonomous write workers. Explicit user operations retain their tools; unsuccessful forget receipts do not claim deletion. +- List Runtime and Documents newest first by creation time. Preserve creation dates after editing and keep long workspace paths and controls usable. +- Fix npm CLI launchers under Electron while preserving native binaries and embedding overrides. + +Implementation: [#216](https://github.com/omdsh-dev/dsh-mnemon/pull/216), [#212](https://github.com/omdsh-dev/dsh-mnemon/pull/212), [#213](https://github.com/omdsh-dev/dsh-mnemon/pull/213), [#214](https://github.com/omdsh-dev/dsh-mnemon/pull/214), [#199](https://github.com/omdsh-dev/dsh-mnemon/pull/199). + +### Packages and upgrade + +The Starter advances to **0.5.6**. Runtime, Documents, Memory Spaces, Mnemon Native Provider and Default Three-tier Strategy advance to **0.5.5**; the other eleven official plugins remain at **0.5.4**. + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.6 +``` + +Restart DSH and reload the browser. Use `headless` for that Profile. Existing storage formats remain compatible. Scope switches preserve old roots without migrating data; renamed or moved workspaces get new path-derived identities. Before downgrading to v0.5.5, switch away from `workspaces` to a supported scope and retain a backup. A Pack contains only its selected root. + +The release workflow verifies the versioned workspace and independent packages, freezes the six changed artifacts, reads back and installs the full npm combination, and tests a Registry upgrade from v0.4.7 before creating this release. + +### 中文说明 + +本版将集中式工作区隔离内置于 Core/Host,配置位于设置 → 记忆系统,可自定义集中根目录并独立保留全局 USER.md。Sidebar、Builtin 与 Headless 使用一致的工作区路由,切换范围保留旧数据,移动或重命名工作区不会自动迁移。 + +同时修复 Runtime 容量归档、Native 相似事实保留、UTF-8 输出、自动写入保护、创建时间排序和 Electron CLI 启动。主包发布 0.5.6,五个相关子包发布 0.5.5,其余十一个子包保持 0.5.4。回滚到 v0.5.5 前需先退出新 `workspaces` 范围并备份数据。 + +See the [English release notes](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.6/docs/en/releases/v0.5.6.md), [中文发布说明](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.6/docs/zh-CN/releases/v0.5.6.md) and [nine-screenshot Core WebUI report](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.6/docs/pr-assets/issue-189-core-workspaces/README.md). Historical Flash experiments retain their recorded limits and unresolved quality findings; ordinary CI does not rerun live-model or Windows-only checks. diff --git a/dsh/.github/release-notes/v0.5.7.md b/dsh/.github/release-notes/v0.5.7.md new file mode 100644 index 000000000..772802cf7 --- /dev/null +++ b/dsh/.github/release-notes/v0.5.7.md @@ -0,0 +1,29 @@ +## v0.5.7 · DSH 0.1.5 Compatibility and Document Archival + +v0.5.7 fixes Memory system HTTP 405 errors on the published DSH 0.1.5-rc.1 profile and makes document archival validate its destination before writing an index. + +- Restore authenticated Settings, Status and Source RPC routes on DSH 0.1.5-rc.1 while retaining earlier supported DSH peer ranges. +- Emit valid Session metadata and ship `dsh-mnemon-repair-session` for explicit preview or copy repair of the two known legacy Mnemon summary fields. Existing Sessions are not modified automatically. Stop DSH, back up the complete Session root and follow the [recovery procedure](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.7/docs/en/guides/operations.md#dsh-015-compatibility-and-legacy-session-recovery) before replacing a file. +- Build archive lineage from Host-owned durable receipts, reuse exact indexes and compensate new indexes if the document commit fails. Invalid plans create no index, and live read-only policy is respected. + +Implementation: [#226](https://github.com/omdsh-dev/dsh-mnemon/pull/226), [#224](https://github.com/omdsh-dev/dsh-mnemon/pull/224). + +### Packages and upgrade + +The Starter advances to **0.5.7**. Runtime, Documents and Memory Spaces Sources advance to **0.5.6**. Mnemon Native Provider and Default Three-tier Strategy remain at **0.5.5**; the other eleven plugins remain at **0.5.4**. + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.7 +``` + +Restart DSH and reload the browser; use `headless` for that Profile. The full DSH profile requires Node `^22.19.0 || >=24.0.0`. Memory storage formats remain unchanged. Keep pre-migration Session backups if you may return to an older DSH version. + +The release workflow freezes and verifies four changed artifacts, reads back and installs the complete npm composition, and tests a Registry upgrade from v0.4.7 before creating this release. + +### 中文说明 + +本版修复 DSH 0.1.5-rc.1 下记忆系统设置与状态的 HTTP 405,纠正新会话消息元数据,并提供旧会话的显式副本修复命令。修复前需停止 DSH、备份整个会话根目录,按[恢复流程](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.7/docs/zh-CN/guides/operations.md#dsh-015-兼容与旧会话恢复)验证副本;不会自动改写已有会话。 + +文档归档改为 Host 验证目标、写入索引并生成回执;无效提案不产生孤立索引,提交失败时补偿清理,并遵循实时只读策略。主包发布 0.5.7,三个 Source 发布 0.5.6,其余插件保持原版本。 + +See the [English release notes](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.7/docs/en/releases/v0.5.7.md), [中文发布说明](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.7/docs/zh-CN/releases/v0.5.7.md), [DSH compatibility evidence](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.7/docs/pr-assets/issue-223-dsh-015/README.md) and [document archival evidence](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.7/docs/pr-assets/issue-222-document-archive/README.md). Historical screenshots retain their tested revisions; loopback fixtures do not establish live-model quality. diff --git a/dsh/.github/release-notes/v0.5.8.md b/dsh/.github/release-notes/v0.5.8.md new file mode 100644 index 000000000..f712ff4ba --- /dev/null +++ b/dsh/.github/release-notes/v0.5.8.md @@ -0,0 +1,31 @@ +## v0.5.8 · Runtime Archive Safety and Review Reliability + +This patch release includes the six fixes merged in [#241](https://github.com/omdsh-dev/dsh-mnemon/pull/241), [#242](https://github.com/omdsh-dev/dsh-mnemon/pull/242), [#243](https://github.com/omdsh-dev/dsh-mnemon/pull/243), [#245](https://github.com/omdsh-dev/dsh-mnemon/pull/245), [#246](https://github.com/omdsh-dev/dsh-mnemon/pull/246) and [#247](https://github.com/omdsh-dev/dsh-mnemon/pull/247). + +- Preflight Runtime archive destinations, retain exact receipts and compensate only proven-new writes. +- Fall back safely on invalid routing batches while preserving complete original content. +- Stabilize delegated result tool definitions with revocable per-run requests and child ownership checks. +- Prevent composed plugins from bypassing idle-review tool restrictions; reuse complete inherited evidence. +- Surface background review errors in WebUI and logs, including context-window guidance. +- Restore the Mnemon sidebar entry under the ORCA LINK skin. + +[#244](https://github.com/omdsh-dev/dsh-mnemon/pull/244) also records fresh regression evidence for legacy Session and WebUI fixes already shipped in v0.5.7. + +### Packages and upgrade + +Only **dsh-mnemon 0.5.8** is newly published. Its sixteen pinned plugins retain their existing versions. Storage formats and supported DSH peer ranges are unchanged. + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.8 +``` + +Restart DSH and reload the browser; substitute `headless` for that Profile. The full DSH profile requires Node `^22.19.0 || >=24.0.0`. + +### 中文说明 + +本版修复运行时归档预检与失败清理、无效路由整批回退、固定结果工具的并发与生命周期控制、组合插件绕过后台审查工具限制、审查错误不可见,以及 ORCA 皮肤下侧栏入口隐藏。归档保留完整原文与可验证回执,审查复用继承证据。 + +仅主包发布 0.5.8,十六个插件保持现有固定版本。发布流程验证完整组合、npm 制品完整性和从 v0.4.7 开始的真实升级,再创建此 Release。 + +See the [English release notes](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.8/docs/en/releases/v0.5.8.md) and [中文发布说明](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.8/docs/zh-CN/releases/v0.5.8.md) for verification evidence and the limits of remote-write compensation and model-cache claims. + diff --git a/dsh/.github/release-notes/v0.5.9.md b/dsh/.github/release-notes/v0.5.9.md new file mode 100644 index 000000000..bc970952d --- /dev/null +++ b/dsh/.github/release-notes/v0.5.9.md @@ -0,0 +1,25 @@ +## v0.5.9 · Runtime Archive Destinations and Legacy Session Recovery + +- [#252](https://github.com/omdsh-dev/dsh-mnemon/pull/252): let capacity-triggering Runtime writes use the owning Source's eligible archive destinations, including spaces activated or created after recall was pinned. Recheck authority before each batch and preserve pending input on refusal. +- [#253](https://github.com/omdsh-dev/dsh-mnemon/pull/253): extend explicit legacy Session copy repair to the three known summaries, compatible v2 descriptors, empty/null stream placeholders and provable restoration of previously recorded provider tool IDs. Keep original artifacts, unrelated plugins and call/result content intact; refuse ambiguous histories. +- [#249](https://github.com/omdsh-dev/dsh-mnemon/pull/249): align the Mnemon sidebar label with sibling plugin entries. + +### Packages and upgrade + +New publications: **dsh-mnemon 0.5.9** and **dsh-mnemon-source-memory-spaces 0.5.7**. The other fifteen pinned plugins keep their existing versions. + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.9 +``` + +Restart DSH and reload the browser; use `headless` for that Profile. Legacy repair remains an explicit preview/new-copy operation. No tool ID is generated, and Issue #251 remains open for confirmation against the reporter's original Windows artifacts. + +### 中文说明 + +本版修复容量维护首次写入误报“没有合格空间”,扩展旧会话副本修复,并调整侧栏文字对齐。旧会话修复覆盖已知 summary、兼容 descriptor、空值流占位和可证明的既有工具 ID 恢复;原文件与其他插件内容得到保留,有歧义的历史继续拒绝输出。 + +主包发布 0.5.9,Memory Spaces Source 发布 0.5.7,其余十五个插件保持现有固定版本。升级后重启 DSH;旧会话需要显式预览并写入新副本。Issue #251 保留用于原始 Windows 文件的恢复确认。 + +The combined fixes passed 1,452 tests, real CLI/WebUI verification and independent plugin installation, with 26 preserved screenshots. The release workflow verifies the versioned artifacts, npm composition and real Registry upgrade before creating this Release. + +See the [English release notes](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.9/docs/en/releases/v0.5.9.md) and [中文发布说明](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.9/docs/zh-CN/releases/v0.5.9.md). diff --git a/dsh/.github/workflows/issue-dedup.yml b/dsh/.github/workflows/issue-dedup.yml new file mode 100644 index 000000000..13be65df6 --- /dev/null +++ b/dsh/.github/workflows/issue-dedup.yml @@ -0,0 +1,126 @@ +name: 重复 Issue 检测 / Detect Duplicate Issues + +on: + issues: + types: [opened] + +permissions: + contents: read + issues: write + +jobs: + dedup: + name: 关闭重复 Issue / Close duplicate issues + runs-on: ubuntu-latest + + steps: + - name: 检测重复 Issue / Detect duplicate issue + uses: actions/github-script@v8 + with: + script: | + const issue = context.payload.issue; + if (!issue || issue.pull_request) { + return; + } + + const title = (issue.title || '').trim(); + if (title.length < 4) { + return; + } + + const STOPWORDS = new Set([ + 'the', 'a', 'an', 'is', 'are', 'of', 'to', 'in', 'on', 'for', 'and', 'or', + 'bug', 'issue', + ]); + const tokens = (title.match(/[\p{L}\p{N}]+/gu) || []) + .filter((token) => !STOPWORDS.has(token.toLowerCase())); + const sanitize = (value) => + value.replace(/[^\p{L}\p{N}\s]/gu, ' ').replace(/\s+/g, ' ').trim(); + const query = sanitize(tokens.length > 0 ? tokens.slice(0, 8).join(' ') : title); + if (!query) { + return; + } + + const { data: search } = await github.rest.search.issuesAndPullRequests({ + q: 'repo:' + context.repo.owner + '/' + context.repo.repo + + ' is:issue is:open in:title ' + query, + per_page: 30, + }); + const candidates = (search.items || []).filter( + (item) => item.number !== issue.number && !item.pull_request, + ); + + const levenshtein = (left, right) => { + const rows = left.length; + const columns = right.length; + if (rows === 0) return columns; + if (columns === 0) return rows; + const distance = new Array(columns + 1); + for (let column = 0; column <= columns; column += 1) distance[column] = column; + for (let row = 1; row <= rows; row += 1) { + let previous = distance[0]; + distance[0] = row; + for (let column = 1; column <= columns; column += 1) { + const current = distance[column]; + distance[column] = Math.min( + distance[column] + 1, + distance[column - 1] + 1, + previous + (left[row - 1] === right[column - 1] ? 0 : 1), + ); + previous = current; + } + } + return distance[columns]; + }; + const normalize = (value) => value.replace(/\s+/g, ' ').trim().toLowerCase(); + const similarity = (left, right) => { + const maxLength = Math.max(left.length, right.length); + return maxLength === 0 ? 1 : 1 - levenshtein(left, right) / maxLength; + }; + + const target = normalize(title); + const duplicate = candidates.find((candidate) => { + const candidateTitle = normalize(candidate.title); + const contained = + (target.includes(candidateTitle) || candidateTitle.includes(target)) && + Math.abs(target.length - candidateTitle.length) <= + Math.max(4, Math.min(target.length, candidateTitle.length) * 0.3); + return contained || similarity(target, candidateTitle) >= 0.7; + }); + if (!duplicate) { + return; + } + + const comment = [ + '该 Issue 疑似与已有 Issue 重复,已自动关闭。', + 'This issue appears to duplicate an existing issue and was closed automatically.', + '', + `- 原 Issue / Existing issue: #${duplicate.number}「${duplicate.title}」`, + `- 链接 / Link: ${duplicate.html_url}`, + '', + '如果现象、版本或根因不同,请回复说明差异并请求重开;也可以到原 Issue 下补充新证据。', + 'If the behavior, version, or root cause differs, reply with those differences and request reopening, or add the new evidence to the existing issue.', + ].join('\n'); + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issue.number, + body: comment, + }); + try { + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issue.number, + labels: ['duplicate'], + }); + } catch (error) { + core.warning(`Could not add duplicate label: ${error.message}`); + } + await github.rest.issues.update({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issue.number, + state: 'closed', + state_reason: 'not_planned', + }); diff --git a/dsh/.github/workflows/issue-template-enforcer.yml b/dsh/.github/workflows/issue-template-enforcer.yml new file mode 100644 index 000000000..772dae76e --- /dev/null +++ b/dsh/.github/workflows/issue-template-enforcer.yml @@ -0,0 +1,140 @@ +name: Issue 信息检查 / Check Issue Information + +on: + issues: + types: [opened, reopened, edited] + +concurrency: + group: issue-template-check-${{ github.event.issue.number }} + cancel-in-progress: false + +permissions: + contents: read + issues: write + +jobs: + enforce: + name: 提示缺失的 Issue 信息 / Request missing issue information + runs-on: ubuntu-latest + + steps: + - name: 校验 Issue 模板 / Validate issue template + uses: actions/github-script@v8 + with: + script: | + const eventIssue = context.payload.issue; + if (!eventIssue || eventIssue.pull_request) { + return; + } + + const issueParams = { + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: eventIssue.number, + }; + const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const readSection = (body, label) => { + const heading = escapeRegExp(label); + const pattern = new RegExp(`(?:^|\\n)###\\s+${heading}\\s*\\n+([\\s\\S]*?)(?=\\n###\\s+|$)`, 'i'); + const match = body.match(pattern); + return match ? match[1].replace(//g, '').trim() : ''; + }; + + const isBlank = (value) => { + const normalized = value.replace(/\s+/g, ' ').trim().toLowerCase(); + return normalized === '' || normalized === 'no response' || normalized === '_no response_'; + }; + const hasCheckedBox = (value) => /^-\s*\[[xX]\]\s+\S+/m.test(value); + const labels = { + duplicate: '提交前查重 / Duplicate check', + area: '涉及区域 / Affected area', + type: 'Issue 类型 / Issue type', + summary: '摘要 / Summary', + expected: '预期结果 / Expected outcome', + details: '详情或复现步骤 / Details or reproduction steps', + environment: '环境信息 / Environment', + }; + const requiredSections = [ + labels.duplicate, + labels.area, + labels.type, + labels.summary, + labels.expected, + labels.details, + labels.environment, + ]; + const validateIssue = (issue) => { + const section = (label) => readSection(issue.body || '', label); + // Evidence is assessed by maintainers across the whole report. + // Its section, code-block language and formatting are not gates. + const missingSections = requiredSections.filter((label) => isBlank(section(label))); + const invalidSections = []; + if (!isBlank(section(labels.duplicate)) && !hasCheckedBox(section(labels.duplicate))) { + invalidSections.push('请确认已经搜索过 open/closed Issue。 / Please confirm that you searched open and closed issues.'); + } + + return [ + ...(missingSections.length > 0 ? [`缺少或为空的必填部分 / Missing or empty required sections: ${missingSections.join(', ')}`] : []), + ...invalidSections, + ]; + }; + + // The event payload is a snapshot. Read current state before acting, + // including after comment lookup or adding a label. + const { data: initialIssue } = await github.rest.issues.get(issueParams); + if (initialIssue.state !== 'open') return; + + const marker = ''; + const comments = await github.paginate(github.rest.issues.listComments, { + ...issueParams, + per_page: 100, + }); + const previousComment = comments.find((comment) => + comment.user?.login === 'github-actions[bot]' && + comment.user?.type === 'Bot' && + comment.body?.startsWith(marker), + ); + + let { data: currentIssue } = await github.rest.issues.get(issueParams); + if (currentIssue.state !== 'open') return; + + const issueType = readSection(currentIssue.body || '', labels.type).trim().toLowerCase(); + const hasBugLabel = (currentIssue.labels || []).some((label) => String(label.name || '').toLowerCase() === 'bug'); + if (issueType === 'bug 报告 / bug report' && !hasBugLabel) { + // API/CLI reporters may not have permission to classify an issue. + await github.rest.issues.addLabels({ ...issueParams, labels: ['bug'] }); + currentIssue = (await github.rest.issues.get(issueParams)).data; + if (currentIssue.state !== 'open') return; + } + + const details = validateIssue(currentIssue); + if (details.length === 0 && !previousComment) return; + + const comment = details.length > 0 ? [ + marker, + '此 Issue 保持打开。为便于排查,请补充以下基础信息:', + 'The issue remains open. Please add the following basic information to help triage:', + '', + ...details.map((detail) => `- ${detail}`), + '', + '在正文中补充即可,本提示会在编辑后更新。证据可以是普通文本、JSON 或其他代码块,也可以直接放在复现步骤中。', + 'Update the issue body and this reminder will refresh. Evidence may be plain text, JSON or other code blocks, including content in the reproduction steps.', + ].join('\n') : [ + marker, + '基础信息已补齐,后续由维护者排查和处理。', + 'Required report information is present. Maintainers will review the report.', + ].join('\n'); + + // Update only this check's own reminder. Never change issue state. + if (previousComment) { + if (previousComment.body !== comment) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: previousComment.id, + body: comment, + }); + } + } else { + await github.rest.issues.createComment({ ...issueParams, body: comment }); + } diff --git a/dsh/.github/workflows/pr-contribution-rules.yml b/dsh/.github/workflows/pr-contribution-rules.yml new file mode 100644 index 000000000..d8928382f --- /dev/null +++ b/dsh/.github/workflows/pr-contribution-rules.yml @@ -0,0 +1,155 @@ +name: PR 贡献规范校验 / Enforce PR Contribution Rules + +on: + pull_request_target: + types: [opened, edited, synchronize, reopened, ready_for_review] + +permissions: + contents: read + pull-requests: read + +jobs: + enforce: + name: 校验 PR 贡献信息 / Validate PR contribution evidence + runs-on: ubuntu-latest + if: ${{ !github.event.pull_request.draft }} + + steps: + - name: 校验 PR 描述 / Validate pull request body + uses: actions/github-script@v8 + with: + script: | + const pr = context.payload.pull_request; + const body = pr.body || ''; + const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const readSection = (label) => { + const heading = escapeRegExp(label); + const pattern = new RegExp(`(?:^|\\n)##\\s+${heading}\\s*\\n+([\\s\\S]*?)(?=\\n##\\s+|$)`, 'i'); + const match = body.match(pattern); + return match ? match[1].replace(//g, '').trim() : ''; + }; + const isBlank = (value) => { + const normalized = value.replace(/\s+/g, ' ').trim().toLowerCase(); + return normalized === '' || normalized === 'no response' || normalized === '_no response_'; + }; + const checkedLines = (value) => value.match(/^-\s*\[[xX]\]\s+\S.*$/gm) || []; + const hasCheckedLine = (value, label) => { + const pattern = new RegExp(`^-\\s*\\[[xX]\\]\\s*${escapeRegExp(label)}\\s*$`, 'm'); + return pattern.test(value); + }; + const cleanText = (value) => value + .replace(/```[a-zA-Z0-9_-]*\s*/g, '') + .replace(/```/g, '') + .trim(); + const readLabeledPart = (value, label, nextLabel = null) => { + const start = escapeRegExp(label); + const end = nextLabel ? `\\n\\s*${escapeRegExp(nextLabel)}\\s*[::]` : '$'; + const pattern = new RegExp(`${start}\\s*[::]\\s*([\\s\\S]*?)(?=${end})`, 'i'); + const match = value.match(pattern); + return match ? cleanText(match[1]) : ''; + }; + const hasEvidence = (value) => /!\[[^\]]*]\([^)]+\)/i.test(value) + || /https?:\/\/\S*(?:github\.com\/user-attachments\/assets\/|github\.com\/[^)\s]+\/assets\/|githubusercontent\.com\/|[./][^)\s]+\.(?:png|jpe?g|gif|webp|mp4|mov|webm))(?:[?#]\S*)?/i.test(value); + + let isMaintainer = false; + try { + const { data } = await github.rest.repos.getCollaboratorPermissionLevel({ + owner: context.repo.owner, + repo: context.repo.repo, + username: pr.user.login, + }); + isMaintainer = ['admin', 'maintain', 'write'].includes(data.permission); + } catch (error) { + core.info(`Contributor permission lookup returned no write grant: ${error.message}`); + } + + const sections = { + summary: '摘要 / Summary', + related: '关联 Issue 或背景 / Related Issue or Context', + affected: '涉及区域 / Affected Areas', + type: 'PR 类型 / PR Type', + latest: '最新代码确认 / Latest Codebase Confirmation', + ai: 'AI 编码披露 / AI Coding Disclosure', + rules: '仓库规范检查 / Repository Rules', + compatibility: '兼容性与数据安全 / Compatibility and Data Safety', + validation: '本地验证 / Local Validation', + evidence: '用户可见变更证据 / Local Feature Evidence', + }; + const labels = { + latestConfirmation: '我已基于最新 `main` 分支开发,或在提交前已 rebase 或合并最新 `main`。 / I developed from the latest `main`, or rebased or merged the latest `main` before submitting.', + syncCommand: '同步命令 / Sync command', + aiModel: '使用的 AI 模型 / AI model used', + aiTool: '使用的编码 Agent 工具 / Coding Agent tool used', + commands: '执行的命令 / Commands run', + result: '结果摘要 / Result summary', + userFacing: '面向用户的功能或行为变更 / User-facing feature or behavior change', + }; + + const summary = readSection(sections.summary); + const related = readSection(sections.related); + const affected = readSection(sections.affected); + const prType = readSection(sections.type); + const latest = readSection(sections.latest); + const ai = readSection(sections.ai); + const repoRules = readSection(sections.rules); + const compatibility = readSection(sections.compatibility); + const validation = readSection(sections.validation); + const evidence = readSection(sections.evidence); + + const errors = []; + const titlePattern = /^(feat|fix|chore|docs|test|refactor|perf|build|ci|revert)(\([^)]+\))?!?:\s+\S.+$/; + if (!titlePattern.test((pr.title || '').trim())) { + errors.push('PR 标题必须使用 Conventional Commits:`type(scope): subject`。 / The PR title must use Conventional Commits: `type(scope): subject`.'); + } + if (isBlank(summary)) errors.push('请填写“摘要 / Summary”。 / Complete “摘要 / Summary”.'); + if (isBlank(related)) errors.push('请填写“关联 Issue 或背景 / Related Issue or Context”;没有 Issue 时填写 N/A 并解释。 / Complete “关联 Issue 或背景 / Related Issue or Context”; enter N/A with an explanation when no Issue exists.'); + if (checkedLines(affected).length === 0) errors.push('请在“涉及区域 / Affected Areas”中至少勾选一项。 / Check at least one item in “涉及区域 / Affected Areas”.'); + if (checkedLines(prType).length === 0) errors.push('请在“PR 类型 / PR Type”中至少勾选一项。 / Check at least one item in “PR 类型 / PR Type”.'); + + if (!hasCheckedLine(latest, labels.latestConfirmation)) { + errors.push('请确认本 PR 基于最新 `main`。 / Confirm that this PR is based on the latest `main`.'); + } + if (isBlank(readLabeledPart(latest, labels.syncCommand))) { + errors.push('请填写同步最新 `main` 时执行的命令。 / Provide the command used to synchronize with the latest `main`.'); + } + + if (checkedLines(ai).length !== 1) { + errors.push('“AI 编码披露 / AI Coding Disclosure”必须且只能勾选一项。 / Check exactly one option in “AI 编码披露 / AI Coding Disclosure”.'); + } + const aiModel = readLabeledPart(ai, labels.aiModel, labels.aiTool); + const aiTool = readLabeledPart(ai, labels.aiTool); + if (isBlank(aiModel) || isBlank(aiTool)) { + errors.push('请填写使用的 AI 模型和编码 Agent 工具;未使用时两项均填 N/A。 / Provide the AI model and coding Agent tool; enter N/A in both fields when none were used.'); + } + + const requiredRuleCount = 7; + if (checkedLines(repoRules).length < requiredRuleCount) { + errors.push('请逐项确认“仓库规范检查 / Repository Rules”的全部七项。 / Confirm all seven items in “仓库规范检查 / Repository Rules”.'); + } + if (isBlank(compatibility)) { + errors.push('请填写“兼容性与数据安全 / Compatibility and Data Safety”;没有影响时填 N/A 并说明理由。 / Complete “兼容性与数据安全 / Compatibility and Data Safety”; enter N/A with a reason when there is no impact.'); + } + + const validationCommands = readLabeledPart(validation, labels.commands, labels.result); + const validationSummary = readLabeledPart(validation, labels.result); + if (isBlank(validationCommands) || isBlank(validationSummary)) { + errors.push('请填写“本地验证 / Local Validation”中的执行命令与结果摘要。 / Provide commands and a result summary in “本地验证 / Local Validation”.'); + } + + const userFacing = hasCheckedLine(prType, labels.userFacing); + if (userFacing && !isMaintainer && !hasEvidence(evidence)) { + errors.push('外部贡献者提交用户可见变更时,必须附本地截图或短视频证据。 / External contributors must attach local screenshot or short-video evidence for user-facing changes.'); + } + + if (errors.length === 0) { + return; + } + + await core.summary + .addHeading('PR contribution requirements') + .addRaw('Update the PR description and keep the complete template.\n') + .addList(errors) + .addRaw('After correction, edit the PR description or push a commit to rerun this check.\n') + .write(); + for (const error of errors) core.error(error); + core.setFailed(errors.join('\n')); diff --git a/dsh/.github/workflows/publish.yml b/dsh/.github/workflows/publish.yml new file mode 100644 index 000000000..cc65efe0a --- /dev/null +++ b/dsh/.github/workflows/publish.yml @@ -0,0 +1,224 @@ +name: Publish to npm + +on: + workflow_dispatch: + inputs: + revision: + description: Full commit SHA already merged as the current main HEAD + required: true + type: string + +concurrency: + group: npm-publish-${{ inputs.revision }} + cancel-in-progress: false + +permissions: + contents: read + +jobs: + prepare: + name: Freeze, verify, upgrade and pack + runs-on: ubuntu-latest + timeout-minutes: 45 + env: + RELEASE_REVISION: ${{ inputs.revision }} + outputs: + version: ${{ steps.release.outputs.version }} + tag: ${{ steps.release.outputs.tag }} + base: ${{ steps.release.outputs.base }} + artifact: ${{ steps.release.outputs.artifact }} + prerelease: ${{ steps.release.outputs.prerelease }} + notes: ${{ steps.release.outputs.notes }} + steps: + - name: Check out frozen revision + uses: actions/checkout@v6 + with: + ref: ${{ inputs.revision }} + fetch-depth: 0 + + - name: Verify frozen main revision + run: | + [[ "$RELEASE_REVISION" =~ ^[0-9a-f]{40}$ ]] + git fetch origin main + test "$(git rev-parse HEAD)" = "$RELEASE_REVISION" + test "$(git rev-parse origin/main)" = "$RELEASE_REVISION" + test -z "$(git status --porcelain)" + + - name: Set up Node.js + uses: actions/setup-node@v6 + with: + node-version: 24 + registry-url: https://registry.npmjs.org + package-manager-cache: false + + - name: Install release tooling + run: npm install --global npm@11 pnpm@10.13.1 + + - name: Resolve release metadata + id: release + run: | + version=$(node --print "require('./package.json').version") + if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + prerelease=false + elif [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+-(alpha|beta|rc)\.[0-9]+$ ]]; then + prerelease=true + else + echo "Unsupported release version: $version" >&2 + exit 1 + fi + tag="v${version}" + base_tag=$(git describe --tags --match 'v[0-9]*' --abbrev=0 "$RELEASE_REVISION^") + base_revision=$(git rev-list -n 1 "$base_tag") + [[ "$base_revision" =~ ^[0-9a-f]{40}$ ]] + notes=".github/release-notes/${tag}.md" + test -f "$notes" + artifact="npm-release-${version}-${RELEASE_REVISION:0:12}" + echo "RELEASE_TAG=$tag" >> "$GITHUB_ENV" + echo "RELEASE_PRERELEASE=$prerelease" >> "$GITHUB_ENV" + echo "RELEASE_BASE_REVISION=$base_revision" >> "$GITHUB_ENV" + echo "version=$version" >> "$GITHUB_OUTPUT" + echo "tag=$tag" >> "$GITHUB_OUTPUT" + echo "base=$base_revision" >> "$GITHUB_OUTPUT" + echo "artifact=$artifact" >> "$GITHUB_OUTPUT" + echo "prerelease=$prerelease" >> "$GITHUB_OUTPUT" + echo "notes=$notes" >> "$GITHUB_OUTPUT" + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Verify release composition, selection and channel + run: pnpm run release:check + + - name: Run complete workspace verification + run: pnpm run verify + + - name: Verify independent plugins and packed v0.5.15 upgrade E2E + env: + MNEMON_PLUGIN_VERIFY_CONCURRENCY: '4' + run: pnpm run verify:plugins --skip-build + + - name: Pack selected frozen artifacts + run: node scripts/release.mjs --pack --artifacts release-artifacts + + - name: Upload frozen release artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ steps.release.outputs.artifact }} + path: release-artifacts/ + if-no-files-found: error + retention-days: 30 + + publish: + name: Approve, publish, verify and upgrade + needs: prepare + runs-on: ubuntu-latest + timeout-minutes: 30 + environment: + name: npm-release + url: https://www.npmjs.com/package/dsh-mnemon/v/${{ needs.prepare.outputs.version }} + permissions: + contents: read + id-token: write + env: + RELEASE_REVISION: ${{ inputs.revision }} + RELEASE_BASE_REVISION: ${{ needs.prepare.outputs.base }} + RELEASE_PRERELEASE: ${{ needs.prepare.outputs.prerelease }} + RELEASE_TAG: ${{ needs.prepare.outputs.tag }} + NPM_CONFIG_REGISTRY: https://registry.npmjs.org + steps: + - name: Check out frozen revision + uses: actions/checkout@v6 + with: + ref: ${{ inputs.revision }} + fetch-depth: 0 + + - name: Set up Node.js + uses: actions/setup-node@v6 + with: + node-version: 24 + registry-url: https://registry.npmjs.org + package-manager-cache: false + + - name: Install release tooling and runtime dependencies + run: | + npm install --global npm@11 pnpm@10.13.1 + pnpm install --frozen-lockfile + + - name: Download frozen release artifacts + uses: actions/download-artifact@v5 + with: + name: ${{ needs.prepare.outputs.artifact }} + path: release-artifacts + + - name: Verify npm publication identity + run: npm whoami + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Publish selected plugin dependency layers + run: node scripts/release.mjs --publish-plugins --artifacts release-artifacts + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + RELEASE_PUBLISH_CONCURRENCY: 4 + + - name: Read back and install the Registry plugin composition + run: node scripts/release.mjs --verify-plugins --artifacts release-artifacts + + - name: Publish the Starter + run: node scripts/release.mjs --publish-starter --artifacts release-artifacts + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Read back and install the complete Registry release + run: node scripts/release.mjs --verify-release --artifacts release-artifacts + + - name: Smoke-test a real v0.5.16 Registry upgrade + run: >- + node scripts/verify-headless-profile.mjs + --upgrade-from dsh-mnemon@0.5.16 + --upgrade-registry https://registry.npmjs.org + --package dsh-mnemon@${{ needs.prepare.outputs.version }} + --registry https://registry.npmjs.org + --strategy-extensions true + + release: + name: Create GitHub release last + needs: [prepare, publish] + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: write + env: + GH_TOKEN: ${{ github.token }} + RELEASE_REVISION: ${{ inputs.revision }} + RELEASE_TAG: ${{ needs.prepare.outputs.tag }} + RELEASE_PRERELEASE: ${{ needs.prepare.outputs.prerelease }} + RELEASE_NOTES_FILE: ${{ needs.prepare.outputs.notes }} + steps: + - name: Check out frozen revision + uses: actions/checkout@v6 + with: + ref: ${{ inputs.revision }} + fetch-depth: 0 + + - name: Create immutable GitHub release + run: | + if git rev-parse "refs/tags/$RELEASE_TAG" >/dev/null 2>&1; then + test "$(git rev-list -n 1 "$RELEASE_TAG")" = "$RELEASE_REVISION" + fi + if gh release view "$RELEASE_TAG" >/dev/null 2>&1; then + test "$(gh release view "$RELEASE_TAG" --json isPrerelease --jq .isPrerelease)" = "$RELEASE_PRERELEASE" + else + release_args=( + "$RELEASE_TAG" + --target "$RELEASE_REVISION" + --title "$RELEASE_TAG: Composable Memory Plugins" + --notes-file "$RELEASE_NOTES_FILE" + ) + if test "$RELEASE_PRERELEASE" = true; then + release_args+=(--prerelease) + else + release_args+=(--latest) + fi + gh release create "${release_args[@]}" + fi diff --git a/dsh/.github/workflows/reject-docs-pr.yml b/dsh/.github/workflows/reject-docs-pr.yml new file mode 100644 index 000000000..87a87b78c --- /dev/null +++ b/dsh/.github/workflows/reject-docs-pr.yml @@ -0,0 +1,66 @@ +name: 拒绝仅文档 PR / Reject Documentation-only PRs + +on: + pull_request_target: + types: [opened, reopened, edited, ready_for_review] + +permissions: + contents: read + issues: write + pull-requests: write + +jobs: + reject: + name: 关闭外部仅文档 PR / Close external documentation-only PRs + runs-on: ubuntu-latest + if: ${{ !github.event.pull_request.draft }} + + steps: + - name: 自动关闭外部仅文档 PR / Auto-close external documentation-only PRs + uses: actions/github-script@v8 + with: + script: | + const pr = context.payload.pull_request; + const title = (pr.title || '').trim(); + if (!/^docs(?:\b|\s*\()/i.test(title)) { + return; + } + + let isMaintainer = false; + try { + const { data } = await github.rest.repos.getCollaboratorPermissionLevel({ + owner: context.repo.owner, + repo: context.repo.repo, + username: pr.user.login, + }); + isMaintainer = ['admin', 'maintain', 'write'].includes(data.permission); + } catch (error) { + core.info(`Contributor permission lookup returned no write grant: ${error.message}`); + } + if (isMaintainer) { + core.info(`PR #${pr.number} is a maintainer documentation change; keeping it open.`); + return; + } + + const comment = [ + '本仓库不直接接受外部贡献者的仅文档类 PR,此 PR 已被自动关闭。', + 'This repository does not directly accept documentation-only PRs from external contributors, so this PR was closed automatically.', + '', + `请先到 https://github.com/${context.repo.owner}/${context.repo.repo}/issues 使用 Issue Form 说明需要修正的内容。 / Please use an Issue Form at https://github.com/${context.repo.owner}/${context.repo.repo}/issues to describe the requested documentation change first.`, + '若本 PR 实际包含代码修复,请使用对应的 Conventional Commits 标题并按完整 PR 模板重新提交。', + 'If this PR contains a code fix, use the corresponding Conventional Commits title and resubmit with the complete PR template.', + ].join('\n'); + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pr.number, + body: comment, + }); + await github.rest.issues.update({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pr.number, + state: 'closed', + state_reason: 'not_planned', + }); + core.setFailed(`PR #${pr.number} 是外部仅文档贡献,已自动关闭。 / PR #${pr.number} is an external documentation-only contribution and was closed automatically.`); diff --git a/dsh/.gitignore b/dsh/.gitignore new file mode 100644 index 000000000..fb944a1b7 --- /dev/null +++ b/dsh/.gitignore @@ -0,0 +1,23 @@ +node_modules/ +coverage/ +/lib/ +/plugins/*/lib/ +*.log +.DS_Store +*.tgz + +# Local API keys and endpoints (never committed) +.env +.env.* +!.env.example + +# Local IDE settings +/.idea/ + +# Local tool state: agent worktrees and settings, research and benchmark caches +/.claude/worktrees/ +/.claude/settings.local.json +/.cache/ + +# Mnemon workspace-local data +/.mnemon/ diff --git a/dsh/CONTRIBUTING.md b/dsh/CONTRIBUTING.md new file mode 100644 index 000000000..b9428902e --- /dev/null +++ b/dsh/CONTRIBUTING.md @@ -0,0 +1,88 @@ +# Contributing + +[简体中文](./CONTRIBUTING.zh-CN.md) | **English** + +Thank you for contributing to dsh-mnemon. This document defines the Issue, Pull Request, verification, and maintenance rules. See the [Development and Verification Guide](./docs/en/development/README.md) for implementation details and test scenarios. + +## Pull Request scope + +The repository accepts these external Pull Requests: + +- **Fixes**: reproducible bugs, compatibility problems, and security hardening; +- **Enhancements and optimization**: improvements to existing capabilities, performance, stability, and user experience; +- **Maintenance**: tests, build work, refactors, and dependency compatibility. + +New capabilities, Providers, persistence formats, RPC authority, or security-boundary changes require an Issue and maintainer approval before implementation begins. + +Documentation-only PRs from external contributors are not accepted directly. Open an Issue first so maintainers can confirm the scope. Maintainer release notes, bilingual synchronization, and documentation maintenance are exempt. + +## Prerequisites + +- Node.js `^22.19.0 || >=24.0.0`; DSH 0.1.1-rc.2 uses host primitives unavailable on Node 20, and CI covers Node.js 22.19 plus 24 with pnpm 10.13.1; +- use only published `@deepseek-ai/*` NPM contracts; do not modify DSH source or point tsconfig at a DSH source checkout; +- do not commit tokens, credentials, private memory, sensitive paths, or unredacted logs in repository configuration, fixtures, screenshots, or PR descriptions; +- `lib/` is generated build output and must not be committed or edited manually. + +## Quick start + +```sh +git clone https://github.com/mnemon-dev/mnemon.git +cd mnemon/dsh +pnpm install +pnpm run verify +``` + +`verify` runs TypeScript checks, Vitest, reproducible double builds, isolated real Headless-profile activation, and published-package validation. + +## Commit convention + +Commit messages and PR titles use Conventional Commits: + +```text +type(scope): subject +``` + +Allowed types are `feat`, `fix`, `chore`, `docs`, `test`, `refactor`, `perf`, `build`, `ci`, and `revert`. Use a module or topic for the scope, for example: + +```text +fix(version): run pnpm update inside the owning profile +fix(client): honor trusted remote management grant +``` + +Code, comments, documentation, commit messages, and PR titles must not contain emoji. + +## Checklist before opening a PR + +1. **Use the latest main**: rebase onto or merge the latest `main`, resolve conflicts, and rerun verification before submitting. +2. **Run complete verification**: run `pnpm run verify` by default. If a check cannot run, document the reason, alternative evidence, and remaining risk in the PR. +3. **Add targeted regression coverage**: bugs need a test that fails before the fix and passes afterward. UI behavior must cover both authorized and rejected paths. +4. **Provide real evidence**: attach a local screenshot or short video for user-visible changes. For Host, CLI, or Headless changes, attach redacted logs or command results. +5. **Keep both languages synchronized**: long-lived documents under `docs/en/` and `docs/zh-CN/` must have matching names and responsibilities. Keep `README.md` and `README.zh-CN.md` synchronized. +6. **Explain security and data effects**: changes to persistence formats, paths, RPC authority, Provider credentials, or import/export must explain compatibility, migration or rejection, rollback, and data retention. +7. **Keep boundaries stable**: `src/host/protocol.ts` is the browser-safe source of Client and Host wire DTOs. Do not import executable Host modules into browser code. +8. **Disclose AI coding**: complete the PR template with the model and coding tool used. Contributors remain responsible for the final code, verification, and security. +9. **Do not commit generated output**: ensure the worktree contains no `lib/` output or temporary Provider-lab data. + +## Documentation and persistence constraints + +- When user-visible behavior changes, review the English and Chinese versions of `ui-guide.md`, `getting-started.md`, `configuration.md`, and `operations.md`; +- commands, configuration keys, paths, and code symbols must match across both languages; +- Runtime, Documents, and Memory Space registry format changes must not happen silently; include legacy-format handling and damaged-input tests; +- do not send user preferences into long-term Memory Spaces or use real private data in fixtures and documentation examples. + +## Issue rules + +- Search open and closed Issues before submitting; +- use the bilingual [Bug report form](.github/ISSUE_TEMPLATE/bug_report.yml) for bugs, describing the symptom, expected outcome, reproduction steps and environment. Evidence may be plain text, JSON or another code block anywhere in the report; separate evidence, smoke tests, code references and patch proposals are optional; +- use the bilingual [standard Issue form](.github/ISSUE_TEMPLATE/standard_issue.yml) for features, enhancements, documentation, and questions; +- CLI/API submissions may use the same section headings; the bot adds `bug` when the report type is “Bug 报告 / Bug report”, so reporters do not need label permissions; +- template checks request missing basic information without closing the Issue. Edit the original body to supply details; the bot updates its existing reminder. Maintainers decide whether further investigation or closure is appropriate; +- see [Issue Triage](./ISSUE_TRIAGE.md) or [Issue 分类标准](./ISSUE_TRIAGE.zh-CN.md) for labels, classification, and closure criteria; +- report security vulnerabilities privately through [SECURITY.md](./SECURITY.md), not in a public Issue. + +## Pull Request rules + +- Keep the bilingual [PR template](.github/pull_request_template.md) intact and complete its summary, context, affected areas, type, latest-code confirmation, AI disclosure, compatibility and data safety, local validation, and user-visible evidence; +- CI and PR policy checks must pass. Correct the description or implementation when a check fails; do not remove template sections; +- PRs superseded by a more complete solution, based on an obsolete design, or unable to upgrade safely will be closed with an explanation; +- reviews use the current `main`, public contracts, and released behavior as the source of truth, not only the PR description. diff --git a/dsh/CONTRIBUTING.zh-CN.md b/dsh/CONTRIBUTING.zh-CN.md new file mode 100644 index 000000000..ce2e557d4 --- /dev/null +++ b/dsh/CONTRIBUTING.zh-CN.md @@ -0,0 +1,88 @@ +# 贡献指南 + +**简体中文** | [English](./CONTRIBUTING.md) + +欢迎为 dsh-mnemon 贡献代码。本文件定义 Issue、Pull Request、验证和维护规则;实现细节与测试场景见[开发和验证指南](./docs/zh-CN/development/README.md)。 + +## PR 范围 + +本仓库接受以下外部 PR: + +- **修复**:可复现的 Bug、兼容性问题和安全加固; +- **增强与优化**:现有能力的改进,以及性能、稳定性和体验优化; +- **维护**:测试、构建、重构和依赖兼容性工作。 + +全新能力、新 Provider、持久化格式、RPC 权限或安全边界变更必须先提交 Issue,获得维护者确认后再开始实现。 + +外部贡献者的仅文档类 PR 不直接接受;请先提交 Issue 讨论,由维护者确认范围。仓库维护者的发布说明、双语同步和文档维护不受此限制。 + +## 开发前置 + +- Node.js `^22.19.0 || >=24.0.0`;DSH 0.1.1-rc.2 使用 Node 20 缺失的宿主原语,CI 使用 pnpm 10.13.1 覆盖 Node.js 22.19 与 24; +- 仅使用正式发布的 `@deepseek-ai/*` NPM 契约,禁止修改 DSH 源码或让 tsconfig 指向 DSH 源码 checkout; +- 不要在仓库配置、测试夹具、日志、截图或 PR 描述中提交 token、凭据、私有记忆、敏感路径或未脱敏日志; +- `lib/` 是构建生成物,不进入版本库,也不得手工编辑。 + +## 快速开始 + +```sh +git clone https://github.com/mnemon-dev/mnemon.git +cd mnemon/dsh +pnpm install +pnpm run verify +``` + +`verify` 包含 TypeScript 检查、Vitest、可复现双构建、隔离的真实 Headless profile 激活和发布包验证。 + +## 提交规范 + +提交信息和 PR 标题使用 Conventional Commits: + +```text +type(scope): subject +``` + +允许的 type 包括 `feat`、`fix`、`chore`、`docs`、`test`、`refactor`、`perf`、`build`、`ci` 和 `revert`。scope 使用模块或主题,例如: + +```text +fix(version): run pnpm update inside the owning profile +fix(client): honor trusted remote management grant +``` + +代码、注释、文档、提交信息和 PR 标题禁止 emoji。 + +## 提 PR 前检查清单 + +1. **基于最新 main**:提交前 rebase 或合并最新 `main`,解决冲突并重新验证。 +2. **完整验证**:默认运行 `pnpm run verify`;若无法运行某一项,在 PR 中写明原因、替代验证和剩余风险。 +3. **定向回归**:为缺陷补充失败前和通过后的回归测试;UI 行为同时验证授权和拒绝路径。 +4. **真实证据**:用户可见变更附本地截图或短视频;Host、CLI、Headless 变更附脱敏日志或操作结果。 +5. **双语同步**:长期文档在 `docs/en/` 和 `docs/zh-CN/` 保持同名、同职责;根 README 同步维护 `README.md` 和 `README.zh-CN.md`。 +6. **安全与数据**:持久化格式、路径、RPC authority、Provider 凭据或导入导出变更必须说明兼容、迁移或拒绝、回滚和数据保留行为。 +7. **边界稳定**:`src/host/protocol.ts` 是可在浏览器使用的 Client 与 Host wire DTO 来源;不得在浏览器半区引入可执行的 Host 模块。 +8. **AI 披露**:按 PR 模板如实填写使用的模型和编码工具;贡献者仍对最终代码、验证和安全负责。 +9. **不提交生成物**:确认工作区不包含 `lib/` 或临时 Provider lab 数据。 + +## 文档和持久化约束 + +- 修改用户可见行为时检查 `ui-guide.md`、`getting-started.md`、`configuration.md` 和 `operations.md` 的中英文版本; +- 命令、配置键、路径和代码符号在两种语言中必须一致; +- Runtime、Documents 和 Memory Space registry 的格式变更不得静默发生,必须提供旧格式处理和损坏输入测试; +- 不得把用户偏好发送到长期 Memory Space,也不得在测试或文档示例中使用真实私有数据。 + +## Issue 规则 + +- 提交前搜索 open 和 closed Issue,确认没有重复; +- Bug 使用双语[Bug 报告表单](.github/ISSUE_TEMPLATE/bug_report.yml),描述现象、预期结果、复现步骤和环境。证据可用普通文本、JSON 或其他代码块,放在正文任意位置;单独的证据、冒烟测试、代码引用和补丁草案均为选填; +- 功能、增强、文档和问题使用双语[标准 Issue 表单](.github/ISSUE_TEMPLATE/standard_issue.yml); +- CLI/API 提交可使用相同段落标题;类型为“Bug 报告 / Bug report”时机器人会补齐 `bug` 标签,无需提交者拥有标签权限; +- 模板检查仅提示补充缺失的基础信息,不关闭 Issue。直接编辑原正文补充信息,机器人会更新已有提示;进一步排查或关闭由维护者决定; +- 标签体系、分类和关闭标准见[Issue 分类标准](./ISSUE_TRIAGE.zh-CN.md)或英文版 [Issue Triage](./ISSUE_TRIAGE.md); +- 安全漏洞必须按 [SECURITY.md](./SECURITY.md) 私下报告,不要创建公开 Issue。 + +## PR 规则 + +- 保持双语 [PR 模板](.github/pull_request_template.md)完整,填写摘要、背景、涉及区域、类型、最新代码确认、AI 披露、兼容和数据安全、本地验证与用户可见证据; +- CI 和 PR 规则检查必须通过;失败时修正 PR 描述或实现,不要删除模板段落; +- 已被更完整方案取代、基于过时设计或无法安全升级的 PR 会被关闭并说明原因; +- Review 结论以当前 `main`、公开契约和已发布行为为准,而不是仅以 PR 描述为准。 diff --git a/dsh/ISSUE_TRIAGE.md b/dsh/ISSUE_TRIAGE.md new file mode 100644 index 000000000..99a8fb604 --- /dev/null +++ b/dsh/ISSUE_TRIAGE.md @@ -0,0 +1,77 @@ +# Issue Triage + +[简体中文](./ISSUE_TRIAGE.zh-CN.md) | **English** + +This document defines the dsh-mnemon Issue label system, classification, information requests, and closure process. The goal is to keep every open Issue searchable, verifiable, assignable, and traceable. + +## Labels + +| Label | Meaning | When to use it | +| --- | --- | --- | +| `bug` | A report of unexpected behavior, an error, regression, or compatibility problem | The reporter selects Bug report; maintainers verify the symptom and reproduction during triage | +| `enhancement` | A new capability or improvement to an existing capability | The request has a clear use case, constraints, and acceptance result | +| `documentation` | Missing, obsolete, or inconsistent README, docs, or comments | No runtime behavior changes | +| `question` | A usage or design question | The Issue asks how something works or why it behaves that way | +| `good first issue` | Suitable for a new contributor | Small scope, clear acceptance criteria, and no deep security or storage context required | +| `help wanted` | Approved for community implementation | Maintainers accept the approach and have no internal schedule | +| `duplicate` | Duplicates an existing Issue | The symptom, root cause, or objective substantially overlaps another Issue | +| `invalid` | Not a repository problem, a public security report, or persistently missing information | The Issue cannot proceed through the template | +| `wontfix` | Deliberately not planned | Out of scope, conflicts with a security boundary, or has insufficient benefit | + +Labels use GitHub's default names. When adding a long-lived label, update the Issue Forms, automation, and this document. Do not create one-off temporary labels. + +## Classification process + +Triage new Issues in this order: + +1. **Security check**: if the Issue contains a vulnerability, token, credential, private memory, or sensitive log, remove the public material and direct the reporter to `SECURITY.md`. +2. **Information check**: look for the symptom, expected outcome, reproduction steps and environment, plus classification and duplicate-search confirmation. Ask for specific missing details without closing the Issue. Evidence may be plain text, JSON or other code blocks anywhere in the report; separate evidence, smoke tests, code references and patch proposals are optional. +3. **Duplicate check**: search open and closed Issues and merged PRs. Label duplicates with `duplicate`, link the original Issue in a comment, and close the duplicate. +4. **Assign a type**: apply `bug`, `enhancement`, `documentation`, or `question` based on the objective. The information check adds `bug` for the Bug report type, including CLI/API submissions; this is classification, not confirmation that a defect has been verified. +5. **Verify current state**: reproduce against current `main` and the latest release. If another PR or release already solved the problem, cite its commit, PR, or version and close the Issue. +6. **Confirm scope**: route problems that require DSH core or an upstream Provider change to the upstream project. Keep only a concrete compatibility-layer or workaround task here. +7. **Open for contribution**: add `help wanted` when the scope is clear and external implementation is accepted; add `good first issue` when it is also suitable for a new contributor. + +## Closure criteria + +Close an Issue when any of these conditions apply, and always leave a traceable explanation: + +- **Implemented**: the change is on `main`; cite the PR or commit and add the minimum fixed release after publication; +- **Superseded**: a more complete or safer implementation exists; cite the superseding PR; +- **Duplicate**: link the original Issue; +- **Answered**: provide the conclusion and an authoritative documentation link; +- **Obsolete**: the dependency, interface, or design has changed; +- **Out of scope**: an upstream DSH, Mnemon, or Provider change is required; identify the upstream destination; +- **Information missing long-term**: after a maintainer requests specific details and allows time to respond, the report still cannot be investigated; explain the unresolved gap and apply `invalid`. + +Use GitHub's `completed` or `not_planned` closure reason. Do not close without an explanation. Reporters may request reopening after adding new evidence. + +Formatting, the location of a log, a missing code reference or the absence of a patch alone are not closure reasons. A reopened Issue stays available for maintainer review; the template check never closes it. + +## Pull Request linkage + +- Fix PRs should use `Fixes #` or `Closes #` to link an accepted Issue; +- reviews must compare the PR with current `main` and determine whether history or a more complete solution has superseded it; +- proving that an old version had a problem does not by itself establish that a current PR should merge; +- an Issue may close when the fix reaches `main`; add the minimum fixed version after release. + +## Maintainer command reference + +```sh +gh issue edit -R mnemon-dev/mnemon --add-label "bug,good first issue" +gh issue comment -R mnemon-dev/mnemon --body "Explanation" +gh api -X PATCH repos/omdsh-dev/dsh-mnemon/issues/ \ + -f state=closed -f state_reason=completed + +gh issue list -R mnemon-dev/mnemon --state open \ + --json number,title,labels --jq '.[] | select(.labels|length==0)' +``` + +## Automation + +- `.github/workflows/issue-template-enforcer.yml` reads current state on opening, reopening and editing, adds `bug` for Bug reports, and maintains one bilingual reminder for missing basic information. The reminder updates after corrections; this check never changes Issue state; +- `.github/workflows/issue-dedup.yml` posts a bilingual explanation, links the original Issue, labels a highly similar open Issue as a possible duplicate, and closes it; +- `.github/workflows/pr-contribution-rules.yml` validates PR titles, the bilingual template, AI disclosure, local verification, compatibility information, and user-visible evidence; +- `.github/workflows/reject-docs-pr.yml` posts a bilingual explanation and closes documentation-only PRs from external contributors; repository collaborators with `write`, `maintain`, or `admin` permission are exempt. + +Automation performs initial screening only. Reporters may explain differences and request reopening; maintainers make the final decision. diff --git a/dsh/ISSUE_TRIAGE.zh-CN.md b/dsh/ISSUE_TRIAGE.zh-CN.md new file mode 100644 index 000000000..1db4e8087 --- /dev/null +++ b/dsh/ISSUE_TRIAGE.zh-CN.md @@ -0,0 +1,77 @@ +# Issue 分类标准与处理流程 + +**简体中文** | [English](./ISSUE_TRIAGE.md) + +本文件定义 dsh-mnemon 的 Issue 标签体系、分类、补充信息和关闭流程。目标是让每个 Open Issue 可检索、可验证、可认领、可追溯。 + +## 标签体系 + +| 标签 | 含义 | 何时使用 | +| --- | --- | --- | +| `bug` | 报告功能不符合预期、报错、回归或兼容性问题 | 提交者选择 Bug 报告;维护者在分类过程中核实具体表现和复现 | +| `enhancement` | 新能力或现有能力改进 | 有清楚场景、约束和验收结果 | +| `documentation` | README、docs 或注释缺失、过时、不一致 | 不改变运行时行为 | +| `question` | 使用或设计疑问 | 需要回答“怎么用”或“为什么” | +| `good first issue` | 适合新贡献者 | 范围小、验收明确、不依赖深层安全或存储上下文 | +| `help wanted` | 已确认接受社区实现 | 维护者认可方案且暂无内部排期 | +| `duplicate` | 与已有 Issue 重复 | 现象、根因或目标与现有 Issue 高度重叠 | +| `invalid` | 非本仓库问题、公开安全报告或信息长期不足 | 无法按模板继续分类 | +| `wontfix` | 经讨论不做 | 超出职责、破坏安全边界或收益不足 | + +标签使用 GitHub 默认命名。新增长期标签时应同步更新 Issue Forms、自动化和本文档,不创建一次性临时标签。 + +## 分类流程 + +新 Issue 按以下顺序处理: + +1. **安全检查**:若包含漏洞、token、凭据、私有记忆或敏感日志,立即移除公开内容并引导至 `SECURITY.md`。 +2. **信息检查**:查看现象、预期结果、复现步骤、环境、分类和查重确认。缺少具体信息时提示补充,不关闭 Issue。证据可用普通文本、JSON 或其他代码块,放在正文任意位置;单独的证据、冒烟测试、代码引用和补丁草案均为选填。 +3. **查重**:搜索 open/closed Issue 和已合并 PR;重复项标记 `duplicate`,评论原 Issue 后关闭。 +4. **定类型**:根据目标标记 `bug`、`enhancement`、`documentation` 或 `question`。信息检查会为 Bug 报告补齐 `bug` 标签,包括 CLI/API 提交;该标签表示分类,不代表已验证缺陷成立。 +5. **验证当前状态**:以当前 `main` 和最新发布版本复现;已经由其他 PR 或版本解决的 Issue 直接注明 commit、PR 或版本并关闭。 +6. **确认范围**:需要修改 DSH 核心或上游 Provider 的问题转交上游;本仓库只保留兼容层或绕过方案的明确任务。 +7. **开放认领**:范围清楚且接受外部实现时添加 `help wanted`;适合入门时再添加 `good first issue`。 + +## 关闭标准 + +满足任一条件即可关闭,关闭时必须留下可追溯说明: + +- **已实现**:改动已进入 `main`,注明 PR 或 commit;发布后再注明最低修复版本; +- **被取代**:已有更完整或更安全的实现,注明 superseding PR; +- **重复**:链接原 Issue; +- **已回答**:给出结论和权威文档链接; +- **过时**:依赖、接口或设计已经变化; +- **超范围**:需要上游 DSH、Mnemon 或 Provider 修改,说明转交位置; +- **长期缺信息**:维护者提出具体补充要求并给予回复时间后,报告仍无法排查;说明尚缺的信息并标记 `invalid`。 + +关闭理由使用 GitHub 的 `completed` 或 `not_planned`,不要无说明关闭。作者补充新证据后可以请求重开。 + +仅有排版、日志位置、缺少代码引用或没有补丁方案等问题,不构成关闭理由。重开的 Issue 保留给维护者继续处理,模板检查不会将其关闭。 + +## PR 关联规则 + +- 修复 PR 应使用 `Fixes #` 或 `Closes #` 关联已确认 Issue; +- PR Review 必须对照当前 `main`,确认没有被历史提交或更完整方案取代; +- 仅证明旧版本存在问题,不足以证明当前 PR 应合并; +- Issue 在修复进入 `main` 后即可关闭,最低发布版本在 release 后补充。 + +## 维护者操作速查 + +```sh +gh issue edit -R mnemon-dev/mnemon --add-label "bug,good first issue" +gh issue comment -R mnemon-dev/mnemon --body "说明" +gh api -X PATCH repos/omdsh-dev/dsh-mnemon/issues/ \ + -f state=closed -f state_reason=completed + +gh issue list -R mnemon-dev/mnemon --state open \ + --json number,title,labels --jq '.[] | select(.labels|length==0)' +``` + +## 自动化 + +- `.github/workflows/issue-template-enforcer.yml`:在创建、重开和编辑时读取最新状态,为 Bug 报告补齐 `bug` 标签,并维护一条缺少基础信息的双语提示。正文补充后更新提示;该检查不修改 Issue 状态; +- `.github/workflows/issue-dedup.yml`:标题与已有 open Issue 高度相似时,发布双语说明、附原 Issue、标记疑似重复并关闭; +- `.github/workflows/pr-contribution-rules.yml`:校验 PR 标题、双语模板、AI 披露、本地验证、兼容说明和用户可见证据; +- `.github/workflows/reject-docs-pr.yml`:发布双语说明并自动关闭外部贡献者的仅文档 PR;具有 `write`、`maintain` 或 `admin` 权限的仓库协作者不受该规则限制。 + +自动化只执行初筛。作者可以通过评论说明差异并请求重开,最终决定由维护者作出。 diff --git a/dsh/LICENSE b/dsh/LICENSE new file mode 100644 index 000000000..e32e112dc --- /dev/null +++ b/dsh/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 dsh-external contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dsh/README.md b/dsh/README.md new file mode 100644 index 000000000..1581bedc3 --- /dev/null +++ b/dsh/README.md @@ -0,0 +1,147 @@ +

dsh-mnemon

+ +

English · 简体中文

+ +

+ npm version + npm downloads + GitHub release + GitHub stars + License: MIT + dshfind + dshfind downloads +

+ +

Composable, view-based memory for DeepSeek Harness.

+

Pluggable sources and strategies, with layered memory out of the box.

+ +

+ + A question answered from working memory, Project Documents and Memory Spaces; the expanded turn memory bar opens the document it read in Project Documents + +

+ +

+ Install and start · + Watch the demo · + Documentation · + Build a plugin +

+ +## Why dsh-mnemon + +An Agent that starts every session from scratch keeps asking what it was already told. Putting everything into one memory store is no better: it either floods each turn or misses what matters. dsh-mnemon gives DeepSeek Harness memory that is layered, visible and composable. + +- **The right memory for each turn.** Preferences and working facts stay in context. Project documents and long-term evidence are searched only when a question needs them. +- **You can see what a turn used.** Under each reply, the turn memory bar lists the documents and memories the turn read and wrote, and one click opens each in the Memory System. The Memory System shows everything that is stored and lets you edit it. +- **Save to memory in one click.** When a conversation turns up a fact worth keeping, the brain mark under the reply hands it to a task Agent, which removes duplicates, distils it and writes it to the right Memory Space; the receipt says where it went. +- **Compose it on the Plugins page.** Choose a main strategy and optional enhancements, and switch them without moving any data. Every component has its own page for its settings. +- **Keep data where you want it.** Local by default with Mnemon Native, or one of eight third-party Providers. Storage can be global, per workspace or centralized, with ZIP backup. +- **Extend it.** Sources and Strategies are ordinary DSH plugins built on public SDKs. An installed component gets the same pages and switches as the shipped ones. + +## See it in action + +These recordings come from a real WebUI with the live DeepSeek model answering, on a seeded, fictional project, Lumen. Waits for the model play faster, marked in the lower right corner. + +**Save a new fact to memory.** A reply reports a new measurement. Select the brain mark under it, cut the candidate down to the sentence worth keeping and send it to the task Agent; the receipt names the Memory Space it went to and opens it in one click. + +

+ Save to memory: edit the candidate, send it to the task Agent, then view the new memory from the Saved receipt +

+ +**See all memory in one place.** Status, runtime memory, Project Documents and Memory Spaces live in the Memory System. The graph links memories through the entities they mention, and Ask Agent answers with the memories it cites, by content. + +

+ The Memory System: status, runtime memory, a document, the Memory Spaces graph, then an Agent answer with citations +

+ +**Compose memory on the Plugins page.** One main strategy, its memory sources and optional enhancements, each switch applying at once. Every component has its own page, and storage, backup and interface settings live here too. + +

+ The Plugins page: memory composition, the main strategy menu, the Layered strategy's and Memory Spaces' pages, then storage and interface +

+ +Every step is explained in the [UI guide](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/guides/ui-guide.md); all screens, recordings and the capture environment are in the [v0.5.19 gallery](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/assets/webui-v0.5.19/README.md). + +## Three kinds of memory + +| Memory | Keep here | How it reaches the Agent | +|---|---|---| +| **Runtime memory** | Preferences, working agreements, facts needed on the next turn | Injected every turn as compact USER.md and MEMORY.md | +| **Project Documents** | Designs, investigations, procedures and handoffs | Searched first, then read when relevant | +| **Memory Spaces** | Durable facts, decisions, entities and their relationships | Recalled on demand from the enabled Providers | + +The default **Layered strategy** keeps runtime memory resident and reads the other two on demand. The **General strategy** offers every available source within one budget and lets the model decide how to use each. A **memory space** is one named, Provider-backed scope for long-term evidence; the Chinese product term is 记忆空间. + +## Quick start + +New to DSH? [Install and start](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/guides/installation.md) walks from an empty machine to your first memory, with a screenshot for every step. The short version, with [Node.js](https://nodejs.org/) 22.19 or later: + +```sh +npm install --global pnpm +npx @deepseek-ai/dsh web +``` + +

+ On the Plugins page: Add plugin, type dsh-mnemon, Install, Enable now; the Memory System appears in the sidebar +

+ +1. In the page that opens, click **Plugins → Add plugin**, type `dsh-mnemon`, click **Install**, then **Enable now**. +2. Open **Memory System** in the sidebar. **Status** shows each memory component and Provider. +3. Tell a conversation something to remember. The turn memory bar under the reply lists what the turn read and wrote, and the brain mark saves a reply to memory. +4. For Memory Spaces, install the Mnemon CLI for Mnemon Native with `npm install --global @mnemon-dev/mnemon`, or enable another Provider on the Memory Spaces page. +5. Choose the main strategy and enhancements under **Plugins → dsh-mnemon**. + +dsh-mnemon supports DSH `0.2.0-rc.2` (npm `latest` and `next`) and `0.1.7-rc.2`. The same package serves the desktop app's Plugins page, `dsh plugin --profile web add dsh-mnemon` on the command line, and Headless with `dsh plugin --profile headless add dsh-mnemon`; keep `v0.5.16` on older hosts. [Getting started](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/guides/getting-started.md) continues from here, and [compatibility and upgrades](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/reference/compatibility.md) covers existing installations. + +## How it works + +[![Source facts flow through a Strategy and Core validation into one View for the DSH Host](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/diagrams/en/composable-memory.png)](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/development/architecture.md) + +- **Sources** own memory and its operations: Runtime memory, Project Documents and Memory Spaces, whose Providers are its children. +- A **Strategy** decides how the available Sources take part in a turn: what stays resident, what can be searched, and which tools and budgets apply. Enhancements add to it through standard slots. +- **Core** validates the result into one immutable **View** for the turn. The DSH Host pins it to that turn and controls tool access. + +The same public contracts serve the shipped plugins and external repositories. [Architecture](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/development/architecture.md) explains ownership, the turn lifecycle and the UI regions components contribute to. + +## Official plugins + +The Starter pins a tested combination of independently versioned packages. One main strategy runs at a time; the enhancements are off until you turn them on. + +| Package | Role | Default | +|---|---|---| +| [dsh-mnemon-source-runtime](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-source-runtime/README.md) | Runtime memory: USER.md, MEMORY.md, revisions and local hot storage | On | +| [dsh-mnemon-source-documents](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-source-documents/README.md) | Project Documents: Markdown, search, revisions and archiving | On | +| [dsh-mnemon-source-memory-spaces](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-source-memory-spaces/README.md) | Memory Spaces: durable evidence and their Providers | On | +| [dsh-mnemon-strategy-default-three-tier](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-strategy-default-three-tier/README.md) | Layered strategy: resident runtime memory, the rest on demand | Selected | +| [dsh-mnemon-strategy-general](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-strategy-general/README.md) | General strategy: every available source in one budget | Off | +| [dsh-mnemon-strategy-auto-capture](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-strategy-auto-capture/README.md) | Active capture: in-turn guidance to keep useful facts | Off | +| [dsh-mnemon-strategy-light-context](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-strategy-light-context/README.md) | Light context: a shared ceiling on resident context | Off | +| [dsh-mnemon-strategy-scoped](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-strategy-scoped/README.md) | Scoped composition: ordered sources and a writable subset | Off | + +Memory Spaces Providers: [Mnemon Native](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-provider-mnemon-native/README.md) (default, local) · [OpenViking](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-provider-openviking/README.md) · [Honcho](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-provider-honcho/README.md) · [Mem0](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-provider-mem0/README.md) · [Hindsight](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-provider-hindsight/README.md) · [Holographic](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-provider-holographic/README.md) · [RetainDB](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-provider-retaindb/README.md) · [ByteRover](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-provider-byterover/README.md) · [Supermemory](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-provider-supermemory/README.md). Third-party Providers stay off until configured; graph, deletion and enumeration capabilities differ by backend. See [Providers](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/guides/memory-providers.md). + +## Build your own + +Define a Source or Strategy with `dsh-mnemon/extension-sdk`, add an enhancement through the standard slots, or write a Memory Spaces driver with `dsh-mnemon-source-memory-spaces/provider-sdk`. A component can also add its own settings and Status card to the pages dsh-mnemon shows. Your repository owns its manifest, dependencies, tests and build; DSH installs and mounts it, and choosing it as the main strategy is a separate decision. + +Start with the [plugin author guide](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/development/extensions.md). Discuss new capabilities and Providers in an Issue before a pull request; see [CONTRIBUTING](https://github.com/omdsh-dev/dsh-mnemon/blob/8466e3560a3b9de4e9f4b7302cbf005c84e8e69f/CONTRIBUTING.md). + +## Data and trust + +- Runtime memory and Project Documents are local files; Mnemon Native is local. Third-party Providers use their own services and scopes. +- Turning a component off does not erase its memory, and changing the storage location does not move data. Use ZIP backup to carry it. +- Saved Provider credentials stay on the Host and are never exported. Backups still contain private memory; protect them. +- Sources and Strategies are trusted in-process JavaScript, not sandboxed code. Remembered history never outranks current instructions. + +[Backup and recovery](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/guides/operations.md) · [Security policy](https://github.com/omdsh-dev/dsh-mnemon/blob/8466e3560a3b9de4e9f4b7302cbf005c84e8e69f/SECURITY.md) · [Release history](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/releases/README.md) · [Roadmap](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/roadmap.md) + +## Develop and verify + +```sh +pnpm install --frozen-lockfile +pnpm verify +pnpm verify:plugins +``` + +Use Node.js `^22.19.0 || >=24.0.0` and pnpm. `node scripts/serve-e2e.mjs` starts a disposable real WebUI; add `--docs-demo` for the seeded project behind these screenshots, and `--live-model` to have the DeepSeek API answer, with the key read from `DEEPSEEK_API_KEY`. Mechanical tests are not claims about model accuracy or live cloud Providers. [Development and verification](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/development/README.md). diff --git a/dsh/README.zh-CN.md b/dsh/README.zh-CN.md new file mode 100644 index 000000000..e2b88c9e0 --- /dev/null +++ b/dsh/README.zh-CN.md @@ -0,0 +1,148 @@ +

dsh-mnemon

+ +

English · 简体中文

+ +

+ npm 版本 + npm 累计下载量 + GitHub 发布版本 + GitHub 收藏数 + MIT 许可证 + dshfind + dshfind 下载量 +

+ + +

面向 DeepSeek Harness 的可组合视图记忆。

+

记忆来源与策略可插拔,开箱即用提供分层记忆。

+ +

+ + 提问后,回答同时用到工作记忆、项目档案与记忆空间;展开回合记忆栏,点击读到的档案,直接在项目档案中打开它 + +

+ +

+ 安装与启动 · + 观看演示 · + 文档中心 · + 制作插件 +

+ +## 为什么需要 dsh-mnemon + +每次会话都从零开始的 Agent,会反复询问你早已说过的事情;把所有内容塞进同一个记忆库也不行,要么每轮都被淹没,要么漏掉真正要紧的信息。dsh-mnemon 为 DeepSeek Harness 提供分层、可见、可组合的记忆。 + +- **每一轮都拿到合适的记忆。** 偏好和工作中的事实常驻上下文;项目档案与长期证据只在问题需要时才检索。 +- **看得见这一轮用了什么。** 每条回复下方的回合记忆栏列出这一轮读到和写入的档案与记忆,点一下就在记忆系统中打开它;记忆系统展示全部已保存的内容,并可直接编辑。 +- **一键存入记忆。** 对话里出现值得保留的事实,点击回复下的脑形图标,由任务 Agent 去重、提炼并写入合适的记忆空间,回执写明存到了哪里。 +- **在插件页组合。** 选择一个主策略和若干可选增强,切换时无需迁移任何数据;每个组件都有自己的设置页。 +- **数据放在你想放的地方。** 默认由 Mnemon Native 在本地保存,也可以接入八种第三方 Provider;存储范围可选全局、按工作区或集中存储,并支持 ZIP 备份。 +- **可以扩展。** Source 与 Strategy 都是基于公开 SDK 的普通 DSH 插件;安装的组件与随附组件拥有同样的页面和开关。 + +## 看看实际效果 + +以下录屏来自真实 WebUI,由真实的 DeepSeek 模型作答,项目是预置的虚构项目 Lumen;等待模型的片段加速播放,画面右下角有标记。 + +**把新事实存进记忆。** 回复中出现了新的测量结果:点击回复下的脑形图标,把候选内容改成要记住的那一句,交给任务 Agent;回执写明存进了哪个记忆空间,一键即可查看。 + +

+ 存入记忆:编辑候选内容,交给任务 Agent,收到已存入的回执后在记忆空间中查看新记忆 +

+ +**在一处查看全部记忆。** 状态、运行时记忆、项目档案与记忆空间集中在记忆系统中;图谱按实体把记忆连接起来,Agent 查询给出按内容引用记忆的回答。 + +

+ 记忆系统:依次查看状态、运行时记忆、项目档案、记忆空间图谱,再用 Agent 查询得到带引用的回答 +

+ +**在插件页组合记忆。** 一个主策略、它的记忆来源与可选增强,每个开关即时生效;每个组件都有自己的页面,存储、备份与界面设置也在这里。 + +

+ 插件页:记忆组合、主策略菜单、分层策略与记忆空间的组件页,以及存储与界面 +

+ +更多画面与每一步的说明见[界面指南](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/zh-CN/guides/ui-guide.md),全部截图、录屏与采集环境见 [v0.5.19 图集](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/assets/webui-v0.5.19/README.md)。 + +## 三类记忆 + +| 记忆 | 适合保存 | 如何进入 Agent 上下文 | +|---|---|---| +| **运行时记忆** | 偏好、协作约定、下一轮就需要的事实 | 每轮以紧凑的 USER.md 与 MEMORY.md 注入 | +| **项目档案** | 设计、调查、流程与交接材料 | 先检索,相关时再阅读全文 | +| **记忆空间** | 长期事实、决策、实体及其关系 | 按需从已启用的 Provider 召回 | + +默认的**分层策略**让运行时记忆常驻,另外两类按需读取;**通用策略**在同一份预算内提供全部可用来源,由模型决定如何使用。**记忆空间**是由 Provider 承载、可以独立命名和激活的长期证据范围,英文界面称 memory space。 + +## 快速开始 + +第一次使用 DSH?[安装与启动](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/zh-CN/guides/installation.md)从一台空白电脑一直带你到第一条记忆,每一步都有截图。简要步骤如下,需要 [Node.js](https://nodejs.org/) 22.19 或更高: + +```sh +npm install --global pnpm +npx @deepseek-ai/dsh web +``` + +

+ 在插件页点击添加插件,输入 dsh-mnemon,安装后立即启用,侧栏出现记忆系统 +

+ +1. 在打开的页面中点击**插件 → 添加插件**,输入 `dsh-mnemon`,点击**安装**,再点击**立即启用**。 +2. 在侧栏打开**记忆系统**,“状态”页列出每个记忆组件和 Provider。 +3. 在对话中说一句需要记住的话。回复下方的回合记忆栏列出这一轮读到和写入的记忆,脑形图标可以把回复存入记忆。 +4. 使用记忆空间时,可用 `npm install --global @mnemon-dev/mnemon` 安装 Mnemon Native 所需的 CLI,或在记忆空间页面启用其他 Provider。 +5. 在**插件 → 可组合记忆**中选择主策略与增强。 + +dsh-mnemon 支持 DSH `0.2.0-rc.2`(npm `latest` 与 `next`)与 `0.1.7-rc.2`。同一个包也用于桌面版的插件页、命令行 `dsh plugin --profile web add dsh-mnemon`,以及 Headless:`dsh plugin --profile headless add dsh-mnemon`;更早的宿主请继续使用 `v0.5.16`。接下来可以看[快速开始](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/zh-CN/guides/getting-started.md),已有安装的升级见[兼容性与升级](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/zh-CN/reference/compatibility.md)。 + +## 工作原理 + +[![来源事实经策略组合与核心校验,形成交给 DSH 宿主的唯一上下文视图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/diagrams/zh-CN/composable-memory.png)](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/zh-CN/development/architecture.md) + +- **Source(记忆来源)** 拥有记忆及其操作:运行时记忆、项目档案和记忆空间,Provider 是记忆空间的子模块。 +- **Strategy(策略)** 决定可用的 Source 如何参与一轮对话:哪些常驻、哪些可以检索、使用哪些工具和预算。增强通过标准插槽为它补充能力。 +- **Core(核心)** 把结果校验为本轮唯一的不可变 **View(上下文视图)**,DSH 宿主将它固定到这一轮并控制工具访问。 + +随附插件与外部仓库使用同一套公开契约。[架构](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/zh-CN/development/architecture.md)说明了归属关系、回合生命周期,以及组件可以贡献内容的界面区域。 + +## 官方插件 + +Starter 固定一组经过测试、各自独立版本的包。同一时间只运行一个主策略;增强在你打开之前保持关闭。 + +| 包 | 作用 | 默认 | +|---|---|---| +| [dsh-mnemon-source-runtime](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-source-runtime/README.md) | 运行时记忆:USER.md、MEMORY.md、修订与本地热存储 | 开启 | +| [dsh-mnemon-source-documents](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-source-documents/README.md) | 项目档案:Markdown、检索、修订与归档 | 开启 | +| [dsh-mnemon-source-memory-spaces](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-source-memory-spaces/README.md) | 记忆空间:长期证据及其 Provider | 开启 | +| [dsh-mnemon-strategy-default-three-tier](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-strategy-default-three-tier/README.md) | 分层策略:运行时记忆常驻,其余按需读取 | 选中 | +| [dsh-mnemon-strategy-general](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-strategy-general/README.md) | 通用策略:全部可用来源共享一份预算 | 关闭 | +| [dsh-mnemon-strategy-auto-capture](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-strategy-auto-capture/README.md) | 主动记录:在回合中提示保留有用的事实 | 关闭 | +| [dsh-mnemon-strategy-light-context](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-strategy-light-context/README.md) | 轻量上下文:为常驻内容设置共同上限 | 关闭 | +| [dsh-mnemon-strategy-scoped](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-strategy-scoped/README.md) | 范围组合:按顺序选择来源,并限定可写子集 | 关闭 | + +记忆空间 Provider:[Mnemon Native](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-provider-mnemon-native/README.md)(默认,本地)· [OpenViking](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-provider-openviking/README.md) · [Honcho](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-provider-honcho/README.md) · [Mem0](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-provider-mem0/README.md) · [Hindsight](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-provider-hindsight/README.md) · [Holographic](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-provider-holographic/README.md) · [RetainDB](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-provider-retaindb/README.md) · [ByteRover](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-provider-byterover/README.md) · [Supermemory](https://github.com/mnemon-dev/mnemon/blob/master/dsh/plugins/dsh-mnemon-provider-supermemory/README.md)。第三方 Provider 在配置前保持关闭;图谱、删除与枚举能力因后端而异。详见 [Provider 指南](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/zh-CN/guides/memory-providers.md)。 + +## 自己动手扩展 + +用 `dsh-mnemon/extension-sdk` 定义 Source 或 Strategy,通过标准插槽编写增强,或用 `dsh-mnemon-source-memory-spaces/provider-sdk` 编写记忆空间驱动。组件还可以把自己的设置和状态卡片加入 dsh-mnemon 的页面。你的仓库自己负责清单、依赖、测试与构建;DSH 负责安装和挂载,是否把它选为主策略是另一个独立决定。 + +从[插件开发指南](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/zh-CN/development/extensions.md)开始。新能力与新 Provider 请先在 Issue 中讨论,再提交 PR,详见 [CONTRIBUTING](https://github.com/omdsh-dev/dsh-mnemon/blob/8466e3560a3b9de4e9f4b7302cbf005c84e8e69f/CONTRIBUTING.zh-CN.md)。 + +## 数据与信任 + +- 运行时记忆与项目档案是本地文件,Mnemon Native 也在本地;第三方 Provider 使用各自的服务与作用域。 +- 关闭组件不会删除其中的记忆,更换存储位置也不会搬移数据;需要迁移时使用 ZIP 备份。 +- 已保存的 Provider 凭据只留在宿主上,不会被导出;但备份仍包含私有记忆,请妥善保护。 +- Source 与 Strategy 是受信任的进程内 JavaScript,**不是沙箱代码**;历史记忆永远不会凌驾于当前指令之上。 + +[备份与恢复](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/zh-CN/guides/operations.md) · [安全策略](https://github.com/omdsh-dev/dsh-mnemon/blob/8466e3560a3b9de4e9f4b7302cbf005c84e8e69f/SECURITY.md) · [版本历史](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/zh-CN/releases/README.md) · [路线图](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/zh-CN/roadmap.md) + +## 开发与验证 + +```sh +pnpm install --frozen-lockfile +pnpm verify +pnpm verify:plugins +``` + +需要 Node.js `^22.19.0 || >=24.0.0` 与 pnpm。`node scripts/serve-e2e.mjs` 会启动一个用后即弃的真实 WebUI;加上 `--docs-demo` 即可得到这些截图背后的示例项目,再加 `--live-model` 则由 DeepSeek API(读取 `DEEPSEEK_API_KEY`)真实作答。机制测试不代表模型准确率,也不代表云端 Provider 的实际表现。详见[开发与验证](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/zh-CN/development/README.md)。 diff --git a/dsh/SECURITY.md b/dsh/SECURITY.md new file mode 100644 index 000000000..f63475167 --- /dev/null +++ b/dsh/SECURITY.md @@ -0,0 +1,96 @@ +# Security Policy + +**English** | [简体中文](#简体中文) + +## Supported versions + +| Version | Supported | +|---|---| +| Latest stable `dsh-mnemon` Starter and its pinned official plugins | Security fixes | +| Older releases | Upgrade to the latest stable Starter | + +Only the latest stable release receives security fixes. Official Source, Strategy and Provider packages in this repository are in scope even when published independently; report both the Starter version and the affected package version. The Starter pins a tested combination. See the verified Host and runtime baselines in [Compatibility](./docs/en/reference/compatibility.md). + +## Reporting a vulnerability + +Please **do not open a public issue** for security vulnerabilities. + +Report privately through one of these channels, in order of preference: + +1. **GitHub Security Advisories** — use the “Report a vulnerability” flow on the [Security tab](https://github.com/mnemon-dev/mnemon/security) of this repository (private vulnerability reporting). +2. **Email** — `grivn.wang@gmail.com` (maintainer). Use a subject prefix like `[dsh-mnemon security]`. + +Please include, when possible: + +- affected version(s); +- a minimal reproduction or proof of concept; +- whether the issue is already exploited publicly; +- your disclosure preference (e.g., coordinated disclosure, credit, embargo). + +## What to expect + +- Acknowledge receipt within **7 days**. +- A fix or a reasoned “not a vulnerability” response within **30 days** where feasible. +- Credit in the release notes unless you prefer to stay anonymous. + +## Scope + +In scope: + +- The plugin bundle (`lib/`, `cordis.patch.yml`): data-loss, privilege-escalation, path-traversal, secret-leak, and memory-corruption style issues in plugin code. +- The control plane: lock handling, revision conflict checks, atomic writes, and subagent isolation boundaries. +- The composable-memory boundary: generation/turn ownership, View validation, grant and live authority checks, and Evidence/Receipt integrity. +- The WebUI: XSS or injection via rendered memory content. + +Out of scope: + +- Issues in upstream dependencies (`mnemon`, `cordis`, DSH core, React) — report those upstream; we will still help triage and pin workarounds. +- Missing features, documentation gaps, and non-security bugs — use regular issues. +- Credentials or secrets you intentionally stored in memory data yourself; the plugin has no secret scanner (see the disclosure in the README). + +## Known limitations + +- There is no deterministic credential/secret detection today. Do not write keys, tokens, or private keys into Runtime Memory, Documents, or Memory Spaces. +- Cordis isolation provides lifecycle ownership and scope composition, not a security sandbox. Sources, their Providers, and Strategies are ordinary JavaScript in the DSH Host process. Install extensions only from trusted sources. +- Model-generated Strategy source is not executed automatically. Manifests, scoped grants, replay, and Core/Host validation reduce accidental authority but cannot make hostile in-process code safe. +- Runtime Memory and Documents are local, and Mnemon Native is local by default. Enabled third-party Providers can make network or local-process calls and expose data according to their configured endpoints and credentials. + +--- + +## 简体中文 + +### 支持的版本 + +| 版本 | 支持 | +|---|---| +| 最新稳定版 `dsh-mnemon` Starter 及其固定的官方插件组合 | 提供安全修复 | +| 较旧版本 | 升级到最新稳定版 Starter | + +仅最新稳定版本获得安全修复。本仓库的官方 Source、Strategy 和 Provider 包即使独立发布,也属于报告范围;请同时提供 Starter 与受影响包的版本。Starter 固定经过验证的组合,已验证的宿主和运行环境见[兼容性说明](./docs/zh-CN/reference/compatibility.md)。 + +### 报告漏洞 + +请**不要**为安全漏洞开公开 issue,按优先级使用以下渠道私下报告: + +1. **GitHub Security Advisories**:在本仓库 [Security 页](https://github.com/mnemon-dev/mnemon/security) 使用“Report a vulnerability”(私密漏洞报告)。 +2. **邮件**:`grivn.wang@gmail.com`(维护者),主题加 `[dsh-mnemon security]` 前缀。 + +请尽量附上:受影响版本、最小复现或 PoC、是否已被公开利用、披露偏好(协调披露 / 署名 / 保密期)。 + +### 预期 + +- **7 天**内确认收到;可行情况下 **30 天**内给出修复或“非漏洞”的结论。 +- 除非你要求匿名,否则在发布说明中致谢。 + +### 范围 + +**范围内**:插件 bundle 与 `cordis.patch.yml` 中的数据丢失、提权、路径穿越、秘密泄露类问题;控制面(锁、revision 冲突检查、原子写、子 Agent 隔离);可组合记忆边界(generation/turn 所有权、View 校验、grant 与当前权限检查、Evidence/Receipt 完整性);WebUI 对记忆内容渲染的 XSS/注入。 + +**范围外**:上游依赖(`mnemon`、`cordis`、DSH 核心、React)自身的问题(报告给上游,我们可协助定位与临时规避);功能缺失、文档问题(走普通 issue);你自行写入记忆数据中的凭据或秘密(插件当前没有秘密检测器,见 README 披露)。 + +### 已知限制 + +- 当前没有确定性的凭据/秘密检测,请勿向热记忆、Documents 或 Memory Spaces 写入密钥、token、私钥。 +- Cordis isolate 提供生命周期所有权和作用域组合,不是安全沙箱。Source、其内部 Provider 与 Strategy 都是在 DSH Host 进程执行的普通 JavaScript,只能安装受信任来源的扩展。 +- 模型生成的 Strategy 源码不会自动执行。manifest、限定范围的 grant、replay 与 Core/Host 校验可以降低意外越权,但无法让恶意同进程代码变安全。 +- Runtime Memory 与 Documents 为本地存储,Mnemon Native 默认本地;显式启用的三方 Provider 可能按配置 endpoint 与凭据发起网络调用或本地进程调用,并向对应服务暴露数据。 diff --git a/dsh/THIRD_PARTY_NOTICES.md b/dsh/THIRD_PARTY_NOTICES.md new file mode 100644 index 000000000..54e36ac91 --- /dev/null +++ b/dsh/THIRD_PARTY_NOTICES.md @@ -0,0 +1,7 @@ +# Third-party notices + +`dsh-mnemon` is licensed under the MIT License. Its provider adapters are original TypeScript integrations adapted to DSH's contracts and verified against the relevant public API or CLI behavior. This package does not bundle provider SDKs or provider services. + +Honcho's open-source server is licensed under AGPL-3.0. dsh-mnemon does not copy or bundle that server; its Honcho adapter communicates with a separately operated v3 HTTP API. Other external providers and hosted services—including OpenViking, Mem0, Hindsight, Holographic, RetainDB, ByteRover, and Supermemory—remain separate products governed by their own licenses and service terms. + +Provider marks displayed by the Web UI are sourced from each provider's official website or source repository and bundled solely to identify the configured integration. Holographic is a built-in provider without an independent brand asset, so the UI uses its canonical mirror motif. Names, marks, and trademarks belong to their respective owners. Inclusion identifies interoperability only and does not imply endorsement. diff --git a/dsh/bin/repair-legacy-session.mjs b/dsh/bin/repair-legacy-session.mjs new file mode 100755 index 000000000..95e9bc115 --- /dev/null +++ b/dsh/bin/repair-legacy-session.mjs @@ -0,0 +1,742 @@ +#!/usr/bin/env node +import { createHash } from 'node:crypto' +import { createReadStream, realpathSync } from 'node:fs' +import { link, mkdtemp, open, rm } from 'node:fs/promises' +import { dirname, join, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' +import { isDeepStrictEqual } from 'node:util' +import * as zlib from 'node:zlib' + +const maximumBytes = 128 * 1024 * 1024 +const summaries = { + recall: ['Memory View snapshot', 'Runtime memory snapshot'], + instructions: ['Optional memory recall and remember reminder'], +} +const sha256 = value => createHash('sha256').update(value).digest('hex') +const record = value => value !== null && typeof value === 'object' && !Array.isArray(value) +const nonempty = value => typeof value === 'string' && value.length > 0 +const integer = value => Number.isSafeInteger(value) && !Object.is(value, -0) +const count = value => integer(value) && value >= 0 +const exactKeys = (value, required, optional = []) => record(value) && required.every(key => Object.hasOwn(value, key)) && + Object.keys(value).every(key => required.includes(key) || optional.includes(key)) + +// Official dsh-subagent 0.1.1-rc.2 v2 -> 0.1.2-alpha.2 v3 adds only the +// optional agentReasoningEffort composition input. With that field absent, +// these exact old records reconstruct the same declared child options. Apply +// the stricter frozen-v3 constraints too; accepting v2 alone is insufficient. +function supportedDescriptorV2(value) { + if (!record(value) || value.version !== 2 || !nonempty(value.provider)) return false + const base = ['version', 'mode', 'provider', 'label'] + if (value.mode === 'one-shot') return Object.keys(value).every(key => base.includes(key)) && + (!Object.hasOwn(value, 'label') || typeof value.label === 'string') + if (value.mode !== 'continuable' || !nonempty(value.label)) return false + if (!Object.keys(value).every(key => [...base, 'agentProvider', 'agentModel', 'persona', 'toolFilter'].includes(key))) return false + for (const key of ['agentProvider', 'agentModel', 'persona']) if (Object.hasOwn(value, key) && !nonempty(value[key])) return false + if (Object.hasOwn(value, 'agentProvider') !== Object.hasOwn(value, 'agentModel')) return false + if (!Object.hasOwn(value, 'toolFilter')) return true + const filter = value.toolFilter + return record(filter) && Object.keys(filter).length > 0 && Object.keys(filter).every(key => + ['allow', 'deny'].includes(key) && Array.isArray(filter[key]) && filter[key].every(nonempty)) +} + +function supportedPackedToolRun(row, placeholdersOnly = true) { + if (!exactKeys(row, ['type', 'seq0', 'time0', 'data']) || !count(row.seq0) || !integer(row.time0)) return false + const data = row.data + if (!exactKeys(data, ['turn', 'step', 'index', 'id', 'dt', 'args'], ['name']) || typeof data.id !== 'string' || (placeholdersOnly && data.id !== '' && data.name !== '' && data.name !== null) || + (Object.hasOwn(data, 'name') && data.name !== null && typeof data.name !== 'string')) return false + if (![data.turn, data.step, data.index].every(count) || !Array.isArray(data.args) || data.args.length === 0 || + !data.args.every(value => typeof value === 'string') || !Array.isArray(data.dt) || data.dt.length !== data.args.length - 1 || + data.args.length - 1 > Number.MAX_SAFE_INTEGER - row.seq0) return false + let time = row.time0 + return data.dt.every(gap => integer(gap) && integer(time += gap)) +} + +const rawChunkEnvelope = row => exactKeys(row, ['type', 'seq', 'time', 'data'], ['ignorable']) && count(row.seq) && integer(row.time) && + (!Object.hasOwn(row, 'ignorable') || row.ignorable === true) && exactKeys(row.data, ['turn', 'step', 'chunk']) && [row.data.turn, row.data.step].every(count) +const surfaceEnvelope = row => exactKeys(row, ['type', 'seq', 'time', 'data'], ['surfaceOp', 'sourceEventSeqs', 'ignorable']) && count(row.seq) && integer(row.time) && + (!Object.hasOwn(row, 'ignorable') || row.ignorable === true) +const messageShape = value => exactKeys(value, ['role', 'id', 'source', 'content']) && nonempty(value.id) && Array.isArray(value.content) +const toolBlockShape = value => exactKeys(value, ['type', 'id', 'name', 'arguments']) && value.type === 'tool-call' && typeof value.id === 'string' && nonempty(value.name) && typeof value.arguments === 'string' + +function supportedPackedTextRun(row) { + const data = row.data + return exactKeys(data, ['turn', 'step', 'index', 'dt', 'texts']) && supportedPackedToolRun({ ...row, + data: { turn: data.turn, step: data.step, index: data.index, id: 'unchanged-text-run', dt: data.dt, args: data.texts } }, false) +} + +function supportedNullToolDelta(row) { + if (!rawChunkEnvelope(row)) return false + const chunk = row.data.chunk + return exactKeys(chunk, ['type', 'index', 'id', 'name', 'argumentsDelta']) && chunk.type === 'tool-call-delta' && + chunk.name === null && count(chunk.index) && typeof chunk.id === 'string' && typeof chunk.argumentsDelta === 'string' +} + +// This is a proof for the published DeepSeek empty-continuation-ID bug, not a +// nearest-call heuristic. Everything is planned before any output is published. +function recoverRecordedToolIdentities(entries) { + const rawPatches = new Map() + const packedIds = new Map() + const refusals = { occurrences: 0, samples: [] } + let chains = 0 + let fields = 0 + const emptyBlock = block => block?.type === 'tool-call' && block.id === '' || block?.type === 'tool-result' && block.toolCallId === '' + const emptyDurable = row => row.type === 'tool/call' && row.data?.callId === '' || + row.type === 'assistant/chunk' && row.data?.chunk?.type === 'block-end' && emptyBlock(row.data.chunk.block) || + ['assistant/message', 'tool/result'].includes(row.type) && (row.data?.message?.content?.some?.(emptyBlock) || row.data?.message?.source?.callId === '') + const result = () => ({ rawPatches, packedIds, chains, fields, refusals }) + if (!entries.some(entry => emptyDurable(entry.row))) return result() + const require = (condition, reason) => { if (!condition) throw reason } + const refuse = (entry, reason) => { + refusals.occurrences++ + if (refusals.samples.length < 10) refusals.samples.push({ line: entry.index + 1, ...(count(entry.row.seq) ? { seq: entry.row.seq } : {}), reason }) + } + const logical = [] + let turn, step + try { + for (const entry of entries.slice(1)) { + const row = entry.row + objectMembers(entry.line, 0) // Sequence identity cannot come from duplicate envelope keys. + if (row.type === 'turn/start') turn = row.data?.turn + if (row.type === 'step/start') { turn = row.data?.turn; step = row.data?.step } + entry.scope = { turn: row.data?.turn ?? turn, step: row.data?.step ?? step } + if (['tool-call-chunks', 'text-chunks', 'reasoning-chunks'].includes(row.type)) { + const tool = row.type === 'tool-call-chunks' + require(tool ? supportedPackedToolRun(row, false) : supportedPackedTextRun(row), 'A packed source has unsupported fields or coordinates.') + const fragments = tool ? row.data.args : row.data.texts + let time = row.time0 + for (let offset = 0; offset < fragments.length; offset++) { + if (offset) time += row.data.dt[offset - 1] + require(logical.length < maximumBytes / 128, 'The identity proof exceeds its bounded event count.') + logical.push({ entry, offset, row: { type: 'assistant/chunk', seq: row.seq0 + offset, time, + data: { turn: row.data.turn, step: row.data.step, chunk: tool ? { type: 'tool-call-delta', index: row.data.index, id: row.data.id, + ...(Object.hasOwn(row.data, 'name') ? { name: row.data.name } : {}), argumentsDelta: fragments[offset] } + : { type: row.type === 'text-chunks' ? 'text-delta' : 'reasoning-delta', index: row.data.index, text: fragments[offset] } } } }) + } + } else logical.push({ entry, row }) + if (row.type === 'step/end') step = undefined + if (row.type === 'turn/end') { turn = undefined; step = undefined } + } + require(logical.every((value, index) => value.row.seq === index && integer(value.row.time)), 'Logical events must have unique complete sequence coordinates and safe times.') + } catch (error) { + if (typeof error !== 'string') throw error + refuse(entries.find(entry => emptyDurable(entry.row)), error) + return result() + } + const sourceSeqs = (row, nonempty = true) => { + const values = row.sourceEventSeqs + require(Array.isArray(values), 'Tool history needs explicit, valid assistant chunk provenance.') + const selected = new Set() + const hasRanges = values.some(Array.isArray) + let previous = -1 + for (const member of values) { + const range = Array.isArray(member) + require(!range || member.length === 2, 'A provenance range must contain exactly two coordinates.') + const start = range ? member[0] : member + const end = range ? member[1] : member + require(count(start) && count(end) && start <= end && end < row.seq && end - start < logical.length - selected.size, + 'Provenance contains an unsafe, missing or excessive source range.') + require((!hasRanges && row.type !== 'assistant/message') || start > previous, 'Chunk provenance is not one complete ordered attempt.') + for (let seq = start; seq <= end; seq++) { + require(!selected.has(seq), 'Provenance contains duplicate or overlapping source coordinates.') + selected.add(seq) + } + previous = end + } + require(!nonempty || selected.size > 0, 'A completed tool stream needs nonempty chunk provenance.') + // v0 accepts unordered flat sets, but the active v1 -> v2 stage requires an + // ordered complete attempt. Do not silently rewrite that provenance here. + return [...selected].sort((left, right) => left - right) + } + const sameStep = (left, right) => left?.turn === right?.turn && left?.step === right?.step + const messages = entries.filter(entry => entry.row.type === 'assistant/message') + for (const assistant of messages) { + const row = assistant.row + const data = row.data + const content = data?.message?.content + if (!Array.isArray(content) || !content.some(block => block?.type === 'tool-call') || + !entries.some(entry => sameStep(entry.scope, data) && emptyDurable(entry.row))) continue + try { + require(surfaceEnvelope(row) && row.surfaceOp === 'append' && exactKeys(data, ['turn', 'step', 'message'], ['usage']) && count(data.turn) && count(data.step) && + messageShape(data.message) && data.message.role === 'assistant' && exactKeys(data.message.source, ['kind', 'provider', 'model']) && + data.message.source.kind === 'model' && data.message.source.provider === 'deepseek-official' && nonempty(data.message.source.model), + 'Only the uninterrupted recorded DeepSeek append-message shape without owner replay state is supported.') + const sources = sourceSeqs(row) + const selected = new Set(sources) + const stream = sources.map(seq => logical[seq]) + require(stream.every(value => value?.row.type === 'assistant/chunk' && sameStep(value.row.data, data)), 'Chunk provenance cites another event, turn or step.') + require(logical.slice(sources[0], sources.at(-1) + 1).every(value => value.row.type !== 'assistant/chunk' || !sameStep(value.row.data, data) || selected.has(value.row.seq)), + 'Chunk provenance omits a delta inside this stream attempt.') + for (const other of messages) if (other !== assistant && Object.hasOwn(other.row, 'sourceEventSeqs')) { + require(!sourceSeqs(other.row, false).some(seq => selected.has(seq)), 'Two assistant messages share source chunks.') + } + const blocks = new Map() + let finished = false + let usage + for (const value of stream) { + const current = value.row + const chunk = current.data.chunk + require(rawChunkEnvelope(current) && record(chunk) && !finished, 'The stream has unsupported fields or chunks after finish.') + assertUniqueJson(value.entry.line, value.entry.row) + if (chunk.type === 'usage') { + require(exactKeys(chunk, ['type', 'usage']) && record(chunk.usage), 'An unrecognized usage chunk prevents identity recovery.') + usage = chunk.usage + continue + } + if (chunk.type === 'finish') { + require(exactKeys(chunk, ['type', 'reason']) && exactKeys(chunk.reason, ['kind']) && chunk.reason.kind === 'tool-calls', 'The tool stream is interrupted, truncated or has owner replay state.') + finished = true + continue + } + require(count(chunk.index), 'A stream block index is unsafe.') + let block = blocks.get(chunk.index) + if (chunk.type === 'block-start') { + require(exactKeys(chunk, ['type', 'index', 'blockType']) && ['text', 'reasoning', 'tool-call'].includes(chunk.blockType) && !block, + 'A block start is repeated or unsupported.') + blocks.set(chunk.index, { type: chunk.blockType, started: true, text: '', arguments: '', deltas: [] }) + continue + } + const type = chunk.type === 'text-delta' ? 'text' : chunk.type === 'reasoning-delta' ? 'reasoning' : chunk.type === 'tool-call-delta' ? 'tool-call' : chunk.block?.type + require(['text', 'reasoning', 'tool-call'].includes(type), 'A stream chunk has an unsupported block type.') + if (!block) { + require(type !== 'tool-call', 'A tool delta or end is missing its block start.') + block = { type, text: '', arguments: '', deltas: [] } + blocks.set(chunk.index, block) + } + require(block.type === type && !block.end, 'A block changes type, repeats its end or receives a late delta.') + if (chunk.type === 'block-end') { + require(exactKeys(chunk, ['type', 'index', 'block']) && record(chunk.block), 'A block end has unsupported fields.') + block.end = value + block.content = chunk.block + if (type === 'tool-call') require(toolBlockShape(chunk.block) && + chunk.block.name === block.name && chunk.block.arguments === block.arguments, 'The final tool name or arguments disagree with its deltas.') + continue + } + if (type === 'tool-call') { + require(exactKeys(chunk, ['type', 'index', 'id', 'argumentsDelta'], ['name']) && typeof chunk.id === 'string' && typeof chunk.argumentsDelta === 'string' && + (!Object.hasOwn(chunk, 'name') || chunk.name === null || typeof chunk.name === 'string'), 'A tool delta has unsupported identity or argument fields.') + if (chunk.name) block.name = chunk.name + block.arguments += chunk.argumentsDelta + block.deltas.push(value) + } else { + require(exactKeys(chunk, ['type', 'index', 'text']) && typeof chunk.text === 'string', 'A text or reasoning delta has unsupported fields.') + block.text += chunk.text + } + } + require(finished && blocks.size === content.length, 'The completed stream and assistant block list do not match.') + require(isDeepStrictEqual(usage, data.usage), 'The completed stream and assistant usage do not match.') + const ordered = [...blocks.values()] + const advertised = messages.filter(entry => sameStep(entry.row.data, data)).flatMap(entry => entry.row.data?.message?.content ?? []).filter(block => block?.type === 'tool-call') + const calls = entries.filter(entry => entry.row.type === 'tool/call' && sameStep(entry.row.data, data)) + const results = entries.filter(entry => entry.row.type === 'tool/result' && sameStep(entry.row.data, data)) + const proposals = [] + const provenChains = [] + for (const [position, block] of ordered.entries()) { + const actual = block.content ?? { type: block.type, text: block.text } + const declared = content[position] + if (block.type !== 'tool-call') { + require(isDeepStrictEqual(actual, declared), 'Text or reasoning differs between the stream and assistant message.') + continue + } + require(block.end && toolBlockShape(declared) && + isDeepStrictEqual({ ...actual, id: '' }, { ...declared, id: '' }), 'The tool block and assistant advertisement disagree.') + const matchingCalls = calls.filter(entry => entry.row.data.name === actual.name && entry.row.data.arguments === actual.arguments) + require(matchingCalls.length === 1 && advertised.filter(value => value.name === actual.name && value.arguments === actual.arguments).length === 1, + 'The tool advertisement and execution cannot be paired uniquely by name and arguments.') + const call = matchingCalls[0] + require(surfaceEnvelope(call.row) && !Object.hasOwn(call.row, 'surfaceOp') && !Object.hasOwn(call.row, 'sourceEventSeqs') && + exactKeys(call.row.data, ['turn', 'step', 'callId', 'name', 'arguments']) && row.seq < call.row.seq && typeof call.row.data.callId === 'string', + 'The tool execution has unsupported fields, precedes its advertisement or has an invalid ID.') + const matchingResults = results.filter(entry => Object.hasOwn(entry.row, 'sourceEventSeqs') && sourceSeqs(entry.row).includes(call.row.seq)) + let toolResult + if (matchingResults.length === 1) { + toolResult = matchingResults[0] + require(isDeepStrictEqual(sourceSeqs(toolResult.row), [call.row.seq]), 'The result provenance does not select exactly this tool execution.') + } else { + require(matchingResults.length === 0 && advertised.length === 1 && calls.length === 1 && results.length === 1 && !Object.hasOwn(results[0].row, 'sourceEventSeqs'), + 'The tool result is missing, ambiguous or has conflicting provenance.') + require(!logical.some(value => value.row.type === 'assistant/chunk' && sameStep(value.row.data, data) && !selected.has(value.row.seq)), + 'A result without provenance cannot be matched across a retry or another stream attempt.') + toolResult = results[0] + } + const resultMessage = toolResult.row.data?.message + const resultBlock = resultMessage?.content?.[0] + require(surfaceEnvelope(toolResult.row) && exactKeys(toolResult.row.data, ['turn', 'step', 'message'], ['error', 'meta']) && + call.row.seq < toolResult.row.seq && toolResult.row.surfaceOp === 'append' && messageShape(resultMessage) && resultMessage.role === 'user' && + exactKeys(resultMessage.source, ['kind', 'callId']) && resultMessage.source.kind === 'tool' && typeof resultMessage.source.callId === 'string' && resultMessage.content.length === 1 && + exactKeys(resultBlock, ['type', 'toolCallId', 'content'], ['isError']) && resultBlock.type === 'tool-result' && typeof resultBlock.toolCallId === 'string' && + Array.isArray(resultBlock.content) && (!Object.hasOwn(resultBlock, 'isError') || typeof resultBlock.isError === 'boolean'), 'The tool result is not a complete append result with a single identity.') + const slots = [ + [block.end, ['data', 'chunk', 'block', 'id'], actual.id], + [{ entry: assistant, row }, ['data', 'message', 'content', position, 'id'], declared.id], + [{ entry: call, row: call.row }, ['data', 'callId'], call.row.data.callId], + [{ entry: toolResult, row: toolResult.row }, ['data', 'message', 'source', 'callId'], resultMessage.source.callId], + [{ entry: toolResult, row: toolResult.row }, ['data', 'message', 'content', 0, 'toolCallId'], resultMessage.content[0].toolCallId], + ] + if (!slots.some(slot => slot[2] === '')) { + require(slots.every(slot => slot[2] === actual.id) && block.deltas.every(value => value.row.data.chunk.id === '' || value.row.data.chunk.id === actual.id), + 'An already nonempty tool chain contains conflicting recorded identities.') + provenChains.push(slots) + continue + } + const candidates = new Set(block.deltas.map(value => value.row.data.chunk.id).filter(nonempty)) + require(candidates.size === 1, 'The tool stream has no unique previously recorded provider ID.') + const candidate = [...candidates][0] + require(slots.every(slot => slot[2] === '' || slot[2] === candidate), 'A durable ID conflicts with the recorded provider ID.') + require(advertised.every(value => value === declared || value.id !== candidate), 'The recorded ID is already advertised by another tool in this step.') + let seen = false + const deltas = [] + for (const value of block.deltas) { + if (value.row.data.chunk.id === candidate) seen = true + else if (seen) deltas.push([value, ['data', 'chunk', 'id'], '']) + } + require(deltas.length > 0, 'No empty continuation delta follows the recorded provider ID.') + provenChains.push(slots) + proposals.push({ candidate, name: actual.name, assistant, call, toolResult, block, slots: [...deltas, ...slots.filter(slot => slot[2] === '')], sources }) + } + require(new Set(proposals.map(value => value.candidate)).size === proposals.length, 'Multiple tools claim the same recorded provider ID.') + for (const proposal of proposals) { + const owned = new Map() + const allow = (entry, path) => { + if (!owned.has(entry)) owned.set(entry, new Set()) + owned.get(entry).add(JSON.stringify(path)) + } + // Only the exact slots of proven advertisement/call/result chains are + // owned; another core-looking call is not automatically trustworthy. + for (const slots of provenChains) for (const [value, path] of slots) allow(value.entry, path) + const linkedSeqs = new Set([...sources, row.seq, proposal.call.row.seq, proposal.toolResult.row.seq]) + const linkedMessages = new Set([data.message.id, proposal.toolResult.row.data.message.id]) + const cites = value => { + if (!record(value) && !Array.isArray(value)) return false + return Object.entries(value).some(([key, child]) => { + if (key === 'sourceEventSeq' && linkedSeqs.has(child)) return true + if ((/^(?:messageId|.*MessageId)s?$/.test(key) || key === 'id' && ['assistant', 'user'].includes(value.role)) && + (linkedMessages.has(child) || Array.isArray(child) && child.some(id => linkedMessages.has(id)))) return true + if (key === 'shadowedRange' && record(child) && [...linkedSeqs].some(seq => count(child.start) && count(child.end) && child.start <= seq && seq <= child.end)) return true + if (['sourceEventSeqs', 'shadowedSeqs', 'shadowedRange'].includes(key) && Array.isArray(child)) { + if (key === 'shadowedRange' && child.length === 2 && [...linkedSeqs].some(seq => count(child[0]) && count(child[1]) && child[0] <= seq && seq <= child[1])) return true + if (child.some(member => linkedSeqs.has(member) || Array.isArray(member) && member.length === 2 && [...linkedSeqs].some(seq => count(member[0]) && count(member[1]) && member[0] <= seq && seq <= member[1]))) return true + } + return cites(child) + }) + } + for (const entry of entries.slice(1)) { + const value = entry.row + const replacement = value.surfaceOp + const surfaces = [row.seq, proposal.toolResult.row.seq] + const replaces = record(replacement) && replacement.op === 'replace' && surfaces.some(seq => count(replacement.start) && count(replacement.end) && replacement.start <= seq && seq <= replacement.end) + require(!replaces, 'A surface replacement also owns the affected historical message.') + const copiesMessage = [data.message.id, proposal.toolResult.row.data.message.id].includes(value.data?.message?.id) && entry !== assistant && entry !== proposal.toolResult + require(!copiesMessage, 'A copied message identity needs owner-aware recovery.') + if (!sameStep(entry.scope, data) && !cites(value)) continue + assertUniqueJson(entry.line, value) + require(!(value.type === 'approval/asked' && value.data?.toolName === proposal.name && + (value.data.callId === undefined || value.data.callId === '' || value.data.callId === proposal.candidate)), 'An associated approval retains the historical tool identity.') + const visit = (current, path = []) => { + if (!record(current) && !Array.isArray(current)) return + for (const [key, child] of Object.entries(current)) { + const next = [...path, Array.isArray(current) ? Number(key) : key] + const identity = ['callId', 'toolCallId', 'rootCallId', 'parentCallId', 'subCallId'].includes(key) || key === 'id' && current.type === 'tool-call' + if (key === 'replayState') require(false, 'Owner replay state needs owner-aware identity recovery.') + if (identity && (child === '' || child === proposal.candidate || typeof child === 'string' && (child.startsWith(`${proposal.candidate}:code:`) || child.startsWith(':code:')))) { + require(owned.get(entry)?.has(JSON.stringify(next)), 'An additional approval, dispatch, plugin or copied-content identity reference remains.') + } + visit(child, next) + } + } + visit(value) + } + } + for (const proposal of proposals) { + chains++ + for (const [value, path] of proposal.slots) { + fields++ + if (value.offset !== undefined) { + if (!packedIds.has(value.entry.index)) packedIds.set(value.entry.index, new Map()) + packedIds.get(value.entry.index).set(value.offset, proposal.candidate) + } else { + if (!rawPatches.has(value.entry.index)) rawPatches.set(value.entry.index, []) + rawPatches.get(value.entry.index).push({ path, value: proposal.candidate }) + } + } + } + } catch (error) { + if (typeof error !== 'string') throw error + refuse(assistant, error) + } + } + if (refusals.occurrences > 0) { rawPatches.clear(); packedIds.clear(); chains = 0; fields = 0 } + return result() +} + +// Diagnose the other concrete legacy shapes reported in #251 without inventing +// descriptor authority or tool associations. This is not a Session validator: +// the owning DSH installation still validates the complete repaired artifact. +function legacyBlockers() { + const issues = new Map() + const add = (code, message, row, line, path) => { + if (!issues.has(code)) issues.set(code, { code, message, occurrences: 0, samples: [] }) + const issue = issues.get(code) + issue.occurrences++ + const seq = row.seq ?? row.seq0 + if (issue.samples.length < 10) issue.samples.push({ line, type: row.type, ...(count(seq) ? { seq } : {}), path }) + } + const identifier = (value, row, line, path) => { + if (value === '') add('empty-durable-tool-id', 'An empty durable tool ID lacks a proven closed chain to its recorded provider identity; new identities are not synthesized.', row, line, path) + } + const block = (value, row, line, path) => { + if (value?.type === 'tool-call') identifier(value.id, row, line, `${path}.id`) + if (value?.type === 'tool-result') identifier(value.toolCallId, row, line, `${path}.toolCallId`) + } + const message = (value, row, line, path) => { + if (value?.source?.kind === 'tool') identifier(value.source.callId, row, line, `${path}.source.callId`) + if (Array.isArray(value?.content)) value.content.forEach((value, index) => block(value, row, line, `${path}.content[${index}]`)) + } + return { + inspect(row, line) { + const data = row?.data + if (row?.type === 'subagent/descriptor' && data?.version !== 3 && !supportedDescriptorV2(data)) { + add('unsupported-subagent-descriptor', 'Only exact historical v2 descriptors with equivalent v3 composition are supported; this descriptor needs recovery by its original writer.', row, line, 'data.version') + } + if (row?.type === 'tool-call-chunks') { + if ((data?.id === '' || data?.name === '' || data?.name === null) && !supportedPackedToolRun(row)) add('unsupported-packed-tool-chunks', 'The packed row cannot be expanded losslessly: it needs exact fields, string deltas and safe sequence/time coordinates.', row, line, 'data') + } + if (row?.type === 'assistant/chunk') { + if (data?.chunk?.type === 'tool-call-delta' && data.chunk.name === null && !supportedNullToolDelta(row)) { + add('null-stream-tool-name', 'A null streaming tool name needs exact raw fields, string ID/arguments and safe coordinates before normalization.', row, line, 'data.chunk.name') + } + if (data?.chunk?.type === 'block-end') block(data.chunk.block, row, line, 'data.chunk.block') + } + if (row?.type === 'assistant/message') { + message(data?.message, row, line, 'data.message') + // Frozen v0 also normalizes the older unwrapped assistant shape. + if (record(data) && !Object.hasOwn(data, 'message') && Object.hasOwn(data, 'content') && Object.hasOwn(data, 'provenance')) { + message(data, row, line, 'data') + } + } + if (row?.type === 'tool/call') identifier(data?.callId, row, line, 'data.callId') + if (row?.type === 'tool/result') { + identifier(data?.callId, row, line, 'data.callId') // Historical unwrapped result. + message(data?.message, row, line, 'data.message') + } + }, + report: () => [...issues.values()], + } +} + +// Locate JSON object members without reserializing message content or numbers. +// JSON.parse validates each complete row before this scanner is used. +function objectMembers(text, start, array = false) { + let cursor = start + const space = () => { while (/\s/.test(text[cursor] ?? '') && cursor < text.length) cursor++ } + const stringEnd = () => { + cursor++ + while (cursor < text.length) { + if (text[cursor++] === '"') return + if (text[cursor - 1] === '\\') cursor++ + } + } + const valueEnd = () => { + if (text[cursor] === '"') return stringEnd() + if (text[cursor] === '{' || text[cursor] === '[') { + let depth = 0 + do { + if (text[cursor] === '"') stringEnd() + else { + if ('{['.includes(text[cursor])) depth++ + if ('}]'.includes(text[cursor])) depth-- + cursor++ + } + } while (depth > 0) + } else { + while (cursor < text.length && !/[\s,}\]]/.test(text[cursor])) cursor++ + } + } + space() + if (text[cursor++] !== (array ? '[' : '{')) throw new Error('Expected a JSON container in the legacy message path.') + const members = [] + const names = new Set() + space() + const close = array ? ']' : '}' + while (text[cursor] !== close) { + const start = cursor + let name = members.length + if (!array) { + stringEnd() + name = JSON.parse(text.slice(start, cursor)) + if (names.has(name)) throw new Error('Duplicate JSON property in the legacy message path; refusing ambiguous repair.') + names.add(name) + space() + cursor++ // colon, already validated by JSON.parse + space() + } + const valueStart = cursor + valueEnd() + members.push({ name, start, end: cursor, valueStart }) + space() + if (text[cursor] === close) break + cursor++ // comma + space() + } + return members +} + +function assertUniqueJson(line, row, start = 0) { + if (!record(row) && !Array.isArray(row)) return + for (const member of objectMembers(line, start, Array.isArray(row))) assertUniqueJson(line, row[member.name], member.valueStart) +} + +function replaceIdentityValues(line, patches) { + for (const patch of patches) { + let start = 0 + let member + for (const key of patch.path) { + member = objectMembers(line, start, typeof key === 'number').find(value => value.name === key) + if (!member) throw new Error('An identity patch no longer matches its proven JSON path.') + start = member.valueStart + } + line = line.slice(0, member.valueStart) + JSON.stringify(patch.value) + line.slice(member.end) + } + return line +} + +function removeSummary(line) { + const data = objectMembers(line, 0).find(member => member.name === 'data') + const source = objectMembers(line, data.valueStart).find(member => member.name === 'source') + const members = objectMembers(line, source.valueStart) + const index = members.findIndex(member => member.name === 'summary') + const member = members[index] + const start = index === members.length - 1 && index > 0 ? members[index - 1].end : member.start + const end = index < members.length - 1 ? members[index + 1].start : member.end + return line.slice(0, start) + line.slice(end) +} + +function promoteDescriptor(line) { + const data = objectMembers(line, 0).find(member => member.name === 'data') + const members = objectMembers(line, data.valueStart) + const filter = members.find(member => member.name === 'toolFilter') + if (filter) objectMembers(line, filter.valueStart) // Reject ambiguous nested fields as well. + const version = members.find(member => member.name === 'version') + return line.slice(0, version.valueStart) + '3' + line.slice(version.end) +} + +function normalizeNullToolName(line) { + const data = objectMembers(line, 0).find(member => member.name === 'data') + const chunk = objectMembers(line, data.valueStart).find(member => member.name === 'chunk') + const name = objectMembers(line, chunk.valueStart).find(member => member.name === 'name') + // Old/current assemblers only assign truthy names; token timing instead uses + // name !== undefined. Keep this property: deleting it would change TTFT for + // an empty argument fragment. Every other raw byte remains unchanged. + return line.slice(0, name.valueStart) + '""' + line.slice(name.end) +} + +function expandPackedToolRun(line, row, retain, recoveredIds) { + const data = objectMembers(line, 0).find(member => member.name === 'data') + objectMembers(line, data.valueStart) // Reject duplicate fields before reserializing this row. + let time = row.time0 + const expanded = [] + for (let index = 0; index < row.data.args.length; index++) { + if (index > 0) time += row.data.dt[index - 1] + // Historical packed-row expansion after preserving null-name presence as + // an empty string. No ID/name inference and + // no dropped chunks: the active v1 -> v2 migration retains these as raw + // stream records when their IDs or names are empty strings. + expanded.push(retain(JSON.stringify({ + type: 'assistant/chunk', seq: row.seq0 + index, time, + data: { turn: row.data.turn, step: row.data.step, chunk: { + type: 'tool-call-delta', index: row.data.index, id: recoveredIds?.get(index) ?? row.data.id, + ...(Object.hasOwn(row.data, 'name') ? { name: row.data.name ?? '' } : {}), argumentsDelta: row.data.args[index], + } }, + }) + (line.endsWith('\r\n') ? '\r\n' : '\n'))) + } + return expanded.join('') +} + +async function readBounded(path) { + const chunks = [] + let length = 0 + for await (const chunk of createReadStream(path)) { + length += chunk.length + if (length > maximumBytes) throw new Error('Session exceeds the 128 MiB repair limit.') + chunks.push(chunk) + } + return Buffer.concat(chunks) +} + +// RFC 8878 framing: reject torn headers, blocks and checksums before Node's +// decoder (which can otherwise accept an unfinished frame) sees the bytes. +function frameLength(input, start) { + const requireBytes = end => { if (end > input.length) throw new Error('Truncated Zstandard frame; no output written.') } + requireBytes(start + 5) + if (input.readUInt32LE(start) !== 0xfd2fb528) throw new Error('Unsupported Zstandard frame; only ordinary DSH frames are accepted.') + const descriptor = input[start + 4] + if (descriptor & 0x18) throw new Error('Reserved Zstandard frame header bits are set.') + const single = (descriptor & 0x20) !== 0 + const sizeFlag = descriptor >>> 6 + const contentSizeBytes = sizeFlag === 0 ? (single ? 1 : 0) : [0, 2, 4, 8][sizeFlag] + let offset = start + 5 + (single ? 0 : 1) + [0, 1, 2, 4][descriptor & 3] + contentSizeBytes + requireBytes(offset) + let last = false + while (!last) { + requireBytes(offset + 3) + const block = input.readUIntLE(offset, 3) + last = (block & 1) !== 0 + const type = (block >>> 1) & 3 + if (type === 3) throw new Error('Reserved Zstandard block type.') + offset += 3 + (type === 1 ? 1 : block >>> 3) + requireBytes(offset) + } + offset += descriptor & 4 ? 4 : 0 + requireBytes(offset) + return offset - start +} + +function decodeFrames(input) { + const chunks = [] + let offset = 0 + let length = 0 + while (offset < input.length) { + const lengthOnDisk = frameLength(input, offset) + const { buffer, engine } = zlib.zstdDecompressSync(input.subarray(offset, offset + lengthOnDisk), { + info: true, maxOutputLength: maximumBytes - length, + }) + if (engine.bytesWritten !== lengthOnDisk) throw new Error('Invalid Zstandard frame length.') + offset += engine.bytesWritten + length += buffer.length + chunks.push(buffer) + } + return Buffer.concat(chunks) +} + +function encodeFrames(text) { + // DSH requires the first frame to contain exactly its header line. + const end = text.indexOf('\n') + 1 + const options = { params: { [zlib.constants.ZSTD_c_checksumFlag]: 1 } } + return Buffer.concat([text.slice(0, end), text.slice(end)].filter(Boolean).map(part => zlib.zstdCompressSync(part, options))) +} + +export async function repairLegacySession(input, output) { + const original = await readBounded(input) + const compressed = original.subarray(0, 4).equals(Buffer.from([0x28, 0xb5, 0x2f, 0xfd])) + if (compressed && (typeof zlib.zstdDecompressSync !== 'function' || typeof zlib.zstdCompressSync !== 'function')) { + throw new Error('Zstandard sessions require Node 22.19 or newer (or Node 24+).') + } + const decoded = compressed ? decodeFrames(original) : original + const text = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(decoded) + if (!text.endsWith('\n')) throw new Error('Session must end with a complete newline-terminated JSON record.') + const lines = text.match(/[^\n]*\n/g) ?? [] + const entries = lines.map((line, index) => { + try { return { line, index, row: JSON.parse(line) } } catch { throw new Error(`Invalid JSON at line ${index + 1}; no output written.`) } + }) + const recovery = recoverRecordedToolIdentities(entries) + let changes = 0 + let descriptors = 0 + let expandedRows = 0 + let expandedChunks = 0 + let normalizedNames = 0 + let outputBytes = 0 + const retain = line => { + outputBytes += Buffer.byteLength(line) + if (outputBytes > maximumBytes) throw new Error('Expanded Session exceeds the 128 MiB repair limit; no output written.') + return line + } + const diagnostics = legacyBlockers() + const repaired = entries.map(entry => { + const { index } = entry + let { line, row } = entry + if (index === 0) { + objectMembers(line, 0) + if (row.type !== 'session' || row.version !== 0 || typeof row.id !== 'string' || !row.id || + !Number.isSafeInteger(row.createdAt) || row.createdAt < 0 || + !Number.isSafeInteger(row.delegationDepth) || row.delegationDepth < 0) { + throw new Error('Only a legacy DSH format v0 Session header is supported.') + } + return retain(line) + } + const patches = recovery.rawPatches.get(index) + if (patches) { line = replaceIdentityValues(line, patches); row = JSON.parse(line) } + diagnostics.inspect(row, index + 1) + if (row?.type === 'subagent/descriptor' && supportedDescriptorV2(row.data)) { + descriptors++ + return retain(promoteDescriptor(line)) + } + if (row?.type === 'tool-call-chunks' && supportedPackedToolRun(row)) { + expandedRows++ + expandedChunks += row.data.args.length + if (row.data.name === null) normalizedNames += row.data.args.length + return expandPackedToolRun(line, row, retain, recovery.packedIds.get(index)) + } + if (row?.type === 'assistant/chunk' && supportedNullToolDelta(row)) { + normalizedNames++ + return retain(normalizeNullToolName(line)) + } + const source = row?.data?.source + if (row?.type !== 'user/message' || source?.kind !== 'plugin' || source.plugin !== 'dsh-mnemon' || + !Object.hasOwn(summaries, source.form) || !summaries[source.form].includes(source.summary)) return retain(line) + changes++ + return retain(removeSummary(line)) + }).join('') + const plain = Buffer.from(repaired) + // A clean artifact stays byte-identical, including its existing frame layout. + const result = changes + descriptors + expandedRows + normalizedNames + recovery.fields === 0 ? original : compressed ? encodeFrames(repaired) : plain + if (result.length > maximumBytes) throw new Error('Repaired Session exceeds the 128 MiB repair limit; no output written.') + const blockers = diagnostics.report() + if (output !== undefined && blockers.length === 0) { + if (resolve(input) === resolve(output)) throw new Error('Output must be a new copy; the input cannot be overwritten.') + const temporary = await mkdtemp(join(dirname(resolve(output)), '.mnemon-repair-')) + try { + const path = join(temporary, 'session') + const file = await open(path, 'wx', 0o600) + try { await file.writeFile(result); await file.sync() } finally { await file.close() } + // Publish exclusively, including when output is a symlink or hard link. + await link(path, output) + } finally { await rm(temporary, { recursive: true, force: true }) } + } + return { + mode: output === undefined ? 'preview' : blockers.length === 0 ? 'copy' : 'refused', + format: 0, + encoding: compressed ? 'zstd' : 'jsonl', + repairedMessages: changes, + repairedDescriptors: descriptors, + expandedToolChunkRows: expandedRows, + expandedToolChunks: expandedChunks, + normalizedToolChunkNames: normalizedNames, + recoveredToolIdentityChains: recovery.chains, + recoveredToolIdentityFields: recovery.fields, + toolIdentityRecoveryRefusals: recovery.refusals, + inputSha256: sha256(original), + outputSha256: blockers.length === 0 ? sha256(result) : null, + originalPreserved: true, + migrationValidated: false, + blockers, + } +} + +async function main(args) { + const usage = 'Usage: dsh-mnemon-repair-session --input FILE [--output NEW_FILE]\nWithout --output, preview the three known Mnemon v0 summary repairs, exact compatible v2 subagent descriptors, packed deltas with empty IDs/names, property-preserving null-to-empty delta names and proven DeepSeek chains with a previously recorded provider ID. Unsupported descriptor/tool shapes are reported with exit status 1 and prevent output. DSH must still validate the repaired copy. Stop DSH and work on a backup copy. The input and existing output files are never overwritten.' + if (args.length === 1 && args[0] === '--help') { console.log(usage); return } + const options = new Map() + for (let index = 0; index < args.length; index += 2) { + const name = args[index] + const value = args[index + 1] + if (!['--input', '--output'].includes(name) || !value || value.startsWith('--') || options.has(name)) throw new Error(usage) + options.set(name, value) + } + if (!options.has('--input')) throw new Error(usage) + const report = await repairLegacySession(options.get('--input'), options.get('--output')) + console.log(JSON.stringify(report, null, 2)) + if (report.blockers.length > 0) { + console.error('Unsupported legacy descriptor/tool data remains; no output written. Review blockers and request recovery from the original DSH writer with a sanitized reproducer.') + process.exitCode = 1 + } +} + +// npm global installs expose this executable through a symlink. Resolve both +// sides while keeping imports from stdin or another entry point side-effect free. +function isEntrypoint() { + try { return process.argv[1] !== undefined && realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url)) } + catch { return false } +} + +if (isEntrypoint()) { + main(process.argv.slice(2)).catch(error => { console.error(error.message); process.exitCode = 1 }) +} diff --git a/dsh/cordis.patch.yml b/dsh/cordis.patch.yml new file mode 100644 index 000000000..9ce6943e1 --- /dev/null +++ b/dsh/cordis.patch.yml @@ -0,0 +1,89 @@ +# Mount the Mnemon bridge into a DSH profile. A profile's final patch may +# replace this config row as a whole; use user settings or environment +# variables for one-field overrides such as cliPath. +- insert: + # Keep the v0.4 `mnemon` Entry as the public configuration target while + # making every v0.5 component inherit its enabled state. The expression + # reads the core row's own flag (rather than its effective state) to avoid + # recursing through this ancestor gate. The group itself prepares the + # Starter's dependency resolution before it imports its children. + - id: mnemon-bundle + name: dsh-mnemon/bundle + group: true + disabled: !!js >- + ((entry) => entry.options.disabled?.__jsExpr + ? Boolean(entry.evaluate(entry.options.disabled.__jsExpr)) + : Boolean(entry.options.disabled)) + ([...loader.entries()].find(entry => entry.options.id === 'mnemon')) + config: + - id: mnemon + # Core/Host and default settings. Sources own their independent Entries. + name: dsh-mnemon + config: + routingGuidance: true + lifecycleEnabled: true + recallMode: guided + writebackMode: guided + idleReviewMs: 30000 + tabEnabled: true + writeEnabled: true + remoteAccess: read-only + timeoutMs: 10000 + defaultRecallLimit: 10 + embedding: + enabled: false + endpoint: http://localhost:11434 + model: nomic-embed-text + recallQuality: + policy: strict-v1 + lowScoreThreshold: 0.25 + highScoreThreshold: 0.6 + candidateMultiplier: 3 + maxMediumResults: 4 + maxUnknownResults: 2 + - id: mnemon-source-runtime + name: dsh-mnemon-source-runtime + - id: mnemon-source-documents + name: dsh-mnemon-source-documents + - id: mnemon-source-memory-spaces + name: dsh-mnemon-source-memory-spaces + config: + # Every default Provider is an + # explicit child. A custom parent composition may choose a subset. + providers: + - use: dsh-mnemon-provider-mnemon-native + instanceId: mnemon-native + - use: dsh-mnemon-provider-openviking + instanceId: openviking + - use: dsh-mnemon-provider-honcho + instanceId: honcho + - use: dsh-mnemon-provider-mem0 + instanceId: mem0 + - use: dsh-mnemon-provider-hindsight + instanceId: hindsight + - use: dsh-mnemon-provider-holographic + instanceId: holographic + - use: dsh-mnemon-provider-retaindb + instanceId: retaindb + - use: dsh-mnemon-provider-byterover + instanceId: byterover + - use: dsh-mnemon-provider-supermemory + instanceId: supermemory + - id: mnemon-strategy-default-three-tier + name: dsh-mnemon-strategy-default-three-tier + # A second main Strategy; memoryView.strategyTypeId selects which one + # composes the View. It stays off until selected. + - id: mnemon-strategy-general + name: dsh-mnemon-strategy-general + disabled: true + # v0.5 ships these narrowly scoped enhancements with the Starter so the + # Settings switches are always available. They remain opt-in. + - id: mnemon-strategy-auto-capture + name: dsh-mnemon-strategy-auto-capture + disabled: true + - id: mnemon-strategy-light-context + name: dsh-mnemon-strategy-light-context + disabled: true + - id: mnemon-strategy-scoped + name: dsh-mnemon-strategy-scoped + disabled: true diff --git a/dsh/docs/README.md b/dsh/docs/README.md new file mode 100644 index 000000000..2f228c9de --- /dev/null +++ b/dsh/docs/README.md @@ -0,0 +1,12 @@ +# dsh-mnemon Documentation / 文档 + +- [English](./en/README.md) +- [简体中文](./zh-CN/README.md) + +Start with [Getting started](./en/guides/getting-started.md), then the [UI guide](./en/guides/ui-guide.md). Existing users should read the [v0.5.17 release notes](./en/releases/v0.5.17.md) and [compatibility and upgrades](./en/reference/compatibility.md); extension authors continue with [Architecture](./en/development/architecture.md) and [Plugin development](./en/development/extensions.md). + +第一次使用请从[快速开始](./zh-CN/guides/getting-started.md)进入,再阅读[界面指南](./zh-CN/guides/ui-guide.md)。已有用户先看 [v0.5.17 发布说明](./zh-CN/releases/v0.5.17.md)与[兼容性与升级](./zh-CN/reference/compatibility.md);扩展作者继续阅读[架构设计](./zh-CN/development/architecture.md)与[插件开发](./zh-CN/development/extensions.md)。 + +The root README stays focused on value, a visual tour and the quick start. Architecture, data model, lifecycle, interfaces, operations and development details live here. + +根目录 README 只保留价值说明、界面概览与快速开始;架构、数据模型、生命周期、接口、运维和开发细节在此维护。 diff --git a/dsh/docs/assets/README.md b/dsh/docs/assets/README.md new file mode 100644 index 000000000..40b5f4e1e --- /dev/null +++ b/dsh/docs/assets/README.md @@ -0,0 +1,22 @@ +# Product media / 产品素材 + +The current product gallery is [v0.5.19 in Light appearance](./webui-v0.5.19/README.md): 50 bilingual screenshots and nine recordings of a seeded, fictional project, captured from a real WebUI with the live DeepSeek model answering. Use it for the README, guides and configuration illustrations. Regenerate it with `node scripts/serve-e2e.mjs --docs-demo --live-model` (or `--docs-demo=en --live-model`) and the steps in its record. + +当前产品图集为 [v0.5.19 浅色界面](./webui-v0.5.19/README.md):基于预置的虚构项目,由真实的 DeepSeek 模型作答,从真实 WebUI 采集 50 张双语截图与九段录制,用于 README、指南与配置说明。可用 `node scripts/serve-e2e.mjs --docs-demo --live-model`(或 `--docs-demo=en --live-model`)按记录中的步骤重新生成。 + +| Directory / 目录 | Purpose / 用途 | +|---|---| +| [webui-v0.5.19](./webui-v0.5.19/README.md) | Current Light UI, seeded fictional data, live model, provenance and hashes / 当前浅色界面、预置虚构数据、真实模型、来源与哈希 | +| [install-v0.5.19](./install-v0.5.19/README.md) | A new user's first run on DSH 0.2: first-run dialogs, Add plugin, Enable now, Status, the first memory with the live model, and a refused older release / 新用户在 DSH 0.2 上的首次使用:首次打开的对话框、添加插件、立即启用、状态页、真实模型写入的第一条记忆,以及被拒绝的旧版本 | +| [webui-v0.5.4](./webui-v0.5.4/README.md) | Historical v0.5.4 Light UI with imported data / v0.5.4 浅色界面与导入数据(历史) | +| [diagrams](./diagrams) | Code-authored architecture diagrams; current terminology retained / 代码绘制的架构图,已采用当前术语 | +| [memory-space-terminology](./memory-space-terminology/README.md) | Historical pre-release terminology and layout evidence, based on v0.5.3 / 基于 v0.5.3 的版本化前用语与布局证据 | +| [showcase](./showcase/README.md) | Historical v0.5.2 screenshots and demo / v0.5.2 历史截图与演示 | +| [screenshots](https://github.com/omdsh-dev/dsh-mnemon/tree/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/screenshots) | Earlier implementation and release screenshots / 较早实现与发布截图 | +| [media](https://github.com/omdsh-dev/dsh-mnemon/tree/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/media) | Earlier promotional image, GIF and video / 较早宣传图片、GIF 与视频 | +| [issues](https://github.com/omdsh-dev/dsh-mnemon/tree/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/issues) | Issue-specific evidence / Issue 专属证据 | +| [PR assets](../pr-assets/README.md) | Dated engineering reports and their original evidence / 带日期的工程记录及原始证据 | + +The [repository audit](./webui-v0.5.4/repository-audit.json) inventories all 142 pre-existing media files and their repository-local references. OCR found legacy labels in 68 of the 135 checked PNG/JPEG files. All 142 original files retain their hashes; current usage guides now point to the Light gallery. A missing local reference is not permission to delete a file: GitHub Issues, PRs and release records may link to it externally. + +[全仓库审计](./webui-v0.5.4/repository-audit.json)记录原有 142 份媒体及仓库内引用;135 张 PNG/JPEG 中有 68 张经 OCR 检出旧标签。142 份原文件的哈希均保持不变,当前使用指南已改用浅色图集。仓库内无引用不代表可以删除,GitHub Issue、PR 和发布记录可能仍有外部引用。 diff --git a/dsh/docs/assets/diagrams/README.md b/dsh/docs/assets/diagrams/README.md new file mode 100644 index 000000000..791813772 --- /dev/null +++ b/dsh/docs/assets/diagrams/README.md @@ -0,0 +1,13 @@ +# Architecture assets / 架构素材 + +The README overview has matching [English](./en/composable-memory.svg) and [Chinese](./zh-CN/composable-memory.svg) editable sources. Each adjacent PNG is a 2× rendered fallback for Markdown consumers that do not render Mermaid or SVG. Labels describe the current public boundary, not a proposed new pipeline. + +[中文](./zh-CN/composable-memory.svg)与[英文](./en/composable-memory.svg)概览各自保留可编辑源,同目录 PNG 是以 2 倍分辨率生成的兼容版本,供不支持 Mermaid 或 SVG 的 Markdown 展示使用。图中描述当前公开边界,并非待实现的新流程。中英文 README 使用各自语言的图,不翻译 API 标识。 + +Detailed ownership, data-flow and sequence diagrams stay in the [English architecture guide](../../en/development/architecture.md) / [中文架构指南](../../zh-CN/development/architecture.md). Keep SVG and PNG synchronized when changing the overview. Older language-specific `memory-system-flow.svg` files remain the sources of the historical three-tier media; they are not unused capture files. + +完整归属、数据流和时序图见上述中英文架构指南。修改概览时同步更新两种语言的 SVG 与 PNG;旧版 `memory-system-flow.svg` 仍是历史三层记忆素材的源文件,应予保留。 + +The Chinese SVG labels use 记忆空间. Their PNGs were regenerated from the editable sources with resvg at 2× on 2026-09-07; the historical flow diagram retains its original v0.2.0 behavior and version label. + +中文 SVG 标签统一使用“记忆空间”;对应 PNG 于 2026-09-07 由 resvg 按 2 倍尺寸重新生成。历史流程图仍保留 v0.2.0 的行为与版本标识。 diff --git a/dsh/docs/assets/diagrams/en/composable-memory.svg b/dsh/docs/assets/diagrams/en/composable-memory.svg new file mode 100644 index 000000000..ccd636dfa --- /dev/null +++ b/dsh/docs/assets/diagrams/en/composable-memory.svg @@ -0,0 +1,36 @@ + + Source + Strategy → View + Sources own memory. A Strategy proposes composition from Source facts. Core validates the proposal and calls Source projection to produce an immutable View. The DSH Host pins it to one executing turn and routes reads and authorized actions to the owning Source. + + + + + + COMPOSABLE MEMORY · ONE VIEW PER EXECUTING TURN + + + + + + + + + SourcesStrategyCoreViewDSH Host + + + Runtime · DocumentsMemory Spaces · Custom + Select · budgetCompose · guide + Validate proposalCall Source projection + ContextRoutes · actions + Pin to turnPresent to LLM + + + + + + factsspec + + Reads and authorized actions return to their Source. A new turn can observe a new revision. + Provider children belong to Memory Spaces, not to Core. + + diff --git a/dsh/docs/assets/diagrams/en/memory-system-flow.svg b/dsh/docs/assets/diagrams/en/memory-system-flow.svg new file mode 100644 index 000000000..cf99ba4cc --- /dev/null +++ b/dsh/docs/assets/diagrams/en/memory-system-flow.svg @@ -0,0 +1,59 @@ + + dsh-mnemon execution flow from click to receipt + Separates deterministic reads, Agent Query, supervised writes, and maintenance/archive paths, including concurrency, isolation, and progressive feedback. + + + + + + + From one click to verifiable data and receipts + Deterministic work passes through the Host; judgment starts clean task Agents; every page is concurrent, progressive, and failure-isolated. + v0.2.0 live paths + + + + 01Deterministic reads · render as results arrive + Status / Search / Content / EntitiesStarts immediately, read-only, cancellable + Concurrent Host dispatchTimeout, schema, cache, error isolation + Fastest provider-native readEvery active instance returns independently + Progressive page updateOne spinner · local status · no page block + + + + + + 02Agent Query · evidence first, answer second + Click Agent QueryFocused question + optional scope + Recall bounded evidenceRank fusion retains Provider source + Independent top-level task AgentNo session history, no Mnemon tools + Answer beside evidenceRead-only; raw recall survives failure + + + + + + 03Supervised writes · user confirms, Agent judges, Host commits + Remember / Save to memoryEdit candidate; no write before confirmation + Qualify · dedupe · distill · routeHard rules first, model only if needed + Host permission boundaryTool allowlist · path · lock · revision + Target Provider + receiptWrite, skip, and failure are all explicit + + + + + + 04Maintenance and archive · isolate by object, never share failure + AI metadata / ArchiveSelect spaces or confirm one DocumentNo dependency on a main session + One isolated task per objectFast sample, or cold-reference firstAsync state never overlaps + Host verifies the resultMetadata stays in the local catalogArchive passes a revision fence + Card updates in placeAnimation, success, or local errorFailure keeps dialog and layout stable + + + + Deterministic Host path + Independent task Agent path + One full sync on first visit · then reconnect by Provider + ID on demand + diff --git a/dsh/docs/assets/diagrams/zh-CN/composable-memory.svg b/dsh/docs/assets/diagrams/zh-CN/composable-memory.svg new file mode 100644 index 000000000..f73bbc8e1 --- /dev/null +++ b/dsh/docs/assets/diagrams/zh-CN/composable-memory.svg @@ -0,0 +1,36 @@ + + 记忆来源 + 组合策略 → 上下文视图 + Source 拥有记忆;Strategy 根据来源事实提议组合;Core 校验提案并调用 Source 投影,生成不可变 View。DSH 宿主将它固定到执行回合,并把读取和已授权的写入交回所属 Source。 + + + + + + 可组合记忆 · 每个执行回合只有一个视图 + + + + + + + + + Source · 来源Strategy · 策略Core · 核心View · 视图DSH 宿主 + + + 运行时 · 档案记忆空间 · 自定义 + 选择 · 预算组合 · 使用指引 + 校验组合提案调用来源投影 + 上下文读取 · 写入操作 + 固定到回合呈现给 LLM + + + + + + 事实提案 + + 读取与已授权写入交回所属 Source;新的回合可以读取新的修订。 + Provider 子模块归记忆空间 Source 所有,不属于 Core。 + + diff --git a/dsh/docs/assets/diagrams/zh-CN/memory-system-flow.svg b/dsh/docs/assets/diagrams/zh-CN/memory-system-flow.svg new file mode 100644 index 000000000..85d2be86b --- /dev/null +++ b/dsh/docs/assets/diagrams/zh-CN/memory-system-flow.svg @@ -0,0 +1,59 @@ + + dsh-mnemon 从点击到回执的执行流 + 区分确定性只读、Agent 查询、受监督写入和维护归档四条路径,并说明并发、隔离和渐进反馈。 + + + + + + + 从一次点击,到可验证的数据与回执 + 确定性操作直接经过 Host;需要判断的操作启动干净的独立任务 Agent;所有页面并发、渐进、局部失败隔离。 + v0.2.0 实际路径 + + + + 01确定性只读 · 结果到达即展示 + 状态 / 直接检索 / 内容 / 实体点击即发起,只读且可取消 + Host 并发调度超时、schema、缓存与错误隔离 + Provider 最快原生读取每个已激活实例独立返回 + 页面渐进更新一个转圈 · 局部状态 · 不阻塞整页 + + + + + + 02Agent 查询 · 先取证据,再组织答案 + 点击 Agent 查询聚焦问题 + 可选范围与策略 + 并发召回有界证据排名融合,保留 Provider 来源 + 独立顶层任务 Agent无会话历史、无 Mnemon 工具 + 答案与证据同屏只读 · 失败保留原始召回 + + + + + + 03受监督写入 · 用户确认、Agent 判断、Host 落盘 + 沉淀记忆 / 存入记忆编辑候选,确认前不启动写入 + 判断 · 查重 · 提炼 · 选路硬规则优先,必要时模型决策 + Host 权限边界工具白名单 · 路径 · 锁 · revision + 目标 Provider + 回执写入、跳过或失败都明确可见 + + + + + + 04维护与归档 · 按对象隔离,不共享失败 + AI 元信息 / 归档多选记忆空间,或确认一个档案不依赖当前主 session + 每个对象独立任务最快采样,或先生成冷引用异步执行,互不覆盖状态 + Host 验证结果元信息只写本地目录归档需通过 revision fence + 卡片原位更新动画、成功或局部错误失败不关闭窗口、不挤动布局 + + + + 确定性 Host 路径 + 独立任务 Agent 路径 + 首次进入全量同步 · 之后按 Provider + ID 按需重连 + diff --git a/dsh/docs/assets/install-v0.5.19/README.md b/dsh/docs/assets/install-v0.5.19/README.md new file mode 100644 index 000000000..ead49c56c --- /dev/null +++ b/dsh/docs/assets/install-v0.5.19/README.md @@ -0,0 +1,52 @@ +# Installation gallery, v0.5.19 / 安装图集,v0.5.19 + +A new user's first run, captured on **2026-09-28/29 (Asia/Shanghai)** from real DSH 0.2.0-rc.1 WebUIs in **light** appearance, in English and Chinese: DSH's first-run dialogs, installing and enabling dsh-mnemon on the Plugins page, Status, and a first memory written by the **live DeepSeek model**. [English guide](../../en/guides/installation.md) · [中文指南](../../zh-CN/guides/installation.md) · [Media index](../README.md) · [Manifest](./manifest.json) + +新用户的首次使用,于 **2026-09-28/29(Asia/Shanghai)**从真实的 DSH 0.2.0-rc.1 WebUI 采集,中英文均为**浅色**界面:DSH 首次打开时的对话框、在插件页安装并启用 dsh-mnemon、状态页,以及由**真实的 DeepSeek 模型**写入的第一条记忆。 + +![Add plugin, type dsh-mnemon, Install, Enable now; the Memory System appears in the sidebar](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/en/install.gif) + +## Recordings / 录制 + +| Flow / 流程 | English | 简体中文 | +|---|---|---| +| Install and enable: Plugins, Add plugin, Install, Enable now / 安装并启用:插件、添加插件、安装、立即启用 | [install.mp4](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/en/install.mp4) · [GIF](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/en/install.gif) · 16 s | [install.mp4](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/zh-CN/install.mp4) · [GIF](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/zh-CN/install.gif) · 16 秒 | +| First memory: a sentence to remember, the turn memory bar, Runtime memory / 第一条记忆:一句要记住的话、回合记忆栏、运行时记忆 | [first-memory.mp4](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/en/first-memory.mp4) · [GIF](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/en/first-memory.gif) · 12 s | [first-memory.mp4](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/zh-CN/first-memory.mp4) · [GIF](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/zh-CN/first-memory.gif) · 10 秒 | + +Frames are Chrome screencast frames played at their own timestamps (H.264, 30 fps, no audio); the GIFs are 880 px at 8 fps. The install wait and the model's reply play 4x, with a badge in the lower right corner; pauses while screenshots were taken are left out. A drawn cursor ring shows where each click lands; there is no composited UI. + +画面来自 Chrome 屏幕录制,按原始时间戳播放(H.264,30 fps,无音轨);GIF 为 880 像素、8 fps。安装等待与模型回复以 4 倍速播放,右下角有标记;截图时的停顿已去掉。画面中绘制的光标圆环指示点击位置,没有拼接的界面。 + +## Screens / 截图 + +| Step / 步骤 | English | 简体中文 | +|---|---|---| +| DSH 0.2 Preview Notice / DSH 0.2 预览版说明 | [install-01-preview](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/en/install-01-preview.jpg) | [install-01-preview](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/zh-CN/install-01-preview.jpg) | +| Add an API key, or configure later / 添加 API Key,或稍后配置 | [install-02-api-key](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/en/install-02-api-key.jpg) | [install-02-api-key](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/zh-CN/install-02-api-key.jpg) | +| Plugins page / 插件页 | [install-03-plugins](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/en/install-03-plugins.jpg) | [install-03-plugins](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/zh-CN/install-03-plugins.jpg) | +| Add plugin with dsh-mnemon typed / 添加插件并输入 dsh-mnemon | [install-04-add](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/en/install-04-add.jpg) | [install-04-add](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/zh-CN/install-04-add.jpg) | +| Installed, version 0.5.19 / 已安装,版本 0.5.19 | [install-05-installed](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/en/install-05-installed.jpg) | [install-05-installed](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/zh-CN/install-05-installed.jpg) | +| Enabled; Memory System in the sidebar / 已启用,侧栏出现记忆系统 | [install-06-enabled](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/en/install-06-enabled.jpg) | [install-06-enabled](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/zh-CN/install-06-enabled.jpg) | +| Status, top of the page / 状态页上部 | [install-07-status](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/en/install-07-status.jpg) | [install-07-status](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/zh-CN/install-07-status.jpg) | +| The reply and what the turn wrote / 回复及本回合写入 | [first-01-reply](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/en/first-01-reply.jpg) | [first-01-reply](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/zh-CN/first-01-reply.jpg) | +| The entry in Runtime memory / 运行时记忆中的条目 | [first-02-runtime](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/en/first-02-runtime.jpg) | [first-02-runtime](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/zh-CN/first-02-runtime.jpg) | +| DSH 0.2 refusing dsh-mnemon 0.5.18 / DSH 0.2 拒绝 dsh-mnemon 0.5.18 | [trouble-incompatible](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/en/trouble-incompatible.jpg) | [trouble-incompatible](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/zh-CN/trouble-incompatible.jpg) | +| `dsh-mnemon/starter` turned off: no Memory System / `dsh-mnemon/starter` 被关闭:没有记忆系统 | [trouble-starter-off](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/en/trouble-starter-off.jpg) | [trouble-starter-off](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/zh-CN/trouble-starter-off.jpg) | + +Screens are captured at 1280 × 800 and 2x, and stored 1920 px wide. The Status screen stops above Storage Domains, whose paths belong to the capture machine. + +截图以 1280 × 800、2 倍像素采集,保存为 1920 像素宽。状态页截图止于“存储域”之上,因为那里的路径属于采集机器。 + +## Environment and data / 环境与数据 + +| Item / 项目 | Capture environment / 采集环境 | +|---|---| +| Product / 产品 | dsh-mnemon 0.5.19 as `pnpm release:version` builds it from `b5f7914b`, with its pinned plugins / 由 `pnpm release:version` 从 `b5f7914b` 构建的 dsh-mnemon 0.5.19 及其固定的插件版本 | +| Host / 宿主 | Published DSH 0.2.0-rc.1, installed with npm into an isolated prefix; a fresh DSH home, user home and pnpm store for every capture; no Host source modifications / 正式 DSH 0.2.0-rc.1,用 npm 安装到隔离目录;每次采集使用全新的 DSH home、用户目录与 pnpm store;未修改宿主源码 | +| Tools / 工具 | macOS, Node.js 24.19, pnpm 11.19, Mnemon CLI 0.2.7 | +| Model / 模型 | DeepSeek API, `deepseek-flash` (DeepSeek-V41-Flash in the WebUI), for the first conversation only / 仅用于第一次对话 | +| Browser / 浏览器 | Headless Google Chrome over the DevTools protocol, light color scheme / 通过 DevTools 协议驱动的无头 Chrome,浅色 | + +0.5.19 was not yet on npm during the capture. A local registry served npm's real metadata for every dsh-mnemon package and added this revision's release build as the newest version, published a week earlier; a small pnpm wrapper sent the requests for the install source DSH chose to that registry. The dialogs show the source DSH picked on its own: its speed check chose the mainland China mirror for the Chinese capture, and the English capture remembered npm's own registry, which a user outside mainland China gets. The refused-install screen used npm's real metadata only, where 0.5.18 was the newest release. The Starter-off screens came later, at 02:22 on 2026-09-29: npm's published dsh-mnemon 0.5.19 in a fresh DSH home, with the `mnemon-starter` row turned off in the profile patch the way the Plugins page stores it. The API key reached DSH through the environment for the live conversation and was never typed or shown. The first message is typed; the model decides to write runtime memory, and its reply and entries are its own, so another run words them differently. No personal memory or credentials appear. + +采集时 0.5.19 尚未发布到 npm。本地 registry 提供 npm 上每个 dsh-mnemon 包的真实元数据,并把本修订的发布构建作为最新版本加入,发布时间设为一周前;一个小的 pnpm 包装把 DSH 所选安装源的请求转到该 registry。对话框显示的是 DSH 自己选择的安装源:中文采集中测速选择了中国大陆镜像源,英文采集沿用了 npm 官方源,也就是中国大陆以外的用户得到的默认值。安装被拒的截图只使用 npm 的真实元数据,当时最新版本为 0.5.18。Starter 关闭时的截图于 2026-09-29 02:22 另行采集:全新 DSH home 中安装 npm 上已发布的 dsh-mnemon 0.5.19,并按插件页的保存方式在 profile patch 中关闭 `mnemon-starter` 一行。第一次对话所需的 API Key 通过环境变量交给 DSH,从未输入或显示。第一条消息是逐字输入的;模型自己决定写入运行时记忆,回复与条目都由模型生成,再次运行时措辞会不同。素材中没有个人记忆或凭据。 diff --git a/dsh/docs/assets/install-v0.5.19/manifest.json b/dsh/docs/assets/install-v0.5.19/manifest.json new file mode 100644 index 000000000..17eb0430e --- /dev/null +++ b/dsh/docs/assets/install-v0.5.19/manifest.json @@ -0,0 +1,412 @@ +{ + "schemaVersion": 1, + "productVersion": "0.5.19", + "packageVersionAtCapture": "0.5.19", + "sourceRevision": "b5f7914bb7fa6bfbd0bb60b5ccf65b757e1f1514", + "captureWindow": "2026-09-28 23:58 – 2026-09-29 00:03", + "captureTimezone": "Asia/Shanghai", + "theme": "light", + "hostVersion": "0.2.0-rc.1", + "host": "Published @deepseek-ai/dsh 0.2.0-rc.1 installed with npm into an isolated prefix; a fresh DSH home, user home and pnpm store for every capture; no Host source modifications", + "mnemonCliVersion": "0.2.7", + "pnpmVersion": "11.19.0", + "model": "DeepSeek API, deepseek-flash (shown as DeepSeek-V41-Flash)", + "browser": "Headless Google Chrome driven over the DevTools protocol", + "packages": "The 18 packages `pnpm release:version` produces from this revision (dsh-mnemon 0.5.19 and its pinned plugins), served before they were on npm by a local registry that adds them to npm's real metadata as the newest release; the Add plugin dialog showed the install source DSH chose (Chinese: its speed check picked the mainland China mirror; English: npm's own registry, the choice a user outside mainland China gets), and a small pnpm wrapper sent that source's requests to the local registry. The refused-install screens used npm's real metadata only.", + "starterOffScreens": "Captured 2026-09-29 02:22 (Asia/Shanghai) on the same published DSH 0.2.0-rc.1 with npm's published dsh-mnemon 0.5.19, installed with `dsh plugin --profile web add dsh-mnemon@0.5.19` into a fresh DSH home, after the `mnemon-starter` row was turned off in the profile patch (`disabled: true`, as the Plugins page stores it). DSH started with the warning `mnemon-bundle (cordis:group): pending (waiting for service: mnemonStarterReady)` and no Memory System; turning the switch back on brought the Memory System up without a restart, on DSH 0.1.7-rc.2 as well.", + "conversation": "The first message is typed; the live model decides to write runtime memory, and the reply and entries are the model's own. Another run words them differently.", + "recordingNote": "Chrome screencast frames played at their own timestamps (H.264 CRF 27, 30 fps, no audio; GIF 880 px, 8 fps). The install wait plays 4x and the reply wait 4x, with an on-screen badge; pauses while screenshots were taken are left out. A drawn cursor ring shows where each click lands.", + "data": { + "personalData": false, + "credentials": false, + "apiKey": "passed to DSH through the environment for the live conversation; never typed or shown" + }, + "runs": { + "zh-CN": { + "installSource": "中国大陆镜像源", + "installSeconds": 4, + "installResult": "已安装 dsh-mnemon Composable, view-based memory for DeepSeek Harness. Pluggable sources and strategies, with layered memory out of the box. 版本 0.5.19 查看安装详情 立即启用", + "turnMemory": "本回合记忆 写入 1", + "turnItems": [ + "运行时记忆更新 负责 Lumen 项目的结账页(checkout)性能;偏好简洁回答。" + ], + "replySeconds": 5, + "refusedRelease": "dsh-mnemon@0.5.18" + }, + "en": { + "installSource": "Official npm registry", + "installSeconds": 4, + "installResult": "Installed dsh-mnemon Composable, view-based memory for DeepSeek Harness. Pluggable sources and strategies, with layered memory out of the box. Version 0.5.19 Show install details Enable now", + "turnMemory": "Turn memory wrote 2", + "turnItems": [ + "Runtime memory update ×2 Prefers short, concise answers. User owns checkout performance on the Lumen project." + ], + "replySeconds": 6, + "refusedRelease": "dsh-mnemon@0.5.18" + } + }, + "files": [ + { + "file": "zh-CN/install-01-preview.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 94048, + "sha256": "a6294818a2e6ad4097916cd26b8b3903252040652cb3b90969f7cec06dca3095" + }, + { + "file": "zh-CN/install-02-api-key.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 63459, + "sha256": "b84aaf04739eb6bcdcfba18d933c6721f42fb1418430858b0a9ce85c58c08bf2" + }, + { + "file": "zh-CN/install-03-plugins.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 96651, + "sha256": "438433750e252381b0de09710dc5ed25ac288d42d32b086f7e3ee913a648c05b" + }, + { + "file": "zh-CN/install-04-add.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 102211, + "sha256": "16f17fb7d9b93b1c475b8387824ebf8073d3cc693c5064e20fcd0b01eec17806" + }, + { + "file": "zh-CN/install-05-installed.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 95022, + "sha256": "462311d98e3c2c5bb1b1284270f1110a803f082447d203a15baf595c4ff546f8" + }, + { + "file": "zh-CN/install-06-enabled.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 106446, + "sha256": "a77f0e766d1c6dff2a4d077f6ddef98157a01dc215e106e87e515d78e632370f" + }, + { + "file": "zh-CN/install-07-status.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "crop": "top of the viewport, above Storage Domains", + "width": 1920, + "height": 720, + "bytes": 87737, + "sha256": "76cee542f000838e5ba2f598bed5ea6b31579da06f90e35463698b7a648a9f1a" + }, + { + "file": "zh-CN/first-01-reply.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 91385, + "sha256": "43e44fa1172669dd3bcd4faa68f2015c7e2802df512717d6dfb7c26ca6b2bcdc" + }, + { + "file": "zh-CN/first-02-runtime.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 125248, + "sha256": "ae6f5e88eef82c06035a954e46bb09811cc274ccbf6ccac655507cbf794fa40f" + }, + { + "file": "zh-CN/trouble-incompatible.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 101469, + "sha256": "33416266852e81efab53fcc82c683cca38749077258d3a3f17233cea0ff8fb93" + }, + { + "file": "zh-CN/trouble-starter-off.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 105452, + "sha256": "2f9bead7d1c853a0347e144edf97f634eddf2386f98c88c64630d5d180330238" + }, + { + "file": "zh-CN/install.mp4", + "kind": "recording", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "width": 1280, + "height": 800, + "durationSeconds": 15.97, + "bytes": 279580, + "sha256": "caef774ba096a56b77042f60b9450f55c27aaba2099784b00dd5261cc84a1737" + }, + { + "file": "zh-CN/install.gif", + "kind": "animation", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "width": 880, + "height": 550, + "durationSeconds": 16.01, + "bytes": 548835, + "sha256": "173dce7508b88de988964a86ba85c5e33ff86fef1bed27eae1abfe1e53775266" + }, + { + "file": "zh-CN/first-memory.mp4", + "kind": "recording", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "width": 1280, + "height": 800, + "durationSeconds": 9.97, + "bytes": 142694, + "sha256": "5ae57c276980f9785cca6027365fe98710c809aba071275aef58a3dfac3c6c0f" + }, + { + "file": "zh-CN/first-memory.gif", + "kind": "animation", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "width": 880, + "height": 550, + "durationSeconds": 10.01, + "bytes": 291336, + "sha256": "f102ce3e84c701ea3a7fc897ab3b26936b47965e8f4b1eab9e2fa4d450ffd6c1" + }, + { + "file": "en/install-01-preview.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 109505, + "sha256": "c5f90712b8ea664c31cd6091a72e981901f0feb6c49e17ad36a22b301007a777" + }, + { + "file": "en/install-02-api-key.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 67982, + "sha256": "d247952f5f8288078ef4027b405b3745531851c2eb24621889be26782fc87d63" + }, + { + "file": "en/install-03-plugins.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 109848, + "sha256": "576ac9bbb2e5a5838786a6635fa92c1f5303930de530adf6691405d921cd5cc3" + }, + { + "file": "en/install-04-add.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 108557, + "sha256": "c63f19a1231f91aa8f0a81ae85e3a3d62af821fe61f87c0ddce40e322c9e2e22" + }, + { + "file": "en/install-05-installed.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 103005, + "sha256": "7e7fa9720aa2041969d22f112ab0242d355389665f7a68012ca926c2f2a5004d" + }, + { + "file": "en/install-06-enabled.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 119601, + "sha256": "6cc74564ed32a95e08ce5ddc1cdd991ec12e03c3430698c1e3d75d7aed186aa0" + }, + { + "file": "en/install-07-status.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "crop": "top of the viewport, above Storage Domains", + "width": 1920, + "height": 720, + "bytes": 98027, + "sha256": "34b6d3b3c4b8ae0251e9d51d798a15311f1f19486896e17ed9035dccf6a309e0" + }, + { + "file": "en/first-01-reply.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 101179, + "sha256": "a7939f3d2de907aab256ece863c4fcf94920e77fe049c94334da84c98b5ab073" + }, + { + "file": "en/first-02-runtime.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 144446, + "sha256": "c8a33e5b26bfe678a997bb4c95d6cecc99296be60a3c758dfb126653f7eb5e21" + }, + { + "file": "en/trouble-incompatible.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 110193, + "sha256": "45fa1193bbb9169b908d19b1edb7b58b4b5f222fd757f965ff11beddcfab26bc" + }, + { + "file": "en/trouble-starter-off.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 111225, + "sha256": "b6705164ac4bbca00840f17d474f70decd8e3d41f81f203d3dd5f6e2f2d29d8d" + }, + { + "file": "en/install.mp4", + "kind": "recording", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "width": 1280, + "height": 800, + "durationSeconds": 15.97, + "bytes": 284134, + "sha256": "e5af5604284616bab82159031045051de864e1de391f2a64046087d5a1844529" + }, + { + "file": "en/install.gif", + "kind": "animation", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "width": 880, + "height": 550, + "durationSeconds": 16.01, + "bytes": 537520, + "sha256": "84e3535a7e96e88996531d3503ae40de2a491af2384d17b6da190a92f2eecbf5" + }, + { + "file": "en/first-memory.mp4", + "kind": "recording", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "width": 1280, + "height": 800, + "durationSeconds": 12, + "bytes": 161581, + "sha256": "48486b723055f7ac41da56b64830a0d047ca44bf14160733ecad308fb09cd460" + }, + { + "file": "en/first-memory.gif", + "kind": "animation", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "width": 880, + "height": 550, + "durationSeconds": 12.01, + "bytes": 321657, + "sha256": "fff27d24172d79318e17dffef2945bf33a234c8b5ef8edb708ce5c99ba9656ab" + } + ] +} diff --git a/dsh/docs/assets/memory-space-terminology/README.md b/dsh/docs/assets/memory-space-terminology/README.md new file mode 100644 index 000000000..19bbd92e7 --- /dev/null +++ b/dsh/docs/assets/memory-space-terminology/README.md @@ -0,0 +1,42 @@ +# Memory space terminology UI evidence + +This is the pre-release terminology evidence. Current Light screenshots and bilingual recordings using imported data are in [the v0.5.4 gallery](../webui-v0.5.4/README.md). + +本目录是版本化前的用语改动证据。当前浅色截图与双语录制已移至 [v0.5.4 图集](../webui-v0.5.4/README.md),使用备份导入数据。 + +Captured on 2026-09-07 from the real local DSH WebUI, using the `codex/memory-space-terminology` working tree based on v0.5.3 (`7e6216094a9361e9abb091a5edb2b83cb7ed05df`). The version badge identifies the base package version. These captures were made before versioning and include the terminology update released in v0.5.4. + +## Environment and scope + +- Node.js 25.1.0, DSH 0.1.2-rc.1, and the existing Mnemon CLI 0.2.7 on macOS. +- `node scripts/serve-e2e.mjs` supplied a disposable DSH Profile, workspace, and data directory. The fixture linked the local Starter, Sources, and Providers. +- Synthetic Atlas data: one native memory space, two memories, one Runtime item, and one managed Document. Some earlier screenshots precede creation of the Runtime item and Document. User-authored sample content is intentionally bilingual and does not change with the interface language. +- Desktop viewport: 1365 × 1000. Narrow viewport: 390 × 844. +- Screenshots are unchanged JPEG bytes returned by browser capture. [The manifest](./manifest.json) records their sizes and SHA-256 hashes. [Layout measurements](./layout-checks.json) record the final narrow-layout checks. +- No production memory, cloud Provider calls, or LLM requests were used. Creation, editing, activation, browsing, language switching, and form cancellation were exercised; AI metadata and semantic archiving were not invoked. The disposable fixture was removed after verification. + +## Captures + +| Surface | English | 中文 | +|---|---|---| +| Status | [Status](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/memory-space-terminology/en/status.jpg) | [状态](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/memory-space-terminology/zh-CN/status.jpg) | +| Runtime | [Runtime](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/memory-space-terminology/en/runtime.jpg) | [运行时](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/memory-space-terminology/zh-CN/runtime.jpg) | +| Documents | [Documents](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/memory-space-terminology/en/documents.jpg) | [档案](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/memory-space-terminology/zh-CN/documents.jpg) | +| Memory Spaces | [Directory](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/memory-space-terminology/en/spaces.jpg) | [记忆空间目录](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/memory-space-terminology/zh-CN/spaces.jpg) | +| Content | [Content](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/memory-space-terminology/en/content.jpg) | [内容](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/memory-space-terminology/zh-CN/content.jpg) | +| Create | [Form](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/memory-space-terminology/en/create.jpg) | [创建表单](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/memory-space-terminology/zh-CN/create.jpg) | +| Settings | [Layers and enhancements](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/memory-space-terminology/en/enhancements.jpg) | [记忆层与增强](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/memory-space-terminology/zh-CN/enhancements.jpg) | +| Narrow directory | [390 px directory](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/memory-space-terminology/en/spaces-mobile.jpg) | [390 像素目录](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/memory-space-terminology/zh-CN/spaces-mobile.jpg) | +| Narrow create | [390 px form](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/memory-space-terminology/en/create-mobile.jpg) | [390 像素表单](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/memory-space-terminology/zh-CN/create-mobile.jpg) | + +## Layout findings + +The longer labels exposed a directory header that exceeded its available width. The header now has a bounded grid column, the directory path truncates within that column, and action buttons wrap. Provider metadata badges also wrap. Compact navigation uses tighter spacing, and the storage badge can move below the title. Both locales keep the directory and page canvas within their widths, with creation buttons inside the viewport after the sheet animation completes. + +## 中文说明 + +本组截图来自真实 DSH WebUI 与临时测试数据,展示基于 v0.5.3 的用语更新;界面统一使用“记忆空间 / memory space”。截图在版本化前采集,包版本号仍是基础版本;所展示的用语与布局改动纳入 v0.5.4。 + +桌面与窄屏分别按 1365 × 1000、390 × 844 检查。实测覆盖创建、编辑、激活、浏览、语言切换和取消表单;没有调用模型、云端 Provider 或生产记忆。窄屏目录按钮、Provider 状态标签和顶部存储标识可以换行,创建表单的底部按钮保持可见。截图保留浏览器原始 JPEG 数据,哈希与布局测量见上方记录。 + +The [v0.5.2 showcase](../showcase/README.md) remains historical evidence with its original screenshot pixels and recording. diff --git a/dsh/docs/assets/memory-space-terminology/layout-checks.json b/dsh/docs/assets/memory-space-terminology/layout-checks.json new file mode 100644 index 000000000..8b4095621 --- /dev/null +++ b/dsh/docs/assets/memory-space-terminology/layout-checks.json @@ -0,0 +1,106 @@ +[ + { + "locale": "en", + "page": "spaces", + "viewport": 390, + "oldTerm": false, + "regions": [ + { + "class": "IIa07q_nav _Bh55G_nav", + "clientWidth": 310, + "scrollWidth": 312 + }, + { + "class": "IIa07q_canvas _Bh55G_canvas", + "clientWidth": 326, + "scrollWidth": 326 + }, + { + "class": "_Bh55G_memoryTabs", + "clientWidth": 246, + "scrollWidth": 246 + }, + { + "class": "IIa07q_bodyDirectory", + "clientWidth": 274, + "scrollWidth": 274 + } + ], + "scrollWidth": 390, + "width": 390 + }, + { + "locale": "en", + "page": "create", + "viewport": 390, + "buttons": [ + { + "bottom": 834, + "text": "Cancel" + }, + { + "bottom": 834, + "text": "Create" + } + ], + "dialog": [ + { + "clientWidth": 388, + "scrollWidth": 388 + } + ], + "height": 844 + }, + { + "locale": "zh", + "page": "spaces", + "viewport": 390, + "oldTerm": false, + "regions": [ + { + "class": "IIa07q_nav _Bh55G_nav", + "clientWidth": 310, + "scrollWidth": 310 + }, + { + "class": "IIa07q_canvas _Bh55G_canvas", + "clientWidth": 326, + "scrollWidth": 326 + }, + { + "class": "_Bh55G_memoryTabs", + "clientWidth": 246, + "scrollWidth": 246 + }, + { + "class": "IIa07q_bodyDirectory", + "clientWidth": 274, + "scrollWidth": 274 + } + ], + "scrollWidth": 390, + "width": 390 + }, + { + "locale": "zh", + "page": "create", + "viewport": 390, + "buttons": [ + { + "bottom": 834, + "text": "取消" + }, + { + "bottom": 834, + "text": "创建" + } + ], + "dialog": [ + { + "clientWidth": 388, + "scrollWidth": 388 + } + ], + "height": 844 + } +] diff --git a/dsh/docs/assets/memory-space-terminology/manifest.json b/dsh/docs/assets/memory-space-terminology/manifest.json new file mode 100644 index 000000000..9c8fb04c3 --- /dev/null +++ b/dsh/docs/assets/memory-space-terminology/manifest.json @@ -0,0 +1,110 @@ +[ + { + "file": "en/content.jpg", + "format": "jpeg", + "bytes": 99265, + "sha256": "276ba55eca34cb166f77181bd68b89ff957ef0e11ba2068cb9aa7babb29ce33c" + }, + { + "file": "en/create-mobile.jpg", + "format": "jpeg", + "bytes": 41643, + "sha256": "ac6de77aa745ac6f10f9631266751de357e41ae3b6bdb87627cc5159be2f18a5" + }, + { + "file": "en/create.jpg", + "format": "jpeg", + "bytes": 88988, + "sha256": "65779522334ac63c88bc5553848c0d018781fa7980290a407ef8266ad6ccf215" + }, + { + "file": "en/documents.jpg", + "format": "jpeg", + "bytes": 104792, + "sha256": "9c4b99fd14d5bcfe2404d33b564f2bb20a728ba9426d02b11dcb3ff81d5a0b1d" + }, + { + "file": "en/enhancements.jpg", + "format": "jpeg", + "bytes": 74775, + "sha256": "a263f91445e5f4b3e70acab8696f6837c5776e3a8cfb1a5251a15f60e3754858" + }, + { + "file": "en/runtime.jpg", + "format": "jpeg", + "bytes": 81166, + "sha256": "a4a121d100ad4b420f66620af5f8f89205f098732effd64bf928fefe6c6a5cc1" + }, + { + "file": "en/spaces-mobile.jpg", + "format": "jpeg", + "bytes": 49850, + "sha256": "98f76c0f693c187a0ae95c55a5e984e9f4c94e2f27057dcd9da6d5a6860e3614" + }, + { + "file": "en/spaces.jpg", + "format": "jpeg", + "bytes": 100721, + "sha256": "a6e922edcf9eb3c479351f524c507ab39db725bf49910a9b6436b6d195ca6983" + }, + { + "file": "en/status.jpg", + "format": "jpeg", + "bytes": 87259, + "sha256": "85ebddd17f501823177cdca120f05e66582c1dedc0eb9d2aea939bf635b83818" + }, + { + "file": "zh-CN/content.jpg", + "format": "jpeg", + "bytes": 87440, + "sha256": "c50d34780f6df8268bc3b31f5f86a4c68d4685e06d75d03969d0f8b8f216f563" + }, + { + "file": "zh-CN/create-mobile.jpg", + "format": "jpeg", + "bytes": 38565, + "sha256": "7c8cb3a13b8fdf785b46827874058ff9f36bd8ae68a4a236c2c2d671c2cba3d8" + }, + { + "file": "zh-CN/create.jpg", + "format": "jpeg", + "bytes": 77940, + "sha256": "02d9548aa645fc71be0ac5a43e05cd609343baa83b74b51437f9eec4b3f4c871" + }, + { + "file": "zh-CN/documents.jpg", + "format": "jpeg", + "bytes": 99636, + "sha256": "aa2f16a1753111a124ed640d616b923aec9821f0f568678e0bfed12f85ec8da5" + }, + { + "file": "zh-CN/enhancements.jpg", + "format": "jpeg", + "bytes": 72146, + "sha256": "c67a5fbf4354855e05f33eba9adc1f3c46a624beefb372495e16139cb97307c5" + }, + { + "file": "zh-CN/runtime.jpg", + "format": "jpeg", + "bytes": 75382, + "sha256": "f600d15ac73b86489947f68d181ba6dc54bd35e14ea541dedfed4d27d724de81" + }, + { + "file": "zh-CN/spaces-mobile.jpg", + "format": "jpeg", + "bytes": 45334, + "sha256": "05915d9824173b22d38709c097b314c3f56936e4a750e3947b2777780c73368f" + }, + { + "file": "zh-CN/spaces.jpg", + "format": "jpeg", + "bytes": 89662, + "sha256": "0a0f62ead04951da1c0ed8c7cfffc0187fbd6f42edd7125cffb231bda2bb795c" + }, + { + "file": "zh-CN/status.jpg", + "format": "jpeg", + "bytes": 81878, + "sha256": "bc6c4b97d73f0a0e0832c9e85082e5bf8a6209d3478f9ed7cbfb9a1cd9c8e9a8" + } +] diff --git a/dsh/docs/assets/showcase/README.md b/dsh/docs/assets/showcase/README.md new file mode 100644 index 000000000..eb3cf4fef --- /dev/null +++ b/dsh/docs/assets/showcase/README.md @@ -0,0 +1,55 @@ +# Historical v0.5.2 WebUI showcase / v0.5.2 历史界面展示 + +This is the historical v0.5.2 showcase. Current **Light / 浅色** screenshots and bilingual recordings are in [the v0.5.4 gallery](../webui-v0.5.4/README.md). 当前素材入口为 v0.5.4 浅色图集;本目录保留历史截图与录制。 + + +Captured on **2026-09-06 (Asia/Shanghai)** from a real, isolated local DSH Web server. This is product documentation, not an LLM benchmark. + +## Environment and scope + +| Item | Capture environment | +|---|---| +| Production source | `d94dfecddc0976b9d0620d1e0e387a5340bd6526` (main after PR #190); documentation edits do not change it | +| Starter and official plugins | `0.5.2`, locally built and linked by `pnpm e2e:serve` | +| DSH | Published `0.1.2-rc.1` packages; no DSH source modification | +| Browser | Chrome `152.0.7977.76`, Playwright-controlled real WebUI | +| Desktop | `1440 × 960`, device scale 1; English and Chinese | +| Narrow layout | `390 × 844` browser viewport; clipping observed, not a passing mobile test | +| Mnemon CLI | Existing local `0.2.7`; SHA-256 `1b977a07772f512905d957a0309b0e2f184c70286d38fc56c9994e9cc224bcd1` | +| Data | Synthetic Atlas workspace; 4 Runtime entries, 2 Documents, 2 Native spaces containing 4 evidence items | +| Models | Loopback fixture endpoint; no paid model, semantic distillation or cloud Provider used | + +The CLI checksum identifies the installed binary; it is not an independent upstream signature verification. This capture does not expand the full compatibility matrix to every 0.2.7 feature. + +## Media + +[Watch the 22.72-second recording](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/showcase/demo.mp4) (English UI, no audio). It is a continuous real browser recording transcoded to H.264 / yuv420p with fast-start metadata: no constructed interface, label replacement, generated answer or composited frame. A single MP4 replaces the old duplicated GIF/video presentation on the project landing page. + +| Surface | English | 简体中文 | +|---|---|---| +| Status | [Screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/showcase/en/status.png) | [截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/showcase/zh-CN/status.png) | +| Runtime | [Screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/showcase/en/runtime.png) | [截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/showcase/zh-CN/runtime.png) | +| Documents | [Screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/showcase/en/documents.png) | [截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/showcase/zh-CN/documents.png) | +| Memory Spaces | [Screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/showcase/en/spaces.png) | [截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/showcase/zh-CN/spaces.png) | +| Evidence content | [Screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/showcase/en/content.png) | [截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/showcase/zh-CN/content.png) | +| Enhancements | [Screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/showcase/en/enhancements.png) | [截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/showcase/zh-CN/enhancements.png) | + +## Reproduce and interpret + +1. Build the Starter and official packages, then run `pnpm e2e:serve` as described in [Development](../../en/development/README.md). Keep its temporary credentials and paths out of shared logs. +2. Choose an isolated Atlas workspace through DSH. Create Runtime entries, Documents and Native spaces through the real UI. The four long-term demonstration facts were seeded with the real CLI into the fixture's explicit storage root, without embeddings or an LLM. Do not point these operations at personal memory. +3. Activate both spaces and synchronize. Verify that each reports two memories; browse the actual evidence in Content. Filter Runtime to Working Memory, open a Document, then visit Settings → Memory System. +4. Capture with DSH's actual language setting. Enable all three enhancement switches, reopen Settings to verify persistence, then return all three to Off. Both desktop language runs passed with no page-level JavaScript exceptions. A separate 390px resize exposed clipped settings content; see [the retained failure evidence](../../pr-assets/documentation-refresh/README.md). Container bounds alone did not detect that failure. +5. Close the browser context to finalize the recording. Transcode once; keep the screenshot pixels unchanged. Stop the fixture to dispose its server and test data. + +The screenshots show the default disabled enhancement state. The recording also demonstrates temporary activation. They prove visible operations and persistence in this fixture, not the quality of automatic capture, recall ranking, summarization, external services or production conversation workloads. + +## 中文说明 + +本组素材来自真实本地 DSH、当时的 0.5.2 实现与隔离的合成数据,不是设计稿或拼接界面。四条热记忆、两份档案及两个记忆空间由页面创建;四条长期证据由真实 CLI 写入夹具的明确存储根,未调用嵌入或模型。页面随后完成激活、同步与浏览。 + +中英文桌面均验证了三个增强开关的开启、重新打开设置后的持久化,以及恢复默认关闭。390px 窄屏另发现设置裁切,失败证据单列,不计作通过。录屏为连续浏览器画面转码,保留真实按钮和内容。它不证明 LLM 效果、云 Provider 兼容性或真实手机表现。 + +截图通过 DSH 实际语言设置切换界面;两种语言保留同一组英文合成记忆,便于对照,不代表界面漏译。[22.72 秒演示](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/showcase/demo.mp4)使用英文界面、无音轨。没有对截图或视频中的文字做后期替换。 + +旧版图片、GIF、视频与 PR 证据保留原路径和版本身份,见[历史证据索引](../../pr-assets/README.md)。不覆盖历史结果,也不因主文档停止引用就删除被旧 PR 使用的素材。 diff --git a/dsh/docs/assets/webui-v0.5.19/README.md b/dsh/docs/assets/webui-v0.5.19/README.md new file mode 100644 index 000000000..43a4211a0 --- /dev/null +++ b/dsh/docs/assets/webui-v0.5.19/README.md @@ -0,0 +1,74 @@ +# v0.5.19 Light WebUI, live model / v0.5.19 浅色界面,真实模型 + +Captured on **2026-09-28 (Asia/Shanghai)** from a real local DSH WebUI in **light** appearance, in English and Chinese, with the **live DeepSeek model** answering. [English guide](../../en/guides/ui-guide.md) · [中文指南](../../zh-CN/guides/ui-guide.md) · [Media index](../README.md) · [Manifest](./manifest.json) + +本组素材于 **2026-09-28(Asia/Shanghai)**从真实本地 DSH WebUI 采集,中英文均为**浅色**界面,由**真实的 DeepSeek 模型**作答。 + +![A question answered from working memory, Project Documents and Memory Spaces; the turn memory bar opens the document it read](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/recall.gif) + +## Recordings / 录制 + +| Flow / 流程 | English | 简体中文 | +|---|---|---| +| Recall: an answer that uses memory, and one click to what it read / 召回:用到记忆的回答,一键打开读到的内容 | [recall.mp4](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/recall.mp4) · [GIF](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/recall.gif) · 16 s | [recall.mp4](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/recall.mp4) · [GIF](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/recall.gif) · 14 秒 | +| Save to memory: edit the candidate, the task Agent writes, the receipt leads to the result / 存入记忆:编辑候选内容,任务 Agent 写入,回执直达结果 | [save.mp4](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/save.mp4) · [GIF](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/save.gif) · 34 s | [save.mp4](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/save.mp4) · [GIF](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/save.gif) · 29 秒 | +| Correct: a correction replaces the runtime entry, and its item opens it / 纠正:替换运行时记忆,点击条目直达 | [correct.mp4](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/correct.mp4) · 13 s | [correct.mp4](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/correct.mp4) · 12 秒 | +| Memory System: the four pages, the graph and an Agent answer with citations / 记忆系统:四个页面、图谱与带引用的 Agent 回答 | [memory.mp4](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory.mp4) · [GIF](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory.gif) · 28 s | [memory.mp4](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory.mp4) · [GIF](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory.gif) · 27 秒 | +| Plugins page: composition, component pages, storage / 插件页:记忆组合、组件页与存储 | [plugins.mp4](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/plugins.mp4) · [GIF](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/plugins.gif) · 22 s | [plugins.mp4](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/plugins.mp4) · [GIF](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/plugins.gif) · 22 秒 | + +Frames are Chrome screencast frames played at their own timestamps (H.264, 30 fps, no audio); the GIFs are 880 px at 8 fps. Waits for the model play faster, with a badge in the lower right corner: 4x for chat replies, 6x for the task Agent and the Agent answer. The save recording joins three consecutive segments; the pauses between them, while its screenshots were taken, are left out. A drawn cursor ring shows where each click lands; there is no composited UI. + +画面来自 Chrome 屏幕录制,按原始时间戳播放(H.264,30 fps,无音轨);GIF 为 880 像素、8 fps。等待模型的片段加速播放,右下角有标记:对话回复 4 倍速,任务 Agent 与 Agent 查询 6 倍速。存入记忆的录制由三段连续操作拼接而成,只去掉了中间截图时的停顿。画面中绘制的光标圆环指示点击位置,没有拼接的界面。 + +## Environment and data / 环境与数据 + +| Item / 项目 | Capture environment / 采集环境 | +|---|---| +| Product / 产品 | dsh-mnemon at `0035dd5c`, the source of 0.5.19; the Plugins page shows the package version 0.5.17 it had when captured / 即将发布为 0.5.19 的源码,插件页显示采集时的包版本 0.5.17 | +| Host / 宿主 | Published DSH 0.1.7-rc.2; no Host source modifications / 正式 DSH 包,未修改宿主源码 | +| Model / 模型 | DeepSeek API, `deepseek-flash` (DeepSeek-V41-Flash in the WebUI), for the conversation, the task Agent and the Agent answer / 对话、任务 Agent 与 Agent 查询均使用 | +| Runtime / 运行环境 | macOS, Node.js 25.1.0, Mnemon CLI 0.2.7 | +| Browser / 浏览器 | Headless Google Chrome over the DevTools protocol, light color scheme / 通过 DevTools 协议驱动的无头 Chrome,浅色 | +| Desktop / 桌面 | 1280 × 800 at 2x, stored 1920 px wide; 22 screens per locale / 2 倍像素采集,保存为 1920 像素宽;每种语言 22 张 | +| Phone / 窄屏 | 390 × 844 at 2x; three screens per locale / 2 倍像素;每种语言 3 张 | +| Data / 数据 | A fictional project, Lumen: 3 profile and 5 working-memory entries, 5 active and 1 archived document, 3 native memory spaces (2 active) with 17 memories / 虚构项目 Lumen:3 条用户画像与 5 条工作记忆,5 份活跃档案与 1 份归档,3 个原生记忆空间(2 个激活)共 17 条记忆 | + +Each locale is one pass over a fresh fixture: `node scripts/serve-e2e.mjs --docs-demo --live-model` (Chinese) or `--docs-demo=en --live-model` (English) seeds a disposable Profile through the Sources' own management operations, then the WebUI talks to the DeepSeek API with a key read from `DEEPSEEK_API_KEY`. The questions are typed; the model decides which memory tools to call, the task Agent performs Save to memory and the Agent answer, and every reply, write and receipt is the model's own. Another run words things differently and may read other documents. No personal memory or credentials appear, and the storage path shown is the fixture's temporary directory. + +每种语言都在全新的夹具上完整走一遍:`node scripts/serve-e2e.mjs --docs-demo --live-model`(中文)或 `--docs-demo=en --live-model`(英文)先通过各 Source 自己的管理操作预置一次性 Profile,再由 WebUI 通过 `DEEPSEEK_API_KEY` 中的密钥调用 DeepSeek API。问题是逐字输入的;模型自己决定调用哪些记忆工具,任务 Agent 完成存入记忆与 Agent 查询,每条回复、写入与回执都由模型生成。再次运行时措辞会不同,读到的档案也可能不同。素材中没有个人记忆或凭据,界面中显示的存储路径是夹具的临时目录。 + +## Screens / 截图 + +| Surface / 场景 | English | 简体中文 | +|---|---|---| +| Answer with the turn memory bar's items / 回合记忆栏列出读到的内容 | [chat-recall](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/chat-recall.jpg) | [chat-recall](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/chat-recall.jpg) | +| Save to memory: the candidate / 存入记忆:候选内容 | [chat-save](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/chat-save.jpg) | [chat-save](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/chat-save.jpg) | +| Save to memory: the task Agent's receipt / 存入记忆:任务 Agent 的回执 | [chat-save-receipt](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/chat-save-receipt.jpg) | [chat-save-receipt](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/chat-save-receipt.jpg) | +| A correction and its writes / 纠正及其写入 | [chat-correction](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/chat-correction.jpg) | [chat-correction](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/chat-correction.jpg) | +| Status / 状态 | [memory-status](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory-status.jpg) | [memory-status](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory-status.jpg) | +| Runtime memory, opened from a turn / 从回合打开的运行时记忆 | [memory-runtime](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory-runtime.jpg) | [memory-runtime](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory-runtime.jpg) | +| Project Documents, opened from a turn / 从回合打开的项目档案 | [memory-documents](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory-documents.jpg) | [memory-documents](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory-documents.jpg) | +| Memory Spaces / 记忆空间 | [memory-spaces](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory-spaces.jpg) | [memory-spaces](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory-spaces.jpg) | +| Graph with an entity selected / 选中实体的图谱 | [memory-graph](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory-graph.jpg) | [memory-graph](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory-graph.jpg) | +| Direct search / 直接检索 | [memory-recall](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory-recall.jpg) | [memory-recall](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory-recall.jpg) | +| Agent answer with citations / 带引用的 Agent 回答 | [memory-agent](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory-agent.jpg) | [memory-agent](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory-agent.jpg) | +| Content with the saved memory / 内容中新存入的记忆 | [memory-content](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory-content.jpg) | [memory-content](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory-content.jpg) | +| Entities / 实体 | [memory-entities](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory-entities.jpg) | [memory-entities](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory-entities.jpg) | +| Save to memory on Memory Spaces / 记忆空间中的存入记忆 | [memory-remember](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory-remember.jpg) | [memory-remember](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory-remember.jpg) | +| Plugins page: Memory composition / 插件页:记忆组合 | [plugin-composition](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/plugin-composition.jpg) | [plugin-composition](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/plugin-composition.jpg) | +| Main strategy selector / 主策略选择器 | [plugin-strategy-menu](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/plugin-strategy-menu.jpg) | [plugin-strategy-menu](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/plugin-strategy-menu.jpg) | +| Layered strategy's page / 分层策略页面 | [plugin-layered](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/plugin-layered.jpg) | [plugin-layered](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/plugin-layered.jpg) | +| Memory Spaces' page: Providers / 记忆空间页面:Provider | [plugin-spaces](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/plugin-spaces.jpg) | [plugin-spaces](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/plugin-spaces.jpg) | +| Storage and Interface / 存储与界面 | [plugin-storage](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/plugin-storage.jpg) | [plugin-storage](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/plugin-storage.jpg) | +| Backup import preview / 备份导入预览 | [plugin-backup-preview](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/plugin-backup-preview.jpg) | [plugin-backup-preview](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/plugin-backup-preview.jpg) | +| DSH's component list / DSH 组件列表 | [plugin-rows](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/plugin-rows.jpg) | [plugin-rows](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/plugin-rows.jpg) | +| DSH's page for a component / DSH 组件页面 | [plugin-row-page](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/plugin-row-page.jpg) | [plugin-row-page](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/plugin-row-page.jpg) | +| Phone: conversation / 窄屏:对话 | [narrow-chat](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/narrow-chat.jpg) | [narrow-chat](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/narrow-chat.jpg) | +| Phone: Memory Spaces / 窄屏:记忆空间 | [narrow-spaces](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/narrow-spaces.jpg) | [narrow-spaces](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/narrow-spaces.jpg) | +| Phone: configuration / 窄屏:配置 | [narrow-plugin](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/narrow-plugin.jpg) | [narrow-plugin](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/narrow-plugin.jpg) | + +## Validation / 校验 + +Both locales were captured with no browser console errors, and each screen and recording was checked by eye for the intended state. The manifest records every file's dimensions, duration, size and SHA-256, and what each turn read or wrote. The capture reflects the stated revision, model and environment only; it does not measure model accuracy, cover live third-party Providers or establish complete phone support. + +两种语言采集过程中浏览器控制台均无错误,每张截图与每段录制都经过人工检查。manifest 记录了每个文件的尺寸、时长、大小与 SHA-256,以及每一轮读到和写入的内容。本组素材只代表上述版本、模型与环境,不衡量模型准确率,不涉及在线第三方 Provider,也不代表完整的手机端支持。 diff --git a/dsh/docs/assets/webui-v0.5.19/manifest.json b/dsh/docs/assets/webui-v0.5.19/manifest.json new file mode 100644 index 000000000..a04dd0885 --- /dev/null +++ b/dsh/docs/assets/webui-v0.5.19/manifest.json @@ -0,0 +1,955 @@ +{ + "schemaVersion": 1, + "productVersion": "0.5.19", + "packageVersionAtCapture": "0.5.17", + "sourceRevision": "0035dd5c9758bf1b7037425ecf351a6869b5cbdf", + "captureDate": "2026-09-28", + "captureTimezone": "Asia/Shanghai", + "theme": "light", + "hostVersion": "0.1.7-rc.2", + "mnemonCliVersion": "0.2.7", + "model": "DeepSeek API, deepseek-flash (shown as DeepSeek-V41-Flash)", + "browser": "Headless Google Chrome driven over the DevTools protocol", + "fixture": "node scripts/serve-e2e.mjs --docs-demo --live-model (zh-CN) and --docs-demo=en --live-model: a disposable Profile, a seeded fictional project, and the live DeepSeek API answering", + "data": { + "project": "Lumen (fictional)", + "runtimeEntries": 8, + "userProfileEntries": 3, + "workingMemoryEntries": 5, + "documents": 6, + "activeDocuments": 5, + "archivedDocuments": 1, + "memorySpaces": 3, + "activeMemorySpaces": 2, + "storedMemories": 17, + "activeSpaceMemories": 15, + "personalData": false, + "credentials": false + }, + "conversation": "Each locale is one pass over a fresh fixture. The questions are typed; the model decides which memory tools to call, the task Agent performs the Save to memory and the Agent answer, and every reply is the model's own. Another run gives different wording and may read different documents.", + "recordingNote": "Chrome screencast frames played at their own timestamps (H.264 CRF 27, 30 fps, no audio; GIF 880 px, 8 fps). Waits for the model play faster, with an on-screen badge: 4x for chat replies, 6x for the task Agent and the Agent answer. The save recording joins three consecutive segments; the pauses between them, while its screenshots were taken, are left out. A drawn cursor ring shows where each click lands.", + "runs": { + "zh-CN": { + "turns": { + "q1": { + "replySeconds": 6, + "memoryItems": [ + [ + "项目档案检索", + "架构决策:事件摄取改用队列", + "故障复盘:9 月 12 日指标延迟" + ], + [ + "记忆空间召回", + "事件摄取改为 Kafka 队列,由消费者批量写入 ClickHouse。 Lumen 项目" + ] + ] + }, + "q2": { + "replySeconds": 5, + "memoryItems": [ + [ + "项目档案检索", + "架构决策:事件摄取改用队列", + "故障复盘:9 月 12 日指标延迟" + ], + [ + "记忆空间召回", + "事件摄取改为 Kafka 队列,由消费者批量写入 ClickHouse。 Lumen 项目" + ], + [ + "运行时记忆更新", + "支付 SDK 改为用户交互后再加载:结账页 p75 LCP 从 2.9 秒降到 2.4 秒,已达成 p75 < 2.5 秒目标。" + ] + ] + }, + "q3": { + "replySeconds": 5, + "memoryItems": [ + [ + "运行时记忆更新", + "本周重点:结账页性能,目标 p75 LCP 低于 2.2 秒。" + ] + ] + } + }, + "receipt": "已存入 已在「Lumen 项目」中新增记录:支付 SDK 改为用户交互后再加载,结账页 p75 LCP 从 2.9 秒降至 2.4 秒。既有的一条「同步加载支付 SDK 阻塞首屏约 400 毫秒」的事实已明显过时,但现有记忆无法删除,故以这条更正记录为准。 在记忆空间中查看", + "notes": [] + }, + "en": { + "turns": { + "q1": { + "replySeconds": 8, + "memoryItems": [ + [ + "Document search", + "Decision: queue event ingestion", + "Investigation: checkout LCP", + "Incident review: delayed metrics on September 12" + ], + [ + "Memory Spaces recall", + "Event ingestion moves to a Kafka queue; consumers write to ClickHouse in batches. Lumen project" + ] + ] + }, + "q2": { + "replySeconds": 13, + "memoryItems": [ + [ + "Document search", + "Decision: queue event ingestion", + "Investigation: checkout LCP", + "Incident review: delayed metrics on September 12" + ], + [ + "Memory Spaces recall", + "Event ingestion moves to a Kafka queue; consumers write to ClickHouse in batches. Lumen project" + ], + [ + "Runtime memory update", + "Checkout performance target: p75 LCP under 2.5 s (this week's focus); after deferring the payment SDK to first interaction, checkout p75 LCP went 2.9 s → 2.4 s…" + ], + [ + "Document update", + "Investigation: checkout LCP" + ] + ] + }, + "q3": { + "replySeconds": 9, + "memoryItems": [ + [ + "Runtime memory update", + "Checkout performance target this week: p75 LCP under 2.2 s (tightened from 2.5 s); deferring the payment SDK to first interaction brought checkout p75 LCP from…" + ], + [ + "Document update", + "Investigation: checkout LCP" + ] + ] + } + }, + "receipt": "Saved Saved the checkout performance result — deferring the payment SDK until first interaction cut p75 LCP from 2.9 s to 2.4 s — in the Lumen project Memory Space, linked to the existing note about the synchronous payment SDK load. View in Memory Spaces", + "notes": [] + } + }, + "files": [ + { + "file": "zh-CN/chat-correction.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 129641, + "sha256": "13c565a29c8c3927b9b7d0d325029358457b43a86bc701ebd87fe6623dd98736" + }, + { + "file": "zh-CN/chat-recall.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 155270, + "sha256": "bad84f1ef15f4a1650333f06b528469541fbe062f4e71d2867180b936f2e1779" + }, + { + "file": "zh-CN/chat-save-receipt.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 120920, + "sha256": "0d1213614889bf13ed2760730597d3f0d82c4ceebb802e6d18251cc57fb61413" + }, + { + "file": "zh-CN/chat-save.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 122209, + "sha256": "5d95a4b64ebbad2a4e0bf3cc6f7cb3d75b9e617a7097de8bd923bcd1c7563fa4" + }, + { + "file": "zh-CN/memory-agent.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 176066, + "sha256": "6bf1afb62ed1520697167d14a67a9a4ce78eb76ab0380b86c9a19f6d9a61a7e4" + }, + { + "file": "zh-CN/memory-content.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 178190, + "sha256": "f6c490ff011305bb58114beed4d9448730a62830f0fe2bedf55f89fbe096e4eb" + }, + { + "file": "zh-CN/memory-documents.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 155069, + "sha256": "9f3f0b375f0ea080c40ab6f4a077348a9ae2d36fe8a664c847c37048efe117c5" + }, + { + "file": "zh-CN/memory-entities.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 151799, + "sha256": "ec37e37c5ab7b170f021a14958fbb0a803d2e1b7815b5b8ba03697d13612e555" + }, + { + "file": "zh-CN/memory-graph.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 155057, + "sha256": "8d76273b23ac8c649a1d266d3b5b21e1e6bd42acf526d2d6ba52d01d2dec3fa0" + }, + { + "file": "zh-CN/memory-recall.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 140972, + "sha256": "336baaa9076d6ec1f62ee7bc7c41ab50cb23f62f7b5c7067020d0c1c955419d1" + }, + { + "file": "zh-CN/memory-remember.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 132838, + "sha256": "791526f71354255a04fb54ced16492d84f1aeccc3b322b6f95be83c0fce64a71" + }, + { + "file": "zh-CN/memory-runtime.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 152899, + "sha256": "5f0daa7677a8d9b87f93d41846f8f0a6867462b448a988d199459adbe4540fa7" + }, + { + "file": "zh-CN/memory-spaces.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 146758, + "sha256": "a0a41891bb36451e0c66be2545d9c05bf9e630445a65748cb0e8d27ec5365ad9" + }, + { + "file": "zh-CN/memory-status.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 143726, + "sha256": "53a450762dcb0fdbfa50a11f36a30beb01d7feb70ba34924e0da11d0b80ab916" + }, + { + "file": "zh-CN/narrow-chat.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "390x844", + "deviceScaleFactor": 2, + "width": 780, + "height": 1688, + "bytes": 129419, + "sha256": "fa6cd46d10217d7ef0725e4f09a037bbabf6c0ac12924a0f8615a72bc6bb374a" + }, + { + "file": "zh-CN/narrow-plugin.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "390x844", + "deviceScaleFactor": 2, + "width": 780, + "height": 1688, + "bytes": 83141, + "sha256": "9215d35c82ebd55a93fd32fc2e9fc0284076ef6c40f16597368c70163fd56281" + }, + { + "file": "zh-CN/narrow-spaces.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "390x844", + "deviceScaleFactor": 2, + "width": 780, + "height": 1688, + "bytes": 108275, + "sha256": "98d8a542129ebd86d526e8b9f63e4749f302159e63be9f17aa26bb81815969de" + }, + { + "file": "zh-CN/plugin-backup-preview.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 108163, + "sha256": "472901cbda515029a394c2fbc80b8a7a7c279d4c5067168f0516ef2a978111bd" + }, + { + "file": "zh-CN/plugin-composition.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 109897, + "sha256": "039049e3bc32b0d3a0b339beaca6ce9f50e5bb97b01037e54534f392c26642e8" + }, + { + "file": "zh-CN/plugin-layered.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 124661, + "sha256": "f1594433c557927dd3acdf459eaf2fe98b33b93b636dabd726ec000f126de39f" + }, + { + "file": "zh-CN/plugin-row-page.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 105553, + "sha256": "37fd4438e10c8944f69474e0344ee11a10a08befa664ffc934853fd70daaadf1" + }, + { + "file": "zh-CN/plugin-rows.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 125145, + "sha256": "0428149df6150ad68a6bce540c82187cecbed7168e78a40f6fa26b2adf9f14c5" + }, + { + "file": "zh-CN/plugin-spaces.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 150077, + "sha256": "d056c9e95b297587eb4261992a93cfc3d4d118e433ba42866b2564a89f0db114" + }, + { + "file": "zh-CN/plugin-storage.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 104964, + "sha256": "0a95e44398057933029efd494d78ea03e3e3bd0917b449fb9cded8efd6005688" + }, + { + "file": "zh-CN/plugin-strategy-menu.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1416, + "height": 290, + "bytes": 26788, + "sha256": "c3293802047a559519dd4ea1da4f281b75f5622b15d71f57b4a04a9e31ea1a4f" + }, + { + "file": "zh-CN/recall.mp4", + "kind": "recording", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "width": 1280, + "height": 800, + "durationSeconds": 13.6, + "bytes": 247994, + "sha256": "0e1f2b870c52c7d92352e22623c677dde620e2ffda14cdc6eeda2cb3b17ba291" + }, + { + "file": "zh-CN/recall.gif", + "kind": "animation", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "width": 880, + "height": 550, + "durationSeconds": 13.63, + "bytes": 390200, + "sha256": "83a80e62a444df84fb37cfdbdbff93600ca28d4c4c04ddf02659b34ce3b6fa96" + }, + { + "file": "zh-CN/save.mp4", + "kind": "recording", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "width": 1280, + "height": 800, + "durationSeconds": 28.73, + "bytes": 613725, + "sha256": "3303c6d359a44130d54d76b062b95b614740ec300c2cdb84b516217f2b699f4d" + }, + { + "file": "zh-CN/save.gif", + "kind": "animation", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "width": 880, + "height": 550, + "durationSeconds": 28.76, + "bytes": 950828, + "sha256": "869c0fa6a23e089626a6f1b28f60c861ea2f249ba9629719b8ed83291b5f368f" + }, + { + "file": "zh-CN/correct.mp4", + "kind": "recording", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "width": 1280, + "height": 800, + "durationSeconds": 11.6, + "bytes": 262032, + "sha256": "0bc47ed43edf8eb8cf27bccae5c7e18918b6e3a381bedf0d8347cceaf1c8dbcd" + }, + { + "file": "zh-CN/memory.mp4", + "kind": "recording", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "width": 1280, + "height": 800, + "durationSeconds": 26.57, + "bytes": 534229, + "sha256": "e4b46a65782583e35f85380032cafb317de2baba8e11562f66d040f7bdf9a403" + }, + { + "file": "zh-CN/memory.gif", + "kind": "animation", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "width": 880, + "height": 550, + "durationSeconds": 26.63, + "bytes": 842356, + "sha256": "52a0d8b65df0eea3f37f4ceab7638f7b8a5f6c49505404b3402c193a1098be4d" + }, + { + "file": "zh-CN/plugins.mp4", + "kind": "recording", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "width": 1280, + "height": 800, + "durationSeconds": 22, + "bytes": 476718, + "sha256": "bda82ec8937b0f8be47d4ddb593e727cbaf40d68f96ae97b711cffd9f2141d96" + }, + { + "file": "zh-CN/plugins.gif", + "kind": "animation", + "locale": "zh-CN", + "theme": "light", + "viewport": "1280x800", + "width": 880, + "height": 550, + "durationSeconds": 22.01, + "bytes": 886686, + "sha256": "d757f8a5437586fa509801318314c965e025f2f66a58cde4c7c50ff347b2b36f" + }, + { + "file": "en/chat-correction.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 153408, + "sha256": "6c1aef7d660789917de8e4c6e76082d774a3d8639e6ca9c6af6f466268a0dd9b" + }, + { + "file": "en/chat-recall.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 167934, + "sha256": "ebd6fa6ddb9ebfdece6e26eab8f6a34b4988ee75c0c1942590f66a7fe6a5e623" + }, + { + "file": "en/chat-save-receipt.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 132628, + "sha256": "e38d456b36c3bd56357f0810cbde436d11dd0d81257e9e5a574a5487d7b88675" + }, + { + "file": "en/chat-save.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 132517, + "sha256": "5524a60250a502d9c004bda480694f11c038144f810fd1b6786b97c5cd2f80bf" + }, + { + "file": "en/memory-agent.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 197187, + "sha256": "83ad6b74596e15899736c6b55857b2d95eff85c0345d3fae88aa18f8f6f76eb1" + }, + { + "file": "en/memory-content.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 190834, + "sha256": "6ad812f8a99a43c1bc975203ce019a0d3fdc9d40d8ce8a2d79706a5f2f8a2e92" + }, + { + "file": "en/memory-documents.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 166465, + "sha256": "6dbf25f07729af6b88026314db98dc989efdef6441f1ac9c3022558daaadaa7d" + }, + { + "file": "en/memory-entities.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 170190, + "sha256": "be2c3842b21af4bd3d2bf9f1a49d465e05d79faf3510255dcd3617441425e400" + }, + { + "file": "en/memory-graph.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 165510, + "sha256": "0863774a6e403327b3d12799f9ba6b02acf3af5b12a8473e01a0e6f00e47db42" + }, + { + "file": "en/memory-recall.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 158818, + "sha256": "4345118a586fbf07e4e9b1759b4fbfd6359e52bd1245f398dd11c8a6a11952dd" + }, + { + "file": "en/memory-remember.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 157339, + "sha256": "2cd03730db3cc1bd0a7a22bb9c8504db74fb4c5f0706fb6212830b7bbb055d76" + }, + { + "file": "en/memory-runtime.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 168745, + "sha256": "5218f334adce9cd1a6dbd3f122b61e75ef51e90825766a3012696387353241b0" + }, + { + "file": "en/memory-spaces.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 157713, + "sha256": "bf1494830900ec3144e7f0f211500b1269980536b399eab5e77a595d2ce66d8d" + }, + { + "file": "en/memory-status.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 158153, + "sha256": "aa961080234a6bf1d9652c6483c3d00d33d7aeda7f539f5c6950544b7cc02d0b" + }, + { + "file": "en/narrow-chat.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "390x844", + "deviceScaleFactor": 2, + "width": 780, + "height": 1688, + "bytes": 119820, + "sha256": "b7293314719e7e0e77dbad3fdba7b0117423ec00a952f9725312b3856c1c6db6" + }, + { + "file": "en/narrow-plugin.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "390x844", + "deviceScaleFactor": 2, + "width": 780, + "height": 1688, + "bytes": 92472, + "sha256": "7ee9707d299c8838fa0b24adace0f69229bd0939b542f5d6367a023bcb5c96e8" + }, + { + "file": "en/narrow-spaces.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "390x844", + "deviceScaleFactor": 2, + "width": 780, + "height": 1688, + "bytes": 111060, + "sha256": "dbf1c258b8940c988d57ec4db9abb9116f731f8abde1e8839845ec7b0c713bf5" + }, + { + "file": "en/plugin-backup-preview.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 120681, + "sha256": "d0584c0854878edcbfa82f6d0ee3a45a46ab3b1f8b3a24add72427b67c8e0c06" + }, + { + "file": "en/plugin-composition.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 125725, + "sha256": "48b4e8428f2cb21b0e435838fb8e7557b43900881eca3222c37d7c85faf1ebdc" + }, + { + "file": "en/plugin-layered.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 146305, + "sha256": "bd3d4bac06a93f19e1d7957c7a1471b3ad57de6e77ff89b925eee6473fc150ee" + }, + { + "file": "en/plugin-row-page.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 119792, + "sha256": "fd25e44e12a4f9991b5a18767f9e810a08057baec46625522dcafd60f5379681" + }, + { + "file": "en/plugin-rows.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 137422, + "sha256": "4d0d5ceb8b53315e7298f63967da48bec16da20cdb51c0d642c79843a007d7d8" + }, + { + "file": "en/plugin-spaces.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 155802, + "sha256": "0ed0c5b1b0b5bcf9e53194ef87b599d4bbfeadef583a78dbc00adc2ea641d794" + }, + { + "file": "en/plugin-storage.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1920, + "height": 1200, + "bytes": 117275, + "sha256": "535b72dbc6de705b742e6327ad1937abc512768ebfb73b4d51ca69c64b1a23fa" + }, + { + "file": "en/plugin-strategy-menu.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "deviceScaleFactor": 2, + "width": 1416, + "height": 344, + "bytes": 40401, + "sha256": "ae5c0eb6e48aa33b4c18d654632c2f4b7a1f1a67e09bc1a9f96a03326e7816f1" + }, + { + "file": "en/recall.mp4", + "kind": "recording", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "width": 1280, + "height": 800, + "durationSeconds": 16.07, + "bytes": 365608, + "sha256": "ef603a6135caf1aa572594d0f33ec93bc046a370522352ce82bf0779a277addb" + }, + { + "file": "en/recall.gif", + "kind": "animation", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "width": 880, + "height": 550, + "durationSeconds": 16.13, + "bytes": 482462, + "sha256": "381eb96ed5a2e898a202e8aef635ff2af84cec27d48d6f785a9f5e6ca0d8aaf9" + }, + { + "file": "en/save.mp4", + "kind": "recording", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "width": 1280, + "height": 800, + "durationSeconds": 34.17, + "bytes": 875940, + "sha256": "f358ecfe6fde551eb86ae92a1c26f84a6a82575eb59f09ed42d388f698ea92dc" + }, + { + "file": "en/save.gif", + "kind": "animation", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "width": 880, + "height": 550, + "durationSeconds": 34.13, + "bytes": 1226374, + "sha256": "d1ca13e98bdd18c3b0c4a0880a460b8fa43a88de5ec54ccf9b82832f7e543d8d" + }, + { + "file": "en/correct.mp4", + "kind": "recording", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "width": 1280, + "height": 800, + "durationSeconds": 13.47, + "bytes": 452703, + "sha256": "4eb6ff082763efe7a2f8fbe2906927aa664d9407f58f71c907bc472664060b4f" + }, + { + "file": "en/memory.mp4", + "kind": "recording", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "width": 1280, + "height": 800, + "durationSeconds": 27.83, + "bytes": 602665, + "sha256": "abea03e3df28df50eb55da635243a4ace6740e9947e7f3d1dbaa683b5baaaa3a" + }, + { + "file": "en/memory.gif", + "kind": "animation", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "width": 880, + "height": 550, + "durationSeconds": 27.88, + "bytes": 910186, + "sha256": "789c921872d6ce1308015ed882c5e2ed2dbde39dc7c73768b860b4cae0df8305" + }, + { + "file": "en/plugins.mp4", + "kind": "recording", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "width": 1280, + "height": 800, + "durationSeconds": 22.03, + "bytes": 554272, + "sha256": "1b270a34eef55801d9ffa9e1a1d853b1fca4d091b4cecf8633fa783d6c239125" + }, + { + "file": "en/plugins.gif", + "kind": "animation", + "locale": "en", + "theme": "light", + "viewport": "1280x800", + "width": 880, + "height": 550, + "durationSeconds": 22.01, + "bytes": 1088393, + "sha256": "cf0624f771c297317d5a3f30eb535b9fbd26110c13d416cf1eba01e86dc5c247" + } + ] +} diff --git a/dsh/docs/assets/webui-v0.5.4/README.md b/dsh/docs/assets/webui-v0.5.4/README.md new file mode 100644 index 000000000..c983c0e87 --- /dev/null +++ b/dsh/docs/assets/webui-v0.5.4/README.md @@ -0,0 +1,112 @@ +# v0.5.4 Light WebUI / v0.5.4 浅色界面 + +Captured on **2026-09-07 (Asia/Shanghai)** from the real local DSH WebUI, with **Light** selected explicitly in both languages. The supplied Mnemon Pack was imported before every screenshot and recording in this directory. [English guide](../../en/guides/ui-guide.md) · [中文指南](../../zh-CN/guides/ui-guide.md) · [Media index](../README.md). + +本组素材于 **2026-09-07(Asia/Shanghai)**从真实本地 DSH WebUI 采集,中英文均明确选择**浅色模式**,全部在导入提供的 Mnemon Pack 后拍摄。此前未提交的深色草稿已从本次素材中移除。 + +![Imported memory spaces in Light appearance](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/spaces.jpg) + +## Environment and data / 环境与数据 + +| Item / 项目 | Capture environment / 采集环境 | +|---|---| +| Product source / 产品源码 | Released v0.5.4, `6902155e0f3877dca5c5d70468f2a2a1784c7b29` | +| Starter and official plugins / 主包与官方插件 | 0.5.4, built locally and linked through `node scripts/serve-e2e.mjs` | +| Host / 宿主 | Published DSH 0.1.2-rc.1; no Host source modifications / 正式 DSH 包,未修改宿主源码 | +| Runtime / 运行环境 | macOS, Node.js 25.1.0, pnpm 10.13.1, existing Mnemon CLI 0.2.7 | +| Browser / 浏览器 | Codex in-app browser, controlled through the real UI / Codex 内置浏览器,真实 UI 操作 | +| Appearance / 外观 | Light in both locales / 中英文均为浅色 | +| Desktop / 桌面 | 1280 × 720; 26 screenshots per locale / 每种语言 26 张 | +| Narrow / 窄屏 | 390 × 844; four screenshots per locale / 每种语言四张 | +| Runtime data / 运行时数据 | 20 entries: two User Profile, 18 Working Memory / 20 条:用户画像 2 条、工作记忆 18 条 | +| Documents / 档案 | 12 total: 11 active, one archived / 共 12 份:11 份活跃、1 份归档 | +| Memory Spaces / 记忆空间 | Four native spaces, two active; 27 stored memories, 21 in active spaces / 四个原生空间,两个激活;共 27 条记忆,激活空间包含 21 条 | +| Models / 模型 | Loopback fixture endpoint; zero model requests / 回环夹具端点,模型请求为零 | + +The pack is format 1, full backup, with 23 ZIP entries and 21 payload files. All payload hashes were checked before the UI safe import. Its SHA-256 is `acc854f386931f928c2feeee41e8df98f8fcc44b5616aabbd3a1a79efc87c31f`. The manifest's historical plugin version is 0.1.0; that is source-backup metadata, not the running UI version. The original ZIP, personal memory directories and raw databases are not part of this PR. + +备份为 format 1 完整包,共 23 个 ZIP 条目、21 个有效载荷文件;导入前逐项核验哈希,通过真实页面执行安全合并。上述 SHA-256 标识原始 ZIP。备份 manifest 中的 0.1.0 是历史来源元数据,不是本次运行界面版本;原始 ZIP、个人记忆目录和数据库不进入 PR。 + +Only the disposable copy's catalog description for `dsh-mnemon 架构方向演进` was edited through the real form, replacing its old product noun with `记忆空间`. IDs, record bodies and historical dates remain unchanged. The imported records stay Chinese when the interface switches to English. Screenshots select public project/community references and generic collaboration preferences; no credentials or raw logs are included. The UI still shows its real temporary storage paths and the public repository's local source path. + +仅在临时副本中,通过真实编辑表单将 `dsh-mnemon 架构方向演进` 的目录说明更新为“记忆空间”用语。ID、记录正文与历史日期未改;切换英文界面时,导入记录仍保留中文。画面选取公开项目、社区资料与通用协作偏好,不包含凭据或原始日志;界面中的临时存储路径与公开仓库的本地源码路径保留真实显示。 + +## Recordings / 录制 + +[English Light demo — 41.88 s](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/demo.mp4) · [中文浅色演示 — 42.13 秒](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/demo.mp4) + +Both demos continuously sample the actual browser during one uninterrupted interaction sequence, targeting four source frames per second (166 English frames, 167 Chinese frames). Actual frame timestamps determine playback timing. H.264 MP4 encodes at 24 fps, full-range 4:2:0, with fast-start metadata and no audio; repeated output frames do not imply 24 distinct captures per second. There are no cuts, fabricated answers, composited UI or replaced labels. The manifest records exact durations and chapter times. + +两段演示分别连续采集一次完整浏览器操作,目标每秒四个源画面,英文 166 帧、中文 167 帧,按真实时间戳控制播放时序。输出为 24 fps 的 H.264 MP4、全范围 4:2:0、快速起播、无音轨;编码重复帧不代表每秒采集了 24 个不同画面。没有剪切、构造回答、拼接界面或替换标签;精确时长与章节时间见 manifest。 + +| Approximate time / 约计时间 | Visible workflow / 可见流程 | +|---|---| +| 0:00 | Four imported Memory Spaces / 四个导入的记忆空间 | +| 0:04 | Create a Memory Space draft, then cancel / 填写创建草稿后取消 | +| 0:09 | Remember draft, then cancel / 填写沉淀草稿后取消 | +| 0:13 | Keyword recall of imported evidence / 关键词检索导入证据 | +| 0:17 | Native APPSO entity connections / 原生 APPSO 实体关联 | +| 0:22 | Live relationship graph / 实时关系图谱 | +| 0:26 | Imported totals on Status / 状态页导入数据汇总 | +| 0:30 | Detected CLI installation and npm guidance / CLI 当前安装状态与 npm 引导 | +| 0:34 | Expand 16 subpackage versions / 展开 16 个子包版本 | +| 0:39 | Runtime User Profile / 运行时用户画像 | + +## Screenshot gallery / 截图图集 + +Screenshots preserve the browser's original JPEG bytes. All 60 files use Light appearance. / 截图保留浏览器原始 JPEG 数据,60 张均为浅色。 + +| Surface / 场景 | English | 简体中文 | +|---|---|---| +| Status / 状态 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/status.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/status.jpg) | +| npm version guidance / npm 版本引导 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/versions.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/versions.jpg) | +| Expanded subpackages / 展开子包 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/versions-expanded.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/versions-expanded.jpg) | +| Runtime / 运行时 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/runtime.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/runtime.jpg) | +| Configured Runtime budgets / 运行时容量配置 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/runtime-configured.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/runtime-configured.jpg) | +| Runtime creation draft / 新增运行时记忆草稿 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/runtime-create.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/runtime-create.jpg) | +| Document directory / 档案目录 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/documents.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/documents.jpg) | +| Document reader / 档案阅读器 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/document-reader.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/document-reader.jpg) | +| Document creation draft / 新建档案草稿 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/document-create.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/document-create.jpg) | +| Documents disabled / 档案停用状态 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/documents-disabled.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/documents-disabled.jpg) | +| Memory Space directory / 记忆空间目录 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/spaces.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/spaces.jpg) | +| Create Memory Space draft / 创建记忆空间草稿 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/space-create.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/space-create.jpg) | +| Edit Memory Space / 编辑记忆空间 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/space-edit.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/space-edit.jpg) | +| Metadata selection / 元信息选择 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/metadata.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/metadata.jpg) | +| Distillation strategy / 沉淀策略 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/strategy.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/strategy.jpg) | +| Remember draft / 沉淀记忆草稿 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/remember.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/remember.jpg) | +| Direct keyword recall / 直接关键词检索 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/recall.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/recall.jpg) | +| Filtered content / 内容筛选 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/content.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/content.jpg) | +| Entity connections / 实体关联 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/entities.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/entities.jpg) | +| Live graph / 实时图谱 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/graph.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/graph.jpg) | +| General settings / 通用设置 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/settings.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/settings.jpg) | +| Memory Source switches / 记忆层开关 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/settings-layers.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/settings-layers.jpg) | +| Optional enhancements / 可选记忆增强 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/enhancements.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/enhancements.jpg) | +| Native embedding settings / 原生嵌入设置 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/native-settings.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/native-settings.jpg) | +| Backup controls / 备份操作 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/backup.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/backup.jpg) | +| ZIP import preview / ZIP 导入预览 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/backup-preview.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/backup-preview.jpg) | +| Narrow navigation / 窄屏导航 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/spaces-mobile.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/spaces-mobile.jpg) | +| Narrow directory cards / 窄屏目录卡片 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/space-directory-mobile.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/space-directory-mobile.jpg) | +| Narrow creation sheet / 窄屏创建表单 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/space-create-mobile.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/space-create-mobile.jpg) | +| Narrow version maintenance / 窄屏版本维护 | [Open](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/en/versions-mobile.jpg) | [查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.4/zh-CN/versions-mobile.jpg) | + +## Reproduce and interpret / 复现与判读 + +1. Build v0.5.4 and its official plugins, then run `pnpm e2e:serve` with a disposable Profile and storage root. Follow the [development guide](../../en/development/README.md); never substitute a personal memory directory. +2. In Settings → Memory System → Mnemon Native → Backup and migration, select a suitable Mnemon Pack, inspect the preview and perform safe import. Confirm totals on Status before recording. This source pack is not distributed; use your own publishable sample data to reproduce the workflow. +3. Set Light explicitly. Switch the actual DSH language setting for matching English and Chinese coverage. Runtime captures select User Profile; Content and Recall use `后台自我进化`, Recall uses Keyword search, Entities uses `APPSO`, and Documents uses `生态目录` with the matching community-index document selected. +4. Capture at 1280 × 720 and inspect the key narrow surfaces at 390 × 844. Cancel all creation, Remember and metadata drafts. Temporarily disable Documents and change Runtime limits only for their named state captures; re-enable Documents and restore default limits afterward. +5. Capture a continuous browser sequence for each demo and encode the timestamps once. Inspect every screenshot and representative decoded video frames; check all source frames for legacy product labels. Stop the fixture to dispose the imported copy after validation. + +中文复现:构建 v0.5.4 并启动临时 `pnpm e2e:serve` 环境;在 Native 备份区预览并安全导入适合公开展示的数据,先核对状态汇总;明确设置浅色,使用 DSH 真实语言设置切换中英文;按上述筛选采集桌面与窄屏界面。所有创建、沉淀与元信息草稿均取消,临时关闭的档案与提高的 Runtime 容量在采集后恢复。连续采集两种语言的实际操作,核对画面后停止夹具并清理导入副本。来源备份不随仓库分发,复现时需自行准备可公开的样例。 + +The [manifest](./manifest.json) records all 62 media files, dimensions, timestamps, theme, hashes and video chapters. OCR checked all 60 screenshots and all 333 source video frames, with zero legacy-label matches or recognition errors; manual inspection checked their visible context. Both MP4 files decoded without errors, and the active browser reported no warnings or errors after capture. OCR is a supporting check, not proof that every possible application state was tested. + +[清单](./manifest.json)记录全部 62 份媒体的尺寸、时间戳、主题、哈希及视频章节。60 张截图与 333 个视频源画面均完成 OCR,旧标签匹配与识别错误为零,并人工检查可见内容。两段 MP4 均可无错解码,采集后浏览器无 warning/error。OCR 只作为辅助检查,不代表覆盖所有应用状态。 + +The npm panel shows the real Homebrew CLI 0.2.7 installation, npm's checked 0.2.8 release, and source-linked dsh-mnemon 0.5.4. No install or update was executed. Agent Query, semantic writes, AI metadata generation, embeddings and cloud Providers were not evaluated. The graph displays 47 elements and 301 connections from two active spaces; its 24 projected graph entities and the Entities page's 39 indexed entities have different scopes. + +npm 面板真实展示 Homebrew CLI 0.2.7、查询到的 npm 0.2.8,以及源码链接的 dsh-mnemon 0.5.4,未执行安装或更新。未评估 Agent 查询、语义写入、AI 元信息生成、嵌入或云 Provider。图谱展示两个激活空间的 47 个元素与 301 条连接;图谱投影中的 24 个实体与实体页的 39 个索引实体统计范围不同。 + +At 390px, long card names and some metrics truncate. The Chinese disabled Documents surface still exposes the Source title `Project Documents`. These are observed v0.5.4 limits, not edited away. This capture does not claim all Host settings or physical phones pass; the [historical settings limitation](../../en/reference/compatibility.md) remains explicitly scoped. + +390px 下长卡片名称与部分指标会截断;中文档案停用页仍显示 Source 标题 `Project Documents`。这些是 v0.5.4 的实际限制,未修饰画面。本次不声称所有 Host 设置页或真实手机均通过,[历史设置限制](../../zh-CN/reference/compatibility.md)继续保留明确边界。 diff --git a/dsh/docs/assets/webui-v0.5.4/manifest.json b/dsh/docs/assets/webui-v0.5.4/manifest.json new file mode 100644 index 000000000..883d4094c --- /dev/null +++ b/dsh/docs/assets/webui-v0.5.4/manifest.json @@ -0,0 +1,902 @@ +{ + "schemaVersion": 1, + "productVersion": "0.5.4", + "sourceRevision": "6902155e0f3877dca5c5d70468f2a2a1784c7b29", + "captureDate": "2026-09-07", + "captureTimezone": "Asia/Shanghai", + "theme": "light", + "hostVersion": "0.1.2-rc.1", + "mnemonCliVersion": "0.2.7", + "browser": "Codex in-app browser", + "backup": { + "formatVersion": 1, + "mode": "full", + "sha256": "acc854f386931f928c2feeee41e8df98f8fcc44b5616aabbd3a1a79efc87c31f", + "zipEntries": 23, + "payloadFiles": 21, + "verifiedPayloadHashes": 21, + "importedThrough": "Native backup UI safe import into a disposable root", + "historicalManifestPluginVersion": "0.1.0", + "includedInRepository": false + }, + "data": { + "runtimeEntries": 20, + "userProfileEntries": 2, + "workingMemoryEntries": 18, + "documents": 12, + "activeDocuments": 11, + "archivedDocuments": 1, + "memorySpaces": 4, + "activeMemorySpaces": 2, + "storedMemories": 27, + "activeSpaceMemories": 21 + }, + "validation": { + "screenshots": 60, + "recordings": 2, + "sourceVideoFrames": 333, + "ocrLegacyLabelMatches": 0, + "ocrErrors": 0, + "browserWarnings": 0, + "browserErrors": 0, + "modelRequests": 0, + "manualVisualInspection": true, + "bothRecordingsDecode": true + }, + "files": [ + { + "file": "en/backup-preview.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:24:44.375Z", + "bytes": 62209, + "sha256": "6bb6f7fffbe06078c232d969920b52b3bc54bf6ed4cbae2fc53f2afa872718aa", + "legacyLabelsDetected": false + }, + { + "file": "en/backup.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:24:35.077Z", + "bytes": 60969, + "sha256": "e899d619255bfb4a984e5599e81056a006c962725e7e57a9c47cd06c1b3f99de", + "legacyLabelsDetected": false + }, + { + "file": "en/content.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:26:34.594Z", + "bytes": 83638, + "sha256": "790703bf1a436d32d7f278ae24a43b195daed5e8f743a5156349ac2add18a4bd", + "legacyLabelsDetected": false + }, + { + "file": "en/document-create.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:25:49.179Z", + "bytes": 83378, + "sha256": "d21d77d8fe195fcb508170e696a0824e19adfa6bc989176e2b6279f5a4c5a870", + "legacyLabelsDetected": false + }, + { + "file": "en/document-reader.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:25:48.675Z", + "bytes": 102149, + "sha256": "16c651463c63d39ee6d83445150eed91a2581d2ef248cd4ee31a7c4e3cac63f3", + "legacyLabelsDetected": false + }, + { + "file": "en/documents-disabled.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:24:46.216Z", + "bytes": 38395, + "sha256": "0b4c70c4ed814989cdce325f0c2a8f70827ce788701e915a7ea985d2ed30c3ab", + "legacyLabelsDetected": false + }, + { + "file": "en/documents.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:25:38.055Z", + "bytes": 81500, + "sha256": "79b15e98e22192155a9e556c4d5e89e1caa3b69c58955dbe3bd33fcdb7df3c7f", + "legacyLabelsDetected": false + }, + { + "file": "en/enhancements.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:24:11.118Z", + "bytes": 58216, + "sha256": "d704ed74a9cc70d15804252c1e9daf69ab82e03edc8cd2fb4bcae9fada1edef0", + "legacyLabelsDetected": false + }, + { + "file": "en/entities.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:26:47.160Z", + "bytes": 84468, + "sha256": "17b55d2d2f8a3c25dc0576766ada6e50923215837101e20c5a02b30c7de3f3c6", + "legacyLabelsDetected": false + }, + { + "file": "en/graph.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:27:03.017Z", + "bytes": 68423, + "sha256": "ae378009fed1ac452f0adf14f617825170bfae540714c72487d10ac926896aed", + "legacyLabelsDetected": false + }, + { + "file": "en/metadata.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:26:14.131Z", + "bytes": 100817, + "sha256": "0c119500432e3da7397df106f4ac9597edeeba66a6248d34f132ddb53a5bdacd", + "legacyLabelsDetected": false + }, + { + "file": "en/native-settings.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:24:28.396Z", + "bytes": 68453, + "sha256": "67cb30370d928f7717a5ebd6fe76efa32722b036e4e88c291175807dd82d23e1", + "legacyLabelsDetected": false + }, + { + "file": "en/recall.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:26:56.843Z", + "bytes": 75974, + "sha256": "cf63cbd84fc720b08520419d3f0b6dabec76ec0369c9b3df792ce0bb56f10029", + "legacyLabelsDetected": false + }, + { + "file": "en/remember.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:26:15.541Z", + "bytes": 73762, + "sha256": "61234dbb8a22f8e64e113080df4c89f210e8de9e058d8229eae8f8ce07111d08", + "legacyLabelsDetected": false + }, + { + "file": "en/runtime-configured.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:33:01.759Z", + "bytes": 80393, + "sha256": "cba9981d444001f90abd0276f41eae00507aa792a60eb2f6d31bb07b31bbbc41", + "legacyLabelsDetected": false + }, + { + "file": "en/runtime-create.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:25:22.606Z", + "bytes": 73497, + "sha256": "a52ad487c793164749009d8f951235c31c933297463449ecf535c083607c85eb", + "legacyLabelsDetected": false + }, + { + "file": "en/runtime.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:25:22.103Z", + "bytes": 80641, + "sha256": "dd4f436170a6996604bd7321c257c2f47e4b24096c203cdf46f195f5461cc2c4", + "legacyLabelsDetected": false + }, + { + "file": "en/settings-layers.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:24:05.151Z", + "bytes": 63275, + "sha256": "8f1e813c0c4f35a76b78a4b6f160969568ae035ab6170efde6bce67d6c74ef21", + "legacyLabelsDetected": false + }, + { + "file": "en/settings.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:23:53.167Z", + "bytes": 61586, + "sha256": "3ae476145dd7662681ac454711c23c9370d9707464d07101360828399ececbaa", + "legacyLabelsDetected": false + }, + { + "file": "en/space-create-mobile.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 390, + "height": 844, + "capturedAt": "2026-09-06T19:27:12.690Z", + "bytes": 39681, + "sha256": "638a0267575cb08e52289d4f924b14c821e468e074c37c714fdb9a60dfe6c52a", + "legacyLabelsDetected": false + }, + { + "file": "en/space-create.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:26:01.006Z", + "bytes": 77386, + "sha256": "807bb543b253be6a7dd2cbce19d68882fbeeebe40535e69ca2dfe4b9759ac5ec", + "legacyLabelsDetected": false + }, + { + "file": "en/space-directory-mobile.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 390, + "height": 844, + "capturedAt": "2026-09-06T19:27:19.114Z", + "bytes": 43720, + "sha256": "836f15ee69d91b6f7e6c55c6820871487288c08dedb7b4b34b743c5c848c6a21", + "legacyLabelsDetected": false + }, + { + "file": "en/space-edit.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:26:01.732Z", + "bytes": 91401, + "sha256": "1e5cbb12ddd0bcbc86a117901158d1f97dec262b0403fd6c924420492084ed0a", + "legacyLabelsDetected": false + }, + { + "file": "en/spaces-mobile.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 390, + "height": 844, + "capturedAt": "2026-09-06T19:27:12.294Z", + "bytes": 47160, + "sha256": "b37d00ce795600dbe37d6775bc3f392836734a059a68b84d2f7ec785c0fe3186", + "legacyLabelsDetected": false + }, + { + "file": "en/spaces.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:26:00.599Z", + "bytes": 103533, + "sha256": "4a4c83a7f60f7685d63dc2362631ec8faa647609d620c9b5c7634f59529bba51", + "legacyLabelsDetected": false + }, + { + "file": "en/status.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:24:59.773Z", + "bytes": 66501, + "sha256": "8e6820ef727ab5ec3aae998b0a12bf1a7c64a9774db4f0bf563506f6ed33b562", + "legacyLabelsDetected": false + }, + { + "file": "en/strategy.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:26:14.776Z", + "bytes": 74974, + "sha256": "deeb82090120bbea72366dfa6b514ea93a7368338a318972eff6fe4bb88e8a33", + "legacyLabelsDetected": false + }, + { + "file": "en/versions-expanded.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:25:20.887Z", + "bytes": 71295, + "sha256": "e4a50e953d02b355a9261b180410a86c3ba61532a1cdd6a744c90ec2ec36a030", + "legacyLabelsDetected": false + }, + { + "file": "en/versions-mobile.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 390, + "height": 844, + "capturedAt": "2026-09-06T19:27:32.838Z", + "bytes": 42485, + "sha256": "187d347639f3c9b8f6f2bc63e39774b80733c92e0e09a65c087fc89bcf7e921c", + "legacyLabelsDetected": false + }, + { + "file": "en/versions.jpg", + "kind": "screenshot", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:25:08.478Z", + "bytes": 69327, + "sha256": "e71cc7f3ad5151aeaeadd51cc161a98d8ea90238cd0fc45ffd49934d670d64d8", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/backup-preview.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:32:08.862Z", + "bytes": 57199, + "sha256": "ee6193a8db7b88823a78920a3d64e00c0ac90ab89f34d2f751b8a356b8c83807", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/backup.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:31:52.682Z", + "bytes": 57008, + "sha256": "0a9f904a5f797a0e6eee4a2cc149f59e86b02e2def6503b530a5f17ae650dc15", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/content.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:29:38.772Z", + "bytes": 77164, + "sha256": "a383343bca3d19391e8b874a4e6f6245b933eea5894374890eb4f9289c7e1fca", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/document-create.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:28:58.128Z", + "bytes": 73894, + "sha256": "a66f86402c6755755fb6d378c7d0bd59f36d0cf393c02c4c873f5c3bf4670bee", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/document-reader.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:28:57.730Z", + "bytes": 98098, + "sha256": "bad5d1eb1823340b5780186b4adca37f911a5d02cee2dce29ad1fa0b8a2ef63e", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/documents-disabled.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:32:10.513Z", + "bytes": 34361, + "sha256": "7485de801f4a258371e393d126de0b79256f35077168afcb37729742e9dac0d3", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/documents.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:28:45.992Z", + "bytes": 77782, + "sha256": "d7f07f064e73682ef3ed9cd80b91251005cdd4b1cabbd028c78156b8de24cfdc", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/enhancements.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:31:37.205Z", + "bytes": 52185, + "sha256": "115d59ca8e1c1f8440a2f17ac216b3598a6d7be9ff81e2a5b98aecac6e1c19e0", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/entities.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:32:22.210Z", + "bytes": 76889, + "sha256": "622d6787254e3a2296ca7b891ead7cdd656657d3dd557c3c4b8da807d558bc5b", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/graph.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:32:50.610Z", + "bytes": 65922, + "sha256": "8216626041baecccc1fa8a6343350f735b70bcee92db6db6ca57c13e70b4244b", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/metadata.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:29:26.979Z", + "bytes": 91266, + "sha256": "1bc63a9eee0cb932d9af7895768759a7a04ea62958e4284a5a8f1a7cbf880709", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/native-settings.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:31:43.465Z", + "bytes": 61252, + "sha256": "1d20893a34729bf8c9cdbf7e3b543334c08464de2e00188e53a13b532783efd2", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/recall.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:32:30.596Z", + "bytes": 71831, + "sha256": "53a48cd6fe952ea3703e5fd49a88febddd36347af920c58f0d104564d8225525", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/remember.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:29:28.378Z", + "bytes": 69674, + "sha256": "81748e27e77d0554b4f9fa2f0e584d736188d18c8c5f7366c1585f6618c58f07", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/runtime-configured.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:33:00.012Z", + "bytes": 68779, + "sha256": "c436ebbbb4ac0ddbec102a0dee16aa4532273ee822d1f3f2f870dfb37f5a07cf", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/runtime-create.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:28:29.759Z", + "bytes": 64400, + "sha256": "df7d46440b750c4b49c66260decf651fe0a2ce60f57c064f810652d6b5957d36", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/runtime.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:28:29.320Z", + "bytes": 68991, + "sha256": "2298d5178fe91097baa39c3c6ae47ba86058e2a2c5a9834abbc91926a30115dd", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/settings-layers.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:31:31.537Z", + "bytes": 50828, + "sha256": "d42d0663d1d8e3c8fad62116be60a8032b87e9571d05a5a0d9a3b36c5c53d2e2", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/settings.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:31:22.602Z", + "bytes": 57085, + "sha256": "8cc5edcf2a51afafcd9a340c55b40b41dee5e303cf5f44573fad65b14585d9c4", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/space-create-mobile.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 390, + "height": 844, + "capturedAt": "2026-09-06T19:27:48.071Z", + "bytes": 37359, + "sha256": "c26f49c3d1f87e7ed13e2b02edca9689a13c2da69b8c5655f8482c9857f3bb89", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/space-create.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:29:10.464Z", + "bytes": 70285, + "sha256": "7ef737353a6deb140e13d5c7e537a2613a3573a121d28ebaad7417eb92fdc4c7", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/space-directory-mobile.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 390, + "height": 844, + "capturedAt": "2026-09-06T19:27:54.394Z", + "bytes": 44406, + "sha256": "a365a94fb348e54d129a06e8307b2302fe22e080e4dc71edc9839925f7151295", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/space-edit.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:29:11.176Z", + "bytes": 88583, + "sha256": "cbea5657d782286f19d4e091853fe220f4acc1c7a1bb3887067615d779fa4c42", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/spaces-mobile.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 390, + "height": 844, + "capturedAt": "2026-09-06T19:27:47.701Z", + "bytes": 43481, + "sha256": "a22bc5560bfcc450283279904c691254d288d00d57714b5a8c9825d598e66131", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/spaces.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:29:10.058Z", + "bytes": 97400, + "sha256": "670992a3682c89aee5e4e06a01c3b8b77e1d99043d6f59e949a8e0014a7eecac", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/status.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:28:08.541Z", + "bytes": 60411, + "sha256": "a59369ae4b1863dca22e6b4c2c7395322c441ee62c8f6d1255ce7bfcfd07b35f", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/strategy.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:29:27.682Z", + "bytes": 73910, + "sha256": "11bfcb8bc41af5eef3ef3c8706e9ec2decae65cecaf5c123d5aa7d5221cc51e8", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/versions-expanded.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:28:28.239Z", + "bytes": 68253, + "sha256": "ae3efaecd655635c36d70c0b7a4baf41940e7185ba4bc614930c87a62be45025", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/versions-mobile.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 390, + "height": 844, + "capturedAt": "2026-09-06T19:28:00.763Z", + "bytes": 36734, + "sha256": "470ad1065051a91c4ccd887a1a4ed174c6cd012eadb5585c1671175efcda38b7", + "legacyLabelsDetected": false + }, + { + "file": "zh-CN/versions.jpg", + "kind": "screenshot", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:28:15.563Z", + "bytes": 63193, + "sha256": "6e4c738ecf7062e9cfadaedb64e797c71313becf34970a383ea450194e5c0942", + "legacyLabelsDetected": false + }, + { + "file": "en/demo.mp4", + "kind": "recording", + "locale": "en", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:37:14.709Z", + "captureDurationMs": 41669, + "durationSeconds": 41.875, + "sourceFrames": 166, + "targetCaptureFps": 4, + "captureMethod": "Continuous actual browser screenshots during uninterrupted UI actions; playback follows source timestamps.", + "orderedSourceFrameIndexSha256": "75769fbb4a2f3a184d0980ba1b27013657e60d69b5a8d4014cb13aa066de8ebd", + "codec": "h264", + "pixelFormat": "yuvj420p", + "outputFps": 24, + "encodedFrames": 1005, + "audio": false, + "fastStart": true, + "chapters": [ + { + "label": "Memory Space directory", + "startMs": 0 + }, + { + "label": "Create Memory Space draft", + "startMs": 4292 + }, + { + "label": "Distill Memory draft", + "startMs": 8867 + }, + { + "label": "Keyword recall from imported memory", + "startMs": 12777 + }, + { + "label": "Imported entity connections", + "startMs": 17376 + }, + { + "label": "Live relation graph", + "startMs": 21962 + }, + { + "label": "Imported system totals", + "startMs": 26245 + }, + { + "label": "npm installation and maintenance", + "startMs": 29511 + }, + { + "label": "Subpackage maintenance", + "startMs": 33793 + }, + { + "label": "Runtime memory imported from backup", + "startMs": 38643 + } + ], + "bytes": 1409802, + "sha256": "2b774a3de3af666158430d81c7ef2ddb5b78fd60f30e95b46bdce6094c0093c5", + "legacyLabelsDetectedInSourceFrames": false, + "decodeErrors": 0 + }, + { + "file": "zh-CN/demo.mp4", + "kind": "recording", + "locale": "zh-CN", + "theme": "light", + "width": 1280, + "height": 720, + "capturedAt": "2026-09-06T19:38:24.155Z", + "captureDurationMs": 41925, + "durationSeconds": 42.125, + "sourceFrames": 167, + "targetCaptureFps": 4, + "captureMethod": "Continuous actual browser screenshots during uninterrupted UI actions; playback follows source timestamps.", + "orderedSourceFrameIndexSha256": "f8964d61cfdc046176bfa42de7eebd863454e083f2baa55d191f7e1a0d42852d", + "codec": "h264", + "pixelFormat": "yuvj420p", + "outputFps": 24, + "encodedFrames": 1011, + "audio": false, + "fastStart": true, + "chapters": [ + { + "label": "Memory Space directory", + "startMs": 0 + }, + { + "label": "Create Memory Space draft", + "startMs": 4303 + }, + { + "label": "Distill Memory draft", + "startMs": 8870 + }, + { + "label": "Keyword recall from imported memory", + "startMs": 12781 + }, + { + "label": "Imported entity connections", + "startMs": 17413 + }, + { + "label": "Live relation graph", + "startMs": 21998 + }, + { + "label": "Imported system totals", + "startMs": 26265 + }, + { + "label": "npm installation and maintenance", + "startMs": 29533 + }, + { + "label": "Subpackage maintenance", + "startMs": 33818 + }, + { + "label": "Runtime memory imported from backup", + "startMs": 38681 + } + ], + "bytes": 1290091, + "sha256": "ba52e708186388487e305a000708f4789115db7e12ceda8a79c414f9bc539591", + "legacyLabelsDetectedInSourceFrames": false, + "decodeErrors": 0 + } + ] +} diff --git a/dsh/docs/assets/webui-v0.5.4/repository-audit.json b/dsh/docs/assets/webui-v0.5.4/repository-audit.json new file mode 100644 index 000000000..bd83fab72 --- /dev/null +++ b/dsh/docs/assets/webui-v0.5.4/repository-audit.json @@ -0,0 +1,3472 @@ +{ + "schemaVersion": 1, + "auditDate": "2026-09-07", + "baselineRevision": "6902155e0f3877dca5c5d70468f2a2a1784c7b29", + "scope": "All pre-existing repository image/video files under docs and .github, and Markdown/HTML media references in repository Markdown documents. External PR/Issue references are not enumerated.", + "summary": { + "existingMedia": 142, + "unchangedExistingMedia": 142, + "currentArchitectureDiagrams": 8, + "retainedHistoricalMedia": 134, + "existingPngJpegOcrChecked": 135, + "existingPngJpegLegacyLabels": 68, + "existingOcrErrors": 0, + "currentGuideMediaReferences": 64, + "currentGuideHistoricalMediaReferences": 0, + "newLightScreenshots": 60, + "newLightRecordings": 2 + }, + "ocrMethod": "macOS Vision accurate text recognition, zh-Hans and en-US, language correction disabled; case/whitespace-normalized old Chinese noun and memory body/bodies detection. SVG sources read as text; historical motion files retained without a new frame-level audit. OCR results support manual inspection.", + "currentGallery": "docs/assets/webui-v0.5.4/README.md", + "currentGuideMediaReferences": [ + { + "file": "README.md", + "line": 63, + "target": "docs/assets/diagrams/en/composable-memory.png" + }, + { + "file": "README.md", + "line": 20, + "target": "docs/assets/webui-v0.5.4/en/spaces.jpg" + }, + { + "file": "README.md", + "line": 26, + "target": "docs/assets/webui-v0.5.4/en/demo.mp4" + }, + { + "file": "README.zh-CN.md", + "line": 63, + "target": "docs/assets/diagrams/zh-CN/composable-memory.png" + }, + { + "file": "README.zh-CN.md", + "line": 20, + "target": "docs/assets/webui-v0.5.4/zh-CN/spaces.jpg" + }, + { + "file": "README.zh-CN.md", + "line": 26, + "target": "docs/assets/webui-v0.5.4/zh-CN/demo.mp4" + }, + { + "file": "docs/en/guides/getting-started.md", + "line": 177, + "target": "docs/assets/webui-v0.5.4/en/status.jpg" + }, + { + "file": "docs/en/guides/operations.md", + "line": 25, + "target": "docs/assets/webui-v0.5.4/en/status.jpg" + }, + { + "file": "docs/en/guides/operations.md", + "line": 25, + "target": "docs/assets/webui-v0.5.4/en/status.jpg" + }, + { + "file": "docs/en/guides/operations.md", + "line": 33, + "target": "docs/assets/webui-v0.5.4/en/versions.jpg" + }, + { + "file": "docs/en/guides/operations.md", + "line": 33, + "target": "docs/assets/webui-v0.5.4/en/versions.jpg" + }, + { + "file": "docs/en/guides/operations.md", + "line": 44, + "target": "docs/assets/webui-v0.5.4/en/versions-expanded.jpg" + }, + { + "file": "docs/en/guides/operations.md", + "line": 73, + "target": "docs/assets/webui-v0.5.4/en/backup-preview.jpg" + }, + { + "file": "docs/en/guides/ui-guide.md", + "line": 11, + "target": "docs/assets/webui-v0.5.4/en/demo.mp4" + }, + { + "file": "docs/en/guides/ui-guide.md", + "line": 35, + "target": "docs/assets/webui-v0.5.4/en/status.jpg" + }, + { + "file": "docs/en/guides/ui-guide.md", + "line": 43, + "target": "docs/assets/webui-v0.5.4/en/versions.jpg" + }, + { + "file": "docs/en/guides/ui-guide.md", + "line": 51, + "target": "docs/assets/webui-v0.5.4/en/enhancements.jpg" + }, + { + "file": "docs/en/guides/ui-guide.md", + "line": 63, + "target": "docs/assets/webui-v0.5.4/en/runtime.jpg" + }, + { + "file": "docs/en/guides/ui-guide.md", + "line": 71, + "target": "docs/assets/webui-v0.5.4/en/documents.jpg" + }, + { + "file": "docs/en/guides/ui-guide.md", + "line": 85, + "target": "docs/assets/webui-v0.5.4/en/spaces.jpg" + }, + { + "file": "docs/en/guides/ui-guide.md", + "line": 94, + "target": "docs/assets/webui-v0.5.4/en/graph.jpg" + }, + { + "file": "docs/en/guides/ui-guide.md", + "line": 111, + "target": "docs/assets/webui-v0.5.4/en/space-create.jpg" + }, + { + "file": "docs/en/guides/ui-guide.md", + "line": 136, + "target": "docs/assets/webui-v0.5.4/en/recall.jpg" + }, + { + "file": "docs/en/guides/ui-guide.md", + "line": 148, + "target": "docs/assets/webui-v0.5.4/en/content.jpg" + }, + { + "file": "docs/en/guides/ui-guide.md", + "line": 152, + "target": "docs/assets/webui-v0.5.4/en/entities.jpg" + }, + { + "file": "docs/en/guides/ui-guide.md", + "line": 156, + "target": "docs/assets/webui-v0.5.4/en/spaces-mobile.jpg" + }, + { + "file": "docs/en/guides/ui-guide.md", + "line": 156, + "target": "docs/assets/webui-v0.5.4/en/space-directory-mobile.jpg" + }, + { + "file": "docs/en/guides/ui-guide.md", + "line": 156, + "target": "docs/assets/webui-v0.5.4/en/space-create-mobile.jpg" + }, + { + "file": "docs/en/guides/ui-guide.md", + "line": 156, + "target": "docs/assets/webui-v0.5.4/en/versions-mobile.jpg" + }, + { + "file": "docs/en/reference/configuration.md", + "line": 115, + "target": "docs/assets/webui-v0.5.4/en/runtime.jpg" + }, + { + "file": "docs/en/reference/configuration.md", + "line": 117, + "target": "docs/assets/webui-v0.5.4/en/runtime-configured.jpg" + }, + { + "file": "docs/en/reference/configuration.md", + "line": 151, + "target": "docs/assets/webui-v0.5.4/en/settings-layers.jpg" + }, + { + "file": "docs/en/reference/configuration.md", + "line": 151, + "target": "docs/assets/webui-v0.5.4/en/settings-layers.jpg" + }, + { + "file": "docs/en/reference/configuration.md", + "line": 155, + "target": "docs/assets/webui-v0.5.4/en/documents-disabled.jpg" + }, + { + "file": "docs/en/reference/configuration.md", + "line": 155, + "target": "docs/assets/webui-v0.5.4/en/documents-disabled.jpg" + }, + { + "file": "docs/zh-CN/guides/getting-started.md", + "line": 177, + "target": "docs/assets/webui-v0.5.4/zh-CN/status.jpg" + }, + { + "file": "docs/zh-CN/guides/operations.md", + "line": 25, + "target": "docs/assets/webui-v0.5.4/zh-CN/status.jpg" + }, + { + "file": "docs/zh-CN/guides/operations.md", + "line": 25, + "target": "docs/assets/webui-v0.5.4/zh-CN/status.jpg" + }, + { + "file": "docs/zh-CN/guides/operations.md", + "line": 33, + "target": "docs/assets/webui-v0.5.4/zh-CN/versions.jpg" + }, + { + "file": "docs/zh-CN/guides/operations.md", + "line": 33, + "target": "docs/assets/webui-v0.5.4/zh-CN/versions.jpg" + }, + { + "file": "docs/zh-CN/guides/operations.md", + "line": 44, + "target": "docs/assets/webui-v0.5.4/zh-CN/versions-expanded.jpg" + }, + { + "file": "docs/zh-CN/guides/operations.md", + "line": 73, + "target": "docs/assets/webui-v0.5.4/zh-CN/backup-preview.jpg" + }, + { + "file": "docs/zh-CN/guides/ui-guide.md", + "line": 11, + "target": "docs/assets/webui-v0.5.4/zh-CN/demo.mp4" + }, + { + "file": "docs/zh-CN/guides/ui-guide.md", + "line": 35, + "target": "docs/assets/webui-v0.5.4/zh-CN/status.jpg" + }, + { + "file": "docs/zh-CN/guides/ui-guide.md", + "line": 43, + "target": "docs/assets/webui-v0.5.4/zh-CN/versions.jpg" + }, + { + "file": "docs/zh-CN/guides/ui-guide.md", + "line": 51, + "target": "docs/assets/webui-v0.5.4/zh-CN/enhancements.jpg" + }, + { + "file": "docs/zh-CN/guides/ui-guide.md", + "line": 63, + "target": "docs/assets/webui-v0.5.4/zh-CN/runtime.jpg" + }, + { + "file": "docs/zh-CN/guides/ui-guide.md", + "line": 73, + "target": "docs/assets/webui-v0.5.4/zh-CN/documents.jpg" + }, + { + "file": "docs/zh-CN/guides/ui-guide.md", + "line": 89, + "target": "docs/assets/webui-v0.5.4/zh-CN/spaces.jpg" + }, + { + "file": "docs/zh-CN/guides/ui-guide.md", + "line": 98, + "target": "docs/assets/webui-v0.5.4/zh-CN/graph.jpg" + }, + { + "file": "docs/zh-CN/guides/ui-guide.md", + "line": 117, + "target": "docs/assets/webui-v0.5.4/zh-CN/space-create.jpg" + }, + { + "file": "docs/zh-CN/guides/ui-guide.md", + "line": 142, + "target": "docs/assets/webui-v0.5.4/zh-CN/recall.jpg" + }, + { + "file": "docs/zh-CN/guides/ui-guide.md", + "line": 154, + "target": "docs/assets/webui-v0.5.4/zh-CN/content.jpg" + }, + { + "file": "docs/zh-CN/guides/ui-guide.md", + "line": 158, + "target": "docs/assets/webui-v0.5.4/zh-CN/entities.jpg" + }, + { + "file": "docs/zh-CN/guides/ui-guide.md", + "line": 162, + "target": "docs/assets/webui-v0.5.4/zh-CN/spaces-mobile.jpg" + }, + { + "file": "docs/zh-CN/guides/ui-guide.md", + "line": 162, + "target": "docs/assets/webui-v0.5.4/zh-CN/space-directory-mobile.jpg" + }, + { + "file": "docs/zh-CN/guides/ui-guide.md", + "line": 162, + "target": "docs/assets/webui-v0.5.4/zh-CN/space-create-mobile.jpg" + }, + { + "file": "docs/zh-CN/guides/ui-guide.md", + "line": 162, + "target": "docs/assets/webui-v0.5.4/zh-CN/versions-mobile.jpg" + }, + { + "file": "docs/zh-CN/reference/configuration.md", + "line": 117, + "target": "docs/assets/webui-v0.5.4/zh-CN/runtime.jpg" + }, + { + "file": "docs/zh-CN/reference/configuration.md", + "line": 119, + "target": "docs/assets/webui-v0.5.4/zh-CN/runtime-configured.jpg" + }, + { + "file": "docs/zh-CN/reference/configuration.md", + "line": 153, + "target": "docs/assets/webui-v0.5.4/zh-CN/settings-layers.jpg" + }, + { + "file": "docs/zh-CN/reference/configuration.md", + "line": 153, + "target": "docs/assets/webui-v0.5.4/zh-CN/settings-layers.jpg" + }, + { + "file": "docs/zh-CN/reference/configuration.md", + "line": 157, + "target": "docs/assets/webui-v0.5.4/zh-CN/documents-disabled.jpg" + }, + { + "file": "docs/zh-CN/reference/configuration.md", + "line": 157, + "target": "docs/assets/webui-v0.5.4/zh-CN/documents-disabled.jpg" + } + ], + "existingMedia": [ + { + "file": "docs/assets/diagrams/en/composable-memory.png", + "bytes": 104006, + "sha256": "66440c056298542936879e9a1304e82121581483c085af3983dd0f77871c679c", + "classification": "current_architecture_diagram", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "README.md", + "line": 63 + } + ], + "referencesAfter": [ + { + "file": "README.md", + "line": 63 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/diagrams/en/composable-memory.svg", + "bytes": 2707, + "sha256": "9cd310e48929cd5ee60fbdd43cc9ba6bbbefeff2e7a6a622f54cb519f43481a2", + "classification": "current_architecture_diagram", + "action": "retained_unchanged", + "ocr": { + "checked": false, + "legacyLabelsDetected": null + }, + "referencesBefore": [ + { + "file": "docs/assets/diagrams/README.md", + "line": 3 + }, + { + "file": "docs/assets/diagrams/README.md", + "line": 5 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/diagrams/README.md", + "line": 3 + }, + { + "file": "docs/assets/diagrams/README.md", + "line": 5 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/diagrams/en/memory-system-flow.png", + "bytes": 283110, + "sha256": "ac51974965c65a30876b8690e17e8eab04fa45e4c91e0677f6a530bc4686b535", + "classification": "current_architecture_diagram", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/diagrams/en/memory-system-flow.svg", + "bytes": 7892, + "sha256": "f4041fced132d8e6bc0092cef18a8c1d08a4e624d05ce38e12cfbf705c8e42dd", + "classification": "current_architecture_diagram", + "action": "retained_unchanged", + "ocr": { + "checked": false, + "legacyLabelsDetected": null + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/diagrams/zh-CN/composable-memory.png", + "bytes": 97860, + "sha256": "9ee85b4f1287ed109aad6201f1f2a68d5f0c28999baeddb127526b305be40bb5", + "classification": "current_architecture_diagram", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "README.zh-CN.md", + "line": 63 + } + ], + "referencesAfter": [ + { + "file": "README.zh-CN.md", + "line": 63 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/diagrams/zh-CN/composable-memory.svg", + "bytes": 2794, + "sha256": "0b9a022bd2045837547384b0465942f3896d527b749a2ebb9125396ecc3ba5b3", + "classification": "current_architecture_diagram", + "action": "retained_unchanged", + "ocr": { + "checked": false, + "legacyLabelsDetected": null + }, + "referencesBefore": [ + { + "file": "docs/assets/diagrams/README.md", + "line": 3 + }, + { + "file": "docs/assets/diagrams/README.md", + "line": 5 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/diagrams/README.md", + "line": 3 + }, + { + "file": "docs/assets/diagrams/README.md", + "line": 5 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/diagrams/zh-CN/memory-system-flow.png", + "bytes": 393004, + "sha256": "72d22b857692480b21f77b84b0221324d8e103117fffa32a1138dc16b7e14cd5", + "classification": "current_architecture_diagram", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/diagrams/zh-CN/memory-system-flow.svg", + "bytes": 7803, + "sha256": "0692841af8bee1bef20f04ca151afe72ffb6a6f3b2b3f5525dbac348cbddbda6", + "classification": "current_architecture_diagram", + "action": "retained_unchanged", + "ocr": { + "checked": false, + "legacyLabelsDetected": null + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/issues/172/reproduction-runtime-pin.jpg", + "bytes": 58513, + "sha256": "deb4301587d43b179739183dd8f846229628710ce427bd746dcd960574677c20", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/issues/172/verified-new-turn-completes.jpg", + "bytes": 62401, + "sha256": "1dff9daa71b9b6b963568b89f9623d332caacbe46f250cd72000cd199ac582c9", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/issues/176/reproduction-read-only-notice.jpg", + "bytes": 48450, + "sha256": "8ac1b14dcd98ab998cfe31ebdecd4ae22d51de796ea29b9c674802ceeece5fb3", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/issues/176/reproduction-read-only.jpg", + "bytes": 50887, + "sha256": "dd22093e319c880e6b445852260abec65cc5270932a579aca4b705c4a3f3dbe6", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/issues/176/verified-editable.jpg", + "bytes": 55873, + "sha256": "0146a98c34f2076ab0db771a96e5f73331213f13ffc54fe5ac752f505dce04cf", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/issues/176/verified-writable-footer.jpg", + "bytes": 49070, + "sha256": "b38d63d9277b0f71a3a22dee68ea17baed24f64322080a942e9e4c467d20d29f", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/issues/185/verified-all-mnemon-plugins-disabled.jpg", + "bytes": 45513, + "sha256": "8428d2dbafb2fc5c28ef84839b138507baea26eceb7b1c310fad1a757059013c", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/media/dsh-mnemon-memory-system-demo-poster.jpg", + "bytes": 153837, + "sha256": "5629fff9eefa4127705138043d05dd570fdb9ea84b41e8e009825df35623b56c", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/media/dsh-mnemon-memory-system-demo.gif", + "bytes": 8495823, + "sha256": "cf488d6e69a68f9d68012fd9df5036a403d7dfdcef17abd925a21cab44fa2cef", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": false, + "legacyLabelsDetected": null + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/media/dsh-mnemon-memory-system-demo.mp4", + "bytes": 5512802, + "sha256": "d617ad0754fb415810b9c7bc81cc8e97ff27092171bca2cfe1dbc1c6bf7985a4", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": false, + "legacyLabelsDetected": null + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/memory-space-terminology/en/content.jpg", + "bytes": 99265, + "sha256": "276ba55eca34cb166f77181bd68b89ff957ef0e11ba2068cb9aa7babb29ce33c", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 22 + }, + { + "file": "docs/en/guides/ui-guide.md", + "line": 142 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 26 + } + ], + "currentGuideReferencesReplaced": true + }, + { + "file": "docs/assets/memory-space-terminology/en/create-mobile.jpg", + "bytes": 41643, + "sha256": "ac6de77aa745ac6f10f9631266751de357e41ae3b6bdb87627cc5159be2f18a5", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 26 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 30 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/memory-space-terminology/en/create.jpg", + "bytes": 88988, + "sha256": "65779522334ac63c88bc5553848c0d018781fa7980290a407ef8266ad6ccf215", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 23 + }, + { + "file": "docs/en/guides/ui-guide.md", + "line": 107 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 27 + } + ], + "currentGuideReferencesReplaced": true + }, + { + "file": "docs/assets/memory-space-terminology/en/documents.jpg", + "bytes": 104792, + "sha256": "9c4b99fd14d5bcfe2404d33b564f2bb20a728ba9426d02b11dcb3ff81d5a0b1d", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 20 + }, + { + "file": "docs/en/guides/ui-guide.md", + "line": 69 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 24 + } + ], + "currentGuideReferencesReplaced": true + }, + { + "file": "docs/assets/memory-space-terminology/en/enhancements.jpg", + "bytes": 74775, + "sha256": "a263f91445e5f4b3e70acab8696f6837c5776e3a8cfb1a5251a15f60e3754858", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 24 + }, + { + "file": "docs/en/guides/ui-guide.md", + "line": 49 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 28 + } + ], + "currentGuideReferencesReplaced": true + }, + { + "file": "docs/assets/memory-space-terminology/en/runtime.jpg", + "bytes": 81166, + "sha256": "a4a121d100ad4b420f66620af5f8f89205f098732effd64bf928fefe6c6a5cc1", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 19 + }, + { + "file": "docs/en/guides/ui-guide.md", + "line": 61 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 23 + } + ], + "currentGuideReferencesReplaced": true + }, + { + "file": "docs/assets/memory-space-terminology/en/spaces-mobile.jpg", + "bytes": 49850, + "sha256": "98f76c0f693c187a0ae95c55a5e984e9f4c94e2f27057dcd9da6d5a6860e3614", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 25 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 29 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/memory-space-terminology/en/spaces.jpg", + "bytes": 100721, + "sha256": "a6e922edcf9eb3c479351f524c507ab39db725bf49910a9b6436b6d195ca6983", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "README.md", + "line": 20 + }, + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 21 + }, + { + "file": "docs/en/guides/ui-guide.md", + "line": 83 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 25 + } + ], + "currentGuideReferencesReplaced": true + }, + { + "file": "docs/assets/memory-space-terminology/en/status.jpg", + "bytes": 87259, + "sha256": "85ebddd17f501823177cdca120f05e66582c1dedc0eb9d2aea939bf635b83818", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 18 + }, + { + "file": "docs/en/guides/ui-guide.md", + "line": 35 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 22 + } + ], + "currentGuideReferencesReplaced": true + }, + { + "file": "docs/assets/memory-space-terminology/zh-CN/content.jpg", + "bytes": 87440, + "sha256": "c50d34780f6df8268bc3b31f5f86a4c68d4685e06d75d03969d0f8b8f216f563", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 22 + }, + { + "file": "docs/zh-CN/guides/ui-guide.md", + "line": 148 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 26 + } + ], + "currentGuideReferencesReplaced": true + }, + { + "file": "docs/assets/memory-space-terminology/zh-CN/create-mobile.jpg", + "bytes": 38565, + "sha256": "7c8cb3a13b8fdf785b46827874058ff9f36bd8ae68a4a236c2c2d671c2cba3d8", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 26 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 30 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/memory-space-terminology/zh-CN/create.jpg", + "bytes": 77940, + "sha256": "02d9548aa645fc71be0ac5a43e05cd609343baa83b74b51437f9eec4b3f4c871", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 23 + }, + { + "file": "docs/zh-CN/guides/ui-guide.md", + "line": 113 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 27 + } + ], + "currentGuideReferencesReplaced": true + }, + { + "file": "docs/assets/memory-space-terminology/zh-CN/documents.jpg", + "bytes": 99636, + "sha256": "aa2f16a1753111a124ed640d616b923aec9821f0f568678e0bfed12f85ec8da5", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 20 + }, + { + "file": "docs/zh-CN/guides/ui-guide.md", + "line": 71 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 24 + } + ], + "currentGuideReferencesReplaced": true + }, + { + "file": "docs/assets/memory-space-terminology/zh-CN/enhancements.jpg", + "bytes": 72146, + "sha256": "c67a5fbf4354855e05f33eba9adc1f3c46a624beefb372495e16139cb97307c5", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 24 + }, + { + "file": "docs/zh-CN/guides/ui-guide.md", + "line": 49 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 28 + } + ], + "currentGuideReferencesReplaced": true + }, + { + "file": "docs/assets/memory-space-terminology/zh-CN/runtime.jpg", + "bytes": 75382, + "sha256": "f600d15ac73b86489947f68d181ba6dc54bd35e14ea541dedfed4d27d724de81", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 19 + }, + { + "file": "docs/zh-CN/guides/ui-guide.md", + "line": 61 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 23 + } + ], + "currentGuideReferencesReplaced": true + }, + { + "file": "docs/assets/memory-space-terminology/zh-CN/spaces-mobile.jpg", + "bytes": 45334, + "sha256": "05915d9824173b22d38709c097b314c3f56936e4a750e3947b2777780c73368f", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 25 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 29 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/memory-space-terminology/zh-CN/spaces.jpg", + "bytes": 89662, + "sha256": "0a0f62ead04951da1c0ed8c7cfffc0187fbd6f42edd7125cffb231bda2bb795c", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "README.zh-CN.md", + "line": 20 + }, + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 21 + }, + { + "file": "docs/zh-CN/guides/ui-guide.md", + "line": 87 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 25 + } + ], + "currentGuideReferencesReplaced": true + }, + { + "file": "docs/assets/memory-space-terminology/zh-CN/status.jpg", + "bytes": 81878, + "sha256": "bc6c4b97d73f0a0e0832c9e85082e5bf8a6209d3478f9ed7cbfb9a1cd9c8e9a8", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 18 + }, + { + "file": "docs/zh-CN/guides/ui-guide.md", + "line": 35 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/memory-space-terminology/README.md", + "line": 22 + } + ], + "currentGuideReferencesReplaced": true + }, + { + "file": "docs/assets/screenshots/ai-metadata-dialog.png", + "bytes": 772732, + "sha256": "c892f499ce276ba9d3fdb3d107603f6939350e8fc8a4554659fe18dd77a9b844", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/screenshots/distillation-strategy.png", + "bytes": 654113, + "sha256": "1fe19d0210f346f7bf52cd06609e997b8b0df4aa8e57fa3fb34c7088c2a5464d", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/screenshots/document-create-dialog.png", + "bytes": 376329, + "sha256": "4cdfb966b19b70972779f9b98161d55fbaf4ce33a7d9b2615d465aca569251a1", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/screenshots/documents-markdown.png", + "bytes": 422644, + "sha256": "d2b83c1159e1409a2d8e48368c45fe3ad7355cd9e60da1cbe1681c3f10a220c3", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/screenshots/entities-context.png", + "bytes": 126906, + "sha256": "6738958f23bf7f8b6e8e442ae60a9530b98ee47817435c5044826b74acded1b8", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/screenshots/memory-content.png", + "bytes": 554952, + "sha256": "46ead323210fae81c64c5ea82c47bc943955b168f63993c918b12b8626d79411", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/screenshots/memory-space-create-dialog.png", + "bytes": 623899, + "sha256": "ced6c4560d08dfb960491e35f6e2cb00df75a375ac198a1031166353291c626f", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/screenshots/mobile-dialogs-catalog-memory-spaces.png", + "bytes": 149354, + "sha256": "a939da0a83862657421ae7b54dc1b959019d47fe822074c643f0484e3eff5407", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/screenshots/mobile-dialogs-catalog-runtime-documents.png", + "bytes": 271993, + "sha256": "60ed59309984e8f0acd0227de9e5a6952fbf6292e67a29eb7176e2ac25805d1b", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/screenshots/mobile-dialogs-catalog-system-conversation.png", + "bytes": 108610, + "sha256": "6a133eebdf04f09a50f13319937324966997151dbd73509c9d47342e50258bef", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/screenshots/mobile-dialogs-catalog-workflows.png", + "bytes": 195548, + "sha256": "6bcf02464e01eda0e980615a6dbe7736c79cfa7143ce476da668d55e589afb97", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/screenshots/mobile-dialogs-confirm-before-after.png", + "bytes": 190599, + "sha256": "3c8ce4a25e831ab9deb55511e308c701dba4cae22e769b4e914e79d5267b44c9", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/screenshots/mobile-dialogs-preview-before-after.png", + "bytes": 265094, + "sha256": "040ff3a1499d133cef54445731f44e60c6731993c172f4922408dea3d314ce15", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/screenshots/mobile-dialogs-responsive-before-after.png", + "bytes": 746047, + "sha256": "0261a37a5571f5f3c4f32a94cd637370225eb2362563a5dbc97106fb84ad449a", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/screenshots/mobile-dialogs-short-viewport-before-after.png", + "bytes": 149177, + "sha256": "ea6011c3c322a0f0f9b7f26ad9fc9bfc4183d337a3900412805b78020a34f105", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/screenshots/mobile-dialogs-version-before-after.png", + "bytes": 78485, + "sha256": "93173f9a831dcdcc3da68a0e0fa76f8cfda73adca6708b29f1737943afa16f48", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/screenshots/overview-memory-graph.png", + "bytes": 594993, + "sha256": "8664b481f8e2e56d3b9110e9895d44a4cb19db0d86a2e2ab90cc129848ca6427", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/screenshots/recall-agent-answer.png", + "bytes": 403824, + "sha256": "3c16b038fa1ebd962fdf5a08171a5a1be442af6a53e947df95f795148f4d2005", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/screenshots/remember-dialog.png", + "bytes": 597616, + "sha256": "27d27dd3f8d031b89256772cd695ca172d5cdcca2a79dd1709b58d64eeab21b4", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/screenshots/runtime-memory-add.png", + "bytes": 396530, + "sha256": "31ebdac12fd38106b4a76b14c4a95ed3d2a1afa83b5e9c31da07d1bd70376d3b", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/screenshots/runtime-memory-capacity-configuration.png", + "bytes": 276873, + "sha256": "32c22ff351c276020f8f1809832df36edfc0df99226a54f49b35bbe84c8dba05", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/en/reference/configuration.md", + "line": 115 + }, + { + "file": "docs/en/reference/configuration.md", + "line": 115 + }, + { + "file": "docs/zh-CN/reference/configuration.md", + "line": 117 + }, + { + "file": "docs/zh-CN/reference/configuration.md", + "line": 117 + } + ], + "referencesAfter": [], + "currentGuideReferencesReplaced": true + }, + { + "file": "docs/assets/screenshots/runtime-memory.png", + "bytes": 367273, + "sha256": "1cd3416548fea03007ebc7afe198926ddd2f7a7059a7003e6986515db0d08bf7", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/screenshots/settings-entry-placement.png", + "bytes": 61734, + "sha256": "adf7ee5ee749e04b746b32d3b594b5cf3b15284094c5a3e347387fba15637225", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/builtin-139/README.md", + "line": 69 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/builtin-139/README.md", + "line": 69 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/screenshots/settings-memory-layers-en.jpg", + "bytes": 62035, + "sha256": "6c2117aaa2301e009c74df3211337f2432ff5f028bd03180fa18c69e2844faf4", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/en/reference/configuration.md", + "line": 149 + }, + { + "file": "docs/en/reference/configuration.md", + "line": 149 + } + ], + "referencesAfter": [], + "currentGuideReferencesReplaced": true + }, + { + "file": "docs/assets/screenshots/settings-memory-layers-zh-CN.jpg", + "bytes": 55417, + "sha256": "dbb53378304c6b46049979f264fd0a0f2fb2f1d288fdf362b7f848f76b9c6c26", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/zh-CN/reference/configuration.md", + "line": 151 + }, + { + "file": "docs/zh-CN/reference/configuration.md", + "line": 151 + } + ], + "referencesAfter": [], + "currentGuideReferencesReplaced": true + }, + { + "file": "docs/assets/screenshots/settings-memory-system.png", + "bytes": 234159, + "sha256": "63e6ab6a7b7486e445174d6477d36e4a2f16a68d754c8bef90fde742aa283008", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/screenshots/settings-native-embeddings.jpg", + "bytes": 71907, + "sha256": "fe0b882b4b765486fb89c30fda16b8b062615a4fb0744018583ff8cd211738c7", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/screenshots/settings-sidebar-only.png", + "bytes": 53238, + "sha256": "16c64655733fa1de41a1f5ce5676ea581d7fb348aa11d42ecff837fcd3ce9333", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/en/releases/v0.4.0.md", + "line": 35 + }, + { + "file": "docs/zh-CN/releases/v0.4.0.md", + "line": 35 + } + ], + "referencesAfter": [ + { + "file": "docs/en/releases/v0.4.0.md", + "line": 35 + }, + { + "file": "docs/zh-CN/releases/v0.4.0.md", + "line": 35 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/screenshots/settings-task-agent-routing.png", + "bytes": 332844, + "sha256": "389d951ace3f1d4fa53bfc498f9cbf7345e644067882858477492c874e087532", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/screenshots/sidebar-layer-disabled-en.jpg", + "bytes": 29349, + "sha256": "cd3af9ab329998306fb38131ac34fcb3d213360833bec87a327e73f939e07e8a", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/en/reference/configuration.md", + "line": 153 + }, + { + "file": "docs/en/reference/configuration.md", + "line": 153 + } + ], + "referencesAfter": [], + "currentGuideReferencesReplaced": true + }, + { + "file": "docs/assets/screenshots/sidebar-layer-disabled-zh-CN.jpg", + "bytes": 25062, + "sha256": "51b7c76c3d85431fbfe7bad90902b690fd907ba740702253f88d888333e5f2d6", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/zh-CN/reference/configuration.md", + "line": 155 + }, + { + "file": "docs/zh-CN/reference/configuration.md", + "line": 155 + } + ], + "referencesAfter": [], + "currentGuideReferencesReplaced": true + }, + { + "file": "docs/assets/screenshots/status-overview.png", + "bytes": 228429, + "sha256": "fd18292b435f123192dd25666f7e9b6b6902e7044aaa56ff01f8a6fe9723ef46", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/en/guides/operations.md", + "line": 25 + }, + { + "file": "docs/en/guides/operations.md", + "line": 25 + }, + { + "file": "docs/zh-CN/guides/operations.md", + "line": 25 + }, + { + "file": "docs/zh-CN/guides/operations.md", + "line": 25 + } + ], + "referencesAfter": [], + "currentGuideReferencesReplaced": true + }, + { + "file": "docs/assets/screenshots/version-check.png", + "bytes": 304705, + "sha256": "039c82e8e3fc4c9739f406b62e80436b3c566fa873a68ff954b58236f7fb55ed", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/en/guides/operations.md", + "line": 33 + }, + { + "file": "docs/en/guides/operations.md", + "line": 33 + }, + { + "file": "docs/zh-CN/guides/operations.md", + "line": 33 + }, + { + "file": "docs/zh-CN/guides/operations.md", + "line": 33 + } + ], + "referencesAfter": [], + "currentGuideReferencesReplaced": true + }, + { + "file": "docs/assets/showcase/demo.mp4", + "bytes": 1093216, + "sha256": "f7ae1991a4ac90425cbc6a85983e6184ca47f501142859e1bba6bb1ce8adc618", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": false, + "legacyLabelsDetected": null + }, + "referencesBefore": [ + { + "file": "README.md", + "line": 26 + }, + { + "file": "README.zh-CN.md", + "line": 26 + }, + { + "file": "docs/assets/showcase/README.md", + "line": 26 + }, + { + "file": "docs/assets/showcase/README.md", + "line": 53 + }, + { + "file": "docs/en/guides/ui-guide.md", + "line": 11 + }, + { + "file": "docs/zh-CN/guides/ui-guide.md", + "line": 11 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/showcase/README.md", + "line": 26 + }, + { + "file": "docs/assets/showcase/README.md", + "line": 53 + } + ], + "currentGuideReferencesReplaced": true + }, + { + "file": "docs/assets/showcase/en/content.png", + "bytes": 145867, + "sha256": "4ccbe5a551f03898e8ce6503c717c35429747cb77acdb3f4353cdbd51817f948", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/assets/showcase/README.md", + "line": 34 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/showcase/README.md", + "line": 34 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/showcase/en/documents.png", + "bytes": 154432, + "sha256": "5cba8f77cebb86db72b3247c001ecd5b9c52373cd0c9f9ce63c3e5957649dfc4", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/assets/showcase/README.md", + "line": 32 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/showcase/README.md", + "line": 32 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/showcase/en/enhancements.png", + "bytes": 195760, + "sha256": "54f4b1d1e679d152819dbc28ec6e69536cb71ae3e517d0d60f84c58126315593", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/assets/showcase/README.md", + "line": 35 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/showcase/README.md", + "line": 35 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/showcase/en/runtime.png", + "bytes": 191234, + "sha256": "1530e741a3bcd4ba1688dd17f6a7b3baae74e2c055b3bb862ca92cc4e324f8f6", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/assets/showcase/README.md", + "line": 31 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/showcase/README.md", + "line": 31 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/showcase/en/spaces.png", + "bytes": 186893, + "sha256": "2b070de8814a0b60427ce850d09bb0a973331c2169c9688d236c8521c1075c00", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/assets/showcase/README.md", + "line": 33 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/showcase/README.md", + "line": 33 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/showcase/en/status.png", + "bytes": 115480, + "sha256": "8cd838fee6127ae91336e778f7b6cf7982a1288553414d3a5cc9d419a770d836", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/assets/showcase/README.md", + "line": 30 + }, + { + "file": "docs/en/guides/getting-started.md", + "line": 177 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/showcase/README.md", + "line": 30 + } + ], + "currentGuideReferencesReplaced": true + }, + { + "file": "docs/assets/showcase/zh-CN/content.png", + "bytes": 146154, + "sha256": "b8a8be01fe2c1ade34d8c414d2893c21746056faa9eea720ae1c79c47c60dd6e", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/assets/showcase/README.md", + "line": 34 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/showcase/README.md", + "line": 34 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/showcase/zh-CN/documents.png", + "bytes": 154617, + "sha256": "f5b4288310ed75a9d9fe822fd583177d0831f0d8521411d721a1b266849a6c42", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/assets/showcase/README.md", + "line": 32 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/showcase/README.md", + "line": 32 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/showcase/zh-CN/enhancements.png", + "bytes": 192074, + "sha256": "488311d622c6080d85ae3077f801881fe1af5eaa2d8c352965c59fb4351759f7", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/assets/showcase/README.md", + "line": 35 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/showcase/README.md", + "line": 35 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/showcase/zh-CN/runtime.png", + "bytes": 192768, + "sha256": "5934e80494ae9484db21db8573b09e5becb25567cf2104ca6675aeec66e00a41", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/assets/showcase/README.md", + "line": 31 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/showcase/README.md", + "line": 31 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/showcase/zh-CN/spaces.png", + "bytes": 182882, + "sha256": "83dc286e505a60958f9eb978a12d666a8b614ad76227ee3b183e3ecd60513aa8", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/assets/showcase/README.md", + "line": 33 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/showcase/README.md", + "line": 33 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/assets/showcase/zh-CN/status.png", + "bytes": 118920, + "sha256": "96e03d5ada0c42d0959d699ebf6d25fd0107ab07e18aeb210ba05f3b1df2ffbf", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/assets/showcase/README.md", + "line": 30 + }, + { + "file": "docs/zh-CN/guides/getting-started.md", + "line": 177 + } + ], + "referencesAfter": [ + { + "file": "docs/assets/showcase/README.md", + "line": 30 + } + ], + "currentGuideReferencesReplaced": true + }, + { + "file": "docs/pr-assets/architecture-cleanup/builtin-runtime-en-light.png", + "bytes": 72959, + "sha256": "6d0190f00089a4803f8b5922487329083fc96f5bd530e7436250de065acf0a99", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/architecture-cleanup/README.md", + "line": 81 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/architecture-cleanup/README.md", + "line": 81 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/architecture-cleanup/documents-disabled-en.png", + "bytes": 39267, + "sha256": "a99cc4a788e0a0fb4bb43e81dd697b1acae6978fba348d51e82017826b125b2b", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/architecture-cleanup/README.md", + "line": 80 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/architecture-cleanup/README.md", + "line": 80 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/architecture-cleanup/documents-mobile-en.png", + "bytes": 37517, + "sha256": "0ddd8acfd99655321f59b28fc5eb348a5dc28a957e263512ca94682a8e4a14a9", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/architecture-cleanup/README.md", + "line": 79 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/architecture-cleanup/README.md", + "line": 79 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/architecture-cleanup/documents-revision-desktop.png", + "bytes": 66304, + "sha256": "d98a5c942f6a56bf3671d75f13908738c9504a6f56794945b4b901b61b10990c", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/architecture-cleanup/README.md", + "line": 75 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/architecture-cleanup/README.md", + "line": 75 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/architecture-cleanup/enhancements-enabled-desktop.png", + "bytes": 54620, + "sha256": "871f53ee8f80f5c80251177713c11cb64d9ce35c90e05b63eda0a8f41ce88058", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/architecture-cleanup/README.md", + "line": 77 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/architecture-cleanup/README.md", + "line": 77 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/architecture-cleanup/native-recall-desktop.png", + "bytes": 64911, + "sha256": "99c116c28d1cf038e34615a7778ef0cb34005aba28335ccd68217def2ed02b08", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/architecture-cleanup/README.md", + "line": 76 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/architecture-cleanup/README.md", + "line": 76 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/architecture-cleanup/runtime-edited-desktop.png", + "bytes": 65630, + "sha256": "b8d1243516ed54eef569a0207010938871cf4f3d693f8fc7b7ce3360cc1c8cce", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/architecture-cleanup/README.md", + "line": 74 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/architecture-cleanup/README.md", + "line": 74 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/architecture-cleanup/space-create-mobile-actions.png", + "bytes": 37204, + "sha256": "34c0619202c46d8046dfe7be01d61ca39767c5a951a454721344a87392163e37", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/architecture-cleanup/README.md", + "line": 78 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/architecture-cleanup/README.md", + "line": 78 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/architecture-cleanup/space-create-mobile.png", + "bytes": 36448, + "sha256": "fffdede4b6598312e342d838bca7d012223b583ea258bffca312162746d06693", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/architecture-cleanup/README.md", + "line": 78 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/architecture-cleanup/README.md", + "line": 78 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/better-sidebar-153/after-memory-tab.jpg", + "bytes": 103523, + "sha256": "36558a0670a646de870c1e6f1fda3c9b64ddf69b965886f5f6b824e86aa61f38", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/better-sidebar-153/README.md", + "line": 35 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/better-sidebar-153/README.md", + "line": 35 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/better-sidebar-153/after-tab-menu.jpg", + "bytes": 99117, + "sha256": "6a7a865c7a919c3faf74baa5b8526e2f0ab2115818f01a008f0d9718c3a2902c", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/better-sidebar-153/README.md", + "line": 34 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/better-sidebar-153/README.md", + "line": 34 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/builtin-139/after-builtin-runtime.png", + "bytes": 72115, + "sha256": "85a6f6eb2df829e240016e4f5d83dc08c9973af709ed15ce324a3a6c08b6519d", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/builtin-139/README.md", + "line": 70 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/builtin-139/README.md", + "line": 70 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/builtin-139/after-native-status.png", + "bytes": 71341, + "sha256": "372d64485bab905c4ba90bb5356a64d07354c6db9d6e55ed2f1b198a2f30ff7f", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/builtin-139/README.md", + "line": 73 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/builtin-139/README.md", + "line": 73 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/builtin-139/after-sidebar-runtime.png", + "bytes": 70503, + "sha256": "e229b9ede99f7bc10c5795ac9b0c8bbfac93c6570cc87e233c45d28877d24fdb", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/builtin-139/README.md", + "line": 71 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/builtin-139/README.md", + "line": 71 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/builtin-139/after-workspace-b-isolated.png", + "bytes": 72669, + "sha256": "fa759784aaaa9973fcb86bdf688103ef34d5156fd2bb910c71916576ba675eb7", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/builtin-139/README.md", + "line": 72 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/builtin-139/README.md", + "line": 72 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/builtin-139/before-0.4.1-conversation.png", + "bytes": 48922, + "sha256": "98629d250614f4fca4adf550be5bef220d1ea4cc44dbbc5d0a1bd0a29958b0a7", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/builtin-139/README.md", + "line": 68 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/builtin-139/README.md", + "line": 68 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/checkpoint-subagent-token-after.png", + "bytes": 25185, + "sha256": "e32edf92f5642b0806cd4c4eec5a2a6a071c92a7ffa21754c8401b4cad9280fb", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/checkpoint-subagent-token-before.png", + "bytes": 25058, + "sha256": "6ad5206074bce4267a6fc9bc181fc65ff73090924a3313d0b04da4f35e9f3da0", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/default-theme-dark-after.jpg", + "bytes": 78189, + "sha256": "bf9c15cc00473e2c0725fa00337c7c0ed3da974739d69d720f58fc5708e9dacd", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/default-theme-light-after.jpg", + "bytes": 72356, + "sha256": "a8231091b6c3bbcd789a8ea7747ec77dee4f66c183b7e0f6df16ae876c06cf76", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/default-theme-taskboard-dark.jpg", + "bytes": 43126, + "sha256": "8f5ecd44235576ae102c5b8c778bfc6a86c55db33b091441e22e9934a4317648", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/dialog-surfaces/after-dark-desktop-document.jpg", + "bytes": 64285, + "sha256": "e8c81b1736ab5496880ea2db852501c8044a8d69148f250adab4c59c77338b41", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/dialog-surfaces/README.md", + "line": 38 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/dialog-surfaces/README.md", + "line": 38 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/dialog-surfaces/after-dark-desktop-runtime.jpg", + "bytes": 56644, + "sha256": "f7e4fa299ddee36934af141f090c32c0dd613f588cdecea952b2e64b7c9b1867", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/dialog-surfaces/README.md", + "line": 31 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/dialog-surfaces/README.md", + "line": 31 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/dialog-surfaces/after-dark-mobile-document.jpg", + "bytes": 27089, + "sha256": "751a28750ae21c6462dac031f591bd1e812382ec959e0129f5543f59b5e72289", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/dialog-surfaces/README.md", + "line": 38 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/dialog-surfaces/README.md", + "line": 38 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/dialog-surfaces/after-dark-mobile-runtime.jpg", + "bytes": 42209, + "sha256": "7a7f96ba0f4ee586e3e10f9e0728939681efb482ac0dcd5d74487ecd5c1cda0d", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/dialog-surfaces/README.md", + "line": 30 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/dialog-surfaces/README.md", + "line": 30 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/dialog-surfaces/after-dark-strategy.jpg", + "bytes": 77538, + "sha256": "20b9f1bc075c77a1ce85710a489ba019d39bd093a1264665b40502e1d18d21ae", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/dialog-surfaces/README.md", + "line": 38 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/dialog-surfaces/README.md", + "line": 38 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/dialog-surfaces/after-light-desktop-runtime.jpg", + "bytes": 62351, + "sha256": "88804cd2f4e913bd249978a969eb8b70bf744f40c90575836f1dccb254c3825e", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/dialog-surfaces/README.md", + "line": 32 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/dialog-surfaces/README.md", + "line": 32 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/dialog-surfaces/before-dark-desktop-runtime.jpg", + "bytes": 55900, + "sha256": "caa8927bedfe3dac819a0adf2d0ab0c5b50629dfbd480adb26383d62153e36e9", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/dialog-surfaces/README.md", + "line": 31 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/dialog-surfaces/README.md", + "line": 31 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/dialog-surfaces/before-dark-mobile-runtime.jpg", + "bytes": 41524, + "sha256": "7f7a5195ad7120d95c3e4efab3c8b481e222fe2bf06a4673b81ddfe5a0270e86", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/dialog-surfaces/README.md", + "line": 30 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/dialog-surfaces/README.md", + "line": 30 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/dialog-surfaces/before-light-desktop-runtime.jpg", + "bytes": 61879, + "sha256": "8c7b47bed7571f0fa3aac79299b932106df3b59cd2ba65e0ee0996af41ecbd50", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/dialog-surfaces/README.md", + "line": 32 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/dialog-surfaces/README.md", + "line": 32 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/documentation-refresh/narrow-fresh-390.png", + "bytes": 47435, + "sha256": "ce03001bb489f2e756e5e5768323475f331cb37099b884993a83a99f23a29f2c", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/documentation-refresh/README.md", + "line": 64 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/documentation-refresh/README.md", + "line": 64 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/documentation-refresh/narrow-resize-390.png", + "bytes": 73455, + "sha256": "9b6f0b2ddc6cf67d364d4323cd073ea099a8a4717f4d4fa17f7c7030b4a28473", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/documentation-refresh/README.md", + "line": 62 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/documentation-refresh/README.md", + "line": 62 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/documentation-refresh/readme-en-1280.png", + "bytes": 200480, + "sha256": "bca348a3b1f95055282f9ef8be8ad42c872e4a0115b30d794f07e752a0964f7c", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/documentation-refresh/README.md", + "line": 53 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/documentation-refresh/README.md", + "line": 53 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/documentation-refresh/readme-en-390.png", + "bytes": 87841, + "sha256": "dc8ddcc65ea6e5d9c8ac5a1fec3e9797e42dec0ce18f5875771d22e35fa55df5", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/documentation-refresh/README.md", + "line": 53 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/documentation-refresh/README.md", + "line": 53 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/documentation-refresh/readme-zh-CN-1280.png", + "bytes": 199889, + "sha256": "1866db1f68c791c0b16f6c81b71f22b93890b54ba131b7356ad2c3ae5b9c1688", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/documentation-refresh/README.md", + "line": 54 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/documentation-refresh/README.md", + "line": 54 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/documentation-refresh/readme-zh-CN-390.png", + "bytes": 97081, + "sha256": "8da9e5cfd7592dd04031b7b78e0a9e8b1d75f1fe368be8ae88e59a3e3b763623", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/documentation-refresh/README.md", + "line": 54 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/documentation-refresh/README.md", + "line": 54 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/dsh-rc1-compat/rc1-conversation.png", + "bytes": 134311, + "sha256": "59deeb64125f9c6bff64bd69878a2f5d4628db9a4020b20f5a2e6b34bdc395d5", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/dsh-rc1-compat/README.md", + "line": 23 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/dsh-rc1-compat/README.md", + "line": 23 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/dsh-rc1-compat/rc1-status.png", + "bytes": 183291, + "sha256": "55a86ebea7acfd3c85c1c3ba31602d65b256d9925eb8548d13a0c8f637fac49b", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/dsh-rc1-compat/README.md", + "line": 19 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/dsh-rc1-compat/README.md", + "line": 19 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/dsh-rc1-compat/rc2-conversation.png", + "bytes": 147652, + "sha256": "82db6f333d485fb8bd910b153bb3d4a7b010762261c562b11f88e67c3f26428a", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/dsh-rc1-compat/README.md", + "line": 29 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/dsh-rc1-compat/README.md", + "line": 29 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/npm-sidebar-cli/after-controlled.jpg", + "bytes": 64712, + "sha256": "508f016b7ecbdb01b4e0b22fda269be8dab1dbf02a7ee36417cf4a06856bbe0a", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/npm-sidebar-cli/README.md", + "line": 87 + }, + { + "file": "docs/pr-assets/npm-sidebar-cli/README.md", + "line": 173 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/npm-sidebar-cli/README.md", + "line": 87 + }, + { + "file": "docs/pr-assets/npm-sidebar-cli/README.md", + "line": 173 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/npm-sidebar-cli/after-normal.jpg", + "bytes": 63438, + "sha256": "067a19e52938cbecfc371df97e73c87e5cee1946334deab2bad3c23eea4d85f1", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/npm-sidebar-cli/README.md", + "line": 85 + }, + { + "file": "docs/pr-assets/npm-sidebar-cli/README.md", + "line": 171 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/npm-sidebar-cli/README.md", + "line": 85 + }, + { + "file": "docs/pr-assets/npm-sidebar-cli/README.md", + "line": 171 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/npm-sidebar-cli/before-cli.jpg", + "bytes": 65129, + "sha256": "c0fee05751a6198239495da55872c1a44e6da70ea35119b49528f335766445d4", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/npm-sidebar-cli/README.md", + "line": 84 + }, + { + "file": "docs/pr-assets/npm-sidebar-cli/README.md", + "line": 170 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/npm-sidebar-cli/README.md", + "line": 84 + }, + { + "file": "docs/pr-assets/npm-sidebar-cli/README.md", + "line": 170 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/npm-sidebar-cli/before-controlled.jpg", + "bytes": 49835, + "sha256": "5263c5a696d9c253c29dba523a0453afdf3999ec1e51f1c5cc6c5850bb21cb48", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/npm-sidebar-cli/README.md", + "line": 86 + }, + { + "file": "docs/pr-assets/npm-sidebar-cli/README.md", + "line": 172 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/npm-sidebar-cli/README.md", + "line": 86 + }, + { + "file": "docs/pr-assets/npm-sidebar-cli/README.md", + "line": 172 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/pr72-entity-rail-before-after.png", + "bytes": 17887, + "sha256": "59efe6f8e0f7e8f578e5208fed4e28b4f5e849b5ccf5df0e6368ffa3584dcf30", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/pr73-entity-live-filter.png", + "bytes": 51475, + "sha256": "8eecaf3370fa39f4eb284f9af467ce03e54f8c2c2b60a1fd1c174f7f52134427", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [], + "referencesAfter": [], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/session-events-156/after-alpha4.jpg", + "bytes": 38218, + "sha256": "a8b47ca1dcf6fa948a02205a1a606446ea91e06a07c7ad94de6d005e62ec5df5", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/session-events-156/README.md", + "line": 27 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/session-events-156/README.md", + "line": 27 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/session-events-156/after-alpha5.jpg", + "bytes": 38251, + "sha256": "11c0af265e17c861ed24dca7d037ba7bde35599939eebbbc6d089a1c72b5245a", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/session-events-156/README.md", + "line": 31 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/session-events-156/README.md", + "line": 31 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/session-events-156/after-rc2.jpg", + "bytes": 40385, + "sha256": "b33568671afd9a6c5065c13f320425ba57ffdbc883cadd28eedc8f975db15c19", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/session-events-156/README.md", + "line": 35 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/session-events-156/README.md", + "line": 35 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/session-events-156/before-alpha4-v0.4.4.jpg", + "bytes": 27570, + "sha256": "14b40422ac19cf4844a2560504e1066ee5465f2f038ff463fe1505121e79fcba", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/session-events-156/README.md", + "line": 23 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/session-events-156/README.md", + "line": 23 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/sidebar-icon/after-classic-dark.jpg", + "bytes": 21809, + "sha256": "86aa71431789c54b24fe8ef6d4147401bb576fda45200186d1b01ac73fd9434c", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/sidebar-icon/README.md", + "line": 40 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/sidebar-icon/README.md", + "line": 40 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/sidebar-icon/after-classic-light-expanded.jpg", + "bytes": 25672, + "sha256": "92082644ddad438fb7b26f3aa80529ddda081c45227ef5bbf607404520cb08ae", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/sidebar-icon/README.md", + "line": 43 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/sidebar-icon/README.md", + "line": 43 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/sidebar-icon/after-classic-light.jpg", + "bytes": 21734, + "sha256": "043d37d83e2c33f61fc658d6da11f01acfbbb99a85926bd2a8ab83ea983dfdec", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/sidebar-icon/README.md", + "line": 39 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/sidebar-icon/README.md", + "line": 39 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/sidebar-icon/after-web-0.3.9-expanded.jpg", + "bytes": 95599, + "sha256": "205bfd4a19d9926da0c35d08dc3a820809ee926bbe9a661142af82dfd7e15864", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/sidebar-icon/README.md", + "line": 43 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/sidebar-icon/README.md", + "line": 43 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/sidebar-icon/after-web-0.3.9.jpg", + "bytes": 95675, + "sha256": "e9d3cbce9cfad5bbca59900b1dd4372dcc77b263c782236ac56ad1c3ddac0468", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/sidebar-icon/README.md", + "line": 41 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/sidebar-icon/README.md", + "line": 41 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/sidebar-icon/before-classic-dark.jpg", + "bytes": 21631, + "sha256": "bde2cf9cee71f65a5c4b20c949a0b09b952ebeef2b48161a6a5b8dba77d1bbf2", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/sidebar-icon/README.md", + "line": 40 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/sidebar-icon/README.md", + "line": 40 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/sidebar-icon/before-classic-light.jpg", + "bytes": 21532, + "sha256": "b35d6d5fb4ffe82dfa0faefe4ba5f90b526359be9264b05869e6c57a2f8a8bc7", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/sidebar-icon/README.md", + "line": 39 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/sidebar-icon/README.md", + "line": 39 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/sidebar-icon/before-web-0.3.9.jpg", + "bytes": 95655, + "sha256": "7021f9a5284a33f2ef0741ea3ada24c064a068ccf99180a62614074a0c903266", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/sidebar-icon/README.md", + "line": 41 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/sidebar-icon/README.md", + "line": 41 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/status-version-maintenance/desktop-collapsed.png", + "bytes": 58136, + "sha256": "70b916a4bb1ca399960e5e92aab0caed4e8abb7a90ad3876ae8b01510204648e", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/status-version-maintenance/README.md", + "line": 5 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/status-version-maintenance/README.md", + "line": 5 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/status-version-maintenance/desktop-expanded.png", + "bytes": 66344, + "sha256": "847e817dd3d003fd84cc3f115b7175dd76e07672d75acecc21e9af1f003d7e5b", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": true + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/status-version-maintenance/README.md", + "line": 6 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/status-version-maintenance/README.md", + "line": 6 + } + ], + "currentGuideReferencesReplaced": false + }, + { + "file": "docs/pr-assets/status-version-maintenance/mobile-expanded.png", + "bytes": 43601, + "sha256": "df32e1ab55fcc326968b1c38f126ab9a0bbd83e0cb937a36fbe8412483e1b398", + "classification": "historical_evidence", + "action": "retained_unchanged", + "ocr": { + "checked": true, + "legacyLabelsDetected": false + }, + "referencesBefore": [ + { + "file": "docs/pr-assets/status-version-maintenance/README.md", + "line": 7 + } + ], + "referencesAfter": [ + { + "file": "docs/pr-assets/status-version-maintenance/README.md", + "line": 7 + } + ], + "currentGuideReferencesReplaced": false + } + ] +} diff --git a/dsh/docs/en/README.md b/dsh/docs/en/README.md new file mode 100644 index 000000000..4da5b9c62 --- /dev/null +++ b/dsh/docs/en/README.md @@ -0,0 +1,44 @@ +# Documentation + +**English** | [简体中文](../zh-CN/README.md) | [Project home](../../README.md) + +dsh-mnemon gives DeepSeek Harness three kinds of memory: **runtime memory** in every turn, **Project Documents** searched when a question needs them, and **Memory Spaces** for durable evidence on the Provider you choose. A main strategy decides how they take part in each turn; you pick it, and any enhancements, on the Plugins page. Start with the default Layered strategy; you never need to manage plugins to use it. + +[![The Memory composition board on the dsh-mnemon plugin page](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/plugin-composition.jpg)](./guides/ui-guide.md#on-the-plugins-page) + +## Use the system + +| Task | Guide | +|---|---| +| Install from scratch on the web, the desktop app or the command line, through to the first memory | [Install and start](./guides/installation.md) | +| Store and use the first memories | [Getting started](./guides/getting-started.md) | +| Find your way around the conversation, the Memory System and the Plugins page | [UI guide](./guides/ui-guide.md) | +| Understand what each component and strategy does | [Capability map](./guides/capabilities.md) | +| Choose and connect a long-term backend | [Providers](./guides/memory-providers.md) | +| Back up, move storage or troubleshoot | [Operations](./guides/operations.md) | +| Upgrade an existing installation | [Compatibility and upgrades](./reference/compatibility.md) | + +## Look up a contract + +| Question | Reference | +|---|---| +| Which settings exist, where are they edited and saved? | [Configuration](./reference/configuration.md) | +| What is stored where, shared or archived? | [Storage model](./reference/storage-model.md) | +| When do reads, writes and maintenance run? | [Workflows](./reference/workflows.md) | +| Which tools, commands, RPC channels and UI regions are exposed? | [Interfaces](./reference/interfaces.md) | + +## Build an extension + +| Task | Developer guide | +|---|---| +| Understand Source, Strategy, View, ownership and the UI regions | [Architecture](./development/architecture.md) | +| Create a Source, Strategy, enhancement or Provider | [Plugin development](./development/extensions.md) | +| Adapt a skin to Mnemon backgrounds and transparency | [Skin development](./development/skin-integration.md) | +| Build, test and capture a real WebUI | [Development and verification](./development/README.md) | +| Version and release independent packages | [Release process](./development/releasing.md) | + +## What is new + +[v0.5.24](./releases/v0.5.24.md) makes the Entities page count and list every memory that carries an entity, shows them as soon as one is selected, and finds related memories only when asked. [v0.5.23](./releases/v0.5.23.md) reduced repeated work in Runtime compaction, Document queries, space discovery, multi-space recall, batch preparation and Holographic ranking, preserving result order and storage formats. [All releases](./releases/README.md) · [Roadmap](./roadmap.md) · [Historical evidence](../pr-assets/README.md) + +Guides describe the current release. Screenshots and recordings come from the [v0.5.19 gallery](../assets/webui-v0.5.19/README.md), and the installation steps from the [installation gallery](../assets/install-v0.5.19/README.md); dated PR records establish only their named revisions and environments. Internal Host RPCs are not an external plugin SDK. diff --git a/dsh/docs/en/development/README.md b/dsh/docs/en/development/README.md new file mode 100644 index 000000000..d053bfc2f --- /dev/null +++ b/dsh/docs/en/development/README.md @@ -0,0 +1,173 @@ +# Development and Verification + +[简体中文](../../zh-CN/development/README.md) | **English** | [Documentation Center](../README.md) + +## Environment and commands + +The plugin's Node engine floor is 20. The pinned complete DSH development profile is the published 0.1.7-rc.2 release and needs Node `^22.19.0 || >=24.0.0`; use Node 24 for development. Root, Source Client tests and the external artifact consumer use that cohort. `dsh-invariants` closes its peer graph, while `dsh-client-store` owns the public selector type used by the subagent projection adapter. Public Node entries are also smoke-tested on Node 20 in CI. DSH 0.2.0-rc.1 on npm `next` is supported as well: `tests/dsh-host-compatibility.spec.ts` runs DSH's own pre-install compatibility check over all 18 package manifests for both runtimes. To check 0.2 behavior, install DSH globally in an isolated directory as [Compatibility](../reference/compatibility.md#dsh-02) describes, or move the development dependencies to 0.2.0-rc.1 temporarily. + +DSH 0.1.7 UI primitives import Markdown/highlighting dependencies that its published manifest lists as development dependencies. Root, the three Source packages and the external consumer declare that complete cohort explicitly for standalone Client tests; Host artifacts still use DSH’s provided UI module. Tests use the public async Agent factory and durable `assistant/message` events. `tests/legacy-session-repair.spec.ts` runs the real released Session v0 → v3 migration over synthetic historical logs in plain and compressed form, checking explicit copy recovery, cold reopen and timed stream replay. Its audited cases cover all three old Mnemon summaries, compatible v2 descriptors, packed-placeholder expansion, null-to-empty delta names and closed tool chains with a previously recorded provider ID. It checks multi-call provenance, owner-reference refusal and original/plugin preservation. `pnpm e2e:serve --legacy-session-replay` additionally makes the actual WebUI's loopback continuation server verify the historical wire call/result IDs and payload before returning success. + +```sh +pnpm install --frozen-lockfile +pnpm run verify +pnpm run verify:plugins +``` + +`verify` checks types, deterministic Root builds, independent plugin builds, the full test suite, a real isolated DSH Headless profile and package exports/contents. Independent plugin checks run as separate type/test phases after building all public artifacts. Do not mix `pnpm -r verify` clean builds with tests reading sibling artifacts; use `pnpm verify` for the whole workspace. `verify:plugins` repeats verification **outside** the workspace against semver-installed tarballs and an external Source/Strategy/Provider/Client consumer. It also installs only the packed Root into real DSH, resolving all seventeen official plugins from a loopback registry without workspace links or manifest rewrites, then separately verifies the three shipped enhancements moving from disabled defaults to simultaneous activation. The external consumer compiles its own Strategy extension against the owning Strategy's packed SDK. + +## Repository ownership + +```text +src/ + core/ contracts, View compilation, generations, turn leases + sdk/ installMemory, validation, test fixtures + host/ DSH lifecycle, settings, tools, RPC, worker coordination + client/ shared workspace, settings, Source-page SDK +plugins/ + dsh-mnemon-source-runtime/ + dsh-mnemon-source-documents/ + dsh-mnemon-source-memory-spaces/ + dsh-mnemon-strategy-default-three-tier/ # the Layered strategy, the default main strategy + dsh-mnemon-strategy-general/ # shipped, alternative main strategy + dsh-mnemon-strategy-scoped/ # shipped, disabled selection contribution + dsh-mnemon-strategy-light-context/ # shipped, disabled projection contribution + dsh-mnemon-strategy-auto-capture/ # shipped, disabled in-turn capture contribution + dsh-mnemon-provider-*/ +tests/ Host/Core/UI composition and boundary tests +scripts/ reproducible build, artifacts, Headless and Web fixtures +cordis.patch.yml default Starter composition +``` + +Root owns Core/SDK and the DSH Host/default Starter, not Source storage implementations. Each directory under `plugins/` is a publishable standalone project. The default distribution depends on all seventeen official plugins by public semver. The Starter installs the General strategy and the three enhancements with their Entries disabled; `memoryView.strategyTypeId` records which main strategy composes the View, and DSH's plugin manager records each component switch. Source/Strategy peers depend on Core's public SDK, Strategy extensions on their owner's public SDK, and Providers on the Memory Spaces SDK. Peer/development relationships can produce a package-manager cycle warning; production import boundaries are independently checked. + +No private workspace packages, forwarding controller modules, business bindings or compatibility directory remain. Compatibility means retained user configuration, data and workflows, not retention of historical internal symbols. + +Build Root before plugin clients that need its public browser artifact: + +```sh +pnpm build +pnpm --workspace-concurrency=1 -r build +pnpm --filter dsh-mnemon-source-runtime verify +``` + +A plugin can be copied to a new repository and use its own `pnpm install && pnpm verify` once its declared peer versions are available. During unreleased development, use packed artifacts and the local-registry verification harness; do not substitute repository source paths. + +## Test ownership and coverage + +| Boundary | Tests | +|---|---| +| Core/SDK | Immutable Views, budgets, Strategy validation, concurrent turns, grants, leases, generation replacement, cleanup and performance | +| Strategy extension | Independent slots, combination/order, unload, conflict, read-only scope, shared quotas and actual Host activation | +| Source | Its controller/storage, revisions, snapshots, JSON operations, own Client clicks and instance isolation | +| Provider | Driver behavior, credentials, capability truth and fault responses | +| Memory Spaces | Provider child lifecycle, cross-provider conformance, merge/routing/quality and Native process serialization | +| Host | Default composition, settings/data scope, tools, supervised workflows, RPC authority, receipts and UX | +| Artifact | Every public entry, standalone install/build/test, external composition and browser artifacts | + +The workspace identity, Client platform boundary and delegated workspace scope tests also run on Windows with Node 22.19 and 24. They cover real filesystem errors and junction aliases as well as a portable regression for Windows returning `ENOENT` below a file; that simulated error is not a substitute for the Windows run. + +`tests/bundle-activation.spec.mjs` boots the published DSH Loader with disposable activation counters for all seventeen Starter packages. It checks the legacy `mnemon` configuration/disable target and independent component choices, then drives the published plugin manager: real component and bundle toggles must persist across reloads and restarts, the core gate must stop every active child, and no package may leave duplicate instances. Both cases run on the pinned host; set `MNEMON_BUNDLE_TEST_PROFILE=/absolute/profile` to check another installation instead. The fixture processes do not inherit the `NODE_PATH` that `pnpm exec` sets: DSH 0.2 routes plugin packages through Node's search paths, `NODE_PATH` included, so inheriting it would resolve this repository's real plugins instead of the fixture's packages. The fixture creates its own temporary profile and does not edit the supplied installation. It tests lifecycle contracts; packed Headless and WebUI checks still own real Mnemon behavior. Finally it checks that DSH's config schema collector reports only the Starter's component group, as an unrecognized tree carrier (a known trade-off, see [Compatibility](../reference/compatibility.md#installing-and-enabling-the-starter-without-a-restart)), and no other error. Run `node --expose-internals tests/fixtures/bundle-activation.mjs /absolute/profile manager --check-declared-rows` to additionally require every advertised bundle component to be manageable. That diagnostic fails on the known `0.1.7-rc.2` native-group listing defect described in [Compatibility](../reference/compatibility.md#dsh-017-bundle-component-list). + +Remote Provider suites use controlled HTTP responses; Native process suites use controlled command runners plus an optional Windows binary smoke. A separate opt-in test uses an official checksum-verified Native binary to create a disposable space, write through a View, recall and forget: + +```sh +MNEMON_NATIVE_TEST_CLI=/absolute/path/to/mnemon pnpm --filter dsh-mnemon-source-memory-spaces exec vitest run tests/native-integration.spec.ts +MNEMON_NATIVE_TEST_CLI=/absolute/path/to/mnemon pnpm exec vitest run tests/runtime-capacity-workflow.spec.ts -t 'two same-View Native' +``` + +The test never discovers a personal data root or installs a binary. These checks do not certify every live external service or every account configuration. Provider Lab is an explicit separate integration environment. + +OpenViking has an opt-in loopback integration test: `MNEMON_OPENVIKING_TEST_ENDPOINT=http://127.0.0.1:1933 pnpm --filter dsh-mnemon-provider-openviking exec vitest run tests/integration.spec.ts`. Use only a disposable backend. It verifies a unique bilingual canary through create, exact readback, search, browse and exact deletion; normal CI skips it. A published v0.4.20 server with local deterministic embeddings validates the HTTP/storage/index contracts, not semantic-model quality. + +The Runtime case creates and activates two Native spaces through real Host tools after the View is pinned, archives two exact checkpoints and verifies the pending add. Routing decisions are fixed locally; no model API is used. + +The Agent Teams matrix loads the published DSH packages through their package exports only and runs with the ordinary test suite on the pinned host; `MNEMON_TEAM_TEST_PROFILE=/absolute/profile pnpm exec vitest run tests/agent-team-review-host.spec.ts` checks another installation of the same DSH/Teams version. It exercises real fork/spawn, native/Code Mode tools, all three Strategy extensions, restricted child execution, actual Runtime commits and parent Team tools. Reproduction profiles, the packed WebUI fixture and reviewed evidence are in the [Issue #275 record](../../pr-assets/issue-275-agent-teams/README.md). + +The opt-in Flash pressure suite uses four real DSH sessions, delegated writers, independent maintenance tasks and a disposable Native store. It keeps the default 10 KiB limit and verifies exact committed content across repeated archival, namespace routing and session-free browser management. Supply a DeepSeek credential through `DEEPSEEK_API_KEY` and a verified CLI through `MNEMON_NATIVE_TEST_CLI`, then run: + +```sh +MNEMON_RUN_FLASH_STRESS=1 MNEMON_FLASH_STRESS_ROUNDS=8 MNEMON_FLASH_STRESS_REPORT=/tmp/mnemon-flash-stress.json pnpm exec vitest run tests/runtime-capacity-flash-stress.spec.ts +``` + +Every outgoing request and returned model is checked against `deepseek-v4-flash`; the suite uses non-thinking mode and never selects Pro. It reports model input changes separately from storage changes, uses synthetic project facts, and removes its temporary stores and sessions. Destination authorization is a hard assertion; semantic topic matches are reported separately as model quality. Set `MNEMON_FLASH_STRESS_JSON_PROMPT=1` to repeat the quoted-JSON input diagnostic. It is skipped in ordinary CI and requires explicitly authorized live API usage. + +The separate automatic-memory acceptance suite simulates backend, frontend, Android and operations work in four concurrent DSH sessions. Developer prompts contain accepted decisions, corrections and disposable diagnostics; the model chooses its own memory actions. Actual temporary JSON files provide bounded development tasks, while the real default lifecycle performs idle review. Fresh reader sessions have no development transcript or file access. Supply the same credential and CLI environment variables, then run: + +```sh +MNEMON_RUN_FLASH_QUALITY=1 MNEMON_FLASH_QUALITY_REPORT=/tmp/mnemon-flash-quality.json pnpm exec vitest run tests/runtime-memory-flash-quality.spec.ts +MNEMON_RUN_FLASH_QUALITY=1 MNEMON_FLASH_QUALITY_WAVES=24 MNEMON_FLASH_QUALITY_REPORT=/tmp/mnemon-flash-quality-long.json pnpm exec vitest run tests/runtime-memory-flash-quality.spec.ts -t 'four preconfigured' +``` + +The default is 12 waves per session; the extended workload has 24. Both use Flash with thinking disabled, guided recall/writeback and the default 10,240-byte memory limit. Only the idle debounce is shortened from 30 to 5 seconds; eligibility rules stay unchanged. The suite records retained facts, transient markers, corrected answers, module attribution, no-write turns, tool errors and archival. A successful Vitest run means the experiment completed: inspect `finalEvaluation.automatedVerdict` and manually audit retained prose for stale documents, duplicates and scope errors before declaring quality acceptance. The [2026-09-09 acceptance report](../../pr-assets/runtime-memory-quality-flash-20260909/README.md) records failures as well as successful checks. Ordinary CI skips both live cases. This simulation does not substitute for full application builds, a half-day human workflow or Windows testing. + +Keep generated live-run output outside the repository, as the commands above do. Follow the [evidence storage policy](../../pr-assets/README.md): commit summaries, reproduction inputs and minimal examples; attach complete sanitized run data to the PR with its revision and SHA-256. + +The performance regression composes 100 three-Source Views under wall/CPU budgets. Deterministic builds compare all generated hashes. Neither check promises production network latency or LLM quality. + +`pnpm bench:algorithms` measures local query, storage, compaction and recall scaling at N=1/10/100/1000 in temporary synthetic stores. It writes `.cache/algorithm-benchmark.json`; use `MNEMON_BENCH_OUTPUT` for another destination or `MNEMON_BENCH_SIZES=1,10,100` for smaller cases. Run it without concurrent tests/builds. The [algorithm audit](../../pr-assets/algorithm-performance-20261003/README.md) records the workload definitions, before/after results, operation counters, complexity bounds and external-Provider limitations. Normal CI uses deterministic equivalence/work-bound tests, not latency assertions from this benchmark. + +Both the default and three-extension profiles run that performance fence. The +[2026-09-01 Strategy contribution verification](../../pr-assets/strategy-extensions-20260901/README.md) +records coexistence, independent artifacts, real Headless activation and limits. + +## Real WebUI + +For stylesheet placement, stable selectors, activation scope, migration from generated classes and acceptance steps, see [Skin development and Mnemon integration](./skin-integration.md). + +```sh +pnpm build +pnpm --workspace-concurrency=1 -r build +pnpm e2e:serve +``` + +The fixture prints a temporary workspace and loopback URL. It isolates `DSH_HOME`, `MNEMON_DATA_DIR`, browser workspace and model endpoint. Select **Mnemon E2E** for a conversation; this test-owned preset omits Shell requirements while retaining Host memory tools. The model returns a fixed answer, so this fixture checks UI and transport, not real-model distillation quality. Stop with Ctrl-C to remove its synthetic data. + +`pnpm e2e:serve --review-evidence --strategy-extensions` adds a per-Agent synthetic overview tool and fixed parent/reviewer calls. It verifies five inherited chunks and attempts the same foreign read before and after the guard; see the [issue #211 evidence](../../pr-assets/issue-211-20260911/README.md). `tests/review-evidence-host.spec.ts` also covers real DSH native and Code Mode dispatch before the provider's start promise returns. + +Check Sidebar without a session, all primary/secondary tabs, Runtime add/edit/remove and branch clear, Documents create/search/read, Provider settings/discovery, activation, error states, dialog cancellation, Save-to-memory, layout switching, locale and restoration of chat interaction. On the `dsh-mnemon` page under Plugins, also check the main strategy selector, each component's switch and page, DSH's component rows and the pages they open, the Default/Custom storage location, and a backup import preview. Use a disposable real Provider or controlled fixture for write/read/forget; never test against personal memory. + +For issue #233, run `node scripts/fixtures/openviking-protocol.mjs` and `pnpm e2e:serve --openviking-write`. Configure OpenViking at `http://127.0.0.1:19335`, account/user `default`, with no API key, then send `openviking-write-233` in Mnemon E2E. The model fixture drives the real Host and delegated writer tools exactly once. The protocol fixture deliberately reports a cosmetic extraction update with no stored candidate to expose the baseline's false receipt; its `/__fixture` endpoint labels and reports synthetic requests/files. Repeat against a disposable real OpenViking server for backend acceptance. The protocol fixture is not evidence of live extraction or semantic quality. + +For an embedded Electron Host, pass `pnpm e2e:serve --electron=/absolute/path/to/electron` (on macOS, use `Electron.app/Contents/MacOS/Electron`). Supply a separately installed test Electron executable and an isolated npm prefix through `MNEMON_CLI_PATH` and `npm_config_prefix`. The fixture runs the published DSH Web stack in Electron's main process with no `ELECTRON_RUN_AS_NODE` on the Host. It exposes Node internals for the published Cordis loader, without rebuilding or modifying DSH packages. Stop with Ctrl-C as usual. + +Also switch `displayMode` live: the Sidebar and the conversation tab (`builtin`) must never mount together. Both use the same Source pages; the conversation tab follows its owning session for global/workspace/workspaces/custom reads, writes and tasks, hides scope controls, and clears stale data and editors when the session changes. Check legacy `buildin` normalization and the collapsed icon under the native Sidebar skin as well as supported layout plugins. + +The [2026-09-04 main-rebase verification](../../pr-assets/main-rebase-20260904/README.md) records the exact v0.4.7/DSH rc.1 revisions, full registry and source-overlay suites, independent artifacts, plugin composition persistence and real shared-placement checks, including their limits. + +The [2026-08-30 npm regression record](../../pr-assets/npm-sidebar-cli/README.md#english) preserves the old Taskboard/SSH and CLI investigation. Its removed legacy harness is not a current checkout command; use `pnpm e2e:serve` for current WebUI work. + +Separate upstream profile/transport warnings from Mnemon failures rather than hiding the console. + +For the Documents archive regression, use `pnpm e2e:serve --document-archive`. Create and activate a disposable exact-write Memory Space, create a document and archive it from the workbench. A title containing `REJECT` deliberately proposes an invalid destination; verify that the document stays active and no index appears. Rename it and retry. Send `archive-tool-222 prepare`, `archive-tool-222 update`, and `archive-tool-222` in separate Mnemon E2E conversation turns to drive real create → update → archive tools and assert the returned lineage. Only model decisions are scripted; storage, tools, transport and the browser remain real. The same fixture can reproduce the legacy receipt-index mismatch when used with the old Host build. + +For Runtime write-scope regression, use `MNEMON_CLI_PATH=/absolute/path/to/mnemon pnpm e2e:serve --runtime-write-scope --strategy-extensions` in a fresh disposable fixture. Send `archive-scope-250` in Mnemon E2E. The scripted model saves two checkpoints, creates and activates two real Native spaces within the pinned View, then adds an overflowing checkpoint. The baseline rejects this add despite the active spaces; the fixed Host archives the originals to both authorized destinations and commits the add. `archive-scope-250 retry` retries the same pending input in a new turn. The fixture bounds child calls and requires real create/update receipts before completing them. + +`pnpm e2e:serve --idle-review --strategy-extensions` exercises partial failure with deterministic loopback model choices. Send two substantive synthetic user turns (at least 150 characters each), then wait five seconds. The real reviewer creates one document and one Runtime entry before the fixture returns a deliberate model error. Refresh Memory System status and verify both committed receipts, then send more turns and confirm the one-attempt session cap prevents another child. Only the debounce and minimum interval (both 5 seconds) and attempt cap (1) differ from production defaults. This fixture does not simulate the actual Agent Teams policy; the optional Agent Teams matrix above covers that composition. No personal credentials or memory are used. See the [bilingual reproduction and evidence](../../pr-assets/idle-review-agent-team/README.md). + +`pnpm e2e:serve --strict-template` reproduces [#327](https://github.com/omdsh-dev/dsh-mnemon/issues/327). The loopback model behaves as Ollama 0.33 serving a model whose chat template requires a user query: +- it keeps the system prompt and the longest suffix of the other messages that fits its window, always the last message; +- it answers 500 `no user query found in messages` when no user text turn survives; +- the window is sized when the idle review sends its third request. + +Send two substantive user turns (at least 150 characters each), then wait five seconds. With the fix, the fourth review request is refused once and retried with a user turn, the review creates the Document "Review checkpoint storage", and Status shows no review failure. On a build without it, Status shows the background review failure with the committed Document receipt. The fixture prints each review request as a `Strict template:` line. Only the idle review timing (5 seconds, one attempt per session) differs from production defaults. See the [bilingual reproduction and evidence](../../pr-assets/issue-327-subagent-user-turn/README.md). + +`pnpm e2e:serve --general-strategy` starts with the General strategy selected and the Layered strategy disabled. Send `general-strategy-check remember`, then `general-strategy-check recall` in the next turn. The scripted model checks that the general memory protocol is in the system prompt and that the Runtime, Documents and Memory Spaces Sources are admitted, saves one fact through the named Runtime tool, and answers the second turn only if that fact is projected as resident memory. The fixture prints each check as a `General strategy:` line. Only model decisions are scripted; the Strategy, its View, the Runtime write and the browser stay real. + +`pnpm e2e:serve --without-mnemon-cli` points `cliPath` at a missing file, so the Host behaves as if the Mnemon CLI were not installed even when one is on PATH. Use it to check that Status shows no Mnemon Native card, the version dialog lists the CLI as optional, the plugin page disables the embedding test, and a new Memory Space starts on a ready Provider. Connect one first, for example a Provider Lab service on the `dsh-mnemon` page under Plugins. + +`pnpm e2e:serve --docs-demo` (Chinese) or `--docs-demo=en` seeds the fictional Lumen project used for the [documentation gallery](../../assets/webui-v0.5.19/README.md): profile and working memory, six project documents and three Memory Spaces, written through the Sources' own management operations before the Host starts. A message containing “checkout” is answered through a Documents search and two Memory Spaces recalls over the real View tools; a later message containing “remember”, such as a new LCP target, replaces the working-memory entry. Only the model's tool choices are scripted, and nothing personal is involved. Add `--live-model` to have the DeepSeek API answer instead, with the key read from `DEEPSEEK_API_KEY`; the questions then run through the real model, the task Agent and the Agent answer, as in the gallery. Recapture the gallery from this fixture whenever the UI changes. + +`pnpm e2e:serve --trusted-host=memory.test:4331` adds that authority to DSH's browser-trust fence so the WebUI can be checked as a remote page; add `--remote-management` to set `remoteAccess: trusted-host`. Set `MNEMON_E2E_PORT=4331` so the port matches, map the name to 127.0.0.1 in the test browser (for Chrome, `--host-resolver-rules="MAP memory.test 127.0.0.1"` with `--no-proxy-server`), and open the printed launch URL with `memory.test:4331` in place of the loopback address. Without the grant the `dsh-mnemon` page under Plugins is read-only; with it, a save persists through the API Gateway. + +## Releasing + +Official packages use independent versions; the Starter pins a tested combination. See [Release process](./releasing.md) for changesets, frozen artifacts, Registry checks and recovery. + +## Documentation, storage and historical evidence + +Keep English/Chinese pages aligned and public examples executable. Code-native Mermaid diagrams describe ownership and flow; real screenshots remain under `docs/assets`. Do not retain dead directory stubs or historical wrapper files. + +Retain persisted formats and existing config keys unless an explicit migration is designed and tested. Verify locking, atomic rename, revisions, corrupt inputs and copied-root upgrade/rollback before changing storage. + +The v0.3 release benchmark is a frozen historical result, not a test of this architecture. Its obsolete executable harness was removed from the working tree; [release notes](../releases/v0.3.0.md) link to the versioned historical source. Current gates are the scripts above. diff --git a/dsh/docs/en/development/architecture.md b/dsh/docs/en/development/architecture.md new file mode 100644 index 000000000..d3ce6eb61 --- /dev/null +++ b/dsh/docs/en/development/architecture.md @@ -0,0 +1,121 @@ +# Composable View Memory Architecture + +[简体中文](../../zh-CN/development/architecture.md) | **English** | [Documentation Center](../README.md) + +The system has three domain concepts: a **Source** owns memory and its operations; a **Strategy** proposes how available Sources participate; a **View** is the bounded context and interaction shape presented to the LLM for one scope and scenario. Runtime, Documents and Memory Spaces are the default composition, not Core's universal memory taxonomy. + +## Ownership and assembly + +```mermaid +flowchart TB + Starter["dsh-mnemon Starter · cordis.patch.yml"] --> Host["Host · ctx.mnemonMemory"] + Starter --> Runtime["dsh-mnemon-source-runtime"] + Starter --> Docs["dsh-mnemon-source-documents"] + Starter --> Spaces["dsh-mnemon-source-memory-spaces"] + Starter --> Strategy["dsh-mnemon-strategy-default-three-tier · Layered, the default"] + Starter --> General["dsh-mnemon-strategy-general · alternative, disabled"] + Starter --> Helpers["three shipped enhancements · disabled"] + Helpers -. selection / projection / capture .-> Strategy + Spaces --> Providers["dsh-mnemon-provider-* · private child Fibers"] +``` + +Solid edges show Starter installation ownership; the dotted edge shows Strategy contributions that take effect only after the user enables them, not business calls. DSH creates the top-level Entries/Fibers. Sources, Strategies and their contributions use the same `installMemory(ctx, ...)` SDK and Cordis-owned disposer. Core provides only `ctx.mnemonMemory`; it does not implement a Memory Spaces Fiber or publish `ctx.mnemonMemorySpace`. + +Memory Spaces authors its **own** child Fibers and Provider protocol. Each configured Provider is an explicitly installed module; two Source instances can use the same child id without sharing their registry or credentials. No dependency scan or global Provider registry selects implementations. + +Like a Spring Boot starter, the default distribution chooses dependencies and explicit defaults. It does not turn Source business code into Core. Users still install only `dsh-mnemon`; 17 plugin packages can be independently built, tested and published. The Starter installs every official package. The General strategy and three Strategy enhancements ship as disabled Entries: an enhancement joins the View once its switch is on, and the General strategy composes it only after it is chosen as the main strategy. Replacing the main strategy is always an explicit choice. + +| Owner | Owns | Does not own | +|---|---|---| +| Core | Internal registration, contract validation, immutable Views, budgets, generations and leases | Provider drivers, Source data formats/storage decisions, pages, DSH lifecycle policy | +| SDK | Small contribution service, Source/Strategy author contracts, installation helpers and scoped test tools | Engine/registry constructors, installed records or generation handles | +| Source | Storage/remote authority, facts, projections, grants, query/mutation and optional management/Client | Other Sources' controllers or global strategy selection | +| Strategy | Pure deterministic `request + facts + owned-slot contributions → ViewSpec`; owns slot semantics | Raw data, credentials, drivers, side effects or new authority | +| Host | Scope, phase hooks, tool/RPC adapters, authentication, settings and supervised tasks | Source implementations or private registries | +| Starter | Package set, Entry ids and default configuration | A second loader or runtime | + +`ctx.mnemonMemory` is a real restricted service object, not the engine cast to a narrower type. It exposes one registration primitive, used through `installMemory`; Host execution stays internal. Provider modules follow the same principle inside their own Source, with only a bound `host.install` capability. Public test fixtures exercise these protocols without handing out their private owners. + +## Built-in storage scope + +Storage layout is part of the root package's Host infrastructure. The same resolver handles `global`, `workspace`, `custom` and `workspaces`; Core carries the selected operation scope and enforces View authority. Filesystem canonicalization, environment/home defaults and workspace directory hashes stay in Host code, without a storage contribution API or a separate package. + +The Host passes the resolved directory to each default Source. Sources continue to own their formats, transactions and Provider state; a layout change does not move their data. The opt-in global USER.md uses a separate global root while project data stays under the chosen workspace root. Settings and read-only inventory use the same layout resolver. + +## Default plugin combination + +| Plugin | Memory authority | Default View contribution | +|---|---|---| +| `dsh-mnemon-source-runtime` | Runtime JSON, USER/MEMORY projections, branch filtering, capacity | Exact working context, eager | +| `dsh-mnemon-source-documents` | Managed Markdown, index, search, revision and archive | Bounded narrative cover and search route | +| `dsh-mnemon-source-memory-spaces` | Space directory, private Providers, capability/quality policy | Bounded durable-evidence cover and recall/related routes | +| `dsh-mnemon-strategy-default-three-tier` | No memory storage | The Layered strategy: selects the three roles and allocates projection/routes/actions | +| `dsh-mnemon-strategy-general` | No memory storage | Offered instead of Layered: admits every available Source in one shared budget and lets the model route | + +The nine independent Provider plugin packages are `dsh-mnemon-provider-{mnemon-native,openviking,honcho,mem0,hindsight,holographic,retaindb,byterover,supermemory}`. A Provider runs *inside* Memory Spaces as its storage/retrieval driver, not as a new Core contribution. A Git/Notion/health plugin should normally be a Source; an alternate way to combine them is a Strategy. + +The unextended Layered strategy rejects duplicate roles. Enable `strategy-scoped` to compose multiple instances explicitly; disabling restores that ambiguity check rather than guessing by load order. The `selection`, `projection` and `capture` extension slots are standard: both shipped main strategies declare them, so an enhancement keeps working when the main strategy changes. Core only carries bounded contributions and enforces the existing budget and authority contract. + +## View data flow + +```mermaid +flowchart LR + Facts["Source facts"] --> Strategy["Strategy → ViewSpec"] + Strategy --> Core["Core validation"] + Core --> Project["Source projection + ReadGrant"] + Project --> View["Immutable View"] + View --> Wake["Wake → LLM"] + View --> Route["Route / Action → owning Source"] + Route --> Result["Evidence / Receipt"] +``` + +A View holds projection fragments, routes, action offers and Host-only ReadGrants. Only its bounded model-facing representation enters Wake; private grant payloads, controller handles and credentials do not. Route/action schemas accompany the offers. Evidence records provenance and consistency; it is not a new persistent memory store. + +Strategy output is only a proposal. Core validates instance identity, manifest capabilities, allowed routes/actions and budgets. The Host checks current authority on execution. Source consistency is explicit: `exact-snapshot` for a captured document/runtime snapshot, or `namespace-pinned-live-read` for a Provider whose namespace can be pinned but remote contents remain live. The latter never promises historical database snapshots. + +```mermaid +sequenceDiagram + participant DSH + participant Host + participant Core + participant Source + participant LLM + DSH->>Host: turn begins (scope, scenario) + Host->>Core: acquire Serving generation, compose + Core->>Source: facts, project after Strategy selection + Source-->>Core: fragments + opaque ReadGrant + Core-->>Host: immutable View + Host->>LLM: own plugin message: bounded Wake + routes/actions + LLM->>Host: selected route/action + input + Host->>Core: scope, authority and budget checks + Core->>Source: query / mutate + Source-->>LLM: bounded Evidence / committed Receipt via Host + DSH->>Host: turn ends + Host->>Core: release lease, drain retired generation +``` + +## Lifecycle and failures + +Host `MemoryExecutions` pairs each Core turn with its runtime binding. Agent lifecycles and background coordinators use the same owner: concurrent maintenance shares one View, and a foreground handoff waits for child/tool cleanup before resolving the current runtime. The lifecycle decides whether to cancel idle review; the owner does not cancel unrelated authorized writes or grant new authority. + +Candidate composition is validated before publication. A rejected additional candidate does not silently replace the Serving generation. Explicit removal of a required contribution retires that generation and prevents new turns from acquiring it. Existing turns and in-flight operations retain leases until they finish; then the old Source runtime and private resources drain. + +Each turn pins one immutable View. Writes yield receipts; later turns see new revisions. Concurrent root/child work cannot substitute another turn's grant. A Source/Provider failure remains scoped and observable; partial, failed, cancelled and committed results are not conflated. Disabling participation does not delete data. + +A child captures its delegated View and generation at dispatch, retaining both until disposal even if the parent completes or the Serving generation changes. Each child execution has its own turn identity and retrieval budget; it never falls back to the parent's latest View. Wake is appended after shared context as a `dsh-mnemon` plugin message, without reinjecting or interpolating other plugins' context. + +## WebUI and management + +Default Sources own their bilingual copy and layout assets under `presentation/`. Their Clients load those assets themselves; the Starter also composes them through public package paths to retain the existing optional page-kit exports. The workbench owns navigation, containers and theme, not Source-specific selectors. This is default-product presentation, not a Core requirement or a new UI framework. + +Each Source owns its optional `./client` DSH module, pages, management operations and tests. It registers through the public Source-page SDK into the workspace's `mnemon.source.page` Slot. DSH still owns Client lifecycle and React rendering. + +The Host supplies a scoped management client and sanitized instance metadata, not raw RPC/Host Context or an LLM grant. Reads and confirmed revision-fenced mutations address one Source. Default workflow assistance (such as Document-to-Space archival) lives in Host coordination and uses those same public operations. + +One shared workspace has two mutually exclusive DSH placements. Sidebar contributes the matching `mnemon` entry to the public `sidebar.panellist` and `main` slots of the supported DSH releases, 0.1.7-rc.2 and 0.2.0-rc.2. DSH owns the button, icon size, label, selected state and main-panel navigation. The `shell.overlay` registration retains Source child-render authority and portals the workspace into a persistent native main seat, preserving page state across panel switches and the independent Better Sidebar seat. Replacement layouts without the native panel contracts retain the existing launcher and overlay fallback. Sidebar opens without a conversation, keeps its own workspace selection, coordinates with Taskboard/SSH and returns through `layout.selectPanel(null)`. The conversation tab (`builtin`) uses `conversation.view` and the owning session's storage scope for reads, writes and tasks, with no independent workspace picker. Both placements render the same Source-owned child Slots. No separate React root, fallback page registry or cloned business page exists. + +## Compatibility and future evolution + +The default Starter retains storage selection, persisted formats, named tools and user workflows. `displayMode` selects `sidebar` (default) or `builtin`; the Host accepts legacy `buildin` and saves the canonical spelling through DSH's revision-fenced settings writer when writable. This changes one preference, not memory data or Core/Source contracts. Other configuration keys retain their meaning. This does **not** preserve private controllers, old `kernel/layers/provider-sdk` root exports or historical wrapper packages. The current public entry list is in [Extension development](./extensions.md). + +The RSI seam is deliberately small: create a candidate Source/Strategy artifact, test/replay it with fixed facts and requests, review its requested authority, then install/select it normally. Generations support verified replacement and drain; they are not an autonomous code-execution or promotion service. Cordis ownership/isolation is not a security sandbox. High-risk external actions require a separate authority boundary and are not authorized merely by being called “memory”. diff --git a/dsh/docs/en/development/extensions.md b/dsh/docs/en/development/extensions.md new file mode 100644 index 000000000..47220af84 --- /dev/null +++ b/dsh/docs/en/development/extensions.md @@ -0,0 +1,250 @@ +# Building Memory Plugins + +[简体中文](../../zh-CN/development/extensions.md) | **English** | [Documentation Center](../README.md) + +Choose the ownership boundary before writing code. The complete external examples under [plugin-consumer](../../../scripts/fixtures/plugin-consumer) are compiled and tested against packed artifacts outside this repository. + +## Distinguish contribution responsibilities + +| Plugin | Owns | Public dependency | +|---|---|---| +| `dsh-mnemon-source-*` | A memory authority, projection, retrieval/mutation and optional pages | Core contracts and extension SDK | +| `dsh-mnemon-strategy-*` | A complete View Strategy, or an additive contribution supported by its target Strategy | Core contracts and extension SDK; the target Strategy's extension SDK | +| `dsh-mnemon-provider-*` | A driver under Memory Spaces, with its descriptor, capabilities, connection schema and icons | Memory Spaces Provider SDK | + +A Source need not implement a Provider. A Provider need not know View composition. A Strategy receives facts, not Source objects. Cross-package imports target declared public exports only; never reach into another plugin's `src`, controller, registry or build configuration. + +| Entry | Responsibility | +|---|---| +| `dsh-mnemon` | DSH Host and default Starter | +| `dsh-mnemon/core` | Cordis plugin installing the Source-neutral service; no engine exports or Host/UI | +| `dsh-mnemon/contracts` | Source/Strategy callback contracts and JSON-safe manifests, facts, ViewSpec, View, Evidence and Receipt | +| `dsh-mnemon/extension-sdk` | Source/Strategy definitions, lifecycle installation and validators | +| `dsh-mnemon/testing` | Scoped composition, route/action and management testing; JSON diagnostics; built Client artifact loader | +| `dsh-mnemon/client` | DSH workspace and Source-page SDK | +| `dsh-mnemon-source-memory-spaces/provider-sdk` | Memory Spaces' own Provider child-module contract | +| `dsh-mnemon-source-memory-spaces/testing` | Private-child module fixture and driver authority/connection fixture | + +## A contribution service, not an engine + +Following the DSH service/Slot pattern, the public extension point is a small contribution contract. `Context.mnemonMemory` is typed as `MnemonMemoryService` and its actual, frozen object exposes only `installContributions`. Authors call `installMemory(ctx, contribution, options?)`: it resolves the calling Entry identity and binds registration cleanup to that Fiber. Do not introduce a parallel registration API. + +The engine, installed records, registry, generations and leases stay internal. Neither `extension-sdk`, `contracts` nor the Core plugin exports them. `MemorySourceRuntime` is intentionally public: it describes the callbacks **your Source implements**, not an engine handle. Definitions/factories are Host-side executable contracts; metadata, operation inputs and results are JSON-safe. This service boundary is not a sandbox for arbitrary JavaScript. + +## Source lifecycle + +Define a `MemorySourceDefinition` with `defineMemorySource`. Its manifest declares API version, type id, package name, role, consistency and supported routes/actions. Its factory receives stable instance provenance and immutable configuration. + +- `facts(request)`: bounded, non-sensitive availability, revision and capabilities. +- `project(request)`: bounded text plus a Source-owned ReadGrant consistent with the selected revision. Capture concurrent snapshots per request/scope; do not share one mutable “last snapshot”. +- `query`: receive only a `{ id, scope }` View identity and this instance's own `grant`, enforce the Source's scope, and return bounded Evidence with provenance. +- `mutate`: perform only an authorized action, honor cancellation and distinguish committed/partial/failed results. It receives the same narrow View identity and an optional instance-local `grant`; a read grant is not write authorization. +- `manage` (optional): authenticated human operations, separate from model grants; validate confirmation and exact revision again. +- `dispose` (optional): release runtime-owned resources after generation leases drain. + +The complete `ComposableMemoryView` stays in the Host and composition tests, never in Source callbacks. Do not inspect other instances' projections or grants through `request.view`; writes needing their own pinned read scope use `request.grant` directly. + +`facts(request, signal)` and `project(request, signal)` receive cancellation separately from the JSON input. Forward it to network reads and never write data in these callbacks. Core reads independent Sources concurrently with a default 10-second deadline per read. The DSH Host uses its existing `timeoutMs`; independent tests can set `MemoryCompositionRunner({ sourceTimeoutMs })`. Timeouts and remote failures produce sanitized `view.diagnostics`; malformed protocols still reject the View. Cancelling a turn is never downgraded to an optional Source outage. Deadlines cannot interrupt synchronous code blocking the event loop: this is not a malicious-plugin sandbox. + +```ts +import type { Context } from '@deepseek-ai/cordis' +import { defineMemoryPlugin, installMemory } from 'dsh-mnemon/extension-sdk' +import { notesSource } from './source.js' + +export const name = 'dsh-mnemon-source-notes' +export const inject = ['mnemonMemory'] +export const memoryPlugin = defineMemoryPlugin({ + packageName: name, + label: { en: 'Notes', 'zh-CN': '笔记' }, + description: { en: 'Durable personal notes.', 'zh-CN': '长期个人笔记。' }, + roles: ['source'], + provides: [{ id: 'source' }, { id: 'source.durable-evidence' }], +}) +export function apply(ctx: Context): void { + installMemory(ctx, { plugin: memoryPlugin, sources: [notesSource] }) +} +``` + +The example assumes `source.js` exports the definition. [external-source.ts](../../../scripts/fixtures/plugin-consumer/src/external-source.ts) implements the complete file-backed example. Source and Strategy are roles, not mandatory package/repository boundaries. One package can call `installMemory(ctx, { sources: [source], strategies: [strategy] })`: both contributions install and unload together under the same Fiber, retaining distinct instance keys. Split packages only for independent reuse or replacement. Strategy selection remains explicit; bundling one does not override the user's selected Strategy. + +Every composable plugin should export one JSON-safe `memoryPlugin` descriptor and pass the same object to `installMemory`. `roles` describe contributions; they do not create separate plugin classes. `provides` and `requires` form the activation graph. Mark a provided capability `exclusive: true` only when two providers cannot participate together. Declare only hard requirements: a plugin that can truthfully degrade or no-op must not force an unrelated dependency. Core validates the active graph before running Source factories, and remains the only component that compiles the graph into one View. Plugins without this prerelease descriptor keep their existing composition behavior, but the Host can infer only limited identity and relationship information. + +An Entry id identifies an instance; type id identifies its implementation. Never strip Loader include prefixes. Direct `ctx.plugin()` mounts without Loader identity must supply `installMemory(..., { instanceId })`. Paths/credentials remain instance-owned. Do not use a module-global database or service registry. + +## Complete Strategies and additive contributions + +Use `defineMemoryStrategy` and install with `{ strategies: [definition] }`. Declare deterministic composition, supported roles and maxima; a role-agnostic Strategy lists `ANY_MEMORY_SOURCE_ROLE` (`'*'`) alone. Pure `compose` returns a `MemoryViewSpec` selecting exact Source keys, eager/routed projection budgets and Source-local route/action ids. It performs no network, storage or secret access. + +Several main Strategies can be installed; exactly one composes each View. `memoryView.strategyTypeId` selects it, and when the selected Strategy is switched off while exactly one other remains, the Host composes with that one and reports a `strategy-fallback` diagnostic. A Strategy may declare exclusive `extensionSlots`. A small plugin uses `defineMemoryStrategyExtension`, installed through `{ strategyExtensions: [definition] }`. Enabling contributes bounded JSON to one target slot; disabling removes only that contribution. Different slots compose into one View. Duplicate slots for the same target reject registration instead of using installation order. Contributions targeting an unselected Strategy are observable but do not execute. Unsupported slots reject the candidate generation and preserve existing Serving; invalid dynamic results reject the affected turn rather than silently ignoring the plugin. + +Core validates identities, JSON and the 64,000-character bound, deterministic replay, lifecycle, and the final View's existing budgets and permissions. It defines only the standard `selection`, `projection` and `capture` value contracts below; other slot names belong to the owning Strategy. Callbacks see only the request and permission-filtered Source facts, never Source handles, grants, or write callbacks. The owning Strategy's public SDK defines slot semantics. + +### Optional configuration descriptor + +A dedicated Strategy Entry can additionally export `memoryStrategyConfiguration`, created with `defineMemoryStrategyConfiguration` from the Core SDK. It declares public fields (`number`, `text`, `textarea`, `string-list`, `source-list`) and a **pure `create(config)` factory shared with `apply()`**. The factory returns exactly one Strategy or extension contribution together with the same `memoryPlugin` descriptor; it performs no I/O, credential access or Fiber mounting. This optional convention is for the Host and future tooling; it does not define plugin identity or activation relations. + +The helper validates and freezes a copy of the metadata without running the factory. Its returned `create(config)` validates supplied fields and the declared contribution before returning it; omitted defaults remain the factory's responsibility. `number` values are finite integers, lists contain at most 32 unique nonempty strings of at most 500 characters, and text defaults to a 4,000-character limit. Host discovery uses the same validation for modules without the helper, while retaining Loader identity checks and local error isolation. + +Ordinary users see no generic memory-plugin discovery, dependency graph, or installation dialog. Every installed memory component appears on the **Memory composition** board of the `dsh-mnemon` page under **Plugins**, above DSH's own component list. Where DSH's plugin manager is available, their enablement is saved in the DSH profile patch, so the native component switches on the Plugins page and Mnemon's controls always agree; Mnemon saves only the selected main Strategy and each Entry's configuration. + +#### How a plugin appears on the board + +The board is drawn from what each plugin declares, so a new plugin needs no Mnemon change to be listed, switched and configured: + +| Declaration | On the board | +|---|---| +| `roles` | `strategy` is one choice of **Main strategy**; `source` has a row under **Memory sources**, even while off; `strategy-extension` has a row under **Enhancements**; any other role gets a group of its own | +| `label`, `description` (`en`, `zh-CN`) | The row's name and description, in the page's language. Shipped components declare theirs the same way; the component's page also shows its package and whether it ships with dsh-mnemon | +| `strategyTypeId` of an extension | `'*'` or the selected main Strategy's type lists the row with the others; another type moves it to a closed group for other main Strategies | +| `requires`, `provides` | Turning a component on also turns on the first installed provider of a capability it requires that nothing provides; turning one off also turns off what loses a capability it required. A main Strategy keeps its last Source: that switch is refused. Rows name a single provider they need, and the running component that depends on them alone, as link chips; the component's page lists every relation | +| `provides[].exclusive`, extension `slot` | Two components claiming the same capability, where either claims it alone, or two extensions filling the same slot for overlapping main Strategies, cannot run together: turning one on turns the other off | +| `memoryStrategyConfiguration.fields` | A gear on the row, and an options section on the component's page with one control per field, its label, description, default and limits; **Apply** appears once one changes. `source-list` offers the running Source instances whose role is in `sourceRoles`, named by the components that registered them | + +The component's page says what else its switch would move, and the toast after a switch names what moved, with **Undo**. Declare `requires` and `provides` precisely: they are how users see what your plugin needs, and what it cannot run beside. + +DSH's own plugin list names a package from its `locale/en.json` and `locale/zh.json` (`meta.title`, `meta.description`), exported as `./locale/*.json` and listed in `files`; without them it shows the package name. Give them the same text as the declaration's `label` and `description`, as the shipped packages do, so the component reads the same on DSH's list and on the board. + +#### A component's own settings and Status card + +When declared options are not enough, a component adds its own settings to its page from its `./client` entry, the way the shipped components do: Runtime Memory's user profile scope, Memory Spaces' Providers and embedding, the Layered strategy's background tasks. A Source can also say what its card on the Memory System's Status page shows while it runs. + +```tsx +import { installMemoryComponentUI } from 'dsh-mnemon/client' + +export function apply(ctx) { + ctx.effect(() => installMemoryComponentUI(ctx, { + packageName: 'acme-memory-notes', + settings: ({ component, writable, language }) => , + status: ({ language }) => <>{notesHeadline(language)}

{notesDetail(language)}

, + })) +} +``` + +The settings render in the `mnemon.component.settings` region, keyed by the package name the plugin's declaration names, after the page's state, relations and declared options; the component's row gets a gear that opens them. They receive only what the page knows: `component` (`packageName`, `label`, `enabled`), `writable`, `language`, and the conversation's `sessionId` and `workspace` when there is one; bring your own services through your own registration. Registration waits until dsh-mnemon's configuration exists and ends with the returned function. Follow the page's interaction rules: switches and selectors apply when they change, typed values wait for your own **Apply**, and your settings never join another group's save. + +The Status card renders in the `mnemon.component.status` region, keyed the same way, as a headline in `` and one line in `

` under the component's declared name. It receives `component`, `language`, and the Memory System's `sessionId` and `workspace` when there is one, and reads its own Source. Every Source component has a card whether or not it contributes one: a component that is off, or whose Source is not running, shows the page's own note instead, and one that contributed nothing shows that it runs. Tabs, the card and the header use the name the component declares. + +Third-party packages continue to use DSH's native Profile/Loader workflow. Install an exact package with `dsh plugin --profile add @ --save-exact`, verify its `peerDependencies` and `dsh.bundle.patch`, then activate it explicitly in Profile composition after restarting. Downloading an npm package is not activation, and Mnemon does not hot-load it into the current process. External standalone repositories following this guide are welcome; generic graphical management may be revisited after the contracts and community cases settle, but is not a v0.5 promise. + + + +### Standard View extensions + +`defineMemoryViewExtension` from `dsh-mnemon/extension-sdk` targets `ANY_MEMORY_STRATEGY` (`'*'`): the extension follows whichever selected Strategy declares its slot, and stays inactive with a diagnostic otherwise. A slot has one owner: registering a second extension for it is rejected when both target the same Strategy or either targets `'*'`, because the pair would meet after a main Strategy switch. `validateMemoryViewExtension` and `memoryViewExtensionValues` give a Strategy the same validated values. `dsh-mnemon-strategy-default-three-tier/extension-sdk` keeps `defineThreeTierExtension` for an extension that should apply only to that Strategy. The shipped enhancements use the standard slots: + +| Optional plugin | Slot | Contribution | +|---|---|---| +| `dsh-mnemon-strategy-scoped` | `selection` | Source key order and writable subset; does not create Sources or change physical storage scope | +| `dsh-mnemon-strategy-light-context` | `projection` | One shared projection cap; not incremental injection or summarization | +| `dsh-mnemon-strategy-auto-capture` | `capture` | Current-turn instructions, targets and explicit recording Action ids; no background Agent or direct writes | + +The Starter installs all three packages and registers their DSH Entries disabled. The switches are therefore always available, while the default composition, allocation and guidance preserve v0.4 behavior. Enabling one contributes to the selected main Strategy without changing it; disabling it removes only that contribution and never deletes Source data. The General strategy (`dsh-mnemon-strategy-general`) accepts the same three slots. Runtime currently has no expansion route: an aggressively small resident cap can hide hot context and needs workload-level evaluation. + +Source keys in `scoped` must retain any Loader include prefix. Omitting its configuration deterministically selects existing instances by role/key and creates no storage. The board's **Options** panel edits the fields the package declares; Profile configuration remains available for automation. + +`scoped.sourceKeys` expresses priority and `writableSourceKeys` narrows the writable subset. Automatic capacity maintenance also checks the current View's write scope; a denied operation preserves the original data and fails instead of migrating around the restriction. Explicit operator management remains separately authorized. + +```ts +import type { Context } from '@deepseek-ai/cordis' +import { defineMemoryViewExtension, installMemory } from 'dsh-mnemon/extension-sdk' + +export const inject = ['mnemonMemory'] +export function apply(ctx: Context): void { + installMemory(ctx, { strategyExtensions: [defineMemoryViewExtension({ + typeId: 'my-light-context', packageName: 'dsh-mnemon-strategy-my-light-context', + slot: 'projection', contribute: () => ({ maxProjectionCharacters: 4096 }), + })] }) +} +``` + +In-turn writes still use Host tools, authorization and Source receipts. A capture contributor must name actual recording Actions, not infer them from generic write capability. Retrieval limits remain shared across the executing turn; Source-qualified replay and Related admission prevent cross-instance aliasing. If removal cannot produce a replacement generation, new turns fail closed rather than revive the disabled policy: they run without a memory View, and the conversation itself continues. Existing pinned turns retain their leases. + +Optional `createTurn(view)` supplies an execution-local `query(request, read)` policy. The only supplied I/O is `read(input, narrowerLimits?)`, bound to the selected Route and its private grant. Core still validates inputs, ceilings, dispatched calls and lifetime. A policy may admit/replay results and supply a compact `Evidence.output` for the model; it does not obtain a Source object, write continuation or authority. Separate executions get separate policy state even when they inherit the same immutable View. Without this hook, reads go directly to the Source through the same Core fences. + +The Layered strategy plugin uses this hook for the old Documents slot, two-query Recall envelope, deduplication and Related admission. Named tools and generic View routes share that policy. Source implementations retain raw search, storage and maintenance; explicit DSH-assisted writing/archiving remains a Host workflow, not a generic Core background job. + +The default plugin's public, pure `threeTierActionWorkflow` policy identifies capacity maintenance for Runtime `mutate`. The Host applies it to named tools, generic View Actions, child Agents and browser management only while the Layered strategy is selected. Sources retain independent management protocols; Core gains no three-tier storage logic. Model writes retain their initiating View, instance and authority, with archival limited to that View's writable Memory Spaces Source and its Source-defined write scope. Ambiguous writable archive Sources are rejected. Browser operations carry the registered workspace scope, exact instance and confirmed revision without requiring a user conversation. An independent maintenance task is created only when model judgment is needed. + +For archive preflight the Host can pass `{ writeScope: { viewId, grant } }` to the selected Memory Spaces Source's `body-directory` read. The optional response `writeScope: { viewId, sourceInstanceKey, memoryBodyIds }` uses the same authority as `remember`: known namespaces in the grant plus namespaces created by that View. The Source validates grant ownership; the Host validates the echoed View and Source identity, treats empty scopes as deny-all, and rechecks authority and live capabilities before writes. Source implementations that omit this response retain the narrower active namespace pins. Malformed responses fail closed. The Host never substitutes another Source or a live catalog for missing authority; recall keeps its original namespace pins. + +Selected Sources are required by default. Set `required: false` to explicitly permit omission when that instance is unavailable or its projection fails. Required failures reject the turn without silently switching strategies. The Layered strategy selects available Sources as optional so an external read failure does not remove healthy layers. A Strategy must explicitly reject a missing required instance rather than return an empty selection. + +[external-strategy.ts](../../../scripts/fixtures/plugin-consumer/src/external-strategy.ts) is a working explicit-selector example. Once installed, it appears in the **Main strategy** selector on the `dsh-mnemon` page under Plugins, which saves its type id as `memoryView.strategyTypeId`; for automation, `mnemon.memoryTopology.strategyId` selects it when no choice has been saved. More than one applicable Strategy is an error, not “last imported wins”. A profile replaces default Entries explicitly; installing a package alone is not authority to replace them. + +Optional `ViewSpec.guidance` carries the Strategy's trusted `system`, `routing` and read/write reminders separately from quoted Source data. It is validated and pinned into the View digest. The Host supplies generic routing guidance when none is provided and renders named tool availability without repeating schemas already in DSH's tool catalog; external/unbound operations keep their exact ids and schemas. Existing product tools and explicit management remain available; tool presence does not mean that a Source participates in this turn's View. Automatic three-tier background review runs only with `default-three-tier`, never implicitly under a custom Strategy. + +## Open operations, bounded execution + +A Source owns its operation names, input schemas, storage, indexes and maintenance. There is no mandatory five-action taxonomy, summary tree, representation vocabulary or multidimensional budget protocol. A Strategy selects public operations it understands, by id, role and/or capability. Core derives `MemoryAvailableSource.routes/actions` from manifests, live facts and Host permissions; plugin authors do not duplicate descriptors in facts. + +Core bounds projection characters, evidence characters/items and dispatched calls. These are payload ceilings, not complete LLM-prompt token estimates. Sources receive effective bounds and own excerpts and structured formatting; Core omits oversized evidence rather than manufacturing a summary or splitting JSON. Failed dispatched reads consume a call. There is no automatic retry. + +Every mutation receipt declares `completion`: `accepted`, `candidate`, `committed`, `partial`, `failed`, or `unknown`. Only a confirmed full commit can include `committedAt`; Core never invents that timestamp. `createMemoryMutationReceipt(..., completion)` defaults to `unknown`; pass `'committed'` only after the requested durable effect completes. Cancellation or a transport exception does not prove that no write occurred, and a cross-Source workflow is not atomic. Generic and named product tools preserve this distinction for the model. + +## Provider child and Client page + +Provider authors use `defineMemorySpaceProvider` from Memory Spaces' SDK. A module receives only its bound `host.install(ctx, definition)` capability; the private parent Host, Snapshot and Registry are not SDK exports. `installMemorySpaces` mounts explicit children and returns `Promise`, not a parent handle. [external-provider.ts](../../../scripts/fixtures/plugin-consumer/src/external-provider.ts) is tested in two independent parent Sources with identical child ids. + +Use `mountMemorySpaceProvider` from the Source's `/testing` entry to test real child registration, aliased adapter creation and cleanup. It returns frozen descriptor/manifest metadata, `registered`, `createAdapter` and `dispose`. `createMemorySpaceProviderFixture` separately supplies validated connection data and a scoped driver authority. The [published API test](../../../scripts/fixtures/plugin-consumer/tests/public-api.spec.ts) combines both without private imports. + +```yaml +- id: work-spaces + name: dsh-mnemon-source-memory-spaces + config: + dataDir: /absolute/path/to/work-memory + providers: + - use: dsh-mnemon-provider-holographic + instanceId: local-facts +``` + +The Source itself authors the Provider Fibers. Core supplies no Provider factory registry and no second Context service. Each module owns truthful capabilities: a query-only backend must not fabricate browse results or graph edges. + +A Provider that declares `entities` feeds the Entities page. By default the Source builds each space's entity index from `list()`: return every memory up to the requested limit with its `entities`, and an entity's count is then the number of listed memories that carry it, the same memories the page lists. A Provider whose `list()` stops short of the whole space, such as at a service page size, or that cannot list, implements the optional `entityIndex(body, signal)` and returns `{ memories, complete }`: the memories that carry entities, and whether that is all of them. The page marks an incomplete index as partial. An entity Provider with neither listing nor `entityIndex` is shown as query-only; its memories reach the page only as related memories found by recall. The Source keeps one index per space while the space's `status()` statistics stay the same, so return statistics that change with every write. + +An optional `./client` entry calls `installMemorySourceUI` from `dsh-mnemon/client`; DSH loads it as an ordinary Client plugin. It receives `MemorySourcePageProps` with selected instance, locale, writability and scoped `management.read/mutate`. Use `MemorySourcePageFrame` for shared locale/appearance. No Host Context, driver, token, LLM grant or transport is passed to React. Missing pages use generic management; duplicate page ownership and rendering failure have local diagnostics. + +The workbench owns the outer page inset, minimum height and page scroll container. Nested `memoryPageStyles.page` content shares that frame instead of adding another viewport height or inset, including through DSH renderer wrappers. A Source keeps its business layout and may retain bounded readers or dialogs. On local page changes, call the optional `onResetScroll` callback before paint; it resets only the owning workbench canvas. Do not scroll DSH ancestors or peer plugins. A Source with a compound sticky title/tab header, such as Memory Spaces, sets `navigation.stickyHeader: false` to prevent the Host from also pinning its subordinate headings, and owns that compound header's sticky layout itself. + +To reveal content, pass an element from the Source's own ref to the optional `onRevealElement(element, topInset)` callback. Without `topInset` it lands below the locked page header; a Source that pins its own header passes that header's measured height instead. The Host scrolls only its canvas and ignores elements outside it; avoid global IDs and `scrollIntoView`, which can move DSH ancestors. Cancel pending animation frames when the selection closes or the Source unmounts. Both scroll callbacks are optional: new Sources still load with the existing Root peer floor, with manual scrolling when an older Host does not supply them. + +`refreshKey` changes each time the user presses **Refresh** in the workspace header; reload the page's data when it changes instead of adding a refresh or sync button. `navigationInput` is `{ seed, nonce }` only when a conversation anchor, such as an item in the turn memory bar, opens the page; a tab opens it without one. The default Sources read the seed as a document id (Project Documents selects it), a recall query (Memory Spaces runs it on arrival) or an entry's text (Runtime memory highlights and reveals it). A new `nonce` means a new request for the same seed. + +`dsh-mnemon/client` also exports the controls the default Sources use, so an installed Source looks and behaves like them: `SearchField` (DSH's input with a search icon), `SelectField` (a labelled DSH selector menu, with `inline`, `size: 'sm'`, `hideLabel` and `ariaLabel`), `WriteReceipt` (a write's outcome, summary and optional view action) and `TaskAgentTag` (whether a task Agent can take the request). They follow the [interaction conventions](../guides/ui-guide.md#interaction-conventions). A Source that imports them, or reads `refreshKey`, declares a `dsh-mnemon` peer floor of 0.5.19, the first Starter to export them. + +## Independent repository checklist + +Keep business copy and layout inside the Source package. The default Sources demonstrate this with public data-only `presentation/locales.json`, `presentation/page.module.css` and `presentation/sidebar.module.css` assets, plus a private Client presentation module. Their own builds load the assets without importing repository build scripts. They retain the existing page-kit class namespace for usage compatibility; a third-party Source can use its own namespace and does not need to add words or selectors to Root. + +A plugin directory owns `package.json`, exports, `src/`, `tests/`, TypeScript/build/test configuration and README. Declare compatible public peers and development dependencies. Publish built Host/Client artifacts and declarations; never depend on sibling paths, workspace source aliases, root test helpers or implicit hoisting. + +The default Starter alone mounts defaults. Source/Strategy packages do not ship self-activating default patches. A user's profile, bundle or explicit parent composition controls activation and replacement. + +```ts +import { MemoryCompositionRunner } from 'dsh-mnemon/testing' +import * as notes from './index.js' +import * as focus from './strategy.js' + +const runner = new MemoryCompositionRunner() +try { + await runner.mount(notes, { instanceId: 'work', config: { path: '/tmp/test-notes.txt' } }) + await runner.mount(focus, { + instanceId: 'focus', + config: { sourceKeys: ['source:work'], mode: 'eager' }, + }) + const turn = await runner.beginTurn() + try { + const route = turn.view.routes.find(route => route.sourceRouteId === 'read')! + const evidence = await turn.executeRoute(route.id, {}) + // Assert projection, evidence and provenance. For writes, use + // turn.executeAction(offer.id, input, authorize), with explicit test authority. + } finally { turn.release() } +} finally { await runner.dispose() } +``` + +Use unique temporary paths in real tests. Release turns before disposing the runner. `runner.inspect()` returns JSON-only evaluation/generation diagnostics; `managementClient(sourceKey)` supplies scoped human operations. No turn exposes a lease, and no runner exposes the engine. Unmount/remount plugins to test replacement instead of editing an internal registry. Disposal also releases retained turns; in-flight operations hold their own leases until completion. This is a test harness over real Cordis/Core, not another production Loader. + +Test at least: valid composition; missing/ambiguous dependencies; two instances; schema/capability/authority denial; concurrent snapshots; stale revisions; cancellation and partial failure; unload/drain/reload; persistence; management and actual page clicks. Providers additionally test credentials, truthful capabilities, malformed upstream data, timeouts and conformance inside their parent Source. + +Run each plugin's `pnpm verify`. At repository level, `pnpm verify:plugins` packs all 17 artifacts, installs every plugin outside the workspace through ordinary semver manifests, then type-checks/tests/builds each and compiles the external consumer. No source aliases, manifest overrides or workspace links are permitted in that gate. + +For RSI, keep candidate inputs/artifacts reproducible, compare against a known composition, and promote only through an explicit installation/selection decision. Passing a Strategy replay does not sandbox arbitrary JavaScript or grant permission to trade, send messages or delete external data. diff --git a/dsh/docs/en/development/releasing.md b/dsh/docs/en/development/releasing.md new file mode 100644 index 000000000..ea4a55fd4 --- /dev/null +++ b/dsh/docs/en/development/releasing.md @@ -0,0 +1,22 @@ +# Releasing independent packages + +**English** | [简体中文](../../zh-CN/development/releasing.md) | [Development](./README.md) + +Use a release PR; do not publish directly from a feature branch. README and other files shipped in npm artifacts also require release intent. Site-only documentation and historical evidence do not change package versions. + +```text +Change + changeset → reviewed PR → release-version PR → main revision +→ verify and pack changed packages → npm-release Environment approval +→ publish changed dependency layers → install the full pinned combination +→ publish Starter → Registry upgrade smoke → GitHub Release +``` + +Official packages use independent versions. The `dsh-mnemon` Starter is a tested bill of materials: it pins every included plugin exactly, while plugin peer dependencies describe compatibility across the current minor line. A plugin version advances only when its own artifact or published metadata changes; the Starter advances whenever that pinned composition changes. Packages that are only reverse-dependency test targets are not republished. + +Add a release intent with `pnpm changeset` to every pull request that changes a published artifact. `pnpm release:status` previews the resulting package bumps, while the existing source CI job compares the PR with its exact base revision and rejects any changed published package missing from the new changesets. A pull request that adds a package includes a release intent for it too; the package first publishes with the next release pull request, at the version that intent produces. A dedicated release pull request runs `pnpm release:version`, which applies all intents, synchronizes the exact Starter and external-fixture pins, updates generated Provider version declarations, and refreshes the lockfile. Workspace peer and development relationships use compatible minor-line ranges so an unchanged package's manifest remains unchanged. When a changed plugin starts using a Starter SDK export added in the same release, the release pull request also raises that plugin's `dsh-mnemon` peer floor to the new Starter version. `pnpm release:check` is read-only and validates the complete mixed-version composition, compatible internal ranges, exact Starter pins, repository metadata, and each package's npm channel. + +The manually dispatched npm workflow accepts a full commit SHA that must already equal `main`. It derives the preceding release revision from Git history and selects only packages whose versions advanced. Before requesting any npm credential, it runs the complete workspace and independent-plugin suites, then packs the selected plugins plus the Starter once and records the full composition, both revisions, byte sizes, and SHA-512 integrity. The protected `npm-release` Environment gates Registry writes. After approval, changed plugins publish concurrently within dependency-safe layers; every layer becomes readable before its dependents continue. The workflow installs the complete mixed-version composition through the frozen local Starter, publishes the Starter last, verifies a clean Registry install, and runs the real Registry upgrade before creating the GitHub Release. + +Interrupted runs are resumable: an existing selected version is reused only when its Registry integrity exactly matches the frozen tarball. A different artifact at the same version stops the release. Stable packages use `latest`; prereleases use their explicit `alpha`, `beta`, or `rc` channel. Unchanged packages must already exist on the Registry and are verified through the complete Starter install, but they are neither packed nor published again. + +npm publication is not transactional. On failure, preserve the frozen artifacts and rerun the same revision; the integrity checks safely skip matching packages and continue missing ones. Never overwrite or unpublish an immutable version. A release is complete only after the new Starter and every version in its pinned composition are installable. Development verification never creates a Git tag, GitHub Release or npm publication. diff --git a/dsh/docs/en/development/skin-integration.md b/dsh/docs/en/development/skin-integration.md new file mode 100644 index 000000000..1316fc43e --- /dev/null +++ b/dsh/docs/en/development/skin-integration.md @@ -0,0 +1,81 @@ +# Skin development and Mnemon integration + +**English** | [简体中文](../../zh-CN/development/skin-integration.md) | [Development and verification](./README.md) + +This guide is for authors who want the Mnemon workbench to match their host skin. Mnemon provides a stable page selector and background properties; the skin chooses colors, transparency and readability. Installing an existing skin does not automatically enable a glass effect for Mnemon. The skin must supply an explicit override. + +## Public contract and scope + +The Sidebar and the conversation tab share this root selector: + +```css +[data-dsh-plugin="dsh-mnemon"][data-dsh-part="mnemon-view"] +``` + +The supported background properties are `--mn-bg`, `--mn-backdrop` and `--mn-surface`. The [UI guide's skin contract](../guides/ui-guide.md#theme-skin-overrides) defines their value types, default composition, cascade priority and inheritance scope. Without overrides, the default background remains and the official theme stays opaque. + +This contract was introduced by [PR #284](https://github.com/omdsh-dev/dsh-mnemon/pull/284); test with a build containing that change. Older versions may lack the root marker, in which case the rule does not match. CSS-module hashes, unlisted internal properties and DOM nesting are not compatibility interfaces. + +Background integration belongs in the skin's Client styles. It needs no new Mnemon setting or changes to Host, Source, Strategy, Provider or storage formats. The root rule does not cover other plugins or dialogs portaled to `body`; dialogs retain their own surface styles. + +The native Sidebar entry is rendered by DSH through `sidebar.panellist`, just like Plugins. The skin's native panel-row rules therefore supply its color, typography, spacing, selection and collapsed presentation. Mnemon supplies only its label and size-responsive icon; the workbench background contract above applies to the opened page. + +A replacement layout without the native panel seat gets Mnemon's own entry under New Session instead: `[data-dsh-plugin="dsh-mnemon"][data-dsh-part="sidebar-entry"]`. It copies DSH's panel-row values: inherited 14px type, primary label color, 36px rows with `7px 8px` padding and `0 2px` margins, `--dsw-radius-md` corners, a 16px icon (18px on the collapsed rail), and the hover background for both hover and the open workspace. The open state also sets `data-active` and `aria-current="page"`. A skin that restyles native panel rows can target this selector to match. + +The native main seat reserves DSH's `--dsh-frame-top-clearance` once at its outer boundary, keeping desktop window controls accessible. Its page layer sits above decorative body layers and below DSH window chrome; skins should not add another window-title offset to the inner workbench. + +## Add an override to your skin + +1. Put the rule in a CSS file that the skin actually loads, installs, switches and removes. Do not edit Mnemon source or generated `lib/` files. +2. Scope it to the skin's own activation marker, followed by the complete Mnemon root selector. The rule then stops matching when the skin changes. +3. Assign the properties on the Mnemon root element. Assigning `--mn-*` only on `html` or another ancestor is shadowed by the root's defaults. + +For example, the v2 skin format in the [dsh-web skin center](https://github.com/zhu1090093659/dsh-skins) uses `html[data-dsh-skin=""]` as its activation marker. Put the integration rule in the `patches.css` file referenced by `skin.json` through `contributes.patches`. Append to an existing file without replacing its contents; when adding one, add only the relevant field to the existing manifest. + +Replace the example ID `my-glass-skin` below with the `id` in your own `skin.json`. This example assumes that the skin already sets `--dsw-alias-bg-base` to a suitable translucent color for both dark and light modes: + +```css +html[data-dsh-skin="my-glass-skin"] +[data-dsh-plugin="dsh-mnemon"][data-dsh-part="mnemon-view"] { + --mn-bg: var(--dsw-alias-bg-base); + --mn-backdrop: var(--mn-bg); + --mn-surface: var(--mn-bg); +} +``` + +The rule replaces the default backing composition with the skin's base color in workbench regions that consume `--mn-surface`. A fully transparent base produces a fully transparent result, so skin authors must choose suitable opacity. Wallpaper and blur remain the skin's responsibility; these three properties do not create them. The dsh-web file format and activation marker belong to the skin center. Other skin systems should use their own loading mechanism and activation scope with the same Mnemon root selector. + +Keep the rule in an unlayered stylesheet and retain both Mnemon attributes. Those two attributes alone have higher specificity than the defaults, so the override needs neither a particular stylesheet order nor `!important`. A normal declaration inside `@layer` ranks below Mnemon's unlayered defaults. + +## Migrate from generated class names + +For an existing `[class*="_shell"]` rule, keep the skin's background calculation and replace the target with the public root selector scoped to the active skin. Remove the broad old match so it cannot affect similarly named internal layouts in other plugins. Both placements use the same rule; the Sidebar and the conversation tab need no separate skin implementations. + +For example, `--pg-panel-rgb`, `--pg-bg-glass` and `--pg-glass-floor` in Issue #273 belong to that skin. Mnemon does not provide them. Keep their definitions in the skin when migrating; do not add them to Mnemon configuration. + +## Verify in the real WebUI + +Use an isolated environment from the [development and verification guide](./README.md). Install the skin and Mnemon artifacts intended for release, then perform these checks with synthetic memory: + +| Check | Expected result | +|---|---| +| Default theme and disabled skin | The default surface remains, without leftover skin overrides | +| Sidebar and conversation tab | The same root selector matches the current workbench; navigation works in both placements | +| Dark, light and narrow windows | Text, buttons and inputs remain readable; backgrounds and responsive behavior match the skin's design | +| Runtime add, edit and reload | Operations succeed and the record survives reload | +| Open and cancel dialogs | Dialogs stay readable; the workbench remains usable after closing them | +| Plugin composition and return to conversation | Mnemon rules do not alter peer plugin pages or conversation inputs | +| Skin switching and reload | Activation scope follows the skin and matches the persisted choice | + +Capture default and customized screenshots with the same page, data, color mode and viewport. Record Mnemon, DSH, skin-center and skin versions. Clearly distinguish an original skin preview from a preview with added integration CSS. + +If the override does not apply, check whether the skin is actually active, the root marker exists and the stylesheet loaded, then inspect the computed `--mn-surface` in developer tools. For crossed-out rules, check the selector and `@layer`. If the computed value changed but wallpaper is absent, check the skin's own background, scrim and responsive rules. Record skin installation or settings-save failures separately; they do not establish that the Mnemon hook failed. + +When maintaining Mnemon itself, also run the existing targeted regression and documentation checks: + +```sh +pnpm exec vitest run tests/client.spec.tsx -t 'accepts scoped skin variables' +pnpm verify:docs +``` + +The targeted regression covers both placements and style isolation from peer plugins. Actual painting, stylesheet order and readability still need real browser verification. Existing reproduction steps and before/after evidence are in the [Issue #273 acceptance record](../../pr-assets/issue-273-skin-hooks/README.md). diff --git a/dsh/docs/en/guides/capabilities.md b/dsh/docs/en/guides/capabilities.md new file mode 100644 index 000000000..840a2c9a9 --- /dev/null +++ b/dsh/docs/en/guides/capabilities.md @@ -0,0 +1,108 @@ +# Capability map + +[简体中文](../../zh-CN/guides/capabilities.md) | **English** | [Documentation hub](../README.md) + +dsh-mnemon is the memory system for DeepSeek Harness (DSH). It does not force every kind of knowledge into one database. It keeps frequent context, complete project narratives and long-term evidence apart, lets a main strategy decide how they reach each turn, and brings nine long-term memory Providers into one workflow. + +The short rule: **keep every-turn context in runtime memory, complete narratives in Project Documents, and evidence you need across tasks in Memory Spaces.** + +## What you can do + +| Goal | Where | How it runs | Changes data? | +|---|---|---|---| +| Keep preferences, conventions and environment facts in every turn | **Runtime memory** | The Host maintains `USER.md` and `MEMORY.md` projections | Yes, after you confirm | +| Keep complete designs, investigations, procedures and handoffs | **Project Documents** | The Host manages Markdown, search, capacity and revisions | Creating and editing do; reading and searching do not | +| Create a durable space on one of nine backends | **Memory Spaces → Overview → Create Memory Space** | You choose an enabled Provider | Yes | +| Decide the Provider for spaces a task Agent creates | **Memory Spaces → Provider for new spaces** | One fixed Provider, or Host rules first and then the task Agent among the eligible ones | Saving the setting does | +| Find raw durable evidence | **Memory Spaces → Recall → Direct search** | Active spaces answer concurrently with their native recall | No | +| Turn evidence into an answer | **Memory Spaces → Recall → Ask Agent** | A clean task Agent receives only the bounded evidence | No | +| Qualify, deduplicate, distil and write a candidate | **Save to memory** under a reply or on Memory Spaces | A clean task Agent behind Host-enforced tools, paths, locks and receipts, with a receipt for you | Only if the Agent decides to write | +| Title and describe several spaces | **Memory Spaces → Overview → Tidy names and descriptions** | One isolated task per space | Local catalog metadata only | +| Move a document out of hot capacity | **Project Documents → Archive** | A task Agent indexes a cold reference before the Host moves the original | Yes | +| See what memory a turn used | **Turn memory** under the reply | The documents and memories each tool read or wrote, each opening where it lives | No | +| Change how memory is composed | **Plugins → dsh-mnemon → Memory composition** | Switches apply to future turns | Configuration only | + +## Three kinds of memory + +| Memory | Best for | How it reaches context | Source of truth | +|---|---|---|---| +| **Runtime memory** | Frequent preferences, collaboration rules and project facts | Compact injection on every turn | `runtime/memories.json`; the Markdown files are projections | +| **Project Documents** | Long-form knowledge that must keep structure and provenance | Search first, then full text on demand | `documents/index.json` plus managed Markdown | +| **Memory Spaces** | Facts, decisions, entities and relationships across sessions | Bounded evidence recalled from active spaces | A Mnemon Native store or the selected external Provider | + +Each is its own Source plugin. Runtime memory and Project Documents keep local storage; Memory Spaces swaps backends through Providers without touching the other two. External Sources can join through the same public contracts. See [Architecture](../development/architecture.md). + +## Strategies and enhancements + +One **main strategy** composes each turn. **Enhancements** add to whichever main strategy runs, through standard slots, and can be combined. All are chosen under **Plugins → dsh-mnemon**. + +| Component | What it does | Options | +|---|---|---| +| **Layered strategy** (default) | Keeps runtime memory resident; searches Documents and recalls Memory Spaces on demand; runs idle review and capacity maintenance | Task Agent model and idle review on its page | +| **General strategy** | Offers every available source in one budget; the model decides how to use each; no automatic review or capacity maintenance | Resident Sources; Additional guidance | +| **Active capture** | In-turn guidance to keep useful facts. It is guidance, not an autonomous recorder | Recording targets, instruction and operations | +| **Light context** | A shared ceiling on resident context. It is not token accounting | Resident character ceiling | +| **Scoped composition** | Orders the sources that take part and limits which may be written. It creates no storage | Sources in priority order; writable Sources | + +Switching strategies never moves data. A turn keeps the composition it started with. See [Configuration](../reference/configuration.md) for how these choices are saved. + +## Nine long-term memory Providers + +| Provider | Form | Best fit | Scope | +|---|---|---|---| +| **Mnemon Native** | Official local CLI and SQLite | Full graph, exact writes, local-first default | Global, workspace or custom root | +| **OpenViking** | HTTP and `viking://` | Existing resource trees, verified exact writes and semantic retrieval | Target URI and user identity | +| **Honcho** | HTTP workspace and peers | Team and Agent-peer conclusions | Provider workspace | +| **Mem0** | Platform or self-hosted HTTP | Existing Mem0 user and Agent memories | User and agent identity | +| **Hindsight** | HTTP memory bank | Banks, entities and a native graph | Bank id | +| **Holographic** | Local structured fact files | Auditable local entity and semantic facts | Follows the workspace; path override allowed | +| **RetainDB** | HTTP project and user | Project- and user-scoped memory | Project and user identity | +| **ByteRover** | Local `brv` CLI | Code knowledge and curation workflows | Follows the workspace; directory override allowed | +| **Supermemory** | HTTP container | Document ingestion and container sharing | Container tag | + +Memory Spaces' page under **Plugins → dsh-mnemon** holds each Provider's reusable service settings and switch; the Memory System's Memory Spaces page holds the spaces themselves. Providers are off until enabled. Only Mnemon Native needs the Mnemon CLI; when it is missing, another ready Provider can serve Memory Spaces. See [Providers](./memory-providers.md). + +## Who does the work + +**The Host, deterministically.** Status, direct search, content and entity browsing, activation, ordinary runtime edits and document reading or editing need no model. They pass through the Host's schema, path, permission, lock, revision, capacity, timeout and cancellation checks. + +**Independent task Agents.** These never reuse the main conversation's history or context window: + +- **Save to memory** qualifies, routes, deduplicates, distils, writes and reports a receipt; +- **Ask Agent** answers from bounded recalled evidence; +- **Tidy names and descriptions** runs one title and description task per selected space; +- **Document archive** indexes a cold reference before the Host moves the original; +- **Provider for new spaces**, with smart selection, calls a model only when rules leave several candidates. + +Task Agents follow DSH's default route for new sessions. **Task Agent model**, under Background tasks on the Layered strategy's page, can choose a separate Provider and model. Tasks are isolated: a failure shows on its own space or operation and never blocks the page. + +## Where it shows up + +| Surface | What it offers | +|---|---| +| **Memory System** | Status, Runtime memory, Project Documents and Memory Spaces, with every confirmation step | +| **Plugins page** | Memory composition, component pages with their settings (including Providers), storage and interface | +| **Conversation** | The turn memory bar, Save to memory and links into the Memory System | +| **Headless** | The same runtime injection, document search, memory space tools, workspace routing and supervised writes, without a WebUI | +| **Commands and tools** | `/mnemon` commands and the least-privilege tools Agents use | + +## Storage scope + +**Global** uses `MNEMON_DATA_DIR` or `~/.mnemon`, shared by local workspaces. **Workspace** uses `/.mnemon`; local backends such as Mnemon Native, Holographic and ByteRover can follow it. **Custom** is a global scope at a path you choose. **Centralized** keeps each workspace in its own subdirectory of one root, with an optional global `USER.md`. Switching never moves, merges or deletes a previous root. Remote Provider namespaces (workspaces, users, banks, projects, containers, URIs) never change with the DSH workspace. See the [storage model](../reference/storage-model.md). + +## What it does not do + +- Remembered history never outranks current instructions, live tool results or repository facts. +- Provider differences stay visible; missing graph, delete or exact-write support is never faked. +- Provider credentials never reach the browser, a task Agent or a backup. +- Turning a Provider off never deletes remote data; it clears the local catalog, which reconnecting rebuilds. +- Changing storage scope never migrates, merges or deletes an old root. +- There are no distributed transactions across local files and remote Providers. + +## Continue + +1. [Get started](./getting-started.md) +2. [Walk through the UI](./ui-guide.md) +3. [Compare the Providers](./memory-providers.md) +4. [Lifecycle, concurrency and failure boundaries](../reference/workflows.md) +5. [Compatibility and upgrades](../reference/compatibility.md) diff --git a/dsh/docs/en/guides/getting-started.md b/dsh/docs/en/guides/getting-started.md new file mode 100644 index 000000000..8519e8915 --- /dev/null +++ b/dsh/docs/en/guides/getting-started.md @@ -0,0 +1,144 @@ +# Getting started + +[简体中文](../../zh-CN/guides/getting-started.md) | **English** | [Documentation hub](../README.md) + +This guide starts from a DSH with dsh-mnemon installed and ends with memory that a conversation actually uses. It keeps the defaults: the Memory System in the sidebar, global storage and the Layered strategy. You do not need to know about Views or Strategies for everyday use. + +First time? Start with [Install and start](./installation.md): from installing Node.js to installing and enabling dsh-mnemon on the Plugins page and your first memory, with a screenshot for every step. Already installed? Jump to [Open the Memory System](#2-open-the-memory-system). Upgrading? Follow [compatibility and upgrades](../reference/compatibility.md). + +## 1. Install and upgrade from the command line + +This section is for command-line users and also covers development checkouts, cloud access and Headless. You need: + +- DSH `0.2.0-rc.2` (npm `latest` and `next`) or `0.1.7-rc.2`, Node.js `^22.19.0 || >=24.0.0`, and pnpm; +- a DSH model route that can create independent task Agents; +- for Mnemon Native only, a local `mnemon` CLI; [Install and start](./installation.md#other-ways-to-install-the-mnemon-cli) covers each platform. The other Providers connect to their own services. + +Install and check DSH: + +```sh +npm install -g @deepseek-ai/dsh +dsh --version +npm view @deepseek-ai/dsh dist-tags +``` + +The Starter pins a tested combination of the official plugins; the [compatibility matrix](../reference/compatibility.md) lists it. Mnemon's Node 20 entry checks do not establish full Host compatibility. + +

+How task Agents are started + +Semantic work prefers a DSH provider named `spawn` with `toolFilter`, `persona` and `depthLimit`. Mnemon keeps one stable `mnemon_subagent_result` tool and issues a revocable `requestId` for each child. The child returns `{ requestId, result }`; the Host validates `result` against that operation's schema and rejects stale or foreign submissions. Optional background review defaults to a guarded `spawn` child with a bounded checkpoint; a full-context `fork` is opt-in. See [review compatibility and limits](../reference/configuration.md#provider-requirements). + +
+ +Install into the Web profile for the complete workbench: + +```sh +dsh plugin --profile web add dsh-mnemon +``` + +Use an absolute path for a development checkout: + +```sh +dsh plugin --profile web add "link:/absolute/path/to/dsh-mnemon" +``` + +Then start or restart the profile: + +```sh +dsh --profile web +``` + +If the Web profile is reached through a cloud hostname, do not publish port 3080 directly. DSH authenticates every Mnemon RPC and stream through a browser session established from the one-time URL printed at Host startup. Configure the HTTPS reverse proxy or access gateway and trusted authority together, then open that launch URL, by following [Cloud-hosted WebUI](./operations.md#cloud-hosted-webui). + +Upgrade and uninstall: + +```sh +dsh plugin --profile web update dsh-mnemon +dsh plugin --profile web remove dsh-mnemon +``` + +Restart DSH after an update. For 24 hours after a release, pnpm 11 keeps `update` on the installed version; add the new version by name instead (`dsh plugin --profile web add dsh-mnemon@`). Uninstall removes the plugin registration, not memory data in global, workspace, or custom roots. + +Profiles have independent plugin rosters. Install the package separately into Headless when one-shot tasks also need memory: + +```sh +dsh plugin --profile headless add dsh-mnemon +dsh --profile headless "Check durable project context before answering this task." +``` + +For a development checkout, replace the package name with `"link:/absolute/path/to/dsh-mnemon"`. Headless mounts the same Runtime context, Documents, Memory Space tools, lifecycle guidance, and supervised write path as a Web Agent. It does not mount the workbench, conversation buttons, RPC channels, or an interactive slash-command surface. + +With `storageScope=workspace`, Headless resolves `/.mnemon`; no Web workspace registry is required. The one-shot runner exits when its Agent becomes idle, so shutdown cancels any delayed score-based background review that has not started. Explicit or model-guided writes that finish during the task are durable. + +## 2. Open the Memory System + +Click **Memory System** in the sidebar. It opens on **Status**. + +![Status with each memory component and the Providers](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory-status.jpg) + +Check that: + +- the header says **Connected** and names the main strategy, *Layered strategy* by default; +- the engine card shows the dsh-mnemon version, and the Mnemon CLI appears under **Memory providers** if you installed it; +- Runtime memory, Project Documents and Memory Spaces each have a card without errors; +- the storage root matches your storage scope. + +Project Documents needs a DSH workspace even with global storage. Select a workspace for the conversation; "Waiting for workspace" means the project context is missing, not the CLI. If the Mnemon CLI is missing, run `command -v mnemon` and `mnemon --version` on macOS or Linux, or `Get-Command mnemon` on Windows. See [troubleshooting](./operations.md#troubleshooting) for other symptoms. + +## 3. Store your first memories + +**Runtime memory.** Open **Runtime memory**, choose **Add memory** and save a preference in the user profile or a project fact in working memory. It is injected into every later turn. + +**A document.** Open **Project Documents**, choose **New document** and save a short design note or checklist. The Agent searches documents when a question needs them. + +**A memory space.** Open **Memory Spaces → Overview** and choose **Create Memory Space**: + +1. Pick a Provider. Mnemon Native, the local default, is offered once its CLI is installed; enable third-party Providers on [Memory Spaces' page](./ui-guide.md#on-the-plugins-page) first. +2. Give it a narrow name, such as *Project decisions*, and describe what belongs there. +3. Keep it active so conversations can read it. + +In an empty storage root, the first Mnemon Native space uses Mnemon's `default` store id while keeping your name and description; spaces on other Providers get their own ids. Then choose **Save to memory**, enter something stable and secret-free, and choose **Send to task Agent**. An independent task Agent picks the space, removes duplicates and writes, and its receipt says where it went. + +**Check it.** Open **Memory Spaces → Recall**, ask a concrete question and choose **Direct search**. Each result keeps its memory space, category, importance and score; copy its id when you need it. + +![Direct search across the active memory spaces](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory-recall.jpg) + +You can also use conversation commands: + +```text +/mnemon status +/mnemon recall +``` + +## 4. Use memory in a conversation + +Ask a question that depends on what you stored, and let the Agent decide whether it needs memory. After the reply: + +- a **Turn memory** line appears if the turn used memory; expand it to see the documents and memories each tool read or wrote, and select one to open it where it lives; +- the brain mark under the reply is **Save to memory**: it opens an editable dialog, **Cancel** writes nothing, and sending it to the task Agent returns a receipt. + +![An answer that uses working memory, Project Documents and Memory Spaces, with the items the turn read](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/chat-recall.jpg) + +Ordinary conversation does not force recall. Current requests, repository files and live tool results outrank remembered history. + +## 5. Choose how memory is composed + +Open **Plugins → dsh-mnemon**, or the gear in the Memory System header. + +![The Memory composition board](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/plugin-composition.jpg) + +- **Main strategy**: keep the **Layered strategy**, or choose the **General strategy** to offer every available source in one budget and let the model decide. +- **Memory sources**: Runtime memory, Project Documents and Memory Spaces, one switch each. Turning one off stops its context, tools and background work without deleting data. +- **Enhancements**: Active capture, Light context and Scoped composition are off by default; each works with either main strategy. +- **Storage**: Global (default) shares one directory; Workspace keeps each workspace's own `.mnemon`; Centralized keeps each workspace under one root. **Data directory** is Default or Custom. Changing either never moves existing data. +- **Interface**: open the Memory System in the sidebar or as a conversation tab, and turn the conversation controls on or off. + +Switches and selectors apply at once; storage changes wait for **Apply**. The [UI guide](./ui-guide.md#on-the-plugins-page) covers every page, and [Configuration](../reference/configuration.md) lists the settings behind them. + +## 6. Next steps + +- Learn every page in the [UI guide](./ui-guide.md). +- Decide what belongs in runtime memory, documents or memory spaces with the [storage model](../reference/storage-model.md). +- Export your first ZIP backup and prepare for upgrades with the [operations guide](./operations.md). +- Connect a long-term backend with the [Provider guide](./memory-providers.md). diff --git a/dsh/docs/en/guides/installation.md b/dsh/docs/en/guides/installation.md new file mode 100644 index 000000000..bb7c07b51 --- /dev/null +++ b/dsh/docs/en/guides/installation.md @@ -0,0 +1,258 @@ +# Install and start + +[简体中文](../../zh-CN/guides/installation.md) | **English** | [Documentation hub](../README.md) + +This page is for your first time: it starts from a computer that has never run DeepSeek Harness (DSH), installs dsh-mnemon, and ends with a conversation that uses your first memory. It takes about ten minutes. Already using DSH? Skip to [step 3](#3-install-and-enable-dsh-mnemon). + +![Add dsh-mnemon on the Plugins page: type the package name, install, Enable now, and the Memory System appears in the sidebar](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/en/install.gif) + +The screenshots and recordings come from real DSH 0.2.0-rc.1 and dsh-mnemon 0.5.19; the [installation gallery](../../assets/install-v0.5.19/README.md) describes how they were captured. + +## Choose how you use DSH + +| Way | Suits | Where to install dsh-mnemon | +|---|---|---| +| **Web (recommended)** | Using DSH in a browser; the easiest start | **Plugins → Add plugin** in the web sidebar | +| **Desktop app** | You already have the DeepSeek Harness desktop app | **Plugins → Add plugin** in the app's sidebar; see [Desktop app](#6-desktop-app) | +| **Command line and Headless** | Servers, scripts and one-shot jobs | `dsh plugin --profile add dsh-mnemon`; see [Command line and Headless](#7-command-line-and-headless) | + +All three install the same dsh-mnemon. By default your memory lives in `~/.mnemon` on the computer, and the web page and the desktop app share it. + +## 1. Get Node.js, pnpm and the Mnemon CLI + +| Tool | What it is for | Needed | +|---|---|---| +| [Node.js](https://nodejs.org/) 22.19 or later (22 or 24 LTS recommended) | Runs DSH | Yes | +| pnpm | DSH calls it to install plugins; without it the Plugins page says pnpm was not found | Yes | +| Mnemon CLI | Mnemon Native, the default local storage for Memory Spaces, needs it; runtime memory and Project Documents do not | Recommended; you can add it later | + +With Node.js installed, run these in a terminal (PowerShell on Windows): + +```sh +node --version +npm install --global pnpm +npm install --global @mnemon-dev/mnemon@latest +mnemon --version +``` + +Both `--version` commands should print a version. Homebrew, Go and a manual Windows installation are covered in [Other ways to install the Mnemon CLI](#other-ways-to-install-the-mnemon-cli). + +## 2. Start DSH + +```sh +npx @deepseek-ai/dsh web +``` + +DSH starts at `http://127.0.0.1:3080` and opens a one-time link in your browser; the terminal prints the same link. Keep the terminal open while you use DSH, and press `Ctrl+C` to stop it. + +- This runs the DSH release tagged `latest` on npm (0.1.7-rc.2 at the time of writing). For the DSH 0.2 preview, run `npx @deepseek-ai/dsh@next web` instead. dsh-mnemon supports both from 0.5.19. +- If you use DSH often, install it globally with `npm install --global @deepseek-ai/dsh` and then run `dsh web`; the [command line](#7-command-line-and-headless) needs the global `dsh` too. + +The first time you open it: + +1. DSH 0.2 shows a **Preview Notice** first. Click **Continue**. +2. In **Add an API key to get started**, paste an API key you created on the [DeepSeek Platform](https://platform.deepseek.com/) and click **Save and continue**. You can also click **Configure later** and add it in **Settings** afterwards; installing plugins needs no key, conversations do. + +

DSH's first-run dialog Add an API key to get started, with Configure later and Save and continue

+ +## 3. Install and enable dsh-mnemon + +1. Click **Plugins** in the sidebar, then **Add plugin** in the top right corner. +2. Type `dsh-mnemon` and click **Install**. DSH picks the **Registry** on the right of the dialog: usually the **Official npm registry**, or the **Mainland China mirror** in mainland China. You rarely need to change it. +3. After a few seconds the dialog says **Installed** with the version. Click **Enable now**; DSH does not need a restart. +4. **Memory System** appears in the sidebar. That is it. + +| Type the package name and install | Installed; enable it now | +|---|---| +| ![The Add plugin dialog with dsh-mnemon typed and the Official npm registry chosen](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/en/install-04-add.jpg) | ![The Installed dialog shows dsh-mnemon version 0.5.19 and Enable now](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/en/install-05-installed.jpg) | + +If you prefer the command line, you can install with the command below and then start DSH as in step 2; if DSH is already running, restart it after installing: + +```sh +npx @deepseek-ai/dsh plugin --profile web add dsh-mnemon +``` + +## 4. Check the installation + +Click **Memory System**. It opens on **Status**: + +- the header says **Connected · Layered strategy**; +- the **Memory engine** card shows `dsh-mnemon 0.5.19` or later; +- with the Mnemon CLI installed, Mnemon Native under **Memory Providers** reads "Service ready" with the CLI version. Without it, it reads "Mnemon CLI not found", and runtime memory and Project Documents work as usual. + +![The Memory System's Status page: dsh-mnemon 0.5.19, the three memory layers, and Mnemon Native showing Mnemon 0.2.7](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/en/install-07-status.jpg) + +## 5. Save your first memory + +1. Click **New Session** and type something you want remembered, for example: `Remember: I own checkout performance on the Lumen project, and I prefer short answers.` +2. Under the reply, **Turn memory · wrote** appears; expand it to see the runtime memory this turn wrote. +3. Click an entry: the Memory System opens **Runtime memory** with that entry highlighted. Every later turn carries it. + +![Typing a sentence to remember; under the reply Turn memory lists what it wrote, and an entry opens in Runtime memory](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/en/first-memory.gif) + +| What this turn wrote | Where it lives | +|---|---| +| ![The reply confirms, and the turn memory bar lists two runtime memory updates](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/en/first-01-reply.jpg) | ![Runtime memory highlights the entry that was written](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/en/first-02-runtime.jpg) | + +The model decides the wording and where to write it, so your text may differ. Continue with [Getting started](./getting-started.md): Project Documents, Memory Spaces, and saving a reply with **Save to memory**. + +## 6. Desktop app + +The **Plugins** page of the DeepSeek Harness desktop app works like the web page: **Plugins → Add plugin → type `dsh-mnemon` → Install → Enable now**. + +- The desktop app uses its own `desktop` profile. From DSH 0.2 the command line no longer manages that profile, so install and manage plugins on the app's Plugins page. +- Desktop windows load from the app's own `dsh-app://app/` address. dsh-mnemon 0.5.18 and earlier treated them as remote pages, which made the Memory System and the plugin settings read only ([#310](https://github.com/omdsh-dev/dsh-mnemon/issues/310)). 0.5.19 fixes this; update dsh-mnemon, with no configuration change. +- DSH's Plugins page cannot update an installed plugin. Update dsh-mnemon from **Memory System → Status → Check versions → Update**, which installs the new version with the app's own installer. dsh-mnemon 0.5.21 and earlier cannot do this in the desktop app; update from them once by removing dsh-mnemon on the Plugins page and adding it again. Your memory data stays. +- After updating dsh-mnemon in the desktop app, quit the app completely and open it again (`Cmd+Q` on macOS) so the new version loads. If an error appears after an update, see [Common problems](#common-problems). + +## 7. Command line and Headless + +With DSH installed globally, you can manage each profile's plugins from the command line: + +```sh +npm install --global @deepseek-ai/dsh +dsh plugin --profile web add dsh-mnemon +dsh web +``` + +Profiles keep separate plugin lists. To give one-shot jobs memory too, install it into the Headless profile as well: + +```sh +dsh plugin --profile headless add dsh-mnemon +dsh --profile headless "Check persistent project context before answering." +``` + +Upgrade and uninstall: + +```sh +dsh plugin --profile web update dsh-mnemon +dsh plugin --profile web remove dsh-mnemon +``` + +Restart DSH after upgrading. For 24 hours after a release, pnpm 11 keeps `update` on the installed version; add the new version by name instead, for example `dsh plugin --profile web add dsh-mnemon@0.5.20`. Uninstalling removes the plugin, never your memory data. Development checkouts, cloud access and Headless details are in [Getting started](./getting-started.md#1-install-and-upgrade-from-the-command-line). + +## Other ways to install the Mnemon CLI + +Only Mnemon Native uses the Mnemon CLI. Install it on the machine that runs DSH. The npm command from step 1 is recommended on macOS, Linux and Windows (Node.js 22+); update it later with `mnemon update`, or use **Status → Check versions** when the page recognizes the owning npm installation. If migrating from Homebrew, Go, or a downloaded binary, put npm's global bin directory before the old command on PATH and update any `MNEMON_CLI_PATH` / `mnemon.cliPath` override. Restart DSH after changing its environment, then recheck the executable path on Status. + +Homebrew Cask remains an alternative on macOS: + +```sh +brew install --cask mnemon-dev/tap/mnemon +``` + +Go works on macOS and Linux: + +```sh +go install github.com/mnemon-dev/mnemon@latest +``` + +For a manual installation on Windows, the official release provides ZIP archives for AMD64 and ARM64. The following PowerShell installs v0.2.9 under the auto-discovered per-user Programs directory and verifies it against the published checksum: + +```powershell +$version = '0.2.9' +$arch = if ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture -eq 'Arm64') { 'arm64' } else { 'amd64' } +$archiveName = "mnemon_${version}_windows_${arch}.zip" +$releaseBase = "https://github.com/mnemon-dev/mnemon/releases/download/v${version}" +$archive = Join-Path $env:TEMP $archiveName +$checksumFile = Join-Path $env:TEMP "mnemon_${version}_checksums.txt" +Invoke-WebRequest "${releaseBase}/${archiveName}" -OutFile $archive +Invoke-WebRequest "${releaseBase}/checksums.txt" -OutFile $checksumFile +$line = Get-Content $checksumFile | Where-Object { $_.EndsWith(" $archiveName") } | Select-Object -First 1 +if (-not $line) { throw "Checksum entry not found for $archiveName" } +$expected = (($line -split '\s+')[0]).ToLowerInvariant() +$actual = (Get-FileHash -Path $archive -Algorithm SHA256).Hash.ToLowerInvariant() +if ($actual -ne $expected) { throw "Checksum mismatch for $archiveName" } +$installDir = Join-Path $env:LOCALAPPDATA 'Programs\mnemon' +New-Item -ItemType Directory -Force -Path $installDir | Out-Null +Expand-Archive -Path $archive -DestinationPath $installDir -Force +$mnemon = Join-Path $installDir 'mnemon.exe' +& $mnemon --version +``` + +Go remains an alternative on Windows when a Go toolchain is already available: + +```powershell +go install github.com/mnemon-dev/mnemon@latest +$mnemonBin = go env GOBIN +if (-not $mnemonBin) { + $mnemonBin = Join-Path (((go env GOPATH) -split ';')[0]) 'bin' +} +$mnemon = Join-Path $mnemonBin 'mnemon.exe' +& $mnemon --version +``` + +On Windows, dsh-mnemon discovers native `mnemon.exe` from `PATH`, an exported `GOBIN` or `GOPATH`, the default `%USERPROFILE%\go\bin`, `%LOCALAPPDATA%\Programs\mnemon`, and Program Files. The official npm `mnemon.cmd` launcher is also supported: dsh-mnemon validates its package and invokes its JavaScript entry with Node, without a shell. Other `.cmd` and `.bat` wrappers remain unsupported. + +When DSH runs inside an Electron desktop main process, verified npm launchers run with `ELECTRON_RUN_AS_NODE=1` in the child process. This covers memory commands, version checks, and npm updates, while preserving saved embedding settings. The desktop application's own environment is unchanged. If the shell disables Electron's `runAsNode` fuse, point `mnemon.cliPath` at the platform's native Mnemon binary instead; see [Troubleshooting](./operations.md#troubleshooting). + +If DSH still cannot find the binary, set `MNEMON_CLI_PATH`, or set `mnemon.cliPath` as a user setting rather than replacing the plugin's profile patch (see [Configuration](../reference/configuration.md)): + +```yaml +mnemon: + cliPath: 'C:\Users\alice\AppData\Local\Programs\mnemon\mnemon.exe' +``` + +`mnemon status` opens the effective Store and may initialize data or run upstream migrations, so it is not a side-effect-free installation probe. + +## Common problems + +### Installing says "dsh-mnemon@… is incompatible with DSH 0.2.0-rc.1" + +![The plugin could not be installed: dsh-mnemon@0.5.18 is incompatible with DSH 0.2.0-rc.1, with Edit and Retry](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/en/trouble-incompatible.jpg) + +Before DSH installs a plugin, and each time it starts, it checks which DSH versions the plugin declares support for. dsh-mnemon releases before 0.5.19 declare DSH 0.1.7 only, so DSH 0.2 refuses them. + +- Check that npm has 0.5.19 or later: `npm view dsh-mnemon version`. +- For 24 hours after a release, pnpm does not pick the new version by default and installs an earlier one instead, which leads to this message. After two releases within a day it falls back further, for example to 0.5.17. DSH 0.1.7 accepts such an earlier release without a message, so check the version on Status. Click **Edit**, type the version as well, for example `dsh-mnemon@0.5.24` (use the version `npm view` shows), and click **Install**; on the command line, run `dsh plugin --profile web add dsh-mnemon@0.5.24`. Or retry after 24 hours. +- A versioned install pins the profile to that version. To upgrade later, run `dsh plugin --profile web update --latest dsh-mnemon` once the next release is a day old; within that day, add the new version by name the same way. +- Do not accept the risk for an older release with `allow-version` or similar: it really has not been verified on DSH 0.2. + +### The Plugins page says pnpm was not found + +Run `npm install --global pnpm`, then start DSH again from a new terminal so that DSH finds pnpm on PATH. On the command line the message is `pnpm was not found; install pnpm and make it available on PATH.` + +### The registry cannot be reached + +Click **Registry** on the right of the dialog, switch between the Official npm registry and the Mainland China mirror, and retry; on a company network you can enter a custom address. + +### Status says "Mnemon CLI not found" + +Install the CLI as in [step 1](#1-get-nodejs-pnpm-and-the-mnemon-cli) and restart DSH. DSH looks for `mnemon` on PATH and in common install directories; if it still cannot find it, set `mnemon.cliPath` as described in [Other ways to install the Mnemon CLI](#other-ways-to-install-the-mnemon-cli). + +### The Memory System is read only when opened from another device + +When DSH is opened from another device (for example through an address set up with `--trusted-host`), the Memory System is read only by default and says why. If you do need remote management, set `remoteAccess: trusted-host` and restart DSH as the [operations guide](./operations.md#remote-management) describes. Desktop app windows and a browser on the same computer are not remote pages. + +### ERR_PACKAGE_PATH_NOT_EXPORTED after an update + +If dsh-mnemon was updated while DSH was running (for example on the desktop app's Plugins page), enabling a component can then show: + +```text +Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './starter' is not defined by "exports" in …/node_modules/dsh-mnemon/package.json +``` + +The new version is installed, but the running DSH still loads it with the old version's package information, which lacks an entry point the new version added: `./starter` after an update from 0.5.17 or earlier, `./bundle` after an update from 0.5.18 or 0.5.19. After an update from 0.5.18 or 0.5.19 the message can instead read `mnemon-bundle (dsh-mnemon/bundle): pending (waiting for service: mnemonStarterReady)`, because the running DSH keeps the old component group. Either way, quit DSH completely and start it again: `Cmd+Q` for the desktop app on macOS, or `Ctrl+C` and then `dsh web` again on the command line. + +On 0.5.18 or 0.5.19, do not turn off `dsh-mnemon/starter` to make the message go away; that leads to the next problem. + +### DSH says "waiting for service: mnemonStarterReady" + +![The dsh-mnemon page under Plugins: Components 11 total, 11 off, with dsh-mnemon/starter first and Off; the sidebar has no Memory System](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/en/trouble-starter-off.jpg) + +DSH prints this in the terminal when it starts, or shows it when you enable the plugin: + +```text +dsh: warning: 1 entry did not activate +mnemon-bundle (cordis:group): pending (waiting for service: mnemonStarterReady) +``` + +The sidebar has no **Memory System**, and every dsh-mnemon component on the Plugins page reads Off. `dsh-mnemon/starter` has been turned off: in 0.5.18 and 0.5.19 this separate row prepares dependency resolution before the memory components load, and the others wait for it. + +- Open **Plugins → dsh-mnemon** and turn on the `dsh-mnemon/starter` row. The Memory System appears right away; if it does not, restart DSH. +- The desktop app treats this as a failed start and shows its plugin recovery page. Click **Remove this plugin and continue** (your memory data stays), and once the app is up, add dsh-mnemon again as in [step 3](#3-install-and-enable-dsh-mnemon). Reinstalling 0.5.18 or 0.5.19 keeps the switch off, because the profile saves it, so then turn it on as above. +- From 0.5.20 there is no separate switch: the group prepares dependency resolution itself, and a leftover setting for it is ignored. If the message names `dsh-mnemon/bundle`, DSH was updated to 0.5.20 without a restart and still runs the old group; quit DSH completely and start it again. + +### Moving DSH to 0.2 + +First run `dsh plugin --profile web update dsh-mnemon` on your current DSH to get dsh-mnemon 0.5.19 or later, then upgrade DSH. Otherwise DSH 0.2 turns the incompatible older release off when it starts; your memory is untouched and comes back once the plugin is updated. See [Compatibility and upgrades](../reference/compatibility.md) for details. diff --git a/dsh/docs/en/guides/memory-providers.md b/dsh/docs/en/guides/memory-providers.md new file mode 100644 index 000000000..c3cf7ef04 --- /dev/null +++ b/dsh/docs/en/guides/memory-providers.md @@ -0,0 +1,74 @@ +# Long-term memory providers + +**English** | [简体中文](../../zh-CN/guides/memory-providers.md) | [Documentation](../README.md) + +Memory Spaces hold dsh-mnemon's long-term memory, and their backend is a Provider you can swap. The Memory Space contract stays stable while the Provider supplies the data plane. **Mnemon Native is the official, prioritized default**; external Providers are opt-in integrations for teams that already use another memory engine or need a different sharing, extraction or retrieval model. + +Only Mnemon Native needs the Mnemon CLI. When the CLI is missing, another ready Provider can serve Memory Spaces, and a saved Provider choice is still honored. + +![Memory Spaces' page under Plugins with Mnemon Native and the third-party Providers](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/plugin-spaces.jpg) + +Each adapter is an independently published `dsh-mnemon-provider-*` package, installed as a child of the Memory Spaces Source. The Starter includes all nine packages, but external services remain disabled until configured. No external backend server or CLI is bundled. See the [official package list](../../../README.md#official-plugins) and [Provider author contract](../development/extensions.md). + +## Provider matrix + +| Provider | Data plane | Recall / browse | Graph / related | Write | Forget | +|---|---|---|---|---|---| +| **Mnemon Native** | Local `mnemon.db` through the official CLI | Yes / yes | Full typed graph / yes | Exact | Soft delete | +| **OpenViking** | Existing HTTP service and `viking://` memory root | Yes / yes | Projected nodes / no | Verified exact content | Guarded hard delete for exact user `.md` resources | +| **Honcho** | v3 workspace conclusions | Yes / yes | No / no | Exact peer conclusion | Hard delete | +| **Mem0** | Platform v3 or self-hosted HTTP API | Yes / yes | No / no | Async extraction | Hard delete | +| **Hindsight** | Memory bank API and knowledge graph | Yes / yes | Provider graph / yes | Async retain | Invalidate (soft) | +| **Holographic** | Local atomic structured-fact file | Yes / yes | Entity/semantic graph / yes | Exact fact | Hard delete | +| **RetainDB** | Project/user-scoped HTTP API | Yes / yes | No / no | Exact memory | Hard delete | +| **ByteRover** | Local `brv` CLI and knowledge directory | Yes / no | No / no | Async curate | Unsupported | +| **Supermemory** | Container-scoped HTTP API | Yes / yes | Projected nodes / no | Async document ingest | Provider forget | + +The Host exposes only capabilities an adapter can honor. UI actions and Agent tools do not fabricate missing graph, related, link, browse, or deletion behavior. + +## Service and Memory Space fields + +| Provider | Workspace behavior | Service configuration on Memory Spaces' page | Instance configuration in Memory Spaces | +|---|---|---|---| +| OpenViking | Keeps the provider-global scope | `endpoint`, `apiKey`, `account`, optional `discoveryUser` | `targetUri`, `user`, `actorPeerId` | +| Honcho | Keeps the provider-global scope | `endpoint`, `apiKey` | `workspace`, `userId`, `agentId` | +| Mem0 | Keeps the provider-global scope | `endpoint`, `apiKey`, `mode` | `userId`, `agentId`, `rerank` | +| Hindsight | Keeps the provider-global scope | `endpoint`, `apiKey` | `bankId`, `budget` | +| Holographic | Follows by default; path can override | `dataPath` | `defaultTrust`, `minTrust` | +| RetainDB | Keeps the provider-global scope | `endpoint`, `apiKey` | `project`, `userId` | +| ByteRover | Follows by default; directory can override | `cliPath`, `apiKey`, `defaultDirectory` | `workingDirectory` | +| Supermemory | Keeps the provider-global scope | `endpoint`, `apiKey` | `containerTag`, `searchMode` | + +**Memory providers**, on Memory Spaces' page (open **Plugins → dsh-mnemon** and select Memory Spaces), owns reusable provider service configuration. Enabling or saving a provider performs authoritative discovery and synchronizes every visible provider-native namespace into the Memory Space directory—for example banks, projects, workspaces, users, or container tags. Provider titles and descriptions become the local routing metadata. **Memory Spaces → Overview** controls DSH activation and shows the synchronized instance scope. The Host merges both layers immediately before calling an adapter. Secrets stay in `/state/memory-providers.json` with mode `0600`; the WebUI represents configured secrets only as a mask, and entering a new value replaces the saved secret. + +DSH workspace mode does not rewrite every provider namespace. Mnemon Native follows the workspace automatically. Holographic and ByteRover default to workspace-local paths but allow explicit path overrides. Remote providers continue to use the URI, workspace, user, bank, project, or container configured on the Memory Space; switching DSH workspaces never rewrites those identities implicitly. + +## Manual and smart placement + +Manual placement preserves the existing workflow: create a Memory Space, choose one engine, configure it, and continue to use the same Recall, Content, Entities, and Remember surfaces. + +Smart placement builds an allowlist from the candidates selected by the user: + +1. The Host enforces data boundary and required-capability rules. +2. If one eligible provider remains, rules select it deterministically. +3. If several remain, an independent task Agent considers the routing description, soft preference, and user-authored strategy prompt. +4. The Host validates the returned provider against the eligible set and persists the decision, reason, confidence, and candidate IDs. + +Connection secrets never enter the selector prompt. `local-only` excludes every remote provider before model selection. Mnemon Native stays a local candidate while its CLI is available. + +## Operational boundaries + +- OpenViking writes a new `.md` file with `content/write`, `mode: "create"`, `wait: true`, and source/category tags. Categories map to `preferences`, `experiences` (insight/decision), `events` (context), or `entities` (fact/general). A stored receipt requires matching URI, byte count, completed vector indexing and exact public full-content readback; semantic processing may be `skipped` for memory files. Recall and browse read full content rather than treating summaries as the stored original. Session extraction and LLM curation are no longer used for these writes. +- OpenViking requires a server supporting that content API; the published v0.4.20 backend is covered by the optional integration test. By default, discovery uses the Admin API; configure `account` and a key allowed to enumerate its users. For a user key without admin access, set **User key owner (skip admin)** (`discoveryUser`) alongside `endpoint`, `apiKey`, and `account`. This explicitly selects `viking://user//memories`, validates it with read-only `GET /api/v1/fs/ls`, and synchronizes one space. It omits account/user identity headers because the server resolves them from the key; `account` names the local projection and cannot override the key's tenant. Obtain these identifiers from the service administrator. Missing roots, denied access and invalid responses reject the save without replacing existing configuration. No health-only or automatic admin-error fallback is used. Cloud service availability and write permission require separate validation; no live cloud account is certified by the deterministic tests. See upstream [authentication](https://github.com/volcengine/OpenViking/blob/main/docs/en/guides/04-authentication.md) and [managed-service quick start](https://github.com/volcengine/OpenViking/blob/main/docs/en/getting-started/02-quickstart.md). +- Existing explicit `viking://user//memories` roots remain valid. Legacy `viking://user/memories` resolves using the configured `user`, or the authenticated system-status identity when omitted. User-key discovery uses its explicit owner and rejects mismatched memory owners. Unsafe path components and deletion outside the selected user root are rejected. The registry format is unchanged; before downgrading, clear `discoveryUser` with the current version and use an admin-capable setup, or disable the service and restore a compatible configuration. Remote memories remain untouched. +- OpenViking errors, incomplete indexing and timeouts never produce a committed receipt. A write may already exist remotely; its error includes the requested URI for inspection before retrying. There is no automatic extraction fallback or retry. A verified receipt carries the exact file `id`, allowing the Host to compensate a later failed local archive by deleting only its new index. Unknown remote write outcomes remain for inspection. Upgrading or rolling back the adapter does not delete existing remote files; a downgrade restores the older write behavior. +- The WebUI never calls external services or local CLIs directly. Provider I/O stays in the Host with cancellation, timeouts, bounded process output, and shell-disabled argument arrays. +- Disabling a provider removes all of its local Memory Space mappings, activation state, and mapped title/description metadata. Re-enabling discovers them again from the provider. Reconciliation never deletes provider-owned data; per-memory Forget remains a separate capability-controlled action. +- Holographic is a TypeScript adaptation of local structured-fact semantics, using an atomic JSON store and an independent data format and lifecycle implementation. +- Hindsight uses a lightweight liveness probe and reads real statistics, entities, and relationships from the provider's bank stats, entity catalog, and graph responses. Recall and graph remain usable against older deployments that lack the newer statistics surfaces. +- ByteRover exposes focused `status`, `query`, and `curate` operations. Broad knowledge-tree browsing and deletion are intentionally not invented. +- Supermemory browse results merge extracted memory entries with still-browseable ingested documents and deduplicate by provider ID, so documents do not disappear from Content while extraction is incomplete. +- Mnemon Packs include Mnemon Native Memory Spaces, Runtime, and Documents. External connections, credentials, local third-party stores, and remote provider data are excluded. +- Availability, pricing, privacy, retention, and licensing of external products are governed by their respective operators. Review those boundaries before sending private memory to a remote provider. + +See [Third-party notices](../../../THIRD_PARTY_NOTICES.md) for source attribution and licensing boundaries. diff --git a/dsh/docs/en/guides/operations.md b/dsh/docs/en/guides/operations.md new file mode 100644 index 000000000..7dac9ec28 --- /dev/null +++ b/dsh/docs/en/guides/operations.md @@ -0,0 +1,326 @@ +# Operations, security and troubleshooting + +[简体中文](../../zh-CN/guides/operations.md) | **English** | [Documentation hub](../README.md) + +## Health checks + +Open **Status** in the Memory System. If you use Mnemon Native, also check its binary; the other Providers do not need it: + +```sh +command -v mnemon +mnemon --version +``` + +Windows PowerShell: + +```powershell +Get-Command mnemon -ErrorAction SilentlyContinue +Test-Path "$env:LOCALAPPDATA\Programs\mnemon\mnemon.exe" +``` + +```text +/mnemon status +``` + +![Status with each memory component, the Providers and the storage areas](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory-status.jpg) + +Status shows the dsh-mnemon and Mnemon versions, one card per memory component, the Providers and the effective directories. `mnemon status` opens the effective Store and may initialize data or run upstream migrations, so it is not a completely side-effect-free probe. + +If OpenViking reports `/api/v1/admin/*` access restrictions, configure **User key owner (skip admin)** (`discoveryUser`), the account identifier, endpoint and user API key on Memory Spaces' page under **Plugins → dsh-mnemon**. This verifies access to the selected memory root using the data API; a denied or missing root keeps the previous configuration. It does not prove write permission or change the identity bound to the key. Leave the field empty for admin discovery. See [Provider boundaries and downgrade steps](./memory-providers.md#operational-boundaries). + +## Version checks and updates + +**Check versions** on Status opens the version panel: + +- **dsh-mnemon**: installed is the version the owning Profile records (Status shows the version that runs); updates from npm `latest`. An installed beta/alpha/rc also checks its own channel and can graduate to a newer stable version. Stable users never opt into prereleases automatically. +- **Mnemon CLI**: installed from `mnemon --version`; latest from the official `@mnemon-dev/mnemon` npm package. Only Mnemon Native needs it, so a missing CLI shows as optional with its install command. + +Checking is read-only and never installs automatically. Update appears only when a newer version exists and the source is safely recognized. Mnemon supports the official npm launcher, Homebrew Cask / Formula, and `go install`; dsh-mnemon supports npm installations in the owning DSH Profile. `link:` / `file:` development builds and unrecognized manual installs show guidance only. + +dsh-mnemon updates through DSH's own plugin installer, as `dsh plugin add dsh-mnemon@` would, and so does an optional Strategy the Profile added on its own. The installer uses the Profile's package manager, which in the desktop app is the app's bundled pnpm, so no pnpm is needed on PATH. It follows the Profile's registry settings and fallbacks and waits for the Profile's lock. Before keeping the new version, DSH checks the DSH versions it supports. A failed install restores the Profile's `package.json` and lockfile, the dialog shows pnpm's error line with DSH's log path, and the enabled plugins stay as they were; files the install downloaded can stay, and Check versions keeps showing the version the Profile records. Through DSH's installer the Host updates only the Profile it runs in. Where DSH offers no plugin installer, the Host runs pnpm from PATH in the owning Profile. With neither the app's pnpm nor one on PATH, the dialog asks you to install pnpm and restart DSH. dsh-mnemon 0.5.21 and earlier only know the pnpm route: in the desktop app, update from those versions once by removing dsh-mnemon on the Plugins page and adding it again, which keeps your memory data. + +npm updates require the active launcher to belong to the global root reported by the current npm. A different Node/npm installation or a broken launcher shows repair guidance instead. Use `npm install --global @mnemon-dev/mnemon@latest` to install or migrate, then `mnemon update` for later updates. The commands run on the DSH Host and need Node.js 22+. After changing PATH or a CLI override, recheck the executable path shown in the dialog. + +On Windows desktop Hosts, memory operations and version checks use the native binary pinned by a recognized official Mnemon npm installation directly. This avoids the npm launcher's additional console window while retaining hidden subprocesses, timeouts, cancellation, and saved embedding settings. The displayed installation path and npm update ownership still refer to the original launcher. Unknown package layouts or missing native dependencies retain the launcher fallback and its repair diagnostics. + +Expand dsh-mnemon's subpackage list to inspect Sources, Strategies, and Providers. Starter pins update with the Starter. Only independently installed packages in the owning Profile can update individually; source links are preserved. Package writes are serialized, and restart reminders survive subsequent checks and reopening the dialog. Read-only connections retain checks and command copying; page updates require management authority and `writeEnabled`. + +Go updates additionally require the active executable to resolve to the current Go installation output (`GOBIN`, or the first `GOPATH` entry's `bin` directory), with no cross-compilation target. A downloaded binary is not a Go-managed installation merely because it contains Go build metadata. CLI updates must verify that the active executable actually reaches the checked release before reporting success. + +The Host fixes update commands and arguments. The browser cannot supply either; shell is disabled and execution/output are bounded. A plugin update installs the exact checked version in its owning profile and verifies the installed package before reporting success; pinned beta versions cannot silently remain on an older release. After an update, the UI rechecks both components and refreshes Status automatically. Mnemon applies on the next CLI call. dsh-mnemon loads once DSH restarts: stop and rerun `dsh web`, or quit the desktop app completely (`Cmd+Q` on macOS) and reopen it. Until then the Host keeps running the previous version, while DSH loads the new Memory System page as soon as the files change; that page reopens **Check versions** with the update and the restart it needs. Until DSH restarts, a notice above every Memory System page names the installed version and the one that still runs, whether the update came from Check versions or `dsh plugin`; packages updated on their own are named too. Status shows the running version, and **Check versions** the installed one, so it does not offer that update again. + + + +## Legacy Session recovery + +DSH `0.1.7-rc.2` and `0.2.0-rc.2` are the supported hosts; see the [compatibility matrix](../reference/compatibility.md). Restart the Web Profile after upgrading Mnemon: the Starter patch gives the owning `connection` entry both `webRuntime` and `webServer`. This restores all seven Mnemon RPC channels when **Memory System** or its configuration page previously returned HTTP 405. Custom profiles that install the Host without the Starter must apply the same dependency declaration to their connection entry, preserving any additional dependencies in their own composition. No DSH package source is changed; browser authentication and Mnemon grants still apply. + +The separate error `source summary requires notice form; source v0 artifact remains unchanged` comes from older Mnemon messages in DSH Session logs. New recall/instruction messages omit that invalid summary. Updating the plugin does not rewrite an existing Session. To repair one affected log: + +1. Stop the owning DSH process. Back up its entire Session storage root, including every generation, separately from a Mnemon Pack. Use the exact `raw log` path reported by DSH; do not search or rewrite unrelated sessions. +2. With the updated Mnemon package installed, preview the command below against a **backup copy**. Use Node `22.19+` or `24+` for `.jsonl.zstd`; plain `.jsonl` also works on Node 20. +3. Write to a new path outside the Session directory. Review `repairedMessages`, `repairedDescriptors`, `expandedToolChunkRows`, `expandedToolChunks`, the SHA-256 report and `blockers`. Preview counts describe candidate changes. Known unsupported shapes cause exit status 1; a requested copy has `mode: "refused"` and no output is published. Existing output files are rejected. +4. In a disposable copy of the Profile, replace only the affected `session.jsonl` or `session.jsonl.zstd` with the repaired copy, retaining the original backup. Let DSH load and resume it, then restart and verify the conversation. Repeat that explicit replacement in the stopped original Profile only after validating the copy. The command itself never replaces the input or a live Session. + +```sh +dsh-mnemon-repair-session --input /backup/session.jsonl.zstd +dsh-mnemon-repair-session --input /backup/session.jsonl.zstd --output /backup/repaired-session.jsonl.zstd +``` + +The executable is installed with `dsh-mnemon`; a Profile-local installation can use `pnpm exec dsh-mnemon-repair-session` from that Profile. In a checkout use `node bin/repair-legacy-session.mjs`. The two stream normalizations below preserve IDs themselves; a proven identity recovery can also apply to the same log. The copy repair supports these audited historical shapes: + +| Historical shape | Copy behavior | +|---|---| +| `dsh-mnemon` instructions summary `Optional memory recall and remember reminder`; recall summary `Memory View snapshot` or `Runtime memory snapshot` | Remove only that source's `summary` member. Other plugin messages and unfamiliar summaries remain untouched. | +| Exact historical subagent descriptor v2 with a composition accepted by the frozen v3 contract | Change only its version value to 3. Keep provider, model pair, persona, label and tool filters intact; add no defaults or reasoning effort. Extra fields, unpaired models and unsupported versions are refused. | +| Exact packed `tool-call-chunks` with string `id: ""` or `name: ""` | Expand to the original raw delta events, preserving IDs, name presence, argument strings, sequence numbers and timestamps. No chunk is dropped and provenance references need no renumbering. Already raw empty-string deltas remain byte-identical. | +| Exact raw or packed tool deltas with an own `name: null` member | Keep the member and change its value to `""`; expand packed rows with the same logical sequence and timing. IDs, arguments and durable messages remain intact. Both values leave assembly unchanged, and keeping the member preserves token timing. `normalizedToolChunkNames` counts affected logical deltas. | +| A completed `deepseek-official` stream recorded a unique nonempty provider ID before empty continuation IDs corrupted its durable tool chain | Restore that existing ID only after checking exact ordered chunk provenance, indexed block assembly, matching name/arguments and a unique call/result association. Preserve placeholders before the first candidate, message UUIDs, payloads and other plugin content. Independently proven multiple calls and text/reasoning blocks are supported. No ID is generated. | +| No recorded ID, conflicting identities, interruptions, unknown affected owner shapes or unaccounted approval/dispatch/plugin/copy references | Report blockers and refuse output. Arbitrary empty-ID histories and owner replay state need recovery by their original writer. | + +The ID check uses the original stream's complete `sourceEventSeqs`, including packed logical members. Result provenance must select its exact call; if absent, only a unique single-call step with no other attempt qualifies. An explicit wrong reference never uses that fallback. Extra references through sequence numbers or message IDs are checked across step boundaries; ordinary compaction sequence references and unrelated plugins can remain unchanged. `recoveredToolIdentityChains` counts restored chains, `recoveredToolIdentityFields` counts logical identity slots, and `toolIdentityRecoveryRefusals` explains unsupported proofs with bounded location samples. + +The tool accepts only v0, valid UTF-8 JSON records and complete ordinary Zstandard frames, with a 128 MiB limit on stored input, decoded input and expanded output. Malformed input, incomplete frames, ambiguous duplicate keys on transformed or identity-proof paths, and unsafe delta coordinates are refused without publishing output. Only the changed summary/version/name/identity members and expanded packed rows are rewritten; every other decoded byte is retained. Diagnostics count every known blocker and include at most ten line/event/path samples per class, without message bodies. `migrationValidated: false` is deliberate: this scanner does not recover unrelated corruption or guarantee that every Session can migrate. The installed official DSH loader and stream replay remain the final checks. See the [issue #251 contract audit and verification](../../pr-assets/issue-251-legacy-repair/README.md). + +DSH migrates old Sessions to immutable v3 generations when opening for write. Mnemon Runtime, Documents and Memory Spaces retain their existing formats. To roll DSH back, restore the pre-upgrade Session backup in a separate old-version Profile; do not open v3 generations with an older DSH. See [verification and screenshots](../../pr-assets/issue-223-dsh-015/README.md). + +## Backup and recovery + + + +### Recommended: ZIP from the plugin page + +**Storage → Backup and migration** on the `dsh-mnemon` page under **Plugins** operates on the **currently effective root**: + +- **Export ZIP** includes Runtime, Documents, and every Mnemon Native Memory Space. Third-party connections, local external stores, and remote data are excluded. +- **Import ZIP** previews, validates, then merges into the effective root. +- Packs include `manifest.json`, SHA-256 inventory, and component summaries. +- Export/import hold component locks; a Memory Space with an uncheckpointed WAL is rejected. +- Import checks paths, counts, compressed/expanded limits, JSON schemas, Document hashes, registry consistency, and SQLite headers. +- Merge is staged before replacing component directories; commit failure restores pre-import directories. + +The UI offers safe merge, not “overwrite everything”: + +- Runtime deduplicates by target and content. +- Identical Document ID + hash is skipped; conflicting content receives a new ID. +- Identical Memory Space ID + database is skipped; conflicting content receives a new ID. + +Import is governed by `writeEnabled` and is rejected in read-only deployments. A ZIP contains private memory—encrypt it, restrict access, and rehearse recovery. Provider credentials live in `state/memory-providers.json` with mode `0600`; they are excluded from ZIP. Saved credential values are not returned through management responses either. Protect the entire `state/` directory in the offline snapshot below if connections must be backed up. + +![A verified backup preview before Safe import](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/plugin-backup-preview.jpg) + +### Recovery rehearsal + +1. Under **Storage**, set **Data directory** to **Custom**, enter an isolated directory and apply. +2. Confirm the current read/write root on Status is that directory. +3. Select the backup under **Backup and migration**, review its preview, then import. +4. Check Runtime, Documents, Memory Spaces, and directories on Status. +5. Run one focused direct recall and read one Document. +6. Only after verification decide whether to switch a production scope. + +Never restore directly into the only production root without another backup. + +### Filesystem snapshot + +To preserve reserved `state` or take an offline complete snapshot, stop every DSH / Mnemon process using the root and copy: + +```text +/runtime +/documents +/data +/state # when present; outside the built-in Pack's three data components +``` + +Generate an inventory or checksums and rehearse recovery in isolation. A normal directory copy while writers are running is not a consistent snapshot. + +## Changing storage scope + +Saving `global` / `workspace` / `custom` / `workspaces` initializes a new runtime graph before switching atomically. The page reloads automatically, but **data is not migrated**: + +```text +old scope -- save --> new empty or existing root + +no automatic copy +no automatic merge +no automatic delete +``` + +Recommended migration: export from the old scope → switch and confirm the new root → import → verify. In Workspace mode, confirm both inspection and execution targets. + +With `workspaces`, back up the complete central directory for every workspace, or export a Pack for the selected workspace only. A renamed/moved workspace receives a new path hash; restoring its old data is an explicit operator action. + + +Existing turns and delegated child activations may still use the old runtime. Wait for them to finish or cancel them before moving or retiring its data. Parent completion alone does not release an asynchronous child's delegation; a newly created or cold-resumed activation captures its own authorized generation. + + + +## Cloud-hosted WebUI + +DSH 0.1.7-rc.2 and 0.2.0-rc.2 are the supported registry targets. It authenticates the page, every RPC, and every stream through an authority-bound browser session created from the launch-token URL printed by the Host. `--trusted-host` remains a Host/Origin fence; it does not replace HTTPS or deployment access controls. + +1. Terminate HTTPS at a reverse proxy or access gateway and protect the public entry for its intended users. Proxy the same-origin `/` and `/api` traffic, including streams, to `http://127.0.0.1:3080` while preserving the external `Host` authority. +2. Start the loopback service with the external authority. Use a bare `host[:port]`, not a URL: + + ```sh + dsh web --trusted-host memory.example.com --no-open + ``` + + For a non-default public port, use the exact authority, for example `memory.example.com:8443`. DSH deliberately rejects `--host 0.0.0.0`; keep the service on loopback and let the proxy or an SSH tunnel reach it. +3. For a browser that does not already have a valid cookie for this public authority, use the launch-token URL printed as `dsh web: ...`. With a reverse proxy, replace only the printed loopback origin with the public HTTPS origin and preserve the `/` path and `?token=...` query. For example, transform `http://127.0.0.1:3080/?token=...` into `https://memory.example.com/?token=...`. Treat that URL as a credential and do not put it in logs, tickets, or chat. DSH exchanges it for an HttpOnly, SameSite cookie and redirects to a clean `/`; a still-valid authority-bound cookie can survive a Host restart. +4. Open the clean external URL and verify that **Status** and the `dsh-mnemon` page under **Plugins** both load and a page reload remains authenticated. Remote settings are read-only by default. If management is intended, apply the [explicit remote management grant](#remote-management), restart DSH, then verify one deliberate small save. + +An HTTP 403 can indicate a Host/Origin mismatch or an old remote Client still using standalone channels: check `--trusted-host`, the public authority and proxy routing, then upgrade dsh-mnemon, restart DSH and reload the browser. Remote Mnemon calls use the authenticated API Gateway. An HTTP 401 requires restoring the Host's browser authentication or pairing. A response saying remote management requires `remoteAccess: trusted-host` is a separate Mnemon grant check; successful browser authentication alone does not authorize management. + +### Disable the complete Starter + +The legacy `mnemon` Entry remains the lifecycle switch for the complete Starter. To disable Mnemon without leaving its Source or Strategy Entries waiting on the missing Host service, add this profile patch and restart DSH: + +```yaml +- id: mnemon + disabled: true +``` + +This disables the Core/Host, all three bundled Sources, both main Strategies, and all three optional Strategy enhancements together. It does not remove installed packages or delete memory data. Remove the override, or change it to `false`, and restart DSH to enable the Starter again. + + + +### Remote management + +For authenticated remote clients, `remoteAccess: trusted-host` grants management operations; remote reads and narrow activation do not need it. Without it, a remote page's Memory System and plugin settings say why they are read only. A browser on the same computer and DSH desktop windows are not remote clients and do not need it. Configure management only for the intended authenticated users. To run an older DSH, keep the Mnemon release verified with it and its pre-upgrade Session backup; see the [compatibility matrix](../reference/compatibility.md). + +1. Open `~/.dsh/profiles/web/cordis.patch.yml`, or `$DSH_HOME/profiles/web/cordis.patch.yml` when `DSH_HOME` is set. Edit an existing top-level `- id: mnemon` entry instead of adding a duplicate. If the initialized file still ends in `[]`, replace that marker with the complete row below; otherwise append the row to the existing top-level YAML list: + + ```yaml + - id: mnemon + config: + routingGuidance: true + lifecycleEnabled: true + recallMode: guided + writebackMode: guided + idleReviewMs: 30000 + tabEnabled: true + writeEnabled: true + remoteAccess: trusted-host + timeoutMs: 10000 + defaultRecallLimit: 10 + embedding: + enabled: false + endpoint: http://localhost:11434 + model: nomic-embed-text + recallQuality: + policy: strict-v1 + lowScoreThreshold: 0.25 + highScoreThreshold: 0.6 + candidateMultiplier: 3 + maxMediumResults: 4 + maxUnknownResults: 2 + ``` + + A profile patch replaces the targeted row's complete `config` instead of deep-merging one field. Preserve existing customizations, and compare it with `dsh web --dump-default-config` after a plugin upgrade so new bundled defaults are not masked. +2. Inspect the effective tree with `dsh web --dump-config`. Confirm that the final `mnemon` row contains `remoteAccess: trusted-host` and that stderr reports no unmatched `mnemon` target. +3. Start DSH with the same `--trusted-host` command, and restart after any `remoteAccess` change because Mnemon captures that policy at startup. Verify **Status**, settings loading, and one deliberate small save through the authenticated remote connection. + +## Security boundaries + +### Process + +- CLI uses `spawn(command, args, { shell: false })`. +- stdout + stderr are capped at 2 MiB per call. Mnemon Native reads a whole Memory Space for its contents list, its graph and the exact-content check before runtime memory archives entries; those reads grow with the Store and are capped at 128 MiB. +- A call that fails says why: only a failed launch suggests installing Mnemon or setting `mnemon.cliPath`, while a timeout, a cancellation and an output over the cap each name themselves. +- Calls use `timeoutMs` and AbortSignal; cancellation sends `SIGTERM`, then `SIGKILL` after 1.5 seconds. +- One Runner serializes calls; separate DSH processes still rely on Mnemon / SQLite concurrency. + +### Files + +- Runtime, Documents, and Pack operations use in-process queues or component locks. +- Lock wait defaults to 5 seconds; stale threshold is 30 seconds. +- Writes use temporary files, staging, and rename. +- Runtime revisions block stale compaction; Document revisions block movement of updated originals. +- `sourcePaths` cannot escape the initiating workspace or point into managed Documents. + +### Web and model + +- DSH owns authentication or pairing for remote RPCs and streams. Mnemon's API Gateway projection additionally requires `remoteAccess: trusted-host` for management; local loopback clients and DSH desktop windows (`dsh-app://app/`) use their own channels. +- Provider catalogs and management responses are redacted; the UI receives configured field names, never saved credential values. +- The WebUI follows the Host's writable settings snapshot instead of inferring capability from transport locality; an unavailable settings channel renders an explicit diagnostic rather than an empty page. +- The WebUI neither reads SQLite, starts processes, calls remote providers, nor supplies arbitrary update commands; provider network access remains inside the Host. +- Workers use persona, tool allowlists, and `maxDepth: 1`. A stable result tool accepts only the current child's revocable request ID and validates each operation's result schema. +- Distillation and supervised writeback workers cannot call `mnemon_forget`. Idle review has only the create-only Documents tool for document writes, so it cannot replace user originals or archive documents to make room. These restrictions are enabled by default and do not require an enhancement plugin. +- Idle review writes each pass to one layer: after it creates a Document, working-memory changes are refused, and after a working-memory change, a Document is refused. `idleReview.runtimeMemory: false` removes its runtime memory tool entirely. +- Queries, candidates, Document bodies, and historical memory are treated as untrusted data. + +These boundaries are not a secret scanner. There is no deterministic credential detection; never submit keys, tokens, private keys, or raw sensitive logs. + +### Security reporting + +Report vulnerabilities privately through [SECURITY.md](../../../SECURITY.md), not a public issue. Data loss, path traversal, lock/revision bypasses, subagent-isolation breaks, and injection through rendered memory are in scope. + +## Troubleshooting + +`mnemon.cliPath` accepts an explicit path or a command name resolved against the Host's PATH. If the binary is installed or restored into an existing search directory while DSH is running, choose **Refresh** in the Memory System header to update availability without restarting. Changes to the Host process's environment still require a restart. Status and version checks resolve the same configured command. + +| Symptom | Check and resolution | +|---|---| +| Plugin details show the `mnemon-bundle` container row (named `dsh-mnemon/bundle` from 0.5.20, `cordis:group` before) as off, and its switch returns `unknown-plugin` | Known container-display issue in DSH `0.1.7-rc.2` (`0.2.0-rc.1` shows it as off too). Check **Memory System → Status** and the actual components; continue using Mnemon if they and memory operations work. Use the top-level bundle or `mnemon` core component switch for the composition; do not reset data or remove the group. See [cause, handling and fix boundaries](../reference/compatibility.md#dsh-017-bundle-component-list) and [upstream #649](https://github.com/dsh-external/issues/issues/649). | +| Terminal flashes when switching conversations on Windows | Update the Starter, or Runtime Source if independently installed, then restart the DSH Host. Runtime Git branch detection hides its console window; Git failures, timeouts and detached HEAD still fall back to an unfiltered Runtime view. | +| Mnemon unavailable | macOS/Linux: run `command -v mnemon`, `mnemon --version`. Windows PowerShell: run `Get-Command mnemon`, `Test-Path "$env:LOCALAPPDATA\Programs\mnemon\mnemon.exe"`. Set `MNEMON_CLI_PATH` or `mnemon.cliPath`, then restart | +| Electron desktop Host cannot run npm CLI scripts | Verified npm launchers use child-only `ELECTRON_RUN_AS_NODE=1`. If the desktop shell disables the [Electron `runAsNode` fuse](https://www.electronjs.org/docs/latest/tutorial/fuses#runasnode), this flag is ignored; set `mnemon.cliPath` to the official native binary (`mnemon.exe` on Windows). Automatic npm updates still require a Host that can run the JavaScript launcher | +| Headless Agent has no Mnemon tools | Plugins are profile-local. Run `dsh plugin --profile headless add dsh-mnemon`; a Web-profile installation does not carry over | +| Memory System entry missing | Check `tabEnabled=true`; `displayMode=sidebar` uses the sidebar, while `displayMode=builtin` uses the open conversation's tabs. For a local link run `pnpm run build`, then restart the profile | +| An old `buildin` preference opens a conversation tab | It is saved as `builtin`; choose **Sidebar** under **Interface** to keep the standalone entry. Memory scope and stored data are unchanged | +| Status healthy but recall empty | Check active spaces, storage scope, inspected root, effective session root, and query focus | +| Header reports misalignment | Confirm that the inspected workspace is intended. Workbench tasks execute there; conversation tools retain their own session scope | +| A setting appears unchanged | Switches and selectors apply at once; typed values need **Apply**. A failed write shows its reason beside the control and restores the saved value | +| Custom directory rejected | Use an absolute path, `~`, or `~/...` | +| `memoryBodyId is required...` | Active count is not exactly one; select a target explicitly | +| `memory space is not active for reading` | Activate it in Overview; inactive writes are allowed, reads are not | +| Provider error | Review needs guarded local child publication; use bounded `spawn` or opt-in `fork`. With DSH/Teams 0.1.7-rc.2, opt into `idleReview.agentTeams: scoped`; `pause` stays the default. Inspect partial-write receipts before retrying | +| Background review fails with `no user query found in messages` on a local model, such as Qwen3.x on Ollama | Once tool results filled the model's context window, Ollama's truncation dropped the subagent's prompt. From 0.5.22, Mnemon adds a short user turn to a refused subagent step and retries it ([#327](https://github.com/omdsh-dev/dsh-mnemon/issues/327)); update dsh-mnemon. A larger Ollama context length (`OLLAMA_CONTEXT_LENGTH` or the model's `num_ctx`) also avoids the truncation. The review may have created a Document before it failed; check the partial-write receipts | +| Runtime replace exceeds capacity | Shorten it or organize first; automatic maintenance handles add overflow only | +| Document source path rejected | Keep it inside the session workspace and outside managed Documents | +| CLI timeout | Increase `timeoutMs`; large Stores may need more than 10 seconds for status or graph | +| Lock timeout | Check other writers; never delete a lock owned by a live process | +| ZIP export reports WAL busy | Wait for Memory Space writes to settle; do not bypass the uncheckpointed-WAL guard | +| ZIP import checksum/schema failure | The backup is damaged or incompatible; preserve the current root and never unzip over it manually | +| No Update button | Already current, remote check failed, or the source is link/manual; follow panel guidance | +| An authenticated remote page can read or activate a Memory Space but cannot save settings or perform other writes | Default management restriction; for intended remote management, preserve the current configuration, set `remoteAccess: trusted-host` locally, and restart DSH | +| In the DSH desktop app, runtime memory, Memory Spaces and the plugin settings are read only while agent tools still write | dsh-mnemon 0.5.18 and earlier treated desktop windows as remote pages ([#310](https://github.com/omdsh-dev/dsh-mnemon/issues/310)); update to 0.5.19 or later on the app's Plugins page, with no `remoteAccess` change | +| DSH 0.2 reports dsh-mnemon as incompatible when installing or starting | A release before 0.5.19 was installed; update dsh-mnemon, and within 24 hours of a release install the versioned package as [Install and start](./installation.md#common-problems) describes | +| After dsh-mnemon was updated while DSH ran, enabling a component reports `ERR_PACKAGE_PATH_NOT_EXPORTED` (`Package subpath './starter'` or `'./bundle'`) | The running process still loads with the old version's package information; quit DSH completely and start it again (`Cmd+Q` for the desktop app). On 0.5.18 or 0.5.19, do not turn off `dsh-mnemon/starter`; see [Install and start](./installation.md#err_package_path_not_exported-after-an-update) | +| `mnemon-bundle (cordis:group): pending (waiting for service: mnemonStarterReady)`, or the desktop app fails to start and shows its plugin recovery page | `dsh-mnemon/starter` of 0.5.18 or 0.5.19 is off; turn it on under **Plugins → dsh-mnemon**, or update to 0.5.20, which has no separate readiness switch. On the desktop app, remove the plugin from the recovery page and add dsh-mnemon again; see [Install and start](./installation.md#dsh-says-waiting-for-service-mnemonstarterready) | +| `mnemon-bundle (dsh-mnemon/bundle): pending (waiting for service: mnemonStarterReady)` right after an update | DSH was updated to 0.5.20 without a restart and still runs the old component group; quit DSH completely and start it again, as [Install and start](./installation.md#err_package_path_not_exported-after-an-update) describes | +| Mnemon RPC returns 401 after a DSH restart or authority change | Open the launch URL printed by `dsh web` so the one-time token can establish a fresh authority-bound browser cookie | + +## Known limitations + +### Feature read-only is not disk read-only + +`writeEnabled=false` disables semantic mutation and Pack import, but startup may initialize/repair Runtime, Document search updates `lastAccessedAt`, and Mnemon reads may migrate a database. + +### Shared Documents scope + +`global` and `custom` can share one Document index across workspaces; records have no independent workspace-ownership field. `sourcePaths` are checked against the initiating cwd only when written. + +### Cross-system transactions + +Cold-index-first protects active content but is not a rollback-capable distributed transaction across Mnemon SQLite and the filesystem. A revision conflict after indexing may leave a duplicate reference; the system preserves data. + +### Background watermark + +Activity score, latest checkpoint, and retry state are not persisted. Host restart clears unprocessed activity. Failure backoff, circuit breaking, and manual retry are not implemented yet. + +### Versions and internationalization + +DSH 0.1.7-rc.2 and 0.2.0-rc.2 are supported; see the [compatibility matrix](../reference/compatibility.md). The Web interface is bilingual, while commands, tool cards, compatibility metadata and some errors remain partly untranslated. + +## Document archive recovery + +Document archive no longer asks the worker to number remember/recall receipts. If an older attempt left a cold index while the document stayed active, retrying can reuse an index whose exact path and content hash match the current revision. Updated documents need a matching new revision index. Providers with asynchronous extraction or no safe forget are rejected before indexing. A cleanup failure names the destination and newly created id; keep existing data until the outcome is established. + +## Runtime archive recovery + +Capacity archival requires an active Memory Space whose Provider supports exact writes and safe forget. Asynchronous extraction targets, including Hindsight, are excluded before any archive write. If no eligible target remains, activate a suitable space or increase `runtimeMemory.memoryLimitBytes`; the rejected mutation and existing hot entries stay unchanged. Direct Provider writes retain their existing asynchronous behavior. + +Conversation archival uses the selected Source's write scope: known spaces activated during the turn and spaces created by that View can receive the archive. A space created elsewhere after the View was pinned requires a new turn. An empty scope never grants access to every space. Destination errors distinguish an empty directory, an empty or excluding View scope, and unsupported Provider capabilities, and report the catalog, authorized and writable counts. The pending add has not been saved on failure; retry the same tool input in a new turn after correcting the reported cause. Existing hot entries are preserved. + +If an archive receipt or local commit fails, the Host attempts to forget only entries proven newly created by this attempt. Skipped or reused entries are preserved. A changed or unreadable Runtime revision after a commit error is treated as uncertain: archive entries are retained to avoid losing committed memory. Cleanup errors identify the remaining destination and item ids. A Provider request that fails without receipts may have an unknown remote outcome, so this is not a distributed transaction; inspect that Provider before retrying. diff --git a/dsh/docs/en/guides/ui-guide.md b/dsh/docs/en/guides/ui-guide.md new file mode 100644 index 000000000..448ed15d6 --- /dev/null +++ b/dsh/docs/en/guides/ui-guide.md @@ -0,0 +1,292 @@ +# UI guide + +[简体中文](../../zh-CN/guides/ui-guide.md) | **English** | [Documentation hub](../README.md) + +You meet dsh-mnemon in three places. The conversation shows what each turn read and wrote, and lets you save a reply to memory. The **Memory System** is where you browse, search and edit what is stored. The `dsh-mnemon` page under **Plugins** is where you compose memory and change settings. + +| Where | What you do there | How to open it | +|---|---|---| +| [In a conversation](#in-a-conversation) | See the memory a reply read and wrote, and open it in one click; save a reply to memory | Under each finished reply | +| [The Memory System](#the-memory-system) | Check status; browse, search and edit runtime memory, documents and memory spaces | **Memory System** in the sidebar, or a session tab | +| [On the Plugins page](#on-the-plugins-page) | Choose strategies and enhancements; set up components, storage and the interface | **Plugins → dsh-mnemon** (可组合记忆), or the gear in the Memory System header | + +The recordings and screenshots come from the [v0.5.19 gallery](../../assets/webui-v0.5.19/README.md): a real WebUI in light appearance with a seeded, fictional project, Lumen. The live DeepSeek model wrote every answer, write and receipt; waits for the model play at 4x or 6x, marked in the lower right corner. + +Two rules hold everywhere. **Switches and selectors apply as soon as you change them**; if a write fails, the control shows the saved value again next to the reason. **Typed values wait for their group's Apply**, which appears once something changed, together with a note on what applying will do. + +## In a conversation + +![Ask a question, expand the turn memory bar, and open the document it read in Project Documents](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/recall.gif) + +### Turn memory bar + +A reply that used memory ends with a **Turn memory** line: the Memory System's brain mark, then how many recalls, document searches and writes the turn made. Expanded, it has one row per tool. On the left, the tool says in plain words what it did, such as **Document search** or **Memory Spaces recall ×2**; hover to see the tool's own name. On the right are the items it read or wrote. + +![An answer drawn from working memory, Project Documents and Memory Spaces; the expanded bar lists three documents and a memory](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/chat-recall.jpg) + +- Select a tool to open its page in the Memory System. +- Select an item to open it where it lives: a document is selected in **Project Documents**; a memory runs as a query on the **Recall** page; a runtime entry is highlighted and scrolled to in **Runtime memory**. +- Each tool lists up to three items and counts the rest; an item known only by its id stays out. On a phone, the items sit below their tool. +- Turns without memory activity show no bar. + +When you correct a fact, the Agent replaces the runtime memory entry and the bar reports the write; select it to see the replaced entry. Here the Agent also brought the checkout investigation up to date, so the bar lists two writes. [Watch the correction](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/correct.mp4). + +| A correction and its writes | Its runtime item opens the entry, highlighted | +|---|---| +| ![The reply to a new checkout target; the turn memory bar lists a runtime memory update and a document update](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/chat-correction.jpg) | ![Runtime memory with the replaced checkout target entry highlighted](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory-runtime.jpg) | + +Temporary progress, guesses and secrets are not written. + +### Save to memory + +![Select the brain mark under a reply, edit the candidate, send it to the task Agent, then view the new memory from the receipt](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/save.gif) + +Each finished reply has a brain-shaped **Save to memory** in its action strip, the same mark as the Memory System; the database icon beside it is DSH's usage, not memory. Selecting it reads that reply and opens a dialog: + +- The title is **Save to memory**, with one line on what the task Agent does. **Task Agent ready** means you can send. +- **Candidate (editable)** starts with the whole reply; usually you keep only the sentence worth remembering. **Cancel** changes nothing. +- **Send to task Agent** hands it to an independent task Agent, which decides whether it is worth keeping, picks the right Memory Space, removes duplicates, distils and writes, outside the conversation. +- A receipt follows: the outcome (**Saved**, **Updated**, **Submitted; the Provider confirms later**, **Skipped**, **Partly saved**, **Not finished** or **Failed**), the task Agent's summary of where it went, in the candidate's language, and **View in Memory Spaces**. **Cancel** becomes **Close**, and sending again needs an edit to the candidate. + +| Edit the candidate | The task Agent's receipt | +|---|---| +| ![Save to memory with Task Agent ready and the candidate cut down to one fact](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/chat-save.jpg) | ![Receipt: saved in the Lumen project Memory Space, with View in Memory Spaces](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/chat-save-receipt.jpg) | + +**Save to memory** at the top of Memory Spaces opens the same dialog. The turn memory bar and the Save to memory button are on by default and can be turned off under [Interface](#interface). In the conversation-tab placement, these entries open the Memory System tab of the conversation they came from; with several eligible tabs in split panes, open the intended one yourself. + +## The Memory System + +![Status, runtime memory, a document, the Memory Spaces overview and graph, then an Agent answer with citations](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory.gif) + +The Memory System has four pages, named by the components that provide them: **Status**, **Runtime memory**, **Project Documents** and **Memory Spaces**. An installed Source adds its own page the same way. The header shows the connection and the composing main strategy, for example *Connected · Layered strategy*, the storage scope, **Refresh** and a gear that opens the Plugins page. **Refresh** reloads the open page, so pages carry no refresh or sync buttons of their own. + +By default the Memory System is a sidebar entry that keeps its page when you come back. Opened from a turn's item, a page lands on that item; a tab in the header opens the page fresh. Under **Interface → Memory System opens in**, choose **Conversation tab** to open it inside a conversation instead; the pages are the same. **Back to chat** and Escape return to the conversation. + +### Status + +![Status: the engine, one card per memory component, the running Provider and the storage areas](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory-status.jpg) + +The top row has one card per component: the engine with its version, then each Source with what it holds. **Memory providers** lists only the running Providers with their spaces' connection state and sums up the rest in one line, *N more Providers are off*, with **Open configuration** beside it; the storage areas follow. Regions load independently; one failure never hides the rest. + +A notice with **Open configuration** appears only when memory needs attention: + +- **Memory not in use**: no main strategy runs, so conversations continue without memory. +- **The selected main strategy is not running**: another strategy composes memory for now. +- **The latest component change did not take effect**: the previous composition keeps serving. + +**Check versions** is read-only. It tells whether the Mnemon CLI is missing, current or out of date, with the npm command to copy, and lists each subpackage's installed, published and pinned version. When a newer dsh-mnemon exists, **Update** installs it through DSH's own plugin installer, in the desktop app too. Restart DSH after updating packages; until then, a notice above every page names the installed version. + +### Runtime memory + +Runtime memory is the compact context injected into every turn. **User profile** (`USER.md`) holds who you are, your preferences and collaboration rules. **Working memory** (`MEMORY.md`) holds project facts, environment, decisions and lessons. Each card shows its size against its limit. Opened from a turn, the entry that turn wrote carries a highlighted border; see [the correction above](#turn-memory-bar). + +- Entries are newest first, with filters for target and text. Editing an older entry keeps its place. **Add memory** opens the dialog for a new runtime entry. +- Keep entries short, independent and repeatedly useful; temporary progress and raw logs do not belong here. +- A working-memory entry can be limited to git branches. It is then injected only while the session's workspace is on one of them; the page and the files on disk are unaffected. +- When working memory reaches its limit, the Host archives the oldest entries into a memory space before adding the new one. + +
+Editing, capacity and branch details + +Edit and Remove match an entry's full content within its target, so a short entry such as `X` can change without touching `EGO_LINUX_CHROME`. Identical entries in one target are ambiguous and are rejected without changing data. + +Capacity archiving moves the exact original entries. If a routing batch fails or returns an invalid proposal, that batch goes to the eligible default memory space, or the first eligible one, and the summary records why. Spaces created or activated during the current turn can receive the archive once they are active and support it. If no destination works, existing entries stay intact and the pending add is not saved; retry it after fixing the cause. + +When the Agent writes the user profile, `branches` must be omitted or empty. For working memory, `branches: []` on replace clears the scope; omitting it keeps the scope. The model-facing snapshot adds each entry's importance and age in days for the new turn; stored text is unchanged. See [projection format and limits](../reference/storage-model.md#source-of-truth-and-projections). + +
+ +### Project Documents + +![Project Documents opened from a turn, with “Decision: queue event ingestion” selected](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory-documents.jpg) + +Documents hold complete narratives: designs, investigations, procedures and handoffs. Select a DSH workspace first; documents belong to a workspace even when storage is global. + +- Switch between **Active** and **Archived**. The reader keeps the title, retrieval description, provenance, revision, hash, size and full Markdown. +- Search matches the title, description and body. Lists are newest first; updating an older document does not move it. +- Source files in your project are never modified; a document is a managed copy. +- Archiving indexes the document in a memory space before moving the original, so the cold copy stays findable. The destination must be active and support exact writes and safe forget. + +
+Archiving and background review details + +For an explicit archive or capacity maintenance, an independent task Agent without memory tools proposes a summary and one eligible memory space. The Host validates it, adds the exact cold path and content hash, writes the index and records lineage before moving the original. An invalid proposal changes nothing. If the move fails, the Host removes only the index it just created; existing verified indexes are reused and never deleted. If cleanup fails or the Provider outcome is uncertain, inspect the reported destination before retrying. + +Background review never edits existing documents. It searches first, skips what is covered, and creates a separate supplementary document only for substantial new knowledge; with no capacity left, it skips creation. It reuses evidence already in the finished conversation and cannot reopen files. + +
+ +### Memory Spaces + +![The Memory Spaces overview: two active spaces and one inactive](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory-spaces.jpg) + +A memory space is one named scope of long-term evidence on one Provider. **Overview** lists the spaces as cards; the switch on a card decides whether dsh-mnemon reads it, and the tag names its Provider, such as Mnemon Native. Clicking a card reconnects only that space. **Save to memory** and **Provider for new spaces** sit at the top right, with **Recall**, **Content** and **Entities** beside Overview. + +![The graph of the active spaces with the ClickHouse entity selected](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory-graph.jpg) + +Below the cards, the graph merges what the active spaces can show: memories, entities, and the links between them. Select a space, entity or memory to see its context on the right, here the ClickHouse entity with **Recall around this**. Layout, dragging and reset only change the picture. The footer counts spaces, memories and entities, and how many elements are drawn. + +Each space declares what its Provider can really supply. Mnemon Native gives typed relationships; Hindsight and Holographic give their own graphs; Providers without edges contribute content only; query-only Providers such as ByteRover wait for a question. Nothing unsupported is invented. + +- **Create Memory Space** always lets you pick the Provider, with ready Providers first; turn more on from [Memory Spaces' page](#memory-spaces-page). +- **Provider for new spaces** matters only when a task Agent creates a space. **Fixed Provider** always creates it on the Provider you choose; **Smart selection rules** let the Host enforce data and capability rules first, then the task Agent chooses among the eligible Providers. Existing spaces are still chosen by name and description. +- **Tidy names and descriptions** samples each selected active space and gives it to an independent task Agent for a title and description. Each space runs on its own; a failure stays on its card. + +#### Recall + +| Direct search | Ask Agent | +|---|---| +| ![Direct search across the active spaces, with scores and provenance](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory-recall.jpg) | ![An Agent answer drawn from five memories, with its citations listed by content](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory-agent.jpg) | + +**Direct search** returns raw evidence from every active space without an Agent, with each Provider's own score, space and category. **Ask Agent** runs the same search, then gives the evidence to a read-only task Agent for an answer. The answer speaks in terms of the evidence, and **Cites** below lists the memories it used by their content, never by id. Providers answer concurrently, so one failure never hides the others. **View related** and **Forget** appear only where the Provider supports them. Opening Recall with a query, from a turn's item, **Recall around this** or **View related**, runs it right away. Focused questions work better than bare keywords. + +#### Content and Entities + +| Content | Entities | +|---|---| +| ![Content: the memory just saved appears in the list](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory-content.jpg) | ![Entities: ClickHouse selected, with the memories that mention it](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory-entities.jpg) | + +**Content** lists memories where the Provider can enumerate them; query-only Providers show content once you enter a query. **Entities** uses only real entity indexes, currently Mnemon Native, Hindsight and Holographic. Each entity's count is the number of memories that carry it across the active spaces, and the list shows every entity. Selecting one lists exactly those memories, most important first, at once. Below them, **Find related memories** runs recall for the entity among memories that do not carry it. Recall takes longer, so it runs only when asked for: once opened, it stays open for the next entities until you hide it, for the rest of the browser session. When no memory carries a name you entered, related memories show by themselves. + +#### Save to memory + +![Save to memory on the Memory Spaces page, the same dialog as in a conversation](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory-remember.jpg) + +**Save to memory** here is the same dialog as in a conversation: write the candidate, send it to the task Agent, and view the result under **Content** from the receipt. Open **Advanced options** only when a target, category or importance is genuinely required. + +### On a phone + +| Conversation | Memory Spaces | Configuration | +|---|---|---| +| ![A conversation at 390 px: the turn memory bar's items sit below each tool](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/narrow-chat.jpg) | ![The Memory Spaces page at 390 px](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/narrow-spaces.jpg) | ![The Memory composition board at 390 px](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/narrow-plugin.jpg) | + +At 390 px the sidebar becomes DSH's icon rail and the pages stack. Long names truncate. These captures do not establish complete phone support; see [known limits](../reference/compatibility.md). + +## On the Plugins page + +![Open dsh-mnemon under Plugins, open the main strategy menu, visit the Layered strategy's and Memory Spaces' pages, then scroll to Storage and Interface](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/plugins.gif) + +![The dsh-mnemon page under Plugins: header, Memory composition and component rows](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/plugin-composition.jpg) + +DSH 0.1.7 keeps a plugin's configuration on its own page under **Plugins**. The `dsh-mnemon` page (可组合记忆 in Chinese) has **Memory composition**, **Storage** and **Interface**, then DSH's list of the components the Starter includes. **Open Memory System** at the top leads back. Each component's own settings live on its own page. + +| Where | Contains | Applies | +|---|---|---| +| Memory composition | Main strategy; one switch per memory source and enhancement | At once, to future turns | +| Storage | Storage scope, data directory, backup and migration | Scope and directory with their **Apply**; ZIP import and export at once | +| Interface | Memory System entry, turn memory bar, save to memory | At once | +| Runtime memory's page | User profile scope | At once | +| Memory Spaces' page | Memory providers and the Mnemon Native embedding | Switches at once; typed connections with **Apply** | +| Layered strategy's page | Task Agent model and idle review | Choices at once; review limits with **Apply** | + +A change saved from another window, or an edit of the profile, shows up without a reload. + +### Memory composition + +![The main strategy selector: Layered strategy and General strategy](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/plugin-strategy-menu.jpg) + +- **Main strategy** is one selector listing each installed main strategy with its description. The **Layered strategy** keeps runtime memory resident, reads Documents and Memory Spaces on demand and runs the background maintenance. The **General strategy** offers every available source in one budget and lets the model decide, without automatic review or capacity maintenance. +- **Memory sources** and **Enhancements** follow, one row per component with its state, a switch, and a gear when it has settings. Chips name the components a row needs or that depend on it; each opens that page. +- A problem line appears only while memory is not composed as chosen, with the one switch that fixes it. + +Every switch applies to future turns; a turn that already started keeps its View. Turning a component on also turns on what it needs and turns off what cannot run beside it; turning one off also turns off what depends on it; choosing a main strategy turns the others off. The last running source stays on. When a switch moved other components or changed what composes memory, a toast says so with **Undo**. + +### Component pages + +![The Layered strategy's page with its background tasks](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/plugin-layered.jpg) + +The gear on a row, the gear beside **Main strategy**, and a component's name anywhere open its page. It shows the component's state and switch, whether it ships with dsh-mnemon or came from an installed package, what it needs, what depends on it and what cannot run beside it, its declared options and its own settings. A related name opens in place with **Back**. Options are checked against the Host's limits; **Apply** appears once one changes, and **Reset to default** restores one. + + + +![Memory Spaces' page with Mnemon Native and the third-party Providers](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/plugin-spaces.jpg) + +- **Runtime memory's page**: **User profile scope**. *Shared globally* combines a global `USER.md` with workspace or custom `MEMORY.md`, without moving either. +- **Memory Spaces' page**: the **Memory providers**. Mnemon Native comes first and, expanded, holds only its embedding settings. Every third-party Provider has its own switch and is off by default; its endpoint, key and fields appear once it is on. The Global or Workspace tag shows its effective scope. OpenViking's **User key owner (skip admin)** field selects one user namespace for keys without admin access; see [its limits](./memory-providers.md#operational-boundaries). +- **Layered strategy's page**: **Background tasks**. **Task Agent model** follows DSH's default route for new sessions, or **Choose a model** picks a complete Provider and model; it applies to Save to memory, Ask Agent, Tidy names and descriptions, smart selection for new spaces and document archiving, never to the main conversation. **Idle review**, **Write runtime memory**, its method and Agent Teams compatibility apply at once; **Review limits** apply with **Apply**. With **Write runtime memory** off, review only creates Documents. Review runs only after turns the Layered strategy composed; see [configuration](../reference/configuration.md#provider-requirements). + +
+Mnemon Native embedding + +Expand the Mnemon Native card. **Manage embedding settings in DSH** makes the saved endpoint, model, protocol and optional key authoritative for Mnemon child processes, including Desktop launches that do not read shell startup files. While it is on, those fields apply together with their **Apply**. Automatic protocol selection treats an endpoint ending in `/v1` as OpenAI-compatible; other endpoints need an explicit protocol. The URL rejects embedded credentials, queries and fragments. Memory and query text are sent to the configured service. **Test status** checks saved values, not an unsaved draft. See [embedding configuration](../reference/configuration.md) before turning it on. + +
+ +### Storage + +![Storage: the scope, the default data directory, backup, and the Interface settings](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/plugin-storage.jpg) + +**Storage** names the components that keep their data in its directory; each name opens that component's page. + +- **Storage scope**: **Global** shares one directory across workspaces; **Workspace** keeps each workspace's memory in its own `.mnemon`; **Centralized · isolated by workspace** keeps each workspace in `workspaces//` under one root. +- **Data directory**: **Default** or **Custom**, and the row shows the one directory memory uses. Default is `MNEMON_DATA_DIR` or `~/.mnemon`. Custom opens a field for an absolute path or one starting with `~/`; under Centralized it sets the root. An empty field waits for a path; choosing **Default** is how you return to the default. +- Scope and directory apply together with **Apply**, which states the consequence: memory reads and writes the new location, and existing data is never moved, merged or deleted. + +![Backup and migration: an exported ZIP previewed before a safe import](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/plugin-backup-preview.jpg) + +**Backup and migration** exports the current data directory as a ZIP, with the components Storage names, and never includes third-party Provider data or credentials. Importing first shows a verified preview of what the ZIP contains; **Safe import** merges it into the current directory. See [backup and recovery](./operations.md#backup-and-recovery). + +### Interface + +- **Memory System opens in**: **Sidebar** (default) or **Conversation tab**. +- **Turn memory bar** and **Save to memory action** turn the conversation controls on or off. + +### DSH's component list + +![DSH's component list: each component named as it declares itself](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/plugin-rows.jpg) + +Below the configuration, DSH lists the components the Starter includes under the names and descriptions they declare. The `mnemon-bundle` row is the Starter's group entry (shown as `dsh-mnemon/bundle` from 0.5.20; the picture above is from 0.5.19, where it read `cordis:group` below the separate `dsh-mnemon/starter` readiness row); its "Off" state does not affect anything. A component's name opens DSH's page for it, which carries the same component page as the board. + +![DSH's own page for the Layered strategy](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/plugin-row-page.jpg) + +## Inspected and effective workspace + +| Concept | Selected by | Affects | +|---|---|---| +| **Inspected workspace** | The Memory System header | Which `/.mnemon` the pages show and edit | +| **Effective workspace** | The current conversation's working directory | Which root conversation tools, commands and lifecycle hooks use | + +You can inspect project B while talking in project A. The conversation stays on A; Save to memory, Ask Agent, Tidy names and descriptions and document archiving started from the Memory System run as task Agents scoped to B. Remote Provider namespaces never change with the DSH workspace. Global and custom storage resolve to one root and need no alignment. + + + +## Theme and skin overrides + +Skin authors can start with [Skin development and Mnemon integration](../development/skin-integration.md) for an example, migration from generated classes and verification in a real WebUI. This section defines the supported contract. + +The sidebar and the conversation tab expose one workspace root: `[data-dsh-plugin="dsh-mnemon"][data-dsh-part="mnemon-view"]`. Set these custom properties directly on that element; values on an ancestor are shadowed by the workspace defaults. Generated CSS-module class names are not public selectors. + +| Property | Accepted value and purpose | Default | +|---|---|---| +| `--mn-bg` | CSS color for the base surface | `var(--dsw-alias-bg-base)` | +| `--mn-backdrop` | CSS color behind the base in the default layered surface | `var(--dsw-alias-bg-overlay, var(--mn-bg))` | +| `--mn-surface` | CSS `background` value for the workspace, header and canvas | Base gradient over backdrop gradient, backed by the base color | + +The default surface is `linear-gradient(var(--mn-bg), var(--mn-bg)), linear-gradient(var(--mn-backdrop), var(--mn-backdrop)) var(--mn-bg)`. The layered backing keeps text readable when a skin makes the host base transparent; the official theme stays opaque. A skin can replace the composition, for example: + +```css +[data-dsh-plugin="dsh-mnemon"][data-dsh-part="mnemon-view"] { + --mn-bg: rgb(232 241 249 / 74%); + --mn-backdrop: var(--mn-bg); + --mn-surface: var(--mn-bg); +} +``` + +Use the paired selector in an unlayered stylesheet. Its specificity `(0,2,0)` beats the defaults `(0,1,0)` whether the skin loads before or after Mnemon. A lone `[data-dsh-part="mnemon-view"]` ties the defaults and depends on load order, and a declaration inside `@layer` ranks below them. Add your skin's own ancestor selector when it needs an activation scope. Dialogs portaled to the body and other plugins' roots are outside this scope. Replacing `--mn-surface` is the skin's choice of readability and transparency; it changes no settings, memory or Provider behavior. + +## Interaction conventions + +Memory surfaces follow one set of conventions everywhere: + +- **One mark**: the turn memory bar, Save to memory and the Memory System in the sidebar share the brain mark; DSH's database icon means usage, not memory. +- **One name, one dialog**: writing to long-term memory is called **Save to memory** everywhere and opens the same dialog, which a task Agent completes with a receipt. +- **Names over identifiers**: memories are referred to by title, content and space name, never by raw id or path; **Copy ID** stays on the cards for when you need one. +- **The same controls**: search boxes, choices, switches and dialog buttons use DSH's input, selector, switch and outlined Cancel; menus and tooltips open above dialogs. +- **Refresh lives in the header**: **Refresh** reloads the open page; pages carry no refresh or sync buttons of their own. +- **Weight of an action**: primary actions use DSH's primary button and secondary ones such as Edit an outlined button; red is reserved for Delete, Disconnect, Archive and Forget. +- **One-line descriptions**: each page and group describes itself in one line; the details live in these guides. +- **Status shows what runs**: Status lists the running Providers and sums up the rest in one line with Open configuration. + +Also: a memory space's switch controls only whether dsh-mnemon reads it, not the Mnemon CLI's default store; deleting a Mnemon Native space asks for confirmation, while external spaces use Disconnect and leave Provider data untouched; repeated clicks, including choosing the value already selected, never blank a page. + +Next: [Capability map](./capabilities.md) · [Getting started](./getting-started.md) · [Providers](./memory-providers.md) · [Configuration](../reference/configuration.md) diff --git a/dsh/docs/en/reference/compatibility.md b/dsh/docs/en/reference/compatibility.md new file mode 100644 index 000000000..902734204 --- /dev/null +++ b/dsh/docs/en/reference/compatibility.md @@ -0,0 +1,105 @@ +# Compatibility and upgrades + +**English** | [简体中文](../../zh-CN/reference/compatibility.md) | [Documentation](../README.md) + +The Starter pins a tested combination of official plugins. The table records verification scope, not a promise that every upstream release or account configuration works. + +| Component | Baseline | What is verified | +|---|---|---| +| DSH | `0.2.0-rc.2` (npm `latest` and `next`), `0.1.7-rc.2` | The two supported hosts; `0.2.0-rc.1` also installs. 0.1.7-rc.2 is the pinned development baseline: published contracts, WebUI and isolated Headless activation, profile settings and retained-settings recovery, producer-owned Session V4 messages, plugin manager activation and the Agent Teams review matrix. For DSH 0.2 see [below](#dsh-02) | +| Node.js | `22.19` and `24` | Source CI and packed-artifact CI respectively; development requires `^22.19.0 || >=24.0.0` | +| Node.js 20 | Public package imports only | Does not establish that the DSH Host runs on Node 20 | +| Mnemon Native CLI | `0.2.9` | Opt-in tests against a real CLI and disposable data; install the CLI separately | +| Third-party Providers | Adapter contracts and fixtures | Does not establish live cloud-account conformance or upstream availability | + +The root DSH peers (`dsh-app-boot` is optional) and the Memory Spaces Source's `dsh-client-ui-primitives` peer are all `^0.1.7-rc.2 || ^0.2.0-rc.1`. The five Strategy packages require `dsh-mnemon ^0.5.17`, whose extension SDK they use; the Sources keep `^0.5.1`, and the Providers depend on the Memory Spaces Source instead. Older DSH releases are no longer supported: keep dsh-mnemon `v0.5.16`, the last release verified with DSH `0.1.5-rc.1` through `0.1.7-alpha.1`, until the host is upgraded. Earlier Headless and WebUI records remain historical evidence. + +Turn memory registers with a stable ID in DSH's `conversation.chat.turnTail` list slot and checks that the turn is complete before reading or showing activity. The sidebar placement and the `dsh-mnemon` page under Plugins follow DSH's public default/main session binding; the conversation-tab placement and Better Sidebar keep their explicit owning session. DSH 0.1.7 keeps live settings in profile Config and edits a plugin's configuration on its page under Plugins; Mnemon's configuration page writes through it and recover retained legacy preferences as described below. Memory data and Provider formats do not change. + +See [DSH 0.1.7 settings verification](../../pr-assets/issue-267-settings-migration/README.md), [RC/alpha verification and before/after screenshots](../../pr-assets/issue-261-dsh-slots/README.md), [DSH 0.1.5 verification](../../pr-assets/issue-223-dsh-015/README.md), [Host compatibility evidence](../../pr-assets/dsh-rc1-compat/README.md), [upgrade evidence](../../pr-assets/main-rebase-20260904/README.md), and [current development checks](../development/README.md). A passing mechanism test is not an LLM quality benchmark. OS-specific and real-CLI checks may be skipped unless their environment is explicitly available. + +The [v0.5.19 Light gallery](../../assets/webui-v0.5.19/README.md) covers the bilingual desktop pages, the Plugins page, and a conversation, Memory Spaces and the composition board at 390 × 844, where long names truncate. It does not retest every Host settings surface or physical phones, so phone support is not declared complete; the historical v0.5.2 layout failure at 390 px keeps its [versioned evidence](../../pr-assets/documentation-refresh/README.md). + +## DSH 0.2 + +Before DSH installs a plugin, and each time a profile starts, it checks every `@deepseek-ai/dsh` and `@deepseek-ai/dsh-*` peer range against its own version, prereleases included. `^0.1.7-rc.2` excludes 0.2.0, so DSH 0.2 refuses to install dsh-mnemon 0.5.18 and earlier as incompatible and turns an installed one off at startup. From 0.5.19 these peers also accept `^0.2.0-rc.1`; `tests/dsh-host-compatibility.spec.ts` runs DSH's own check over all 18 package manifests for 0.1.7-rc.2, 0.2.0-rc.1 and 0.2.0-rc.2. + +**Upgrade order.** Update dsh-mnemon to 0.5.19 or later on your current DSH first, then upgrade DSH. If DSH was upgraded first, the older release is turned off and memory data is untouched; updating the plugin brings it back. Do not use `allow-version` to let an older release through. + +**Verified on 0.2.0-rc.2.** The [v0.5.22 release acceptance](../../pr-assets/release-v0.5.22/README.md) installs the release packages through **Add plugin** on a fresh 0.2.0-rc.2 profile and enables them without a restart. It checks Status, runtime memory, a Native space and CLI-to-WebUI recall. The [sidebar entry record](../../pr-assets/issue-318-sidebar-entry/README.md) shows its panel rows and glyph sizes match 0.1.7-rc.2. + +**Verified on 0.2.0-rc.1.** With the published DSH 0.2.0-rc.1 installed globally, starting from an empty profile: installing from the command line and from the Plugins page, Enable now without a restart, Status, runtime memory writes, a first live-model conversation with Save to memory, a Headless job, and the read and write routes of desktop windows and remote pages. With every DSH development dependency moved to 0.2.0-rc.1, type checking, the builds, all plugin tests and the Headless verification pass, and among the root tests only the assertion about the pinned development baseline itself differs. The [installation gallery](../../assets/install-v0.5.19/README.md) has the screenshots. + +**Host behavior on a first install.** + +- When nobody has chosen a registry yet, **Add plugin** measures which one answers faster: in mainland China it usually defaults to the **Mainland China mirror**, elsewhere to the **Official npm registry**. +- pnpm 11 does not pick versions published less than 24 hours ago. On a release day, installing plain `dsh-mnemon` can install the previous release, which DSH 0.2 refuses as incompatible; install `dsh-mnemon@` instead, or wait 24 hours, as [Install and start](../guides/installation.md#common-problems) describes. A versioned install pins the version; upgrade it later with `dsh plugin --profile web update --latest dsh-mnemon`. Within the same 24 hours, `update` and `update --latest` keep the installed version, so existing installations also add the new version by name. +- The install result card shows the package's English description; the name and description in the plugin list follow the interface language. +- The `desktop` profile belongs to the desktop app, and DSH 0.2's command line refuses to manage it; desktop users install and manage plugins on the app's Plugins page. + +## Desktop windows + +DSH desktop windows load from the app's own `dsh-app://app/` address instead of a loopback URL, and the DSH 0.1.7 desktop app declares no transport for the page. dsh-mnemon 0.5.18 and earlier therefore treated desktop windows as remote pages: every call went through the API Gateway, and the default `remoteAccess: read-only` made runtime memory, Memory Spaces and the plugin settings read only in the interface while agent tools could still write ([#310](https://github.com/omdsh-dev/dsh-mnemon/issues/310)). + +From 0.5.19, a page the application serves itself (an address other than `http:` or `https:`) uses Mnemon's local channels; it still counts as remote only when DSH declares a transport for it that does not own the Host. The DSH 0.2 desktop app declares `ownsHost: true` and uses the local channels as well. Pages opened from another device keep the API Gateway and its read-only default, and the Memory System and the plugin settings now say that `remoteAccess: trusted-host` and a DSH restart are needed. The local channels and `/api` share DSH's Host/Origin checks and browser-session authentication. + +How it was verified: an Electron window standing in for the desktop shell registers the same standard, secure `dsh-app` scheme as the official app and proxies to a Host that listens only on 127.0.0.1. The published 0.5.18 reproduced the read-only state on DSH 0.1.7-rc.2 (all 13 Mnemon calls through the API Gateway); with the fix, both DSH 0.1.7-rc.2 and 0.2.0-rc.1 (`ownsHost: true`) add runtime memory through the local `/dsh-mnemon-write` channel, and Memory Spaces and the plugin settings are editable, while a remote page opened through a trusted authority stays read only and says why. + +## Desktop profile generations + +Desktop may remove private `@deepseek-ai/*` packages from a plugin generation and use its own framework versions. Mnemon builds its live settings schema from the host's public Volatile APIs, which DSH `0.1.7-rc.2` provides through Cosmokit `1.8.5`. A generation whose host carries an older Cosmokit without them, such as `1.8.3`, is an unsupported host; keep dsh-mnemon `v0.5.16` there. + +In the affected `0.5.13` install, Desktop's fallback hides a missing `createVolatile` export behind `Cannot find package 'dsh-mnemon'`. Flattening `mnemon-bundle` does not fix that import failure. The Starter retains its group, root disable gate, independent Source/Strategy choices, private Provider children and existing settings namespaces. Update Mnemon in the owning profile and restart; no memory or configuration migration is required for this fix. + +See the [original Desktop reproduction and verification](../../pr-assets/issue-274-profile-generation/README.md). + +## Installing and enabling the Starter without a restart + +On DSH `0.1.7-rc.2`, Desktop generation directories and pnpm installs can expose only the `dsh-mnemon` root in the profile. The component packages exist in its dependency tree, but a host started without the bundle selected may still use its startup package-resolution table. First activation then reports `ERR_MODULE_NOT_FOUND` for Runtime, Documents, Memory Spaces and the default Strategy. + +The Starter's component group `mnemon-bundle` (the module `dsh-mnemon/bundle`) prepares dependency resolution and then mounts its children with DSH's own `cordis:group`. It uses the host's public package-resolution service, preserves startup routes for other bundles, and lets the host reject incompatible module rebinding. It does not flatten packages, rewrite profile links or patch DSH. Existing Entry IDs, the core disable gate, independent component choices and memory data stay intact. In 0.5.18 and 0.5.19 a separate readiness entry, `dsh-mnemon/starter` (`mnemon-starter`), did this, and the group waited for its `mnemonStarterReady`. From 0.5.20 they are one entry: the Plugins page no longer shows a separate readiness row, and the group cannot be left waiting because that row was turned off. + +Mnemon also registers Web RPC routes in its own injected transport scope, so an already-running official Connection does not need to restart. Use the bundle or core switch to control the composition. In 0.5.18 and 0.5.19, turning `dsh-mnemon/starter` off leaves `mnemon-bundle` waiting for `mnemonStarterReady`: `dsh web` starts without the Memory System, and the desktop app treats it as a failed start; see [Install and start](../guides/installation.md#dsh-says-waiting-for-service-mnemonstarterready). After an update to 0.5.20, a leftover `- id: mnemon-starter` row in a profile patch matches no entry and the host ignores it (the desktop app logs `patch: entry "mnemon-starter" not found`); you can delete those two lines. + +The cost is in DSH's config schema export: `dsh web --dump-config-schema` walks only native `cordis:group` and `cordis:include` carriers, so it reports `mnemon-bundle` as an unrecognized tree carrier and leaves out its components' schemas, as it does for DSH's own agent presets. Runtime configuration, the Plugins page and profile patches are unaffected. + +A fresh installation through the official WebUI can use **Enable now** in the same host process. A previously installed, disabled bundle can also start directly. Updating or removing packages that Node has already loaded remains subject to DSH's normal restart requirements; this fix does not replace loaded modules. For example, enabling components after an in-place update without a restart reports that an entry point the new version added is not exported (`ERR_PACKAGE_PATH_NOT_EXPORTED`): `./starter` after an update from 0.5.17 or earlier, `./bundle` after an update from 0.5.18 or 0.5.19. The Loader does not swap the module of a running entry, so an update from 0.5.18 or 0.5.19 can instead leave the old group waiting for the removed `mnemonStarterReady` (`mnemon-bundle (dsh-mnemon/bundle): pending …`). Both clear on restart; see the [upgrade evidence](../../pr-assets/starter-group-upgrade/README.md). See the [installation and activation evidence](../../pr-assets/desktop-live-activation/README.md) and the [component group evidence](../../pr-assets/starter-group-readiness/README.md). + +## DSH 0.1.7 bundle component list + +In DSH `0.1.7-rc.2`, the **Plugins → dsh-mnemon** detail page lists the internal `mnemon-bundle` container as an off component (shown as `dsh-mnemon/bundle` from 0.5.20, `cordis:group` before); DSH `0.2.0-rc.1` shows it the same way. With all nine real components running the list reads “10 total · 9 running · 1 off”; 0.5.18 and 0.5.19 add a readiness row, for “11 total · 10 running · 1 off.” Clicking the container switch returns `unknown-plugin`; the Chinese UI reports “组件启用失败:找不到该插件”. See the [original screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-native-20260926/before-bundle-toggle-error.jpg) and [upstream issue #649](https://github.com/dsh-external/issues/issues/649). + +The host's display and management inventories disagree: its bundle declaration list includes native groups, but its manageable plugin inventory deliberately excludes them. This container's off state does not mean the Mnemon core or its children are disabled, and does not establish whether memory reads and writes work. + +When this happens: + +1. Inspect **Memory System → Status** and the actual Source and Strategy components. If only the container is incorrectly shown as off, while the required components and reads/writes work, you can continue using Mnemon. Actual component errors or failed operations still need separate investigation. +2. Use the top-level `dsh-mnemon` bundle switch or the core component with entry ID `mnemon` to stop and restore the composition. Do not use the `mnemon-bundle` container row's switch. The `dsh-mnemon` configuration page says this right above the component list. +3. Keep existing configuration and memory. This display issue requires neither a data reset nor configuration or memory migration. + +The Starter retains the stable group ID and the existing `mnemon` configuration target. Disabling the core stops its Sources, Strategies and private Provider children; re-enabling it restores their independent choices. Removing the group leaves enabled dependents waiting for the missing core, while making the group anonymous can leave old instances alive after a profile reload. Do not remove the container, its stable ID or alter the group declaration to hide the row. The [published lifecycle regression](../development/README.md#test-ownership-and-coverage) checks manager persistence, restarts and literal or expression-based legacy disable flags without changing the installed host. + +An [upstream candidate patch](../../pr-assets/sidebar-native-20260926/upstream-fix.patch), verified in an isolated environment, omits containers from the display while retaining their actual plugin children. It is not included in published DSH `0.1.7-rc.2` or Mnemon `v0.5.17`. The local management-adapter approach requires taking over DSH's global plugin-management service; embedding it in Mnemon would compromise independent disabling of Mnemon, so it is not shipped with the plugin. Follow [#649](https://github.com/dsh-external/issues/issues/649) and subsequent DSH release notes for the fix. Screenshots of the candidate environment do not establish that the published host is fixed. + +## DSH 0.1.7 settings recovery + +DSH 0.1.7 replaces `settings.register()` and `settings-file` with Config-backed forms. Mnemon exposes live Config fields and persists its existing UI operations through the host's revision-checked profile writer. Changes to transport authority still require a normal plugin reload. + +Mnemon messages use the producer-owned `dsh-mnemon` source kind accepted by both Session V3 and V4. Historical wrappers and DSH's migrated `plugin:dsh-mnemon` messages remain recognizable for filtering and deduplication; no existing Session files are rewritten by this change. + +On startup, Mnemon can recover the retained `/settings.yaml.imported` backup into the editable root `mnemon` entry. It restores the canonical root settings, `mnemon-ui` conversation toggles, and only the current profile's exact `mnemon-view-*` / `mnemon-plugins-*` namespaces. Explicit current profile values win, including an explicitly empty plugin selection; existing Source and Strategy rows are preserved. A Strategy row using a dynamic expression keeps that expression and skips its legacy overlay rather than freezing the current value. Recovery records `legacySettingsImported: true` in the root Config and leaves the backup unchanged. + +If the original `settings.yaml` still exists when Mnemon starts, DSH owns that import. Restart the profile once more after it finishes to recover Mnemon-specific preferences. DSH exposes no completion signal for its importer, so Mnemon deliberately waits for a new host process instead of racing it. A read-only or ambiguous root cannot be edited; malformed relevant sections are not partially imported or marked complete. Retain the backup and review the startup diagnostic before repairing it. Custom root IDs and another profile's hashed namespaces are not guessed. + +When rolling back DSH, keep a copy of the profile patch and retained settings backup, and reinstall dsh-mnemon `v0.5.16` with the older host. Settings subsequently edited on 0.1.7 are not automatically exported back to the older settings file; restore the matching backup or reapply those preferences in the older host. Runtime, Documents, Memory Spaces, and Provider data are unaffected. + +## Upgrade the default installation + +1. Export a Mnemon Pack and protect any required Provider connection backup as described in [Operations](../guides/operations.md#backup-and-recovery). +2. Upgrade `dsh-mnemon` in each DSH profile that uses it. The Starter installs its exact plugin versions; do not update its child packages independently and assume the resulting mixture is tested. +3. Restart DSH after installing new package code. Check **Memory System → Status**, then read an existing Runtime entry, Document and active Memory Space. +4. Verify the selected storage scope before writing. Changing the scope selects a different authority; it does not migrate data. + +v0.5 preserves the default v0.4 storage, configuration and sidebar workflow. The optional conversation-tab placement (`builtin`) uses the same Source pages, and the retained `buildin` spelling is normalized. The General strategy and the three memory enhancements ship disabled. Memory composition on the Plugins page lists, switches and configures the installed components; there is no separate View tab. + +Independent plugin authors use declared peer ranges and public exports. Old private controller imports are not a supported upgrade surface. Custom compositions must be verified separately from the Starter. See [Extensions](../development/extensions.md) and the [v0.5.0 release boundary](../releases/v0.5.0.md). diff --git a/dsh/docs/en/reference/configuration.md b/dsh/docs/en/reference/configuration.md new file mode 100644 index 000000000..25877eb5c --- /dev/null +++ b/dsh/docs/en/reference/configuration.md @@ -0,0 +1,467 @@ +# Configuration Reference + +[简体中文](../../zh-CN/reference/configuration.md) | **English** | [Documentation Center](../README.md) + +## Configuration Location and Activation + +The configuration is the `mnemon` entry of the DSH profile. DSH's settings service writes user values into the profile patch: + +```text +$DSH_HOME/profiles//cordis.patch.yml +``` + +The Web profile's patch is commonly `~/.dsh/profiles/web/cordis.patch.yml`. A `settings.yaml` left by an older host is imported into the profile once and kept as `settings.yaml.imported`; see [DSH 0.1.7 settings recovery](./compatibility.md#dsh-017-settings-recovery). All current settings are marked `live`; after a change is applied, the Host initializes a candidate runtime graph and then switches to it atomically. + +As DSH 0.1.7 does for every plugin that carries its own configuration, the Web interface edits Mnemon's settings on its page under **Plugins**, not in Settings. The [UI guide](../guides/ui-guide.md#on-the-plugins-page) shows each part. + +| Where in the UI | Settings | Saved as | +|---|---|---| +| Memory composition | Main strategy; one switch per memory source and enhancement; each component's declared options | `memoryView.strategyTypeId` and `memoryView.entries..config`; each component's on/off is its Entry's `disabled` row, written by DSH's plugin manager | +| Storage | Storage scope, data directory, backup and migration | `storageScope`, `dataDir` | +| Interface | Memory System opens in, turn memory bar, save to memory | `displayMode`, `conversationInteraction.turnBar`, `conversationInteraction.saveAction` | +| Runtime memory's page | User profile scope | `runtimeUserScope` | +| Memory Spaces' page | Memory providers; Mnemon Native embedding (auto, Ollama or OpenAI-compatible) | The Provider registry in `state/memory-providers.json`; `embedding` | +| Layered strategy's page | Task Agent model; idle review | `taskAgentModel`; `idleReview` | + +Switches and selectors apply at once; typed values such as the storage directory, the embedding connection, review limits and component options wait for **Apply**. A Provider's reusable service fields (endpoints, credentials, executables) live in the Provider registry, not in Mnemon's YAML; enabling or saving a Provider discovers its namespaces into **Memory Spaces → Overview**, and turning it off removes only those local mappings. Other advanced settings are edited in YAML. + +OpenViking user keys without admin access can opt into one-owner discovery using the service field `discoveryUser` together with `endpoint`, `apiKey`, and `account`. Leave it empty to retain admin enumeration. These service fields stay in the Memory Spaces provider registry; they are not new top-level Mnemon YAML settings. See [OpenViking setup and compatibility](../guides/memory-providers.md#operational-boundaries). + +## Complete Example + +```yaml +mnemon: + storageScope: global # global | workspace | custom | workspaces + runtimeUserScope: storage # storage | global + # dataDir: ~/mnemon-data # required for custom + # cliPath: /opt/homebrew/bin/mnemon + # store: legacy-store # compatibility discovery hint, not a regular routing target + timeoutMs: 10000 + defaultRecallLimit: 10 + runtimeMemory: + memoryLimitBytes: 10240 + userLimitBytes: 4096 + maintenanceMaxTokens: 8192 + embedding: + enabled: false + endpoint: http://localhost:11434 + model: nomic-embed-text + memoryTopology: + layers: + runtime: { enabled: true } + documents: { enabled: true } + memory-spaces: { enabled: true } + recallQuality: + policy: strict-v1 + lowScoreThreshold: 0.25 + highScoreThreshold: 0.6 + candidateMultiplier: 3 + maxMediumResults: 4 + maxUnknownResults: 2 + routingGuidance: true + lifecycleEnabled: true + recallMode: guided + writebackMode: guided + idleReviewMs: 30000 + idleReview: + enabled: true + runtimeMemory: true # false: Documents only + provider: spawn + fallback: spawn + agentTeams: pause # pause | scoped + minIntervalMs: 300000 + maxPerSession: 20 + maxContextChars: 24000 + maxTokens: 4096 + tabEnabled: true + writeEnabled: true + taskAgentModel: + mode: inherit # inherit | fixed + # provider: deepseek # required for fixed + # model: deepseek-chat # required for fixed + remoteAccess: read-only # remote management: read-only | trusted-host +``` + +## Options + +| Setting | Default | Range | Implementation Semantics | +|---|---:|---|---| +| `storageScope` | `global` | `global` / `workspace` / `custom` / `workspaces` | Controls the root for Runtime, Documents, Memory Spaces, and reserved state as one unit | +| `runtimeUserScope` | `storage` | `storage` / `global` | Keeps USER.md in the selected storage root, or overlays the global USER.md while project MEMORY.md and the other layers stay selected-scope | +| `dataDir` | unset | absolute path, `~`, or `~/...` | Required for `custom`; optional central root for `workspaces`; legacy configurations that set only this option automatically resolve to `custom` | +| `cliPath` | auto-discovered | executable path | Explicitly selects the Mnemon CLI | +| `store` | unset | `[A-Za-z0-9][A-Za-z0-9_-]*` | Compatibility discovery/preference hint for legacy Stores; semantic operations are routed through Memory Spaces | +| `timeoutMs` | `10000` | 100–120000 ms | Hard timeout for a single CLI call | +| `defaultRecallLimit` | `10` | 1–50 | Default recall count for the service and UI; individual entry points may impose a lower limit | +| `runtimeMemory.memoryLimitBytes` | `10240` | 1–1048576 bytes | UTF-8 byte limit for the complete `MEMORY.md` projection | +| `runtimeMemory.userLimitBytes` | `4096` | 1–1048576 bytes | UTF-8 byte limit for the complete `USER.md` projection | +| `runtimeMemory.maintenanceMaxTokens` | `8192` | 1–1000000 tokens | Completion-token budget for Runtime migration and compaction workers; does not change Document archive or metadata-maintenance budgets | +| `embedding` | `{ enabled: false, endpoint: http://localhost:11434, model: nomic-embed-text, apiKey: '', protocol: auto }` | enabled + HTTP(S) endpoint + model + optional apiKey + protocol (auto/ollama/openai) | When enabled, the Host injects the saved endpoint, model, API key, and protocol override into every Mnemon CLI child process; an endpoint ending in `/v1` makes Mnemon use the OpenAI-compatible protocol with the API key as a Bearer token, and `protocol: openai` forces it for non-`/v1` endpoints; when disabled, existing Host environment and Mnemon defaults remain untouched | +| `memoryTopology.layers..enabled` | `true` for the three defaults | boolean | Legacy layer flag. A Source's switch on the board turns its DSH Entry on or off and turns this flag back on with it; the flag is used when the components cannot be read. Turning a Source off never deletes or migrates data | +| `memoryTopology.layers..participation` | per layer | `off` / `manual` / `automatic` for `recall`, `write`, `projection`, `maintenance` | How a layer takes part in each kind of work under the Layered strategy | +| `memoryView.strategyTypeId` | unset | Strategy type id, for example `default-three-tier` or `general` | The selected main strategy, written by the **Main strategy** selector; overrides `memoryTopology.strategyId` | +| `memoryView.entries..config` | `{}` | the options a component declares | Options saved from a component's page. On/off is not stored here: DSH's plugin manager writes it as the Entry's `disabled` row in the profile patch, and earlier saved choices are moved there once | +| `persistenceStrategy` | `{ mode: manual }` | `manual` / `automatic`, `providerId`, `prompt`, `rules` | Provider for new spaces: when a task Agent creates a Memory Space, one fixed Provider, or smart selection with allowed Providers, data boundary, required capabilities and preference; existing spaces are still chosen by name and description | +| `recallQuality.policy` | `strict-v1` | registered policy id | Deterministic policy applied before recall content is serialized to an Agent or client | +| `recallQuality.lowScoreThreshold` | `0.25` | 0–1, below high threshold | Normalized scores below this boundary are removed by `strict-v1` | +| `recallQuality.highScoreThreshold` | `0.6` | 0–1, above low threshold | Retained normalized scores at or above this boundary are labeled high relevance | +| `recallQuality.candidateMultiplier` | `3` | 1–5 | Expands each Provider request before filtering, capped by the service limit of 50 candidates | +| `recallQuality.maxMediumResults` | `4` | 0–50 | Maximum medium-relevance rows admitted by `strict-v1` after all high-relevance rows | +| `recallQuality.maxUnknownResults` | `2` | 0–50 | Maximum unscored or unknown-scale rows admitted by `strict-v1` after scored evidence | +| `routingGuidance` | `true` | boolean | Whether to register an additional tiered-routing system section | +| `lifecycleEnabled` | `true` | boolean | Whether to enable the pre-step cue and score-based background review | +| `recallMode` | `guided` | `guided` / `off` | Whether to inject one durable on-demand recall cue per session; does not remove explicit recall | +| `writebackMode` | `guided` | `guided` / `off` | Whether to inject one durable hot-memory cue per session and enable scored, dirty-admitted background review; does not remove explicit writes | +| `idleReviewMs` | `30000` | 5000–600000 ms | Required continuous idle time after the threshold is reached | +| `idleReview.enabled` | `true` | boolean | Independent automatic-review switch | +| `idleReview.runtimeMemory` | `true` | boolean | Lets review change runtime memory (USER.md and MEMORY.md); `false` limits it to creating Documents | +| `idleReview.provider` | `spawn` | `spawn` / `fork` | Bounded checkpoint or inherited parent context | +| `idleReview.fallback` | `spawn` | `spawn` / `skip` | Missing/incompatible fork handling before startup only | +| `idleReview.agentTeams` | `pause` | `pause` / `scoped` | Pause while Team tools are installed or explicitly allow guarded review; verified with DSH/Teams 0.1.7-rc.2 | +| `idleReview.minIntervalMs` | `300000` | 5000–86400000 ms | Minimum interval between attempts | +| `idleReview.maxPerSession` | `20` | 0–200 | Attempt cap per loaded parent Agent; includes failures/cancellations | +| `idleReview.maxContextChars` | `24000` | 1000–1000000 | Spawn checkpoint character limit | +| `idleReview.maxTokens` | `4096` | 128–131072 | Per-response model output limit | +| `displayMode` | `sidebar` | `sidebar` / `builtin`; legacy `buildin` accepted | Where the Memory System opens: standalone Sidebar or a conversation tab using the same workspace UI; legacy spelling is migrated to `builtin` | +| `tabEnabled` | `true` | boolean | Whether to mount the selected entry and workbench; Host RPC, commands, and Agent tools remain registered when off | +| `writeEnabled` | `true` | boolean | Whether to expose semantic write tools, write RPC, and write commands | +| `taskAgentModel` | `{ mode: inherit }` | `inherit` / `fixed` | Model route for independent task Agents used by Tidy names and descriptions, Ask Agent, Save to memory, and Document archiving, plus the idle-review worker; `fixed` requires both `provider` and `model` and also pins their bounded workers for write, answer, provider placement, migration, compaction, archive, and metadata maintenance. Conversation Recall and Related are direct Host reads and do not use this route | +| `remoteAccess` | `read-only` | `read-only` / `trusted-host` | Startup-only grant for non-loopback Mnemon management, enforced by the API Gateway projection | +| `conversationInteraction.turnBar` | `true` | boolean | The turn memory bar under replies; applies at once | +| `conversationInteraction.saveAction` | `true` | boolean | The **Save to memory** action and confirmation on finished replies; applies at once | + +Every value is saved under the `mnemon` entry and applies live. The UI's `mnemon-ui` settings scope is stored as `conversationInteraction`, and its `mnemon-view` scope as `memoryView`. The storage root switches atomically only after the new runtime graph initializes successfully. + +### Runtime Memory budgets + +Long conversations can raise the two hot-memory byte limits and the bounded migration/compaction worker budget without patching generated package files: + +```yaml +mnemon: + runtimeMemory: + memoryLimitBytes: 20480 + userLimitBytes: 10240 + maintenanceMaxTokens: 32768 +``` + +The defaults preserve the released 10240 / 4096 / 8192 behavior. Saving the block builds a new runtime generation, so subsequent Runtime reads, writes, capacity maintenance, and Mnemon Pack validation use the same limits. Existing entries and the `memories.json` format are unchanged. Lowering a byte limit below current usage does not delete data; the Runtime view reports the over-capacity state and further writes require compaction or a higher limit. Rollback only requires removing the block or restoring the defaults. + +Storage byte limits count entry content and delimiters. The model snapshot's importance and age annotations do not consume storage capacity; they do consume the Strategy's existing projection character budget. They add no ranking, relevance filter or separate configuration. + +The Runtime memory page shows each file's size against its limit. + +![Runtime memory with USER.md and MEMORY.md against their default limits](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/memory-runtime.jpg) + +### Mnemon Native embeddings + +Finder- and Dock-launched macOS applications do not normally inherit interactive shell startup files. Enable **Manage embedding settings in DSH** under Mnemon Native to make the saved values authoritative for every Mnemon child process instead: + +```yaml +mnemon: + embedding: + enabled: true + endpoint: http://127.0.0.1:11434 + model: qwen3-embedding:0.6b +``` + +For an OpenAI-compatible server, point the endpoint at its `/v1` base URL; Mnemon automatically switches from the Ollama protocol to the OpenAI protocol (`/v1/embeddings`). Fill in `apiKey` for services that require authentication; it is sent as a Bearer token. For compatible endpoints that do not end in `/v1`, set the protocol explicitly with `protocol: openai`: + +```yaml +mnemon: + embedding: + enabled: true + endpoint: http://127.0.0.1:8080/api + model: bge-m3-mlx-8bit + apiKey: sk-... + protocol: openai +``` + +The Host copies its normal process environment and then overwrites `MNEMON_EMBED_ENDPOINT`, `MNEMON_EMBED_MODEL`, `MNEMON_EMBED_API_KEY`, and `MNEMON_EMBED_PROTOCOL` for the child only (`protocol: auto` injects no protocol variable and leaves Mnemon's `/v1` auto-detection in charge). It does not modify the desktop session, `launchctl`, shell files, or Mnemon's persisted data. Saving swaps to a new runtime graph, so later calls use the new values without restarting DSH. With `enabled: false` or an omitted `embedding` block, dsh-mnemon supplies no override: inherited variables and Mnemon's built-in defaults keep their previous behavior. `MNEMON_EMBED_DIMENSIONS` remains an advanced inherited environment setting. + +The endpoint must be an absolute HTTP(S) URL without credentials, query parameters, or a fragment. Mnemon sends memory and query text to this service; the API key is stored in the DSH settings file like other settings, and a remote plain-HTTP endpoint exposes that text in transit, so use a trusted loopback endpoint or HTTPS. **Test status** runs the effective `mnemon embed --status` command for the current default Store and reports embedding-server reachability, model, the resolved protocol when Mnemon reports one, and embedding coverage without backfilling or changing memories. Save pending edits before testing so the check cannot claim an unsaved value is active. + +### Memory Source switches + +Each memory Source has one switch in **Memory composition**. It turns the Source's DSH Entry on or off through DSH's plugin manager, which saves the choice as the Entry's `disabled` row in the profile patch. On permits the main strategy to use the Source when needed; it does not force recall or writes on every turn. Off stops that Source's context injection, tools, background work and data-plane Web and RPC operations together. + +![Memory composition with one switch per memory Source and enhancement](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/en/plugin-composition.jpg) + +Turning a Source off is reversible routing state, not deletion. Its Memory System page stays, marked **Not running**, and does not read the data plane; Status and the management directories stay observable. Turning it on again uses the original directories and data. The last running Source cannot be turned off. The legacy `memoryTopology.layers..enabled` flag is turned back on with the Source and is used only when the components cannot be read. + +The WebUI reads Source instances from the live management catalog, so a new Source needs no frontend change. Source type ids select configuration; a Strategy selects exact instance keys. Settings update under a revision fence, and candidate compilation must succeed before replacement. Core and Source boundaries recheck capability, scope and current authority. + +### Recall quality policies + +`strict-v1` is the Agent-safe default: for Providers that explicitly declare a normalized 0–1 relevance score, non-positive and below-threshold rows are removed before their content reaches an Agent. It then returns every high-relevance row up to the requested limit, at most four medium-relevance rows, and at most two unscored or unknown-scale rows by default; it does not fill the result limit with weaker evidence. `balanced-v1` retains low-score rows only after primary evidence, and `exhaustive-v1` preserves finite scored rows for direct inspection. An out-of-range score is treated as unknown-scale instead of being fabricated into a confidence value. Cross-provider ordering continues to use reciprocal-rank fusion. + +These pure recall-quality policies belong to the Memory Spaces Source. Configuration selects a policy ID shipped by that Source. Its internal registration function is not a public plugin API; custom retrieval belongs behind the public Source/Provider contracts. Invalid limits, decisions, or selections fall back to `strict-v1`; an unknown configured id rejects the candidate runtime graph. Filtering counts are returned as structured `source.quality` statistics and are not appended to Agent hints. + +### Browser authentication + +DSH owns browser authentication or pairing and Host/Origin validation. Remote pages use the namespaced API Gateway; local loopback clients and DSH desktop windows (`dsh-app://app/`) use their own channels, and an application page counts as remote only when DSH declares a transport for it that does not own the Host. Mnemon's Gateway projection separately enforces `remoteAccess`: `read-only` allows ordinary reads, narrow activation and settings inspection, but rejects writes, ZIP operations, View mutations and settings changes. Settings snapshots report `writable: false` without the `trusted-host` grant. Restart DSH after changing this startup-only policy. DSH `trustedHosts` does not replace HTTPS or deployment access controls. `writeEnabled=false` is a product-level read-only mode, not a substitute for transport authentication. + +For the complete proxy, launch-token, trusted-authority, restart and verification workflow, see [Cloud-hosted WebUI](../guides/operations.md#cloud-hosted-webui). + +## Storage Scopes + +### `global` + +```text +MNEMON_DATA_DIR when non-empty + otherwise ~/.mnemon +``` + +Suitable for users who want Runtime, Documents, and Memory Spaces shared across multiple workspaces. Other Mnemon-enabled agents can also share the Mnemon Memory Spaces when they use the same root. + +### `workspace` + +```text +Agent / tool / lifecycle: resolve(currentSession.header.cwd, ".mnemon") +Web workbench inspection: resolve(workspaceRegistry.get(selectedWorkspaceId).path, ".mnemon") +``` + +Each DSH workspace owns an independent memory root for runtime memory, documents and memory spaces. Conversation Agents, model tools, commands, and lifecycle hooks route by the current session cwd. Independent task Agents launched from the Web workbench instead use the selected Host-registered workspace explicitly; the browser can never submit an arbitrary path. Tidy names and descriptions, Ask Agent, Save to memory, and document archiving therefore target the workspace selected in the Memory System header, even when no main session is selected. + +Headless has no `workspaceRegistry`; its fresh session cwd is the directory from which `dsh --profile headless ...` was launched, so `workspace` resolves directly to `/.mnemon`. + +### Global USER.md with workspace project memory + +To share user-level collaboration requirements across repositories while keeping project facts isolated, set Storage scope to **Workspace** on the `dsh-mnemon` page and User profile scope to **Shared globally** on Runtime Memory's page, or configure: + +```yaml +mnemon: + storageScope: workspace + runtimeUserScope: global +``` + +Each turn then projects `USER.md` from the global root (`MNEMON_DATA_DIR` when set, otherwise `~/.mnemon`) together with `MEMORY.md` from `/.mnemon`. `target=user` mutations and local USER.md compaction go only to the global source; `target=memory`, Documents, Memory Spaces, and Provider state remain workspace-scoped. Global MEMORY.md and workspace USER.md entries stay intact on disk but are not projected in this mode. + +Changing this setting never copies, merges, or deletes entries. Switching back to `runtimeUserScope: storage` reveals the selected root's original USER.md again. A Mnemon Pack still represents one selected storage root, so a workspace Pack does not silently include the separate global USER.md; back up the global root separately when that profile is important. + +### `workspaces` + +This built-in mode is available with the Starter; no additional plugin is required. Select **Centralized · isolated by workspace** as the Storage scope; to set a central root of your own, choose **Custom** under the same group's **Data directory**. + +```yaml +mnemon: + storageScope: workspaces + dataDir: ~/central-memory # optional; otherwise MNEMON_DATA_DIR or ~/.mnemon + runtimeUserScope: global # optional; share only USER.md +``` + +All four areas (`runtime`, `data`, `documents`, `state`) live under `/workspaces//`. Existing symlink aliases resolve to the same ID; different workspace paths remain isolated. A move or rename selects a new ID, with no automatic migration. Sidebar inspection follows the selected registered workspace; the conversation tab and Headless follow the owning session cwd. Global USER.md still uses `MNEMON_DATA_DIR` or `~/.mnemon`, even when the central root is customized. + +Workspace identity resolution allows not-yet-created directory descendants, including Unicode names and descendants below a symlink alias. A path below an existing file is rejected with `ENOTDIR` on every platform, including Windows, before a storage ID is computed. This does not change valid IDs or move, rewrite or remove existing storage. + +Changing scopes never migrates, merges or deletes an old root. A ZIP Pack still contains only the selected workspace root; back up the whole central directory to preserve all workspaces. Remote Provider namespaces retain their own sharing semantics. + +### `custom` + +```yaml +mnemon: + storageScope: custom + dataDir: /absolute/path/to/mnemon-data +``` + +`~` and `~/...` are also allowed. Relative paths are rejected. + +### Choose a Cross-Agent Sharing Scope + +| Goal | Recommended scope | Notes | +|---|---|---| +| Share durable memory among local agents | `global` | Every participant uses `~/.mnemon` or the same `MNEMON_DATA_DIR` | +| Share one explicit data root | `custom` | Every participant configures the same absolute directory for isolation and backup | +| Share only inside one project | `workspace` | Every participant aligns its Mnemon root to that project's `/.mnemon` | + +Mnemon Native interoperates with other Mnemon-enabled agents through `data//mnemon.db`; third-party engines interoperate through their configured provider scope. Runtime, Documents, DSH activation state, and UI metadata remain managed by dsh-mnemon. See [Long-term memory providers](../guides/memory-providers.md). + +External service settings, Memory Space scope settings, and secrets are stored in `state/memory-providers.json` under the selected scope root, not in the profile patch. Multiple Memory Spaces reuse one provider service configuration; the Host merges both layers only at runtime. The Mnemon Native ZIP contains only Runtime, Documents, and native Memory Spaces; external service data, credentials, and local third-party stores are excluded. + +## CLI Discovery Precedence + +```text +config.cliPath + -> executable MNEMON_CLI_PATH + -> each PATH directory + -> Windows: GOBIN/mnemon.exe + first GOPATH/bin/mnemon.exe, or ~/go/bin/mnemon.exe + %LOCALAPPDATA%/Programs/mnemon/mnemon.exe + %ProgramFiles%/mnemon/mnemon.exe + -> Unix: ~/.local/bin/mnemon + /opt/homebrew/bin/mnemon + /usr/local/bin/mnemon + /usr/bin/mnemon +``` + +An explicit `cliPath` accepts either a path or a command name on PATH (for example, `mnemon`). Status checks and execution share the discovery rules and recognize CLI installation/removal on recheck without restarting DSH. If the explicitly configured command is unavailable, calls report a launch error rather than silently selecting another CLI. Windows discovery accepts regular `.exe` files and the verified official npm `mnemon.cmd` launcher. npm launchers are invoked through Node without a shell; arbitrary `.cmd` and `.bat` wrappers remain excluded. After migrating to npm, update any explicit CLI override to the npm launcher and verify the executable path in **Status → Check versions**. + +## Compatibility Store Hint Precedence + +```text +config.store + -> MNEMON_STORE + -> /active + -> default +``` + +After the Memory Space directory has been established, long-term semantic operations use explicit Memory Space IDs and do not rely on the global active Store for routing. + +## Background Task Agent Model Route + +Tidy names and descriptions, Ask Agent, Save to memory in the Memory System and in conversations, and document archiving create a clean independent top-level task Agent. It uses the selected workspace as its cwd, works even when no main Agent session is selected, and is disposed after the task finishes. + +The default `inherit` mode first uses the DSH Provider / Model selected for new sessions, then falls back to a complete route from the current available main Agent. Choosing **Choose a model** under **Background tasks → Task Agent model** on the Layered strategy's page stores a complete Provider + Model and overrides only Mnemon background tasks; it does not change the conversation Agent. When semantic judgment requires a bounded worker inside that task Agent, the worker inherits the task Agent route. + +```yaml +mnemon: + taskAgentModel: + mode: fixed + provider: deepseek + model: deepseek-chat +``` + +DSH's live catalog reports each model's input modalities, and the picker labels image-capable models **Image input**. Choosing one does not make Mnemon background jobs ingest images: Tidy names and descriptions, Ask Agent, Save to memory, smart selection and document archiving still submit text and bounded evidence. In the main conversation, DSH-owned image blocks keep their attachment references when dsh-mnemon adds lifecycle guidance, while activity thresholds count text blocks only. Raw image bytes are never copied into runtime memory, documents or memory spaces. + +## Provider Requirements + +Regular workers prefer `spawn`. If no provider has that name, another provider with all of the following capabilities can be selected: + +```text +toolFilter = true +persona = true +depthLimit = true +``` + +Background review defaults to a non-inheriting `spawn` provider and an explicit, bounded checkpoint from the parent's current public surface. It includes whole visible text messages and successful tool results; omitted or unavailable context is not reconstructed. `idleReview.provider: fork` retains full-parent-context review and requires `inheritsParentContext=true`. If fork is missing or incompatible, `idleReview.fallback` chooses `spawn` or `skip` before startup. A started run is never retried through another provider. + +Review requires local child publication, `agents.isOwnedBy`, and `agent.ctx.tools.guard`; the guard also blocks unrelated own-scope plugin tools and Code Mode subcalls. `maxContextChars` bounds the spawn checkpoint, while `maxTokens` limits each model response, not the inherited fork input or total multi-step usage. + +**What review writes.** Each piece of knowledge goes to one layer. Project records, such as a design, implementation details, paths, ports, scope agreements or a handoff, go to at most one Document. Working memory (MEMORY.md) takes only a compact rule the user stated, such as a convention, a correction, an environment fact or a tool quirk, that no Document or existing entry already covers; USER.md takes identity and preferences. The guard holds each pass to one layer: after a Document, working-memory changes are refused, and after a working-memory change, a Document is refused; USER.md changes stay independent. `idleReview.runtimeMemory: false` withholds `mnemon_runtime_memory` from review, so it only creates Documents. Review never writes Memory Spaces: working memory reaches them through capacity archiving and Documents through cold archiving, while **Save to memory** and `mnemon_remember` write them directly. + +`idleReviewMs` remains the continuous-idle debounce. A separate `minIntervalMs` spaces attempts, including failures and cancellations. `maxPerSession` caps attempts for the loaded parent Agent; zero suspends review. Clearing or compacting the context (DSH replaces the Agent), restarting the Host, or unloading and reopening the Agent starts a new in-memory budget. Completed runs are disposed through DSH's public API. Persisted session history is retained: the published Host provides no plugin-scoped archive/TTL contract, and Mnemon never deletes session files or other plugins' agents. + +**Agent Teams compatibility:** `idleReview.agentTeams: pause` remains the default for existing profiles. It pauses before child creation when both the public `agentTeams` service and parent-scoped `spawn_teammate` tool are present. TeamService alone does not pause review. + +With DSH and all official Agent Teams components at **0.1.7-rc.2**, select **Scoped child review** under Idle review on the Layered strategy's page (**Plugins → dsh-mnemon**), or set `idleReview.agentTeams: scoped`. That published Team policy supports both bounded spawn and explicit fork. The opt-in retains parent ownership checks, local child publication, `maxDepth: 1`, and the monotonic review-tool allowlist, including Code Mode dispatch. Team tools and further delegation stay denied to the reviewer; the parent keeps Teams. No package-version guess or other plugin policy removal is used. Missing guard/ownership support or a policy error fails the run without fallback replay. Keep `pause` to leave review off while Team tools are installed, or use `idleReview.enabled: false` to disable only review while keeping Teams, recall and explicit writes. + +Bounded spawn reads live-user evidence from the public flat `user/message` event payload. It includes only whole visible messages before the completed checkpoint; injected recall, summaries and messages without a live-user source are not promoted to user assertions. This preserves explicit decisions and no-write instructions within the configured character budget. + +Failed reviews remain failures. The workspace shows the child run id and committed mutation receipt metadata when writes happened before failure, including a committed inner tool followed by a failed Code Mode wrapper. No rollback or automatic replay occurs. Inspect the run and the listed document ids or Runtime revisions before any manual retry. A later review still respects the cooldown and session budget. To disable only this maintenance pass, turn off **Idle review** on the Layered strategy's page, or set `idleReview.enabled: false`. + + +## Read-Only Configuration + +```yaml +mnemon: + writeEnabled: false +``` + +Effects: + +- Model write tools are not registered; +- write and activation RPCs remain registered, with memory mutations rejected at the Host boundary; +- `/mnemon remember` and `/mnemon forget` are rejected; +- Source management and model actions cannot bypass the Host write restriction. + +This is feature-level read-only behavior, not a read-only filesystem mode: the Runtime controller may still initialize or repair projections, Document search updates LRU access times, and Mnemon read commands may trigger upstream database migrations. Do not treat `writeEnabled=false` as a safety guarantee for read-only mounts. + +## Switch Interactions + +```text +writeEnabled=false + -> overrides all explicit semantic writes + +writebackMode=off + -> no write cue, no scored review + -> explicit writes remain when writeEnabled=true + +recallMode=off + -> no recall cue + -> explicit recall remains + +lifecycleEnabled=false + -> no lifecycle cues or review + -> UI, commands, and explicit tools remain + +routingGuidance=false + -> removes only mnemon:routing + -> runtime-memory context remains +``` + +## Entry Placement: `displayMode` and `tabEnabled` + +Memory System defaults to Sidebar, opening a dedicated center-column workbench with a minimal, logo-free skin aligned with official DSH panels. Set `displayMode: builtin`, or choose **Conversation tab** under **Interface → Memory System opens in**, to put that same workspace in the current conversation's `conversation.view` tab instead. Pages, navigation, dialogs, and styling remain shared; there is no separate builtin UI. Theme authors can use the [supported surface selectors and custom properties](../guides/ui-guide.md#theme-skin-overrides) in either placement. + +The sidebar entry follows DSH's native Plugins row for styling, collapsed icons and selection. Clicking it again keeps the workspace open; **Back to chat** or Escape returns to the current conversation. Selecting Plugins, another native panel or New Session switches the main panel. Switching to the task board or SSH synchronizes both visibility and entry state, so a missed peer activation notification cannot prevent reopening Memory System. + +The conversation tab omits the header's storage-mode badge, workspace picker and alignment controls. Every read, write, and independent task request follows its owning session through the existing Host routing: + +| `storageScope` | Conversation-tab read/write root | +|---|---| +| `global` | Shared `MNEMON_DATA_DIR` or `~/.mnemon`, regardless of the session workspace | +| `workspace` | The current session's `/.mnemon`; switching conversations follows their respective workspaces | +| `custom` | Configured `dataDir`, regardless of the session workspace | +| `workspaces` | The current session’s subtree under `/workspaces//` | + +The existing `runtimeUserScope: global` exception still keeps USER.md global. Changing placement does not change scope, migrate memory data, or revive the old builtin navigation. A placement change applies when selected. + +The canonical spelling is **`builtin`**. Historical `displayMode: buildin` preferences are accepted, and runtime and UI state normalize them to `builtin`. On startup and external settings changes, the Host rewrites that one field through DSH's revision-fenced settings writer. Old-client RPC writes also persist `builtin` directly. Other fields and document comments are preserved; an explicit newer Sidebar choice wins a concurrent migration. + +Saving on the `dsh-mnemon` page under Plugins updates the current UI immediately. On loopback pages the Client also follows DSH's settings document: a save from another window, or a profile edit the Host reloads, shows up without a browser reload. Remote pages do not receive DSH's settings mirror; reload them to observe changes made elsewhere. + +If the old value comes only from a composition profile, migration saves a canonical user-setting override instead of rewriting the profile file. A read-only settings provider still recognizes the alias but is not written; a persistence failure is reported in the Host log without disabling the normalized entry. + +`tabEnabled=false` removes the selected entry and workbench live; enabling it again restores the configured placement. The two entries are mutually exclusive. Host RPC, commands, and tools remain registered, so an Agent or command already in progress stays valid. The turn memory bar and Save to memory stay controlled by their own Interface switches and navigate to the selected placement. + +## Profile Patch Overrides + +The bundled `cordis.patch.yml` provides the default config row. A DSH profile configuration with the same ID may replace that row as a whole. Do not add only `cliPath` to a final profile patch: use `MNEMON_CLI_PATH` or the `mnemon.cliPath` user setting instead. When a profile patch must be customized for another reason, retain every key that must remain enabled instead of assuming a deep merge. + +A `remoteAccess` override for authenticated Gateway management is one such whole-row customization. Use the complete, upgrade-aware example in the [remote management procedure](../guides/operations.md#remote-management), preserving the rest of the current configuration; a standalone `config: { remoteAccess: trusted-host }` fragment replaces those other fields. + +## Common Configurations + +Workspace isolation: + +```yaml +mnemon: + storageScope: workspace +``` + +The same workspace isolation inside each conversation: + +```yaml +mnemon: + storageScope: workspace + displayMode: builtin +``` + +An explicit Windows CLI path: + +```yaml +mnemon: + cliPath: 'C:\Users\alice\AppData\Local\Programs\mnemon\mnemon.exe' +``` + +A custom data volume and a longer CLI timeout: + +```yaml +mnemon: + storageScope: custom + dataDir: /Volumes/AgentData/mnemon + timeoutMs: 30000 +``` + +Keep explicit tools while disabling lifecycle behavior: + +```yaml +mnemon: + lifecycleEnabled: false +``` + +Disable only background writeback decisions: + +```yaml +mnemon: + writebackMode: off +``` diff --git a/dsh/docs/en/reference/interfaces.md b/dsh/docs/en/reference/interfaces.md new file mode 100644 index 000000000..7175ebcda --- /dev/null +++ b/dsh/docs/en/reference/interfaces.md @@ -0,0 +1,237 @@ +# Web, Headless, Tools, Commands, and RPC + +[简体中文](../../zh-CN/reference/interfaces.md) | **English** | [Documentation hub](../README.md) + +This page is an integration reference. For daily use, start with the [Sidebar and conversation UI guide](../guides/ui-guide.md). + +## Memory space naming and compatibility + +The product term is **memory space** (Chinese: **记忆空间**). The Source exports `MemorySpace`, `MemorySpaceView`, `MemorySpaceCatalog`, and corresponding request and metadata types. Previously published `MemoryBody*` types remain deprecated aliases with the same shapes. + +Existing v0.5.x identifiers such as `mnemon_memory_bodies`, `mnemon_memory_body_create`, `body-directory`, and `memoryBodyId` / `memoryBodyIds` still refer to memory spaces. They remain stable for installed tools, Source pages, Provider adapters, and persisted Document/Pack lineage. The `.dsh-memory-bodies.json` filename and its `bodies` field are also retained. Renaming the product does not rename user-created spaces, change their IDs, move databases, or alter access grants. + +## User-facing entry points + +| Entry | Default | Description | +|---|---:|---| +| Sidebar | Yes | The Memory System workbench: Status, Runtime, Documents and Memory Spaces | +| Conversation tab | No | The same workbench inside a conversation, scoped to that session | +| Turn memory | Yes | Memory-tool summary under a completed turn, with links to the matching page | +| Save to memory | Yes | Action beside finalized assistant replies; confirmation invokes supervised writing | +| dsh-mnemon's page under Plugins | Yes | Memory composition, component pages, storage, interface and backup | +| `/mnemon` | — | Conversation command entry | +| Model tools | — | Structured Root Agent read/write entry | + +`displayMode` selects `sidebar` (default) or `builtin`, the conversation tab; `tabEnabled` controls whether the selected entry appears. The conversation tab does not show the Sidebar's scope controls. The two conversation shortcuts are switched independently under **Interface**, stored through the `mnemon-ui` settings scope. + +## Profile surfaces + +| Capability | Web | Headless | +|---|---:|---:| +| Runtime context and lifecycle guidance | Yes | Yes | +| Model tools and independent task Agents | Yes | Yes | +| Agent-cwd routing for `workspace` scope | Yes | Yes | +| Sidebar / conversation actions | Yes | No | +| Host-to-client RPC | Yes | No | +| Delayed score-based review after Agent idle | While the Host remains alive | Cancelled when the one-shot process exits | + +Headless receives the full model-tool surface. Its task argument is submitted as an ordinary user message, so it does not provide an interactive slash-command dispatcher. Explicit and model-guided writes that finish before the Agent becomes idle are durable. + +Model tools, lifecycle hooks, and system scheduling use an `automatic` trigger. User-initiated data-plane operations over Web/RPC use `manual`. Setting a Source to `manual` therefore preserves direct management while denying model tools and automatic projection. `memory-system` and `status` are control-plane observations and remain readable even when a Source is disabled. + +## Model tools + +### Read-only tools + +| Tool | Purpose | Root Agent path | +|---|---|---| +| `mnemon_status` | Aggregated CLI, configuration, storage, and directory status | Direct service | +| `mnemon_memory_bodies` | Read catalog, provider capabilities, health, and available statistics | Direct service | +| `mnemon_recall` | Recall from active providers with heterogeneous rank fusion | Direct Host service under pinned Source authority | +| `mnemon_related` | Traverse only when `capabilities.related=true` | Direct Host service under pinned Source authority; Root defaults to two hops | +| `mnemon_document_search` | Deterministically search managed Documents | Documents control layer | + +“Read only” means managed bodies and durable semantics do not change. `mnemon_document_search` still updates `lastAccessedAt` for LRU ordering, so feature read-only is not disk read-only. + +### Tools available with `writeEnabled=true` + +| Tool | Purpose | Root Agent path | +|---|---|---| +| `mnemon_runtime_memory` | `add` / `replace` / `remove` hot memory | Deterministic control; add overflow may start a worker | +| `mnemon_document_manage` | Create, update, or archive a Document | Create/update deterministic; archive uses a worker | +| `mnemon_document_create` | Create a separate Document without updating or archiving existing ones | Deterministic Source `create` Action; available to idle review | +| `mnemon_remember` | Retain one insight under Provider semantics; distinguish acceptance from durable completion | `spawn` write worker | +| `mnemon_link` | Create a typed relationship where the provider supports it | `spawn` write worker | +| `mnemon_forget` | Delete an exact ID where the provider supports it | `spawn` write worker for explicit operations only; excluded from autonomous distillation | +| `mnemon_memory_body_create` | Let an Agent create a Mnemon Native space; third-party connections remain user-managed in WebUI | `spawn` write worker | +| `mnemon_memory_body_update` | Update name, description, or active state | `spawn` write worker | +| `mnemon_memory_body_merge` | Non-destructively merge Mnemon Native spaces | `spawn` write worker | + +When a worker invokes the same tool name, it reaches the service directly and is not delegated recursively. + +Autonomous write workers — distillation and supervised writeback runs — decide content on their own, so the host excludes `mnemon_forget` from their hard tool allowlist: a duplicate or conflict is resolved by skipping or storing a corrected entry, never by deleting the existing one. Operations whose request names an exact target supplied by the user or a user-facing flow (`/mnemon forget`, `mnemon_link`, and Memory Space management) keep the full write toolset. + +Idle review can search Documents and call `mnemon_document_create`, but cannot call `mnemon_document_manage` or `mnemon_view_action`. It skips covered candidates or creates one separate document referencing existing document ids. The Source's `create` Action accepts `title`, `content`, optional `description`, `sourcePaths`, and `sessionIds`; it rejects `action`, `id`, and other unknown fields. Capacity exhaustion rejects creation without archiving an existing document. This protection applies to existing user- and Agent-created documents. Explicit editing continues through the existing management paths. + +`mnemon_runtime_memory` accepts an optional `branches` array (git branch names) for `target=memory` writes. Branch-scoped entries are projected into the per-turn Runtime snapshot only when the session's workspace is checked out on a listed branch; untagged entries are always projected. On `replace`, providing `branches` changes the scope, an empty array clears it, and omitting it keeps the current scope. The parameter is rejected for `target=user`. + +## Tool admission + +- **Runtime**: explicit preferences, stable project conventions, environment facts, and high-frequency lessons. +- **Documents**: designs, investigations, procedures, postmortems, or handoffs with complete structure and rationale. +- **Memory Spaces**: stable facts, decisions, and insights that must survive across tasks or benefit from graph relationships. +- **Skip**: questions, guesses, temporary progress, completion logs, raw output, secrets, and ordinary repository facts that are easy to rediscover. + +`mnemon_forget` is a destructive semantic action. Use it only on explicit request or after confirming that content is wrong or obsolete. The host enforces this for autonomous workers by excluding the tool from their allowlist; the main agent can still call it when the user explicitly asks. + +## `/mnemon` commands + +```text +/mnemon +/mnemon status +/mnemon recall +/mnemon related +/mnemon remember +/mnemon forget +``` + +- Empty `/mnemon` equals `status`. +- `status` is deterministic and starts no model. +- `recall` and `related` read through the live Agent's scoped Source route without spawning a worker. `remember` and `forget` use that Agent as the write worker's parent. +- Command recall returns at most 10 results. +- `forget` requires one exact ID without spaces. +- `forget` reports deletion only for a `forgotten` receipt; skipped, failed, or unconfirmed results are reported as errors with the worker summary. + +## UI contributions + +Everything Mnemon shows inside DSH is registered into DSH's own UI regions. The conversation contributions are additive and replace no official DSH rendering. + +| DSH region | Registration | Behavior | +|---|---|---| +| `conversation.chat.turnTail` | list, `id=dsh-mnemon/turn-tail` | `turn-activity` summarizes `mnemon_*` calls from completed turns, with the items each call read or wrote from the tools' activity metadata; open turns and turns without activity render nothing | +| `conversation.chat.assistant-actions` | list, `id=mnemon-save` | `assistant-message` reads finalized text; `supervise` runs only after confirmation | +| `conversation.session.header.lineage` | lower-priority copy of DSH's own entry | A task Agent's session header counts that Agent's own tokens, not the log it was forked from | +| `plugins.bundle.config` | keyed, `dsh-mnemon` | The whole configuration on dsh-mnemon's page under Plugins | +| `plugins.row.config` | keyed, `dsh-mnemon#` | The page each shipped component's row opens, with the settings that component contributed | +| `plugins.detail.actions` | list, `id=dsh-mnemon/open-workspace` | Opens the Memory System from the plugin's detail page | + +Mnemon declares two keyed regions of its own, both keyed by a component's package name: `mnemon.component.settings`, rendered on that component's page, and `mnemon.component.status`, its card on the Status page. Official and third-party components contribute through the same regions; see [Plugin development](../development/extensions.md). + +The assistant-message candidate is editable and long replies are bounded by the UI preview limit. Confirmation starts an independent task Agent, and persistence is complete only after its settled receipt. + +## Workspace routing + +Web workbench requests carry `sessionId` and an optional `workspaceId`. The Host accepts only IDs registered in `workspaceRegistry`: + +- deterministic reads and manual maintenance may route to the inspected root selected by `workspaceId`; +- Agents, tools, commands, and lifecycle hooks still route by the Agent cwd associated with `sessionId`; +- `status.workspaceContext` returns selected / effective roots and `aligned`; +- independent task Agents started from the workbench use the inspected workspace; they do not require the main conversation to match. Conversation tools continue to use their own session cwd and pinned View, not the workbench inspection scope. + +Profiles without a Web workspace registry, including Headless, have no arbitrary inspection target. Agent execution still routes `workspace` scope directly from the session cwd. + +## RPC channels + +RPC is an internal Host-to-client bridge, not a stable external HTTP API. Pages and component plugins should use the scoped clients from `dsh-mnemon/client`; see [Plugin development](../development/extensions.md). + +| Channel | Carries | Remote pages | +|---|---|---| +| `/dsh-mnemon-read` | Status, directories, search and conversation reads | Allowed | +| `/dsh-mnemon-activation` | Turning one Memory Space on or off | Allowed | +| `/dsh-mnemon-write` | Every other mutation | Needs `remoteAccess: trusted-host` | +| `/dsh-mnemon-pack` | Backup export and import | Needs `remoteAccess: trusted-host` | +| `/dsh-mnemon-settings` | Host and interface settings | `get` allowed; `mutate` needs `remoteAccess: trusted-host` | +| `/dsh-mnemon-view` | Memory composition reads | Allowed | +| `/dsh-mnemon-view-settings` | Saving memory composition, installing a component | Needs `remoteAccess: trusted-host` | + +Loopback pages call these channels directly, authenticated by the DSH browser session. Remote pages reach the same handlers through DSH's API Gateway: channel `/api`, endpoints `dshMnemon/read`, `dshMnemon/activation`, `dshMnemon/write`, `dshMnemon/pack`, `dshMnemon/settings`, `dshMnemon/view` and `dshMnemon/viewWrite`. The Gateway owns Host/Origin validation, browser pairing and the response envelope; Mnemon adds only the `remoteAccess` grant shown above, captured at startup. See [Remote management](../guides/operations.md#remote-management). + +### Read channel + +| Endpoint | Behavior | +|---|---| +| `status` / `status-summary` | Service, version, lifecycle, Documents, storage and workspace context, and the current Memory System descriptor; `status-summary` answers without waiting for any Provider I/O | +| `memory-system` | Serving/candidate evaluation, sanitized Source instance descriptors and current participation configuration | +| `versions` | Installed and latest Mnemon CLI and dsh-mnemon versions and their installation sources | +| `task-agent-models` | Models available to independent task Agents | +| `runtime-memory` | Runtime snapshot | +| `documents` / `document` / `document-search` | Directory, body, and deterministic search | +| `graph` / `bodies` / `body-directory` | Active multi-space graph projection, provider-capability catalog, and fast directory projection | +| `body-reconnect` | Invalidate transient health state and refresh one Memory Space without changing persistent data | +| `provider-services` | Redacted Provider service catalog; configured-secret names may be present, secret values never are | +| `embedding-status` | Mnemon's embedding model, availability and default-store coverage | +| `list` / `entities` | Durable content list and entity aggregation | +| `search` / `agent-search` / `related` | Direct retrieval, evidence answer, and relation traversal | +| `turn-activities` / `turn-activity` | Session-wide or single-turn memory-tool activity | +| `assistant-message` | Finalized assistant text by messageId | +| `source-management-catalog` / `source-management-read` / `source-assistance` | Generic Source management: instance catalog, declared read operations, and a read-only assisted search | + +### Activation channel + +`/dsh-mnemon-activation` has a narrower request schema than the write channel. Its `body` endpoint accepts only `memoryBodyId`, a Boolean `active`, and the normal session/workspace routing fields; `source-assistance` accepts the same two fields as a confirmed `activation` operation on a Memory Spaces instance. Both control participation in DSH reads and routing without accepting metadata, Provider connection, credential, deletion, or durable-memory mutations. Read-only mode rejects them at the Host boundary. + +### Write channel + +| Endpoint | Behavior | +|---|---| +| `runtime-memory` | Hot-memory mutation | +| `document` | create / update / archive | +| `supervise` | Process a candidate under an independent task Agent and return a settled receipt | +| `remember` / `link` / `forget` | Durable semantic write, relation, and soft deletion | +| `body-create` / `body-update` / `body-delete` / `body-merge` | Create or connect, edit, confirm Native deletion or remote disconnection, and merge Memory Spaces | +| `body-metadata-maintain` | Let a task Agent refresh names and descriptions of 1 to 20 active Memory Spaces | +| `provider-service-update` | Update one Provider service; credentials remain on the Host and the response is redacted | +| `source-management-mutate` / `source-assistance` | Generic Source management mutation, and Host-assisted operations confirmed against the Source's current revision | +| `version-update` | Update a named component with Host-fixed commands and arguments | + +`provider-services` returns a redacted catalog through the read channel. The settings editor knows which credential fields are configured; it can replace or explicitly clear them without reading saved values back. + +With `writeEnabled=false`, the activation and write channels stay registered but mutations are rejected at the Host boundary. The browser also disables mutation controls from the Host's settings snapshot before transport. + +### Backup channel + +| Endpoint | Behavior | +|---|---| +| `target` | Effective root, scope, and the default root a Default location resolves to | +| `export` | Export a complete ZIP with manifest and SHA-256 checksums | +| `inspect` | Parse and verify an import ZIP, returning component and occupancy preview | +| `import` | Safely merge into the effective root; rejected in read-only mode | + +Backups contain private memory, so callers must treat the authenticated DSH browser session as a full Host authority and protect exported archives separately. + +### Settings channel + +`/dsh-mnemon-settings` offers `get` and `mutate` for two namespaces: `mnemon` owns Host and storage settings, and `mnemon-ui` owns `turnBar` and `saveAction`, saved as `conversationInteraction`. Mutations carry settings revisions so concurrent edits are not overwritten. dsh-mnemon's page under Plugins (`plugins.bundle.config`) reads and writes through this channel on loopback and remote pages alike. On loopback pages it also follows DSH's `ctx.configForms` revision of the `mnemon` entry and re-reads the channel when another page or a profile reload changes it. + +### Memory composition channels + +| Channel | Endpoint | Behavior | +|---|---|---| +| `/dsh-mnemon-view` | `dashboard` | Installed components, the selected main strategy, the current conversation's View and turn activity, and whether components can be installed here | +| `/dsh-mnemon-view` | `preview` | Validate a proposed composition against the current revision without saving it | +| `/dsh-mnemon-view` | `inspect-plugin` | Read a component package's registry manifest: version, `dsh-mnemon` peer range and whether this Profile has it | +| `/dsh-mnemon-view-settings` | `apply` | Save a confirmed composition: main strategy, component switches and options | +| `/dsh-mnemon-view-settings` | `install-plugin` | Install a confirmed package version into the current Profile with the DSH CLI | + +`apply` stores the strategy and each component's options in the `mnemon-view` scope, saved as `memoryView`; DSH's plugin manager records the component switches in the profile patch. See [Configuration](./configuration.md). + +## npm exports and extension service + +Core publishes `ctx.mnemonMemory: MnemonMemoryService`; the actual service exposes only `installContributions`. Source/Strategy authors use `inject = ['mnemonMemory']` and `installMemory`, without receiving an engine, controller or registry. Public subpaths are defined by each package's `package.json#exports`; Sources may provide their own `./contracts` and optional `./client`. + +| Entry | Responsibility | +|---|---| +| `dsh-mnemon` | DSH Host and default Starter | +| `dsh-mnemon/core` | Source-neutral `ctx.mnemonMemory` service, without Host/UI | +| `dsh-mnemon/contracts` | JSON-safe manifests, facts, ViewSpec, View, Evidence and Receipt | +| `dsh-mnemon/extension-sdk` | Source/Strategy definitions, lifecycle installation and validators | +| `dsh-mnemon/testing` | Real Cordis composition fixture and built Client artifact loader | +| `dsh-mnemon/client` | DSH workspace and Source-page SDK | +| `dsh-mnemon-source-memory-spaces/provider-sdk` | Memory Spaces' own Provider child-module contract | +| `dsh-mnemon-source-memory-spaces/testing` | Provider driver fixture | + +Generic model tools `mnemon_view_route` and `mnemon_view_action` execute only routes/offers present in the current View. Existing named tools preserve the default workflow. Browser management uses `source-management-catalog`, `source-management-read`, `source-management-mutate` and optional `source-assistance`; instance identity, confirmation, revision and current authority are checked by the Host/Source. Internal RPC names are not the plugin SDK: use the scoped page client. See [Plugin development](../development/extensions.md). + +## Internationalization + +The main Sidebar workbench, the configuration page under Plugins, and conversation entries support Chinese and English and follow DSH locale live. Brand names, tool names, and configuration keys are not translated. `/mnemon` commands, model-tool cards, some Host errors, and compatibility metadata remain partially untranslated. diff --git a/dsh/docs/en/reference/storage-model.md b/dsh/docs/en/reference/storage-model.md new file mode 100644 index 000000000..1a92c7639 --- /dev/null +++ b/dsh/docs/en/reference/storage-model.md @@ -0,0 +1,221 @@ +# Storage and the Memory Model + +[简体中文](../../zh-CN/reference/storage-model.md) | **English** | [Documentation Center](../README.md) + +## Why three kinds of memory + +The default Starter combines three independent Sources for different access patterns, and the Layered strategy composes them into each turn. This is a useful default, not a Core requirement or a restriction on third-party Sources: + +| Question | Where it lives | Reason | +|---|---|---| +| What must be known directly on the next turn? | Runtime Memory | Tiny and injected directly into the prompt | +| Which design or procedure needs to be read quickly and in full? | active Documents | Preserves Markdown structure without deep recall | +| Which historical facts and relationships should persist across sessions? | Memory Spaces | Independent databases, graph relationships, and on-demand recall | +| What happens when a long document is rarely used but must remain traceable? | archived Documents | Mnemon retains an index while the cold layer retains the original text | + +Recommended query gradient: + +```text +current request and repository facts + | + v +Runtime Memory already in prompt + | + v +search active Documents + | + v +recall active Memory Spaces + | + v +follow an exact cold reference when full text is required +``` + +## Cross-agent sharing boundary + +Sharing does not broadcast conversations or files. Mnemon-enabled agents can share durable Native facts when each integrates Mnemon and can access the same storage root and recognized Stores. Third-party Providers share through their own workspace, user, bank, project or container scope. Runtime and Documents do not automatically enter other agents' context. + +Use `global` for a common local root, `custom` for an explicitly agreed root, or `workspace` to constrain sharing to a project. Concurrent processes rely on the Provider's own consistency guarantees; stop all users before offline copying or directly modifying local databases. See [Operations](../guides/operations.md#backup-and-recovery). + +## Unified Root Directory + +```text +/ ++-- runtime/ +| +-- memories.json +| +-- USER.md +| +-- MEMORY.md ++-- documents/ +| +-- index.json +| +-- active/ +| +-- archived/ ++-- data/ +| +-- .dsh-memory-bodies.json +| +-- / +| +-- mnemon.db ++-- state/ + +-- memory-providers.json # third-party connection control plane; 0600; excluded from Mnemon Packs +``` + +`storageScope` determines the entire root, not just the Mnemon databases. The `workspace` scope resolves an independent `/.mnemon` for every registered DSH workspace. The opt-in `runtimeUserScope=global` is the sole split-root exception: Runtime reads USER.md from the global root while MEMORY.md and every other component remain under the selected root. Workbench tasks use the inspected workspace; conversation tools and lifecycle hooks use their owning session's cwd and pinned View. `state/memory-providers.json` stores third-party endpoints, target URIs, identities, and optional credentials. Its mode is `0600`; the Host returns configured field names, never saved credential values. + +The `workspaces` layout keeps all four areas under `/workspaces//`; Host path resolution never creates files or changes old roots. Only explicit `runtimeUserScope: global` places USER.md outside that workspace subtree. + + +## Runtime Memory + +### Semantics + +- `target=user`: identity, role, long-term preferences, habits, communication style, and explicit collaboration requirements. +- `target=memory`: projects, environment, decisions, conventions, tool characteristics, and reusable experience. +- `importance=critical|normal|low`: recorded importance, visible in the model projection and used for retention priority during maintenance. +- `branches` (optional, `target=memory` only): a list of git branch names limiting where the entry is projected in the per-turn Runtime snapshot; entries without a branch list are visible on every branch. + +There is currently no `daily` target. + +### Source of Truth and Projections + +Within one root, `runtime/memories.json` is the sole source of truth. With `runtimeUserScope=global`, the effective snapshot combines only `target=user` entries from the global source with only `target=memory` entries from the selected source. Both JSON files and both Markdown projection pairs remain complete and unchanged; filtering occurs only in the effective Runtime controller. Each record contains: + +```text +content +created_at +updated_at +target +importance +branches (optional) +``` + +`branches` holds an optional list of git branch names for `target=memory` entries. An entry without `branches` (or with an empty list) is visible on every branch. When a session's workspace is a git working tree on branch `B`, the per-turn Runtime snapshot hides `memory` entries whose `branches` list does not include `B`; in non-git workspaces and at detached HEAD, every entry is projected. `USER.md` entries never carry `branches`. + +`USER.md` and `MEMORY.md` are deterministic derived files of the complete store. Each item is normalized to one line, and items are separated by a line containing only `§`; `§` is a reserved character. During startup and prompt assembly, the control layer repairs missing or manually modified projections from the JSON source. Branch filtering applies only to the prompt projection, never to these files. + +The Runtime Source adds a metadata line before each entry in the model-facing snapshot: + +```text +[importance=critical; created=14d; updated=2d] +Prefer concise replies. +``` + +`created` and `updated` are elapsed whole 24-hour days since the stored timestamps, computed once when the projection is captured. Ages below one day are `0d`; future timestamps show `future`, and unparseable timestamps show `unknown`. One capture time covers both roots when the user profile is global. The current turn retains its captured ages; the next turn recalculates them even if the storage revision has not changed. + +These lines annotate the unchanged entry content. `old_text` / `oldText` must match content only, excluding the metadata line. Recorded importance and age never outrank current instructions. JSON, on-disk Markdown, content matching, entry order and storage capacity remain unchanged. Annotations count toward the existing model projection character budget, so even the default Strategy can truncate or omit entries when there are many short records. Its guidance states that snapshots are budget-limited and that absence does not mean deletion; light-context can impose a smaller budget. + +### Operations + +- `add` writes an independent new fact; exactly identical content is not added twice. +- `replace` and `remove` first match the normalized full content of `old_text` within the requested `target`, then replace or remove that entire item. One exact match takes precedence even when other items contain the same text. +- When no full-content match exists, a unique substring still locates the item. +- Zero matches, duplicate exact matches, and ambiguous substring matches are rejected without changing the store; no fuzzy mutation is performed. Branch tags control projection and are not mutation selectors. + +### Capacity + +| Target | Default limit | Maintenance method | +|---|---:|---| +| `USER.md` | 4 KiB | A local no-tool worker merges conservatively; content never enters a Memory Space | +| `MEMORY.md` | 10 KiB | The Host archives exact committed entries, then deterministically packs the hot remainder | + +Both limits are configurable as `runtimeMemory.userLimitBytes` and `runtimeMemory.memoryLimitBytes`; see [Runtime Memory budgets](./configuration.md#runtime-memory-budgets). With the Layered strategy, capacity maintenance starts when an authorized write would exceed the limit; other strategies reject that write instead. Named tools, generic Actions, background child Agents and browser management share the same Host workflow. Browser writes use the selected storage scope without requiring an open user conversation. Archive failures preserve hot memory and return an error. Standalone Sources retain their own storage semantics; custom Strategies do not implicitly inherit default archival. + +Capacity is measured from the stored content and entry delimiters in UTF-8 bytes, excluding prompt-only metadata. A single item is limited to 8 KiB. On an overflowing `add`, `replace`, or `remove`, the Host rechecks the source revision before any Provider write. With one eligible writable Memory Space it routes without a model; with several spaces, workers see only bounded routing excerpts and return destination ids, never rewritten memory. Mnemon Native reuses exact content from a readonly namespace snapshot, groups identical pending entries, and imports the remaining originals once per destination through a schema-v1 draft with `--no-diff`. Similar but distinct facts cannot be skipped or semantically replace each other during archival. Other Providers use their adapter write semantics. The Host requires one exact terminal receipt per source (and exact Recall evidence for a skipped duplicate), then selects the retained entries by importance within a byte budget and commits that remainder together with the pending mutation under the original revision fence. A Provider cannot share the local filesystem transaction, so a later revision conflict or concurrent external write may leave already archived duplicates; existing hot facts remain protected by the revision fence. + +## Project Documents + +### Purpose + +Documents preserve project knowledge that is more complete than a single memory item but should still be quick to read, such as: + +- architecture designs and rationale; +- evidence-backed investigation findings; +- operating procedures, release checklists, and incident reviews; +- implementation handoffs and long-term maintenance notes. + +User profiles, ordinary conversation, temporary progress, raw large logs, and secrets should not be stored in Documents. + +### Control Plane + +`documents/index.json` is the metadata source of truth. It manages IDs, titles, descriptions, status, filenames, source paths, sessions, timestamps, revisions, SHA-256 values, sizes, and Memory Space references. Managed Markdown copies include generated frontmatter. + +`sourcePaths`: + +- may point only inside the current session workspace; +- are source references only and are never modified by the plugin; +- are not required to exist by the current implementation; +- may not point into the managed `documents/` directory itself. + +### Scope + +The physical sharing scope of Documents is determined by `storageScope`: + +- `workspace` / `workspaces`: normally isolated with the project; +- `global` / `custom`: multiple workspaces may share the same `documents/index.json`. + +Therefore, “Project Documents” describes the content type and does not guarantee physical isolation by workspace. The current session workspace constrains only `sourcePaths` on new writes. + +### Capacity and Hot/Cold Tiering + +| Item | Limit | +|---|---:| +| One body | 2 MiB maximum | +| Total active content | 10 MiB maximum, including generated frontmatter | +| Total archived content | Does not count toward the active limit | + +The actual rendered size is calculated before creation or update. If capacity is insufficient, the least recently accessed active Document is selected by `lastAccessedAt` and then `updatedAt`; a Mnemon cold reference is written and verified first, and the original text is moved only if its revision remains unchanged. + +Default search covers only active Documents. Search updates `lastAccessedAt` for matching Documents, so it is read-only with respect to bodies but writes index metadata. + +## Memory Spaces + +A Memory Space is the uniform semantic and routing unit for long-term memory; its provider chooses the data plane: + +```text +id generated by the Host or inherited from a discovered Mnemon Store +name human-readable name +description routing boundary: what belongs here and when to recall it +active whether it participates in DSH reads and routing +provider mnemon-native or one of the registered third-party engines +location local store/CLI scope, or remote endpoint + provider scope +``` + +### Read and Write Boundaries + +- After initialization, Mnemon's native layer retains at least one Store and selects one default through `/active`; ordinary Mnemon agents continue to use this single-Store model. +- dsh-mnemon activation is an independent control plane: any 0..N Memory Spaces may be active, and making all inactive changes neither Mnemon's default Store nor remote data. +- Recall and browse use active spaces only; graph, entity, related, link, and delete behavior follows provider capabilities. +- Reads explicitly targeting an inactive Memory Space are rejected. +- Writes may target any registered space with `remember`; the receipt reflects the provider's exact or asynchronous extraction semantics. +- After a successful write to an inactive target, the plugin activates it automatically. +- Without an explicit target, if the number of active Memory Spaces is not exactly one, the deterministic service requires the caller to choose a target first. + +### Creation, Discovery, and Merge + +- An uninitialized root may remain at zero Stores. The first explicit Memory Space creation uses Mnemon's native `default` ID while retaining the user-supplied name and routing description; later creations use Host-generated UUIDs. +- The last native Store cannot be deleted after initialization, but its Memory Space may remain inactive. Before deleting Mnemon's default Store, the plugin switches to another existing Store. +- An existing `/data//mnemon.db` is discovered and registered without moving the database. +- Merge imports source content into the target through Mnemon; the source database remains in place, and by default only the source is marked inactive. +- Pack replacement cannot empty an initialized Store set. If replacement removes the former default Store, the plugin repairs the native default pointer to an existing Store. +- `forget` is a soft delete by exact ID, not deletion of a database file. +- Users create Mnemon Native spaces directly. Enabling or saving a third-party service discovers all visible provider-native namespaces, atomically maps their titles/descriptions and scope into the directory, and makes them available as smart-selection candidates. Disabling the provider removes those local mappings and never provider content. +- Smart placement treats the allowlist, data boundary, and required capabilities as Host-enforced rules. Soft preference and prompt guide semantic choice only among multiple eligible candidates and cannot override hard rules. The decision receipt is persisted with Memory Space metadata. +- Merge remains Mnemon Native-specific. Graph, relationships, browsing, exact/async writes, and hard/soft/unsupported deletion follow each provider's declared capabilities; the UI and agents do not pretend missing behavior exists. See the [provider matrix](../guides/memory-providers.md). + +### Cross-Agent Visibility + +`mnemon.db` is Mnemon's native data plane, not a private dsh-mnemon format. Another Mnemon-enabled agent can access the same durable memory when it uses the same `storageRoot` and Store. dsh-mnemon also discovers compatible Stores already present on disk; DSH-specific names, descriptions, and activation state remain managed by `.dsh-memory-bodies.json`. + +Third-party visibility is determined by its provider scope, such as service plus URI, workspace/peers, bank, project/user, knowledge directory, or container. No provider extends sharing to `runtime/` or `documents/`; “shared third-tier memory” must not be presented as automatic sharing of the complete DSH context. + +## Four Relationship Types + +Mnemon Native preserves `temporal`, `semantic`, `causal`, and `entity` relationships. Hindsight projects its provider graph, and Holographic derives local entity/semantic relationships. Providers without graph edges contribute bounded disconnected nodes; adapters never invent unsupported relationships. The UI hides Related, Link, Browse, and Forget where the selected provider does not support them. + +## Data Authority Table + +| Data | Authoritative source | Derived/cache | +|---|---|---| +| Hot memory | `runtime/memories.json` | `USER.md`, `MEMORY.md` | +| Documents | `documents/index.json` + managed Markdown | excerpts, search ranking, status aggregation | +| Mnemon Native catalog | `data/.dsh-memory-bodies.json` + on-disk Stores | Web status aggregation | +| Third-party connections | `state/memory-providers.json` | redacted provider capabilities and status | +| Long-term memory | Mnemon `mnemon.db` or remote provider | graph projection and cross-provider rank fusion | +| Review watermark | Host process memory | status-page snapshot; not yet persisted | diff --git a/dsh/docs/en/reference/workflows.md b/dsh/docs/en/reference/workflows.md new file mode 100644 index 000000000..977998b8a --- /dev/null +++ b/dsh/docs/en/reference/workflows.md @@ -0,0 +1,334 @@ +# Lifecycle and Core Workflows + +[简体中文](../../zh-CN/reference/workflows.md) | **English** | [Documentation Center](../README.md) + +This page describes the **Layered strategy**, the default. The **General strategy** offers every available Source in one shared budget and leaves routing to the model: named tools serve their own Sources, and every other route is read with `mnemon_view_route` and changed with `mnemon_view_action`. It has no recall envelope beyond each route's own limits, no Runtime capacity maintenance or archival, and no idle review. See [Strategies and enhancements](../guides/capabilities.md#strategies-and-enhancements). + +## Per-Turn Context + +The Layered strategy provides stable routing guidance, a static Runtime Memory protocol, and a Wake snapshot message updated when needed: + +- `mnemon:routing`: a system prompt section that, when `routingGuidance=true`, provides concise boundaries for tiered queries; +- `mnemon:runtime-memory-protocol`: a system prompt section containing the invariant Runtime Memory semantics and write rules. It is present only while the eager Runtime source participates in automatic projection and remains byte-identical across memory writes; +- Wake snapshot: rendered from the immutable View pinned for the current root turn, carried by an own message with `source.plugin=dsh-mnemon` and `form=recall`. Runtime Memory contributes a complete revisioned USER/MEMORY state snapshot without repeating the static protocol; Documents and Memory Spaces contribute bounded covers rather than their complete catalogs. + +The Host appends a new user-role plugin message only when the Wake changes; it does not resend other plugins' shared context. Snapshots remain complete states rather than patches that depend on earlier messages. The static protocol stays in the system prefix, and Source text is transmitted literally without template interpolation. See [Architecture](../development/architecture.md#lifecycle-and-failures) for turn, child-Agent and generation ownership. + +The lifecycle pins before the Host assembles the System Prompt, then keeps the same View for every model step in that turn: + +```text +turn/start + -> enter system-prompt/assemble hook + -> beginTurn(root turn + operation scope) + -> Source facts → Strategy ViewSpec → validation → Source projection + -> pin Source revisions/digests and Host-only authority + -> build bounded Wake + -> continue the actual Host prompt assembly + -> align the static protocol section with the pinned Runtime source + -> append the changed Wake as this plugin's own complete snapshot message + +agent/pre-step(step=1) + -> cancel pending/running background review for a new turn + -> mark Prime once + -> append at most one short recall/writeback cue per session + -> main Agent decides whether to call a memory tool +``` + +Source snapshotting does not run semantic recall. Prime only initializes routing state and does not run asynchronous CLI status queries. + +A child captures and retains its live parent's pinned View and runtime at `agent/created`, before its driver starts. Its own turns pin that retained View, even after the parent finishes or moves to a newer generation. The delegation is released when the child activation is disposed. A cold resume receives a new delegation; an explicitly Host-created background child with no parent model turn snapshots a fresh scoped View. Children have their own turn authority without root-only cues or idle review. See [the authority lifecycle](../development/architecture.md#lifecycle-and-failures). + +## Agent Recall + +```text +Root or child Agent calls mnemon_recall(query, optional memoryBodyIds) + | + v +resolve the executing Agent's own turn pin and retained runtime + | + v +read the pinned Memory Space Source state on the Host + | + v +validate requested IDs are a subset; otherwise use every pinned active ID + | + v +Memory Spaces Source searches granted Provider namespaces concurrently + | + v +quality normalization + reciprocal-rank fusion + | + v +drop low relevance and admit at most 4 initial results / 3,600 characters + | + v +LLM judges whether the evidence is sufficient + | yes | no + v v +answer without more Recall explicitly submit one different query + | + v + search once, deduplicate, and close Recall + | + v +both attempts share at most 6 results, 1,200 characters each, +and 4,800 total content characters for this executing Agent turn +``` + +This admission envelope is the Layered strategy's per-turn policy; the General strategy applies only each route's own limits and call budget. + +The model-facing tool deliberately exposes no `category`, `source`, or `intent` filter: a guessed filter must not hide exact evidence. Recall is not forced: a root turn normally issues zero Provider queries. If the LLM calls it, the Host permits one initial query and at most one LLM-chosen, materially different refinement after evidence inspection. Same-query and concurrent repeats join or replay; a third distinct query replays the latest evidence without reaching a Provider. One Related traversal may follow, but only from a `memoryBodyId + id` admitted by either Recall attempt; its repeated result is replayed in the same way. + +Recall, Related and the single Documents search slot are budgeted per executing Agent turn. Parallel calls share that turn's state; sibling tasks, later turns and cold-resumed activations do not share cached evidence or consume each other's budget. Replays are restricted to the requested Memory Space subset. Document search is separately bounded to four records, 2,600 query-local characters per record, and 6,000 content characters total. The model-facing Memory Space catalog is capped at 16 entries, and `mnemon_status` returns only a compact health aggregate. Full records, provider settings, paths, and per-Space statistics remain available to Web/RPC control-plane surfaces, not conversation history. + +If the user has already supplied current facts, or the repository can answer directly, the Agent should not recall merely to “show memory.” + +## Web Retrieval and Agent Queries + +The Web “Recall” page follows a different path from model tools: + +```text +Direct search + -> RPC read channel + -> Source-scoped management search + -> raw evidence + +Agent search + -> the same deterministic direct search + -> spawn a worker with no Mnemon tools + -> answer only from supplied evidence + -> Host filters citations to actual memoryBodyId/id pairs +``` + +The Entities and Content pages also use deterministic Source management reads, without a second model. Content uses the Provider browse contract, not semantic recall. + +## Explicit Long-Term Writes + +The long-term write flow for the root Agent or `/mnemon remember` is: + +```text +durable candidate + | + v +spawn write worker + | + +-- list Memory Spaces + +-- choose the narrowest suitable scope + +-- recall when duplicate/conflict checking is useful + +-- create a new scope only for a recurring distinct domain + +-- remember / link / forget / merge as requested + v +structured receipt +``` + +The first Memory Space created in an empty storage root uses Mnemon's native `default` ID; the Host generates later IDs. A successful write to an inactive target activates it. This activation affects only DSH routing, and merging a source database is non-destructive. + +Runtime `add` / `replace` / `remove` and Document `create` / `update` do not need a model to perform storage I/O; they enter the deterministic control layer through the coordinator. Only capacity maintenance and archiving start dedicated workers. + +## Runtime add: Normal Path + +```text +request + -> normalize content + -> acquire in-process queue and file lock + -> reload memories.json + -> validate unique match / duplicate / capacity + -> write temporary JSON and Markdown projections + -> rename projections + -> rename memories.json as the commit marker + -> return compact receipt +``` + +`replace` and `remove` prefer a unique full-content match through `old_text` within the requested target; only when no exact match exists do they use a unique substring. Duplicate exact entries remain ambiguous. Capacity maintenance is triggered only when the requested add or size-increasing replacement would exceed the target limit. + +## USER.md Capacity Maintenance + +Capacity maintenance and the MEMORY.md archival below are part of the Layered strategy. With the General strategy, a write that would exceed a Runtime limit is rejected instead. + +```text +USER add exceeds 4 KiB + | + v +snapshot revision + committed entries + | + v +spawn no-tool local compactor + | + v +return compacted entries + sourceIndexes + | + v +Host validates: + - every source index appears exactly once + - no duplicate or out-of-range index + - importance is not lowered + - candidate fits the Host byte budget + - revision is still current + | + +-- invalid/conflict -> preserve original data + | + v +deterministic UTF-8 packing + | + v +retry pending add +``` + +The user profile is never sent to Memory Spaces. The worker has no tool permissions. + +## MEMORY.md Archival and Compaction + +```text +Layered strategy: pending MEMORY mutation exceeds the configured limit (10 KiB by default) + | + v +snapshot revision + committed entries eligible for archival +(exclude the pending add and the entry being replaced or removed) + | + v +Host selects existing active writable Memory Spaces within this operation's Source and namespace scope + | + v +One destination: Host routes directly +Several destinations: independent no-tool worker returns source-index routes only + On model execution failure, Host falls back within eligible destinations + | + v +Host validates exact source coverage, destinations, authority, and source revision + | + +-- invalid/revision changed -> no Provider writes; preserve Runtime + | + v +Host writes each original entry exactly to its planned existing Space + - committed receipt -> bind destination digest + - skipped -> require exact Recall evidence + | + v +Host retains exact hot entries by importance within its byte budget + | + v +CAS compactAndMutate(revision, compaction, original mutation, lineage) + -> persist JSON and both Markdown projections as one local commit +``` + +The planner has no data-plane tools and cannot create a Space. The Host owns every write, verifies one durable destination for every committed source entry, and changes Runtime only after all evidence is valid. A late cross-process revision conflict still preserves the original hot entries; because a remote Provider cannot share the local file lock, already committed durable copies are retained as safe duplicates rather than destructively rolled back. + +## Document Creation, Update, and Archiving + +```text +create/update request + | + v +capacityPlan using rendered UTF-8 bytes + | + +----+----+ + | | + fits overflow + | | + v v + commit select least-recently-used active Document + | + v + snapshot document + revision + | + v + spawn archive worker + | + v + write/verify concise Mnemon cold reference + with title, summary, planned path, SHA-256 + | + +----+----+ + | | + failed receipt ok + | | + v v + keep active revision check + | + +----+----+ + | | + conflict current + | | + v v + keep active move file to archived + | + v + retry original mutation +``` + +Manual archiving uses the same “index first, move second” path. When the Mnemon index succeeds but a revision conflict occurs, the index is not rolled back, so a safe duplicate reference may remain while the active original text is never lost. + +## Deterministic Activity Scoring and Background Review + +Completed turns accumulate four signals: + +```text +score = + min(floor(totalUserCharacters / 50), 3) + + completedTurnCount + + min(floor(completedToolResults / 5), 2) + + toolDiversityScore + +toolDiversityScore: + unique tools < 3 -> 0 + unique tools = 3 -> 1 + unique tools >= 4 -> 2 + +eligible when score >= 5 +``` + +Reaching the threshold does not guarantee a write: + +```text +completed turn + | + v +score >= 5 ? -- no --> retain activity for later turns + | + yes + | + v +Host dirty admission + - explicit no-memory intent in current turn -> stop + - persistence intent, >=320 user characters, + >=600 assistant characters, or completed non-Mnemon work -> continue + | + v +wait idleReviewMs (default 30 s), and at least +idleReview.minIntervalMs (default 5 min) after the last attempt + | + +-- new turn --> cancel timer/worker, retain activity + | + v +confirm the Agent is idle, turn/end exists, the turn was composed +by the Layered strategy, and idleReview.maxPerSession is not reached + | + v +start a task Agent on the completed turn + - spawn (default): a bounded checkpoint, idleReview.maxContextChars + - fork: inherits the parent context + | + v +conservative maintenance decision, one layer per pass + - Documents first: search; create at most one separate document + for a project record, never update or archive an existing one + - hot memory: only new, explicit, durable user assertions; + MEMORY.md takes compact rules no Document covers, never + project records (off with idleReview.runtimeMemory: false) + - after a Document, working memory is refused, and vice versa + - no Memory Spaces write tools + | + +-- completed, including skip -> clear activity + | + +-- failed/aborted ------------> retain activity +``` + +The admission check is deliberately structural rather than an LLM classification, so an eligible but ordinary checkpoint starts no background model. The review Agent's tools are exactly `mnemon_document_search`, `mnemon_runtime_memory` and `mnemon_document_create`, without the runtime tool when `idleReview.runtimeMemory` is `false`. The review guard holds each pass to one layer: the first Document creation or working-memory change it admits claims the pass, and the other is refused even if that first call fails; USER.md changes stay independent. “At most one” document or hot-memory change is still enforced by the persona, not by a Host mutation counter. Memory Spaces receive review output only later, through working-memory capacity archiving and Document cold archiving. While Agent Teams tools are installed, review pauses unless `idleReview.agentTeams: scoped`; the Status page says so while it is paused. Background watermarks are not yet persisted, so a Host restart loses accumulated signals that have not been processed. + +## How Configuration Switches Interact + +- `recallMode=off`: stops injecting recall cues; explicit `mnemon_recall` remains available. +- `writebackMode=off`: disables writeback cues and scored background review; explicit writes are still governed by `writeEnabled`. +- `lifecycleEnabled=false`: disables lifecycle reminders and review without removing explicit tools or Web entry points. +- `routingGuidance=false`: removes only the additional routing section; the Runtime Memory context remains registered. +- `writeEnabled=false`: removes semantic write tools and write RPC, and rejects write commands; it does not guarantee a read-only file-system mount. diff --git a/dsh/docs/en/releases/README.md b/dsh/docs/en/releases/README.md new file mode 100644 index 000000000..fe14aa9bf --- /dev/null +++ b/dsh/docs/en/releases/README.md @@ -0,0 +1,41 @@ +# Release history + +**English** | [简体中文](../../zh-CN/releases/README.md) | [Documentation](../README.md) + +For an existing installation, start with [Compatibility and upgrades](../reference/compatibility.md). Notes below describe their named releases; old UI and benchmark results are not current capability claims. + +## v0.5 + +- [v0.5.24: Complete Entity Counts and Related Memories on Demand](./v0.5.24.md) +- [v0.5.23: Faster Memory Compaction, Queries and Multi-Space Recall](./v0.5.23.md) +- [v0.5.22: In-App Updates, Conversation Tabs and Ollama Subagents](./v0.5.22.md) +- [v0.5.21: Large Native Spaces and Leaner Hot Memory](./v0.5.21.md) +- [v0.5.20: No Separate Starter Switch](./v0.5.20.md) +- [v0.5.19: DSH 0.2, Desktop Memory and a Guided First Install](./v0.5.19.md) +- [v0.5.18: Immediate Activation and Quiet Desktop CLI](./v0.5.18.md) +- [v0.5.17: Composable Strategies and Component Settings](./v0.5.17.md) +- [v0.5.16: Chinese Metadata, Native Sidebar and Source Scrolling](./v0.5.16.md) +- [v0.5.15: USER Tool Writes, Runtime Metadata and Skin Hooks](./v0.5.15.md) +- [v0.5.14: Desktop Loading, OpenViking User Keys and Agent Teams Review](./v0.5.14.md) +- [v0.5.13: Workspace, Builtin and DSH 0.1.7 Compatibility](./v0.5.13.md) +- [v0.5.12: DSH RC and Alpha Compatibility](./v0.5.12.md) +- [v0.5.11: Quiet Windows Git Branch Detection](./v0.5.11.md) +- [v0.5.10: Exact Memory Writes and Bounded Automatic Review](./v0.5.10.md) +- [v0.5.9: Runtime Archive Destinations and Legacy Session Recovery](./v0.5.9.md) +- [v0.5.8: Runtime Archive Safety and Review Reliability](./v0.5.8.md) +- [v0.5.7: DSH 0.1.5 Compatibility and Document Archival](./v0.5.7.md) +- [v0.5.6: Built-in Workspace Storage and Memory Reliability](./v0.5.6.md) +- [v0.5.5: Remote RPC Routing and Light Media](./v0.5.5.md) +- [v0.5.4: Memory Space Terminology](./v0.5.4.md) +- [v0.5.3: Actionable Version Maintenance](./v0.5.3.md) + + +## v0.4 + + + +## v0.3 + + + +## v0.2 diff --git a/dsh/docs/en/releases/v0.2.0.md b/dsh/docs/en/releases/v0.2.0.md new file mode 100644 index 000000000..88fc6f6ff --- /dev/null +++ b/dsh/docs/en/releases/v0.2.0.md @@ -0,0 +1,37 @@ +# v0.2.0: A Pluggable Three-tier Memory System + +[简体中文](../../zh-CN/releases/v0.2.0.md) | **English** | [Capability map](../guides/capabilities.md) + +v0.2.0 evolves dsh-mnemon from a Mnemon workbench into a long-term-memory Provider control plane. Runtime Memory and Project Documents remain reliably managed by dsh-mnemon, while third-tier Memory Spaces can now use Mnemon, OpenViking, Honcho, Mem0, Hindsight, Holographic, RetainDB, ByteRover, or Supermemory. + +## What changed + +- Nine Providers share one workflow for service settings, Memory Space instances, activation, recall, content, entities, distillation, and health observation. +- Providers are off by default. Settings owns services; Memory Spaces owns instances. +- New spaces can use an explicit Provider or rule + prompt driven smart selection. +- AI metadata, Agent query, Remember, and Document archive use clean independent top-level task Agents. +- Background task Agents can follow the DSH new-session model or use an explicitly selected Provider and model. +- Memory Spaces perform one full synchronization on first entry and reconnect individual spaces on demand instead of blocking on a 15-second poll. +- Status, Memory Spaces, content, and recall load concurrently and progressively; a local failure no longer blocks the whole page. +- The default Sidebar order is now **Status, Runtime, Documents, Memory Spaces**. +- Global, workspace, and custom scope now extend to local Providers that support those semantics while remote Providers retain their native namespaces. +- Turn memory and Save to memory preserve the established conversation mental model and navigate precisely into the new multi-Provider surfaces. + +## Upgrading from 0.1.x + +1. Back up the current Mnemon root or export a Mnemon Pack from Settings. +2. Update dsh-mnemon and restart the affected DSH profile. +3. Open **Settings → Memory System** and verify presentation, storage, and background task-Agent model routing. +4. Enable and save external Provider services one at a time when needed; every external Provider remains off by default. +5. Open **Memory Spaces → Overview** for first discovery and synchronization, then inspect instance scope and activation. +6. Verify one raw search and one Agent query before writing or migrating data. + +The upgrade never migrates, merges, or deletes `~/.mnemon`, workspace `.mnemon`, custom roots, or remote Provider data automatically. Disabling an external Provider clears only dsh-mnemon's local catalog metadata; reconnecting rebuilds it from the Provider. + +## Recommended baseline + +- Node.js 20 or newer; +- DeepSeek Harness 0.1.0-rc.6; +- Mnemon 0.2.3 or newer when using Mnemon Native. + +See the [capability map](../guides/capabilities.md) for the complete operation surface, [long-term memory providers](../guides/memory-providers.md) for fields and deployment requirements, and [operations](../guides/operations.md) for backup and troubleshooting. diff --git a/dsh/docs/en/releases/v0.2.1.md b/dsh/docs/en/releases/v0.2.1.md new file mode 100644 index 000000000..added5039 --- /dev/null +++ b/dsh/docs/en/releases/v0.2.1.md @@ -0,0 +1,23 @@ +# v0.2.1: Literal Braces in Runtime Memory + +[简体中文](../../zh-CN/releases/v0.2.1.md) | **English** | [Capability map](../guides/capabilities.md) + +v0.2.1 is a focused patch release for system-prompt assembly. Runtime Memory can now contain the exact literal text `{{}}` without causing the entire turn to fail. + +## Fixed + +- Exact literal `{{}}` text in `USER.md` or `MEMORY.md` is preserved in the assembled `mnemon:runtime-memory` section. +- Valid prompt variables in the same memory text continue to interpolate normally. +- Prompt variables and behavior in every other system-prompt section remain unchanged. + +The fix routes only the empty-brace literal through a dedicated Mnemon prompt variable. The host interpolator does not rescan substituted values, so the model receives the original `{{}}` text without weakening strict validation for real prompt-variable references. + +## Documentation + +The repository landing page now features the live multi-memory graph snapshot with clearer descriptions of observable Provider surfaces. + +## Upgrading + +Update dsh-mnemon and restart the affected DSH profile. This patch does not migrate, rewrite, or delete Runtime Memory, Project Documents, Memory Spaces, or Provider data. + +See the [v0.2.0 release notes](./v0.2.0.md) for the complete three-tier memory system introduced in the current minor release. diff --git a/dsh/docs/en/releases/v0.2.10.md b/dsh/docs/en/releases/v0.2.10.md new file mode 100644 index 000000000..64a851a4d --- /dev/null +++ b/dsh/docs/en/releases/v0.2.10.md @@ -0,0 +1,31 @@ +# v0.2.10: Reliable Updates and Subagent Handoffs + +[简体中文](../../zh-CN/releases/v0.2.10.md) | **English** | [Capability map](../guides/capabilities.md) + +v0.2.10 is a focused reliability patch for authorized remote management, in-app package updates, Memory System settings, and delegated memory operations. + +## Fixed + +- Trusted remote WebUI sessions now derive memory-write controls and assistant-message save actions from the Host settings capability grant instead of the transport location. Explicitly authorized remote management works while read-only sessions remain restricted. +- The in-app dsh-mnemon updater now runs `pnpm update` inside the DSH profile that owns the installation, preventing `ERR_PNPM_NO_PKG_MANIFEST` failures caused by an unrelated process working directory. +- The Memory System dialog can save the complete existing `persistenceStrategy` object through the settings bridge. Unsupported top-level fields remain rejected. +- Memory subagents can recover a bounded result from an authoritative successful terminal-tool receipt when the child omits its UUID result-tool handoff. Successful recall and write operations no longer surface the false `memory subagent completed without recording its result` error. + +## Safety and compatibility + +- The UUID result tool remains the primary completion channel. Strict tool allowlists are unchanged, and unrelated tools, failed receipts, abnormal child stops, and failed enclosing Code Mode calls still fail closed. +- Already committed writes are never retried, avoiding duplicate side effects. Nested Code Mode receipts are accepted only after the enclosing `run_code` succeeds. +- Storage formats, Provider connection formats, RPC permissions, public tool schemas, and configuration keys are unchanged. Existing data requires no migration. +- Updating dsh-mnemon and restarting the affected DSH profile is sufficient. + +## Repository maintenance + +- Bilingual Issue Forms, PR templates, contribution guides, and automated contribution checks now define the repository's maintenance workflow. This does not change the installed runtime. + +## Verification + +- The complete local suite passes with 351 tests; one Windows-only smoke test is skipped only on non-Windows local runs. +- Focused coverage exercises authorized remote controls, profile-owned version updates, persistence-strategy settings, Native tool receipts, successful and failed Code Mode handoffs, fail-closed paths, and no-retry write behavior. +- Type checking, deterministic builds, Headless activation, package validation, publint, attw, and CI on Ubuntu and Windows pass. + +See the [v0.2.9 release notes](./v0.2.9.md) for the previous patch release. diff --git a/dsh/docs/en/releases/v0.2.11.md b/dsh/docs/en/releases/v0.2.11.md new file mode 100644 index 000000000..2bbdd2dec --- /dev/null +++ b/dsh/docs/en/releases/v0.2.11.md @@ -0,0 +1,33 @@ +# v0.2.11: Responsive and Reliable Dialogs + +[简体中文](../../zh-CN/releases/v0.2.11.md) | **English** | [Capability map](../guides/capabilities.md) + +v0.2.11 is a focused user-interface patch that keeps Mnemon dialogs usable on phones, narrow sidebars, and short landscape viewports. + +## Improved + +- Mnemon workspace dialogs now use a fixed header, one independently scrollable body, and a fixed action footer. Create, edit, metadata, strategy, Remember, Runtime Memory, Project Document, version, preview, and destructive-confirmation actions remain reachable without scrolling the entire dialog. +- Narrow viewports present workspace dialogs as safe-area-aware bottom sheets. Dynamic viewport units, landscape insets, overflow containment, and 44px touch targets cover phones and coarse-pointer tablets. +- Destructive confirmations keep their cancel and confirm actions visible while long memory, path, or document summaries scroll independently. +- The conversation “Save to memory” dialog is bounded against mobile and short viewports while retaining the native DSH host appearance. + +## Accessibility and reliability + +- Shared workspace dialogs now contain keyboard focus, choose a safe initial control, return focus to the trigger, and move focus to the relevant action after asynchronous content becomes ready. +- Version checks remain dismissible during read-only loading. Closing a check invalidates stale responses so late results cannot reopen or update a dismissed dialog. +- Full-text preview now uses the shared responsive dialog contract with an accessible title and description, focus containment, focus return, safe-area handling, and a touch-sized close action. +- Busy write operations continue to guard dismissal and duplicate submission, while read-only loading is no longer treated as an irreversible mutation. + +## Safety and compatibility + +- Storage formats, Provider connections, configuration keys, RPC permissions, public tool schemas, and server behavior are unchanged. Existing data requires no migration. +- Updating dsh-mnemon and restarting the affected DSH profile is sufficient. A normal package downgrade restores the previous client presentation if rollback is needed. + +## Verification + +- The complete local suite passes with 358 tests across 38 files; one Windows-only smoke test is skipped only on non-Windows local runs. +- Focused interaction coverage validates focus entry and return, focus trapping, async focus movement, close guards, stale-response rejection, fixed footer placement, safe areas, dynamic viewport sizing, and touch targets. +- All 16 dialog interactions were inspected in an isolated DSH WebUI. The version flow includes loading and result states, producing 17 captured mobile states plus before-and-after comparisons at portrait, narrow, and short-landscape sizes. +- Type checking, deterministic builds, Headless activation, package validation, publint, attw, and CI on Ubuntu and Windows pass. + +See the [v0.2.10 release notes](./v0.2.10.md) for the previous patch release. diff --git a/dsh/docs/en/releases/v0.2.12.md b/dsh/docs/en/releases/v0.2.12.md new file mode 100644 index 000000000..59fcd64f2 --- /dev/null +++ b/dsh/docs/en/releases/v0.2.12.md @@ -0,0 +1,36 @@ +# v0.2.12: Top-Layer Mobile Sheets + +[简体中文](../../zh-CN/releases/v0.2.12.md) | **English** | [Capability map](../guides/capabilities.md) + +v0.2.12 is a focused follow-up to v0.2.11 that fixes real-phone dialog occlusion, makes mobile-sheet motion continuous, and turns the sheet handle into a reliable drag-to-dismiss control. + +## Fixed + +- All 15 Mnemon workspace action dialogs now use one shared `MnemonDialog` component and render through a body-level portal. The DSH navigation rail and other host stacking contexts can no longer cover the sheet edge or intercept its controls. +- Mobile sheets expose a real 28px top drag area. Pulling far enough, or completing a deliberate downward flick, dismisses the dialog; a shorter gesture returns smoothly to rest. +- Header close, backdrop close, Escape, footer cancel, and drag dismissal share one coordinated exit path. Dialog state is removed only after the visible exit finishes. +- Long forms retain a fixed header and action footer while the body scrolls independently in portrait, narrow, and short-landscape viewports. + +## Motion and reliability + +- Enter, exit, and snap-back motion uses compositor-friendly `transform` and `opacity` animations. The sheet continues from its current visual offset when a close or second drag begins, avoiding jumps between gesture and animation states. +- Global pointer tracking keeps a gesture alive after the finger leaves the handle. Lost pointer capture, pointer cancellation, window blur, app switching, and visibility changes recover to a stable resting state. +- A new drag generation invalidates stale snap-back cleanup, so quickly grabbing the sheet again cannot be interrupted by the previous animation finishing late. +- Background scrolling is locked while a dialog is open and restored after every close path. Busy mutations still block dismissal, while reduced-motion and browsers without Web Animations receive a safe immediate fallback. +- Existing focus containment, initial focus, asynchronous focus handoff, and focus return behavior remain part of the shared component. + +## Safety and compatibility + +- Storage formats, Provider connections, configuration keys, RPC permissions, public tool schemas, credentials, and server behavior are unchanged. Existing data requires no migration. +- The gesture is limited to the top handle in mobile-sheet layouts, leaving native vertical scrolling available inside the dialog body and leaving desktop dialogs unchanged. +- Updating dsh-mnemon and restarting the affected DSH profile is sufficient. A normal package downgrade restores the v0.2.11 client behavior if rollback is needed. + +## Verification + +- The complete local verification passes with 369 tests; one Windows-only smoke test is skipped only on the non-Windows local run. +- Eight focused dialog regressions cover the body portal and scroll lock, animated close sequencing, distance dismissal, short-drag snap-back, blur recovery, rapid re-grab isolation, busy guards, and reduced-motion fallback. +- Structural coverage verifies the maximum top layer, global pointer listeners, coarse-pointer sizing, and every shared dialog call site. +- Real browser interaction at 390×844 and 667×375 covers an expanded host rail, left-edge hit testing, portrait and landscape scrolling, drag dismissal, snap-back, rapid re-grab, footer cancellation, and version-check states. +- Type checking, deterministic builds, Headless activation, package validation, publint, attw, and CI on Ubuntu and Windows pass. + +See the [v0.2.11 release notes](./v0.2.11.md) for the previous patch release. diff --git a/dsh/docs/en/releases/v0.2.13.md b/dsh/docs/en/releases/v0.2.13.md new file mode 100644 index 000000000..03d12dfd4 --- /dev/null +++ b/dsh/docs/en/releases/v0.2.13.md @@ -0,0 +1,28 @@ +# v0.2.13: DSH rc.8 Compatibility + +[简体中文](../../zh-CN/releases/v0.2.13.md) | **English** | [Capability map](../guides/capabilities.md) + +v0.2.13 verifies dsh-mnemon against DSH 0.1.0-rc.8 and adopts its richer subagent failure diagnostics without changing Mnemon data, configuration, or public tools. + +## Updated + +- The development and release dependency set now uses the exact DSH 0.1.0-rc.8 package family. The client peer range continues to accept rc.6 and newer compatible 0.1 releases, while rc.8 is the recommended and fully verified baseline. +- Failed local and remote child runs now prefer the bounded provider diagnostic added by rc.8. Diagnostic text passes through the existing whitespace, length, and credential-redaction boundary before reaching an operator-visible error. +- Older local child runs retain the event-history fallback, so the compatibility update does not require the new diagnostic field at runtime. +- Getting Started documents the temporary `@deepseek-ai/dsh@next` installation path while rc.8 remains outside npm `latest`. + +## Safety and compatibility + +- Runtime Memory, Project Documents, Memory Spaces, Provider data, configuration keys, RPC permissions, and public tool schemas are unchanged. Existing Mnemon data requires no migration. +- DSH rc.8's incompatible SQLite change concerns the opt-in `@deepseek-ai/dsh-session-persistence-sqlite` session backend. Shipped profiles do not select it, and dsh-mnemon uses separate storage roots. Deployments that mounted that backend manually must back up and recreate only its DSH session database. +- rc.8 multimodal input, file/session mentions, named Codex instances, and persistent Windows PowerShell sessions are Host capabilities. Existing dsh-mnemon surfaces continue to work without widening child-agent tool permissions. +- Update DSH through the `next` tag, update dsh-mnemon, and restart each affected DSH profile. A normal package downgrade remains available because this release performs no data migration. + +## Verification + +- The complete local verification passes with 370 tests; one Windows-only smoke test is skipped only on the non-Windows local run. +- A real isolated DSH 0.1.0-rc.8 Web profile loads the plugin, reports all eight Provider adapters, renders Status and Settings, and produces no browser console errors. +- Type checking, deterministic builds, Headless activation, packed-package validation, publint, attw, and frozen-lockfile installation pass. +- The rc.8 UI primitives package emits a development-only missing-source-map warning; the published JavaScript, types, build, and runtime behavior remain valid. + +See the [v0.2.12 release notes](./v0.2.12.md) for the previous patch release. diff --git a/dsh/docs/en/releases/v0.2.14.md b/dsh/docs/en/releases/v0.2.14.md new file mode 100644 index 000000000..255191114 --- /dev/null +++ b/dsh/docs/en/releases/v0.2.14.md @@ -0,0 +1,27 @@ +# v0.2.14: Readable Sidebar Surfaces + +[简体中文](../../zh-CN/releases/v0.2.14.md) | **English** | [Capability map](../guides/capabilities.md) + +v0.2.14 is a focused Web UI patch that keeps the Mnemon Sidebar workspace readable when a DSH skin makes the base background transparent. + +## Fixed + +- The mounted Mnemon workspace, shared view surface, and Sidebar-specific chrome now prefer `--dsw-alias-bg-overlay` and fall back to `--dsw-alias-bg-base` when the overlay token is unavailable. +- The fallback is applied consistently to the Sidebar shell, headers, navigation, page surfaces, dialogs, footers, canvas, and update banner, preventing the conversation and full-screen skin artwork from showing through the workspace. +- Default-theme behavior is preserved because themes without the overlay token continue to use the existing base surface. + +## Safety and compatibility + +- This release changes Client CSS only. Host behavior, Runtime Memory, Project Documents, Memory Spaces, Providers, configuration, RPC permissions, storage formats, credentials, and existing data are unchanged. +- Skins such as maid-atelier can retain their transparent conversation canvas while providing Mnemon with their high-opacity overlay color. No skin-specific selector or dependency is introduced. +- Update dsh-mnemon and restart the affected DSH profile. A normal downgrade to v0.2.13 restores the previous surface selection and requires no data rollback. + +## Verification + +- Two real isolated DSH 0.1.0-rc.8 Web profiles load v0.2.13 and this release with maid-atelier pinned at `d0419da`; same-viewport captures confirm that the workspace changes from fully transparent to a high-opacity surface and remains readable in both light and dark modes. +- The complete local verification passes with 371 tests; one Windows-only smoke test is skipped only on the non-Windows local run. +- Focused CSS coverage verifies the overlay-to-base fallback in source and compiled output and prevents direct base-only Sidebar surfaces from returning. +- Type checking, deterministic builds, Headless activation, package validation, publint, attw, and CI on Ubuntu and Windows pass. +- The fix resolves [issue #56](https://github.com/omdsh-dev/dsh-mnemon/issues/56). + +See the [v0.2.13 release notes](./v0.2.13.md) for the previous patch release. diff --git a/dsh/docs/en/releases/v0.2.15.md b/dsh/docs/en/releases/v0.2.15.md new file mode 100644 index 000000000..6e05d1fec --- /dev/null +++ b/dsh/docs/en/releases/v0.2.15.md @@ -0,0 +1,38 @@ +# v0.2.15: DSH rc.2 Compatibility and Resilient Task Agents + +[简体中文](../../zh-CN/releases/v0.2.15.md) | **English** | [Capability map](../guides/capabilities.md) + +v0.2.15 verifies dsh-mnemon against DSH 0.1.1-rc.2, carries multimodal Host content through the existing lifecycle without storing image bytes, and includes the task-Agent and advanced-layout fixes merged since v0.2.14. + +## DSH 0.1.1-rc.2 compatibility + +- All ten direct `@deepseek-ai/dsh*` development dependencies are pinned to 0.1.1-rc.2. The lockfile matches the official release graph, which intentionally retains rc.1 for unchanged internal packages and contains no older DSH prerelease line. +- The published UI-primitives peer range keeps support for compatible 0.1.0 hosts and the 0.1.1 prerelease line beginning with rc.1, including rc.2. +- The live model catalog now preserves DSH `inputModalities`. Settings labels the first-party `deepseek-official/deepseek-v4-flash-vision-exp` route as **Image input**. +- Lifecycle processing accepts durable DSH image attachment references, preserves all original content blocks when appending guidance, and counts only text toward activity thresholds. Mnemon background metadata, recall, write, distillation, selection, and archive jobs remain text-only. +- DSH rc.2 prioritizes reusable DeepSeek Files API uploads and normalizes image size and format for the selected model. Those uploads remain owned by DSH; dsh-mnemon does not copy raw image bytes into Runtime, Documents, or Memory Spaces. + +## Fixed + +- A configured background task-Agent model route now reaches every coordinator delegation, including idle review, recall, write, answer, provider placement, migration, USER compaction, document archive, and metadata maintenance. +- Metadata maintenance skips generated candidates whose title or description violates local length bounds. Valid candidates in the same result still apply, omitted spaces retain their existing metadata, and unexpected or duplicate identifiers remain hard failures. +- Sidebar entry discovery and workspace takeover now recognize the DSH advanced-mode frame classes as well as the classic pane layout. + +## Safety and upgrade + +- No Mnemon persistence format, RPC authority, credential handling, public tool schema, or data migration changes. +- The published package keeps the v0.2 surface: the root plugin, Client wrapper, and package metadata only. No v0.3 kernel, topology, workspace package, or SDK entry is included. +- The published plugin keeps its Node.js `>=20` engine for older compatible DSH hosts. A complete DSH 0.1.1-rc.2 profile requires Node.js `^22.19.0 || >=24.0.0` because the Host uses `Promise.withResolvers` and the Node Zstd API. +- The optional DSH SQLite session backend remains separate from Mnemon storage and uses schema 17. Deployments that selected it manually must follow the upstream backup and recreation requirement; shipped profiles do not select it. +- Upgrade DSH and dsh-mnemon, then restart each affected profile. A package downgrade requires no Mnemon data rollback. + +## Verification + +- The complete `pnpm run verify` chain passes 380 tests; one Windows-only smoke test is skipped only in the local non-Windows run. +- Deterministic double builds match across 65 generated files. Isolated Headless activation exposes 35 total tools, including all five representative Mnemon tools. +- Package validation reports 72 files. The package keeps only the v0.2 root and Client code entries; publint strict and attw pass. +- Frozen-lockfile installation and the package-version/tag invariant are checked before publication. +- GitHub CI covers Ubuntu on Node.js 22.19 and 24, plus Windows on Node.js 24. +- DSH UI primitives emit only the known development-time missing-source-map warning. + +See the [v0.2.14 release notes](./v0.2.14.md) for the previous patch release. diff --git a/dsh/docs/en/releases/v0.2.16.md b/dsh/docs/en/releases/v0.2.16.md new file mode 100644 index 000000000..3404357e3 --- /dev/null +++ b/dsh/docs/en/releases/v0.2.16.md @@ -0,0 +1,30 @@ +# v0.2.16: Atomic Runtime Memory Capacity Maintenance + +[简体中文](../../zh-CN/releases/v0.2.16.md) | **English** | [Capability map](../guides/capabilities.md) + +v0.2.16 makes Runtime Memory capacity recovery complete and atomic. An overflow caused by `add`, `replace`, or `remove` can now archive or locally compact the reviewed entries and apply the requested mutation without exposing a partial result or accepting an unverified archive. + +## Fixed + +- Capacity maintenance now supports all three existing runtime mutation actions. MEMORY.md uses the archive worker with a 32,768-token budget, while USER.md remains a strictly local compaction path and is never sent to a Memory Space. +- The Host prepares a plan from an exact file revision, then commits compaction and the original mutation together under one file lock. A concurrent write, an invalid result, or a final capacity overflow leaves the Runtime JSON, MEMORY.md, and USER.md byte-identical to their pre-operation state. +- A `replace` or `remove` plan excludes the matched obsolete entry before archival and compaction. A pending add or replacement is protected from compaction, so stale content cannot be archived or reintroduced and the new content cannot be duplicated. +- MEMORY.md migration must map every one-based committed source entry exactly once to a destination, and the destination set must match the reported Memory Space ids. Each destination must be active, backed by an enabled remember-capable Provider, and supported by a successful `mnemon_remember` receipt or actual `mnemon_recall` evidence. +- Failed, pending, queued, or otherwise provisional Provider receipts are rejected. Results emitted inside Code Mode count only after the enclosing run succeeds. + +## Compatibility and data safety + +- No persistence format, RPC authority, credential handling, public tool schema, or external mutation action changes. `planMaintenance` and `compactAndMutate` are Host-only coordination methods; public runtime actions remain `add`, `replace`, and `remove`. +- Existing schema-version-1 runtime files and Memory Spaces require no migration. Upgrade dsh-mnemon and restart the affected DSH profile; a package downgrade requires no data rollback. +- This closes the data-loss window where a preliminary compaction could persist before a stale mutation retry failed, including a replacement whose `oldText` disappeared during compaction. + +## Verification + +- The complete `pnpm run verify` chain passes 393 tests; one Windows-only smoke test is skipped only in local non-Windows runs. The same release tree passes on Node.js 22.19, 24, and 25. +- Regression coverage includes real-controller overflow recovery for add, replace, and remove; legacy over-capacity removal; obsolete-entry exclusion; USER.md privacy; exact migration coverage; invalid, inactive, disabled, and non-writable destinations; fake or provisional receipts; duplicate and reintroduced compaction output; cross-controller revision conflicts; final overflow; and byte-identical files after every rejected transaction. +- Deterministic double builds match across 65 generated files. Isolated Headless activation exposes 35 total tools, including all five representative Mnemon tools. Package validation reports 72 files; publint strict and attw pass. +- GitHub CI passes on Ubuntu with Node.js 22.19 and 24, and on Windows with Node.js 24, including the Mnemon release download and default-discovery smoke tests. + +The original capacity fix was contributed by [Zn-Dk](https://github.com/Zn-Dk) in [PR #67](https://github.com/omdsh-dev/dsh-mnemon/pull/67) and was merged together with the maintainer's atomicity and evidence-validation hardening while preserving both commits. + +See the [v0.2.15 release notes](./v0.2.15.md) for the previous patch release. diff --git a/dsh/docs/en/releases/v0.2.2.md b/dsh/docs/en/releases/v0.2.2.md new file mode 100644 index 000000000..18914b9d9 --- /dev/null +++ b/dsh/docs/en/releases/v0.2.2.md @@ -0,0 +1,24 @@ +# v0.2.2: Remote Memory Space Activation + +[简体中文](../../zh-CN/releases/v0.2.2.md) | **English** | [Capability map](../guides/capabilities.md) + +v0.2.2 fixes Memory Space activation from a remote trusted DSH page and tightens the surrounding RPC authority boundaries. + +## Fixed + +- Activating or deactivating a Memory Space now uses the dedicated trusted-host `/dsh-mnemon-activation` control channel instead of the loopback-only general write channel. +- The activation handler accepts only a Memory Space ID, a Boolean active state, and normal session/workspace routing fields. Metadata, Provider configuration, credentials, deletion, and durable-memory writes cannot cross this boundary. +- The client falls back to the legacy write endpoint only when an older Host reports the new activation endpoint as missing. It does not retry a `403` or any business failure through a broader channel. +- Remote WebUI sessions keep activation available while disabling edit, delete, reconnect, memory distillation, document/runtime mutation, version update, settings, backups, and Provider configuration before transport. + +## Authority audit + +- The trusted-host Provider service catalog is now redacted and never returns saved credential values. +- Private Provider settings remain available to the local settings editor only through the loopback write channel. +- Message save actions, settings, backup import/export, Provider service controls, and version updates all apply the same loopback preflight rule. + +## Compatibility and upgrading + +Update dsh-mnemon and restart the affected DSH profile. On loopback connections, the browser client remains compatible with pre-v0.2.2 Hosts for activation and private Provider catalog reads through narrow missing-endpoint fallbacks; remote activation requires the updated Host boundary. This patch does not migrate, rewrite, or delete Runtime Memory, Project Documents, Memory Spaces, or Provider data. + +See the [v0.2.1 release notes](./v0.2.1.md) for the previous patch release. diff --git a/dsh/docs/en/releases/v0.2.4.md b/dsh/docs/en/releases/v0.2.4.md new file mode 100644 index 000000000..bb26a7ff4 --- /dev/null +++ b/dsh/docs/en/releases/v0.2.4.md @@ -0,0 +1,23 @@ +# v0.2.4: Trusted Remote Settings + +[简体中文](../../zh-CN/releases/v0.2.4.md) | **English** | [Capability map](../guides/capabilities.md) + +v0.2.4 completes the trusted remote management path introduced in v0.2.3 and fixes the empty **Settings → Memory System** page. + +## Fixed + +- A writable settings snapshot from the Host is now the browser's authoritative management capability. A remote connection is no longer treated as read-only merely because it is not loopback. +- Provider service settings and Mnemon Pack controls use the same Host-authorized management channels. +- When both settings namespaces are unavailable, the settings page shows an actionable authorization diagnostic instead of rendering nothing. + +## Security boundary + +- `remoteAccess` remains `read-only` by default and cannot be changed through Web settings. +- Explicit `remoteAccess: trusted-host` promotes `/dsh-mnemon-write`, `/dsh-mnemon-settings`, and `/dsh-mnemon-pack` together at Host startup. +- The deployment must also authenticate the remote page, configure the DSH Connection `trustedHosts` allowlist, remain same-origin, and restart the Host. Do not enable remote management on an unauthenticated public endpoint. + +## Upgrading + +Update dsh-mnemon and restart the affected DSH profile. This patch does not migrate, rewrite, or delete Runtime Memory, Project Documents, Memory Spaces, Provider data, or saved credentials. + +See the [v0.2.2 release notes](./v0.2.2.md) for the previous published patch release. diff --git a/dsh/docs/en/releases/v0.2.5.md b/dsh/docs/en/releases/v0.2.5.md new file mode 100644 index 000000000..bb934ce9e --- /dev/null +++ b/dsh/docs/en/releases/v0.2.5.md @@ -0,0 +1,28 @@ +# v0.2.5: Cache-Stable Runtime Memory + +[简体中文](../../zh-CN/releases/v0.2.5.md) | **English** | [Capability map](../guides/capabilities.md) + +v0.2.5 projects Runtime Memory through DSH's native runtime-context snapshots. Updating `USER.md` or `MEMORY.md` no longer rewrites the system prompt and invalidates its reusable prefix. + +## Improved + +- Global and per-Agent `mnemon:runtime-memory` contributions now use `systemPrompt.context()` instead of a mutable system-prompt section. +- DSH reevaluates Runtime Memory before each model step and appends a new user-role snapshot only when the projected context changes. The stable system prompt, tools, and preceding conversation remain cacheable. +- Per-Agent workspace routing stays lazy, so writes made through `mnemon_runtime_memory` are visible on the next model step without rebuilding the system prompt. + +## Compatibility + +- Storage layout, schemas, and tool contracts are unchanged; no data migration is required. +- Literal `{{...}}` text continues to bypass placeholder interpolation. +- Runtime Memory now follows DSH's standard runtime-context configuration and suppression behavior. + +## Verification + +- The full verification suite covers system-prompt stability, runtime snapshot refreshes, literal brace handling, and Agent-scoped workspace routing. +- Deterministic builds, headless checks, package checks, and CI on Ubuntu and Windows pass. + +## Upgrading + +Update dsh-mnemon and restart the affected DSH profile. Existing Runtime Memory data remains unchanged. + +See the [v0.2.4 release notes](./v0.2.4.md) for the previous patch release. diff --git a/dsh/docs/en/releases/v0.2.6.md b/dsh/docs/en/releases/v0.2.6.md new file mode 100644 index 000000000..7c93112dd --- /dev/null +++ b/dsh/docs/en/releases/v0.2.6.md @@ -0,0 +1,34 @@ +# v0.2.6: Remote-Safe UI Reliability + +[简体中文](../../zh-CN/releases/v0.2.6.md) | **English** | [Capability map](../guides/capabilities.md) + +v0.2.6 makes Memory System reads and asynchronous UI transitions reliable across trusted remote deployments, slow Providers, and rapid workspace or page changes. + +## Fixed + +- Refreshing an individual Memory Space card now uses the trusted read RPC. Remote read-only deployments no longer report `Failed to fetch` while full synchronization succeeds. +- HTTP-based Providers now honor signals that were already aborted before request setup. OpenViking fallback delays also reject with a proper error. +- Provider settings, Document readers, related-memory traversal, and entity views ignore responses from older scopes or interactions instead of overwriting the newest UI state. +- Related-memory scrolling safely degrades when `scrollIntoView` is unavailable. + +## Deployment Boundary + +- Browser code remains transport-neutral: it delegates RPC and deployment URLs to the DSH host connection and does not construct root-relative API, plugin, or asset URLs. +- Full `/prefix/` hosting still requires DSH to expose one coherent base URL for its shell, assets, plugin bundles, RPC/API, and WebSocket endpoints. The host-level compatibility work is tracked in the Roadmap. + +## Roadmap + +- Added Cordis / DSH contract coverage for service injection, hot reload and disposal, late-registered structured-output tools, and `toolFilter` isolation. +- Added explicit host capability declarations and a real URL-subpath deployment matrix as future work; this release does not weaken subagent tool isolation. + +## Verification + +- The complete suite passes with 330 tests; the Windows-only smoke test is skipped only on non-Windows local runs. +- Deterministic builds, Headless activation, package checks, and CI on Ubuntu and Windows pass. +- Real WebUI testing covered the Memory System status page, Memory Space overview and card refresh, rapid page changes, and rapid entity selection without browser errors or warnings. + +## Upgrading + +Update dsh-mnemon and restart the affected DSH profile. Storage layouts, schemas, Provider configuration, and tool contracts are unchanged; no data migration is required. + +See the [v0.2.5 release notes](./v0.2.5.md) for the previous patch release. diff --git a/dsh/docs/en/releases/v0.2.7.md b/dsh/docs/en/releases/v0.2.7.md new file mode 100644 index 000000000..495039ae7 --- /dev/null +++ b/dsh/docs/en/releases/v0.2.7.md @@ -0,0 +1,30 @@ +# v0.2.7: Isolated Subagent Results + +[简体中文](../../zh-CN/releases/v0.2.7.md) | **English** | [Capability map](../guides/capabilities.md) + +v0.2.7 restores supervised memory operations on DSH 0.1.0-rc.6 while preserving strict subagent tool isolation. + +## Fixed + +- Each delegated Mnemon run now registers a unique inherited result tool whose parameters exactly match the operation's result schema. +- The one-run result tool is explicitly admitted by the existing `toolFilter`; recall, write, review, placement, migration, compaction, Document archive, and metadata tasks keep their original hard tool allowlists. +- The parent validates the result schema and child identity, rejects duplicate or missing completion calls, handles nested code-mode completion, and unregisters the tool and event listener after the run. +- This replaces the dependency on DSH's late-injected `structured_output` path that could become unreachable after child tool filtering and cause `memory subagent stopped with error`. + +## Compatibility + +- The fix is entirely inside dsh-mnemon and does not require a DSH change. +- Public tool contracts, storage layouts, Memory Space schemas, Provider configuration, and persisted data are unchanged. +- The issue affected the shared delegated-result path, so the fix applies to all supervised Mnemon operations rather than only `mnemon_recall` and `mnemon_remember`. + +## Verification + +- The complete suite passes with 334 tests; one Windows-only smoke test is skipped only on non-Windows local runs. +- Deterministic builds, Headless activation, package checks, and CI on Ubuntu and Windows pass. +- Real WebUI testing covered Agent Query and a conversation-level `mnemon_recall`: the child used only its admitted read tools, invoked its unique result tool, and returned the validated structured result to the parent Agent. + +## Upgrading + +Update dsh-mnemon and restart the affected DSH profile. No data migration is required. + +See the [v0.2.6 release notes](./v0.2.6.md) for the previous patch release. diff --git a/dsh/docs/en/releases/v0.2.8.md b/dsh/docs/en/releases/v0.2.8.md new file mode 100644 index 000000000..a8649ddda --- /dev/null +++ b/dsh/docs/en/releases/v0.2.8.md @@ -0,0 +1,37 @@ +# v0.2.8: Bounded Recall Quality + +[简体中文](../../zh-CN/releases/v0.2.8.md) | **English** | [Capability map](../guides/capabilities.md) + +v0.2.8 adds a deterministic, pluggable quality layer that limits weak recall evidence before it enters Agent context. + +## Added + +- Recall now supports `strict-v1`, `balanced-v1`, and `exhaustive-v1` policies plus a registry for custom deterministic policies. +- Providers explicitly declare whether their scores are normalized relevance values. Unknown or undeclared score scales remain supplemental evidence instead of being interpreted through unsafe absolute thresholds. +- Recall sources expose structured quality statistics, including fetched, retained, selected, dropped, unscored, and unknown-scale counts. +- Candidate expansion runs before filtering so strict selection can choose stronger evidence from a wider bounded pool. + +## Default behavior + +- `strict-v1` is the default policy. Scores at or below zero and normalized scores below `0.25` are discarded before serialization to the Agent. +- All high-confidence results (`score >= 0.6`) are eligible up to the requested limit. Medium-confidence results are capped at four and unscored or unknown-scale results are capped at two. +- Recall no longer fills the requested limit with weaker evidence merely because capacity remains. +- Thresholds, candidate expansion, medium-result budget, unknown-result budget, and policy selection are configurable under `recallQuality`. + +## Compatibility and tuning + +- Persisted memory data and Memory Space layouts are unchanged; no migration is required. +- This release intentionally changes default recall selection. Set `recallQuality.policy` to `balanced-v1` for broader low-score fallback or `exhaustive-v1` to preserve the previous top-N behavior. +- Custom policies are validated for bounded candidate limits, eligible selections, duplicates, and result limits; invalid custom behavior falls back to `strict-v1`. + +## Verification + +- The complete suite passes with 343 tests; one Windows-only smoke test is skipped only on non-Windows local runs. Ubuntu and Windows CI pass. +- In an isolated 28-memory benchmark, answerable-query context decreased by 48.3% and absent-query context decreased by 60.4%. +- The measured tradeoff was Recall@10 decreasing from 100% to 85.4%, while nDCG@10 changed from 0.948 to 0.923. Deployments that prioritize maximum coverage should use or fall back to a broader policy. + +## Upgrading + +Update dsh-mnemon and restart the affected DSH profile. Review `recallQuality` if the deployment prioritizes maximum recall over bounded context. + +See the [v0.2.7 release notes](./v0.2.7.md) for the previous patch release. diff --git a/dsh/docs/en/releases/v0.2.9.md b/dsh/docs/en/releases/v0.2.9.md new file mode 100644 index 000000000..72dca6f4b --- /dev/null +++ b/dsh/docs/en/releases/v0.2.9.md @@ -0,0 +1,29 @@ +# v0.2.9: Literal Runtime Memory + +[简体中文](../../zh-CN/releases/v0.2.9.md) | **English** | [Capability map](../guides/capabilities.md) + +v0.2.9 makes Runtime Memory safe for arbitrary literal `{{...}}` text and closes the remaining interpolation failure reported in issue #37. + +## Fixed + +- Every `{{` opener in `USER.md` and `MEMORY.md` is now routed through a dedicated non-recursive prompt variable. Empty, non-ASCII, whitespace-containing, legal, unknown, adjacent, nested, and incomplete groups all reach the model unchanged. +- Runtime Memory no longer expands otherwise valid host variables such as `{{model}}`; memory is quoted data rather than a prompt-template extension point. +- The strict host interpolator remains active for Mnemon's static system sections and unrelated plugins, so malformed real prompt references still fail visibly. + +## Hardened delegated runs + +- Dynamic Runtime Memory snapshots, recalled evidence, managed-document content, and Provider selection data now travel in the delegated run request instead of a child Agent's system persona. +- Child personas contain only static responsibilities, tool boundaries, and completion rules. User-controlled text can no longer become a system-prompt interpolation site in archive, compaction, answer, or placement workflows. + +## Compatibility + +- Storage files, schemas, tools, RPC contracts, and configuration are unchanged; no data migration is required. +- Existing literal braces are preserved exactly. Updating dsh-mnemon and restarting the affected DSH profile is sufficient. + +## Verification + +- Regression coverage exercises empty, multilingual, whitespace, legal and unknown variable names, adjacent and nested groups, the escape-variable name itself, and incomplete groups. +- The complete suite passes with 343 tests; one Windows-only smoke test is skipped only on non-Windows local runs. +- Deterministic builds, Headless activation, package validation, and CI on Ubuntu and Windows pass. + +See the [v0.2.8 release notes](./v0.2.8.md) for the previous patch release. diff --git a/dsh/docs/en/releases/v0.3.0.md b/dsh/docs/en/releases/v0.3.0.md new file mode 100644 index 000000000..7d40b2456 --- /dev/null +++ b/dsh/docs/en/releases/v0.3.0.md @@ -0,0 +1,108 @@ +# v0.3.0: Composable Memory Kernel + +[简体中文](../../zh-CN/releases/v0.3.0.md) | **English** | [Extension guide](../development/extensions.md) + +v0.3.0 lifts the existing three-tier system into a composable memory kernel while preserving one-package installation, default behavior, public tools, and existing data formats. Runtime, Documents, and Memory Spaces are independently switchable Layers. The existing routing behavior is expressed by the `default-three-tier` Strategy, and Provider construction is decoupled through a Registry/Factory seam. + +## What you need to do + +| Reader | Action | +|---|---| +| Existing user | Back up, update, restart, and verify the three default Layers. No data migration or new View interaction is required | +| New user | Keep all three default Layers enabled for the first workflow | +| Operator | Review the saved Layer switches and Status generations after changing scope, Layers, or extensions | +| Extension author | Import only public `dsh-mnemon/*` subpaths and register contributions through `mnemonMemory` / `MemoryBoot` | + +## Delivered + +- Wire-safe Contracts, `MemoryCatalog`, atomic `MemoryTopologyManager`, `MemoryKernel`, Guards, `MemoryPlan`, and `MemoryReceipt`. +- Separate Catalog, Topology, and Guard generation boundaries. A component or Guard change rejects an old Plan before execution and requires re-planning; accepted Plans are atomically claim-once, so sequential or concurrent replay cannot repeat a data-plane step. +- The ordinary Settings surface gives each Layer one master switch. Enabled means the default strategy may use it on demand; it never forces Recall. Disabled stops context, model tools, background processing, and data-plane Web/RPC together. +- The Kernel still distinguishes capabilities and trigger sources and enforces Guards authoritatively. Those constraints are control-plane internals, not fine-grained ordinary-user settings in v0.3. +- `MemoryBoot` and the Cordis `mnemonMemory` service (`MemoryExtensionHost` remains a v0.3 pre-release alias). Extensions may contribute Layers, Adapters, Strategies, Guards, and `MemorySource`s before Host mount or while it is live; unloading advances generations and Source readiness is transactional. +- A descriptor-driven Settings UI backed by the `memory-system` RPC endpoint. A discovered Layer needs no fixed frontend enum and starts disabled; Save writes only changed Layer `enabled` Booleans. +- Strategy manifests, permission boundaries, and deterministic replay primitives as the artifact interface for future model-generated shadow/canary/rollback workflows. +- Multiple private source workspaces while npm still publishes one `dsh-mnemon` package, with new `contracts`, `kernel`, `extension-sdk`, `provider-sdk`, `strategy-sdk`, default Layer, and default Strategy subpaths. +- Every root-Agent user turn pins one immutable, lightweight `MemoryTurnView`. Its bounded `Wake` preserves the complete Runtime projection and count-only covers for Documents and Memory Spaces; Source revisions and digests are stable across every model step in the turn. The complete Document and Memory Space ID sets remain Host-only and are not truncated to the Wake budget. +- Recall and related reads derive the root turn (including through a child agent's `parentSession`), validate optional Memory Space IDs against that pinned Host state, and call `MnemonService` directly. There is no model-facing View/node ID, Zoom tool, View capability, full catalog or selected-ID echo, or second recall LLM. Recall remains LLM-driven: an ordinary turn performs zero Provider queries. After one initial query, the LLM may explicitly issue one materially different refinement only when evidence is insufficient; same-query calls replay, a third query cannot reach a Provider, and no child Agent is created. Both attempts share one envelope of at most six results, 1,200 characters each and 4,800 content characters total; the initial admission reserves recovery capacity. One separately bounded Related traversal may follow. Brittle model-selected semantic filters are not exposed, low relevance is dropped, and medium/unknown evidence remains bounded. +- An authorized Mnemon Native smart search that loses exact structured anchors or sufficient focused-query lexical coverage after quality selection performs one bounded local keyword recovery. Recovered rows are threshold-validated, deduplicated, prioritized ahead of generic rows, and pass through the same quality and output envelopes; remote Providers, Recall scheduling, and model-call counts are unchanged. Each of the two LLM-chosen queries retains one bounded medium/unknown admission slot so refinement remains useful without broadening the shared six-result/4,800-character envelope. +- Model-facing Document search, Memory Space catalog, and status output now have separate hard envelopes. One root turn has one shared Documents search slot across root, child, and parallel calls; later calls return a compact no-I/O notice. Document search uses bounded Han bigrams so natural Chinese queries can reach relevant passages instead of becoming one unmatchable token, while focused queries require minimum token coverage to reject incidental partial overlaps. Complete records, provider settings, filesystem paths, detailed statistics, and inactive provider rows remain on Web/RPC control-plane surfaces instead of accumulating in conversation history. +- Runtime writes remain available for newly supplied facts and explicit save or correction requests, but a read-only question does not copy, promote, or summarize evidence returned by Documents, Recall, or Related into hot memory unless the user explicitly requests that promotion. +- Successful compatibility-Controller and Provider Authority mutations produce the standard `MemoryReceipt`. Receipts coalesce at the turn boundary so the next turn snapshots a fresh TurnView, while the current turn stays immutable. +- MEMORY.md capacity maintenance uses a no-tool planner only for routing and compaction candidates. The Host restricts routes to existing active writable Spaces, writes each original entry exactly, verifies committed receipts or exact Recall evidence, then applies compaction and the pending add/replace/remove in one revision-bound local commit. The pending or replaced/removed entry is never archived as an old source; invalid coverage, byte overflow, or a stale revision fails closed with Runtime retained. +- Background review first passes deterministic dirty-candidate and current-turn no-memory admission. Ordinary eligible activity with no persistence signal starts no child model; the short recall/writeback cue is emitted at most once per session. + +## Benchmark and behavioral verification + +These results are pinned to the 2026-08-24 product candidate `6066aa796326a98575e142d68883b11b09d38f13`, whose file tree is identical to the pre-identity-rewrite commit `95a4a450e62098614cbb443d12f6d675f5e75402` that actually ran the evaluation. The formal baseline is `dsh-mnemon@0.2.16` / `e5aae00797772bee34f414820b545d21391b84c7`, running on DSH `0.1.1-rc.2` with the real `deepseek-v4-flash` model. Behavioral gates are release evidence; token and latency deltas from a real Provider are observations in this environment, not an SLA across models, networks, or hardware. + +- All 27 final v0.3 full-suite runs completed. Two additional stable Recall gates each ran 20 fresh isolated sessions and passed semantics plus tool policy 40/40, with no Runtime, Documents, or Memory Space mutation, no Memory Space Catalog prefetch for Recall, and no child Agent. +- Recall remains an LLM choice: the no-durable-history steady state passed 16/16 with zero memory-tool calls. Only 1/20 misleading-wording runs used the one materially different refinement chosen by the LLM; the Host still prevented a third query. + +| Scenario | Correctness | v0.2.16 → v0.3 median tokens | Median user wall | Scheduling change | +|---|---|---:|---:|---| +| Autonomous Recall | 10/10 → 10/10 | 279,077 → 99,736 (-64.26%) | 86.95s → 14.87s (-82.90%) | child 13 → 0 | +| Isolated Recall | 5/5 → 5/5 | 119,352 → 22,609 (-81.06%) | 47.68s → 8.59s (-81.99%) | child 9 → 0 | +| Capacity maintenance | baseline completed 2/2; v0.3 3/3 | 137,879 → 37,275 (-72.97%) | 58.88s → 28.27s (-51.98%) | child 5 → 1 | +| Steady state | 16/16 → 16/16 | 85,650 → 84,741 (-1.06%) | 8.39s → 8.38s | zero memory tools in both | + +| Context and retrieval | v0.2.16 | v0.3.0 | Interpretation | +|---|---:|---:|---| +| Realistic-corpus first request | 10,198 tokens | 10,167 tokens | No new fixed-context token tax | +| 80 Documents + 80 Spaces first request | 10,200 tokens | 10,169 tokens; 4,158-character Wake | Titles, IDs, descriptions, and content do not enter the Prompt as the catalog grows | +| Direct retrieval hit@1 | 41/60 | 60/60 | Better long-tail coverage | +| Direct retrieval hit@6 | 45/60 | 60/60 | All 60 current-version expected-bearing queries hit | +| Median result body | 304 characters | 304 characters | Recall quality did not come from expanding result bodies | +| Local retrieval P95 | 43.22ms | 62.73ms | A 19.51ms explicit cost for bounded local recovery | + +Bidirectional canonical-data verification passed 4/4 with no migration. Another 20 absent queries could return candidates in both versions, so a non-empty result is not proof that a fact exists. + +The tables above are the public frozen release record. The public [evaluation harness guide](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.3.0/scripts/evaluation/v0.3/README.md) documents the scenarios, methodology, safety boundaries, and rerun commands; each run writes machine-readable metrics into its selected output directory. Raw Provider traces, request/session events, and local paths are intentionally not committed. No private engineering archive is required to understand the published claims or rerun the harness. + +## Upgrade and data compatibility + +Upgrading from v0.2.16 requires no data migration. Runtime JSON/Markdown, Documents, the Memory Space registry, Provider service registry, Mnemon Native Stores, and remote Provider data formats are unchanged. + +TurnViews, Source authority, turn pins, and pending Receipts are Host runtime state and are not written into those user data formats. Existing settings, commands, RPC, WebUI, and user workflows require no View management. Recall uses the pinned Source state automatically and exposes no new interaction. + +Without `memoryTopology` configuration, resolution produces a topology equivalent to the old behavior: all three Layers are enabled under the fixed compatibility policy. Existing `storageScope`, `dataDir`, Provider, recall-quality, lifecycle, tool, and UI settings continue to apply. + +Recommended upgrade sequence: + +1. Back up the current Mnemon root or export a Mnemon Pack using the existing procedure. +2. Update `dsh-mnemon` and restart every DSH profile that uses it. +3. Confirm the three default Layers under **Settings → Memory System → Memory layers**. +4. Inspect Catalog/Topology generations and Providers on **Status**. +5. Open each Sidebar tab, then use one conversation turn to verify Runtime projection and on-demand Recall. + +`enabled=false` stops participation without deleting or moving data. The Sidebar tab remains visible with an Off badge; re-enabling uses the same directories. Before downgrading to v0.2.16, back up and remove v0.3-only Layer switches from YAML. No persistent format changed, so package rollback needs no reverse data migration. v0.2.x ignores these switches and restores the older hard-coded participation behavior even though canonical data remains readable. + +## Extension-author migration + +- Import only public `dsh-mnemon/*` subpaths, never repository-internal `src/` or `packages/` paths. +- Let the disposer returned by `mnemonMemory.register()` own component lifetime; do not maintain a second process-global Catalog. +- Contribute a `MemorySource` for each extension Layer that participates in automatic projection. Use `eager` only for bounded text that must enter every Wake; use `routed` for a bounded cover plus Host-only routing state. +- Keep Strategies to JSON-safe step proposals with no data-plane handles. Wrap permissions in a manifest and replay disabled-Layer, automatic/manual, budget, and escape cases. +- Honor `AbortSignal` in executors and let the Kernel produce `succeeded`, `partial`, `failed`, or `cancelled` Receipts. +- Re-plan after a stale-plan error instead of replaying old steps. + +The repository's old `src/memory-system/*` files are source-compatibility facades only. Third parties should use npm subpaths. + +## Security boundaries and current limits + +- Guards may only deny. A Strategy cannot broaden Host or participation authority, and a changed Guard set invalidates old Plans. +- A Cordis isolate owns scope and unloading; it does not sandbox malicious JavaScript. Layer executors, Provider Adapters, and Strategies must come from trusted packages. +- This release does not automatically execute newly model-generated code. It delivers manifests, permission wrapping, replay, and authoritative Kernel validation. +- Any new engine can integrate today as an independent Layer/Adapter. Fully dynamic Memory Space Provider settings cards, connection schemas, and persistent-registry registration are not generalized yet; the nine current Providers remain in the built-in Catalog. +- Existing controller data planes remain compatible and uniformly subject to Layer participation gates. Compatibility paths that do not yet execute a persisted `MemoryPlan` bridge committed Authority changes into the same `MemoryReceipt` contract instead of creating a second receipt type. + +## Verification scope + +Release verification covers TypeScript, the complete Vitest suite, reproducible double builds, imports of every public subpath, declaration resolution, real isolated Headless-profile activation, package contents, publint, and attw. New regressions focus on Catalog/Topology generations, live registration and unloading, Guard changes, stale Plans, partial Receipts, all eight three-Layer switch combinations, descriptor-driven Settings, and Sidebar disabled states. + +- The complete local suite passes `472` tests. One Windows-only smoke test is skipped only in the current non-Windows environment. +- The double build compares `103` generated files with identical hashes. All 10 Node-compatible public entries import directly; the Client entry is validated as a browser wrapper. +- An isolated Headless profile activates successfully and exposes 35 total tools to the model request, including all five representative Mnemon tools. +- The package contains 110 files; publint strict and the attw ESM profile pass. DSH `0.1.1-rc.2` UI primitives still emit only the upstream development-time missing-source-map warning. + +See the [v0.2.16 release notes](./v0.2.16.md) for the previous release. diff --git a/dsh/docs/en/releases/v0.3.1.md b/dsh/docs/en/releases/v0.3.1.md new file mode 100644 index 000000000..f1c4c1c9e --- /dev/null +++ b/dsh/docs/en/releases/v0.3.1.md @@ -0,0 +1,30 @@ +# v0.3.1: Entity Explorer and Sidebar Polish + +[简体中文](../../zh-CN/releases/v0.3.1.md) | **English** | [Capability map](../guides/capabilities.md) + +v0.3.1 is a focused Web UI patch for the v0.3 composable-memory line. It makes large entity collections faster to inspect, keeps long entity names inside their rail rows, aligns the Memory System entry with sibling sidebar plugins, and replaces fixed README badges with live public project indicators. + +## Updated + +- The Entity explorer now filters its already-loaded top-entity rail as the user types. Matching is locale-aware, case-insensitive, and local: typing does not add an RPC or Provider request, the existing **Inspect** submission remains unchanged, and Escape clears only the live filter. +- Long entity names stay on one truncated line while their frequency counts remain pinned inside the right edge of each row. Sidebar pagination and visible-result totals use the filtered collection. +- The Memory System sidebar entry now uses the same 36px row geometry, 18px glyph, fixed 24px icon box, spacing, active colors, and collapsed circular rail treatment as the current SSH and Task Board entries. +- The English and Chinese README headers now expose live npm version and download counts, the latest GitHub release, stars, license, and the matching dshfind badge instead of fixed product counters. + +Thanks to [@kiligzzz](https://github.com/kiligzzz) for the entity-rail fixes and live filter in [PR #72](https://github.com/omdsh-dev/dsh-mnemon/pull/72) and [PR #73](https://github.com/omdsh-dev/dsh-mnemon/pull/73), and to [@ch3n4y](https://github.com/ch3n4y) for the sidebar-entry alignment in [PR #76](https://github.com/omdsh-dev/dsh-mnemon/pull/76). + +## Compatibility and upgrade + +This patch changes Client UI behavior, CSS, tests, screenshots, and README presentation only. Host behavior, the composable memory kernel, configuration keys, RPC authority, public tool schemas, persistence formats, Providers, credentials, and existing Runtime, Document, and Memory Space data are unchanged. + +No data migration is required. Update `dsh-mnemon`, restart each DSH profile that uses it, and reopen the Memory System surface. Package rollback to `0.3.0` requires no reverse migration. + +## Verification + +- The complete TypeScript and Vitest chain passes with 473 tests; one Windows-only smoke test is skipped on the local non-Windows run. +- The deterministic double build verifies 103 generated files with identical hashes. +- An isolated real Headless profile activates with 35 total tools and five representative Mnemon tools exposed to the model request. +- The packed package contains 110 files; all 10 Node-compatible public entries import successfully, and publint strict plus the attw ESM profile pass. +- `git diff --check` passes for the release commit. + +See the [v0.3.0 release notes](./v0.3.0.md) for the composable-memory architecture and its frozen benchmark record. diff --git a/dsh/docs/en/releases/v0.3.2.md b/dsh/docs/en/releases/v0.3.2.md new file mode 100644 index 000000000..d4c1b0309 --- /dev/null +++ b/dsh/docs/en/releases/v0.3.2.md @@ -0,0 +1,28 @@ +# v0.3.2: Deterministic Runtime and Managed Embeddings + +[简体中文](../../zh-CN/releases/v0.3.2.md) | **English** | [Capability map](../guides/capabilities.md) + +v0.3.2 strengthens the v0.3 composable-memory line in three places: Runtime capacity recovery is deterministic and Host-authoritative, Mnemon Native embedding settings work in desktop sessions without shell configuration, and the invariant Runtime Memory protocol no longer repeats inside every changed dynamic context tail. + +## Updated + +- Runtime capacity recovery now bypasses the model when only one writable Memory Space is available. With multiple destinations, the worker receives bounded excerpts for routing only; the Host imports the exact original entries in destination batches, validates one terminal receipt per source, applies a revision fence, and deterministically packs the hot remainder before committing the pending mutation. Partial external writes leave the local Runtime content intact and safely retryable. This resolves [Issue #70](https://github.com/omdsh-dev/dsh-mnemon/issues/70) through [PR #78](https://github.com/omdsh-dev/dsh-mnemon/pull/78). +- Mnemon Native settings can now manage an Ollama-compatible embedding endpoint and model inside DSH. Saved values are injected only into Mnemon child processes, so Finder- or Dock-launched macOS sessions no longer depend on shell files, `launchctl`, or a LaunchAgent. A real `mnemon embed --status` check reports service reachability, the effective model, and Store coverage. This resolves [Issue #69](https://github.com/omdsh-dev/dsh-mnemon/issues/69) through [PR #79](https://github.com/omdsh-dev/dsh-mnemon/pull/79). +- The invariant 3,450-byte Runtime Memory read/write protocol now lives in a stable system-prompt section. Dynamic runtime context remains a complete replacement snapshot, with revision, entry-count, and UTF-8 usage metadata plus the exact USER.md and MEMORY.md bodies; moving the protocol and former separator removes 3,452 bytes from each changed dynamic tail without introducing a fragile delta chain. This is the safe first phase of [Issue #40](https://github.com/omdsh-dev/dsh-mnemon/issues/40), delivered by [PR #80](https://github.com/omdsh-dev/dsh-mnemon/pull/80); incremental upstream context support remains tracked in the open Issue. + +## Compatibility and upgrade + +No Runtime, Document, Memory Space, Provider, configuration, RPC, or tool persistence format changes require migration. The new managed embedding setting defaults off; omitted or disabled configuration preserves the existing inherited environment exactly. When enabled, Mnemon sends memory and query text to the configured endpoint, so use a trusted HTTPS service for remote deployments. Endpoint input rejects credentials, query strings, and fragments, and the status action cannot select an arbitrary per-call destination. + +Runtime projection remains a complete latest-snapshot replacement, preserving resume, fork, compaction, deletion, and context-trimming behavior. Provider batch writes are optional, so existing Providers retain their current per-entry `remember` fallback. Update `dsh-mnemon`, restart each DSH profile that uses it, and reopen the Memory System surface. Rollback to `0.3.1` requires no reverse migration. + +## Verification + +- The complete TypeScript and Vitest chain passes with 485 tests; one Windows-only smoke test is skipped on the local non-Windows run. +- The deterministic double build verifies 103 generated files with identical hashes. +- An isolated real Headless profile activates with 35 total tools and five representative Mnemon tools exposed to the model request. +- The packed package contains 110 files and 1,673,026 unpacked bytes, below the enforced 1,680,000-byte ceiling; all 10 Node-compatible public entries import successfully, and publint strict plus the attw ESM profile pass. +- The official Mnemon v0.2.3 Native CLI round-trip preserves dense archived source entries, while an official Mnemon v0.2.5 status integration confirms that the saved endpoint overrides a conflicting inherited value and reports the effective embedding model. +- `git diff --check` passes for the release changes. + +See the [v0.3.1 release notes](./v0.3.1.md) for the previous Web UI patch and the [v0.3.0 release notes](./v0.3.0.md) for the composable-memory architecture and frozen benchmark record. diff --git a/dsh/docs/en/releases/v0.3.3.md b/dsh/docs/en/releases/v0.3.3.md new file mode 100644 index 000000000..46eb3bf44 --- /dev/null +++ b/dsh/docs/en/releases/v0.3.3.md @@ -0,0 +1,31 @@ +# v0.3.3: Correct Checkpoint Subagent Usage + +[简体中文](../../zh-CN/releases/v0.3.3.md) | **English** | [Capability map](../guides/capabilities.md) + +v0.3.3 is a focused WebUI correctness patch for checkpoint-started subagents. Their catalog rows now report only the provider usage incurred by that child instead of adding the completed parent history inherited by the fork. + +## Fixed + +- Fork-backed subagents inherit the parent's durable event prefix, while DSH's generic `tokenUsage` projection intentionally totals the complete log. The official subagent dropdown previously treated that aggregate as child-run usage, so a parent using 8.7K tokens and a child using 7K appeared as 15.7K for the child. Mnemon now owns a descriptor-scoped projection that starts at the latest durable `subagent/descriptor`, replaces repeated usage samples from the same turn and step, and exposes the resulting value only to the official subagent catalog component. This was delivered through [PR #87](https://github.com/omdsh-dev/dsh-mnemon/pull/87). +- The compatibility adapter falls back to the official aggregate for legacy child sessions without a durable descriptor. DSH's kernel, checkpoint and fork behavior, generic complete-log `tokenUsage`, official UI source, navigation, locale, and styling remain unchanged. + +## Documentation + +- The English and Chinese README headers now include the live dshfind download count alongside the existing package and repository badges, delivered through [PR #82](https://github.com/omdsh-dev/dsh-mnemon/pull/82). + +## Compatibility and upgrade + +No Runtime, Document, Memory Space, Provider, configuration, RPC, tool, or persistence format changes require migration. The scoped projection is optional when the DSH projection service is absent, and the client adapter remains inert when the official subagent lineage slot is unavailable. + +Update `dsh-mnemon`, restart each DSH profile that uses it, and reopen the affected parent conversation. Existing session logs do not need rewriting; their scoped value is folded from the durable descriptor on read. Rollback to `0.3.2` requires no data rollback, but the dropdown will again show the inherited complete-log aggregate for checkpoint children. + +## Verification + +- The complete TypeScript and Vitest chain passes with 492 tests; one Windows-only smoke test is skipped on the local non-Windows run. +- The deterministic double build verifies 105 generated files with identical hashes. +- An isolated real Headless profile activates with 35 total tools and five representative Mnemon tools exposed to the model request. +- The packed package contains 112 files, 371,923 packed bytes, and 1,685,781 unpacked bytes, below the enforced 1,690,000-byte ceiling; all 10 Node-compatible public entries import successfully, and publint strict plus the attw ESM profile pass. +- A real official Web composition at unmodified DeepSeek Harness commit `b150a551b8d465e31e418e1b2eaf5e79bbb7d28e` renders the identical seeded checkpoint child as 15.7K tokens before the Mnemon adapter and 7K after it, while the parent retains its own 8.7K complete-log value. +- `git diff --check` passes for the release changes. + +See the [v0.3.2 release notes](./v0.3.2.md) for the previous Runtime and managed-embedding patch and the [v0.3.0 release notes](./v0.3.0.md) for the composable-memory architecture and frozen benchmark record. diff --git a/dsh/docs/en/releases/v0.3.4.md b/dsh/docs/en/releases/v0.3.4.md new file mode 100644 index 000000000..d38749b96 --- /dev/null +++ b/dsh/docs/en/releases/v0.3.4.md @@ -0,0 +1,33 @@ +# v0.3.4: Scoped Runtime Memory and Capacity Controls + +[简体中文](../../zh-CN/releases/v0.3.4.md) | **English** | [Capability map](../guides/capabilities.md) + +v0.3.4 makes Runtime Memory boundaries explicit across storage roots, git branches, and capacity budgets. It also hardens multi-target archival and restores guided memory reminders after a conversation rewind. + +## Added + +- `mnemon.runtimeUserScope: global` can now combine the global root's User Profile with Working Memory from the selected workspace or custom root. User writes and USER compaction stay global, while project writes, MEMORY compaction, Documents, Memory Spaces, and Provider state remain selected-scope. The default `storage` mode preserves the previous single-root behavior. This resolves [Issue #77](https://github.com/omdsh-dev/dsh-mnemon/issues/77) through [PR #103](https://github.com/omdsh-dev/dsh-mnemon/pull/103). +- Working Memory entries can carry an optional git `branches` list through the model tool, RPC, and Web add/edit forms. Each turn projects a scoped entry only on a matching checked-out branch; unscoped entries, User Profile entries, non-git workspaces, detached HEAD, and branch-detection failures retain the previous all-visible behavior. The complete JSON and Markdown sources remain visible and unchanged, while compaction preserves the scope and archival emits `branch:` tags. This resolves [Issue #93](https://github.com/omdsh-dev/dsh-mnemon/issues/93) through [PR #91](https://github.com/omdsh-dev/dsh-mnemon/pull/91). +- `mnemon.runtimeMemory` now configures the complete `MEMORY.md` and `USER.md` UTF-8 byte limits plus the completion-token budget for Runtime migration and compaction workers. Defaults remain 10,240 bytes, 4,096 bytes, and 8,192 tokens; validated custom limits are shared by Runtime writes, Pack operations, and background maintenance. This resolves [Issue #96](https://github.com/omdsh-dev/dsh-mnemon/issues/96) through [PR #102](https://github.com/omdsh-dev/dsh-mnemon/pull/102). + +## Fixed + +- When a model fails while routing a multi-target Runtime archive, the Host now routes that batch deterministically to the default writable Memory Space instead of abandoning the complete capacity transaction. Caller cancellation still aborts without Provider writes, invalid routes still fail Host validation, and mixed failed/successful batches retain the first successful delegated run's attribution without double-counting migration. This was delivered through [PR #84](https://github.com/omdsh-dev/dsh-mnemon/pull/84). +- A guided memory reminder removed from the model-visible surface by rewind is composed again on the next first step. Hosts without a surface projection retain the previous session-scoped fallback, and reminder text, ordering, and guided-turn accounting are unchanged. This was delivered through [PR #88](https://github.com/omdsh-dev/dsh-mnemon/pull/88). + +## Compatibility and upgrade + +No persistence migration is required. Existing configuration keeps the v0.3.3 behavior: User Profile follows `storageScope`, Runtime entries remain visible on every branch unless explicitly scoped, and the established capacity and worker limits remain active unless overridden. Both roots keep their complete existing JSON and Markdown data when the global-profile overlay is enabled; changing the setting never copies, merges, or deletes entries. A workspace Mnemon Pack intentionally excludes the separate global User Profile, so back up the global root separately when using this mode. + +Update `dsh-mnemon`, restart each DSH profile that uses it, and reopen the Memory System surface. Rollback to `0.3.3` needs no reverse migration. Older versions ignore the new configuration keys and preserve the on-disk data; branch-scoped records remain readable but become visible on every branch until v0.3.4 is restored. + +## Verification + +- The complete TypeScript and Vitest chain passes with 522 tests; one Windows-only smoke test is skipped on the local non-Windows run. +- The deterministic double build verifies 106 generated files with identical hashes. +- An isolated real Headless profile activates with 35 total tools and five representative Mnemon tools exposed to the model request. +- The packed package contains 113 files, 378,158 packed bytes, and 1,713,376 unpacked bytes, below the enforced 1,720,000-byte ceiling; all 10 Node-compatible public entries import successfully, and publint strict plus the attw ESM profile pass. +- Targeted regression coverage includes split-root authority and Pack exclusion, branch projection and scope preservation, custom capacity enforcement, routing fallback and cancellation, and reminder recovery after rewind. The isolated Web evidence retained in PRs #102 and #103 confirms the configured capacity display and global-profile scope selection. +- `git diff --check` passes for the release changes. + +See the [v0.3.3 release notes](./v0.3.3.md) for the previous checkpoint-subagent usage patch and the [v0.3.0 release notes](./v0.3.0.md) for the composable-memory architecture and frozen benchmark record. diff --git a/dsh/docs/en/releases/v0.3.5.md b/dsh/docs/en/releases/v0.3.5.md new file mode 100644 index 000000000..a8fca7639 --- /dev/null +++ b/dsh/docs/en/releases/v0.3.5.md @@ -0,0 +1,29 @@ +# v0.3.5: Dual DSH Host Compatibility + +[简体中文](../../zh-CN/releases/v0.3.5.md) | **English** | [Capability map](../guides/capabilities.md) + +v0.3.5 keeps the reproducible DSH 0.1.1-rc.2 npm baseline while adding source compatibility with the GitHub-only DSH 0.1.2-alpha.1 preview. One plugin build now spans both Host generations without parsing a version, inspecting function arity, or branching on capabilities. + +## Compatibility + +- The Web client follows the alpha's split ownership model: Session and Workspace state remain available through the controller/renderer services, the renderer owns slots, Chat-specific slot contracts no longer require the removed owner type, Workspace selection no longer depends on `recentWorkspaceId`, and third-party locale IDs pass through safely. +- The package no longer imports the removed `@deepseek-ai/dsh-client-runtime` entry directly. Its DSH client ordering dependency moves to `@deepseek-ai/dsh-client-ui-renderer`, while the published rc.2 dependency graph remains reproducible. +- Generic Mnemon Connection channels survive the alpha's ApiProxy-to-Remote/gateway migration, and the child-local subagent token-usage projection remains isolated from DSH's complete-log metric. +- Host RPC registration uses one unconditional call shape. Mnemon always supplies the trailing authority object required by rc.2; alpha's two-argument JavaScript implementation ignores the extra argument and applies its uniform authenticated browser-session boundary. + +## Security and upgrade + +No persistence or Provider-data migration is required. DSH 0.1.1-rc.2 still consumes the startup-only `remoteAccess` setting: management channels remain `loopback` by default and are promoted together only when explicitly configured as `trusted-host`. DSH 0.1.2-alpha.1 ignores that transport argument and requires its launch-token-derived browser session for every channel. Retaining the setting lets the same configuration move forward to alpha or roll back to rc.2 without weakening rc.2's defaults. + +Update `dsh-mnemon`, restart each DSH profile that uses it, and reopen the Memory System surface. Registry installations should continue using DSH 0.1.1-rc.2; the alpha is a source-verification target, not an npm installation target. Rolling back dsh-mnemon to 0.3.4 requires no data conversion. + +## Verification + +- The complete TypeScript and Vitest chain passes with 524 tests; one Windows-only smoke test is skipped on the local non-Windows run. +- A real Connection compatibility test registers the same three-argument call against both the installed rc.2 implementation and the source-linked alpha implementation. +- CI verifies Linux on Node.js 22.19 and 24, Windows on Node.js 24, and a dedicated Node.js 24 build linked to the exact upstream `dsh-v0.1.2-alpha.1` tag at commit `cd5ef814`. +- The deterministic double build verifies 106 generated files with identical hashes, and an isolated real Headless profile activates with 35 total tools and five representative Mnemon tools. +- The packed package contains 113 files, 378,527 packed bytes, and 1,714,783 unpacked bytes; all 10 Node-compatible public entries import successfully, and publint strict plus the attw ESM profile pass. +- `git diff --check` passes for the release changes. + +See [PR #105](https://github.com/omdsh-dev/dsh-mnemon/pull/105) for the compatibility implementation and evidence. See the [v0.3.4 release notes](./v0.3.4.md) for the preceding Runtime Memory scope and capacity release. diff --git a/dsh/docs/en/releases/v0.3.6.md b/dsh/docs/en/releases/v0.3.6.md new file mode 100644 index 000000000..c43129fad --- /dev/null +++ b/dsh/docs/en/releases/v0.3.6.md @@ -0,0 +1,32 @@ +# v0.3.6: Reliable Async Recall and Web Workflows + +[简体中文](../../zh-CN/releases/v0.3.6.md) | **English** | [Capability map](../guides/capabilities.md) + +v0.3.6 completes the asynchronous subagent Recall lifecycle fix and consolidates the Web navigation, CLI discovery, theme, Runtime Memory and managed embedding improvements merged since v0.3.5. The published DSH baseline remains 0.1.1-rc.2, with the same package verified against the source-only 0.1.2-alpha.1 preview. + +## What changed + +- **Stable asynchronous Recall.** A child retains the View and runtime authority captured when it is created, independently of parent completion, later parent turns, View collection and settings swaps. Each executing turn owns its Recall, Related and Documents budgets and caches; parallel calls within a turn share limits, while siblings and cold-resumed activations do not reuse another task's evidence or budget. Requested Memory Space subsets remain enforced during cache replay. This completes the partial fix from [#116](https://github.com/omdsh-dev/dsh-mnemon/pull/116) in [#130](https://github.com/omdsh-dev/dsh-mnemon/pull/130). +- **Plugin-owned Runtime Memory snapshots.** Mnemon supplies changed runtime snapshots in its own message, attributed to the plugin, instead of the Host's shared runtime-context projection. A memory change no longer forces other plugins' stable context sections to be emitted again. [#107](https://github.com/omdsh-dev/dsh-mnemon/pull/107) +- **Reliable Memory System navigation and CLI checks.** Panel snapshot methods preserve their store receiver on newer DSH clients. Switching between Memory System and other panels follows the visible host state, and configured CLI command names are resolved through PATH so status and version checks agree. [#113](https://github.com/omdsh-dev/dsh-mnemon/pull/113), [#127](https://github.com/omdsh-dev/dsh-mnemon/pull/127) +- **Consistent default themes and dialogs.** The Memory System canvas respects the host's official light/dark backgrounds. Shared dialogs use the host's layer-2 surface with a canvas backing, and transparent footers avoid double-compositing translucent themes. [#126](https://github.com/omdsh-dev/dsh-mnemon/pull/126), [#129](https://github.com/omdsh-dev/dsh-mnemon/pull/129) +- **Managed embedding credentials and protocol selection.** Native embedding settings accept an optional API key and `auto`, `ollama` or `openai` protocol override, including OpenAI-compatible endpoints without a `/v1` suffix. Disabled overrides leave the existing Host environment unchanged. [#117](https://github.com/omdsh-dev/dsh-mnemon/pull/117) +- **Reproducible verification.** The alpha job preserves its intentional source links through pnpm pre-run checks; real DSH Agent-loop and JSONL cold-resume tests now cover the asynchronous lifecycle. Package-size guards account for the added managed embedding and delegated-turn implementation without publishing test fixtures. [#121](https://github.com/omdsh-dev/dsh-mnemon/pull/121), [#130](https://github.com/omdsh-dev/dsh-mnemon/pull/130) + +## Safety and upgrade + +No Runtime, Documents, Memory Space or Provider-data migration is required. Existing embedding settings remain valid; API keys and protocol overrides are optional. Saved keys belong in the DSH settings location and must not be copied into release logs, screenshots or memory content. Model Recall without a valid executing-turn pin is rejected rather than inheriting the parent's latest View. + +An already-running child retains its original runtime until that activation is disposed. Wait for or cancel such tasks before retiring their old data location. Update `dsh-mnemon`, restart each DSH profile using it, and reopen Memory System. Registry installations should continue using DSH 0.1.1-rc.2; alpha is a source-verification target, not an npm installation target. + +Rolling back to 0.3.5 does not require data conversion, but restores the earlier Recall and Web behavior and removes this release's managed embedding key/protocol controls. Retain a settings backup before relying on those new overrides. + +## Verification + +- The complete local Node.js 24 verification chain passes 574 tests, with one Windows-only test skipped on macOS. The async fix adds 29 regression cases, including real DSH Agent-loop, tool-pipeline and JSONL cold-resume coverage. +- Linux Node.js 22.19 and 24, Windows Node.js 24, and the exact source-linked `dsh-v0.1.2-alpha.1` tag are checked by CI. The merged implementation passed all four jobs before release preparation. +- Deterministic double builds verify 107 generated files; an isolated real Headless profile exposes 35 tools including five representative Mnemon tools. Package-content validation, all 10 Node-compatible public-entry imports, publint strict and the attw ESM profile pass. +- The existing real npm WebUI before/after records are preserved in [navigation and CLI regressions](../../pr-assets/npm-sidebar-cli/README.md#english) and [dialog surface evidence](../../pr-assets/dialog-surfaces/README.md). Those records identify the exact pre-release patch builds and are not relabeled as release screenshots. +- Release changes pass `git diff --check`; generated `lib/` files and test-owned data are not committed. + +See the [v0.3.5 release notes](./v0.3.5.md) for the preceding dual-Host compatibility release. diff --git a/dsh/docs/en/releases/v0.4.0.md b/dsh/docs/en/releases/v0.4.0.md new file mode 100644 index 000000000..f3a1e8e10 --- /dev/null +++ b/dsh/docs/en/releases/v0.4.0.md @@ -0,0 +1,37 @@ +# v0.4.0: Sidebar-only Memory System + +[简体中文](../../zh-CN/releases/v0.4.0.md) | **English** | [Capability map](../guides/capabilities.md) + +v0.4.0 makes Sidebar the only Memory System workspace. It removes the builtin presentation and display-mode setting while retaining the v0.3 architecture and memory formats. The complete view-based upgrade is planned separately for v0.5 and is not included in this release. + +## Main changes + +- **One workspace entry.** Memory System opens only from the DSH sidebar; the plugin no longer registers a `conversation.view` tab. Existing Sidebar pages, navigation, dialogs, and workspace selection remain available. [#128](https://github.com/omdsh-dev/dsh-mnemon/pull/128) +- **No display-mode selector.** Remove the presentation setting, its bilingual copy, and the builtin-only appearance, navigation, inline-editing, and styling branches. The minimal Sidebar presentation is now unconditional. +- **Live visibility remains independent.** `tabEnabled=false` hides the Sidebar entry and workspace live; enabling it restores Sidebar. Host RPC, commands, and Agent tools stay registered. Turn memory and Save to memory remain controlled independently by `mnemon-ui`. +- **Keep the v0.3.6 fixes.** Async Recall delegation, per-turn budgets, Runtime Memory snapshot ownership, panel navigation, CLI discovery, and host-aligned dialog surfaces remain intact. This release does not introduce the new view-based core, SDK, or standalone-plugin architecture. + +## Upgrade and compatibility + +This is an intentional presentation and configuration compatibility change, not a data-format migration. + +1. **Builtin users move to Sidebar.** The legacy value is spelled `buildin` in saved configuration. Existing YAML and user settings containing `displayMode`, including that value, still load; the field is ignored and the workspace uses Sidebar. Manual cleanup is optional, and no saved configuration file is automatically rewritten. +2. **Update typed integrations and settings writers.** `Config.displayMode` and `ResolvedConfig.displayMode` are removed. New settings RPC mutations that include `displayMode` are rejected. Remove the field from configuration objects and update payloads rather than relying on legacy read compatibility. +3. **Memory data is retained.** Runtime, Documents, Memory Space registries, Provider data, storage paths, credentials, and authority boundaries are unchanged. No memory data is migrated or deleted. +4. **Choose the minor-version upgrade explicitly.** Install `dsh-mnemon@0.4.0` in the owning DSH profile, then restart profiles using it and reopen Memory System. A dependency constrained to `^0.3.x` does not automatically select v0.4.0. Back up settings and data before upgrading. + +The published DSH baseline remains 0.1.1-rc.2. DSH 0.1.2-alpha.1 remains a source-only compatibility target, not an npm installation target. + +## Rollback + +Reinstall `dsh-mnemon@0.3.6` in the same profile and restart it. No data conversion is needed. A retained legacy `displayMode: buildin` preference becomes effective again on the older version; remove it or select Sidebar there if that is not desired. + +## Verification + +- Local `pnpm run verify` passed on macOS with Node.js 25.1.0: 582 tests passed, with one Windows-only test skipped. Deterministic double builds matched 107 generated files; isolated real Headless activation exposed 35 tools, including 5 representative Mnemon tools. Package-content checks covered 114 files; all 10 Node-compatible public entry imports, publint strict, and the attw ESM profile passed. +- Sidebar-only regressions cover legacy `buildin`/`sidebar` settings, absence of the display selector, live visibility, and rejection of new `displayMode` writes. +- The packed v0.4.0 tarball was installed into an isolated real DSH Web profile with legacy `displayMode: buildin`. Sidebar open/return/reopen, the selector-free settings page, Runtime dialog, and 680px layout with Escape dismissal passed without browser console errors. This UI-only fixture used synthetic data and an intentionally unavailable native CLI; no native-Mnemon end-to-end or real-model result is claimed. +- The merged implementation passed Linux Node.js 22.19 and 24, Windows Node.js 24, and source-linked DSH 0.1.2-alpha.1 CI before release preparation. +- The [Sidebar-only settings screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/screenshots/settings-sidebar-only.png) is pre-release evidence from the implementation PR, not a relabeled npm-release capture. Existing Web navigation and dialog evidence retains its original version labels. + +Previous release: [v0.3.6](./v0.3.6.md). Future architecture work: [versioned roadmap](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.4.0/docs/en/roadmap.md). diff --git a/dsh/docs/en/releases/v0.4.1.md b/dsh/docs/en/releases/v0.4.1.md new file mode 100644 index 000000000..9968c31a7 --- /dev/null +++ b/dsh/docs/en/releases/v0.4.1.md @@ -0,0 +1,33 @@ +# v0.4.1: Reliable settings subscriptions and timezone-safe Packs + +[简体中文](../../zh-CN/releases/v0.4.1.md) | **English** | [Capability map](../guides/capabilities.md) + +v0.4.1 is a focused patch for the Sidebar-only v0.4 series. It fixes DSH settings-store callback binding and Mnemon Pack ZIP timestamps without changing the memory architecture, configuration contract, or data formats. + +## Fixes + +- **Memory System no longer crashes on this-dependent settings stores.** Both the workspace and Save to memory action wrap `subscribe` and `getSnapshot` with stable callbacks before passing them to React. The real host store keeps its receiver instead of being invoked as an unbound method. [#133](https://github.com/omdsh-dev/dsh-mnemon/pull/133), [#136](https://github.com/omdsh-dev/dsh-mnemon/issues/136) +- **Pack export works in timezones behind UTC.** ZIP entry date fields are pinned to local 1980-01-01, preventing a UTC epoch from becoming an out-of-range 1979 date. Identical payloads with the same export time now produce identical archive bytes across timezones. [#137](https://github.com/omdsh-dev/dsh-mnemon/pull/137), [#112](https://github.com/omdsh-dev/dsh-mnemon/issues/112) +- **Regression coverage matches the failure modes.** Settings tests use this-dependent class methods. Pack tests compare complete archives for full, runtime, documents, and memory-spaces exports across UTC, Los Angeles, UTC-12, UTC+14, and Kolkata. + +## Upgrade and compatibility + +Install `dsh-mnemon@0.4.1` in the owning DSH profile, restart that profile, and reopen Memory System. Users upgrading from v0.3.x must also read the [v0.4.0 Sidebar-only compatibility notes](./v0.4.0.md#upgrade-and-compatibility). + +- Runtime, Documents, Memory Space data, Pack manifests and checksums, RPC permissions, storage paths, and Provider credentials are unchanged. Existing Packs remain readable; no migration or deletion is required. +- Only the fixed ZIP entry timestamp changes. UTC exports retain the prior timestamp fields; other timezones now use the same fields. +- DSH 0.1.1-rc.2 remains the published dependency baseline. DSH 0.1.2-alpha.1 remains a source-only compatibility target. +- The Sidebar-only workflow from v0.4.0 is retained. The separately planned v0.5 view-based architecture is not included. + +## Rollback + +Reinstall `dsh-mnemon@0.4.0` in the same profile and restart it. No data conversion is needed, and existing Packs remain readable, but the settings-store crash and timezone-dependent export behavior return. + +## Verification + +- On macOS arm64 with Node.js 25.1.0 and pnpm 11.19.0, `TZ=America/Los_Angeles pnpm run verify` passed: 588 tests passed and one Windows-only smoke test was skipped. Deterministic double builds matched 107 files; isolated real Headless activation exposed 35 tools, including 5 representative Mnemon tools. Package-content checks covered 114 files, and all 10 Node-compatible public entries, publint strict, and attw passed. +- The complete Pack suite passed 16/16 in separately launched UTC, America/Los_Angeles, UTC-12, and UTC+14 processes. +- The actual v0.4.1 tarball was installed into an isolated npm DSH 0.1.1-rc.2 Web profile with Web UI 0.3.6 and checksum-verified Mnemon 0.2.5. All four memory pages rendered, Save to memory opened and canceled, and Settings reported successful ZIP generation in America/Los_Angeles, with no browser console errors. The browser download-completion event was not captured; downloaded-file persistence was not independently verified. The fixture used synthetic data and a local fixed model response, not a paid model or personal memory. +- Both implementation PRs passed Linux Node.js 22.19 and 24, Windows Node.js 24, and source-linked DSH 0.1.2-alpha.1 CI before merge. + +Previous release: [v0.4.0](./v0.4.0.md). diff --git a/dsh/docs/en/releases/v0.4.2.md b/dsh/docs/en/releases/v0.4.2.md new file mode 100644 index 000000000..bd9603983 --- /dev/null +++ b/dsh/docs/en/releases/v0.4.2.md @@ -0,0 +1,40 @@ +# v0.4.2: Shared Builtin placement and canonical settings + +[简体中文](../../zh-CN/releases/v0.4.2.md) | **English** | [Configuration](../reference/configuration.md) + +v0.4.2 restores optional conversation placement for the Sidebar-first memory workspace. It does not restore a separate legacy UI or include the planned v0.5 view-based architecture. [#142](https://github.com/omdsh-dev/dsh-mnemon/pull/142), [#139](https://github.com/omdsh-dev/dsh-mnemon/issues/139) + +## Changes + +- **One workspace, two placements.** Sidebar remains the default. Choose Builtin in Settings to show the same pages, dialogs, and styling in a conversation tab; changes apply live and the entries never mount together. +- **Automatic session scope.** Builtin hides the scope badge, workspace picker, and alignment controls. Global storage remains shared, workspace storage follows the owning session's cwd, and custom storage uses `dataDir`. Reads, writes, and independent tasks use the existing Host routing; the global USER.md exception is unchanged. +- **Canonical `builtin` spelling.** The historical `buildin` value is accepted but normalized to `builtin` in runtime/UI state and saved through DSH's revision-fenced settings writer. Startup, external settings edits, and old-client RPC writes are covered. Comments and unrelated fields are preserved; a concurrent newer Sidebar choice wins. +- **Safe lifecycle behavior.** `tabEnabled` controls the selected placement. Conversation shortcuts open the matching entry, and changing sessions clears stale data and editors without changing stored memories. + +## Upgrade and compatibility + +Install `dsh-mnemon@0.4.2` in the owning DSH profile, then restart that profile. To embed the workspace in each conversation: + +```yaml +mnemon: + displayMode: builtin + storageScope: workspace +``` + +- **Retained preferences become effective again.** v0.4.0–v0.4.1 ignored `displayMode: buildin`; v0.4.2 restores it and saves `builtin`. Select Sidebar if you want to retain the standalone entry. An omitted display setting still defaults to Sidebar. +- Profile-only aliases receive a canonical user-setting override; profile files are not rewritten. A read-only settings provider recognizes the alias without bypassing its permissions. Persistence failures are logged without disabling the normalized entry. +- Runtime, Documents, Memory Spaces, Pack formats, storage paths, Provider credentials, and RPC authority are unchanged. The only automatic migration is the display preference, not memory data. Existing v0.4.1 fixes remain included. +- Published DSH 0.1.1-rc.2 remains the dependency baseline; DSH 0.1.2-alpha.1 remains a source-only compatibility target. + +## Rollback + +Reinstall `dsh-mnemon@0.4.1` and restart the same profile. It ignores `displayMode` and returns to Sidebar without a data conversion. For a rollback to v0.3.x, first use its legacy `buildin` spelling or `sidebar`, since those versions do not accept canonical `builtin`. + +## Verification + +- The v0.4.2 release candidate passed `pnpm run verify`: 630 tests passed with one Windows-only skip on macOS; 108 deterministic build files; 35 Headless tools and five representative calls; a 115-file package; ten Node-compatible public entries; strict publint and attw checks. +- The actual v0.4.2 tarball was installed in an isolated DSH 0.1.1-rc.2 / Web-all 0.3.6 profile with the official Mnemon 0.2.5 CLI. Startup saved the old `buildin` preference as `builtin`; the UI displayed version 0.4.2, hid the Builtin scope controls, and resolved the owning workspace root. All four pages, three create-dialog open/cancel paths, Chinese/English switching, and all ten imports from the installed tarball passed. No memories or component updates were submitted; the model used only a local fixed response, with no paid model or remote Provider testing. +- Implementation PR #142 passed Linux Node.js 22.19/24, Windows Node.js 24, and source-linked DSH 0.1.2-alpha.1 CI before merge. +- The [implementation evidence](../../pr-assets/builtin-139/README.md) records the unmodified npm 0.4.1 control, real Mnemon 0.2.5 status, all three storage scopes, ten navigation round trips, and unchanged memory-file hashes. Its screenshots are explicitly from the pre-release local build, not a published 0.4.2 package. + +Previous release: [v0.4.1](./v0.4.1.md). diff --git a/dsh/docs/en/releases/v0.4.3.md b/dsh/docs/en/releases/v0.4.3.md new file mode 100644 index 000000000..61779316a --- /dev/null +++ b/dsh/docs/en/releases/v0.4.3.md @@ -0,0 +1,30 @@ +# v0.4.3: Aligned collapsed sidebar icon + +[简体中文](../../zh-CN/releases/v0.4.3.md) | **English** + +v0.4.3 fixes Memory System's collapsed sidebar icon without changing its expanded appearance. [Issue #141](https://github.com/omdsh-dev/dsh-mnemon/issues/141), [PR #144](https://github.com/omdsh-dev/dsh-mnemon/pull/144). + +## Fixes + +- Native DSH now applies the collapsed entry's centering and hidden-label rules without requiring a third-party Web UI frame attribute. +- The collapsed icon uses the same primary label color as neighboring controls. Its SVG viewport is 20px to compensate for internal artwork padding and align the visible icon size. +- Expanded styling keeps the original 18px SVG, spacing, label, and secondary color, including with Web UI 0.3.9. The fix changes one CSS file and adds no JavaScript or state. + +## Upgrade and compatibility + +Install `dsh-mnemon@0.4.3` in the owning DSH profile and restart it. Sidebar and optional Builtin placement retain the [v0.4.2 behavior and compatibility rules](./v0.4.2.md#upgrade-and-compatibility). + +No new configuration or migration is introduced. Memory data, storage paths, Pack formats, Provider credentials, RPC authority, and dependencies are unchanged. DSH 0.1.1-rc.2 remains the registry baseline; DSH 0.1.2-alpha.1 remains a source compatibility target. + +## Verification + +- The release candidate passed `pnpm run verify`: 630 tests passed and one Windows-only test was skipped on macOS; 108 deterministic build files, 35 Headless tools with five representative calls, all ten Node-compatible public entries, publint, and attw passed. +- The actual 115-file tarball was installed in isolated DSH 0.1.1-rc.2 profiles, both native and with Web UI 0.3.9, using the official Mnemon 0.2.6 CLI. Version 0.4.3, all four pages in Chinese/English, native light/dark icons, collapsed centering and 20px SVGs, unchanged expanded 18px SVGs, and ten imports from each installed package passed. No browser console errors were observed; existing dependency source-map and Web UI `session/list` warnings remain. Tests used synthetic data and a local model stub, without personal memory or paid model calls. + +The [implementation evidence](../../pr-assets/sidebar-icon/README.md) covers native light/dark themes, Web UI 0.3.9, collapse/expand, navigation, and measured icon geometry. Screenshots and raw measurements stay outside the npm package; the existing package-content check enforces its file whitelist. + +## Rollback + +Reinstall `dsh-mnemon@0.4.2` in the same profile and restart it. No data conversion is needed; the previous collapsed-icon appearance returns. + +Previous release: [v0.4.2](./v0.4.2.md). diff --git a/dsh/docs/en/releases/v0.4.4.md b/dsh/docs/en/releases/v0.4.4.md new file mode 100644 index 000000000..207603a69 --- /dev/null +++ b/dsh/docs/en/releases/v0.4.4.md @@ -0,0 +1,33 @@ +# v0.4.4: DSH session projection compatibility + +[简体中文](../../zh-CN/releases/v0.4.4.md) | **English** + +v0.4.4 fixes session history requests that fail with `Cannot read properties of undefined (reading 'parse')` on older DSH hosts. [Issue #147](https://github.com/omdsh-dev/dsh-mnemon/issues/147), [PR #149](https://github.com/omdsh-dev/dsh-mnemon/pull/149). + +## Fixes + +- The child-local token-usage projection now exposes both the legacy `schema` / `view` contract and the newer `stateSchema` / `wire` contract, sharing one wire schema and view function. +- Live sessions, detached history replay, and restored checkpoints keep working across both registry generations. Parent and ancestor usage remains excluded from child totals, and repeated samples for one step replace rather than accumulate. +- Malformed event payloads are ignored safely. The implementation also remains compatible with the DSH 0.1.2-alpha.1 source contract. + +## Upgrade and compatibility + +Install `dsh-mnemon@0.4.4` in the owning DSH profile and restart it. DSH 0.1.1-rc.2 remains the registry development baseline; DSH 0.1.2-alpha.1 remains a source compatibility target. Legacy compatibility tests use the published DSH 0.1.0-rc.8 projection registry. + +Projection state stays at `stateVersion: 1` with the same stored format. No migration or cache deletion is required. Memory data, storage paths, Pack formats, Provider credentials, RPC authority, settings, and production dependencies are unchanged. The legacy registry alias is only a development test fixture and is not shipped as a runtime dependency. + +Sidebar and optional Builtin placement keep the [v0.4.3 behavior](./v0.4.3.md). This patch contains no UI layout changes or v0.5 development work. + +## Verification + +The compatibility suite runs real legacy and current DSH registries with real sessions, covering empty snapshots, attached and detached replay, duplicate usage samples, checkpoint restoration, cross-generation checkpoints, change notifications, and obsolete-state refolding. See the [development guide](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.4.4/docs/en/development.md) for reproducible commands. + +- The release candidate passed complete `pnpm run verify` on Node 24.18.0 with pnpm 10.13.1: 647 tests passed and one Windows-only test was skipped on macOS. All 108 deterministic build files, Headless activation with 35 tools and five representative Mnemon tools, settings migration and restart, ten Node-compatible public entries, publint, and attw passed. +- A real HTTP comparison made 32 requests through the published legacy/current ApiProxy and projection registries. The unmodified v0.4.3 source reproduced the legacy history-tail failure; v0.4.4 passed ordinary/child sessions, attached/detached histories, and pagination controls while retaining correct projections on newer hosts. +- The 115-file tarball was installed in two isolated DSH 0.1.1-rc.2 Web profiles, native and with Web UI 0.3.9, using official Mnemon CLI 0.2.6. Both installations matched every packed file and imported all ten Node entries. Version 0.4.4, all four pages in Chinese and English, and Task Board round trips passed. A synthetic native conversation survived a browser reload with its history intact. No browser errors were observed; the Web UI's existing Host `session/list` warning remains. Tests used temporary data and a local model stub, without personal memory or paid model calls. + +## Rollback + +Reinstall `dsh-mnemon@0.4.3` in the same profile and restart it. No data conversion is needed, but older DSH hosts can encounter the session history failure again. + +Previous release: [v0.4.3](./v0.4.3.md). diff --git a/dsh/docs/en/releases/v0.4.5.md b/dsh/docs/en/releases/v0.4.5.md new file mode 100644 index 000000000..a136c27f8 --- /dev/null +++ b/dsh/docs/en/releases/v0.4.5.md @@ -0,0 +1,29 @@ +# v0.4.5: Better Sidebar compatibility + +[简体中文](../../zh-CN/releases/v0.4.5.md) | **English** + +v0.4.5 makes Memory System available inside an installed `dsh-better-sidebar` without replacing its normal DSH Sidebar entry. [Issue #153](https://github.com/omdsh-dev/dsh-mnemon/issues/153), [PR #157](https://github.com/omdsh-dev/dsh-mnemon/pull/157). + +## Fixes + +- In the default `displayMode: sidebar`, Mnemon now uses Better Sidebar's documented optional `registerTab` service. The single-instance tab has the stable id `dsh-mnemon:memory` and reuses the same Memory System view as the normal DSH Sidebar entry. +- Registration is activation-order independent: Better Sidebar can load before or after Mnemon, and service replacement, unload, or hot reload disposes and restores registration without duplicates. +- A Better Sidebar tab uses the session and workspace scope supplied for that tab rather than leaking the globally active session. Setting `tabEnabled: false` or `displayMode: builtin` continues to suppress this registration. + +## Upgrade and compatibility + +Install `dsh-mnemon@0.4.5` in the owning DSH profile and restart it. Better Sidebar remains optional and no new runtime dependency is added. The compatibility target tested here is `dsh-better-sidebar@0.18.1-alpha.0`; profiles without that service retain the existing standalone Sidebar behavior. + +Memory data, storage paths, Pack formats, Provider credentials, RPC authority, settings, production dependencies, and session projection state are unchanged. No migration or cache deletion is required. The [v0.4.4 session projection compatibility](./v0.4.4.md) remains intact, and this patch contains no unrelated v0.5 development work. + +## Verification + +The implementation and release candidate passed complete `pnpm run verify` on Node 24.18.0 with pnpm 10.13.1: 649 tests passed and one Windows-only test was skipped on macOS. All 109 deterministic build files, Headless activation with 35 tools and five representative Mnemon tools, settings migration and restart, ten Node-compatible public entries, the 116-file package contents, publint, and attw passed. PR #157 also passed the Linux Node 22.19/24, Windows Node 24, and DSH alpha source CI jobs. + +Two isolated real WebUI profiles covered both client activation orders. DSH 0.1.1-rc.2 with `@linxin666/dsh-web-all@0.3.6` loaded Better Sidebar before Mnemon; DSH 0.1.2-alpha.3 loaded Mnemon first. In both cases the normal Sidebar kept exactly one Memory System row, Better Sidebar exposed exactly one menu item, the scoped view rendered, and a fresh browser tab reported zero console errors and zero warnings. Reload restoration also passed in the reported-version profile. See the [environment record and screenshots](../../pr-assets/better-sidebar-153/README.md). + +## Rollback + +Reinstall `dsh-mnemon@0.4.4` in the same profile and restart it. No data conversion is needed. Better Sidebar can retain an already opened `dsh-mnemon:memory` tab in its own persisted layout; after rollback that tab can temporarily appear orphaned and may be closed. The normal DSH Sidebar entry remains available. + +Previous release: [v0.4.4](./v0.4.4.md). diff --git a/dsh/docs/en/releases/v0.4.6.md b/dsh/docs/en/releases/v0.4.6.md new file mode 100644 index 000000000..9dcd7fac8 --- /dev/null +++ b/dsh/docs/en/releases/v0.4.6.md @@ -0,0 +1,29 @@ +# v0.4.6: DSH Session event API compatibility + +[简体中文](../../zh-CN/releases/v0.4.6.md) | **English** + +v0.4.6 fixes new conversations that fail with `agent.session.events is not iterable` on DSH 0.1.2-alpha.4. It also covers the latest alpha.5 API while retaining DSH 0.1.1-rc.2 as the stable, recommended baseline. [Issue #156](https://github.com/omdsh-dev/dsh-mnemon/issues/156), [PR #159](https://github.com/omdsh-dev/dsh-mnemon/pull/159). + +## Fixes + +- Session event reads now use capability detection instead of version strings. Stable DSH 0.1.1-rc.2 continues to use `events[]`; DSH 0.1.2-alpha.4 and alpha.5 use `snapshotEvents()` and `eventAt()`. +- Lifecycle replay, turn detection, and subagent diagnostics share the same compatibility layer, so complete and indexed durable history follow one contract across the supported releases. +- An unknown future Session shape fails explicitly instead of silently treating durable history as empty. + +## Upgrade and compatibility + +Install `dsh-mnemon@0.4.6` in the owning DSH profile and restart it. DSH 0.1.1-rc.2 remains the stable registry baseline and recommended installation target. DSH 0.1.2-alpha.4 and the latest alpha.5 are preview compatibility targets; support is selected by the available Session capabilities rather than prerelease labels. + +Memory data, storage paths, Pack formats, Provider credentials, RPC authority, settings, production dependencies, and session projection state are unchanged. No migration or cache deletion is required. The [v0.4.4 projection compatibility](./v0.4.4.md) and [v0.4.5 Better Sidebar integration](./v0.4.5.md) remain intact, and this patch contains no unrelated v0.5 development work. + +## Verification + +The implementation and release candidate passed complete `pnpm run verify` on Node 24.18.0 with pnpm 10.13.1: 654 tests passed and one Windows-only test was skipped on macOS. All 110 deterministic build files, Headless activation, all ten Node-compatible public entries, package-content checks, publint, and attw passed. PR #159 also passed Linux Node 22.19/24, Windows Node 24, and source-linked DSH 0.1.2-alpha.5 CI jobs. + +Isolated real WebUI profiles reproduced the failure with published Mnemon 0.4.4 on DSH alpha.4, then completed synthetic conversations with this fix on alpha.4, alpha.5, and stable rc.2. All three corrected runs reported zero browser-console errors and warnings, and their Host logs contained no `error`, `warn`, or `not iterable` matches. See the [environment record and screenshots](../../pr-assets/session-events-156/README.md). + +## Rollback + +Reinstall `dsh-mnemon@0.4.5` in the same profile and restart it. No data conversion is needed. On DSH 0.1.2-alpha.4 or alpha.5, the original Session API error can return after that rollback; return DSH to stable 0.1.1-rc.2 as well if continued conversations are required. + +Previous release: [v0.4.5](./v0.4.5.md). diff --git a/dsh/docs/en/releases/v0.4.7.md b/dsh/docs/en/releases/v0.4.7.md new file mode 100644 index 000000000..cc9465a11 --- /dev/null +++ b/dsh/docs/en/releases/v0.4.7.md @@ -0,0 +1,44 @@ +# v0.4.7: DSH 0.1.2-rc.1 compatibility + +[简体中文](../../zh-CN/releases/v0.4.7.md) | **English** + +v0.4.7 promotes the published DSH 0.1.2-rc.1 release to dsh-mnemon's stable registry and development baseline. It retains complete source verification against the immediately preceding DSH 0.1.2-alpha.5 tag and a real WebUI backward regression on DSH 0.1.1-rc.2. [PR #161](https://github.com/omdsh-dev/dsh-mnemon/pull/161), [PR #162](https://github.com/omdsh-dev/dsh-mnemon/pull/162). + +## Compatibility changes + +- All 22 directly consumed DSH development packages are pinned to exact `0.1.2-rc.1` releases. Direct Store and Invariants dependencies close the published type and peer graph instead of relying on transitive installation accidents. +- The child-token selector now imports `SnapshotSelectorHook` from its public owner, `@deepseek-ai/dsh-client-store`; the UI Slots package remains the owner of `StoredEntry`. This is a type-boundary correction and does not change runtime token accounting. +- The lockfile contains one coherent rc.1 prerelease cohort. pnpm 11 release-age exceptions enumerate only the exact rc.1 packages present in that lockfile, while later `@deepseek-ai` publications remain quarantined. +- DSH 0.1.2-alpha.5 stays covered by the source-overlay job, including Store and Invariants. Capability-based Session reads continue to support both alpha.5/rc.1 snapshots and rc.2's `events[]` property without parsing package versions. + +## Cloud WebUI authentication + +The active deployment guide now follows the DSH 0.1.2-rc.1 transport: every page, RPC, and stream uses the browser session established from the Host's launch-token URL and signed, authority-bound cookie. `--trusted-host` remains a Host/Origin fence, and HTTPS plus deployment access controls remain required for a public entry. + +The previous DSH 0.1.1-rc.2 procedure remains documented as an explicit rollback path. Its method-specific authority tiers still require the startup-only Mnemon `remoteAccess: trusted-host` override for remote settings, backup, Provider-connection, and broad-mutation channels. DSH 0.1.2-rc.1 and alpha.5 ignore that retained compatibility setting. + +## Upgrade and compatibility + +Install stable DSH 0.1.2-rc.1, install `dsh-mnemon@0.4.7` in every owning profile, and restart those profiles: + +```sh +npm install -g @deepseek-ai/dsh@0.1.2-rc.1 +dsh plugin --profile web add dsh-mnemon@0.4.7 +dsh plugin --profile headless add dsh-mnemon@0.4.7 +``` + +DSH profiles have independent plugin rosters, so run only the profile commands that apply to the installation. Memory data, storage paths, Pack formats, Provider credentials, runtime configuration defaults, production dependencies, public package exports, and session projection state are unchanged. No migration or cache deletion is required. + +## Verification + +The implementation and release candidate passed complete `pnpm run verify` on Node 24.18.0 with pnpm 10.13.1: 654 tests passed and one Windows-only test was skipped on macOS. All 110 deterministic build files, isolated Headless activation, all ten Node-compatible public entries, the 117-file package-content check (383,848 packed bytes; 1,707,083 unpacked bytes), publint, and attw passed. + +PR #162 passed Linux Node 22.19/24, Windows Node 24, source-linked DSH 0.1.2-alpha.5, and bilingual contribution-policy jobs. Its pnpm 11.7 registry install checked 936 dependency entries against the exact release-age exclusion set. + +Isolated WebUI profiles completed status loading, Memory System navigation, a synthetic conversation, the read-only save dialog, and reload persistence on DSH 0.1.2-rc.1. A separate DSH 0.1.1-rc.2 profile completed the same conversation and reload regression. Browser warning/error logs and matching Host diagnostics were empty. The screenshots display plugin version 0.4.6 because they capture the executable compatibility commit before this version-only release preparation; no runtime source changed afterward. See the [environment record and screenshots](../../pr-assets/dsh-rc1-compat/README.md). + +## Rollback + +Reinstall `dsh-mnemon@0.4.6` in the same profiles and restart them. No data conversion is needed. v0.4.6 was not released against DSH 0.1.2-rc.1, so return DSH to its former 0.1.1-rc.2 baseline as well when a fully verified rollback is required; follow the rc.2-specific cloud procedure if that profile is remotely exposed. + +Previous release: [v0.4.6](./v0.4.6.md). diff --git a/dsh/docs/en/releases/v0.5.0-rc.1.md b/dsh/docs/en/releases/v0.5.0-rc.1.md new file mode 100644 index 000000000..e646c9a5b --- /dev/null +++ b/dsh/docs/en/releases/v0.5.0-rc.1.md @@ -0,0 +1,43 @@ +# v0.5.0-rc.1: Composable Memory Plugins + +[简体中文](../../zh-CN/releases/v0.5.0-rc.1.md) | **English** | [Extension guide](../development/extensions.md) + +v0.5 introduces the View-based execution model and Source/Strategy extension SDK as a release candidate. The plugin architecture is developer-facing in this release; ordinary users retain the v0.4 Sidebar workflow. + +## Scope and compatibility + +- `dsh-mnemon` remains the one-package Starter. Runtime, Documents, Memory Spaces, the default Strategy, three Strategy enhancements, and nine Providers ship as sixteen independent plugins. The Starter installs the three enhancements disabled. Existing storage formats, workflows, and normal tools remain the user-facing contract; compared with v0.4, the only UI addition is Active capture, Light context, and Scoped composition switches under **Settings → Memory System**—there is no View page or plugin-management entry. No memory data is migrated or deleted. +- Core exposes `ctx.mnemonMemory`; authors register Sources/Strategies through `dsh-mnemon/extension-sdk` and verify them through `dsh-mnemon/testing`. Source-owned pages use the Client SDK and DSH lifecycle. Memory Spaces owns its Provider SDK and child fibers; there is no additional Core-level Space service. +- Source and Strategy are contribution roles, not compulsory package boundaries: one plugin may register both in one Fiber-owned batch. Private indexing and maintenance remain plugin-owned. Operations remain open: no universal memory taxonomy or tree is required. +- Each turn pins one immutable View. Source operations receive only View identity/scope and their own read grant, not another Source's projection or grants. A read grant or action offer never grants write authorization. +- Source facts/projection reads have bounded time and cooperative cancellation. Selected Sources are required by default; `required: false` explicitly permits omission. Failed optional Sources contribute neither routes nor actions, and sanitized diagnostics explain their absence. Generic guidance follows the active View; automatic three-tier review belongs only to `default-three-tier`. + +The extension SDK remains prerelease and may change before v0.5 becomes stable. Official packages pin the same tested version; external authors should test and declare compatible peer versions explicitly. An earlier experimental Source that read a full `request.view` must use `request.view.id`, `request.view.scope` and its own `request.grant` instead. Internal imports, historical symbols and Host RPC are not stable extension APIs. + +Mutation receipts must state their completion: accepted ingestion/candidates are not committed memory, and only confirmed commits carry `committedAt`. The receipt helper defaults to `unknown`; see the [operation and result contract](../development/extensions.md#open-operations-bounded-execution). + +## Upgrade and rollback + +Before trying the release candidate, stop writers and back up the complete effective memory root, workspace Documents, DSH profile/configuration and any separately managed Provider data. Test against copies with an isolated DSH profile; do not point a smoke test at personal memory. No data-format migration is introduced by this release. + +Opt in explicitly; the `rc` channel never replaces npm `latest`: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.0-rc.1 +``` + +Use `headless` instead of `web` for that profile, then restart DSH. The Starter installs all sixteen matching plugins; normal users do not install them individually. Stable users do not enter the release candidate implicitly through npm `latest`. + +Verify existing Runtime entries, Document search/read, Provider selection, recall/write receipts and settings before adopting the copy. To roll back, stop DSH, restore the previous profile and matching data backup together, then restart. Do not assume an old version understands data newly written by an experimental third-party plugin. + +## Deliberate release-candidate limits + +The default Host selects one complete Strategy with `memoryTopology.strategyId` and composes its root-turn View. The shipped-but-disabled `strategy-scoped`, `strategy-light-context`, and `strategy-auto-capture` packages can contribute simultaneously through distinct slots; users see only their three behavior switches. Slot conflicts are explicit, permissions and quotas stay shared, and no background worker is added. The documented Source/Strategy contract welcomes standalone community repositories, but v0.5 has no generic discovery, installation, or dependency-graph UI. Generic multi-scenario scheduling, automatic Strategy discovery/selection, plugin process sandboxing, and autonomous RSI deployment are also not promised. Generation replacement and isolated author tests provide useful building blocks for future RSI; they are not an autonomous evolution loop. External plugins execute trusted JavaScript, and cancellation cannot preempt synchronous or non-cooperative code. + +Release gates are the full workspace verification, all independent plugin repositories, tarball-only installation, external Source/Strategy/Provider/Client tests, copied-data compatibility, performance and real WebUI checks. Controlled HTTP tests do not certify every live Provider account; a fixed model stub does not measure real-model memory quality. See [verification and coordinated publication](../development/README.md). + +## Related community requests + +- `strategy-auto-capture` provides an opt-in, current-turn capture policy for the workflow requested in [Issue #110](https://github.com/omdsh-dev/dsh-mnemon/issues/110). It does not add an unsupervised background writer, so the broader request remains open for feedback. +- `strategy-light-context` provides a bounded projection policy related to the cost concern in [Issue #40](https://github.com/omdsh-dev/dsh-mnemon/issues/40). It deliberately does not claim revisioned incremental injection; that upstream-dependent part remains open. +- `strategy-scoped` generalizes Source ordering and writable subsets around the global/project composition use case originally raised in [Issue #77](https://github.com/omdsh-dev/dsh-mnemon/issues/77). diff --git a/dsh/docs/en/releases/v0.5.0.md b/dsh/docs/en/releases/v0.5.0.md new file mode 100644 index 000000000..f6a9f8fb7 --- /dev/null +++ b/dsh/docs/en/releases/v0.5.0.md @@ -0,0 +1,47 @@ +# v0.5.0: Composable Memory Plugins + +[简体中文](../../zh-CN/releases/v0.5.0.md) | **English** | [Extension guide](../development/extensions.md) + +v0.5.0 introduces the View-based execution model and the Source/Strategy extension SDK as a stable npm release. The plugin architecture is developer-facing; ordinary users retain the v0.4 Sidebar workflow. + +## Scope and compatibility + +- `dsh-mnemon` remains the one-package Starter. Runtime, Documents, Memory Spaces, the default Strategy, three Strategy enhancements, and nine Providers ship as sixteen independent plugins. The Starter installs the three enhancements disabled. Existing storage formats, workflows, and normal tools remain the user-facing contract; compared with v0.4, the only UI addition is Active capture, Light context, and Scoped composition switches under **Settings → Memory System**—there is no View page or plugin-management entry. No memory data is migrated or deleted. +- Core exposes `ctx.mnemonMemory`; authors register Sources/Strategies through `dsh-mnemon/extension-sdk` and verify them through `dsh-mnemon/testing`. Source-owned pages use the Client SDK and DSH lifecycle. Memory Spaces owns its Provider SDK and child fibers; there is no additional Core-level Space service. +- Source and Strategy are contribution roles, not compulsory package boundaries: one plugin may register both in one Fiber-owned batch. Private indexing and maintenance remain plugin-owned. Operations remain open: no universal memory taxonomy or tree is required. +- Each turn pins one immutable View. Source operations receive only View identity/scope and their own read grant, not another Source's projection or grants. A read grant or action offer never grants write authorization. +- Source facts/projection reads have bounded time and cooperative cancellation. Selected Sources are required by default; `required: false` explicitly permits omission. Failed optional Sources contribute neither routes nor actions, and sanitized diagnostics explain their absence. Generic guidance follows the active View; automatic three-tier review belongs only to `default-three-tier`. + +The documented exports from `dsh-mnemon/extension-sdk`, `dsh-mnemon/testing`, `dsh-mnemon/contracts`, and the Source-owned SDKs are the supported v0.5 extension surface. Official packages pin one tested `0.5.0` cohort. External authors should declare and test compatible peer versions explicitly. Internal paths, historical symbols, and Host RPC are not public extension APIs; future pre-1.0 minor versions may revise the extension surface with release notes and migration guidance. + +Mutation receipts must state their completion: accepted ingestion/candidates are not committed memory, and only confirmed commits carry `committedAt`. The receipt helper defaults to `unknown`; see the [operation and result contract](../development/extensions.md#open-operations-bounded-execution). + +## Upgrade and rollback + +Before upgrading, stop writers and back up the complete effective memory root, workspace Documents, DSH profile/configuration, and any separately managed Provider data. Test against copies with an isolated DSH profile; do not point a smoke test at personal memory. v0.5.0 introduces no data-format migration. + +Install the stable release explicitly or use the npm `latest` channel: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.0 +``` + +Use `headless` instead of `web` for that profile, then restart DSH. The Starter installs all sixteen matching plugins; normal users do not install them individually. Existing exact RC installations must select `0.5.0` to leave the prerelease line. + +Verify existing Runtime entries, Document search/read, Provider selection, recall/write receipts, and settings against the copy before adopting it. To roll back, stop DSH, restore the previous profile and matching data backup together, then restart. Do not assume an older version understands data newly written by a third-party plugin. + +## Intentional v0.5 boundaries + +The default Host selects one complete Strategy with `memoryTopology.strategyId` and composes its root-turn View. The shipped-but-disabled `strategy-scoped`, `strategy-light-context`, and `strategy-auto-capture` packages can contribute simultaneously through distinct slots; users see only their three behavior switches. Slot conflicts are explicit, permissions and quotas stay shared, and no background worker is added. The documented Source/Strategy contract welcomes standalone community repositories, but v0.5 has no generic discovery, installation, or dependency-graph UI. Generic multi-scenario scheduling, automatic Strategy discovery/selection, plugin process sandboxing, and autonomous RSI deployment are also not promised. Generation replacement and isolated author tests provide useful building blocks for future RSI; they are not an autonomous evolution loop. External plugins execute trusted JavaScript, and cancellation cannot preempt synchronous or non-cooperative code. + +Release gates include full workspace verification, all independent plugin repositories, tarball-only installation, external Source/Strategy/Provider/Client tests, copied-data compatibility, performance checks, and real WebUI coverage. Controlled HTTP tests do not certify every live Provider account; a fixed model stub does not measure real-model memory quality. See [verification and coordinated publication](../development/README.md). + +## From the release candidate + +The runtime implementation, UI, configuration, and persisted formats are unchanged from `v0.5.0-rc.1`. The stable promotion includes the release-verifier correction already applied to the RC publication path and makes the same frozen, resumable workflow select npm `latest` and create a normal GitHub Release for stable semantic versions. + +## Related community requests + +- `strategy-auto-capture` provides the opt-in current-turn recording policy requested in [Issue #110](https://github.com/omdsh-dev/dsh-mnemon/issues/110). It does not add an unsupervised background writer. +- `strategy-light-context` provides a bounded projection policy related to the cost concern in [Issue #40](https://github.com/omdsh-dev/dsh-mnemon/issues/40). It deliberately does not claim revisioned incremental injection; that upstream-dependent part remains open. +- `strategy-scoped` generalizes Source ordering and writable subsets around the global/project composition use case originally raised in [Issue #77](https://github.com/omdsh-dev/dsh-mnemon/issues/77). diff --git a/dsh/docs/en/releases/v0.5.1.md b/dsh/docs/en/releases/v0.5.1.md new file mode 100644 index 000000000..fa6d4b562 --- /dev/null +++ b/dsh/docs/en/releases/v0.5.1.md @@ -0,0 +1,34 @@ +# v0.5.1: Desktop and Turn Reliability + +[简体中文](../../zh-CN/releases/v0.5.1.md) | **English** + +v0.5.1 is a coordinated patch release for the `dsh-mnemon` Starter and all sixteen official plugins. It fixes two failures reproduced on stable DSH 0.1.2-rc.1 and keeps the v0.5.0 composable architecture, configuration, extension surface, and stored data unchanged. + +## Fixes + +- The Desktop launcher now inserts beside the released Sidebar's new-session control through that control's actual parent. A nested `dshp-panelArea` no longer raises an `insertBefore` `NotFoundError`, and that mount failure no longer cascades into a read-only Memory System settings page. [Issue #176](https://github.com/omdsh-dev/dsh-mnemon/issues/176), [PR #177](https://github.com/omdsh-dev/dsh-mnemon/pull/177). +- Starting an ordinary turn now aborts and awaits an in-flight Idle Review before creating the new Turn View and binding the Agent runtime. It rechecks abort and turn state after cleanup, preserving exclusive runtime ownership without rejecting the user's turn as `already pinned`. [Issue #172](https://github.com/omdsh-dev/dsh-mnemon/issues/172), [PR #178](https://github.com/omdsh-dev/dsh-mnemon/pull/178). + +## Upgrade and compatibility + +Install the exact stable release in the owning DSH profile and restart it: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.1 +``` + +Use `headless` instead of `web` for a Headless profile. The Starter installs all sixteen matching `0.5.1` plugins; normal users do not install them individually. The stable npm channel remains `latest`. + +There is no configuration, RPC authority, credential, storage-path, memory-format, Document-format, Provider-data, or extension-contract migration. Existing v0.5.0 profiles and data remain valid. As before any upgrade, stop writers and back up the effective memory root, workspace Documents, DSH profile/configuration, and separately managed Provider data. + +## Verification + +Both failures were first reproduced in disposable real DSH 0.1.2-rc.1 WebUI profiles. The corrected Desktop profile kept settings writable and persisted `storageScope=workspace`; the corrected lifecycle profile completed a third conversation turn while an Idle Review was deliberately held open. The implementation PRs retain before/after screenshots and synthetic-only fixtures. + +Focused Sidebar/settings and lifecycle regression suites passed, followed by complete workspace verification. The release pipeline additionally checks deterministic builds, all public entries on Node 20, all seventeen packed artifacts, sixteen independently installed plugin repositories, an external SDK consumer, real packed DSH composition, and a Registry-backed v0.4.7 upgrade. PR CI was reduced to two complementary heavy jobs and a bounded four-worker artifact queue without removing those release gates. [PR #179](https://github.com/omdsh-dev/dsh-mnemon/pull/179). + +## Rollback + +Reinstall `dsh-mnemon@0.5.0` in the same profile and restart DSH. No data conversion is required, but the nested Desktop settings failure and Idle Review pin race can return after rollback. Restore a matching pre-upgrade profile/data backup if any writes occurred after upgrading. + +Previous release: [v0.5.0](./v0.5.0.md). diff --git a/dsh/docs/en/releases/v0.5.10.md b/dsh/docs/en/releases/v0.5.10.md new file mode 100644 index 000000000..160c56828 --- /dev/null +++ b/dsh/docs/en/releases/v0.5.10.md @@ -0,0 +1,44 @@ +# v0.5.10: Exact Memory Writes and Bounded Automatic Review + +[简体中文](../../zh-CN/releases/v0.5.10.md) | **English** + +v0.5.10 includes Runtime matching in [#256](https://github.com/omdsh-dev/dsh-mnemon/pull/256), verified OpenViking writes in [#234](https://github.com/omdsh-dev/dsh-mnemon/pull/234), and automatic-review limits and Agent Teams compatibility in [#257](https://github.com/omdsh-dev/dsh-mnemon/pull/257). + +## Runtime and OpenViking writes + +Runtime replace and remove prefer a unique exact content match before trying a unique substring. An entry containing only `X` can therefore be edited or removed alongside `EGO_LINUX_CHROME`. Duplicate exact matches still require disambiguation, and refusal leaves existing data intact. + +OpenViking creates a unique file containing the original text, verifies indexing and exact content readback, then returns the scoped file identity used by Host receipts and archive compensation. It no longer treats an extraction response as proof that the requested text was stored. Search and browse return full content. Account isolation, legacy owner-root resolution and deletion scope are validated; incomplete or uncertain writes return an error with the candidate URI. The original contributor's commit is preserved in the merged history. Servers lacking the required content write/read contract fail explicitly; no settings or stored content are migrated. + +## Automatic review + +Automatic review has an independent switch, a default five-minute minimum interval and a limit of 20 attempts per loaded parent Agent. The default spawn path bounds its context; fork remains explicit, with fallback or skip available before child startup. The per-response token limit is not a total usage cap. Attempt budgets reset when the Agent unloads or the Host restarts; existing session history is retained. + +With the affected published Agent Teams service and tools enabled together, automatic review pauses before creating a child and explains the incompatible composition. This contains the automatic-review failure; it does not repair upstream Team policy or manual delegation. A failed review can expose receipts for writes it already committed so users can reconcile them without replaying the run. + +## Packages and upgrade + +| Package | Version | +| --- | --- | +| `dsh-mnemon` | **0.5.10** | +| `dsh-mnemon-provider-openviking` | **0.5.5** | +| `dsh-mnemon-source-memory-spaces` | **0.5.8** | +| `dsh-mnemon-source-runtime` | **0.5.7** | + +The Starter pins the complete sixteen-plugin composition. The other thirteen plugins retain their versions. Mnemon CLI remains independently maintained. + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.10 +``` + +Restart DSH and reload the browser. Substitute `headless` for that Profile. The full DSH profile requires Node `^22.19.0 || >=24.0.0`. + +## Verification + +The combined implementation passed 1,162 root tests with five opt-in skips, plugin suites, actual Mnemon 0.2.8 CLI checks, Headless activation/restart, deterministic builds and public package checks. Sixteen independent plugins and seventeen artifacts passed installation and composition verification, including all three optional Strategy extensions together. Real OpenViking 0.4.20 passed create/read/search/delete checks; the published DSH 0.1.5-rc.2 and Agent Teams 0.1.5-alpha.2 matrix verified the compatibility pause. + +Real WebUI before/after screenshots and exact tested revisions are retained in the [Runtime report](../../pr-assets/issue-217-exact-runtime-match/README.md), [OpenViking report](../../pr-assets/issue-233-openviking-20260916/README.md) and [automatic-review report](../../pr-assets/idle-review-agent-team/README.md). Model replies and embeddings use deterministic local fixtures; these checks do not measure live model judgment or semantic recall quality. + +The release workflow verifies the versioned workspace and independent artifacts, publishes the three changed plugins before the Starter, checks the complete Registry composition and a real upgrade from v0.4.7, then creates the GitHub Release. + +Previous release: [v0.5.9](./v0.5.9.md). diff --git a/dsh/docs/en/releases/v0.5.11.md b/dsh/docs/en/releases/v0.5.11.md new file mode 100644 index 000000000..1631743ab --- /dev/null +++ b/dsh/docs/en/releases/v0.5.11.md @@ -0,0 +1,30 @@ +# v0.5.11: Quiet Windows Git Branch Detection + +[简体中文](../../zh-CN/releases/v0.5.11.md) | **English** + +v0.5.11 includes [#260](https://github.com/omdsh-dev/dsh-mnemon/pull/260), fixing [#259](https://github.com/omdsh-dev/dsh-mnemon/issues/259). Runtime Git branch detection now hides its Windows console, preventing brief terminal windows when a GUI host refreshes branch-scoped memory. The Git command, two-second timeout, branch filtering and failure fallback are unchanged. + +## Packages and upgrade + +| Package | Version | +| --- | --- | +| `dsh-mnemon` | **0.5.11** | +| `dsh-mnemon-source-runtime` | **0.5.8** | + +The Starter pins the complete sixteen-plugin composition. The other fifteen plugins retain their versions. No configuration or stored-memory migration is needed; Mnemon CLI remains independently maintained. + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.11 +``` + +Restart DSH and reload the browser. Substitute `headless` for that Profile. The full DSH profile requires Node `^22.19.0 || >=24.0.0`. + +## Verification + +Four real Windows process trials observed 104–105 visible console windows per baseline trial and zero after the fix. Each trial made 104 real Git calls and verified branch results and fallback behavior from a console-free parent. The new regression failed before the fix; all 59 Runtime tests pass afterward. + +The implementation passed 1,162 root tests with five opt-in skips, actual Mnemon CLI 0.2.8 checks, real macOS WebUI operations, Headless startup/restart and sixteen-plugin/seventeen-artifact composition checks. All three optional Strategies were enabled together. The [bilingual evidence report](../../pr-assets/issue-259-windows-git-console/README.md) retains tested revisions, before/after screenshots and downloadable Windows observations. It distinguishes Windows process validation from macOS WebUI checks and does not claim a complete replay of the reporter's Windows 11 Electron environment. + +The release workflow verifies versioned artifacts, publishes Runtime before the Starter, checks the full Registry composition and a real upgrade from v0.4.7, then creates the GitHub Release. + +Previous release: [v0.5.10](./v0.5.10.md). diff --git a/dsh/docs/en/releases/v0.5.12.md b/dsh/docs/en/releases/v0.5.12.md new file mode 100644 index 000000000..c624920d6 --- /dev/null +++ b/dsh/docs/en/releases/v0.5.12.md @@ -0,0 +1,29 @@ +# v0.5.12: DSH RC and Alpha Compatibility + +[简体中文](../../zh-CN/releases/v0.5.12.md) | **English** + +v0.5.12 includes [#263](https://github.com/omdsh-dev/dsh-mnemon/pull/263), fixing [#261](https://github.com/omdsh-dev/dsh-mnemon/issues/261). The turn memory bar registers with a stable ID required by DSH 0.1.6-alpha.2's list slot while retaining the 0.1.5-rc.2 chain selector. Activity is read and displayed only for completed turns. The save-to-memory action also mounts correctly after registration succeeds. + +Sidebar and Settings now follow DSH's public default/main session binding. Builtin and Better Sidebar retain their explicitly owning session, including an absent session, so switching workspaces does not borrow another session's memory scope. + +## Packages and upgrade + +Only **`dsh-mnemon` 0.5.12** is newly published. All sixteen companion plugins retain their versions and exact Starter pins. No configuration or stored-memory migration is required; Mnemon CLI remains independently maintained. + +The Client compatibility floor is now **DSH 0.1.5-rc.1**. Both root framework peers declare `^0.1.5-rc.1 || ^0.1.6-alpha.2`; 0.1.5-rc.2 and 0.1.6-alpha.2 were explicitly verified. Older DSH hosts without the public UI Session service must use their previously verified Mnemon release. + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.12 +``` + +Restart DSH and reload the browser. Substitute `headless` for that Profile. The full DSH profile requires Node `^22.19.0 || >=24.0.0`. + +## Verification + +Both tested DSH cohorts passed full production public type checks, normal packed npm installation, real WebUI tool calls, Sidebar/Builtin navigation, turn-bar toggles and page reload. Alpha additionally verified two independent peer slot contributions and memory isolation when switching workspaces. Packed Headless runs checked seventeen Mnemon tools, actual Runtime writes/status, persistence across a new process and zero tools after disabling Root. + +The implementation passed 1,183 root tests with five opt-in live Flash skips, actual Mnemon Native CLI 0.2.8 integration, and sixteen-plugin/seventeen-artifact composition and upgrade checks. Live OpenViking and Windows-only checks were skipped in the macOS run. The [bilingual evidence report](../../pr-assets/issue-261-dsh-slots/README.md) retains original before/after screenshots, artifact integrity and reproducible scripts. Its model responses use an explicit local OpenAI/Anthropic fixture; it makes no live-model quality claim. + +The release workflow revalidates the versioned artifacts, installs the full pinned Registry composition, publishes the Starter, and checks a real upgrade from v0.4.7 before creating the GitHub Release. + +Previous release: [v0.5.11](./v0.5.11.md). diff --git a/dsh/docs/en/releases/v0.5.13.md b/dsh/docs/en/releases/v0.5.13.md new file mode 100644 index 000000000..5b7c0c8e2 --- /dev/null +++ b/dsh/docs/en/releases/v0.5.13.md @@ -0,0 +1,45 @@ +# v0.5.13: Workspace, Builtin and DSH 0.1.7 Compatibility + +[简体中文](../../zh-CN/releases/v0.5.13.md) | **English** + +v0.5.13 includes three fixes: + +- [#268](https://github.com/omdsh-dev/dsh-mnemon/pull/268) fixes [#266](https://github.com/omdsh-dev/dsh-mnemon/issues/266): Windows workspace paths below a regular file are rejected with `ENOTDIR` before a storage identity is computed. Valid workspace identities, junctions, Unicode paths and missing directory descendants retain their behavior. Existing storage directories are not moved or deleted. +- [#269](https://github.com/omdsh-dev/dsh-mnemon/pull/269) fixes [#265](https://github.com/omdsh-dev/dsh-mnemon/issues/265): the conversation that owns a Builtin memory page hides its width handles while that page is open. The handles cannot intercept memory-page input; returning to Chat restores width dragging. The resident composer remains usable, and the rule stays scoped away from Sidebar and other conversations. +- [#270](https://github.com/omdsh-dev/dsh-mnemon/pull/270) fixes [#267](https://github.com/omdsh-dev/dsh-mnemon/issues/267): Mnemon uses DSH 0.1.7-alpha.1's Config-backed live settings, current icon exports and producer-owned Session message sources. Settings writes retain revision checks and profile ownership. Historical Mnemon messages still participate in deduplication, including the official Session migration format. + +## Packages and upgrade + +This release advances only **`dsh-mnemon` to 0.5.13**. All sixteen companion plugins and their exact Starter pins remain unchanged. Mnemon CLI remains independently maintained. + +The Client compatibility floor remains **DSH 0.1.5-rc.1**. The supported framework peer range is `^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-alpha.1`; the evidence covers 0.1.5-rc.2, 0.1.6-alpha.2 and 0.1.7-alpha.1 using unchanged public DSH packages. + +Back up the DSH profile configuration, legacy settings files and relevant Mnemon data before upgrading. + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.13 +``` + +Restart DSH and reload the browser. Substitute `headless` for that Profile. The full DSH profile requires Node `^22.19.0 || >=24.0.0`. + +## Settings recovery and rollback + +On DSH 0.1.7, supplemental recovery reads the retained `settings.yaml.imported` file without changing its bytes. Current explicit settings and native Strategy choices take precedence over older preferences; existing profile expressions remain expressions. Recovery preserves profile-local Source and Strategy choices across configuration reconciliation. + +If the original `settings.yaml` is present at startup, DSH first performs its own import. Mnemon's supplemental UI/View recovery waits for the next cold Host start, so this upgrade can require a second complete DSH stop/start. A browser refresh or plugin remount does not complete that deferred recovery. + +Automatic recovery requires the canonical `mnemon` root and the exact owning profile's legacy namespace. It does not infer custom-root ownership or reuse a foreign profile's suffix. Ambiguous or invalid input remains available in the retained backup for manual review. + +New profile settings are not exported back to the old settings file. Downgrading requires the corresponding configuration backup and manual reconciliation of later changes; there is no automatic reverse export. Runtime, Documents and Memory Spaces data formats are unchanged. + +## Verification + +The implementation evidence includes native Windows before/after observations, real Builtin hit testing and composer/Chat operations, and packed WebUI settings, Runtime writes and Session restoration on the three supported DSH cohorts. Model responses use an explicit loopback fixture without an external model API key; storage, tools and browser operations are real. + +- [Windows workspace evidence](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.13/docs/pr-assets/issue-266-workspace-ancestor/README.md) +- [Builtin width-handle evidence](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.13/docs/pr-assets/issue-265-builtin-width-handles/README.md) +- [DSH settings, migration and Session evidence](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.13/docs/pr-assets/issue-267-settings-migration/README.md) + +The release workflow must verify the versioned artifacts, the full pinned Registry composition and an actual v0.4.7 upgrade before creating the GitHub Release. + +Previous release: [v0.5.12](./v0.5.12.md). diff --git a/dsh/docs/en/releases/v0.5.14.md b/dsh/docs/en/releases/v0.5.14.md new file mode 100644 index 000000000..becc87e39 --- /dev/null +++ b/dsh/docs/en/releases/v0.5.14.md @@ -0,0 +1,45 @@ +# v0.5.14: Desktop Loading, OpenViking User Keys and Agent Teams Review + +[简体中文](../../zh-CN/releases/v0.5.14.md) | **English** + +v0.5.14 includes three fixes: + +- [#277](https://github.com/omdsh-dev/dsh-mnemon/pull/277) fixes [#274](https://github.com/omdsh-dev/dsh-mnemon/issues/274): profile bundles load on older Desktop hosts whose Cosmokit does not provide the Volatile APIs. Live settings remain available on newer hosts; Starter groups, component switches and existing settings namespaces are preserved. +- [#276](https://github.com/omdsh-dev/dsh-mnemon/pull/276) fixes [#271](https://github.com/omdsh-dev/dsh-mnemon/issues/271): OpenViking user-bound API keys can discover their own memory namespace without Admin access. Set the account and **User key owner (skip admin)** explicitly. Discovery validates the scoped data API before saving; an invalid replacement preserves the prior configuration and credentials. Default Admin discovery remains available. +- [#278](https://github.com/omdsh-dev/dsh-mnemon/pull/278) fixes [#275](https://github.com/omdsh-dev/dsh-mnemon/issues/275): an explicit scoped mode enables idle review with the verified DSH and official Agent Teams 0.1.7-rc.1 combination. The reviewer retains complete live-user decisions in its bounded checkpoint and can use only its authorized memory tools. Team delegation stays denied inside the reviewer, while the parent Team continues to work. + +## Packages and upgrade + +| Package | Previous | New | +|---|---|---| +| `dsh-mnemon` | 0.5.13 | **0.5.14** | +| `dsh-mnemon-provider-openviking` | 0.5.5 | **0.5.6** | +| `dsh-mnemon-source-memory-spaces` | 0.5.8 | **0.5.9** | + +The other fourteen companion packages retain their versions. The Starter pins the complete tested combination. Mnemon CLI remains independently maintained; the local verification uses the official CLI 0.2.9. + +Back up the DSH profile configuration and relevant Mnemon data, then upgrade: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.14 +``` + +Restart DSH and reload the browser. Substitute `headless` for that Profile. The Client floor remains DSH 0.1.5-rc.1; the complete DSH profile requires Node `^22.19.0 || >=24.0.0`. + +## Agent Teams compatibility and data + +**The default remains `idleReview.agentTeams: pause`.** Select `scoped` explicitly after using the verified DSH/Teams 0.1.7-rc.1 combination. Older DSH 0.1.5-rc.2 with Teams 0.1.5-alpha.2 can reject the reviewer with `TEAM_NOT_MEMBER`; this release does not automatically enable scoped review on that combination or bypass another plugin's policy. + +Runtime, Documents and Memory Spaces storage formats are unchanged. Profile ownership, revision checks and multi-plugin isolation remain in place. A downgrade restores the prior behavior and requires reconciliation of the new optional settings with the backed-up profile; no reverse migration is added. + +## Verification + +The linked records preserve the original failures and real WebUI results, including screenshots. They cover Desktop generation loading and cold restart, OpenViking scoped discovery and rejected-save retention, and a guarded Team reviewer with complete user evidence and a working parent Team. Provider/model responses use explicit loopback fixtures; browser operations, DSH tools, storage and CLI operations are real. OpenViking cloud-account behavior and live-model quality are not certified by these fixtures. + +- [Desktop profile evidence](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.14/docs/pr-assets/issue-274-profile-generation/README.md) +- [OpenViking discovery evidence](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.14/docs/pr-assets/issue-271-user-key/README.md) +- [Agent Teams and checkpoint evidence](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.14/docs/pr-assets/issue-275-agent-teams/README.md) + +The release workflow verifies versioned artifacts, all sixteen independent plugins, the full pinned Registry composition and a real v0.4.7 upgrade before creating the GitHub Release. + +Previous release: [v0.5.13](./v0.5.13.md). diff --git a/dsh/docs/en/releases/v0.5.15.md b/dsh/docs/en/releases/v0.5.15.md new file mode 100644 index 000000000..f0b6445c7 --- /dev/null +++ b/dsh/docs/en/releases/v0.5.15.md @@ -0,0 +1,46 @@ +# v0.5.15: USER Tool Writes, Runtime Metadata and Skin Hooks + +[简体中文](../../zh-CN/releases/v0.5.15.md) | **English** + +v0.5.15 includes these improvements: + +- [#282](https://github.com/omdsh-dev/dsh-mnemon/pull/282) fixes [#281](https://github.com/omdsh-dev/dsh-mnemon/issues/281): `mnemon_runtime_memory` accepts the empty `branches: []` placeholder supplied by model tool layers for `target=user`, restoring USER add, replace and remove operations. Non-empty USER branches remain rejected; MEMORY branch assignment and clearing are unchanged. +- [#283](https://github.com/omdsh-dev/dsh-mnemon/pull/283) resolves [#280](https://github.com/omdsh-dev/dsh-mnemon/issues/280): Runtime model-context projections include stored importance and creation/update ages in days. Default Strategy guidance explicitly acknowledges the context budget: an absent entry does not mean it was deleted. +- [#284](https://github.com/omdsh-dev/dsh-mnemon/pull/284) implements [#273](https://github.com/omdsh-dev/dsh-mnemon/issues/273): Sidebar and Builtin expose a stable Mnemon root selector supporting `--mn-bg`, `--mn-backdrop` and `--mn-surface` overrides. The [skin development guide](../development/skin-integration.md) covers dsh-web integration, migration from generated class names and verification. + +## Packages and upgrade + +| Package | Previous | New | +|---|---|---| +| `dsh-mnemon` | 0.5.14 | **0.5.15** | +| `dsh-mnemon-source-runtime` | 0.5.8 | **0.5.9** | +| `dsh-mnemon-strategy-default-three-tier` | 0.5.5 | **0.5.6** | + +The other fourteen companion packages retain their versions. The Starter pins the complete combination. Mnemon CLI remains independently maintained; local verification of the fixes uses the official CLI 0.2.9. + +Back up the DSH profile configuration and relevant Mnemon data, then upgrade: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.15 +``` + +Restart DSH and reload the browser. Substitute `headless` for that Profile. The Client floor remains DSH 0.1.5-rc.1; the complete DSH profile requires Node `^22.19.0 || >=24.0.0`. + +## Compatibility and data + +Original Runtime content, JSON/Markdown storage formats, exact matching and capacity accounting are unchanged. The Source generates the added metadata when compiling model projections; no data migration is needed. Each turn still uses a fixed snapshot, and Scoped, Light Context and Auto Capture remain composable. + +The official theme retains its default opaque background. Existing skins must explicitly use the public selector and properties to customize Mnemon backgrounds; installing a skin does not automatically enable a glass effect. Dialogs portaled to `body` keep their own styles. Skin integration does not change Provider or data permissions. + +## Verification + +The records below preserve the original failures, regression tests, real WebUI operations and comparison screenshots. Verification uses isolated published DSH 0.1.7-rc.2, all seventeen Mnemon artifacts and the official CLI. Local loopback fixtures script model decisions; tool calls, storage, browser and CLI operations are real. These checks do not certify live-model quality or Windows WebUI acceptance. + +- [USER tool writes and CLI evidence](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.15/docs/pr-assets/issue-281-user-branches/README.md) +- [Runtime metadata and composed projections](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.15/docs/pr-assets/issue-280-runtime-metadata/README.md) +- [Skin hooks, default backgrounds and both placements](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.15/docs/pr-assets/issue-273-skin-hooks/README.md) +- [v0.5.15 versioned composition and WebUI smoke](../../pr-assets/release-v0.5.15/README.md) + +Before creating the GitHub Release, the release workflow verifies the merged workspace, all sixteen independent plugins, frozen artifacts, the full pinned Registry composition and a real v0.4.7 upgrade. + +Previous release: [v0.5.14](./v0.5.14.md). diff --git a/dsh/docs/en/releases/v0.5.16.md b/dsh/docs/en/releases/v0.5.16.md new file mode 100644 index 000000000..363b94a27 --- /dev/null +++ b/dsh/docs/en/releases/v0.5.16.md @@ -0,0 +1,44 @@ +# v0.5.16: Chinese Metadata, Native Sidebar and Source Scrolling + +[简体中文](../../zh-CN/releases/v0.5.16.md) | **English** + +v0.5.16 includes three UI and packaging fixes: + +- [#286](https://github.com/omdsh-dev/dsh-mnemon/pull/286) adds a Chinese display name and description for the root plugin in DSH, with the existing English manifest fallback. Locale resources are checked in packed artifacts, and locale-only changes now participate in release validation. The contributor's original commit [`d16d017`](https://github.com/omdsh-dev/dsh-mnemon/commit/d16d017aa5bb0c3bbbdb40dc5a95554266b6522a) is preserved, with validation and release maintenance appended. +- [#287](https://github.com/omdsh-dev/dsh-mnemon/pull/287) moves Memory System into DSH's native Sidebar and main panel. Switching to Plugins reveals the selected page; returning preserves Source state. Native row styling, collapsed navigation and header visibility work with the tested official theme and Maid Atelier skin. Better Sidebar and replacement-layout fallbacks remain supported. +- [#288](https://github.com/omdsh-dev/dsh-mnemon/pull/288) aligns Runtime, Documents and Memory Spaces with the Status page's insets and scroll container. Memory Spaces keeps its title and tabs together while scrolling. Tab changes reset only the owning canvas; opening Related places its heading and close button below the fixed header. + +## Packages and upgrade + +| Package | Previous | New | +|---|---|---| +| `dsh-mnemon` | 0.5.15 | **0.5.16** | +| `dsh-mnemon-source-memory-spaces` | 0.5.9 | **0.5.10** | + +The other fifteen companion packages retain their versions. Upgrade Root and Memory Spaces Source together for the complete fix; the Starter pins the full seventeen-package combination. Mnemon CLI remains independently maintained. + +Back up the DSH profile configuration and relevant Mnemon data, then upgrade: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.16 +``` + +Restart DSH and reload the browser. Substitute `headless` for that Profile. The Client floor remains DSH 0.1.5-rc.1; complete profiles require Node `^22.19.0 || >=24.0.0`. + +## Compatibility and data + +No persistence migration is required. Stored memory, storage formats and Provider permissions are unchanged. The Source callbacks `onResetScroll` and `onRevealElement` are optional, retaining compatibility with older Hosts; those Hosts require manual scrolling. The existing Root peer floor is unchanged. + +Published DSH 0.1.7-rc.2 still displays the `cordis:group` container as Off while its children run, and its displayed switch can return `unknown-plugin`. This release does not patch DSH or install a replacement plugin-management adapter. The local upstream candidate retained in the evidence is separate from the shipped Mnemon runtime. + +## Verification + +The original PR records contain regression checks, packed-artifact verification and real WebUI evidence: + +- [Chinese metadata and English fallback](../../pr-assets/pr-286-plugin-metadata/README.md) +- [Native Sidebar, skins and state retention](../../pr-assets/sidebar-native-20260926/README.md) +- [Source frames, scrolling and visible Related controls](../../pr-assets/source-page-frames-20260926/README.md) + +The versioned seventeen-artifact composition passed real Flash WebUI and official CLI acceptance: Runtime writes and capacity archival, Documents search and archival, Native remember/recall/Related, Sidebar state retention, aligned page frames and a cold Host restart. Thirteen real Flash requests returned HTTP 200; restart preserved all five Runtime/Documents file hashes, four Native insights and eight edges. Root verification passed 1,421 tests, and all sixteen independent plugin repositories, the external consumer and the v0.4.7 upgrade passed. See the [v0.5.16 release evidence](../../pr-assets/release-v0.5.16/README.md) for screenshots, artifact identities, local concurrency reruns and scope limits. + +Previous release: [v0.5.15](./v0.5.15.md). diff --git a/dsh/docs/en/releases/v0.5.17.md b/dsh/docs/en/releases/v0.5.17.md new file mode 100644 index 000000000..0b9d4c33b --- /dev/null +++ b/dsh/docs/en/releases/v0.5.17.md @@ -0,0 +1,60 @@ +# v0.5.17: Composable Strategies and Component Settings + +[简体中文](../../zh-CN/releases/v0.5.17.md) | **English** + +v0.5.17 includes the thirteen PRs merged after v0.5.16. The Starter now pins eighteen packages, including the new General strategy. + +- **Strategies and composition** ([#294](https://github.com/omdsh-dev/dsh-mnemon/pull/294), [#299](https://github.com/omdsh-dev/dsh-mnemon/pull/299), [#301](https://github.com/omdsh-dev/dsh-mnemon/pull/301)): choose Layered or General strategy under Plugins, compose Sources and enhancements from their declarations, and keep dependent switches in step. General admits available Sources and lets the model choose how to use them. Standard selection, projection and capture enhancements can follow either strategy. A conversation can continue without memory when no main strategy runs. +- **Component settings and interface** ([#297](https://github.com/omdsh-dev/dsh-mnemon/pull/297), [#298](https://github.com/omdsh-dev/dsh-mnemon/pull/298), [#300](https://github.com/omdsh-dev/dsh-mnemon/pull/300), [#302](https://github.com/omdsh-dev/dsh-mnemon/pull/302)): configure Mnemon on its DSH Plugins page. Each component owns its settings and can contribute a Status card. Switches and selectors apply immediately; typed values use Apply. Storage offers explicit Default and Custom directory choices. The UI follows DSH primitives and theme tokens. +- **Optional CLI and peer Providers** ([#295](https://github.com/omdsh-dev/dsh-mnemon/pull/295)): only Mnemon Native needs the Mnemon CLI. Other ready Providers can serve Memory Spaces when Native is unavailable; saved Provider choices remain honored. +- **Naming and navigation** ([#290](https://github.com/omdsh-dev/dsh-mnemon/pull/290), [#292](https://github.com/omdsh-dev/dsh-mnemon/pull/292), [#303](https://github.com/omdsh-dev/dsh-mnemon/pull/303)): retain “可组合记忆 (dsh-mnemon)”, describe layered memory as the default composition, and use a thin brain outline for memory navigation. Local credentials and tool state are excluded from Git. +- **Host compatibility and documentation** ([#293](https://github.com/omdsh-dev/dsh-mnemon/pull/293), [#291](https://github.com/omdsh-dev/dsh-mnemon/pull/291)): target published DSH 0.1.7-rc.2 contracts, remove older-host compatibility paths, and document the upstream `cordis:group` display issue. + +- **General read receipts**: release acceptance caught a DSH tool-output rejection when Source evidence omitted optional `score` or `revision`. The Host now preserves omission for `mnemon_recall` and `mnemon_related`, while retaining zero scores and supplied revisions. + +## Upgrade and compatibility + +**This release supports DSH 0.1.7-rc.2. Keep v0.5.16 on older hosts.** Complete profiles require Node `^22.19.0 || >=24.0.0`; the package's Node import floor remains 20. + +Back up the Profile configuration and memory data, then install the complete Starter: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.17 +``` + +Restart DSH and reload the browser; substitute `headless` for that Profile. Mnemon CLI has its own release cycle and is needed only for Mnemon Native. + +Memory storage formats are unchanged. Previously saved component choices move into the DSH Profile patch when its plugin manager is available. A data-directory change does not move existing memory; copy or restore data explicitly when changing location. Preserve the Profile backup for rollback as well as the data backup. + +The five Strategy packages now require `dsh-mnemon ^0.5.17`, because they use the new standard View extension SDK. Upgrade the pinned combination together; independent plugin installations must satisfy that peer floor. + +DSH 0.1.7-rc.2 can still show the `cordis:group` container as Off while its children run; clicking that container can return `unknown-plugin`. The release does not patch DSH. See [compatibility guidance](../reference/compatibility.md#dsh-017-bundle-component-list) and [upstream issue #649](https://github.com/dsh-external/issues/issues/649). + +## Package versions + +| Package / 包 | Previous / 上版 | New / 本版 | +|---|---|---| +| `dsh-mnemon` | 0.5.16 | **0.5.17** | +| `dsh-mnemon-provider-byterover` | 0.5.4 | **0.5.5** | +| `dsh-mnemon-provider-hindsight` | 0.5.4 | **0.5.5** | +| `dsh-mnemon-provider-holographic` | 0.5.4 | **0.5.5** | +| `dsh-mnemon-provider-honcho` | 0.5.4 | **0.5.5** | +| `dsh-mnemon-provider-mem0` | 0.5.4 | **0.5.5** | +| `dsh-mnemon-provider-mnemon-native` | 0.5.5 | **0.5.6** | +| `dsh-mnemon-provider-openviking` | 0.5.6 | **0.5.7** | +| `dsh-mnemon-provider-retaindb` | 0.5.4 | **0.5.5** | +| `dsh-mnemon-provider-supermemory` | 0.5.4 | **0.5.5** | +| `dsh-mnemon-source-documents` | 0.5.6 | **0.5.7** | +| `dsh-mnemon-source-memory-spaces` | 0.5.10 | **0.5.11** | +| `dsh-mnemon-source-runtime` | 0.5.9 | **0.5.10** | +| `dsh-mnemon-strategy-auto-capture` | 0.5.4 | **0.5.5** | +| `dsh-mnemon-strategy-default-three-tier` | 0.5.6 | **0.5.7** | +| `dsh-mnemon-strategy-general` | New / 新增 | **0.5.1** | +| `dsh-mnemon-strategy-light-context` | 0.5.4 | **0.5.5** | +| `dsh-mnemon-strategy-scoped` | 0.5.4 | **0.5.5** | + +## Verification + +The [release acceptance record](../../pr-assets/release-v0.5.17/README.md) records the versioned artifacts, functional checks, screenshots and verification limits. Earlier PR evidence remains historical and is not substituted for testing this composition. + +Previous release: [v0.5.16](./v0.5.16.md). diff --git a/dsh/docs/en/releases/v0.5.18.md b/dsh/docs/en/releases/v0.5.18.md new file mode 100644 index 000000000..2cb762f91 --- /dev/null +++ b/dsh/docs/en/releases/v0.5.18.md @@ -0,0 +1,39 @@ +# v0.5.18: Immediate Activation and Quiet Desktop CLI + +[简体中文](../../zh-CN/releases/v0.5.18.md) | **English** + +This patch release contains the two fixes merged after v0.5.17: + +- **Install and enable without restarting DSH** ([#308](https://github.com/omdsh-dev/dsh-mnemon/pull/308)): Desktop and official WebUI profiles with isolated dependencies can load all default Sources and the Strategy when the bundle was not selected at startup. Mnemon prepares its dependency resolution before starting the native group, then registers Web RPC routes in its own injected scope. The official WebUI can use **Enable now** immediately after installation. +- **Quiet native CLI execution** ([#306](https://github.com/omdsh-dev/dsh-mnemon/pull/306), [#305](https://github.com/omdsh-dev/dsh-mnemon/issues/305)): verified Mnemon npm installations run their native executable directly for memory operations and version checks, avoiding the npm launcher's extra Windows console window. npm retains ownership of updates, and other CLI installations remain supported. + +## Upgrade and compatibility + +The supported host remains **DSH 0.1.7-rc.2**. Complete profiles require Node `^22.19.0 || >=24.0.0`; the package's Node import floor remains 20. Older hosts should retain v0.5.16. + +Install `dsh-mnemon@0.5.18` through the official WebUI's **Add plugin** dialog, then choose **Enable now**. The CLI alternative is: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.18 +``` + +Use `headless` for that profile. If upgrading a version already loaded by the running host, restart DSH once and reload the browser to load the new code. Fresh installation and activation of the new bundle do not require a restart. Updating or removing already-loaded packages remains subject to the host's normal restart requirements. + +No memory storage migration is required. Core and component switches retain their existing behavior and saved choices. Keep the new Starter readiness component enabled, and use the bundle or core switch to control the composition. Other loaded plugins retain their dependency routes; DSH source and shared Connection instances are unchanged. + +The upstream `cordis:group` display/toggle issue remains documented in [compatibility guidance](../reference/compatibility.md#dsh-017-bundle-component-list). It does not mean that active Sources or Strategies have stopped. + +## Changed packages + +| Package | Previous | New | +| --- | --- | --- | +| `dsh-mnemon` | 0.5.17 | **0.5.18** | +| `dsh-mnemon-source-memory-spaces` | 0.5.11 | **0.5.12** | + +The Starter still pins 17 component packages. The other 16 component versions are unchanged. + +## Verification + +See the [versioned release acceptance](../../pr-assets/release-v0.5.18/README.md), [activation regression evidence](../../pr-assets/desktop-live-activation/README.md) and [Desktop CLI evidence](../../pr-assets/issue-305-desktop-console/README.md). Publication verifies the complete pinned composition, the Registry installation and the real upgrade before creating the GitHub release. + +Previous release: [v0.5.17](./v0.5.17.md). diff --git a/dsh/docs/en/releases/v0.5.19.md b/dsh/docs/en/releases/v0.5.19.md new file mode 100644 index 000000000..33326fdfc --- /dev/null +++ b/dsh/docs/en/releases/v0.5.19.md @@ -0,0 +1,47 @@ +# v0.5.19: DSH 0.2, Desktop Memory and a Guided First Install + +[简体中文](../../zh-CN/releases/v0.5.19.md) | **English** + +This patch release contains the two pull requests merged after v0.5.18: + +- **DSH 0.2.0-rc.1 support** ([#311](https://github.com/omdsh-dev/dsh-mnemon/pull/311)): DSH checks every `@deepseek-ai/dsh` and `@deepseek-ai/dsh-*` peer range before it installs a plugin and whenever a profile starts, and earlier releases declared DSH 0.1.7 only, so DSH 0.2 refused them. The Starter and the Memory Spaces Source now accept `^0.1.7-rc.2 || ^0.2.0-rc.1`, and a test runs DSH's own check for both runtimes. +- **Manage memory in DSH Desktop** ([#310](https://github.com/omdsh-dev/dsh-mnemon/issues/310), [#311](https://github.com/omdsh-dev/dsh-mnemon/pull/311)): Desktop windows load from the app's own `dsh-app://app/` address. Mnemon treated them as remote pages, so with the default `remoteAccess: read-only` the Memory System and the plugin settings were read only while agent tools could still write. Application windows now use Mnemon's local channels. Pages opened from another device keep the read-only default and now say which grant and restart they need. +- **Status on a fresh install** ([#311](https://github.com/omdsh-dev/dsh-mnemon/pull/311)): Mnemon Native names the CLI version instead of waiting for it indefinitely. +- **Consistent memory interactions** ([#307](https://github.com/omdsh-dev/dsh-mnemon/pull/307)): the turn memory bar and Save to memory carry the Memory System's brain mark; the bar names what each tool did and each item opens where it lives. Save to memory is one dialog in conversations and on Memory Spaces, with a task Agent readiness tag and a receipt in the candidate's language. Mnemon uses DSH's inputs, selectors and dialog buttons, and its dialogs sit on DSH's modal layer. The workspace refresh reloads the open page, Status lists the Providers that run, and the Native Provider is named Mnemon Native. +- **Documentation** ([#307](https://github.com/omdsh-dev/dsh-mnemon/pull/307), [#311](https://github.com/omdsh-dev/dsh-mnemon/pull/311)): a new [installation guide](../guides/installation.md) goes from an empty machine to the first memory on the web, the desktop app or the command line, and the README and guides show interactions recorded with the live model. + +## Upgrade and compatibility + +Supported hosts are **DSH 0.1.7-rc.2** (npm `latest`) and **DSH 0.2.0-rc.1** (npm `next`). Complete profiles require Node `^22.19.0 || >=24.0.0`; the package's Node import floor remains 20. Older hosts should retain v0.5.16. + +Install `dsh-mnemon@0.5.19` through **Plugins → Add plugin**, then choose **Enable now**. The CLI alternative is: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.19 +``` + +Use `headless` for that profile. Include the version for the first 24 hours after publication: pnpm 11 does not pick a version published less than a day ago and would install the previous release, which DSH 0.2 refuses. Afterwards plain `dsh-mnemon` installs 0.5.19. A versioned install pins the version; upgrade it later with `dsh plugin --profile web update --latest dsh-mnemon`. The [installation guide](../guides/installation.md#common-problems) covers these cases. + +- **Moving to DSH 0.2:** update dsh-mnemon first, then DSH. If DSH 0.2 starts with an older release, it turns that release off; memory is untouched and returns after the update. +- **Desktop app:** update from the app's Plugins page. No `remoteAccess` change is needed, and a `remoteAccess: trusted-host` override added only as a workaround for #310 can be removed. +- If the running host already loaded an older version, restart DSH once and reload the browser. A fresh installation and **Enable now** need no restart. + +No memory storage or configuration migration is required. Plugin authors: the Source-page SDK in `dsh-mnemon/client` adds `SearchField`, `SelectField`, `WriteReceipt`, `TaskAgentTag` and `MemorySourcePageProps.refreshKey`, and the three official Sources that use them now require `dsh-mnemon ^0.5.19`. + +## Changed packages + +| Package | Previous | New | +| --- | --- | --- | +| `dsh-mnemon` | 0.5.18 | **0.5.19** | +| `dsh-mnemon-source-runtime` | 0.5.10 | **0.5.11** | +| `dsh-mnemon-source-documents` | 0.5.7 | **0.5.8** | +| `dsh-mnemon-source-memory-spaces` | 0.5.12 | **0.5.13** | +| `dsh-mnemon-provider-mnemon-native` | 0.5.6 | **0.5.7** | + +The Starter still pins 17 component packages. The other 13 component versions are unchanged. + +## Verification + +See the [versioned release acceptance](../../pr-assets/release-v0.5.19/README.md) on both supported hosts, the [desktop window evidence](../../pr-assets/issue-310-desktop-window/README.md), the [installation gallery](../../assets/install-v0.5.19/README.md) and the [live v0.5.19 gallery](../../assets/webui-v0.5.19/README.md). Publication verifies the complete pinned composition, the Registry installation and the real upgrade before creating the GitHub release. + +Previous release: [v0.5.18](./v0.5.18.md). diff --git a/dsh/docs/en/releases/v0.5.2.md b/dsh/docs/en/releases/v0.5.2.md new file mode 100644 index 000000000..c345de569 --- /dev/null +++ b/dsh/docs/en/releases/v0.5.2.md @@ -0,0 +1,50 @@ +# v0.5.2: Atomic Starter Disable and Independent Releases + +[简体中文](../../zh-CN/releases/v0.5.2.md) | **English** + +v0.5.2 fixes the v0.5 Starter lifecycle regression reported in [Issue #185](https://github.com/omdsh-dev/dsh-mnemon/issues/185). It also completes the one-time transition from coordinated package versions to independently versioned Source, Strategy, and Provider releases. + +## Fix + +The existing `mnemon` Entry remains the public configuration target and is again the lifecycle switch for the complete Starter: + +```yaml +- id: mnemon + disabled: true +``` + +This override now disables the Core/Host, Runtime, Documents, Memory Spaces, the default three-tier Strategy, and all three optional Strategy enhancements together. DSH no longer leaves four dependent Entries pending on a missing `mnemonMemory` service, so the Host and WebUI can start normally with Mnemon disabled. + +All existing Entry ids and configuration targets remain valid. Disabling the Starter does not uninstall any package, invoke a Provider, migrate storage, or delete memory data. Remove the override, or change it to `false`, and restart DSH to enable the complete Starter again. [PR #186](https://github.com/omdsh-dev/dsh-mnemon/pull/186). + +## Independent package releases + +This release publishes `dsh-mnemon` and all sixteen official plugins once at `0.5.2`. The one-time full release is required because the independent-version transition changes every package's published dependency metadata. + +After v0.5.2, a package advances only when its own published artifact changes. `dsh-mnemon` remains the aggregate Starter and exact tested bill of materials, so it also advances whenever one of its pinned plugin versions changes. The release workflow computes that selection from the previous release, publishes selected plugin dependency layers with bounded concurrency, verifies Registry read-back, and publishes the Starter last. [PR #181](https://github.com/omdsh-dev/dsh-mnemon/pull/181). + +## Upgrade and compatibility + +Install the exact stable release in the owning DSH profile and restart it: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.2 +``` + +Use `headless` instead of `web` for a Headless profile. The v0.5.2 Starter pins the verified `0.5.2` composition of all sixteen official plugins; normal users do not install them individually. The stable npm channel remains `latest`. + +There is no RPC-authority, credential, storage-path, memory-format, Document-format, Provider-data, or extension-contract migration. Existing v0.5.x profiles and data remain valid. As before any upgrade, stop writers and back up the effective memory root, workspace Documents, DSH profile/configuration, and separately managed Provider data. + +## Verification + +The reported failure was reproduced from a retained `disabled: true` profile on DSH 0.1.2-rc.1 with Node.js 22.23.2. Before the fix, Runtime, Documents, Memory Spaces, and the default Strategy remained pending on `mnemonMemory`; the same profile appeared harmless with the monolithic v0.4.7 Entry. + +The corrected build passed complete workspace verification, all package builds, type checks and tests, deterministic output, public-entry and package checks, real DSH Headless activation, and an isolated Linux Node.js 22.23.2 run. A disposable real WebUI profile started normally and showed all eight Mnemon Entries disabled, with no browser errors and no Mnemon tools exposed to the model. The implementation PR retains the screenshot and exact regression fixture. + +The release workflow additionally freezes and verifies the selected artifacts, independently installs every plugin, compiles an external SDK consumer, verifies the real Starter composition, reads all packages back from npm, and smoke-tests a Registry-backed upgrade from v0.4.7. + +## Rollback + +Reinstall `dsh-mnemon@0.5.1` in the same profile and restart DSH. No data conversion is required. If the profile keeps `id: mnemon` disabled after rollback, the v0.5.1 pending-Entry failure can return; remove that override or explicitly disable all four dependent Source/Strategy Entries before starting the older Host. + +Previous release: [v0.5.1](./v0.5.1.md). diff --git a/dsh/docs/en/releases/v0.5.20.md b/dsh/docs/en/releases/v0.5.20.md new file mode 100644 index 000000000..e1327c5ce --- /dev/null +++ b/dsh/docs/en/releases/v0.5.20.md @@ -0,0 +1,41 @@ +# v0.5.20: No Separate Starter Switch + +[简体中文](../../zh-CN/releases/v0.5.20.md) | **English** + +This patch release contains the three pull requests merged after v0.5.19: + +- **The Starter's readiness is part of its component group** ([#314](https://github.com/omdsh-dev/dsh-mnemon/pull/314)): 0.5.18 and 0.5.19 listed a separate readiness row, `dsh-mnemon/starter`, on the Plugins page, and the component group waited for it. Turning that row off, for example to silence an error after an update, left every component waiting for `mnemonStarterReady`: `dsh web` started without the Memory System, and the desktop app failed its start and offered to remove the plugin. The group, `mnemon-bundle`, now names `dsh-mnemon/bundle`, which prepares the Starter's dependency resolution itself and then mounts the components with DSH's own `cordis:group`. **Enable now** still works without a restart, the row is gone, and a profile stuck in that state recovers after the update and a restart. +- **Recovery after updates** ([#313](https://github.com/omdsh-dev/dsh-mnemon/pull/313), [#315](https://github.com/omdsh-dev/dsh-mnemon/pull/315)): the [installation guide](../guides/installation.md#common-problems) covers `ERR_PACKAGE_PATH_NOT_EXPORTED` after an update without a restart, `waiting for service: mnemonStarterReady` with the desktop app's recovery page, and pnpm's one-day window falling back more than one release. + +## Upgrade and compatibility + +Supported hosts are unchanged: **DSH 0.1.7-rc.2** (npm `latest`) and **DSH 0.2.0-rc.1** (npm `next`). Complete profiles require Node `^22.19.0 || >=24.0.0`. Older hosts should retain v0.5.16. + +Install `dsh-mnemon@0.5.20` through **Plugins → Add plugin**, then choose **Enable now**. The CLI alternative is: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.20 +``` + +Include the version for the first 24 hours after publication. pnpm 11 does not pick a version published less than a day ago, and until 17:31 UTC on 2026-09-29 it falls back to 0.5.17, because 0.5.18 and 0.5.19 were both published on 2026-09-28; DSH 0.2 refuses 0.5.17. A versioned install pins the version; upgrade it later with `dsh plugin --profile web update --latest dsh-mnemon`. + +- **Existing installations:** for the first 24 hours run `dsh plugin --profile web add dsh-mnemon@0.5.20`, because until then pnpm 11 keeps `update` and `update --latest` on the installed version; afterwards `dsh plugin --profile web update dsh-mnemon` (`update --latest` for a versioned install) works. Then restart DSH. Desktop users update from the app's Plugins page and then quit and reopen the app. Until that restart, an update from 0.5.18 or 0.5.19 reports `mnemon-bundle (dsh-mnemon/bundle): pending (waiting for service: mnemonStarterReady)`, because the running Loader keeps the old group. +- A leftover `- id: mnemon-starter` row in a profile patch matches nothing and is ignored. The desktop app logs `patch: entry "mnemon-starter" not found`; the two lines can be deleted. `dsh-mnemon/starter` stays exported for compositions that still insert it. +- The container row on the Plugins page reads `dsh-mnemon/bundle` instead of `cordis:group`, and as before it shows Off ([upstream #649](https://github.com/dsh-external/issues/issues/649)). +- DSH's config schema export (`dsh web --dump-config-schema`) walks only native groups, so it now reports `mnemon-bundle` as an unrecognized carrier and leaves out Mnemon's component schemas, as it does for DSH's own agent presets. Runtime configuration and profile patches are unaffected. + +No memory storage or configuration migration is required. + +## Changed packages + +| Package | Previous | New | +| --- | --- | --- | +| `dsh-mnemon` | 0.5.19 | **0.5.20** | + +The Starter still pins 17 component packages, all unchanged. + +## Verification + +The [versioned release acceptance](../../pr-assets/release-v0.5.20/README.md) installs the release package through Add plugin on both supported hosts. The [component group evidence](../../pr-assets/starter-group-readiness/README.md) compares the Plugins page before and after. The [cold start and upgrade record](../../pr-assets/starter-group-upgrade/README.md) covers the web, the command line, Headless and the desktop layout, and updates from 0.5.16 through 0.5.19. Publication verifies the complete pinned composition, the Registry installation and the real upgrade before creating the GitHub release. + +Previous release: [v0.5.19](./v0.5.19.md). diff --git a/dsh/docs/en/releases/v0.5.21.md b/dsh/docs/en/releases/v0.5.21.md new file mode 100644 index 000000000..657899e70 --- /dev/null +++ b/dsh/docs/en/releases/v0.5.21.md @@ -0,0 +1,57 @@ +# v0.5.21: Large Native Spaces and Leaner Hot Memory + +[简体中文](../../zh-CN/releases/v0.5.21.md) | **English** + +This patch release contains the three pull requests merged after v0.5.20: + +- **Large Native spaces work again** ([#321](https://github.com/omdsh-dev/dsh-mnemon/pull/321), issue [#320](https://github.com/omdsh-dev/dsh-mnemon/issues/320)). + - Mnemon Native reads a whole Memory Space in three places: its contents list, its graph, and the exact-content check that runs before runtime memory archives entries at capacity. Every Mnemon command stopped at 2 MiB of output, but a space of about 1,000 insights returns 3–9 MB. + - On a 900-insight space the contents list read 0 memories and the graph "connection unavailable". Adding working memory at capacity failed with a hint to install Mnemon, so working memory could no longer make room. + - Those whole-space reads now accept up to 128 MiB, and every other command keeps 2 MiB. A failure names its cause: only a failed launch suggests installing Mnemon. +- **Idle review writes one layer** ([#322](https://github.com/omdsh-dev/dsh-mnemon/pull/322), issue [#319](https://github.com/omdsh-dev/dsh-mnemon/issues/319)). + - Idle review, the only unattended writer, sent stable project facts and decisions to MEMORY.md, which every turn loads. One pass could record the same knowledge as a Document and again as a working-memory entry. + - Review now considers a Document first. MEMORY.md takes only a compact rule the user stated, such as a convention, a correction, an environment fact or a tool quirk, that no Document or entry already covers; project records such as designs, implementation details, paths and ports never go there. + - The review guard holds each pass to one layer: after a Document, working-memory changes are refused, and after a working-memory change, a Document is refused. USER.md changes stay independent. + - A new **Write runtime memory** switch under **Idle review** on the Layered strategy's page (`idleReview.runtimeMemory`, on by default) limits review to Documents. +- **The fallback sidebar entry matches DSH's rows** ([#323](https://github.com/omdsh-dev/dsh-mnemon/pull/323), issue [#318](https://github.com/omdsh-dev/dsh-mnemon/issues/318)). + - In layouts without DSH's native panel seat, Mnemon inserts its own Memory System entry, which kept an older style. Next to DSH's rows it looked smaller, greyer and 8px indented. + - It now copies DSH's panel row: inherited 14px text in the primary color, the same padding, margins and corners, a 16px icon (18px collapsed), and DSH's highlight. The open entry is also `aria-current="page"`. Layouts with the native seat are unchanged. + +## Upgrade and compatibility + +Supported hosts: **DSH 0.2.0-rc.2** (npm `latest` and `next`) and **DSH 0.1.7-rc.2**. Complete profiles require Node `^22.19.0 || >=24.0.0`. Older hosts should retain v0.5.16. + +Install `dsh-mnemon@0.5.21` through **Plugins → Add plugin**, then choose **Enable now**. The CLI alternative is: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.21 +``` + +Include the version for the first 24 hours after publication. pnpm 11 does not pick a version published less than a day ago, so until then a plain install or update picks 0.5.20, which both hosts accept. A versioned install pins the version; upgrade it later with `dsh plugin --profile web update --latest dsh-mnemon`. + +- **Existing installations:** for the first 24 hours run `dsh plugin --profile web add dsh-mnemon@0.5.21`. Afterwards `dsh plugin --profile web update dsh-mnemon` works (`update --latest` for a versioned install). Then restart DSH. Desktop users update from the app's Plugins page and then quit and reopen the app. +- **Idle review:** existing profiles keep runtime memory writes under the new rules; turn off **Write runtime memory** to keep review to Documents. Entries that review wrote before this release stay in MEMORY.md until you remove them. +- **Large spaces:** the new cap needs the new Memory Spaces Source and Mnemon Native together, which the Starter pins. Mnemon Native 0.5.8 on an older Memory Spaces host keeps the 2 MiB cap. + +No memory storage or configuration migration is required. + +## Changed packages + +| Package | Previous | New | +| --- | --- | --- | +| `dsh-mnemon` | 0.5.20 | **0.5.21** | +| `dsh-mnemon-source-memory-spaces` | 0.5.13 | **0.5.14** | +| `dsh-mnemon-provider-mnemon-native` | 0.5.7 | **0.5.8** | + +The Starter pins 17 component packages; the other 15 are unchanged. + +## Verification + +The [versioned release acceptance](../../pr-assets/release-v0.5.21/README.md) installs the release packages through Add plugin on both supported hosts. Each fix has its own before-and-after record: +- [large Native spaces](../../pr-assets/issue-320-native-store-dumps/README.md); +- [one-layer idle review](../../pr-assets/issue-319-review-layers/README.md); +- [the fallback sidebar entry](../../pr-assets/issue-318-sidebar-entry/README.md). + +Publication verifies the complete pinned composition, the Registry installation and the real upgrade before creating the GitHub release. + +Previous release: [v0.5.20](./v0.5.20.md). diff --git a/dsh/docs/en/releases/v0.5.22.md b/dsh/docs/en/releases/v0.5.22.md new file mode 100644 index 000000000..3042060ae --- /dev/null +++ b/dsh/docs/en/releases/v0.5.22.md @@ -0,0 +1,56 @@ +# v0.5.22: In-App Updates, Conversation Tabs and Ollama Subagents + +[简体中文](../../zh-CN/releases/v0.5.22.md) | **English** + +This patch release contains the three pull requests merged after v0.5.21: + +- **Check versions → Update works in the desktop app** ([#325](https://github.com/omdsh-dev/dsh-mnemon/pull/325)). + - The Host updated dsh-mnemon by running pnpm from PATH in the owning Profile. The desktop app's Host has no pnpm on PATH, so the dialog only reported that pnpm was missing, and DSH's Plugins page cannot update an installed plugin. + - The update now goes through DSH's own plugin installer, as `dsh plugin add dsh-mnemon@` does. It uses the Profile's package manager, which in the desktop app is the app's bundled pnpm, with the Profile's registry settings, fallbacks and lock. It installs the exact checked version, so pnpm's 24-hour release window does not hold it back. An optional Strategy the Profile added on its own updates the same way. + - DSH checks the DSH versions the new release supports before keeping it. A failed install restores the Profile's files, and the dialog shows pnpm's error line and DSH's log. The enabled plugins stay as they were, and success is reported only once DSH lists the new version. + - DSH loads the new Memory System page as soon as the files change, which used to close the dialog before the update reported back. The new page now reopens Status with the dialog, which shows the update and the restart it needs. + - Until DSH restarts, a notice above every Memory System page names the installed version and the one that still runs, whether the update came from Check versions or `dsh plugin`. Status shows the running version and Check versions the installed one, so it no longer offers an update `dsh plugin` already installed. +- **The Memory System works in a conversation tab whose Agent is not loaded** ([#326](https://github.com/omdsh-dev/dsh-mnemon/pull/326)). + - Mnemon found a conversation's workspace only through its loaded Agent. For a conversation DSH showed but had not resumed, for example one whose Agent preset is no longer in the profile, Status read "Waiting for workspace" and Project Documents failed with `memory Source is unavailable in the requested management scope`. + - With workspace or centralized storage, runtime memory and Memory Spaces in that tab used the directory DSH started from. Entries saved that way stay in that directory. + - The Host now finds the workspace in DSH's workspace registry, which lists every session whether or not its Agent is loaded. A new Project Document is written directly. When Documents are full, a task Agent in the conversation's workspace archives the least recently used one first, and a task Agent also chooses a new Memory Space's Provider; both used to fail with "current DSH agent is not live". +- **Memory subagents recover when a chat template requires a user query** ([#328](https://github.com/omdsh-dev/dsh-mnemon/pull/328), issue [#327](https://github.com/omdsh-dev/dsh-mnemon/issues/327)). + - On Ollama 0.33 with Qwen3.x, idle review and the other memory subagents failed with `no user query found in messages`, sometimes after the review had already created a Document. A subagent's only user turn is its prompt. Once tool results filled the context window, Ollama's truncation dropped that prompt and kept the tool results. + - When a server refuses a subagent request this way, Mnemon adds a short user turn to that step and retries it at once; later tool continuations of that subagent end the same way. Subagents on servers that never refuse, and the main conversation, send exactly what they did before. A larger Ollama context length also avoids the truncation. + +## Upgrade and compatibility + +Supported hosts: **DSH 0.2.0-rc.2** (npm `latest` and `next`) and **DSH 0.1.7-rc.2**. Complete profiles require Node `^22.19.0 || >=24.0.0`. Older hosts should retain v0.5.16. + +Install `dsh-mnemon@0.5.22` through **Plugins → Add plugin**, then choose **Enable now**. The CLI alternative is: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.22 +``` + +Include the version for the first 24 hours after publication. pnpm 11 does not pick a version published less than a day ago, so until then a plain install or update picks 0.5.21, which both hosts accept. A versioned install pins the version; upgrade it later with `dsh plugin --profile web update --latest dsh-mnemon`. + +- **Existing installations:** for the first 24 hours run `dsh plugin --profile web add dsh-mnemon@0.5.22`. Afterwards `dsh plugin --profile web update dsh-mnemon` works (`update --latest` for a versioned install). Then restart DSH. +- **Desktop app:** DSH's Plugins page cannot update an installed plugin, and 0.5.21 and earlier cannot update themselves in the app. Update once by removing dsh-mnemon on the Plugins page and adding `dsh-mnemon@0.5.22` again, then quit the app completely and reopen it. Your memory data stays. From 0.5.22 on, **Memory System → Status → Check versions → Update** installs later releases. +- **Restart notice:** the running Host reports it, so it first appears for updates from 0.5.22. After installing 0.5.22 over an older version, restart DSH as before. + +No memory storage or configuration migration is required. + +## Changed packages + +| Package | Previous | New | +| --- | --- | --- | +| `dsh-mnemon` | 0.5.21 | **0.5.22** | + +The Starter pins 17 component packages; none of them changes. + +## Verification + +The [versioned release acceptance](../../pr-assets/release-v0.5.22/README.md) installs the release package through Add plugin on both supported hosts. Each fix has its own before-and-after record: +- [updates through DSH's installer](../../pr-assets/version-update-through-dsh/README.md); +- [a conversation tab without a loaded Agent](../../pr-assets/conversation-tab-workspace/README.md); +- [subagents and chat templates that require a user query](../../pr-assets/issue-327-subagent-user-turn/README.md). + +Publication verifies the complete pinned composition, the Registry installation and the real upgrade before creating the GitHub release. + +Previous release: [v0.5.21](./v0.5.21.md). diff --git a/dsh/docs/en/releases/v0.5.23.md b/dsh/docs/en/releases/v0.5.23.md new file mode 100644 index 000000000..bd7f9de11 --- /dev/null +++ b/dsh/docs/en/releases/v0.5.23.md @@ -0,0 +1,56 @@ +# v0.5.23: Faster Memory Compaction, Queries and Multi-Space Recall + +[简体中文](../../zh-CN/releases/v0.5.23.md) | **English** + +This patch publishes the algorithm improvements in [PR #330](https://github.com/omdsh-dev/dsh-mnemon/pull/330), reducing repeated scans and allocations as data grows: + +- **Runtime compaction:** index original content once, group entries stably into three priority buckets, and count UTF-8 bytes incrementally. The cost falls from O(N²L), for average text length L, to O(N+B), where B is total text bytes. +- **Document queries:** generate excerpts only for returned documents, preserving live content reads, scoring, result order and access-time updates. +- **Memory Space discovery and multi-space recall:** use request-local Map/Set indexes for catalog matching, pinned-space resolution and quality counters. Full candidate ordering, permissions, failure handling and Provider call order remain intact. +- **Batch-write preparation:** reuse synchronous target resolution and append to grouped arrays, using a Set for membership checks. Provider persistence behavior stays the same. +- **Holographic queries:** compute exact token-set intersections and use a stable bounded heap for the top k items. Finite-score ranking falls from O(M log M) to O(M log k+k log k). Small collections and non-finite scores retain the original sorting path. + +## Performance evidence + +Measurements cover N=1, 10 and 100, with N=1000 added on the same machine and dependency set. These are median times per operation at N=1000: + +| Local operation | Before | After | +| --- | ---: | ---: | +| Runtime compaction | 41.347 ms | 4.047 ms | +| Document search | 40.484 ms | 22.165 ms | +| Multi-space recall orchestration | 254.008 ms | 9.834 ms | +| Pinned-space recall orchestration | 2154.052 ms | 9.400 ms | + +These are local latencies with synthetic data and warm file caches. Recall uses local adapter fixtures and does not measure remote Provider or LLM end-to-end latency. Complexity conclusions also rely on code analysis and deterministic work-count tests; timing alone cannot prove Big-O. The [algorithm audit](../../pr-assets/algorithm-performance-20261003/README.md) and its raw metrics include every size, workload parameters, unchanged complexity bounds and measurement limits. + +## Upgrade and compatibility + +From v0.5.22, use **Memory System → Status → Check versions → Update**, then restart DSH. To install or update to the exact version from the CLI: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.23 +``` + +Restart an existing DSH process after installation; a new installation can choose **Enable now** on the Plugins page. An explicit version avoids package-manager release waiting windows affecting version selection. For older desktop installations, see [Compatibility and upgrades](../reference/compatibility.md). + +The DSH compatibility range from v0.5.22 is unchanged. The complete development profile uses Node `^22.19.0 || >=24.0.0`. Storage formats, configuration, permissions and UI flows are unchanged; existing memory needs no migration. No new Starter SDK export is required, so plugin peer floors stay unchanged. + +## Changed packages + +| Package | Previous | New | +| --- | --- | --- | +| `dsh-mnemon` | 0.5.22 | **0.5.23** | +| `dsh-mnemon-source-runtime` | 0.5.11 | **0.5.12** | +| `dsh-mnemon-source-documents` | 0.5.8 | **0.5.9** | +| `dsh-mnemon-source-memory-spaces` | 0.5.14 | **0.5.15** | +| `dsh-mnemon-provider-holographic` | 0.5.5 | **0.5.6** | + +The Starter pins these four new plugin versions. Its other 13 component versions are unchanged, as are all locked external dependency versions and their resolved peer graph. + +## Verification + +Before merging, the algorithm changes passed 2063 local tests, with 9 optional integration tests skipped, plus the corresponding independent-plugin, packed-installation, real Headless, Node 20/22/24 and Windows CI checks. Equivalence regressions cover stable ranking, UTF-8 capacity boundaries, live file reads, quality counters, permissions and ordered batch receipts. + +The release PR verifies the versioned composition again. Before creating the GitHub release, publication checks frozen artifacts, Registry integrity, installation of all 17 plugins and a real Registry upgrade from v0.5.16. Execution results are available in the repository's [Publish to npm workflow](https://github.com/omdsh-dev/dsh-mnemon/actions/workflows/publish.yml). + +Previous release: [v0.5.22](./v0.5.22.md). diff --git a/dsh/docs/en/releases/v0.5.24.md b/dsh/docs/en/releases/v0.5.24.md new file mode 100644 index 000000000..9a21ae725 --- /dev/null +++ b/dsh/docs/en/releases/v0.5.24.md @@ -0,0 +1,53 @@ +# v0.5.24: Complete Entity Counts and Related Memories on Demand + +[简体中文](../../zh-CN/releases/v0.5.24.md) | **English** + +This patch release publishes [PR #332](https://github.com/omdsh-dev/dsh-mnemon/pull/332). On **Memory System → Memory Spaces → Entities**, the number next to an entity did not match what selecting it listed: an entity counted 22 could list 5, and another counted 5 could list 9. + +- **Counts from every memory.** The list of entities added up each space's 20 most frequent entities from the Provider's status, so most entities were missing and some counts were short. Each space now builds one entity index from the memories its Provider lists. An entity's count is the number of memories that carry it, and the list holds every entity. Spellings merge the way the Overview graph merges them, so the two agree. +- **A selection lists exactly those memories.** Selecting an entity ran Agent recall. Its quality policy keeps at most four medium matches, and memories that do not carry the entity filled the free places. Now every memory that carries the entity is listed as soon as it is selected, most important first, a page at a time. +- **Related memories on demand.** Below the list, **Find related memories** runs recall for the entity among memories that do not carry it. Recall takes about half a second per space, so it runs only when asked for. Once opened, related memories stay open for the next entities until you hide them, for the rest of the browser session. When no memory carries a name you entered, related memories show by themselves. +- **Steadier loading.** The spaces show before their indexes are read. A placeholder appears only for a read that takes a while, and a late answer for an earlier selection is dropped. A forgotten memory leaves the lists at once. +- **Providers.** A Provider whose `list()` stops short of a space can implement the optional `entityIndex(body)`; see [Extensions](../development/extensions.md). The first-party Providers need no change. + +## Measurements + +On synthetic Mnemon Native spaces in the WebUI, on DSH 0.2.0-rc.2 and 0.1.7-rc.2: + +| | v0.5.23 | v0.5.24 | +| --- | --- | --- | +| Entities listed | 52 of 63 | 63 of 63 | +| `Atlas`: count / memories listed | 22 / 20, including 3 without the entity | 22 / 22 | +| `WebUI`: count / memories listed | 3 / 8 | 4 / 4 | +| First memory shown after a click | 400–1,891 ms | 91–99 ms | + +With four spaces of 500 memories each, a selection used to show 20 results after 1.5–1.7 s. It now shows every memory that carries the entity (855, 211 and 86 in the runs) after 7–10 ms, and related memories, when asked for, after 1.5–1.8 s. The [Entities page record](../../pr-assets/entities-complete-index/README.md) has every run, including a copy of the export behind the report. + +## Upgrade and compatibility + +From v0.5.22, use **Memory System → Status → Check versions → Update**, then restart DSH. To install or update to the exact version from the CLI: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.24 +``` + +Restart an existing DSH process after installation; a new installation can choose **Enable now** on the Plugins page. An explicit version keeps pnpm's 24-hour release window from picking an earlier one. For older desktop installations, see [Compatibility and upgrades](../reference/compatibility.md). + +Supported hosts stay **DSH 0.2.0-rc.2** (npm `latest` and `next`) and **DSH 0.1.7-rc.2**; complete profiles use Node `^22.19.0 || >=24.0.0`. Storage formats and configuration are unchanged, and existing memory needs no migration. The entity index is held in memory and rebuilt when a space changes. No new Starter SDK export is required, so plugin peer floors stay unchanged. + +## Changed packages + +| Package | Previous | New | +| --- | --- | --- | +| `dsh-mnemon` | 0.5.23 | **0.5.24** | +| `dsh-mnemon-source-memory-spaces` | 0.5.15 | **0.5.16** | + +The Starter pins the new Memory Spaces version. Its other 16 component versions are unchanged, as are all locked external dependency versions. + +## Verification + +The [versioned release acceptance](../../pr-assets/release-v0.5.24/README.md) installs the release packages through Add plugin on both supported hosts and checks the Entities page. The fix has its own [before-and-after record](../../pr-assets/entities-complete-index/README.md). + +Publication verifies the complete pinned composition, the Registry installation and a real upgrade before creating the GitHub release. + +Previous release: [v0.5.23](./v0.5.23.md). diff --git a/dsh/docs/en/releases/v0.5.3.md b/dsh/docs/en/releases/v0.5.3.md new file mode 100644 index 000000000..cbf099eae --- /dev/null +++ b/dsh/docs/en/releases/v0.5.3.md @@ -0,0 +1,50 @@ +# v0.5.3: Actionable Version Maintenance + +[简体中文](../../zh-CN/releases/v0.5.3.md) | **English** + +v0.5.3 improves status-page version maintenance, clarifies ownership of Host execution and Source presentation, and refreshes the documentation shipped with the official packages. It includes [PR #190](https://github.com/omdsh-dev/dsh-mnemon/pull/190), [PR #191](https://github.com/omdsh-dev/dsh-mnemon/pull/191), and [PR #192](https://github.com/omdsh-dev/dsh-mnemon/pull/192). + +## Version maintenance + +- The version dialog distinguishes missing, unreadable, outdated, current, locally newer, and restart-pending installations. Registry failures leave local installation guidance available. +- Mnemon CLI installation and migration use the official `@mnemon-dev/mnemon` npm package. An npm-managed CLI can update after the Host verifies that the active launcher belongs to the current npm installation. Homebrew and Go updates remain supported. +- Native execution and version checks share the Memory Spaces package's public CLI discovery entry. Verified Windows npm launchers run through Node without invoking arbitrary shell scripts. +- dsh-mnemon subpackages are collapsed initially. Expand them to inspect Source, Strategy, and Provider versions, the Starter's pinned versions, and the owning Profile. Independently installed packages can update in that Profile; Starter dependencies update with the Starter, and source links remain intact. +- Updates honor management authorization and read-only configuration, reject concurrent writes, verify the resulting installed version, and keep a restart reminder until the Host restarts. + +## Ownership and documentation + +Host turn and maintenance execution now share explicit ownership and cleanup, including background-to-foreground handoff and Node 20-compatible public execution helpers. Source packages own their default page copy and layout assets. Memory Spaces configuration and optional Strategy validation use their owning public helpers while preserving existing defaults. + +The compact workbench header no longer overlaps longer translated titles. Published READMEs and the bilingual documentation explain the composable-memory boundary, installation, independent packages, and data scope, with real DSH screenshots and verified navigation. + +## Packages and upgrade + +This release advances the Starter and all sixteen official plugins to `0.5.3`: each package has changed published content, including its README. Packages continue to use independent versions; unchanged packages will not be republished merely because the Starter advances. + +Install the exact release in the owning DSH Profile and restart it: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.3 +``` + +Use `headless` instead of `web` for a Headless Profile. The Starter installs its exact tested plugin composition on the npm `latest` channel. Install or migrate Mnemon CLI separately in the Host's terminal, with Node.js 22 or later: + +```sh +npm install --global @mnemon-dev/mnemon@latest +mnemon --version +``` + +If an older CLI remains active, review `PATH`, `MNEMON_CLI_PATH`, and the configured `cliPath` before restarting DSH. See [Getting started](../guides/getting-started.md) and [Operations](../guides/operations.md). + +## Compatibility, verification, and rollback + +Existing Entry ids, configuration, storage paths, memory formats, and Provider data remain valid. No data migration is required. Before upgrading, back up the effective memory root, workspace Documents, DSH Profile/configuration, and separately managed Provider data as described in Operations. + +The implementation passed 783 root tests, workspace verification, deterministic builds, real isolated DSH Headless activation, and independent verification of sixteen plugin repositories and seventeen packed artifacts. The [status-page evidence](../../pr-assets/status-version-maintenance/README.md) records real desktop and mobile version-dialog checks. Windows launcher behavior uses simulated filesystem/process fixtures; the opt-in real Native and Windows binary tests require their separate environments. This does not remove the [documented narrow-screen settings limitation](../reference/compatibility.md). + +The release workflow verifies and freezes the versioned artifacts, publishes dependency layers before the Starter, installs the complete composition from npm, and smoke-tests a Registry upgrade from v0.4.7 before creating the GitHub Release. + +To roll back, install `dsh-mnemon@0.5.2` in the same Profile and restart DSH. No data conversion is needed. Independently maintained Profile packages and the separately installed CLI must be restored separately if they were changed. + +Previous release: [v0.5.2](./v0.5.2.md). diff --git a/dsh/docs/en/releases/v0.5.4.md b/dsh/docs/en/releases/v0.5.4.md new file mode 100644 index 000000000..bdc458b6a --- /dev/null +++ b/dsh/docs/en/releases/v0.5.4.md @@ -0,0 +1,42 @@ +# v0.5.4: Memory Space Terminology + +[简体中文](../../zh-CN/releases/v0.5.4.md) | **English** + +v0.5.4 standardizes the persistent memory unit as **memory space** in English and **记忆空间** in Chinese. It includes [PR #194](https://github.com/omdsh-dev/dsh-mnemon/pull/194), covering product copy, code models, public SDK names, responsive layouts, and documentation. + +## Consistent terminology and usable layouts + +- The workbench, settings, accessible labels, Agent guidance, command output, package READMEs, and bilingual guides use the same term. Individual memories, Runtime memory, and Documents keep their separate meanings. +- Canonical code names include `MemorySpace`, `MemorySpaceRegistry`, and service operations such as `createSpace` and `mergeSpaces`. +- Directory actions wrap within narrow layouts, Provider metadata and storage badges can wrap, and navigation spacing accommodates longer labels. Chinese and English directory and creation flows were checked at 390 × 844. +- Current documentation uses refreshed desktop and narrow-screen screenshots. Editable Chinese diagrams use the new term; historical screenshots, measurements, and recordings retain their original pixels and provenance. + +## Compatibility and existing data + +Existing v0.5.x integrations remain supported. Deprecated model names and service methods remain available; Provider factories accept the canonical authority name alongside the legacy context, and the old protected HTTP Provider field remains compatible with existing subclasses. + +Published tool/RPC names, `memoryBodyId(s)` and related wire fields, registry file names and JSON shapes, Document/Pack lineage, and revision encoding keep their existing spellings. These identifiers still refer to memory spaces. Existing user-authored names are preserved, including names that contain the previous Chinese term. No data migration is required; IDs, data locations, Provider connections, management authorization, and read-only restrictions remain compatible. + +## Packages and upgrade + +The Starter and all sixteen official plugins advance to `0.5.4` because each has changed code or published documentation. Independent versioning remains in effect, and the Starter pins this tested composition exactly. + +Install the release in the owning DSH Profile, then restart DSH: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.4 +``` + +Use `headless` instead of `web` for a Headless Profile. Mnemon CLI remains separately managed; this release does not change its version. Installation and update guidance continues to use the official `@mnemon-dev/mnemon` npm package. See [Getting started](../guides/getting-started.md) and [Operations](../guides/operations.md). + +To roll back, install `dsh-mnemon@0.5.3` in the same Profile and restart DSH. No data conversion is needed. Restore independently installed plugins separately if they were updated. + +## Verification and evidence + +The implementation passed 783 root tests, 300 plugin tests, deterministic builds, workspace typechecks, real isolated DSH Headless activation, package validation, and independent verification of sixteen plugin repositories and seventeen tarballs. External SDK fixtures compile both old and new model names and a subclass overriding the legacy protected Provider field. Two opt-in Native/platform integration cases require their separate environments and were skipped locally. + +[Real bilingual UI evidence](../../assets/memory-space-terminology/README.md) covers creation, editing, activation, browsing, settings, language switching, and cancellation with disposable synthetic data. The eighteen original JPEG captures were made before versioning, on the v0.5.3 base with the implementation now included in v0.5.4; their version badges identify that capture base. Model quality and cloud Provider behavior were not evaluated. The narrow-layout fixes do not broaden the evidence to every settings surface or remove other [documented compatibility limits](../reference/compatibility.md). + +The release workflow verifies the versioned workspace and independent packages, freezes artifact integrity, publishes dependency layers before the Starter, installs the complete npm composition, and smoke-tests a Registry upgrade from v0.4.7 before creating the GitHub Release. + +Previous release: [v0.5.3](./v0.5.3.md). diff --git a/dsh/docs/en/releases/v0.5.5.md b/dsh/docs/en/releases/v0.5.5.md new file mode 100644 index 000000000..b170c17fe --- /dev/null +++ b/dsh/docs/en/releases/v0.5.5.md @@ -0,0 +1,39 @@ +# v0.5.5: Remote RPC Routing and Light Media + +[简体中文](../../zh-CN/releases/v0.5.5.md) | **English** + +v0.5.5 fixes authenticated remote WebUI calls that could receive HTTP 403 and refreshes the documentation's memory-space screenshots and recordings. It includes [PR #197](https://github.com/omdsh-dev/dsh-mnemon/pull/197) and [PR #196](https://github.com/omdsh-dev/dsh-mnemon/pull/196). + +## Remote pages use the authenticated Gateway + +Remote browser calls for status, Source catalogs, settings, Views, activation and management now use DSH's namespaced API Gateway. Local loopback clients retain the existing RPC channels. Detection also handles remote-desktop connections whose `isLoopback` flag describes Host ownership while the page has a remote origin. + +DSH continues to own Host/Origin checks and browser authentication or pairing. Mnemon separately requires the startup configuration `remoteAccess: trusted-host` for Gateway writes, ZIP operations, View mutations and settings mutations. Without that grant, settings remain readable but report `writable: false`; ordinary reads and narrow activation remain available. Existing `writeEnabled` restrictions still apply. See [remote deployment](../guides/operations.md#cloud-hosted-webui) and [configuration](../reference/configuration.md#browser-authentication). + +The new Gateway projection uses the published Typert protocol peer contract. It reuses existing handlers, preserves cancellation and validates returned RPC envelopes; it does not change Provider contracts or storage formats. + +## Light screenshots and recordings + +The [bilingual Light gallery](../../assets/webui-v0.5.4/README.md) contains 60 original JPEGs and two continuous demonstrations of about 42 seconds each. A Mnemon Pack was safely imported into disposable storage before capture. It covers memory spaces, recall, entities, the graph, dialogs, npm CLI guidance, expandable subpackages, settings, backup preview and narrow layouts. + +The packaged READMEs and current guides use this gallery. Its screenshots honestly retain their **v0.5.4 capture version**; they do not claim to show the v0.5.5 remote transport fix. All 142 historical media files keep their original hashes. The source ZIP, databases, credentials and raw logs are not distributed. + +## Packages and upgrade + +Only **`dsh-mnemon` advances to `0.5.5`**. All sixteen official plugins remain at `0.5.4`, pinned exactly by the Starter; unchanged plugin packages are not republished. + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.5 +``` + +Restart DSH and reload the browser so the new Client and Host routing are both active. Use `headless` instead of `web` for that Profile. Mnemon CLI remains separately installed and maintained through `@mnemon-dev/mnemon` on npm. + +No memory-data migration is required. Existing IDs, Provider connections, configuration formats and stored data remain compatible. Remote management needs the explicit grant described above, even though earlier 0.1.2 Host guidance treated `remoteAccess` only as a rollback option. Rollback installs `dsh-mnemon@0.5.4` in the same Profile and restarts DSH; the sixteen pinned plugin versions already match. + +## Verification and limits + +The merged RPC fix passed local, paired-remote, denied-management and trusted-management tests. Its redacted live paired-HTTPS smoke test reported successful status, Source catalog and settings requests through the Gateway; see #197 for the exact environment. The media audit checked all 60 screenshots and all 333 source video frames with zero legacy-label OCR matches, and decoded both MP4s successfully. + +Release gates cover the versioned workspace, independent plugin artifacts, the complete mixed-version Starter installation, frozen artifact integrity, npm read-back and an upgrade from v0.4.7. Tests of transport and UI mechanics do not establish model quality or live cloud-Provider behavior. Documented narrow-layout and Host-settings limits remain in scope as described in the gallery. + +Previous release: [v0.5.4](./v0.5.4.md). diff --git a/dsh/docs/en/releases/v0.5.6.md b/dsh/docs/en/releases/v0.5.6.md new file mode 100644 index 000000000..f62f35468 --- /dev/null +++ b/dsh/docs/en/releases/v0.5.6.md @@ -0,0 +1,54 @@ +# v0.5.6: Built-in Workspace Storage and Memory Reliability + +[简体中文](../../zh-CN/releases/v0.5.6.md) | **English** + +v0.5.6 adds centralized per-workspace storage to Core/Host and includes the memory reliability fixes merged since v0.5.5: [#216](https://github.com/omdsh-dev/dsh-mnemon/pull/216), [#212](https://github.com/omdsh-dev/dsh-mnemon/pull/212), [#213](https://github.com/omdsh-dev/dsh-mnemon/pull/213), [#214](https://github.com/omdsh-dev/dsh-mnemon/pull/214) and [#199](https://github.com/omdsh-dev/dsh-mnemon/pull/199). + +## Centralized workspace storage + +Choose **Centralized workspaces** in Settings → Memory system. Core/Host routes Runtime, Documents, Memory Spaces and state to `/workspaces//`. The optional root accepts an absolute path or home-relative path; clearing it uses `MNEMON_DATA_DIR` or `~/.mnemon`. The independent USER.md setting can retain a global profile. + +This is built into the main package. Sidebar inspection follows the selected registered workspace; Builtin and Headless operations follow their owning session. Existing symlink aliases share an identity, while moving or renaming a workspace selects a new identity. Scope changes preserve old data without migrating, merging or deleting it. Remote Provider namespaces keep their own sharing semantics. See [configuration](../reference/configuration.md#workspaces). + +Long Memory Space paths now fit their column, and workspace selection and alignment remain accessible when labels wrap. + +## Memory and CLI fixes + +- The default Strategy applies Runtime capacity archival consistently to named tools, generic Actions, child Agents and browser management. Maintenance retains the selected Source and namespace, revision fences and queued-turn ownership. +- Native bulk archival preserves distinct but similar facts, reuses only exact existing content and validates import receipts before Runtime compaction. Host and Memory Spaces subprocess readers preserve UTF-8 characters across output chunks. +- Autonomous write workers cannot call `mnemon_forget`; idle review can create a separate Document but cannot overwrite or archive existing Documents. Explicit user operations retain their write tools, and unsuccessful forget receipts no longer claim deletion. +- Runtime entries and active, archived and searched Documents appear newest first by creation time, including before loading more items. Editing an older item does not move its creation date. +- Verified npm JavaScript launchers run in Node mode under an Electron Host, fixing CLI version detection, memory operations, ownership checks and updates while preserving embedding overrides and native executable behavior. + +## Packages and upgrade + +| Package | Version | +|---|---| +| `dsh-mnemon` | `0.5.6` | +| `dsh-mnemon-source-runtime` | `0.5.5` | +| `dsh-mnemon-source-documents` | `0.5.5` | +| `dsh-mnemon-source-memory-spaces` | `0.5.5` | +| `dsh-mnemon-provider-mnemon-native` | `0.5.5` | +| `dsh-mnemon-strategy-default-three-tier` | `0.5.5` | + +The other eleven official plugins remain pinned at `0.5.4`. The Starter installs the exact tested sixteen-plugin combination. Mnemon CLI remains separately maintained through `@mnemon-dev/mnemon`. + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.6 +``` + +Restart DSH and reload the browser; use `headless` instead of `web` for that Profile. The DSH baseline remains 0.1.2-rc.1, whose complete profile requires Node `^22.19.0 || >=24.0.0`. + +Existing storage formats remain compatible and enabling the new scope does not move existing memory. A Pack exports the selected root only; back up the complete central directory to retain every workspace. Before rolling back to `dsh-mnemon@0.5.5`, switch away from the new `workspaces` scope to a layout supported by that version and retain a backup. Old roots remain on disk; rollback does not combine data from different roots. + +## Verification and limits + +Release gates run the complete versioned workspace suite, independent package verification, a packed Starter install, frozen artifact integrity checks, npm read-back and a real Registry upgrade from v0.4.7. + +The [versioned release verification](../../pr-assets/release-v0.5.6/README.md) records 1,176 passing local tests, 17 packed artifacts and three fresh WebUI screenshots of settings, the 0.5.6 version and memory retained after reload. + +The [Core WebUI report](../../pr-assets/issue-189-core-workspaces/README.md) retains nine screenshots and covers scope validation, two-workspace isolation, global USER.md, real Native storage, Host restart, Sidebar/Builtin routing, Chinese Dark, English Light and a 390-pixel layout. [Electron evidence](../../pr-assets/issue-201-electron-cli/README.md), [creation-order evidence](../../pr-assets/issue-202-created-order/README.md) and [Document protection evidence](../../pr-assets/pr199-document-protection/README.md) retain their original tested revisions. + +The [Flash capacity tests](../../pr-assets/runtime-capacity-flash-20260909/README.md) and [automatic-memory acceptance](../../pr-assets/runtime-memory-quality-flash-20260909/README.md) are historical bounded experiments. The latter was accepted after user review, with partial replacements, stale documents, transient retention and incomplete recall recorded as follow-ups. This release does not claim to resolve those model-quality findings. Ordinary CI skips opt-in live-model checks; the Windows binary smoke test requires Windows. + +Previous release: [v0.5.5](./v0.5.5.md). diff --git a/dsh/docs/en/releases/v0.5.7.md b/dsh/docs/en/releases/v0.5.7.md new file mode 100644 index 000000000..5176f3259 --- /dev/null +++ b/dsh/docs/en/releases/v0.5.7.md @@ -0,0 +1,46 @@ +# v0.5.7: DSH 0.1.5 Compatibility and Document Archival + +[简体中文](../../zh-CN/releases/v0.5.7.md) | **English** + +v0.5.7 fixes Memory system HTTP 405 errors on DSH 0.1.5-rc.1, provides explicit copy recovery for affected legacy Sessions, and makes document archival use verified Host receipts. It includes [#226](https://github.com/omdsh-dev/dsh-mnemon/pull/226) and [#224](https://github.com/omdsh-dev/dsh-mnemon/pull/224). + +## DSH compatibility and legacy Sessions + +The Starter supplies the Connection dependency needed to register Memory system RPC routes on the published DSH 0.1.5-rc.1 profile. Settings, Status and Source operations load through the normal authenticated transport. Earlier supported DSH peer ranges remain accepted; the development and release checks now use the published 0.1.5-rc.1 package cohort. + +New Mnemon recall and instruction messages omit the legacy summary metadata rejected by DSH's Session migration. The new `dsh-mnemon-repair-session` command previews affected v0 JSONL or JSONL.zstd files and can write a separate repaired copy. It removes only the two known historical Mnemon summary fields, preserves the remaining decoded bytes and refuses overwrites or unsupported input. + +Existing Sessions are not repaired automatically. Stop DSH and back up the entire Session root before following the [legacy Session recovery procedure](../guides/operations.md#dsh-015-compatibility-and-legacy-session-recovery). Test the copy before explicitly replacing a Session; DSH then performs its own migration. Retain the original backup for rollback. Plain JSONL recovery works on Node 20; compressed recovery requires Node 22.19 or newer supported Node versions. + +## Document archival + +The archive worker proposes an index summary and an existing eligible Memory Space without writing data. The Host validates the proposal and current access policy before indexing, appends the exact cold path and content hash, and constructs lineage from its own durable receipt. Invalid plans leave the document active and create no index. + +An exact existing index can be reused. If committing the archived document fails, the Host compensates a newly created index. The archive dialog explains that a destination must support synchronous writes and deletion; changes to read-only policy during the operation are respected. The original document content remains available in cold storage. + +## Packages and upgrade + +| Package | Version | +|---|---| +| `dsh-mnemon` | `0.5.7` | +| `dsh-mnemon-source-runtime` | `0.5.6` | +| `dsh-mnemon-source-documents` | `0.5.6` | +| `dsh-mnemon-source-memory-spaces` | `0.5.6` | + +Mnemon Native Provider and Default Three-tier Strategy remain at `0.5.5`; the other eleven official plugins remain at `0.5.4`. The Starter pins this complete sixteen-plugin combination. Mnemon CLI is still maintained separately through `@mnemon-dev/mnemon`. + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.7 +``` + +Restart DSH and reload the browser. Substitute `headless` for that Profile. The full DSH 0.1.5-rc.1 profile requires Node `^22.19.0 || >=24.0.0`. Runtime, Documents and Memory Space storage formats are unchanged. Downgrading Mnemon does not undo DSH's Session migration; use the retained pre-migration backup when returning to an older DSH version. + +## Verification + +The [versioned release report](../../pr-assets/release-v0.5.7/README.md) records 1,218 passing tests, 16 independently verified plugin repositories, 17 packed artifacts and three fresh WebUI screenshots of version 0.5.7, successful archival and the retained Native index after reload. + +The release workflow verifies the versioned workspace, independent plugin packages and packed upgrades, freezes the four changed artifacts, checks npm integrity and clean installation of the complete composition, and tests a Registry upgrade from v0.4.7 before creating the GitHub Release. + +The [DSH compatibility report](../../pr-assets/issue-223-dsh-015/README.md) retains eleven real WebUI screenshots, including the HTTP 405 reproduction and a repaired Session reopened after restart. The [document archive report](../../pr-assets/issue-222-document-archive/README.md) retains thirteen screenshots covering successful archival, invalid plans, retry, cold content, recall and tool lineage. These reports keep their original tested revisions. Loopback model fixtures validate application behavior, not model quality; live-model and platform-specific opt-in tests are separate. + +Previous release: [v0.5.6](./v0.5.6.md). diff --git a/dsh/docs/en/releases/v0.5.8.md b/dsh/docs/en/releases/v0.5.8.md new file mode 100644 index 000000000..aa1d31a4c --- /dev/null +++ b/dsh/docs/en/releases/v0.5.8.md @@ -0,0 +1,40 @@ +# v0.5.8: Runtime Archive Safety and Review Reliability + +[简体中文](../../zh-CN/releases/v0.5.8.md) | **English** + +v0.5.8 packages the six fixes merged in [#241](https://github.com/omdsh-dev/dsh-mnemon/pull/241), [#242](https://github.com/omdsh-dev/dsh-mnemon/pull/242), [#243](https://github.com/omdsh-dev/dsh-mnemon/pull/243), [#245](https://github.com/omdsh-dev/dsh-mnemon/pull/245), [#246](https://github.com/omdsh-dev/dsh-mnemon/pull/246) and [#247](https://github.com/omdsh-dev/dsh-mnemon/pull/247). [#244](https://github.com/omdsh-dev/dsh-mnemon/pull/244) adds regression evidence for legacy Session and WebUI compatibility fixes already available in v0.5.7. + +## Runtime archival + +Archive destinations must support exact synchronous writes and safe deletion before any write starts. The Host rechecks every destination and current Runtime revision, retains per-destination receipts, and compensates only entries proven to have been newly created. Deduplicated or updated records are preserved, and an uncertain local commit does not trigger deletion of the archived copy. + +Invalid routing proposals fall back as a whole batch to the validated default destination. Earlier valid batches and complete original content are retained. A destination disabled during routing is rejected before Provider writes or Runtime changes. A Provider that performs a partial remote write and then throws without reporting a receipt still cannot offer a cross-storage rollback guarantee. + +## Delegated review and WebUI + +- Keep the delegated result tool name and schema stable throughout the Host lifetime. Revocable per-run requests enforce child ownership, result validation, cancellation, concurrent isolation and disposal. +- Reuse complete inherited review evidence. A child execution guard also covers tools registered directly by other plugins and Code Mode subcalls; bounded Document search remains available when evidence is incomplete. +- Show idle-review failures in the Memory system workspace and Host log. Warnings survive ordinary turns until review succeeds or the session resets; context-window failures include actionable guidance. +- Keep the Mnemon sidebar entry visible and clickable under the ORCA LINK skin, including its collapsed layout. + +Tool-definition stability is verified; this release does not claim measured model cache hits, token savings or cost reductions. + +## Packages and upgrade + +Only the `dsh-mnemon` Starter advances, from `0.5.7` to `0.5.8`, on npm's `latest` channel. All sixteen independently versioned plugins retain their tested versions: the Runtime, Documents and Memory Spaces Sources remain at `0.5.6`; Mnemon Native Provider and Default Three-tier Strategy remain at `0.5.5`; the other eleven remain at `0.5.4`. Mnemon CLI continues to be maintained separately. + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.8 +``` + +Restart DSH and reload the browser. Use `headless` for that Profile. The full DSH profile requires Node `^22.19.0 || >=24.0.0`. Existing storage formats and supported DSH peer ranges are preserved. + +## Verification + +The [versioned release report](../../pr-assets/release-v0.5.8/README.md) records 1,301 passing tests, sixteen independently verified plugins, seventeen packed artifacts and fresh WebUI screenshots of Runtime persistence and the 0.5.8 / Native CLI status. + +The merged fixes include real WebUI comparisons, real Mnemon CLI checks and composed-plugin regression coverage. See the [archive preflight](../../pr-assets/issue-240-runtime-archive/README.md), [routing fallback](../../pr-assets/issue-235-runtime-routing/README.md), [stable result tool](../../pr-assets/issue-239-result-tool/README.md), [review guard](../../pr-assets/issue-211-20260911/README.md), [review warning](../../pr-assets/issue-210-review-warning/README.md), [ORCA sidebar](../../pr-assets/issue-232-orca-sidebar/README.md) and [existing compatibility fixes](../../pr-assets/issues-225-230-20260911/README.md) reports. Their screenshots retain the revisions actually tested; deterministic loopback models verify application behavior rather than model quality. + +The release workflow verifies the versioned workspace, all sixteen independent plugins and seventeen packed artifacts, freezes the single changed Starter artifact, verifies the complete npm composition and a real Registry upgrade from v0.4.7, then creates the GitHub Release. + +Previous release: [v0.5.7](./v0.5.7.md). diff --git a/dsh/docs/en/releases/v0.5.9.md b/dsh/docs/en/releases/v0.5.9.md new file mode 100644 index 000000000..b58d4e8dd --- /dev/null +++ b/dsh/docs/en/releases/v0.5.9.md @@ -0,0 +1,38 @@ +# v0.5.9: Runtime Archive Destinations and Legacy Session Recovery + +[简体中文](../../zh-CN/releases/v0.5.9.md) | **English** + +v0.5.9 includes the Runtime archive destination fix in [#252](https://github.com/omdsh-dev/dsh-mnemon/pull/252), legacy Session recovery in [#253](https://github.com/omdsh-dev/dsh-mnemon/pull/253), and the sidebar label alignment already merged in [#249](https://github.com/omdsh-dev/dsh-mnemon/pull/249). + +## Runtime archival + +A capacity-triggering write can now archive to eligible spaces activated or created by its initiating View after recall was pinned. The Host asks the owning Memory Spaces Source for its write scope and rechecks the exact Source, View, active destinations and cancellation before each batch. Recall remains pinned; older Sources retain the conservative fallback. Refusal preserves existing Runtime entries and pending input. + +## Legacy Session recovery + +The explicit copy-repair CLI handles the three known old Mnemon summaries, compatible v2 subagent descriptors, packed empty-string placeholders and null delta names. It preserves stream assembly, sequence/provenance and token timing, as well as unrelated plugin content. + +For completed tool chains whose IDs were cleared by the old DeepSeek adapter, repair can restore the unique provider ID already recorded in that same stream. It requires consistent call/result payloads and closed ownership references. It never generates an ID. Missing candidates, conflicting identities and unaccounted references still refuse the whole output. The original file and existing output paths are never overwritten. See [Operations](../guides/operations.md) for preview and new-copy commands. + +The reporter's original Windows source-build logs were not available for direct validation. [Issue #251](https://github.com/omdsh-dev/dsh-mnemon/issues/251) remains open for confirmation against those artifacts. + +## Packages and upgrade + +- `dsh-mnemon`: **0.5.9**. +- `dsh-mnemon-source-memory-spaces`: **0.5.7**. + +The Starter pins the complete sixteen-plugin composition. The other fifteen plugin versions and the supported DSH peer ranges remain unchanged. Mnemon CLI is maintained separately. + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.9 +``` + +Restart DSH and reload the browser. Substitute `headless` for that Profile. The full DSH profile requires Node `^22.19.0 || >=24.0.0`. Updating the package does not automatically rewrite old Session files; use the explicit repair command when needed. + +## Verification + +The combined implementation passed 1,452 tests with six opt-in skips, real Mnemon 0.2.8 CLI checks, sixteen independent plugin repositories and seventeen packed artifacts. The [Runtime report](../../pr-assets/issue-250-runtime-write-scope/README.md) and [legacy Session report](../../pr-assets/issue-251-legacy-repair/README.md) retain 26 before/after and cold-reopen screenshots with hashes and the revisions actually tested. Legacy continuation was exercised through the actual WebUI, including validation of the model request's recorded tool ID, arguments and result. Model responses used deterministic loopback fixtures. + +The release workflow verifies the versioned workspace and independent artifacts, freezes only the two changed packages, publishes Memory Spaces Source before the Starter, and checks the complete Registry install plus a real upgrade from v0.4.7 before creating the GitHub Release. + +Previous release: [v0.5.8](./v0.5.8.md). diff --git a/dsh/docs/en/roadmap.md b/dsh/docs/en/roadmap.md new file mode 100644 index 000000000..eb32159fa --- /dev/null +++ b/dsh/docs/en/roadmap.md @@ -0,0 +1,37 @@ +# Roadmap + +**English** | [简体中文](../zh-CN/roadmap.md) | [Documentation](./README.md) + +This page separates shipped foundations from remaining work. Future items are directions, not release promises. + +## Shipped foundations + +- Independent Source, Strategy and Provider packages, a small public contribution service, Source-owned pages and one immutable View per executing turn. +- A Starter with the Layered strategy as its default, the General strategy as an alternative main strategy, and three optional enhancements that work with either. +- Source-local Provider descriptors, connection schemas, redaction and child-Fiber registration. +- Public-contract tests, independently installed artifacts, isolated Headless/WebUI fixtures and selective package releases. + +See [Architecture](./development/architecture.md), [Development](./development/README.md) and [Release process](./development/releasing.md) for the actual scope and limits. + +## Next: reliability and recoverability + +- Persist background-review watermarks and pending activity across restarts; add bounded backoff and explicit retry. +- Improve idempotence and cancellation under interrupted maintenance; retain evidence of partial writes and source revisions. +- Add a deterministic sensitive-content defense before memory admission. Current model guidance is not a secret scanner. +- Expand real WebUI and fault-injection coverage: scopes, concurrent edits, capacity limits, remote deployment paths, provider recovery and supported OS combinations. +- Extend the [verified compatibility matrix](./reference/compatibility.md), including explicitly authorized real-Provider tests. Adapter fixtures alone are insufficient. + +## Later: controlled maintenance and extension growth + +- Reviewable long-term consolidation, conflict/decay candidates and deliberate forgetting. +- Stronger multi-component backup/restore drills and repair tools for damaged metadata or missing projections. +- Explicit data-format migration and rollback procedures when persistent formats actually need to change. +- Better background-review history and diagnostics; broader internationalization of Host errors and command output. +- A reusable DSH directory picker when the Host exposes the necessary capability; track [dsh-external/issues#603](https://github.com/dsh-external/issues/issues/603) (private repository; requires access, and unauthenticated requests return 404). +- Candidate Source/Strategy evaluation and controlled promotion for RSI: preserve inputs, artifacts, permissions and comparison results before an explicit installation decision. + +## Boundaries that remain + +The current UI presents memory behavior, not a generic plugin marketplace or View canvas. External plugins follow DSH installation and the public author contracts. + +Cordis ownership is not a sandbox. There is no automatic execution of model-generated code, universal deletion guarantee, cross-Provider transaction, notification daemon or claim that every third-party memory plugin integrates without adaptation. diff --git a/dsh/docs/plans/memory-space-media-refresh.md b/dsh/docs/plans/memory-space-media-refresh.md new file mode 100644 index 000000000..623b3bfd6 --- /dev/null +++ b/dsh/docs/plans/memory-space-media-refresh.md @@ -0,0 +1,40 @@ +# Memory space media refresh + +## Goal and scope + +Audit repository media and current documentation after the memory space terminology change, recapture current UI on v0.5.4, replace stale current references, and submit a PR. Preserve the provenance of versioned release, Issue, and PR evidence. + +## Plan + +1. Inventory every local image and recording with its references. Inspect old screenshots for terminology and distinguish current guidance from historical evidence. +2. Run the real v0.5.4 DSH WebUI in a disposable environment with the user-supplied Mnemon Pack imported first. Use Light appearance for all media. Capture matching English and Chinese desktop surfaces, key dialogs, version maintenance, configuration, disabled states, and narrow memory-space flows. +3. Build one documented current gallery with dimensions, source revision, capture conditions and file hashes. Update README, getting started, operations, configuration and UI guides; replace the current entry point to the old demo with current media. +4. Verify screenshot readability and terminology, media hashes, current-reference coverage and bilingual links. Run repository checks and submit a complete PR with evidence. + +## Acceptance + +- Every media reference in current usage guides points to current evidence or has an explicit historical purpose. +- Product screenshots show 记忆空间 / memory space, the real v0.5.4 interface, and meaningful state from the supplied backup. +- No screenshot labels or application state are painted over; original historical evidence keeps its identity. +- Chinese and English receive matching coverage, including important narrow layouts. +- The PR records validation results and any environment limits. + +## User steering + +Import the supplied Mnemon Pack before capturing screenshots and a new recording. Keep the original archive and personal memory directories unchanged. Inspect visible imported content for publication suitability; never commit the backup, databases, credentials or raw private records. + +Recapture every current screenshot and recording in Light mode, following the user’s latest visual preference. Discard the uncommitted dark drafts. + +## Completed capture and validation + +- Imported the provided format-1 Mnemon Pack through the real safe-import UI before capture. Its original hash remains unchanged. +- Captured 60 Light JPEGs: 26 desktop and four narrow surfaces in each language. Recorded two continuous Light demos with 333 source frames in total. +- Checked every current screenshot and source video frame for legacy product labels; zero OCR matches and errors. Both encoded videos decode successfully. +- Audited all 142 existing media files and retained their original hashes. Current guide media references resolve to the new Light gallery or current architecture diagrams. +- Updated matching bilingual README, onboarding, operations, configuration, UI and compatibility guidance. Added a root-only patch changeset because README ships in the npm package. +- `npx --yes pnpm@10.13.1 run verify` passed: 1083 tests passed; Windows smoke and opt-in native integration were skipped under their existing environment guards. Type checks, 39-file deterministic builds, Headless activation, package contents and exports, publint and attw passed. +- Documentation verification passed with 939 local links and 35 Markdown anchors. Source-map warnings come from the published DSH primitives package's missing map, without test failures. +- Original ZIP and personal roots were not modified. The disposable imported copy and server were removed, and the capture browser tab was closed. +- Known limits remain documented: some narrow card fields truncate, the Chinese disabled Documents page exposes its English Source title, and model/cloud-provider behavior was not evaluated. + +Delivery scope: submit a reviewed GitHub PR. Merging and release belong to a separate maintainer action. diff --git a/dsh/docs/plans/memory-space-terminology.md b/dsh/docs/plans/memory-space-terminology.md new file mode 100644 index 000000000..fb3d193fd --- /dev/null +++ b/dsh/docs/plans/memory-space-terminology.md @@ -0,0 +1,39 @@ +# Memory space terminology + +## Goal + +Use **记忆空间** in Chinese and **memory space** in English for the persistent, Provider-backed unit. Keep individual memories, Runtime memory, Documents, Provider namespaces, and the overall memory system distinct. + +## Plan + +1. Update product copy, accessibility labels, settings, Agent guidance, command output, package documentation, and contributor templates. Review sentences that previously mixed the two terms instead of only replacing words. +2. Use `MemorySpace` for the canonical code model and related types, registry, internal helpers, UI components, and presentation keys. Keep deprecated type aliases where published consumers need them. +3. Preserve published v0.5.x tool/RPC identifiers, request/result fields, Provider adapter context, and persisted registry/Document/Pack formats. Document these compatibility spellings; a terminology change must not change routing, ownership, authorization, or data locations. +4. Preserve historical screenshot pixels and measured evidence. Capture current Chinese and English UI evidence with the new terminology, check narrow layouts, and update the current showcase where it displays the renamed concept. +5. Verify canonical/legacy type compatibility, existing data and Provider integration, UI behavior, bilingual documentation links, complete workspace checks, and independent package artifacts. Add changesets for every changed published package. + +## Acceptance + +- Current UI and authored product descriptions consistently use the new terms, including empty/error states and accessible names. +- Canonical code types and internal names express memory spaces; compatibility names are explicitly documented rather than silently removed. +- Existing configuration, registries, Document lineage, Packs, tools, and Provider adapters continue to work. +- Chinese/English flows remain usable at desktop and mobile widths, with current screenshots and validation limits recorded. + +## Completed implementation + +- Updated current product text, accessibility labels, Agent guidance, command output, bilingual documentation, package READMEs, contributor templates, and editable diagrams. Current README and guide images now use the terminology update; historical recordings and measured evidence remain identified as historical. +- Introduced canonical `MemorySpace` contracts, `MemorySpaceRegistry`, and service operations such as `spaceDirectory`, `createSpace`, and `mergeSpaces`. Existing published types and operations retain compatibility aliases. Provider factories accept the canonical authority name while supporting older hosts; the legacy protected HTTP Provider field remains a field so existing subclasses can still override it. +- Preserved registry file names and JSON shapes, tool/RPC spellings, Document/Pack lineage, revision encoding, CSS class maps, and historical documentation anchors. Migration fixtures verify that user-authored legacy titles are preserved. +- Fixed narrow directory controls and paths, wrapped Provider metadata badges and the header storage badge, and tightened navigation spacing for longer labels. [Bilingual browser evidence](../assets/memory-space-terminology/README.md) covers desktop and narrow layouts. +- Added patch release intent for all 17 changed published packages; no package version was changed or published by this terminology task. + +## Validation + +- Full `pnpm run verify`: passed, including deterministic builds, workspace typechecks, 783 root tests, 300 plugin tests, Headless activation, public entries, and package lint. Two opt-in native integration cases were skipped. +- After preserving the legacy fields as fields, Source build, root typecheck, and all 62 service tests passed again. +- Independent artifact verification: all 16 plugin repositories and 17 tarballs passed standalone installation, build, typecheck, tests, external public-SDK consumption, and real DSH activation. The external fixture compiles both model names and overrides the old protected Provider field. +- Changeset coverage checked against both tracked working-tree changes and newly added files: all 17 changed published packages are covered. +- Real browser checks: Chinese and English creation, editing, activation, content browsing, Runtime and Document flows, settings, language switching, and cancellation. At 390 × 844, the canvas and directory have no horizontal overflow; create-dialog buttons end at y=834. Desktop captures use 1365 × 1000. All 18 original JPEG captures have verified hashes. +- Current locale catalogs contain no old product term. Remaining legacy wording is limited to compatibility migration fixtures, historical raw measurements, and old URL anchors. Existing user data does not require migration. + +Browser data was disposable and removed. Model quality, cloud Provider behavior, and the opt-in platform integrations are outside this verification record. diff --git a/dsh/docs/plans/plugin-first-ui.md b/dsh/docs/plans/plugin-first-ui.md new file mode 100644 index 000000000..6aada274c --- /dev/null +++ b/dsh/docs/plans/plugin-first-ui.md @@ -0,0 +1,66 @@ +# Plugin-first interface + +## Aim + +Every capability of dsh-mnemon may be extended: Sources, main Strategies, enhancements, Providers, and roles not yet defined. The interface is therefore a host of regions that components fill, not a set of screens written for the shipped components. Shipped components use the same path as installed extensions. The structure stays small: DSH's slots, dsh-mnemon's existing declarations, and as few new definitions as the regions require. + +## Two layers + +1. **Declarations, no UI code.** `defineMemoryPlugin` gives a component's name and description (`en`, `zh-CN`), roles, `requires`, `provides`, exclusive claims and extension slot; `memoryStrategyConfiguration.fields` gives its public options. These alone draw the Memory composition board, the component's page (state, relations, what its switch moves, generated option controls), and the names the Memory System shows. +2. **Contributions, when a component needs its own UI.** A component registers into a region dsh-mnemon declares as a DSH slot. Nothing else is required of it. + +## Regions + +| Region | Slot | Kind, key | Declared on | Status | +|---|---|---|---|---| +| Memory System page and tab | `mnemon.source.page` | list | `shell.overlay`, `conversation.view` | existing | +| A component's own settings, on its page | `mnemon.component.settings` | keyed by package name | `plugins.bundle.config` (DSH's row pages render what was registered there, since a child slot has one declaring entry) | new | +| A component's card on the Status page | `mnemon.component.status` | keyed by package name | `shell.overlay`, `conversation.view` | new | + +`installMemoryComponentUI(ctx, { packageName, settings?, status? })` in `dsh-mnemon/client` registers a component's contributions, beside the existing `installMemorySourceUI`. Owner props carry only what the host knows about the component (its declaration view, writability, language); a registrant injects its own services through its registration, as DSH slots already allow. + +## A component's page + +One content, two DSH containers: + +- the dialog opened from the board's row, and +- for rows of the Starter bundle, DSH's own row page through `plugins.row.config` (`dsh-mnemon#`), which gives the row in DSH's component list a configure control. DSH heads that page with the name and description each package's `locale/*.json` gives, the same as its declaration, so the page omits its own package line; related components open over it. + +The page shows, in order: state and switch; origin (shipped or installed package); relations; what the switch would also move; declared options; then the component's contributed settings. + +## Moves for the shipped components + +- **Runtime Memory** contributes where its user profile (USER.md) lives. +- **Memory Spaces** contributes Memory providers and embedding. They leave the page's top level. +- **Layered strategy** contributes the background tasks it drives: the task Agent model and idle review. +- The Sources contribute their Status cards; the Memory System names tabs, cards, storage areas, the header's main Strategy and layers from declarations rather than a table of shipped names. Every Source component has a card, a component that is off included. +- dsh-mnemon keeps what belongs to no component: storage, backup, interface, versions. Storage names the components that keep their data in its directory, from the Sources a backup carries. + +## Rules + +- A region renders nothing for a component that contributed nothing; declarations still give it a page. +- Copy stays short: names, states, chips and one-line descriptions; sentences only where an action needs its reason. +- No new registry, store or protocol beyond the slots above; the View transaction and settings scopes stay the only writers. + +## Interaction + +The same control behaves the same way everywhere, on the configuration and on every component page, shipped or installed. + +| What | Behaves | +|---|---| +| A gear on a component's row | Marks a component with settings of its own (declared options or contributed settings) and opens its page; on the main Strategy row it opens the selected Strategy's page. It is the visible way in. | +| A component's name: a row title, a relation chip, a name on a page, a Storage chip | Opens that component's page too, underlined on hover. A name on a page opens its page in place, with a way back. | +| A switch or a selector | Applies when it changes. It shows the new value at once, and the saved one again beside the reason if the write fails. | +| Typed values (text, numbers, lists) | Wait for their group's Apply, which appears only once something changed and says what to fix while a value is refused. Discard puts the saved values back. The page stays open after Apply. | +| A change that moves data (the storage location) | Waits for Apply, and the Apply line says what applying does. | +| Buttons, focus, hover | DSH's Button and focus ring; no control of dsh-mnemon's own shape. | + +Every dialog body carries the page's box model (`.surface`), since DSH renders dialogs outside the page; field grids fit as many columns as their width allows. + +## Acceptance + +- A third-party component with only a declaration appears on the board, has a page with relations and options, and switches with its dependencies. +- A third-party component that registers settings sees them on its page in both containers; a shipped component's settings arrive the same way. +- The Status page and tabs show a new Source by its declared name, and its card when it registers one. +- Tests cover slot registration and rendering, the moved panels' saves, and unchanged stored settings; documentation for authors lists every region with its props. +- Every row in the tables above behaves as stated, with a test for each kind of control. diff --git a/dsh/docs/plans/status-version-maintenance.md b/dsh/docs/plans/status-version-maintenance.md new file mode 100644 index 000000000..686f4c0d5 --- /dev/null +++ b/dsh/docs/plans/status-version-maintenance.md @@ -0,0 +1,29 @@ +# Status version maintenance + +## Plan + +- Model installed, missing, unreadable, outdated, linked, and restart-pending states separately. Registry failures must not hide local installation guidance. +- Recommend the official `@mnemon-dev/mnemon` npm package (Node.js 22+). Detect its launcher and owning npm installation before offering an update; keep existing Homebrew and Go updates. Installation and migration are explicit terminal commands, with PATH and configured CLI-path guidance. +- Keep the two product cards compact. Expand dsh-mnemon to inspect Sources, Strategies, and Providers, their installed/recommended versions, and maintenance ownership. +- Update independently installed official subpackages only in their owning DSH Profile. Starter dependencies retain the Starter's tested pins and update with the Starter. Never replace source links or install arbitrary package names from RPC input. +- Serialize package writes, verify resulting versions, retain restart reminders across checks, and preserve management authorization. +- Verify Host state transitions, registry failures, ownership and update scope; exercise authorized/read-only UI, disclosure, copy feedback, and responsive rendering. Synchronize installation and operations documentation in English and Chinese. + +## Acceptance + +- Missing or unreadable CLI versions never appear as current; unavailable registry data still leaves npm instructions accessible. +- npm detection follows the executable used by DSH and does not update a different Node/npm installation. +- dsh-mnemon subpackages are collapsed initially and individually actionable only when independently managed by the active Profile. +- A successful package installation remains marked as requiring a Host restart until the Host restarts. +- Checks are read-only. Updates keep the existing management-authority boundary and reject unrecognized targets. + +## Verification + +Implemented and verified on 2026-09-06. + +- The version dialog now distinguishes actionable installation states, keeps npm commands visible, and collapses post-install instructions and the sixteen-package inventory. +- Native CLI discovery belongs to Memory Spaces' public `native-cli` entry and is shared by the Host version checker and the Source runner. This avoids requiring a newer Core SDK when the Source is upgraded independently. +- Full `pnpm@10.13.1 run verify` passed, including 783 root tests, workspace tests, deterministic builds, Headless activation and package checks. +- Independent package verification passed for sixteen plugin repositories and seventeen packed artifacts. +- The real DSH WebUI was checked at 1280 × 720 and 390 × 844. Copying, disclosure, scrolling, and long package names work without horizontal overflow. +- [Screenshots and validation limits](../pr-assets/status-version-maintenance/README.md) record the real UI evidence and the simulated Windows coverage. diff --git a/dsh/docs/pr-assets/README.md b/dsh/docs/pr-assets/README.md new file mode 100644 index 000000000..536217a3f --- /dev/null +++ b/dsh/docs/pr-assets/README.md @@ -0,0 +1,85 @@ +# Verification evidence + +[Documentation](../en/README.md) · [文档中心](../zh-CN/README.md) + +These are dated engineering records, not a gallery of current product features. Each report owns its tested revision, setup, results and limits. Do not replace historical screenshots or reinterpret old benchmark scores as evidence for a later implementation. + +以下是工程验收记录,不是当前功能图集。每份报告只对应其注明的 revision、环境、结果与限制;不要替换历史截图,也不要将旧 benchmark 成绩用作新实现的证据。 + +For new evaluations, commit the reusable harness, workload generator, concise bilingual summary, aggregate metrics, reproduction parameters and minimal failure examples. Put expanded workloads, complete answers, memory snapshots and per-wave output in a sanitized archive attached to the PR. Before removing bulk evidence from the working tree, verify the archive download and record its URL, SHA-256, tested revision and file manifest. Keep reviewed acceptance separate from original automated scores. Routine CI outputs can remain CI artifacts; retain acceptance evidence in a downloadable PR attachment. + +新的评测在仓库中保留可复用脚本、工作负载生成器、简短双语摘要、汇总指标、复现参数和最小失败样例。展开的输入、全部回答、记忆快照和逐轮输出先脱敏打包,作为 PR 附件保存;移出工作树前验证下载,并记录 URL、SHA-256、实测 revision 和文件清单。人工验收结论与原始自动评分分开记录。常规 CI 输出可作为 CI artifact,作为验收依据的数据另保留可下载的 PR 附件。 + +| Record / 记录 | Scope / 范围 | +|---|---| +| [v0.5.24 release](./release-v0.5.24/README.md) / [中文](./release-v0.5.24/README.zh-CN.md) | The release packages through Add plugin on fresh DSH 0.2.0-rc.2 and 0.1.7-rc.2 profiles, Enable now without a restart, Status, Runtime, Native CLI and WebUI recall, and the Entities page on CLI-written entities / 在全新的 DSH 0.2.0-rc.2 与 0.1.7-rc.2 profile 上通过添加插件安装发布包、不重启直接启用、状态页、运行时记忆、Native CLI 与 WebUI 检索,以及 CLI 写入实体后的实体页 | +| [Entities page counts and lists from every memory](./entities-complete-index/README.md) / [中文](./entities-complete-index/README.zh-CN.md) | Synthetic Native spaces on main and with the fix, on DSH 0.2.0-rc.2 and 0.1.7-rc.2: rail counts, the memories a selection lists, related memories and click-to-first-card times, plus timings on 4 × 500 memories / 合成 Native 空间在 main 与修复后、DSH 0.2.0-rc.2 与 0.1.7-rc.2 上的表现:左侧计数、选中后列出的记忆、相关记忆与点击到首张卡片的耗时,以及 4 × 500 条记忆上的计时 | +| [Algorithm performance audit](./algorithm-performance-20261003/README.md) / [中文](./algorithm-performance-20261003/README.zh-CN.md) | N=1/10/100/1000 local query, storage and recall measurements, complexity analysis and behavior-preserving optimization / N=1/10/100/1000 本地查询、存储与召回实测、复杂度分析及保持行为的优化 | +| [v0.5.22 release](./release-v0.5.22/README.md) / [中文](./release-v0.5.22/README.zh-CN.md) | The release package through Add plugin on fresh DSH 0.2.0-rc.2 and 0.1.7-rc.2 profiles, Enable now without a restart, Status, Runtime, Native CLI and WebUI recall / 在全新的 DSH 0.2.0-rc.2 与 0.1.7-rc.2 profile 上通过添加插件安装发布包、不重启直接启用、状态页、运行时记忆、Native CLI 与 WebUI 检索 | +| [One-click update in the desktop app](./version-update-through-dsh/README.md) / [中文](./version-update-through-dsh/README.zh-CN.md) | Check versions updating dsh-mnemon through DSH's plugin installer with the desktop app's pnpm, the page reopening after DSH swaps in the new client, a restart, DSH 0.1.7-rc.2, no package manager, a Strategy added on its own, and the restart reminder after Check versions or `dsh plugin` / 检查版本通过 DSH 插件安装器、用桌面版自带的 pnpm 更新 dsh-mnemon,DSH 换上新客户端后页面重新打开,重启,DSH 0.1.7-rc.2,没有包管理器,单独安装的 Strategy,以及检查版本或 `dsh plugin` 更新后的重启提醒 | +| [Memory subagents and chat templates that require a user query — issue #327](./issue-327-subagent-user-turn/README.md) / [中文](./issue-327-subagent-user-turn/README.zh-CN.md) | Idle review behind an Ollama 0.33 emulation on main and with the fix: request by request in a real DSH composition, and in the WebUI on DSH 0.1.7-rc.2 and 0.2.0-rc.2 / Ollama 0.33 模拟下 main 与修复后的后台审查:真实 DSH 组合中的逐次请求,以及 DSH 0.1.7-rc.2 与 0.2.0-rc.2 上的 WebUI 运行 | +| [Memory System in a conversation tab without a loaded Agent](./conversation-tab-workspace/README.md) / [中文](./conversation-tab-workspace/README.zh-CN.md) | A conversation DSH shows but cannot resume, before and after on DSH 0.2.0-rc.2 and 0.1.7-rc.2: Status, Project Documents, new Documents and Runtime entries in Conversation tab and Sidebar mode, and a nine-scenario switching matrix / DSH 能显示但无法恢复的会话,在 DSH 0.2.0-rc.2 与 0.1.7-rc.2 上的修复前后:会话标签页与侧边栏模式下的状态页、项目档案、新建档案与运行时记忆,以及九个切换场景 | +| [v0.5.21 release](./release-v0.5.21/README.md) / [中文](./release-v0.5.21/README.zh-CN.md) | The release packages through Add plugin on fresh DSH 0.2.0-rc.2 and 0.1.7-rc.2 profiles, Enable now without a restart, Status, Runtime, Native CLI and WebUI recall / 在全新的 DSH 0.2.0-rc.2 与 0.1.7-rc.2 profile 上通过添加插件安装发布包、不重启直接启用、状态页、运行时记忆、Native CLI 与 WebUI 检索 | +| [Fallback sidebar entry as a native row — issue #318](./issue-318-sidebar-entry/README.md) / [中文](./issue-318-sidebar-entry/README.zh-CN.md) | The fallback Memory System entry beside DSH's own rows on main and with the fix: expanded, open and collapsed, with computed styles / main 与修复后的兜底“记忆系统”入口与 DSH 原生行并排:展开、打开与折叠状态,以及计算样式 | +| [Idle review writes one layer — issue #319](./issue-319-review-layers/README.md) / [中文](./issue-319-review-layers/README.zh-CN.md) | A scripted review pass that repeats a Document in working memory on main and with the fix, and Write runtime memory switched off / 脚本化审查在 main 与修复后把项目档案重复写入工作记忆的对比,以及关闭写入运行时记忆 | +| [Large Native Memory Spaces — issue #320](./issue-320-native-store-dumps/README.md) / [中文](./issue-320-native-store-dumps/README.zh-CN.md) | A 900-insight Native store before and after: graph, contents and runtime memory archiving at capacity, with exact CLI readback / 900 条记忆的 Native 库修复前后:图谱、内容与运行时记忆满容量归档,以及 CLI 逐字核验 | +| [v0.5.20 release](./release-v0.5.20/README.md) / [中文](./release-v0.5.20/README.zh-CN.md) | The release package through Add plugin on fresh DSH 0.2.0-rc.1 and 0.1.7-rc.2 profiles, Enable now without a restart, Status, Runtime, Native CLI and WebUI recall / 在全新的 DSH 0.2.0-rc.1 与 0.1.7-rc.2 profile 上通过添加插件安装发布包、不重启直接启用、状态页、运行时记忆、Native CLI 与 WebUI 检索 | +| [Cold start and upgrades with the component group](./starter-group-upgrade/README.md) / [中文](./starter-group-upgrade/README.zh-CN.md) | Web, command line, Headless and desktop-layout cold starts, updates from 0.5.16 through 0.5.19, a stuck Starter row, an update without a restart, and pnpm's release-day fallback / 网页、命令行、Headless 与桌面版布局的冷启动,从 0.5.16 到 0.5.19 的升级,卡住的 Starter 行,不重启的更新,以及 pnpm 发布当天的回退 | +| [Starter component group — PR #314](./starter-group-readiness/README.md) / [中文](./starter-group-readiness/README.zh-CN.md) | The Plugins page before and after, Enable now on both hosts, a leftover Starter row, the desktop generation layout with and without preparation, and the config schema trade-off / 改动前后的插件页、两个宿主上的立即启用、残留的 Starter 行、有无准备步骤时的桌面版 generation 布局,以及配置 schema 的取舍 | +| [Desktop windows manage memory — issue #310](./issue-310-desktop-window/README.md) / [中文](./issue-310-desktop-window/README.zh-CN.md) | Published 0.5.18 read only in a `dsh-app://app/` window, the fix on DSH 0.1.7 and 0.2 Desktop windows, and a remote page that stays read only and says why / 已发布的 0.5.18 在 `dsh-app://app/` 窗口中只读、修复在 DSH 0.1.7 与 0.2 桌面窗口中的表现,以及保持只读并写明原因的远程页面 | +| [Component pages and one interaction rule](./component-pages-20260927/README.md) / [中文](./component-pages-20260927/README.zh-CN.md) | Settings on their components' pages, gears and names that open them, apply-at-once choices and Apply for typed values, storage location, component-named Memory System, DSH's list and row pages, dark and narrow / 设置位于各自组件页、打开它们的齿轮与名称、即时生效的选择与输入值的应用、存储位置、按组件命名的记忆系统、DSH 列表与行页面,深色与窄列 | +| [Memory composition drawn from component declarations](./composition-board-20260927/README.md) / [中文](./composition-board-20260927/README.zh-CN.md) | Board groups by role, relation chips, component pages with declared options, cascades with Undo, problem line, off layers, dark and narrow / 按角色分组的面板、关联标签、带声明选项的组件详情、带撤销的连带开关、问题提示、已关闭的层,深色与窄列 | +| [Component switches and linked UI](./plugin-linkage-20260927/README.md) / [中文](./plugin-linkage-20260927/README.zh-CN.md) | Main Strategy states, turns without memory, component notes on layers and Providers, stopped workspace layers in light and dark / 主策略状态、无记忆时的对话、记忆层与 Provider 的组件说明、工作台未运行的层,浅色与深色 | +| [Configuration under Plugins](./plugin-config-20260927/README.md) / [中文](./plugin-config-20260927/README.zh-CN.md) | The dsh-mnemon page under Plugins, unsaved-change bar, links with the workspace, remote pages and live refresh / 插件中的 dsh-mnemon 页面、未保存修改栏、与工作台的互相跳转、远程页面与实时刷新 | +| [DSH-style memory interface](./dsh-style-ui-20260927/README.md) / [中文](./dsh-style-ui-20260927/README.zh-CN.md) | Settings groups and preference rows, Status Provider list, Memory Spaces and conversation controls in light and dark / 设置分组与偏好行、状态页 Provider 列表、记忆空间与对话控件的浅色和深色对比 | +| [Optional Mnemon CLI](./optional-mnemon-cli-20260927/README.md) / [中文](./optional-mnemon-cli-20260927/README.zh-CN.md) | No-CLI WebUI and Headless persistence on Docker Mem0, eight-Provider lab with recall, content and entities / 无 CLI 的 WebUI 与 Docker Mem0 上的 Headless 沉淀、八个 Provider 的检索、内容与实体 | +| [Main Strategy choice](./main-strategy-choice-20260927/README.md) / [中文](./main-strategy-choice-20260927/README.zh-CN.md) | Plugins page and Settings controls, general Strategy conversation, packed composition and release intent / 插件页与设置控件、通用主策略对话、打包组合与发布意图 | +| [Memory System brain icon](./sidebar-brain-icon-20260927/README.md) / [中文](./sidebar-brain-icon-20260927/README.zh-CN.md) | Shared thin brain outline, native navigation and retained composable naming / 共用细线大脑轮廓、原生导航与可组合记忆命名保留 | +| [Sidebar icon weight](./sidebar-icon-weight-20260927/README.md) / [中文](./sidebar-icon-weight-20260927/README.zh-CN.md) | Native icon stroke alignment, expanded/collapsed comparison and navigation / 原生图标线宽对齐、展开/折叠对比与导航验证 | +| [Composable memory naming](./composable-naming-20260927/README.md) / [中文](./composable-naming-20260927/README.zh-CN.md) | Packed Chinese/English metadata and real plugin-page display / 制品中英文元数据与真实插件页面展示 | +| [v0.5.19 release](./release-v0.5.19/README.md) / [中文](./release-v0.5.19/README.zh-CN.md) | Versioned candidate through Add plugin on DSH 0.2.0-rc.1 and 0.1.7-rc.2, Status, Runtime, Native CLI and WebUI recall, live-model Save to memory / 在 DSH 0.2.0-rc.1 与 0.1.7-rc.2 上通过添加插件安装的版本化候选、状态页、运行时记忆、Native CLI 与 WebUI 检索、真实模型的存入记忆 | +| [v0.5.16 release](./release-v0.5.16/README.md) / [中文](./release-v0.5.16/README.zh-CN.md) | Real Flash, versioned seventeen-package composition, Native CLI, archival and cold-restart persistence / 真实 Flash、十七个版本化包组合、Native CLI、归档与冷重启数据保留 | +| [Chinese plugin metadata — PR #286](./pr-286-plugin-metadata/README.md) / [中文](./pr-286-plugin-metadata/README.zh-CN.md) | Packed locale validation, real DSH metadata reader and Chinese/English plugin display / 打包 locale 验证、真实 DSH 元数据读取与中英文插件展示 | +| [Native Sidebar and bundle diagnostic](./sidebar-native-20260926/README.md) / [中文](./sidebar-native-20260926/README.zh-CN.md) | Native panel alignment, real skins, CLI/WebUI and isolated group-list candidate / 原生导航对齐、真实皮肤、CLI/WebUI 与隔离的分组清单候选补丁 | +| [v0.5.15 release](./release-v0.5.15/README.md) / [中文](./release-v0.5.15/README.zh-CN.md) | Versioned composition, actual USER tool write, model projection metadata and reload persistence / 版本化组合、真实 USER 工具写入、模型投影元数据与刷新保留 | +| [USER empty branches — issue #281](./issue-281-user-branches/README.md) / [中文](./issue-281-user-branches/README.zh-CN.md) | Published rc.2 WebUI before/after, strict scope boundaries, CLI and three-tier workflows / 正式 rc.2 WebUI 前后对比、严格范围边界、CLI 与三层基础流程 | +| [v0.5.14 release](./release-v0.5.14/README.md) / [中文](./release-v0.5.14/README.zh-CN.md) | Versioned artifacts, real CLI, packed WebUI Runtime edits and cold-restart settings / 版本化制品、真实 CLI、制品 WebUI Runtime 编辑与冷启动设置 | +| [Agent Teams idle review — issue #275](./issue-275-agent-teams/README.md) / [中文](./issue-275-agent-teams/README.zh-CN.md) | Published Teams compatibility, whole user checkpoints, guarded child execution, real CLI and packed WebUI / 正式 Teams 兼容、完整用户检查点、子代理执行限制、真实 CLI 与制品 WebUI | +| [OpenViking user-key discovery — issue #271](./issue-271-user-key/README.md) / [中文](./issue-271-user-key/README.zh-CN.md) | Admin-denied discovery before/after, scoped data API validation, multi-plugin isolation and real CLI/WebUI / Admin 拒绝发现前后对比、数据 API 范围校验、多插件隔离与真实 CLI/WebUI | +| [Desktop profile generations — issue #274](./issue-274-profile-generation/README.md) / [中文](./issue-274-profile-generation/README.zh-CN.md) | Original Desktop loader failure, generation installs, legacy/live settings, real WebUI and Headless / 原版 Desktop loader 失败、generation 安装、新旧设置、真实 WebUI 与 Headless | +| [Builtin conversation width handles — issue #265](./issue-265-builtin-width-handles/README.md) / [中文](./issue-265-builtin-width-handles/README.zh-CN.md) | RC2/alpha2 WebUI before/after, composer and width dragging, Source writes and plugin isolation / RC2/alpha2 WebUI 对比、输入框与宽度拖动、Source 写入及插件隔离 | +| [Windows workspace ancestors — issue #266](./issue-266-workspace-ancestor/README.md) / [中文](./issue-266-workspace-ancestor/README.zh-CN.md) | Actual Windows file-ancestor rejection, portable path tests, Native CLI and packed plugin verification / 真实 Windows 文件祖先拒绝、跨平台路径测试、Native CLI 与插件制品验证 | +| [DSH RC / alpha compatibility — issue #261](./issue-261-dsh-slots/README.md) / [中文](./issue-261-dsh-slots/README.zh-CN.md) | Published package types, real WebUI before/after, turn-tail peers, session isolation and packed Headless / 正式制品类型、真实 WebUI 前后对比、回合插件组合、会话隔离与制品 Headless | +| [Windows Git probe console — issue #259](./issue-259-windows-git-console/README.md) / [中文](./issue-259-windows-git-console/README.zh-CN.md) | Actual Windows before/after window observation, real WebUI, CLI and plugin composition / 真实 Windows 窗口前后观测、WebUI、CLI 与插件组合验证 | +| [OpenViking exact writes — issue #233](./issue-233-openviking-20260916/README.md) / [中文](./issue-233-openviking-20260916/README.zh-CN.md) | Controlled false-receipt reproduction, exact content writes and published OpenViking storage/search/delete / 受控错误回执复现、精确正文写入与正式 OpenViking 存储/检索/删除 | +| [Runtime archive write authority — issue #250](./issue-250-runtime-write-scope/README.md) / [中文](./issue-250-runtime-write-scope/README.zh-CN.md) | Same-View Native space creation, baseline next-turn recovery, fixed first-turn success and byte-exact CLI readback / 同 View 创建 Native 空间、基线下一回合恢复、修复后首回合成功与 CLI 逐字节核验 | +| [v0.5.8 release](./release-v0.5.8/README.md) / [中文](./release-v0.5.8/README.zh-CN.md) | Versioned package validation, Runtime persistence and real Native CLI / 版本化制品验证、Runtime 持久化与真实 Native CLI | +| [ORCA LINK sidebar — issue #232](./issue-232-orca-sidebar/README.md) | Real skin reproduction, visible/clickable entry, Runtime and Native CLI / 真实皮肤复现、侧栏入口、热记忆与 Native CLI | +| [Existing fixes — issues #225 and #230](./issues-225-230-20260911/README.md) / [中文](./issues-225-230-20260911/README.zh-CN.md) | Fresh strict-migration recovery, real CLI/WebUI and composed-strategy verification / 严格迁移恢复、真实 CLI/WebUI 与组合策略复验 | +| [Idle review tool composition — issue #211](./issue-211-20260911/README.md) / [中文](./issue-211-20260911/README.zh-CN.md) | Inherited overview reuse, own-scope execution guard, real Code Mode, CLI and WebUI / 继承概览复用、作用域执行限制、真实 Code Mode、CLI 与 WebUI | +| [v0.5.7 release](./release-v0.5.7/README.md) / [中文](./release-v0.5.7/README.zh-CN.md) | Versioned package checks and real DSH 0.1.5 archival / 版本化制品验证与真实 DSH 0.1.5 归档 | +| [DSH 0.1.5 compatibility — issue #223](./issue-223-dsh-015/README.md) / [中文](./issue-223-dsh-015/README.zh-CN.md) | HTTP 405, legacy Session copy recovery, real WebUI and restart / HTTP 405、旧会话副本修复、真实 WebUI 与重启 | +| [Documents archive — issue #222](./issue-222-document-archive/README.md) | Host-owned indexing and lineage, compensation, real WebUI and cross-turn tools / Host 索引与回执、补偿清理、真实 WebUI 与跨回合工具 | +| [Built-in centralized workspaces — issue #189](./issue-189-core-workspaces/README.md) / [中文](./issue-189-core-workspaces/README.zh-CN.md) | Core/Host storage scope, Source isolation, real WebUI and restart / 内置存储范围、Source 隔离、真实 WebUI 与重启 | +| [Automatic memory quality — 2026-09-09](./runtime-memory-quality-flash-20260909/README.md) / [中文](./runtime-memory-quality-flash-20260909/README.zh-CN.md) | Four concurrent developer sessions with real Flash; current facts, corrections, clutter and fresh recall / 四会话自动记忆质量验收 | +| [Runtime capacity — 2026-09-09](./runtime-capacity-flash-20260909/README.md) / [中文](./runtime-capacity-flash-20260909/README.zh-CN.md) | Real Flash, exact archival, concurrent and session-free writes / 真实 Flash 归档与并发写入 | +| [Creation-time ordering](./issue-202-created-order/README.md) | Issue #202 Runtime/Documents ordering, real WebUI writes and retained screenshots | +| [Electron npm CLI — issue #201](./issue-201-electron-cli/README.md) | Real Electron and Node Hosts, npm/native CLI paths, memory operations and version/update evidence | +| [Documentation refresh](./documentation-refresh/README.md) | Historical v0.5.2 bilingual media and an explicitly failed 390px resize check | +| [Architecture cleanup](./architecture-cleanup/README.md) | Public package boundaries, UI and independent artifact verification | +| [DSH rc.1 compatibility](./dsh-rc1-compat/README.md) | Published Host, Sidebar and Builtin | +| [Main rebase — 2026-09-04](./main-rebase-20260904/README.md) | v0.4.7 upgrade, v0.5 composition and Registry evidence | +| [Main rebase — 2026-09-03](./main-rebase-20260903/README.md) | Earlier default-Strategy integration baseline | +| [Main rebase — 2026-08-31](./main-rebase-20260831/README.md) | Earlier architecture transition | +| [Strategy extensions](./strategy-extensions-20260901/README.md) | Three additive contributions and independent packages | +| [View experiment](./view-20260901/README.md) | Retired View-tab experiment; not a shipped v0.5 page / 已撤销的 View 页实验 | +| [Sidebar composition](./better-sidebar-153/README.md) | Better Sidebar and Source-page mounting | +| [Builtin regression](./builtin-139/README.md) | Entry spelling, scope and existing data | +| [Session events](./session-events-156/README.md) | Projection lifecycle and retained sessions | +| [Dialog surfaces](./dialog-surfaces/README.md) | Responsive dialogs and theme integration | +| [Sidebar icon](./sidebar-icon/README.md) | Entry icon alignment | +| [npm Sidebar / CLI — 2026-08-30](./npm-sidebar-cli/README.md) | Historical npm control and fault-injection results | + +Image-only files also have references from GitHub PR bodies. A missing repository-local link is not sufficient evidence to delete them. Current Light product media is indexed in [the asset directory](../assets/README.md); keep historical paths stable for external readers. + +部分独立图片由 GitHub PR 正文引用,不能仅凭仓库内无引用就删除。当前浅色产品素材见[素材目录](../assets/README.md),历史资源路径保留供外部引用。 diff --git a/dsh/docs/pr-assets/algorithm-performance-20261003/README.md b/dsh/docs/pr-assets/algorithm-performance-20261003/README.md new file mode 100644 index 000000000..f0158b1d3 --- /dev/null +++ b/dsh/docs/pr-assets/algorithm-performance-20261003/README.md @@ -0,0 +1,112 @@ +# Algorithm performance audit — 2026-10-03 + +[简体中文](./README.zh-CN.md) | [Development guide](../../en/development/README.md) + +This change removes repeated work in local compaction, retrieval, namespace discovery and batch preparation. Storage formats, selection rules, permission checks, result order and Provider call ordering are preserved. It does not claim a globally optimal implementation or measure a remote database's internal search algorithm. + +## Reproduction and measurement + +Baseline: `d13d9c1ab118e01926c0e60fbf273de8457872df` (main, v0.5.22). Optimized source revision and aggregate measurements are recorded in [metrics.json](./metrics.json). Both checkouts use the same benchmark source and frozen dependency lockfile, run sequentially on the same machine. The primary developer checkout is untouched. + +```sh +pnpm install --frozen-lockfile +MNEMON_BENCH_OUTPUT=/tmp/algorithm-benchmark.json pnpm bench:algorithms +# Optional: only the requested small sizes +MNEMON_BENCH_SIZES=1,10,100 pnpm bench:algorithms +``` + +To reproduce the baseline, copy `tests/algorithm-performance.measure.ts` and `scripts/benchmark-algorithms.config.ts` into a disposable checkout at the baseline revision, then run `pnpm exec vitest run --config scripts/benchmark-algorithms.config.ts`. Baseline lacks the new package script. + +The harness creates synthetic data in temporary directories and removes it afterward. No production data, paid APIs, LLM, real network or external CLI is involved. Each case has three warm-up operations followed by nine samples. A sample averages ten operations at N=1/10/100 or three at N=1000 (one for the expensive pinned-recall and batch-prepare cases). The event loop yields between samples, outside timing. Setup/reset, dependency installation and assertions are outside timing; full local method execution, including its filesystem I/O, is inside timing. Results include median and p95 **of sample means**, not individual-request p95. OS page caches are warm, GC is not forced, and these are single-process latency measurements rather than concurrent throughput measurements. + +N varies independently by case: + +- Runtime: N committed entries plus a sentinel; compaction removes the sentinel and packs N mixed-priority entries. The configured test capacity is 1 MiB per target. +- Documents: N approximately 4 KiB bodies, limit 10, with a 16 MiB test capacity. Search includes its normal last-access metadata write; catalog includes filesystem health checks. +- Holographic: N JSON facts, deterministic trust scores, fixed queries and output limits (10 for search, 20 for related). Writes include full JSON serialization and atomic replacement. +- Discovery: N Provider namespaces; native catalog: N real directories with synthetic database marker files. Provider discovery uses an in-memory registry to isolate reconciliation from persistence. +- Recall: N active spaces with 30 fixed rows each, limit 10, one normalized-score Provider type. `pinned` requests every space explicitly. The real service and registry execute, while the adapter returns local fixtures. This measures orchestration, not remote latency or retrieval quality. +- Batch preparation: N requests to one destination in an N-space registry, with an ordered synthetic batch receipt. `batch-grouping` replaces only `get()` with a constant fixture to separate grouping from directory projection. Provider writes/checkpoint persistence are excluded from these two cases. +- Core: the real three-Source composition with N short Runtime entries; Source count remains three. It is a control case, not a claim about scaling to N Sources. + +N=1/10/100 can be dominated by constant overhead. N=1000 and deterministic work counters help distinguish that overhead from repeated scans. Timings do not prove Big-O; the bounds below follow from the code's loops and data structures. + +## Results + +Environment: Apple M4, darwin/arm64, Node v25.1.0, pnpm 11.25.0. Units: milliseconds per operation, **before → after**, medians. There are 68 cases per revision. Small differences in unchanged control cases reflect run variability. + +| Case | N=1 | N=10 | N=100 | N=1000 | +|---|---:|---:|---:|---:| +| `runtime.snapshot` | 0.023 → 0.019 | 0.033 → 0.033 | 0.167 → 0.174 | 1.749 → 1.618 | +| `runtime.projection` | 0.102 → 0.103 | 0.123 → 0.119 | 0.306 → 0.311 | 2.735 → 2.456 | +| `runtime.compact` | 0.496 → 0.514 | 0.510 → 0.505 | 1.114 → 0.796 | 41.347 → 4.047 | +| `runtime.write` | 0.611 → 0.462 | 0.479 → 0.484 | 0.642 → 0.645 | 2.982 → 2.570 | +| `documents.catalog` | 0.078 → 0.081 | 0.096 → 0.107 | 0.396 → 0.393 | 4.126 → 3.358 | +| `documents.search` | 0.268 → 0.270 | 0.573 → 0.580 | 3.923 → 2.322 | 40.484 → 22.165 | +| `documents.write` | 0.434 → 0.418 | 0.485 → 0.481 | 1.159 → 1.074 | 9.252 → 7.709 | +| `holographic.search` | 0.021 → 0.019 | 0.055 → 0.054 | 0.405 → 0.376 | 4.158 → 3.577 | +| `holographic.related` | 0.017 → 0.016 | 0.048 → 0.046 | 0.343 → 0.306 | 3.746 → 3.044 | +| `holographic.write` | 0.177 → 0.164 | 0.190 → 0.185 | 0.334 → 0.341 | 2.902 → 2.063 | +| `registry.discovery` | 0.013 → 0.013 | 0.032 → 0.033 | 0.226 → 0.194 | 5.311 → 1.820 | +| `registry.native-catalog` | 0.029 → 0.028 | 0.062 → 0.064 | 0.386 → 0.354 | 7.038 → 2.925 | +| `recall.federated` | 0.018 → 0.017 | 0.128 → 0.100 | 3.045 → 0.970 | 254.008 → 9.834 | +| `recall.pinned` | 0.019 → 0.016 | 0.384 → 0.093 | 21.860 → 0.902 | 2154.052 → 9.400 | +| `storage.batch-prepare` | 0.006 → 0.007 | 0.263 → 0.035 | 18.045 → 0.223 | 1825.665 → 2.210 | +| `storage.batch-grouping` | 0.002 → 0.002 | 0.009 → 0.006 | 0.073 → 0.040 | 1.189 → 0.295 | +| `core.compose` | 11.218 → 10.682 | 11.039 → 11.239 | 12.312 → 11.634 | 11.440 → 12.355 | + +Counted UTF-8 input characters during one full Runtime compaction (instrumented separately from timings): + +| N | Before | After | +|---|---:|---:| +| 1 | 252 | 255 | +| 10 | 5,139 | 2,397 | +| 100 | 330,399 | 24,177 | +| 1000 | 31,641,849 | 245,577 | + +From N=100 to 1000, counted work grows approximately 96× before and 10× after. The end-to-end method still performs linear JSON validation and writes. + + +## Complexity and decisions + +Use N for records, B for their total text/serialized bytes, Q for query tokens, M for matching candidates, k for returned rows, S for configured spaces, P for requested spaces, C for total fetched candidates, W for batch writes and U for distinct write destinations. Map/Set lookup bounds are expected/amortized; string hashing and filesystem/Provider costs are additional. Metadata fields, branch count and most configured output limits are bounded. + +| Path | Before | After | Reason and retained behavior | +|---|---|---|---| +| Runtime compaction | O(N²L) for N entries averaging L characters, plus JSON I/O | O(N + B) | Index original content once, preserve its first occurrence; use three stable priority buckets and incremental UTF-8/delimiter accounting. Return retained entries in original order. | +| Runtime snapshot, projection, add/replace/remove | O(N + B) per operation | Same bound | Complete JSON validation, exact matching, projection repair, capacity checks and commit-marker writes remain necessary. A sequence of N individual growing-store writes still costs O(N²) in total. | +| Document search | O(QB + M log M), plus index I/O; excerpt processing over every eligible body | Same main bound; excerpt work only over the returned bodies | Keep live body reads, substring scoring, token coverage, stable score/date ties, allowed IDs and last-access writes. No stale text index is introduced. | +| Document metadata/read/write | Catalog O(N) metadata and stats; get O(N + selected body bytes); write O(index bytes + body bytes + N stats), plus uncached excerpt bytes | Same bound | Existing disk-identity caches, locking, revisions and complete index persistence are retained. Sorting for archival candidates remains O(N log N). | +| Provider namespace reconciliation | O(SD) matching D discovered namespaces against S existing entries | O(S + D) matching | Request-local lookup preserves existing IDs, user/AI metadata, activation, timestamps and first-match behavior. Field normalization and registry serialization still process their bytes. | +| Native directory reconciliation | O(DS) ID comparisons for D disk entries | O(S + D) membership work | Still enumerate and check database existence every time; use a Set only within that scan. | +| Pinned space resolution | P full catalog projections: O(PS), excluding the old directory nested scan | One catalog and P lookups: O(S + P) | Same ID validation, active/enabled checks, deduplication and caller order. Rebuild on every operation. | +| Federated ranking + quality | O(C log C + PC) | O(C log C + C + P) | Replace per-space filtering of all evaluated/selected candidates with one aggregate pass. Preserve rank fusion, every counter, fallback policy, failure status and all policy inputs. C is normally at most 50P for conforming adapters. | +| Batch grouping/preparation | O(W²) array copying/membership plus W destination projections | O(W) grouping plus U destination resolutions | Preserve synchronous validation before any writes, destination order, Native large-entry fallback, receipt order, partial-failure behavior and per-destination activation. Total preparation still includes O(U(S + D)) directory work; committed providers add their own I/O. | +| Holographic overlap | O(a + b) work and temporary union allocation for token sets of sizes a,b | O(min(a,b)) membership work, constant auxiliary space | Use the exact Jaccard union cardinality a+b−intersection. Tokenization remains proportional to text size. | +| Holographic search/related ranking | O(M log M) full sorting | O(M log k + k log k), O(k) ranking auxiliary space | Worst-retained heap, then stable output sort. Small collections use native sort. Non-finite legacy scores retain the old full-sort path; worst-case legacy complexity therefore remains O(M log M). Candidate collection and JSON parsing still occupy O(N+B) space. | +| Holographic list/graph/status/write | Whole-store read/write O(N+B); list O(N log N); entity aggregation plus O(E log E) entity sorting | Same bound | Preserve whole-file refresh, exact deduplication, timestamps, file mode, graph identity and full persistence. Search/related improvements do not turn the JSON file into an indexed database. | +| Core/Strategies | Source facts/projections run concurrently; JSON processing traverses bytes and sorts object keys; strategy-specific source selection also contributes | Unchanged | Preserve deterministic replay, immutable Views, digests, budgets, grants, timeouts and cancellation. Default Layered composition admits three roles; arbitrary custom Strategies have their own bounds. Some generic validation/extension membership scans remain quadratic in small, bounded declaration lists. | +| Graph/list/entity federation | Graph projection O(V+E); list filtering O(returned bytes); entity merge O(E + H log H) for H unique entities, plus space resolution/Providers | Same data-processing bounds; pinned resolution improved | Keep all edges and existing namespace IDs, ordering, totals, per-source diagnostics and filters. | +| Native CLI adapter | Response normalization O(response bytes); exact batch archive O(existing + requested bytes) with Maps | Unchanged | Actual SQLite/vector/graph search complexity belongs to the external Mnemon version. Preserve readonly inspection, exact import verification, output caps and serialized process behavior. | +| Remote Provider adapters | Local decoding O(response bytes); network/service work external | Unchanged | Honcho, Mem0, Hindsight, RetainDB, ByteRover and Supermemory do not expose their server algorithms here. OpenViking additionally hydrates up to k records concurrently. Hindsight related traverses edges per depth: O(depth·E + V), with service depth capped at five. | +| Pack import/export | O(payload bytes) validation/hashing/copying plus file-name sorting | Unchanged | Transaction staging, checksums, authority checks and rollback are correctness work; no persistence format or migration change is made. | + +Full global candidate sorting remains intentional: custom quality policies receive the complete sorted array and can make arbitrary selections. Truncating before policy evaluation would change results. Durable JSON reads/writes and exact arbitrary-substring matching cannot be made constant-time by replacing a loop alone. Persistent indexes, stale cross-request caches, ANN substitution, cancellation changes and altered concurrency ceilings would need separate compatibility/quality evidence. + +## Compatibility and validation + +- Full `pnpm run verify` passed under the CI toolchain, Node 22.19.0 + pnpm 10.13.1: 452 independent-plugin tests and 1,611 Root/Host/Core/UI tests passed; 3 plugin and 6 root optional tests skipped. This includes docs/type checks, deterministic double build, all standalone builds, real isolated Headless activation, package contents/public entries, publint and attw. +- `pnpm run release:intent` confirms changeset coverage for Root and all four changed plugins. +- Both sequential benchmark runs passed: 68 case/size combinations each. [metrics.json](./metrics.json) includes source revisions, workload parameters, median/p95 sample means, counters and identical harness SHA-256 values. +- The baseline run of the new compaction/service regression files has exactly five expected failures (linear-byte-work, two quality-stat sizes, pinned catalog count and destination resolution count); the optimized run passes all 12 tests. Additional ranking and Document/Holographic regressions pass. +- `pnpm run verify:plugins` passed: 17 independent plugin repositories, 18 packed artifacts, no workspace links, external public-SDK/Client consumer typecheck/build/tests, real DSH packed-Starter activation and all three optional packed Strategy plugins activated together. + +The initial system-toolchain attempt exposed two baseline environment problems: pnpm 11.25 rejects this repository's intentional workspace task cycles, and Node 25.1.0 on this Mac fails an existing Native CLI symlink-removal fixture. The latter also fails on untouched main. The same nine CLI fixture tests pass on Node 22.19.0; complete verification uses that CI version. No source/test workaround for these environment issues is included. Benchmarks on both revisions use the same Node 25.1.0 environment, independently of the subsequent compatibility verification. + +Skipped cases require external Native/OpenViking/Flash services or a Windows integration environment. Local tests do not substitute for those live integrations; GitHub CI owns the Windows and Node 20/24 jobs. + +The targeted regressions cover exact UTF-8 boundaries (including supplementary characters and unpaired surrogates), priorities, stable ties, empty/full budgets, preserved timestamps/branch scope, duplicate legacy content, live Document edits and missing files, read-grant membership, disabled/unknown spaces, every quality counter, Provider failures, ordered batch receipts and validation before side effects. Heap results are compared with stable full sorting across ascending, descending, tied, mixed and non-finite scores. + +Deterministic work tests bound Runtime counted characters by input size and recall-stat visits by C+k, so they do not depend on CPU speed. These tests are also run against the baseline to demonstrate that the old quadratic paths violate the bounds. + +No schema, configuration, locale/UI flow, RPC DTO, package dependency version, external request payload, persistent cache, model policy or paid-service configuration changes. Existing on-disk data needs no migration; rollback remains compatible. Results validate the tested workloads and contracts, not an absolute guarantee against every production failure or a prediction of end-to-end LLM/network latency. diff --git a/dsh/docs/pr-assets/algorithm-performance-20261003/README.zh-CN.md b/dsh/docs/pr-assets/algorithm-performance-20261003/README.zh-CN.md new file mode 100644 index 000000000..e1ddb5629 --- /dev/null +++ b/dsh/docs/pr-assets/algorithm-performance-20261003/README.zh-CN.md @@ -0,0 +1,112 @@ +# 算法性能审计 — 2026-10-03 + +**简体中文** | [English](./README.md) | [开发指南](../../zh-CN/development/README.md) + +本次消除本地压缩、检索、空间发现和批量写入准备中的重复工作,保留存储格式、选择规则、权限检查、结果顺序与 Provider 调用顺序。结论限于所审计的路径,不宣称整个系统达到全局最优,也不把客户端测试当作远程数据库内部检索算法的证明。 + +## 复现与测量方法 + +基线为 `d13d9c1ab118e01926c0e60fbf273de8457872df`(main,v0.5.22)。优化版源码 revision 与汇总指标见 [metrics.json](./metrics.json)。两份独立 checkout 使用完全相同的基准源码与冻结依赖锁文件,在同一机器顺序运行。原开发工作区没有改动。 + +```sh +pnpm install --frozen-lockfile +MNEMON_BENCH_OUTPUT=/tmp/algorithm-benchmark.json pnpm bench:algorithms +# 仅测试要求的三个小规模 +MNEMON_BENCH_SIZES=1,10,100 pnpm bench:algorithms +``` + +复现旧版时,将 `tests/algorithm-performance.measure.ts` 与 `scripts/benchmark-algorithms.config.ts` 复制到该基线 revision 的临时 checkout,再执行 `pnpm exec vitest run --config scripts/benchmark-algorithms.config.ts`。旧版没有新增的 package script。 + +脚本在临时目录生成合成数据,结束后清理,不使用生产数据、付费 API、LLM、真实网络或外部 CLI。每项先预热三次,再取九组样本;N=1/10/100 每组平均十次操作,N=1000 每组平均三次(成本较高的 pinned-recall 与 batch-prepare 每组一次)。样本之间让出事件循环,等待不计入耗时。准备/恢复数据、安装依赖和断言在计时外,完整本地方法调用(含其文件 I/O)在计时内。报告中的 median/p95 是**样本均值的分位数**,不是单次请求的 p95。OS 文件缓存已预热,没有强制 GC;这是单进程延迟测试,不是并发吞吐测试。 + +各项 N 含义分别为: + +- Runtime:N 条已提交记忆加一条哨兵;压缩时移除哨兵,按混合优先级装填 N 条候选。测试容量为每个目标 1 MiB。 +- Documents:N 份约 4 KiB 的正文,返回上限 10,测试容量 16 MiB。搜索包含正常的最近访问时间持久化,catalog 包含文件健康检查。 +- Holographic:N 条 JSON 事实,固定信任分数生成规则和查询;search 返回 10 条,related 返回 20 条。写入包含全量 JSON 序列化和原子替换。 +- Provider 发现:N 个命名空间;Native catalog:N 个真实目录和合成数据库标记文件。Provider 发现使用内存 registry,以隔离匹配成本和持久化成本。 +- 多路召回:N 个活跃空间,每路固定 30 条候选,最终上限 10,使用同一种标准化分数 Provider。`pinned` 显式指定全部空间。运行真实 service/registry,adapter 返回本地夹具,因此衡量编排成本,不衡量远程延迟或召回质量。 +- 批量准备:N 空间目录中向一个目标提交 N 条请求,adapter 返回有序合成回执。`batch-grouping` 仅把 `get()` 替换为常量夹具,分离分组与目录投影成本。这两项不包含 Provider 持久化或 checkpoint 落盘。 +- Core:真实三 Source 组合中放入 N 条短 Runtime 记忆;Source 数量始终是三,是对照项,不代表 N 个 Source 的扩展性。 + +N=1/10/100 容易受固定开销影响,因此补充 N=1000 和确定性的工作量计数。耗时本身不能证明 Big-O;下面的复杂度来自代码循环与数据结构分析。 + +## 实测结果 + +环境:Apple M4,darwin/arm64,Node v25.1.0,pnpm 11.25.0。单位为每次操作毫秒,中位数,**优化前 → 优化后**;每版共 68 个测试组合。未修改对照项的小幅差异反映运行波动。 + +| 项目 | N=1 | N=10 | N=100 | N=1000 | +|---|---:|---:|---:|---:| +| `runtime.snapshot` | 0.023 → 0.019 | 0.033 → 0.033 | 0.167 → 0.174 | 1.749 → 1.618 | +| `runtime.projection` | 0.102 → 0.103 | 0.123 → 0.119 | 0.306 → 0.311 | 2.735 → 2.456 | +| `runtime.compact` | 0.496 → 0.514 | 0.510 → 0.505 | 1.114 → 0.796 | 41.347 → 4.047 | +| `runtime.write` | 0.611 → 0.462 | 0.479 → 0.484 | 0.642 → 0.645 | 2.982 → 2.570 | +| `documents.catalog` | 0.078 → 0.081 | 0.096 → 0.107 | 0.396 → 0.393 | 4.126 → 3.358 | +| `documents.search` | 0.268 → 0.270 | 0.573 → 0.580 | 3.923 → 2.322 | 40.484 → 22.165 | +| `documents.write` | 0.434 → 0.418 | 0.485 → 0.481 | 1.159 → 1.074 | 9.252 → 7.709 | +| `holographic.search` | 0.021 → 0.019 | 0.055 → 0.054 | 0.405 → 0.376 | 4.158 → 3.577 | +| `holographic.related` | 0.017 → 0.016 | 0.048 → 0.046 | 0.343 → 0.306 | 3.746 → 3.044 | +| `holographic.write` | 0.177 → 0.164 | 0.190 → 0.185 | 0.334 → 0.341 | 2.902 → 2.063 | +| `registry.discovery` | 0.013 → 0.013 | 0.032 → 0.033 | 0.226 → 0.194 | 5.311 → 1.820 | +| `registry.native-catalog` | 0.029 → 0.028 | 0.062 → 0.064 | 0.386 → 0.354 | 7.038 → 2.925 | +| `recall.federated` | 0.018 → 0.017 | 0.128 → 0.100 | 3.045 → 0.970 | 254.008 → 9.834 | +| `recall.pinned` | 0.019 → 0.016 | 0.384 → 0.093 | 21.860 → 0.902 | 2154.052 → 9.400 | +| `storage.batch-prepare` | 0.006 → 0.007 | 0.263 → 0.035 | 18.045 → 0.223 | 1825.665 → 2.210 | +| `storage.batch-grouping` | 0.002 → 0.002 | 0.009 → 0.006 | 0.073 → 0.040 | 1.189 → 0.295 | +| `core.compose` | 11.218 → 10.682 | 11.039 → 11.239 | 12.312 → 11.634 | 11.440 → 12.355 | + +一次完整 Runtime 压缩中传入 UTF-8 字节计数的字符总量(在计时之外单独插桩): + +| N | 优化前 | 优化后 | +|---|---:|---:| +| 1 | 252 | 255 | +| 10 | 5,139 | 2,397 | +| 100 | 330,399 | 24,177 | +| 1000 | 31,641,849 | 245,577 | + +N 从 100 增至 1000 时,计数字符工作量旧版约增长 96 倍,优化版约增长 10 倍;完整方法仍包含线性 JSON 校验与写入。 + + +## 复杂度与处理决策 + +符号:N 为记录数,B 为全部文本/序列化字节数,Q 为查询 token 数,M 为匹配候选数,k 为返回条数,S 为配置空间数,P 为本次请求空间数,C 为全部召回候选数,W 为批量写入数,U 为不同写入目标数。Map/Set 查找采用期望/摊还复杂度;字符串散列、文件系统与 Provider 成本另外计入。元数据字段、分支数量及多数输出上限有配置边界。 + +| 路径 | 优化前 | 优化后 | 原因与保留语义 | +|---|---|---|---| +| Runtime 压缩 | 平均文本长度 L 时 O(N²L),另加 JSON I/O | O(N+B) | 原内容建立一次索引并保留首个匹配;三个稳定优先级桶,增量计算 UTF-8 与分隔符字节。最终仍按输入顺序返回保留条目。 | +| Runtime 快照、投影、增改删 | 每次 O(N+B) | 同阶 | 保留完整 JSON 校验、精确匹配、投影修复、容量检查与提交标记。对持续增长的存储执行 N 次单条写入,累计仍为 O(N²)。 | +| Document 搜索 | O(QB+M log M),另加 index I/O;所有可搜索正文都生成摘要 | 主复杂度同阶,摘要处理只作用于返回正文 | 保留实时正文读取、子串评分、token 覆盖、分数/日期稳定排序、allowed IDs 与最近访问时间写入,不引入可能过期的文本索引。 | +| Document 元数据、读取、写入 | Catalog 为 O(N) 元数据与 stat;get 为 O(N+目标正文字节);写入含 index、正文、N 次 stat 与未缓存摘要;归档候选排序 O(N log N) | 同阶 | 保留磁盘身份缓存、锁、revision、完整 index 持久化和有界摘要缓存。 | +| Provider 空间发现匹配 | D 个发现项匹配 S 条现有记录:O(SD) | O(S+D) | 请求内索引保留原 ID、人工/AI 元数据、激活状态、时间戳与首个匹配语义;字段规范化和 registry 序列化仍需处理各自字节。 | +| Native 目录发现 | D 个目录与 S 个 ID 比较:O(DS) | O(S+D) | 每次仍枚举目录并检查数据库存在,只在当次扫描中使用 Set。 | +| 固定范围空间解析 | 重复 P 次目录投影:O(PS),未计旧版目录嵌套扫描 | 一次目录加 P 次查找:O(S+P) | 保留 ID 校验、活跃/启用检查、去重与请求顺序,每次操作重新读取。 | +| 多路排名与质量统计 | O(C log C+PC) | O(C log C+C+P) | 一次汇总全部 evaluated/selected,取代按空间反复过滤。融合排名、每个计数器、降级策略、故障状态和传入策略的所有候选不变。遵守上限的 adapter 通常满足 C≤50P。 | +| 批量写入分组与准备 | O(W²) 数组复制/成员检查,加 W 次目标投影 | O(W) 分组,加 U 次目标解析 | 保留全部写入前的同步校验、目标顺序、Native 长条目单写回退、回执顺序、部分失败和逐目标激活。准备仍含 O(U(S+D)) 目录成本,成功持久化另计 Provider I/O。 | +| Holographic token 重叠 | 集合大小 a、b 时 O(a+b),并分配临时并集 | O(min(a,b)) 成员检查、常量辅助空间 | 使用精确 Jaccard 并集大小 a+b−交集;分词仍与文本长度有关。 | +| Holographic search/related 排名 | 全排序 O(M log M) | O(M log k+k log k),排名辅助空间 O(k) | 堆顶保存当前最差入选项,最后稳定排序。小集合仍用原生 sort;旧数据出现非有限分数时保留旧全排序,因此该兼容分支仍为 O(M log M)。候选集合和 JSON 解析仍占 O(N+B) 空间。 | +| Holographic list/graph/status/write | 全量读写 O(N+B);list 排序 O(N log N);实体汇总另加 O(E log E) 排序 | 同阶 | 保留每次读文件、精确去重、时间戳、文件模式、图身份和全量持久化;search/related 优化没有把 JSON 变成索引数据库。 | +| Core 与 Strategy | Source facts/project 并发;JSON 处理遍历字节、排序对象键;另计策略选择成本 | 保留 | 保留确定性重放、不可变 View、digest、预算、授权、超时和取消。默认 Layered 使用三个角色;自定义策略有各自复杂度。一些声明/扩展成员检查对较小的有界列表仍为平方级。 | +| 图谱、列表、实体联邦查询 | 图投影 O(V+E);列表过滤与返回字节有关;实体汇总 O(E+H log H),H 为不同实体数;另加空间解析/Provider | 数据处理同阶,固定范围空间解析改善 | 保留全部边、命名空间 ID、顺序、total、分来源诊断与过滤。 | +| Native CLI adapter | 返回规范化 O(响应字节);精确批量归档通过 Map 为 O(已有+请求字节) | 保留 | SQLite/向量/图内部搜索属于外部 Mnemon 版本。保留只读检查、精确导入确认、输出上限和进程序列化。 | +| 远程 Provider adapter | 本地解析 O(响应字节),网络/服务算法在外部 | 保留 | Honcho、Mem0、Hindsight、RetainDB、ByteRover、Supermemory 的服务端算法不在本仓库。OpenViking 额外并发读取最多 k 条正文;Hindsight related 逐层扫边 O(depth·E+V),service 将 depth 限为五。 | +| Pack 导入导出 | 校验、散列、复制与 payload 字节有关,另加文件名排序 | 保留 | 事务 staging、校验和、权限检查和回滚是正确性工作;没有更改格式或迁移方式。 | + +联邦召回保留完整全局候选排序,因为自定义质量策略接收完整有序数组、可以任意选择;提前截断会改变结果。完整 JSON 持久化和任意子串匹配的成本也不能靠换一个循环变为常数。持久索引、跨请求陈旧缓存、近似向量替代、取消策略和并发上限变更需要独立的兼容性/质量证据。 + +## 兼容性与验证 + +- 使用 CI 工具版本 Node 22.19.0 + pnpm 10.13.1 完整通过 `pnpm run verify`:独立插件 452 项、Root/Host/Core/UI 1611 项测试通过;插件 3 项、根目录 6 项可选测试跳过。包含文档/类型检查、确定性双构建、全部独立构建、真实隔离 Headless 激活、包内容/公共入口、publint 与 attw。 +- `pnpm run release:intent` 确认根包与四个修改的插件均有 changeset。 +- 前后顺序基准均通过,各 68 个规模/项目组合。[metrics.json](./metrics.json) 保存源码 revision、负载参数、中位数/p95 样本均值、工作量计数和一致的基准文件 SHA-256。 +- 新增压缩/service 回归在基线恰有五项预期失败(线性字节工作量、两种规模的统计访问量、固定范围目录次数、目标解析次数),优化版 12 项全部通过。其他排名、Document/Holographic 回归也通过。 +- `pnpm run verify:plugins` 通过:17 个独立插件仓库、18 个发布包、无 workspace 链接、外部公共 SDK/Client 使用方的类型/构建/测试、真实 DSH packed Starter 安装激活,以及三个可选打包 Strategy 插件同时启用。 + +首次使用系统工具验证时发现两个既有环境问题:pnpm 11.25 拒绝仓库有意存在的 workspace 任务环;本机 Node 25.1.0 的旧 Native CLI 符号链接删除夹具失败,未修改 main 也有同样错误。该 CLI 文件的九项测试在 Node 22.19.0 全部通过,完整验证使用 CI 的这个版本,没有为这些环境问题修改源码或测试。两版性能基准均使用相同的 Node 25.1.0 环境,与之后的兼容性验证分开记录。 + +跳过项需要真实 Native/OpenViking/Flash 服务或 Windows 集成环境;本地测试不替代这些真实集成。Windows 与 Node 20/24 由 GitHub CI 验证。 + +定向回归覆盖 UTF-8 精确边界(含补充平面字符和孤立代理项)、优先级、稳定同分排序、空/满预算、时间戳与分支继承、历史重复内容、Document 外部修改和文件丢失、读取范围、停用/未知空间、全部质量计数器、Provider 故障、有序批量回执与副作用前校验。堆排名与原稳定全排序比较升序、降序、同分、混合和非有限分数。 + +确定性工作量测试把 Runtime 被计数字符限制在线性输入规模内,把召回统计访问次数限制为 C+k,不依赖 CPU 速度。将这些测试复制到基线运行,验证旧平方级路径确实违反上界。 + +没有更改 schema、配置、文案/UI 流程、RPC DTO、依赖版本、外部请求 payload、持久缓存、模型策略或付费服务配置。已有数据不需要迁移,回滚仍兼容。上述证据只覆盖已测工作负载与契约,不是对所有生产环境绝无故障的保证,也不预测 LLM/网络端到端延迟。 diff --git a/dsh/docs/pr-assets/algorithm-performance-20261003/metrics.json b/dsh/docs/pr-assets/algorithm-performance-20261003/metrics.json new file mode 100644 index 000000000..9f9c60729 --- /dev/null +++ b/dsh/docs/pr-assets/algorithm-performance-20261003/metrics.json @@ -0,0 +1,776 @@ +{ + "baseline": { + "schemaVersion": 1, + "date": "2026-10-03", + "revision": "d13d9c1ab118e01926c0e60fbf273de8457872df", + "node": "v25.1.0", + "platform": "darwin", + "arch": "arm64", + "cpu": "Apple M4", + "sizes": [ + 1, + 10, + 100, + 1000 + ], + "samples": 9, + "warmups": 3 + }, + "optimized": { + "schemaVersion": 1, + "date": "2026-10-03", + "revision": "b6cf5ca7e7cbe58da9bc4158287818b8d94948d4", + "node": "v25.1.0", + "platform": "darwin", + "arch": "arm64", + "cpu": "Apple M4", + "sizes": [ + 1, + 10, + 100, + 1000 + ], + "samples": 9, + "warmups": 3 + }, + "harnessSha256": { + "tests/algorithm-performance.measure.ts": "3e6ceb962c256743a1e5c2e16801c7f58a88acd4bc6e00280bf8ec61bab8f4d6", + "scripts/benchmark-algorithms.config.ts": "ce608482993d7be06ae605567d50a791cd16ea828b743f02af27a8f366020629" + }, + "measurements": [ + { + "name": "runtime.snapshot", + "n": 1, + "iterationsPerSample": 10, + "beforeMedianMs": 0.02325, + "afterMedianMs": 0.019321, + "beforeP95SampleMeanMs": 0.034046, + "afterP95SampleMeanMs": 0.032563, + "medianRatioBeforeAfter": 1.203 + }, + { + "name": "runtime.projection", + "n": 1, + "iterationsPerSample": 10, + "beforeMedianMs": 0.101875, + "afterMedianMs": 0.103267, + "beforeP95SampleMeanMs": 0.138346, + "afterP95SampleMeanMs": 0.125342, + "medianRatioBeforeAfter": 0.987 + }, + { + "name": "runtime.compact", + "n": 1, + "iterationsPerSample": 10, + "beforeMedianMs": 0.496338, + "afterMedianMs": 0.513583, + "beforeP95SampleMeanMs": 0.608521, + "afterP95SampleMeanMs": 0.548137, + "medianRatioBeforeAfter": 0.966 + }, + { + "name": "runtime.write", + "n": 1, + "iterationsPerSample": 10, + "beforeMedianMs": 0.6108, + "afterMedianMs": 0.461521, + "beforeP95SampleMeanMs": 0.858496, + "afterP95SampleMeanMs": 0.565096, + "medianRatioBeforeAfter": 1.323 + }, + { + "name": "documents.catalog", + "n": 1, + "iterationsPerSample": 10, + "beforeMedianMs": 0.078096, + "afterMedianMs": 0.0811, + "beforeP95SampleMeanMs": 0.096609, + "afterP95SampleMeanMs": 0.09265, + "medianRatioBeforeAfter": 0.963 + }, + { + "name": "documents.search", + "n": 1, + "iterationsPerSample": 10, + "beforeMedianMs": 0.268271, + "afterMedianMs": 0.269992, + "beforeP95SampleMeanMs": 0.539963, + "afterP95SampleMeanMs": 0.568708, + "medianRatioBeforeAfter": 0.994 + }, + { + "name": "documents.write", + "n": 1, + "iterationsPerSample": 10, + "beforeMedianMs": 0.434196, + "afterMedianMs": 0.417771, + "beforeP95SampleMeanMs": 0.478329, + "afterP95SampleMeanMs": 0.516087, + "medianRatioBeforeAfter": 1.039 + }, + { + "name": "holographic.search", + "n": 1, + "iterationsPerSample": 10, + "beforeMedianMs": 0.021063, + "afterMedianMs": 0.0188, + "beforeP95SampleMeanMs": 0.422179, + "afterP95SampleMeanMs": 0.031704, + "medianRatioBeforeAfter": 1.12 + }, + { + "name": "holographic.related", + "n": 1, + "iterationsPerSample": 10, + "beforeMedianMs": 0.017, + "afterMedianMs": 0.016458, + "beforeP95SampleMeanMs": 0.033242, + "afterP95SampleMeanMs": 0.017487, + "medianRatioBeforeAfter": 1.033 + }, + { + "name": "holographic.write", + "n": 1, + "iterationsPerSample": 10, + "beforeMedianMs": 0.176842, + "afterMedianMs": 0.164333, + "beforeP95SampleMeanMs": 0.623808, + "afterP95SampleMeanMs": 0.176538, + "medianRatioBeforeAfter": 1.076 + }, + { + "name": "registry.discovery", + "n": 1, + "iterationsPerSample": 10, + "beforeMedianMs": 0.013396, + "afterMedianMs": 0.012546, + "beforeP95SampleMeanMs": 0.027346, + "afterP95SampleMeanMs": 0.026254, + "medianRatioBeforeAfter": 1.068 + }, + { + "name": "registry.native-catalog", + "n": 1, + "iterationsPerSample": 10, + "beforeMedianMs": 0.029108, + "afterMedianMs": 0.0275, + "beforeP95SampleMeanMs": 0.039733, + "afterP95SampleMeanMs": 0.044867, + "medianRatioBeforeAfter": 1.058 + }, + { + "name": "recall.federated", + "n": 1, + "iterationsPerSample": 10, + "beforeMedianMs": 0.018075, + "afterMedianMs": 0.016729, + "beforeP95SampleMeanMs": 0.038433, + "afterP95SampleMeanMs": 0.034667, + "medianRatioBeforeAfter": 1.08 + }, + { + "name": "recall.pinned", + "n": 1, + "iterationsPerSample": 10, + "beforeMedianMs": 0.019279, + "afterMedianMs": 0.016008, + "beforeP95SampleMeanMs": 0.023183, + "afterP95SampleMeanMs": 0.018713, + "medianRatioBeforeAfter": 1.204 + }, + { + "name": "storage.batch-prepare", + "n": 1, + "iterationsPerSample": 10, + "beforeMedianMs": 0.005846, + "afterMedianMs": 0.006862, + "beforeP95SampleMeanMs": 0.013284, + "afterP95SampleMeanMs": 0.013529, + "medianRatioBeforeAfter": 0.852 + }, + { + "name": "storage.batch-grouping", + "n": 1, + "iterationsPerSample": 10, + "beforeMedianMs": 0.002158, + "afterMedianMs": 0.002433, + "beforeP95SampleMeanMs": 0.003975, + "afterP95SampleMeanMs": 0.004592, + "medianRatioBeforeAfter": 0.887 + }, + { + "name": "core.compose", + "n": 1, + "iterationsPerSample": 10, + "beforeMedianMs": 11.217712, + "afterMedianMs": 10.681637, + "beforeP95SampleMeanMs": 17.359588, + "afterP95SampleMeanMs": 12.23685, + "medianRatioBeforeAfter": 1.05 + }, + { + "name": "runtime.snapshot", + "n": 10, + "iterationsPerSample": 10, + "beforeMedianMs": 0.033129, + "afterMedianMs": 0.033167, + "beforeP95SampleMeanMs": 0.034312, + "afterP95SampleMeanMs": 0.033954, + "medianRatioBeforeAfter": 0.999 + }, + { + "name": "runtime.projection", + "n": 10, + "iterationsPerSample": 10, + "beforeMedianMs": 0.122541, + "afterMedianMs": 0.1192, + "beforeP95SampleMeanMs": 0.135208, + "afterP95SampleMeanMs": 0.136425, + "medianRatioBeforeAfter": 1.028 + }, + { + "name": "runtime.compact", + "n": 10, + "iterationsPerSample": 10, + "beforeMedianMs": 0.510092, + "afterMedianMs": 0.505462, + "beforeP95SampleMeanMs": 0.570766, + "afterP95SampleMeanMs": 0.561163, + "medianRatioBeforeAfter": 1.009 + }, + { + "name": "runtime.write", + "n": 10, + "iterationsPerSample": 10, + "beforeMedianMs": 0.478858, + "afterMedianMs": 0.484425, + "beforeP95SampleMeanMs": 0.510583, + "afterP95SampleMeanMs": 0.531783, + "medianRatioBeforeAfter": 0.989 + }, + { + "name": "documents.catalog", + "n": 10, + "iterationsPerSample": 10, + "beforeMedianMs": 0.096475, + "afterMedianMs": 0.106854, + "beforeP95SampleMeanMs": 0.138417, + "afterP95SampleMeanMs": 0.126209, + "medianRatioBeforeAfter": 0.903 + }, + { + "name": "documents.search", + "n": 10, + "iterationsPerSample": 10, + "beforeMedianMs": 0.573483, + "afterMedianMs": 0.5803, + "beforeP95SampleMeanMs": 0.590162, + "afterP95SampleMeanMs": 0.626842, + "medianRatioBeforeAfter": 0.988 + }, + { + "name": "documents.write", + "n": 10, + "iterationsPerSample": 10, + "beforeMedianMs": 0.485371, + "afterMedianMs": 0.481271, + "beforeP95SampleMeanMs": 0.549267, + "afterP95SampleMeanMs": 0.713025, + "medianRatioBeforeAfter": 1.009 + }, + { + "name": "holographic.search", + "n": 10, + "iterationsPerSample": 10, + "beforeMedianMs": 0.055133, + "afterMedianMs": 0.054446, + "beforeP95SampleMeanMs": 0.060308, + "afterP95SampleMeanMs": 0.065621, + "medianRatioBeforeAfter": 1.013 + }, + { + "name": "holographic.related", + "n": 10, + "iterationsPerSample": 10, + "beforeMedianMs": 0.048321, + "afterMedianMs": 0.045596, + "beforeP95SampleMeanMs": 0.057225, + "afterP95SampleMeanMs": 0.048825, + "medianRatioBeforeAfter": 1.06 + }, + { + "name": "holographic.write", + "n": 10, + "iterationsPerSample": 10, + "beforeMedianMs": 0.189875, + "afterMedianMs": 0.185329, + "beforeP95SampleMeanMs": 0.199279, + "afterP95SampleMeanMs": 0.203513, + "medianRatioBeforeAfter": 1.025 + }, + { + "name": "registry.discovery", + "n": 10, + "iterationsPerSample": 10, + "beforeMedianMs": 0.031542, + "afterMedianMs": 0.032746, + "beforeP95SampleMeanMs": 0.037937, + "afterP95SampleMeanMs": 0.038221, + "medianRatioBeforeAfter": 0.963 + }, + { + "name": "registry.native-catalog", + "n": 10, + "iterationsPerSample": 10, + "beforeMedianMs": 0.061767, + "afterMedianMs": 0.063812, + "beforeP95SampleMeanMs": 0.068371, + "afterP95SampleMeanMs": 0.074517, + "medianRatioBeforeAfter": 0.968 + }, + { + "name": "recall.federated", + "n": 10, + "iterationsPerSample": 10, + "beforeMedianMs": 0.128196, + "afterMedianMs": 0.100379, + "beforeP95SampleMeanMs": 0.137933, + "afterP95SampleMeanMs": 0.123488, + "medianRatioBeforeAfter": 1.277 + }, + { + "name": "recall.pinned", + "n": 10, + "iterationsPerSample": 10, + "beforeMedianMs": 0.383863, + "afterMedianMs": 0.092975, + "beforeP95SampleMeanMs": 0.400183, + "afterP95SampleMeanMs": 0.103258, + "medianRatioBeforeAfter": 4.129 + }, + { + "name": "storage.batch-prepare", + "n": 10, + "iterationsPerSample": 10, + "beforeMedianMs": 0.262779, + "afterMedianMs": 0.034942, + "beforeP95SampleMeanMs": 0.403763, + "afterP95SampleMeanMs": 0.044113, + "medianRatioBeforeAfter": 7.521 + }, + { + "name": "storage.batch-grouping", + "n": 10, + "iterationsPerSample": 10, + "beforeMedianMs": 0.008642, + "afterMedianMs": 0.005921, + "beforeP95SampleMeanMs": 0.010571, + "afterP95SampleMeanMs": 0.006054, + "medianRatioBeforeAfter": 1.46 + }, + { + "name": "core.compose", + "n": 10, + "iterationsPerSample": 10, + "beforeMedianMs": 11.03925, + "afterMedianMs": 11.239021, + "beforeP95SampleMeanMs": 13.298913, + "afterP95SampleMeanMs": 14.907904, + "medianRatioBeforeAfter": 0.982 + }, + { + "name": "runtime.snapshot", + "n": 100, + "iterationsPerSample": 10, + "beforeMedianMs": 0.16725, + "afterMedianMs": 0.173717, + "beforeP95SampleMeanMs": 0.180966, + "afterP95SampleMeanMs": 0.222321, + "medianRatioBeforeAfter": 0.963 + }, + { + "name": "runtime.projection", + "n": 100, + "iterationsPerSample": 10, + "beforeMedianMs": 0.305616, + "afterMedianMs": 0.310637, + "beforeP95SampleMeanMs": 0.334596, + "afterP95SampleMeanMs": 0.376588, + "medianRatioBeforeAfter": 0.984 + }, + { + "name": "runtime.compact", + "n": 100, + "iterationsPerSample": 10, + "beforeMedianMs": 1.113696, + "afterMedianMs": 0.796342, + "beforeP95SampleMeanMs": 1.244088, + "afterP95SampleMeanMs": 0.819754, + "medianRatioBeforeAfter": 1.399 + }, + { + "name": "runtime.write", + "n": 100, + "iterationsPerSample": 10, + "beforeMedianMs": 0.64195, + "afterMedianMs": 0.645392, + "beforeP95SampleMeanMs": 0.674613, + "afterP95SampleMeanMs": 0.731292, + "medianRatioBeforeAfter": 0.995 + }, + { + "name": "documents.catalog", + "n": 100, + "iterationsPerSample": 10, + "beforeMedianMs": 0.395725, + "afterMedianMs": 0.392596, + "beforeP95SampleMeanMs": 0.491875, + "afterP95SampleMeanMs": 0.499113, + "medianRatioBeforeAfter": 1.008 + }, + { + "name": "documents.search", + "n": 100, + "iterationsPerSample": 10, + "beforeMedianMs": 3.923283, + "afterMedianMs": 2.321829, + "beforeP95SampleMeanMs": 6.774962, + "afterP95SampleMeanMs": 2.440733, + "medianRatioBeforeAfter": 1.69 + }, + { + "name": "documents.write", + "n": 100, + "iterationsPerSample": 10, + "beforeMedianMs": 1.159017, + "afterMedianMs": 1.074179, + "beforeP95SampleMeanMs": 1.355946, + "afterP95SampleMeanMs": 1.159229, + "medianRatioBeforeAfter": 1.079 + }, + { + "name": "holographic.search", + "n": 100, + "iterationsPerSample": 10, + "beforeMedianMs": 0.404779, + "afterMedianMs": 0.375829, + "beforeP95SampleMeanMs": 0.42125, + "afterP95SampleMeanMs": 0.395617, + "medianRatioBeforeAfter": 1.077 + }, + { + "name": "holographic.related", + "n": 100, + "iterationsPerSample": 10, + "beforeMedianMs": 0.343408, + "afterMedianMs": 0.305838, + "beforeP95SampleMeanMs": 0.3595, + "afterP95SampleMeanMs": 0.319425, + "medianRatioBeforeAfter": 1.123 + }, + { + "name": "holographic.write", + "n": 100, + "iterationsPerSample": 10, + "beforeMedianMs": 0.334, + "afterMedianMs": 0.341117, + "beforeP95SampleMeanMs": 0.350604, + "afterP95SampleMeanMs": 0.358325, + "medianRatioBeforeAfter": 0.979 + }, + { + "name": "registry.discovery", + "n": 100, + "iterationsPerSample": 10, + "beforeMedianMs": 0.226471, + "afterMedianMs": 0.194371, + "beforeP95SampleMeanMs": 0.241883, + "afterP95SampleMeanMs": 0.215375, + "medianRatioBeforeAfter": 1.165 + }, + { + "name": "registry.native-catalog", + "n": 100, + "iterationsPerSample": 10, + "beforeMedianMs": 0.386475, + "afterMedianMs": 0.353583, + "beforeP95SampleMeanMs": 0.433563, + "afterP95SampleMeanMs": 0.3842, + "medianRatioBeforeAfter": 1.093 + }, + { + "name": "recall.federated", + "n": 100, + "iterationsPerSample": 10, + "beforeMedianMs": 3.0452, + "afterMedianMs": 0.969575, + "beforeP95SampleMeanMs": 3.251771, + "afterP95SampleMeanMs": 2.391775, + "medianRatioBeforeAfter": 3.141 + }, + { + "name": "recall.pinned", + "n": 100, + "iterationsPerSample": 10, + "beforeMedianMs": 21.859771, + "afterMedianMs": 0.902046, + "beforeP95SampleMeanMs": 24.080712, + "afterP95SampleMeanMs": 0.955971, + "medianRatioBeforeAfter": 24.234 + }, + { + "name": "storage.batch-prepare", + "n": 100, + "iterationsPerSample": 10, + "beforeMedianMs": 18.044875, + "afterMedianMs": 0.222721, + "beforeP95SampleMeanMs": 18.259067, + "afterP95SampleMeanMs": 0.275996, + "medianRatioBeforeAfter": 81.02 + }, + { + "name": "storage.batch-grouping", + "n": 100, + "iterationsPerSample": 10, + "beforeMedianMs": 0.073467, + "afterMedianMs": 0.040033, + "beforeP95SampleMeanMs": 0.087933, + "afterP95SampleMeanMs": 0.048254, + "medianRatioBeforeAfter": 1.835 + }, + { + "name": "core.compose", + "n": 100, + "iterationsPerSample": 10, + "beforeMedianMs": 12.311554, + "afterMedianMs": 11.634263, + "beforeP95SampleMeanMs": 15.16055, + "afterP95SampleMeanMs": 13.612917, + "medianRatioBeforeAfter": 1.058 + }, + { + "name": "runtime.snapshot", + "n": 1000, + "iterationsPerSample": 3, + "beforeMedianMs": 1.749361, + "afterMedianMs": 1.617958, + "beforeP95SampleMeanMs": 2.031972, + "afterP95SampleMeanMs": 1.755389, + "medianRatioBeforeAfter": 1.081 + }, + { + "name": "runtime.projection", + "n": 1000, + "iterationsPerSample": 3, + "beforeMedianMs": 2.734875, + "afterMedianMs": 2.455583, + "beforeP95SampleMeanMs": 3.706333, + "afterP95SampleMeanMs": 3.399389, + "medianRatioBeforeAfter": 1.114 + }, + { + "name": "runtime.compact", + "n": 1000, + "iterationsPerSample": 3, + "beforeMedianMs": 41.346917, + "afterMedianMs": 4.047291, + "beforeP95SampleMeanMs": 44.989917, + "afterP95SampleMeanMs": 4.553041, + "medianRatioBeforeAfter": 10.216 + }, + { + "name": "runtime.write", + "n": 1000, + "iterationsPerSample": 3, + "beforeMedianMs": 2.982167, + "afterMedianMs": 2.570069, + "beforeP95SampleMeanMs": 3.83343, + "afterP95SampleMeanMs": 2.831764, + "medianRatioBeforeAfter": 1.16 + }, + { + "name": "documents.catalog", + "n": 1000, + "iterationsPerSample": 3, + "beforeMedianMs": 4.125737, + "afterMedianMs": 3.358264, + "beforeP95SampleMeanMs": 4.546347, + "afterP95SampleMeanMs": 3.567472, + "medianRatioBeforeAfter": 1.229 + }, + { + "name": "documents.search", + "n": 1000, + "iterationsPerSample": 3, + "beforeMedianMs": 40.483805, + "afterMedianMs": 22.164944, + "beforeP95SampleMeanMs": 73.252889, + "afterP95SampleMeanMs": 24.943458, + "medianRatioBeforeAfter": 1.826 + }, + { + "name": "documents.write", + "n": 1000, + "iterationsPerSample": 3, + "beforeMedianMs": 9.252195, + "afterMedianMs": 7.709111, + "beforeP95SampleMeanMs": 9.767611, + "afterP95SampleMeanMs": 9.924903, + "medianRatioBeforeAfter": 1.2 + }, + { + "name": "holographic.search", + "n": 1000, + "iterationsPerSample": 3, + "beforeMedianMs": 4.157625, + "afterMedianMs": 3.576986, + "beforeP95SampleMeanMs": 4.242653, + "afterP95SampleMeanMs": 3.964292, + "medianRatioBeforeAfter": 1.162 + }, + { + "name": "holographic.related", + "n": 1000, + "iterationsPerSample": 3, + "beforeMedianMs": 3.74575, + "afterMedianMs": 3.044097, + "beforeP95SampleMeanMs": 3.932806, + "afterP95SampleMeanMs": 3.1725, + "medianRatioBeforeAfter": 1.23 + }, + { + "name": "holographic.write", + "n": 1000, + "iterationsPerSample": 3, + "beforeMedianMs": 2.902472, + "afterMedianMs": 2.062625, + "beforeP95SampleMeanMs": 3.653389, + "afterP95SampleMeanMs": 2.251319, + "medianRatioBeforeAfter": 1.407 + }, + { + "name": "registry.discovery", + "n": 1000, + "iterationsPerSample": 3, + "beforeMedianMs": 5.31125, + "afterMedianMs": 1.819972, + "beforeP95SampleMeanMs": 8.601916, + "afterP95SampleMeanMs": 2.033472, + "medianRatioBeforeAfter": 2.918 + }, + { + "name": "registry.native-catalog", + "n": 1000, + "iterationsPerSample": 3, + "beforeMedianMs": 7.038111, + "afterMedianMs": 2.92507, + "beforeP95SampleMeanMs": 7.308625, + "afterP95SampleMeanMs": 3.036222, + "medianRatioBeforeAfter": 2.406 + }, + { + "name": "recall.federated", + "n": 1000, + "iterationsPerSample": 3, + "beforeMedianMs": 254.007847, + "afterMedianMs": 9.833792, + "beforeP95SampleMeanMs": 280.917111, + "afterP95SampleMeanMs": 20.894611, + "medianRatioBeforeAfter": 25.83 + }, + { + "name": "recall.pinned", + "n": 1000, + "iterationsPerSample": 1, + "beforeMedianMs": 2154.051541, + "afterMedianMs": 9.399583, + "beforeP95SampleMeanMs": 2357.218708, + "afterP95SampleMeanMs": 11.345375, + "medianRatioBeforeAfter": 229.165 + }, + { + "name": "storage.batch-prepare", + "n": 1000, + "iterationsPerSample": 1, + "beforeMedianMs": 1825.664625, + "afterMedianMs": 2.209708, + "beforeP95SampleMeanMs": 2057.422375, + "afterP95SampleMeanMs": 3.245209, + "medianRatioBeforeAfter": 826.202 + }, + { + "name": "storage.batch-grouping", + "n": 1000, + "iterationsPerSample": 3, + "beforeMedianMs": 1.18893, + "afterMedianMs": 0.295403, + "beforeP95SampleMeanMs": 4.793375, + "afterP95SampleMeanMs": 0.347111, + "medianRatioBeforeAfter": 4.025 + }, + { + "name": "core.compose", + "n": 1000, + "iterationsPerSample": 3, + "beforeMedianMs": 11.439903, + "afterMedianMs": 12.355028, + "beforeP95SampleMeanMs": 12.055722, + "afterP95SampleMeanMs": 12.747417, + "medianRatioBeforeAfter": 0.926 + } + ], + "workCounts": { + "before": [ + { + "name": "runtime.compact", + "n": 1, + "byteLengthCalls": 5, + "byteLengthCharacters": 252 + }, + { + "name": "runtime.compact", + "n": 10, + "byteLengthCalls": 23, + "byteLengthCharacters": 5139 + }, + { + "name": "runtime.compact", + "n": 100, + "byteLengthCalls": 203, + "byteLengthCharacters": 330399 + }, + { + "name": "runtime.compact", + "n": 1000, + "byteLengthCalls": 2003, + "byteLengthCharacters": 31641849 + } + ], + "after": [ + { + "name": "runtime.compact", + "n": 1, + "byteLengthCalls": 6, + "byteLengthCharacters": 255 + }, + { + "name": "runtime.compact", + "n": 10, + "byteLengthCalls": 24, + "byteLengthCharacters": 2397 + }, + { + "name": "runtime.compact", + "n": 100, + "byteLengthCalls": 204, + "byteLengthCharacters": 24177 + }, + { + "name": "runtime.compact", + "n": 1000, + "byteLengthCalls": 2004, + "byteLengthCharacters": 245577 + } + ] + } +} diff --git a/dsh/docs/pr-assets/architecture-cleanup/README.md b/dsh/docs/pr-assets/architecture-cleanup/README.md new file mode 100644 index 000000000..24defc56b --- /dev/null +++ b/dsh/docs/pr-assets/architecture-cleanup/README.md @@ -0,0 +1,81 @@ +# Architecture cleanup verification / 架构清理验收 + +Base: `7bcadbcf368798bfee1f2c2c45aa9638c55dd09d` (v0.5.2). Production/test revision: `1c4e0c4`; subsequent evidence-only commits do not change the implementation. + +## Scope / 范围 + +Host execution resources now have one owner; Strategy configuration rules live in the SDK; Memory Spaces owns its configuration normalization. The internal workbench transport loses unused migration paths, and each default Source owns its page copy and styles. Public RPCs, storage formats, configuration keys, optional page-kit class names, and all 920 bilingual keys remain available. No new Core service, plugin category, UI framework, or automatic write authority was added. + +Host 执行资源统一所有权;Strategy 配置规则归 SDK;Memory Spaces 配置归 Source。删除未使用的工作台传输路径,默认 Source 自己持有页面文案与样式。公开 RPC、存储格式、配置键、可选页面工具类名及全部 920 个双语词条保留;没有增加 Core 服务、插件分类、UI 框架或自动写入权限。 + +The resource-move commit `4e99900` preserved 1,310 CSS rules/conditions and 294 public class mappings against the base. A separate compact-header fix (`3ff8116`) prevents the connection label from shrinking into the title and uses the accessible back icon at narrow widths. The original fingerprint remains recorded alongside this explicit correction; it is not presented as an unchanged visual baseline. + +资源迁移提交 `4e99900` 保持原 1,310 条样式规则/条件及 294 个公开类名映射。后续单独的窄屏页头修正 `3ff8116` 防止连接状态侵入标题,并在窄屏使用保留可访问名称的返回图标。原始指纹与这项明确修正分别记录,不将修正后的界面宣称为完全未变。 + +## Automated verification / 自动化验证 + +Environment: macOS, Node 24.19.0, pnpm 10.13.1, published DSH 0.1.2-rc.1; Native integration uses installed Mnemon CLI 0.2.7 with temporary storage. + +```sh +MNEMON_NATIVE_TEST_CLI=/opt/homebrew/bin/mnemon pnpm verify +pnpm verify:plugins +pnpm release:check +pnpm release:intent +git diff --check origin/main +``` + +- `verify`: 121 test files / 1,050 tests passed across root and packages; one Windows-only file/test skipped on macOS. Deterministic builds, local composition performance fences, real Headless activation/restart, public entries, package contents, publint and type checks passed. +- `verify:plugins`: 16 copied standalone repositories and 17 packed artifacts passed installation, type checking, tests and builds; public SDK/Client consumption, three optional Strategies together, and real DSH v0.4.7-to-current Starter upgrade passed. The Client consumer also tests Source-owned resources against a deliberately stale shared page kit. +- Release metadata and changeset coverage passed. No version bump, publication, or merge is part of this PR. + +- 根与各包共 121 个测试文件、1,050 项测试通过;macOS 跳过 1 个 Windows 专用文件/测试。确定性构建、本地组合性能围栏、真实 Headless 启动/重启、公开入口、包内容、包 lint 与类型检查通过。 +- 16 个仓库外独立插件和 17 个打包产物的安装、类型检查、测试与构建通过;公共 SDK/Client 消费、三个辅助策略共存及真实 DSH v0.4.7 升级通过。消费测试还模拟旧版共享页面工具,验证 Source 自有文案与样式可独立加载。 +- 发布元数据和 changeset 覆盖通过。本 PR 不升级版本、不发布、不合并。 + +The first artifact run hit a registry TLS reset. A subsequent run exposed missing tarball integrity in the test registry, allowing pnpm to reuse old bytes with the same unreleased version. The registry now serves the actual `npm pack` SHA-512 integrity; the full artifact run was repeated successfully, without replacing package dependencies or skipping upgrade checks. Published DSH primitives also emit a missing-source-map warning during Vitest; this is not counted as a test failure. + +首次产物验证遇到 registry TLS 重置;后续发现测试 registry 未提供 tarball 完整性摘要,使 pnpm 复用了同版本旧包。现已提供真实 `npm pack` SHA-512 摘要并重跑完整验证,未替换依赖或跳过升级检查。Vitest 中已发布 DSH primitives 的缺失 source map 警告保留,不记为测试失败。 + +## Browser checks / 浏览器检查 + +Run `pnpm e2e:serve` after building. This uses a disposable real DSH WebUI/Profile with a loopback-only model stub; all notes and Documents below are synthetic. No personal DSH profile, private memory, or cloud Provider account was used. Two Native facts were seeded via the CLI into the same explicit temporary Store, then read through the UI; this is not claimed as LLM-authored capture. + +构建后运行 `pnpm e2e:serve`。环境为隔离的真实 DSH WebUI/Profile,模型仅使用本地桩;所有记忆与档案均为合成材料。未使用个人 DSH 配置、私有记忆或云端 Provider 账户。两条 Native 事实通过 CLI 写入同一显式临时 Store,再由 UI 读取,不将其称为 LLM 主动记录。 + +| Check / 检查 | Observed result / 实际结果 | +| --- | --- | +| Sidebar launch, return, one chat turn / 入口、返回、会话回合 | Connected; local stub reply completed / 已连接,本地桩回合完成 | +| Runtime add/edit/clear branch / 新增、编辑、清空分支 | Count and content updated; scope badge removed / 条目数与内容更新,分支标签移除 | +| Documents create/edit/search / 档案新增、编辑、检索 | Markdown table rendered; revision 1 → 2; query found the document / 表格渲染,修订递增,检索命中 | +| Native space create/activate / 原生记忆空间创建、激活 | Created through the 390px dialog; 1/1 active / 窄屏创建成功,1/1 激活 | +| Native browse/keyword recall/related/forget / 浏览、召回、关联、忘记 | 2 results and a related item; confirmed soft-delete reduced recall to 1 / 召回 2 条并展示关联,确认软删除后剩 1 条 | +| Three enhancements / 三项增强 | All three switches persisted on together / 三个开关同时开启并保存 | +| Layer disable/re-enable / 记忆层关闭与恢复 | Documents alone became Off; Host stayed Connected; revision-2 data returned after enabling / 仅档案关闭,Host 保持连接,重启该层后修订 2 数据恢复 | +| Locale/theme/layout / 国际化、主题、布局 | Chinese dark and English light; Sidebar and Builtin; 390px dialog and page / 中英双语、深浅主题、两种入口、390px 弹窗和页面 | + +The automation's empty-string `fill` initially left a branch input unchanged. Reading the actual input exposed the issue; keyboard select-all/backspace and Save correctly cleared the scope. It was not a Source persistence failure. + +自动化的空字符串 `fill` 曾未清空分支输入。核对真实输入值后,使用键盘全选删除并保存,范围正确清除;这不是 Source 持久化故障。 + +### Remaining integration observation / 保留的集成观察 + +In the DSH 0.1.2-rc.1 Builtin host at 1280×720, a DSH-owned `[data-width-handle="left"]` overlay occupied x=376–416, including the Runtime tab's center. Center clicks hit that drag handle; clicking the uncovered part of the tab worked. The Sidebar checks do not have this interception. No DSH code or global host-handle CSS was modified, and no pre-cleanup browser run was used to assign regression causality. This remains a separate Builtin integration issue, not an all-clear UI claim. + +在 1280×720 的 DSH 0.1.2-rc.1 Builtin 宿主中,DSH 自身的左宽度拖柄覆盖 x=376–416,包含 Runtime 页签中心。中心点击被拖柄拦截,未遮挡区域可正常切换;Sidebar 无此拦截。未修改 DSH 代码或全局拖柄样式,也未用清理前浏览器基线判断回归归因。这是单列保留的 Builtin 集成问题,不宣称界面所有交互均无问题。 + +Windows execution, real cloud Provider services, mobile Safari, LLM summarization quality, and production model latency/cost were not tested here. Model-stub completion and local performance fences do not demonstrate benchmark accuracy or efficiency gains. + +本次未测试 Windows 运行、真实云端 Provider、移动 Safari、LLM 整理质量或生产模型延迟/费用。本地模型桩回合及性能围栏不代表 benchmark 准确率或效率收益。 + +## Screenshots / 截图 + +These are unedited browser captures of this branch using only the synthetic fixture. / 以下均为本分支合成环境的原始浏览器截图。 + +- [Runtime edit / 运行时编辑](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/architecture-cleanup/runtime-edited-desktop.png) +- [Document revision / 档案修订](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/architecture-cleanup/documents-revision-desktop.png) +- [Native recall / 原生召回](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/architecture-cleanup/native-recall-desktop.png) +- [Enhancements together / 增强共存](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/architecture-cleanup/enhancements-enabled-desktop.png) +- [Mobile creation / 窄屏创建](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/architecture-cleanup/space-create-mobile.png), [reachable footer actions / 可达的底部按钮](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/architecture-cleanup/space-create-mobile-actions.png) +- [English compact page / 英文窄屏页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/architecture-cleanup/documents-mobile-en.png) +- [Layer disabled / 关闭单层](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/architecture-cleanup/documents-disabled-en.png) +- [Builtin English light / 内置入口英文浅色](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/architecture-cleanup/builtin-runtime-en-light.png) diff --git a/dsh/docs/pr-assets/better-sidebar-153/README.md b/dsh/docs/pr-assets/better-sidebar-153/README.md new file mode 100644 index 000000000..0db990a3f --- /dev/null +++ b/dsh/docs/pr-assets/better-sidebar-153/README.md @@ -0,0 +1,38 @@ +# Better Sidebar compatibility verification (#153) + +This directory records the real WebUI acceptance check for the optional +`dsh-better-sidebar` integration. Both profiles used test-owned `DSH_HOME`, +workspace, and Mnemon data directories under the issue worktree. They did not +read from or write to the personal Mnemon data root. + +## Environments + +| Case | Harness | Better Sidebar | Client bundle order | +| --- | --- | --- | --- | +| Reported-version acceptance | `0.1.1-rc.2` | `0.18.1-alpha.0` (`b2722ece`) | Better Sidebar, then Mnemon | +| Reverse-order acceptance | `0.1.2-alpha.3` | `0.18.1-alpha.0` (`b2722ece`) | Mnemon, then Better Sidebar | + +The reported-version profile also used `@linxin666/dsh-web-all@0.3.6`. Mnemon +was linked from this worktree after `pnpm run build`. Writes and lifecycle +automation were disabled; storage used a profile-owned custom directory. + +## Results + +- The normal DSH Sidebar kept exactly one **Memory System** row. +- Better Sidebar's **New tab** menu exposed exactly one **Memory System** item. +- Opening that item rendered the shared Mnemon UI with the target session and + workspace scope. Status and Runtime navigation both loaded successfully. +- Reloading restored the Better Sidebar tab. +- A fresh browser tab reported zero console errors and zero warnings. +- Repeating the menu and render checks with Mnemon loaded before Better + Sidebar also reported zero console errors and zero warnings. + +## Screenshots + +| Evidence | Capture | +| --- | --- | +| DSH's normal Memory System row and Better Sidebar's registered menu item are visible together | [Tab menu](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/better-sidebar-153/after-tab-menu.jpg) | +| The Memory System Runtime page is rendered inside Better Sidebar | [Memory tab](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/better-sidebar-153/after-memory-tab.jpg) | + +Both captures are from the isolated Harness `0.1.1-rc.2` profile at its native +1280 × 720 browser viewport. diff --git a/dsh/docs/pr-assets/builtin-139/README.md b/dsh/docs/pr-assets/builtin-139/README.md new file mode 100644 index 000000000..60bc93ada --- /dev/null +++ b/dsh/docs/pr-assets/builtin-139/README.md @@ -0,0 +1,73 @@ +# Issue #139: Builtin placement regression evidence + +## 中文 + +2026-08-31,在从 `main`(`d6fa550`)创建的独立 worktree 中验证 [issue #139](https://github.com/omdsh-dev/dsh-mnemon/issues/139)。修复代码为 `d1bf61ed4fde5a8403ac73ae07d0f0696902bd8f`;截图中的 `dsh-mnemon 0.4.1` 是本地 link 构建保留的包版本,不代表新发布包。原版对照来自未经修改的 npm `dsh-mnemon@0.4.1`。 + +### 环境与复现 + +- Host:发布版 `@deepseek-ai/dsh@0.1.1-rc.2`;Web:`@linxin666/dsh-web-all@0.3.6`,官方默认皮肤。 +- Native CLI:官方 Mnemon 0.2.5,macOS arm64;下载文件已按官方 SHA-256 校验。 +- 独立 DSH_HOME、两份测试工作区、全局根和自定义根,仅使用合成数据;未修改个人配置或记忆。 +- 普通环境,无通知丢失注入。会话由本地固定响应模型生成;最终重启验证禁用模型 Endpoint,只进行设置、读取和编辑器操作。 +- 对照截图为原始 1600×900 页面,补丁截图为原始 1280×720 页面。没有缩放、拼接或改写截图内容;不将不同视口用于像素级布局对比。 + +```sh +pnpm install --frozen-lockfile +pnpm run verify +node scripts/serve-web-regression.mjs --cli /absolute/path/to/test-owned/mnemon --display-mode buildin --storage-scope workspace +node scripts/serve-web-regression.mjs --cli /absolute/path/to/test-owned/mnemon --package dsh-mnemon@0.4.1 --display-mode buildin --storage-scope workspace +``` + +两条 Web 命令均刻意传入历史 `buildin`。对照版仍只有 Sidebar;修复版自动保存 `builtin` 并注册会话标签页。用最终代码重启同一测试根后,再次验证已有数据、配置热更新迁移及所有范围映射。 + +### 验证结果 + +| 场景 | 结果 | +|---|---| +| 配置规范化 | 启动和外部热更新自动将 `buildin` 写回 `builtin`;旧客户端 RPC 也保存规范值;注释及无关字段保留 | +| 幂等与并发 | Headless 重启后配置字节不变;修订号冲突重新读取,新 Sidebar 选择优先;只读设置不绕过权限 | +| 入口切换 | Sidebar 与 Builtin 实时双向切换、刷新后保留,始终只挂载一个入口;默认 Sidebar 不变 | +| 共用界面 | 相同四个主页面、导航、筛选和编辑器;Builtin 只隐藏页眉范围/工作区/对齐控件,无专用 CSS | +| 工作区范围 | A/B 的运行时记忆相互隔离;A 的档案和已激活 Native 记忆空间不出现在 B | +| 全局和自定义范围 | 两个会话分别共享全局根或配置的自定义根,不混入工作区记忆;切回工作区后原数据仍在 | +| 页面往返 | 最终版本连续十轮任务看板与会话 Builtin 往返,始终读取 A 的原记录;存入记忆弹窗取消不写入 | +| Native | 真实 CLI 状态调用成功,A 显示 Mnemon 0.2.5、1/1 记忆空间已激活及一份项目档案 | + +完整 `pnpm run verify` 通过:630 项测试通过,1 项 Windows 专用测试在 macOS 跳过;108 个构建文件确定性检查、35 个 Headless 工具及 5 个代表调用、Headless 配置迁移和幂等重启、115 文件发布包、10 个 Node 兼容入口、publint 与 attw 均通过。组件测试覆盖两种入口的全部八种记忆层组合、旧请求失效、会话切换时编辑器清理、独立任务作用域、可见性和快捷入口清理;Host 测试覆盖 global/workspace/custom 及旧 dataDir 路由。 + +配置迁移前后以及最终范围往返后,以下四份 `runtime/memories.json` 的 SHA-256 完全相同。截图中的旧单词 `buildin` 是刻意保留的测试记忆内容,配置迁移没有修改记忆正文。 + +| 测试根 | SHA-256 | +|---|---| +| Workspace A | `c49c88ad340b981e058b13c5dcddb270e43d584fc20d420d1b7e3db91a1855cd` | +| Workspace B | `45aeff0a6a755584c49741752517615a4465ba7b4088e88b8545351c9a1a71f6` | +| Global | `aff509817b6de2636c926f77c68e7103bc22e6740d86559fb5303a78a9be46bd` | +| Custom | `a8f3639dfe6c3c17a650b87cc924c58e99bf921d5a34b54ac233f0d5a4e3a4d2` | + +边界:未调用真实付费模型或第三方远程 Provider;SSH 只验证入口往返,未配置其后端。主动重启 Host 时出现短暂连接重试和第三方 terminal 断连日志,后续 Mnemon 操作无新增错误。额外的 1600×900 补拍遇到浏览器控制连接超时,因此仅提交已确认完成加载的 1280×720 补丁截图,不宣称完成宽屏补拍。保留上游 rc.2 缺失 source map 的非失败构建告警。机器路径、原始日志和完整 fixture 留在本地,不纳入公开证据。 + +## English + +Verified on 2026-08-31 in an independent worktree based on `main` at `d6fa550`. The patched source is `d1bf61ed4fde5a8403ac73ae07d0f0696902bd8f`; its UI still says 0.4.1 because this is a local linked build, not a newly published release. The control is the unmodified npm `dsh-mnemon@0.4.1` package. + +The environment uses published DSH 0.1.1-rc.2, Web-all 0.3.6 with the official default skin, and the real official Mnemon 0.2.5 macOS arm64 binary verified against its published SHA-256. DSH_HOME, both workspaces, and global/custom roots are fixture-owned. Only synthetic memories and local fixed model responses were used; the final restart deliberately disabled model requests. The control screenshot is 1600×900; patched screenshots are 1280×720. All captures are unmodified, with no pixel-level comparison claimed across different viewports. + +The commands above intentionally start both environments with `buildin`. The control keeps Sidebar and has no conversation memory tab. The patch automatically persists `builtin`. Startup, external settings edits, and legacy RPC writes normalize the setting without changing unrelated fields or comments. Headless restart is byte-idempotent; revision conflicts re-read current preferences so a newer Sidebar choice wins. Read-only settings are recognized without bypassing write restrictions. + +Sidebar and Builtin switch live without duplicate entries and survive reloads. They share all pages, filters, and editors; Builtin hides only the scope/workspace/alignment header and adds no CSS. Workspace A/B memories stay isolated, including A's document and active Native space. Global/custom roots are shared across both sessions independently of workspace-local data. Switching back restores the original records. The final build passed ten Task Board/conversation round trips. Canceling Save to memory did not write. Real Native status shows CLI 0.2.5, one active space, and one document in A. + +Full `pnpm run verify` passed: 630 tests, one Windows-only skip on macOS; 108 deterministic build files; 35 Headless tools and five representative calls; migration plus an idempotent Headless restart; a 115-file package; ten Node-compatible public entries; publint and attw. Component coverage includes all eight layer combinations on both surfaces, stale responses/editors across sessions, task scoping, visibility, and shortcut cleanup. Host routing covers global/workspace/custom and legacy dataDir. The four memory-file hashes listed above remained identical through migration and final scope round trips; the old word `buildin` inside a synthetic memory was deliberately not rewritten. + +Limitations: no paid model or remote Provider validation. SSH navigation only; its backend was not configured. The intentional Host restart produced temporary reconnect and third-party terminal-disconnect messages, but subsequent Mnemon actions added no errors. An additional 1600×900 capture attempt encountered browser-control connection timeouts, so only confirmed fully loaded 1280×720 patched screenshots are included; completed wide-screen recapture is not claimed. The upstream rc.2 missing-source-map warning remains non-fatal. Machine paths, raw logs, and complete fixtures remain local. + +## Screenshots / 截图 + +| 场景 / Scenario | 截图 / Screenshot | +|---|---| +| 原版 0.4.1 忽略旧偏好 / Unmodified 0.4.1 ignores the old preference | [Before](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/builtin-139/before-0.4.1-conversation.png) | +| 规范 Builtin 配置 / Canonical Builtin setting | [Settings](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/screenshots/settings-entry-placement.png) | +| Builtin 中读取 A / Read A through Builtin | [Builtin](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/builtin-139/after-builtin-runtime.png) | +| Sidebar 中读取相同内容 / Same content through Sidebar | [Sidebar](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/builtin-139/after-sidebar-runtime.png) | +| B 的隔离记忆 / Isolated B memory | [Workspace B](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/builtin-139/after-workspace-b-isolated.png) | +| 真实 Native 状态 / Real Native status | [Status](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/builtin-139/after-native-status.png) | diff --git a/dsh/docs/pr-assets/component-pages-20260927/README.md b/dsh/docs/pr-assets/component-pages-20260927/README.md new file mode 100644 index 000000000..5d317e85c --- /dev/null +++ b/dsh/docs/pr-assets/component-pages-20260927/README.md @@ -0,0 +1,79 @@ +# Component pages and one interaction rule + +[简体中文](./README.zh-CN.md) + +Tested implementation: `41fced12` for the configuration, `7fde5ecb` for the Memory System, and `8869ae6f` with `73fc8344` for DSH's list and row pages, stacked on the composition board record ([record](../composition-board-20260927/README.md)). macOS 15.6, Node 25.1.0, published DSH 0.1.7-rc.2, headless Chrome at 1280 × 860 (and 420 × 900). Each run walks an isolated WebUI fixture from the Starter's defaults with its test model. No personal memory or credentials were used. + +## The configuration keeps what belongs to no component + +A gear marks each component with settings of its own and opens its page; the main Strategy's gear opens the selected Strategy. Storage names the components that keep their data in its directory and shows where memory lives now. Interface choices apply at once. + +| Board | Storage and Interface | +|---|---| +| ![Board with gears](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/pages-board.png) | ![Storage and Interface](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/pages-storage-interface.png) | + +Moving the storage waits for its Apply, which says what applying does: + +![Storage Apply line](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/pages-storage-apply.png) + +## Settings on their components' pages + +| Runtime Memory: where USER.md lives | Memory Spaces: Providers and embedding | +|---|---| +| ![Runtime Memory page](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/pages-runtime.png) | ![Memory Spaces page](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/pages-spaces.png) | + +| Default three-tier: background tasks | Review limits wait for their Apply | +|---|---| +| ![Default three-tier page](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/pages-three-tier.png) | ![Review limits edited](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/pages-three-tier-limits.png) | + +## Names open pages + +| Relation chips on rows | A related name opens in place, with Back | +|---|---| +| ![Relation chips](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/pages-chips.png) | ![Memory Spaces opened from Active capture](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/pages-related.png) | + +Declared options show Apply once one changes: + +![Light context option edited](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/pages-options-edit.png) + +## The Memory System names what the components declare + +Tabs, the header's main Strategy and one Status card per Source component carry the names the components declare, as the configuration shows them; each card says what its component contributed. + +| Light | Dark | +|---|---| +| ![Status page](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/workspace-status.png) | ![Status page, dark](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/dark-workspace-status.png) | + +## DSH's component list opens the same pages + +Each component package now gives DSH the name and description it declares, so DSH's own list below the configuration reads like the board; a component's name there opens DSH's page for it, which carries the same component page. Related components open over it. + +| DSH's list before (package names) | DSH's list now | +|---|---| +| ![List before](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/rows-list-before.png) | ![List now](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/rows-list.png) | + +| Default three-tier on DSH's row page | A related component opens over it | +|---|---| +| ![Row page](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/row-page.png) | ![Related page over the row page](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/row-page-related.png) | + +## Dark theme and narrow column + +| Board | Storage and Interface | Review limits | +|---|---|---| +| ![Board, dark](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/dark-pages-board.png) | ![Storage, dark](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/dark-pages-storage-interface.png) | ![Limits, dark](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/dark-pages-three-tier-limits.png) | + +| Board, 420 px | Storage Apply, 420 px | Review limits, 420 px | +|---|---|---| +| ![Board, narrow](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/narrow-pages-board.png) | ![Storage, narrow](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/narrow-pages-storage-apply.png) | ![Limits, narrow](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/narrow-pages-three-tier-limits.png) | + +## Verification + +- Unit tests cover the settings region (registration, release, directory), the shipped panels (Runtime Memory's profile scope, Memory Spaces' managed switch and connection, Default three-tier's route, review choices and limits), storage Apply with legacy keys retired, interface choices at once with a refused write shown and reverted, gears, names, relation chips and Back on the board, declared options with Apply, and the dialog's box model. +- Full `pnpm run verify` and `pnpm run release:intent` passed; the package budget records the measured growth. +- Unit tests also cover the Status card region and the shipped Sources' cards, and a workbench whose tabs, cards (including a Source that is off and one that contributed nothing) and header are named from the components. +- Unit tests also cover the page mode of the board (one component, related pages over it), the eight row registrations, and each package's DSH metadata against its declaration and the Starter's patch; `pnpm run verify:plugins` packed and activated the Starter with its metadata in real DSH. +- WebUI: 14 light, 4 dark and 3 narrow states captured by a scripted walk with no console errors; the same flows were driven by hand in the in-app browser, including an idle review choice written to the real Host and a refused nested write caught before the fix. + +## Limits + +On the Status page, the Provider list and the storage section remain the Host's own sections rather than contributions. Row pages exist for the Starter's own rows; an installed package's rows keep DSH's default page, while its component still has its page on the board. diff --git a/dsh/docs/pr-assets/component-pages-20260927/README.zh-CN.md b/dsh/docs/pr-assets/component-pages-20260927/README.zh-CN.md new file mode 100644 index 000000000..50176649d --- /dev/null +++ b/dsh/docs/pr-assets/component-pages-20260927/README.zh-CN.md @@ -0,0 +1,79 @@ +# 组件页与统一的交互规则 + +[English](./README.md) + +实测实现:配置页为 `41fced12`,记忆系统为 `7fde5ecb`,DSH 列表与行页面为 `8869ae6f` 与 `73fc8344`,叠加在记忆组合面板记录之上([记录](../composition-board-20260927/README.zh-CN.md))。macOS 15.6、Node 25.1.0、已发布的 DSH 0.1.7-rc.2、headless Chrome 1280 × 860(以及 420 × 900)。每次运行都从 Starter 默认配置与其测试模型的隔离 WebUI fixture 开始,未使用个人记忆或凭据。 + +## 配置页只保留不属于任何组件的内容 + +有自己设置的组件显示齿轮,点击打开它的页面;主策略的齿轮打开当前主策略。“存储”列出数据保存在其目录中的组件,并显示当前所在位置。“界面”的选择立即生效。 + +| 面板 | 存储与界面 | +|---|---| +| ![带齿轮的面板](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/pages-board.png) | ![存储与界面](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/pages-storage-interface.png) | + +改动存储位置需要点击其“应用”,应用行说明后果: + +![存储的应用行](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/pages-storage-apply.png) + +## 设置位于各自组件的页面 + +| 运行时记忆:USER.md 的位置 | 记忆空间:Provider 与嵌入 | +|---|---| +| ![运行时记忆页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/pages-runtime.png) | ![记忆空间页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/pages-spaces.png) | + +| 默认三层:后台任务 | 审查参数等待其“应用” | +|---|---| +| ![默认三层页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/pages-three-tier.png) | ![编辑中的审查参数](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/pages-three-tier-limits.png) | + +## 名称即入口 + +| 行内的关联标签 | 关联名称在原处打开,并可返回 | +|---|---| +| ![关联标签](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/pages-chips.png) | ![从主动记录打开记忆空间](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/pages-related.png) | + +声明的选项改动后才出现“应用”: + +![编辑中的轻量上下文选项](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/pages-options-edit.png) + +## 记忆系统使用组件声明的名称 + +标签页、页头的主策略,以及每个 Source 组件的状态卡片都使用组件声明的名称,与配置页一致;每张卡片显示其组件提供的内容。 + +| 浅色 | 深色 | +|---|---| +| ![状态页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/workspace-status.png) | ![状态页,深色](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/dark-workspace-status.png) | + +## DSH 组件列表打开相同的页面 + +每个组件包现在向 DSH 提供其声明的名称与说明,配置下方 DSH 自己的列表因此与面板一致;点击列表中的组件名称会打开 DSH 为它提供的页面,其中是相同的组件页,关联组件在其上以弹窗打开。 + +| 之前的 DSH 列表(包名) | 现在的 DSH 列表 | +|---|---| +| ![之前的列表](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/rows-list-before.png) | ![现在的列表](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/rows-list.png) | + +| DSH 行页面中的“默认三层” | 关联组件在其上打开 | +|---|---| +| ![行页面](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/row-page.png) | ![在行页面上打开的关联组件](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/row-page-related.png) | + +## 深色主题与窄列 + +| 面板 | 存储与界面 | 审查参数 | +|---|---|---| +| ![面板,深色](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/dark-pages-board.png) | ![存储,深色](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/dark-pages-storage-interface.png) | ![审查参数,深色](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/dark-pages-three-tier-limits.png) | + +| 面板,420 px | 存储应用行,420 px | 审查参数,420 px | +|---|---|---| +| ![面板,窄列](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/narrow-pages-board.png) | ![存储,窄列](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/narrow-pages-storage-apply.png) | ![审查参数,窄列](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/component-pages-20260927/narrow-pages-three-tier-limits.png) | + +## 验证 + +- 单元测试覆盖设置区域(注册、释放、目录)、随附面板(运行时记忆的用户画像范围,记忆空间的托管开关与连接信息,默认三层的路由、审查选择与参数)、存储的应用与旧键清理、界面选择即时生效且写入被拒时回退并说明、面板上的齿轮、名称、关联标签与返回、声明选项的应用,以及弹窗的盒模型。 +- 完整的 `pnpm run verify` 与 `pnpm run release:intent` 通过;包体积预算记录了实测增长。 +- 单元测试还覆盖状态卡片区域与随附 Source 的卡片,以及标签页、卡片(包括已关闭的 Source 与未提供内容的 Source)和页头都按组件命名的工作台。 +- 单元测试还覆盖面板的单组件页面模式(单个组件、关联页面在其上打开)、8 个行注册,以及各组件包的 DSH 元数据与其声明、Starter 补丁的一致性;`pnpm run verify:plugins` 在真实 DSH 中打包并激活了带元数据的 Starter。 +- WebUI:脚本化走查采集 14 个浅色、4 个深色与 3 个窄列状态,无控制台错误;同样的流程也在应用内浏览器中手动完成,包括向真实 Host 写入空闲审查选项,以及修复前捕获到的嵌套路径写入被拒。 + +## 限制 + +状态页中的 Provider 列表与存储区仍是 Host 自己的区块,而非组件贡献。行页面只为 Starter 自己的行提供;已安装的包的行仍使用 DSH 默认页面,其组件在面板中仍有组件页。 diff --git a/dsh/docs/pr-assets/composable-naming-20260927/README.md b/dsh/docs/pr-assets/composable-naming-20260927/README.md new file mode 100644 index 000000000..0d3f66ef5 --- /dev/null +++ b/dsh/docs/pr-assets/composable-naming-20260927/README.md @@ -0,0 +1,30 @@ +# Composable memory naming + +[简体中文](./README.zh-CN.md) + +Tested implementation: `6227ed41afdba1212409e7b02dc25e184851a122`, based on `a16ab47f8a61bed19537a07d53ad1030f4d3934b` (v0.5.16). macOS, Node 24.20.0, pnpm 11.19.0, published DSH 0.1.7-rc.2 and Mnemon CLI 0.2.9. The temporary Web profile installed this branch's Root tarball with the sixteen published companion packages from the v0.5.16 composition. The Root still carries 0.5.16 for testing; the changeset requests a subsequent patch release. + +## Visible result + +The Chinese plugin title is `可组合记忆 (dsh-mnemon)`. English retains the package name and uses this description: + +> Composable, view-based memory for DeepSeek Harness. Pluggable sources and strategies, with three-tier memory out of the box. + +The installed artifact was opened in the real Plugins detail page, then switched between Chinese and English through DSH Settings. Both the bundle heading and Root component row showed the new copy. DSH's published `readPluginMeta()` also returned the same strings from that installation; [metadata.json](./metadata.json) includes the tested tarball hash. + +| Language | Before | This implementation | +|---|---|---| +| Chinese | [PR #286 evidence](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/pr-286-plugin-metadata/plugins-zh.jpg) | [Composable memory](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composable-naming-20260927/plugins-zh.png) | +| English | [PR #286 evidence](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/pr-286-plugin-metadata/plugins-en.jpg) | [Updated description](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composable-naming-20260927/plugins-en.png) | + +The screenshots retain the official DSH group-list defect tracked in [dsh-external/issues#649](https://github.com/dsh-external/issues/issues/649). This copy change does not include a plugin-manager replacement. + +## Validation + +- `pnpm verify`: docs, types, deterministic builds and all plugin tests passed. The Root phase had two wall-clock performance failures at its default concurrency; all other Root tests passed. Thresholds were not changed. +- `pnpm exec vitest run --dir tests --maxWorkers=2`: all 1,420 tests passed, with 9 optional/environment skips, including both performance checks. +- `pnpm verify:headless` and `pnpm verify:package`: passed, including restart, legacy disable behavior, public entries, types and package lint. +- `pnpm verify:plugins --skip-build`: sixteen standalone plugin projects and seventeen packed artifacts passed, including the updated external metadata assertion and real DSH installation/composition. +- `pnpm release:intent`: passed for the Root patch changeset. `mnemon --version`: 0.2.9. + +No live model or external Provider API was needed. This record verifies metadata presentation and packaging, not new memory behavior. diff --git a/dsh/docs/pr-assets/composable-naming-20260927/README.zh-CN.md b/dsh/docs/pr-assets/composable-naming-20260927/README.zh-CN.md new file mode 100644 index 000000000..fa1c1ad0f --- /dev/null +++ b/dsh/docs/pr-assets/composable-naming-20260927/README.zh-CN.md @@ -0,0 +1,30 @@ +# 可组合记忆命名 + +[English](./README.md) + +实测实现:`6227ed41afdba1212409e7b02dc25e184851a122`,基于 `a16ab47f8a61bed19537a07d53ad1030f4d3934b`(v0.5.16)。环境为 macOS、Node 24.20.0、pnpm 11.19.0、正式 DSH 0.1.7-rc.2 与 Mnemon CLI 0.2.9。临时 Web profile 安装本分支 Root tarball,以及 v0.5.16 组合中的十六个正式伴随包。测试包仍标记为 0.5.16;changeset 为后续补丁发布提供版本意图。 + +## 展示结果 + +中文插件名为 `可组合记忆 (dsh-mnemon)`,说明为: + +> 面向 DeepSeek Harness 的可组合视图记忆。记忆来源与策略可插拔,开箱即用提供三层记忆。 + +在真实插件详情页打开已安装的制品,并通过 DSH 设置切换中文、英文。组合包标题和 Root 组件行均展示新文案;英文名称保留 `dsh-mnemon`,说明与用户提供的英文原文一致。正式 DSH 的 `readPluginMeta()` 也从同一安装环境读出了相同文案;[metadata.json](./metadata.json) 记录测试 tarball 的哈希。 + +| 语言 | 修改前 | 本次实现 | +|---|---|---| +| 中文 | [PR #286 原始证据](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/pr-286-plugin-metadata/plugins-zh.jpg) | [可组合记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composable-naming-20260927/plugins-zh.png) | +| 英文 | [PR #286 原始证据](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/pr-286-plugin-metadata/plugins-en.jpg) | [新版说明](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composable-naming-20260927/plugins-en.png) | + +截图保留正式 DSH 的分组展示问题,已由 [dsh-external/issues#649](https://github.com/dsh-external/issues/issues/649) 跟踪。本次文案修改没有替换插件管理器。 + +## 验证 + +- `pnpm verify`:文档、类型、确定性构建和各插件测试通过。Root 默认并发下有两项性能测试超过 wall-clock 门槛,其余 Root 测试通过;未调整门槛。 +- `pnpm exec vitest run --dir tests --maxWorkers=2`:1,420 项全部通过,9 项可选或环境测试跳过,包括前述两项性能测试在内均通过。 +- `pnpm verify:headless` 与 `pnpm verify:package`:通过,覆盖重启、旧版禁用语义、公开入口、类型和包检查。 +- `pnpm verify:plugins --skip-build`:十六个独立插件项目与十七个制品通过,包含更新后的外部元数据断言及真实 DSH 安装、组合检查。 +- `pnpm release:intent`:Root patch changeset 检查通过;`mnemon --version` 为 0.2.9。 + +本次无需调用真实模型或外部 Provider API。本记录验证元数据展示与打包,不代表新增记忆行为验收。 diff --git a/dsh/docs/pr-assets/composable-naming-20260927/metadata.json b/dsh/docs/pr-assets/composable-naming-20260927/metadata.json new file mode 100644 index 000000000..2a9379434 --- /dev/null +++ b/dsh/docs/pr-assets/composable-naming-20260927/metadata.json @@ -0,0 +1,16 @@ +{ + "dsh": "0.1.7-rc.2", + "node": "v24.20.0", + "packageVersion": "0.5.16", + "rootTarballSha256": "d38afb85784047b54cdb741862c4d3b6ee6c09aa5f56fee8f2bc4be6a9a9b410", + "metadata": { + "title": { + "en": "dsh-mnemon", + "zh": "可组合记忆 (dsh-mnemon)" + }, + "description": { + "en": "Composable, view-based memory for DeepSeek Harness. Pluggable sources and strategies, with three-tier memory out of the box.", + "zh": "面向 DeepSeek Harness 的可组合视图记忆。记忆来源与策略可插拔,开箱即用提供三层记忆。" + } + } +} diff --git a/dsh/docs/pr-assets/composition-board-20260927/README.md b/dsh/docs/pr-assets/composition-board-20260927/README.md new file mode 100644 index 000000000..0d090b305 --- /dev/null +++ b/dsh/docs/pr-assets/composition-board-20260927/README.md @@ -0,0 +1,50 @@ +# Memory composition drawn from component declarations + +[简体中文](./README.zh-CN.md) + +Tested implementation: `b348fffa`, stacked on the component linkage record ([record](../plugin-linkage-20260927/README.md)). macOS 15.6, Node 25.1.0, published DSH 0.1.7-rc.2, headless Chrome at 1280 × 860 (and 420 × 900). Each run starts from a fresh isolated WebUI fixture with the Starter's defaults and its test model. No personal memory or credentials were used. + +## The board + +Groups come from the roles components declare; names and descriptions come from each component's own declaration. Nothing is said while memory is composed as chosen. + +| Default | Rows name what they relate to | +|---|---| +| ![Board](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composition-board-20260927/board-default.png) | ![Relation chips](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composition-board-20260927/board-relations.png) | + +## A component's page + +Selecting a row opens its page: state and switch, origin and package, relations, and what its switch would also move. The gear opens the same page at its declared options. + +| Relations | Declared options | +|---|---| +| ![Memory Spaces page](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composition-board-20260927/board-details.png) | ![Active capture options](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composition-board-20260927/board-options.png) | + +## Switches that move others, and problems + +| Turning off Memory Spaces takes Active capture along | Memory not in use: the one fix | +|---|---| +| ![Cascade with Undo](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composition-board-20260927/board-cascade.png) | ![Problem line](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composition-board-20260927/board-memory-off.png) | + +The Memory System shows a layer whose Source component is off as off: + +![Project Documents off](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composition-board-20260927/workspace-layer-off.png) + +## Dark theme and narrow column + +| Board | Page | Cascade | +|---|---|---| +| ![Board, dark](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composition-board-20260927/dark-board-default.png) | ![Page, dark](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composition-board-20260927/dark-board-details.png) | ![Cascade, dark](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composition-board-20260927/dark-board-cascade.png) | + +| Board, 420 px | Page, 420 px | +|---|---| +| ![Board, narrow](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composition-board-20260927/narrow-board-default.png) | ![Page, narrow](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composition-board-20260927/narrow-board-details.png) | + +## Verification + +- Unit tests cover switch planning (dependencies, exclusive claims, extension slots, main Strategy switches, unmanaged Sources), relations, option writes with Undo, the board (selector, groups by role, unknown roles, search, relation chips, pages, options, problem line) and the Memory System's off layers. +- WebUI: 7 light, 3 dark and 2 narrow states captured by a scripted walk on a fresh fixture, with no console errors; the same states were driven by hand in the in-app browser, including an option write and reset on the real Host. + +## Limits + +Component-owned settings still sit in page-level groups: Memory providers and embedding belong to Memory Spaces, Background tasks to Default three-tier. Moving them into their components' pages through a contribution slot is the next change. The fixture's shipped enhancements all fit any main Strategy, so the closed group for other Strategies and the search are covered by unit tests only. diff --git a/dsh/docs/pr-assets/composition-board-20260927/README.zh-CN.md b/dsh/docs/pr-assets/composition-board-20260927/README.zh-CN.md new file mode 100644 index 000000000..fa7d68bdb --- /dev/null +++ b/dsh/docs/pr-assets/composition-board-20260927/README.zh-CN.md @@ -0,0 +1,50 @@ +# 由组件声明生成的记忆组合 + +[English](./README.md) + +实测实现:`b348fffa`,叠加在组件联动记录之上([记录](../plugin-linkage-20260927/README.zh-CN.md))。macOS 15.6、Node 25.1.0、正式发布的 DSH 0.1.7-rc.2,headless Chrome 1280 × 860(及 420 × 900)。每次运行都从全新的隔离 WebUI 夹具开始,使用 Starter 默认配置与测试模型,不涉及个人记忆或凭据。 + +## 面板 + +分组来自组件声明的角色,名称与说明来自各组件自己的声明。记忆按所选方式组合时,面板不额外提示。 + +| 默认 | 行内点名相关组件 | +|---|---| +| ![面板](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composition-board-20260927/board-default.png) | ![关联标签](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composition-board-20260927/board-relations.png) | + +## 组件详情 + +点击一行打开组件详情:状态与开关、来源与包名、关系,以及开关还会带动的组件。齿轮打开同一详情中的选项。 + +| 关系 | 声明的选项 | +|---|---| +| ![记忆空间详情](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composition-board-20260927/board-details.png) | ![主动记录选项](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composition-board-20260927/board-options.png) | + +## 带动其他组件的开关与问题提示 + +| 关闭记忆空间会一同关闭主动记录 | 记忆未生效时的唯一修复 | +|---|---| +| ![连带关闭与撤销](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composition-board-20260927/board-cascade.png) | ![问题提示](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composition-board-20260927/board-memory-off.png) | + +记忆系统把 Source 组件已关闭的层显示为“已关闭”: + +![项目档案已关闭](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composition-board-20260927/workspace-layer-off.png) + +## 深色与窄列 + +| 面板 | 详情 | 连带关闭 | +|---|---|---| +| ![面板,深色](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composition-board-20260927/dark-board-default.png) | ![详情,深色](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composition-board-20260927/dark-board-details.png) | ![连带关闭,深色](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composition-board-20260927/dark-board-cascade.png) | + +| 面板,420 px | 详情,420 px | +|---|---| +| ![面板,窄列](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composition-board-20260927/narrow-board-default.png) | ![详情,窄列](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/composition-board-20260927/narrow-board-details.png) | + +## 验证 + +- 单元测试覆盖开关规划(依赖、独占能力、增强槽位、切换主策略、未托管的 Source)、关系、带撤销的选项写入、面板(选择器、按角色分组、未知角色、搜索、关联标签、详情、选项、问题提示)以及记忆系统中已关闭的层。 +- WebUI:脚本在全新夹具上录制 7 个浅色、3 个深色与 2 个窄列状态,无控制台错误;同样的状态也在应用内浏览器中手动操作过,包括在真实 Host 上写入并恢复默认选项。 + +## 限制 + +组件自己的设置仍在页面级分组中:记忆 Provider 与嵌入属于“记忆空间”,后台任务属于“默认三层”。下一步通过贡献插槽把它们移入各自组件的详情。夹具中随附的增强都适用于任一主策略,因此“适用于其他主策略”的折叠分组与搜索仅由单元测试覆盖。 diff --git a/dsh/docs/pr-assets/conversation-tab-workspace/README.md b/dsh/docs/pr-assets/conversation-tab-workspace/README.md new file mode 100644 index 000000000..35bc1ce76 --- /dev/null +++ b/dsh/docs/pr-assets/conversation-tab-workspace/README.md @@ -0,0 +1,74 @@ +# Memory System in a conversation tab without a loaded Agent + +[简体中文](./README.zh-CN.md) | [Verification data](./verification.json) + +With **Memory System opens in → Conversation tab**, Project Documents could fail with `memory Source is unavailable in the requested management scope: source:include:mnemon-source-documents`. Status meanwhile read "Waiting for workspace". The Host found a conversation's workspace only through that conversation's loaded Agent. DSH shows a conversation before it resumes the Agent, and some conversations it shows but cannot resume at all, for example one whose Agent preset is no longer in the profile. Until then, the Memory System had no workspace. + +Baseline: `main` `06182f4d`, the published dsh-mnemon 0.5.21. Fix: `e0f43ab186a84b586e28ba1644d75daa963daee5`. The runs took place on 2026-10-02 (Asia/Shanghai): +- macOS 15.6 arm64 and Node 24.19.0; +- headless Chrome 154 at 1280×800, zh-CN, light; +- the repository's disposable WebUI fixture with its loopback model stub, on DSH 0.2.0-rc.2 and on 0.1.7-rc.2. + +No model is called, and all memories are synthetic. + +## Reproduction + +The fixture creates two conversations under the test preset. Renaming that preset in the disposable profile and restarting DSH leaves the conversations listed and readable, but DSH cannot resume their Agents. Opening one, the Memory System tab, then Status and Project Documents gives the reported state on both hosts: + +| Before: Status | Before: Project Documents | +|---|---| +| ![Status: Project Documents waiting for workspace](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/conversation-tab-workspace/before-status.jpg) | ![Project Documents: memory Source is unavailable in the requested management scope](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/conversation-tab-workspace/before-documents.jpg) | + +**New Document** then failed with `current DSH agent is not live; reopen or resume the conversation and try again`. In the sidebar Memory System, Documents loaded through the selected workspace, but creating a Document failed the same way. + +## Fix + +- **Workspace from DSH's registry.** DSH's workspace registry lists every session, loaded or not (`Workspace.sessionIds`, identical in DSH 0.1.7-rc.2 and 0.2). When a request names a session whose Agent is not loaded, the Host takes that session's workspace from the registry. The request is then aligned with the conversation, as it would be with a loaded Agent. +- **Writes without the conversation Agent.** A new Document and Remember used the conversation's Agent whenever the page was aligned with it. They now do so only when that Agent is loaded; otherwise they write to the Source in the conversation's workspace, as a page without a session does. +- **Task Agents where an Agent is needed.** When Documents are full, a task Agent in the conversation's workspace archives the least recently used Document and then writes, as the conversation's Agent does. A task Agent also chooses a new Memory Space's Provider. Asking an Agent in Memory Spaces and tidying their names and descriptions already worked this way. +- **The right root in workspace storage.** With workspace or centralized storage, such a tab used to read and write Runtime memory and Memory Spaces in the directory DSH was started from. It now uses the conversation's workspace. Entries saved the old way stay in that directory. + +| After: Status | After: a Document created there | +|---|---| +| ![Status: Project Documents counted](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/conversation-tab-workspace/after-status.jpg) | ![Project Documents loaded, with the new Document](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/conversation-tab-workspace/after-documents.jpg) | + +On both hosts, the same conversation: +- shows Status with the Documents count; +- loads Project Documents and creates a Document; +- adds a Runtime memory entry ([Runtime](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/conversation-tab-workspace/after-runtime.jpg)); +- opens Memory Spaces. + +The sidebar Memory System creates the Document too. None of these runs logged a console error. + +## Switching + +A scripted matrix ran on each host with the fix, without renaming the preset. It records visible errors and the Status Documents card: + +1. every Memory System tab in a loaded conversation; +2. a switch to another conversation, opening Project Documents at once; +3. a switch to a conversation that remembers its Memory System tab, after a DSH restart; +4. six fast switches between two conversations across tabs; +5. a Document created in a conversation just switched to; +6. Conversation tab to Sidebar and back while the Memory System is open, through every tab; +7. Sidebar mode switching conversations after a restart; +8. a new conversation before and after its first message; +9. a DSH restart with Project Documents open. + +All nine passed on DSH 0.2.0-rc.2 and on 0.1.7-rc.2, with no visible error and no console error. Without the fix, scenarios 1–3 and 9 also passed on DSH 0.2.0-rc.2, because the fixture's DSH resumes a selected conversation within about a second. The reproduction above covers the state those switches cannot reach. + +## Automated checks + +- New tests route sessions whose Agents are not loaded, in global, workspace and per-workspace storage, through their registry workspaces: + - Status and the Documents management read succeed; + - a manual Document write succeeds; + - a session no workspace lists keeps no workspace. +- Another test sends Remember and a new Document straight to the Sources when the session's Agent is not loaded. +- Further tests cover the task Agents: a full Documents capacity goes to one, while other failures and a page without a conversation keep the Source's answer; Provider placement goes to one. The dashboard aligns such a session with its registry workspace. +- `pnpm run verify` passes: docs (2,981 local links), typecheck, the deterministic build, build, typecheck and tests for all 17 plugins, root tests (113 files, 1,566 passed, 6 skipped), Headless activation, package contents (1,500,772 unpacked bytes, within the 1,501,000 budget), public entries, publint and attw. + +## Limits + +- The reproduction reaches the unresumable state by renaming the fixture's preset. Other ways to get there, such as a session DSH has not resumed yet, use the same Host path, which the unit tests cover. +- A session that no workspace lists still has no workspace. Archived sessions keep their place in the registry (DSH 0.1.7-rc.2 and 0.2.0-rc.2), so they route to their workspace too. +- Work that runs inside the conversation's own Agent, such as its idle review, still waits for DSH to resume the Agent. +- The task Agent paths are covered by unit tests; the live runs above did not fill Documents to capacity. diff --git a/dsh/docs/pr-assets/conversation-tab-workspace/README.zh-CN.md b/dsh/docs/pr-assets/conversation-tab-workspace/README.zh-CN.md new file mode 100644 index 000000000..942776a8f --- /dev/null +++ b/dsh/docs/pr-assets/conversation-tab-workspace/README.zh-CN.md @@ -0,0 +1,74 @@ +# 会话 Agent 未加载时的会话标签页记忆系统 + +[English](./README.md) | [验证数据](./verification.json) + +在**记忆系统入口 → 会话标签页**模式下,项目档案可能报 `memory Source is unavailable in the requested management scope: source:include:mnemon-source-documents`,同时状态页显示“等待工作区”。原因是 Host 只能通过会话已加载的 Agent 找到它所属的工作区。DSH 会在恢复会话的 Agent 之前就显示这个会话;有些会话能显示却根本无法恢复,例如其 Agent 预设已不在当前 profile 中。在 Agent 加载之前,记忆系统拿不到工作区。 + +基线:`main` `06182f4d`,即已发布的 dsh-mnemon 0.5.21。修复:`e0f43ab186a84b586e28ba1644d75daa963daee5`。验证于 2026-10-02(北京时间): +- macOS 15.6 arm64 与 Node 24.19.0; +- 无头 Chrome 154,1280×800,zh-CN,浅色; +- 仓库的一次性 WebUI 测试环境及其本机模型桩,分别运行在 DSH 0.2.0-rc.2 与 0.1.7-rc.2 上。 + +未调用真实模型,记忆均为合成数据。 + +## 复现 + +测试环境先用测试预设创建两个会话。在一次性 profile 中给这个预设改名并重启 DSH 后,会话仍会列出、内容可读,但 DSH 无法恢复它们的 Agent。打开其中一个会话的记忆系统标签,再看状态页和项目档案,两个宿主上都出现了反馈中的状态: + +| 修复前:状态页 | 修复前:项目档案 | +|---|---| +| ![状态页:项目档案等待工作区](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/conversation-tab-workspace/before-status.jpg) | ![项目档案:memory Source is unavailable in the requested management scope](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/conversation-tab-workspace/before-documents.jpg) | + +接着**新建档案**报 `current DSH agent is not live; reopen or resume the conversation and try again`。在侧边栏中的记忆系统里,项目档案能通过所选工作区加载,但新建档案同样失败。 + +## 修复 + +- **从 DSH 注册表取工作区。** 无论会话是否已加载,DSH 的工作区注册表都列有它(`Workspace.sessionIds`,DSH 0.1.7-rc.2 与 0.2 中相同)。请求中的会话 Agent 未加载时,Host 从注册表取该会话的工作区;请求与会话保持对齐,就像 Agent 已加载一样。 +- **写入不再依赖会话 Agent。** 此前只要页面与会话对齐,新建档案和存入记忆就会交给会话的 Agent。现在只有该 Agent 已加载时才这样做;否则直接写入会话所属工作区的 Source,与没有会话的页面一致。 +- **需要 Agent 时改用任务 Agent。** 项目档案已满时,由会话所属工作区中的任务 Agent 先归档最久未用的档案再写入,与会话的 Agent 做法相同。新建记忆空间时也由任务 Agent 选择 Provider。在记忆空间中询问 Agent、整理名称与描述,此前就是这样工作的。 +- **工作区存储使用正确的根目录。** 使用工作区或集中存储时,这种标签页此前在 DSH 启动时所在的目录中读写运行时记忆和记忆空间,现在改用会话所属的工作区。按旧方式保存的条目仍留在那个目录中。 + +| 修复后:状态页 | 修复后:在这里新建的档案 | +|---|---| +| ![状态页:项目档案已计数](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/conversation-tab-workspace/after-status.jpg) | ![项目档案已加载,并出现新建的档案](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/conversation-tab-workspace/after-documents.jpg) | + +两个宿主上,同一个会话: +- 状态页显示项目档案数量; +- 项目档案能加载并新建档案; +- 能添加运行时记忆([运行时记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/conversation-tab-workspace/after-runtime.jpg)); +- 能打开记忆空间。 + +侧边栏中的记忆系统同样能新建档案。所有运行都没有控制台错误。 + +## 切换测试 + +每个宿主上都用修复后的构建运行了一组脚本化场景(不改名预设),记录可见错误与状态页的项目档案卡片: + +1. 已加载会话中依次打开记忆系统的每个标签; +2. 切换到另一个会话后立即打开项目档案; +3. DSH 重启后,切换到停留在记忆系统标签的会话; +4. 在两个会话之间、跨标签快速切换六次; +5. 在刚切换到的会话中新建档案; +6. 记忆系统打开时,从会话标签页切到侧边栏再切回,并走遍每个标签; +7. 侧边栏模式下重启后切换会话; +8. 新会话在发送第一条消息前后; +9. 项目档案打开时重启 DSH。 + +九项在 DSH 0.2.0-rc.2 与 0.1.7-rc.2 上全部通过,没有可见错误或控制台错误。未修复时,场景 1–3 与 9 在 DSH 0.2.0-rc.2 上同样通过,因为测试环境的 DSH 会在约一秒内恢复所选会话。上面的复现覆盖了这些切换到达不了的状态。 + +## 自动检查 + +- 新增测试在全局、工作区与按工作区三种存储方式下,按注册表为 Agent 未加载的会话路由: + - 状态与项目档案的管理读取成功; + - 手动写入档案成功; + - 不在任何工作区中的会话仍然没有工作区。 +- 另一项测试确认会话 Agent 未加载时,存入记忆与新建档案直接写入 Source。 +- 其他测试覆盖任务 Agent:项目档案容量已满时交给任务 Agent,其他失败和没有会话的页面仍返回 Source 的结果;Provider 选择交给任务 Agent。看板会把这种会话按注册表工作区对齐。 +- `pnpm run verify` 通过:文档(2,981 个本地链接)、类型检查、确定性构建,全部 17 个插件的构建、类型检查与测试,根测试(113 个文件,1,566 通过,6 跳过),Headless 激活,包内容(解压后 1,500,772 字节,在 1,501,000 预算内),公开入口、publint 与 attw。 + +## 限制 + +- 复现通过给测试预设改名来得到无法恢复的状态。其他途径,例如 DSH 尚未恢复的会话,走的是同一条 Host 路径,由单元测试覆盖。 +- 不在任何工作区中的会话仍然没有工作区。已归档的会话在注册表中保留原位置(DSH 0.1.7-rc.2 与 0.2.0-rc.2),因此同样路由到其工作区。 +- 在会话自己的 Agent 中运行的工作,例如它的后台审查,仍要等 DSH 恢复 Agent。 +- 任务 Agent 路径由单元测试覆盖;上述实测没有把项目档案写到容量上限。 diff --git a/dsh/docs/pr-assets/conversation-tab-workspace/verification.json b/dsh/docs/pr-assets/conversation-tab-workspace/verification.json new file mode 100644 index 000000000..910ba3b57 --- /dev/null +++ b/dsh/docs/pr-assets/conversation-tab-workspace/verification.json @@ -0,0 +1,218 @@ +{ + "date": "2026-10-02", + "revisions": { + "baseline": "06182f4d828efb1569d2f68a2df5e0e1d1c69f89 (main, published dsh-mnemon 0.5.21)", + "fix": "e0f43ab186a84b586e28ba1644d75daa963daee5" + }, + "environment": { + "os": "macOS 15.6 arm64", + "node": "24.19.0", + "browser": "headless Chrome 154, 1280x800, device scale 1, zh-CN, light", + "hosts": [ + "@deepseek-ai/dsh 0.2.0-rc.2 (scripts/serve-e2e.mjs pointed at it)", + "@deepseek-ai/dsh 0.1.7-rc.2 (repository pin, scripts/serve-e2e.mjs)" + ], + "fixture": "disposable DSH_HOME, data directory and workspace with the loopback model stub; two conversations created under the test preset" + }, + "reproduction": "The test preset is renamed in the disposable profile and DSH restarted: the conversations stay listed and readable, but DSH cannot resume their Agents. The conversation is opened, then its Memory System (Conversation tab mode, and Sidebar mode for writes), Status, Project Documents with New Document, Runtime memory with Add, and Memory Spaces.", + "unresumableConversation": { + "dsh-0.2.0-rc.2": { + "before": { + "conversationTab": { + "statusCard": "项目档案 等待工作区 绑定活动会话后可用", + "documentErrors": [ + "memory Source is unavailable in the requested management scope: source:include:mnemon-source-documents" + ], + "created": false, + "writeErrors": [ + "current DSH agent is not live; reopen or resume the conversation and try again" + ], + "consoleErrors": [] + }, + "sidebar": { + "statusCard": "项目档案 8 份活跃 · 0 份归档 3.2 KB / 10.0 MB 活跃容量", + "documentErrors": [], + "created": false, + "writeErrors": [ + "current DSH agent is not live; reopen or resume the conversation and try again" + ], + "consoleErrors": [] + } + }, + "after": { + "conversationTab": { + "statusCard": "项目档案 5 份活跃 · 0 份归档 2.0 KB / 10.0 MB 活跃容量", + "documentErrors": [], + "created": true, + "writeErrors": [], + "runtimeAdded": true, + "runtimeWriteErrors": [], + "spacesErrors": [], + "consoleErrors": [] + }, + "sidebar": { + "statusCard": "项目档案 6 份活跃 · 0 份归档 2.4 KB / 10.0 MB 活跃容量", + "documentErrors": [], + "created": true, + "writeErrors": [], + "runtimeAdded": true, + "runtimeWriteErrors": [], + "spacesErrors": [], + "consoleErrors": [] + } + } + }, + "dsh-0.1.7-rc.2": { + "before": { + "conversationTab": { + "statusCard": "项目档案 等待工作区 绑定活动会话后可用", + "documentErrors": [ + "memory Source is unavailable in the requested management scope: source:include:mnemon-source-documents" + ], + "created": false, + "writeErrors": [ + "current DSH agent is not live; reopen or resume the conversation and try again" + ], + "consoleErrors": [] + }, + "sidebar": { + "statusCard": "项目档案 1 份活跃 · 0 份归档 407 B / 10.0 MB 活跃容量", + "documentErrors": [], + "created": false, + "writeErrors": [ + "current DSH agent is not live; reopen or resume the conversation and try again" + ], + "consoleErrors": [] + } + }, + "after": { + "conversationTab": { + "statusCard": "项目档案 2 份活跃 · 0 份归档 823 B / 10.0 MB 活跃容量", + "documentErrors": [], + "created": true, + "writeErrors": [], + "runtimeAdded": true, + "runtimeWriteErrors": [], + "spacesErrors": [], + "consoleErrors": [] + }, + "sidebar": { + "statusCard": "项目档案 3 份活跃 · 0 份归档 1.2 KB / 10.0 MB 活跃容量", + "documentErrors": [], + "created": true, + "writeErrors": [], + "runtimeAdded": true, + "runtimeWriteErrors": [], + "spacesErrors": [], + "consoleErrors": [] + } + } + }, + "note": "Before, a failed New Document leaves its dialog open, so the runs stop there and Runtime and Memory Spaces were not reached." + }, + "switchingMatrix": { + "dsh-0.2.0-rc.2": { + "scenarios": [ + { + "name": "builtin-tabs-live", + "pass": true + }, + { + "name": "builtin-switch-to-unloaded-session", + "pass": true + }, + { + "name": "switch-to-session-remembered-on-memory-tab", + "pass": true + }, + { + "name": "builtin-rapid-switching", + "pass": true + }, + { + "name": "builtin-write-in-unloaded-session", + "pass": true + }, + { + "name": "mode-switch-builtin-to-sidebar-and-back", + "pass": true + }, + { + "name": "sidebar-switch-sessions", + "pass": true + }, + { + "name": "builtin-new-draft-then-send", + "pass": true + }, + { + "name": "restart-with-memory-tab-open", + "pass": true + } + ], + "consoleErrors": [] + }, + "dsh-0.1.7-rc.2 (fresh fixture)": { + "scenarios": [ + { + "name": "builtin-tabs-live", + "pass": true + }, + { + "name": "builtin-switch-to-unloaded-session", + "pass": true + }, + { + "name": "switch-to-session-remembered-on-memory-tab", + "pass": true + }, + { + "name": "builtin-rapid-switching", + "pass": true + }, + { + "name": "builtin-write-in-unloaded-session", + "pass": true + }, + { + "name": "mode-switch-builtin-to-sidebar-and-back", + "pass": true + }, + { + "name": "sidebar-switch-sessions", + "pass": true + }, + { + "name": "builtin-new-draft-then-send", + "pass": true + }, + { + "name": "restart-with-memory-tab-open", + "pass": true + } + ], + "consoleErrors": [] + } + }, + "designReview": { + "date": "2026-10-03", + "followUps": [ + "With workspace or centralized storage, a tab whose Agent is not loaded used the DSH launch directory as its root on main; the fix routes it to the conversation workspace. Entries saved the old way stay in the launch directory.", + "A full Documents capacity in such a tab runs under a task Agent that archives the least recently used Document first, as the conversation Agent does (tests/rpc.spec.ts).", + "Automatic Provider placement for a new Memory Space runs under a task Agent in that state (tests/rpc.spec.ts).", + "The dashboard aligns such a session with its registry workspace (tests/view-management.spec.ts).", + "Archived sessions keep their place in the DSH 0.1.7-rc.2 and 0.2.0-rc.2 registries, so they route to their workspace too." + ], + "verify": { + "command": "pnpm run verify", + "rootTests": { + "files": 113, + "passed": 1566, + "skipped": 6 + }, + "docsLocalLinks": 2981, + "packageUnpackedBytes": 1500772, + "packageBudget": 1501000 + } + } +} diff --git a/dsh/docs/pr-assets/data-directory-20260927/README.md b/dsh/docs/pr-assets/data-directory-20260927/README.md new file mode 100644 index 000000000..c5e5fa681 --- /dev/null +++ b/dsh/docs/pr-assets/data-directory-20260927/README.md @@ -0,0 +1,41 @@ +# Data directory: Default or Custom + +[简体中文](./README.zh-CN.md) + +Tested implementation: `edbffc58`, stacked on the Strategy names ([record](../strategy-names-20260927/README.md)). macOS 15.6, Node 25.1.0, published DSH 0.1.7-rc.2, headless Chrome at 1280 × 860 (and 420 × 900). Each run walks an isolated WebUI fixture from the Starter's defaults with its test model; the path shown is that fixture's disposable data directory. No personal memory or credentials were used. + +## One directory on the row + +The row showed where memory lives and, under it, an empty field that meant the default. Data directory is now a choice between Default and Custom, and shows only the directory memory uses. The default path sits under the title, with its end kept in view. + +| Light | Dark | +|---|---| +| ![Default directory](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/data-directory-20260927/directory-default.png) | ![Default directory, dark](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/data-directory-20260927/dark-directory-default.png) | + +## Custom takes a path + +Choosing Custom opens an empty field with the cursor in it. Apply waits for a path without calling the empty field a mistake; a path typed wrong says why. + +| Custom, empty | Typed wrong | +|---|---| +| ![Custom with an empty field](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/data-directory-20260927/directory-custom.png) | ![A relative path](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/data-directory-20260927/directory-invalid.png) | + +| A path ready to apply | Typed wrong, dark | +|---|---| +| ![An absolute path](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/data-directory-20260927/directory-typed.png) | ![A relative path, dark](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/data-directory-20260927/dark-directory-invalid.png) | + +## Narrow + +| Default | Custom | +|---|---| +| ![Default, narrow](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/data-directory-20260927/narrow-directory-default.png) | ![Custom, narrow](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/data-directory-20260927/narrow-directory-custom.png) | + +## Verification + +- Unit tests cover the default path under the title, this workspace's directory under a central root, a custom path shown only in its field, Custom focusing an empty field with Apply disabled, returning to Default by its choice (unsetting a global directory, emptying a central root), relative, Windows drive and UNC paths, legacy Pack migration and read-only settings; a Host test covers the default directory the pack target reports. +- Full `pnpm run verify`, `pnpm run verify:plugins`, `pnpm run verify:docs` and `pnpm run release:intent` passed. +- WebUI: 4 light, 2 dark and 2 narrow states captured by a scripted walk with no console errors; the same flow was driven by hand in the in-app browser. + +## Limits + +Before a scope change is applied, the row names the destination only for the default directory; for Workspace and Centralized the Apply line says what applying does. The ZIP section and the Status page keep their own wording of the current root. diff --git a/dsh/docs/pr-assets/data-directory-20260927/README.zh-CN.md b/dsh/docs/pr-assets/data-directory-20260927/README.zh-CN.md new file mode 100644 index 000000000..ae9076614 --- /dev/null +++ b/dsh/docs/pr-assets/data-directory-20260927/README.zh-CN.md @@ -0,0 +1,41 @@ +# 数据目录:默认或自定义 + +[English](./README.md) + +实测实现:`edbffc58`,叠加在策略名称之上([记录](../strategy-names-20260927/README.zh-CN.md))。macOS 15.6、Node 25.1.0、已发布的 DSH 0.1.7-rc.2、headless Chrome 1280 × 860(以及 420 × 900)。每次运行都从 Starter 默认配置与其测试模型的隔离 WebUI fixture 开始,界面中的路径是该 fixture 的临时数据目录,未使用个人记忆或凭据。 + +## 这一行只显示一个目录 + +此前这一行先显示当前位置,下方再给一个留空即默认的输入框。现在“数据目录”在“默认”与“自定义”之间选择,只显示记忆正在使用的目录。默认目录显示在标题下方,较长时保留路径末尾。 + +| 浅色 | 深色 | +|---|---| +| ![默认目录](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/data-directory-20260927/directory-default.png) | ![默认目录(深色)](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/data-directory-20260927/dark-directory-default.png) | + +## 自定义需要填写路径 + +选择“自定义”后出现空输入框,光标已在其中。填写路径前“应用”不可用,但不会把空输入框标成错误;路径填写有误时说明原因。 + +| 自定义,尚未填写 | 填写有误 | +|---|---| +| ![自定义的空输入框](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/data-directory-20260927/directory-custom.png) | ![相对路径](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/data-directory-20260927/directory-invalid.png) | + +| 可以应用的路径 | 填写有误(深色) | +|---|---| +| ![绝对路径](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/data-directory-20260927/directory-typed.png) | ![相对路径(深色)](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/data-directory-20260927/dark-directory-invalid.png) | + +## 窄屏 + +| 默认 | 自定义 | +|---|---| +| ![默认(窄屏)](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/data-directory-20260927/narrow-directory-default.png) | ![自定义(窄屏)](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/data-directory-20260927/narrow-directory-custom.png) | + +## 验证 + +- 单元测试覆盖:标题下方的默认目录、集中存储下本工作区的目录、自定义目录只出现在输入框中、选择“自定义”后聚焦空输入框且“应用”不可用、通过选择“默认”回到默认目录(全局范围取消目录,集中存储置空根目录)、相对路径与 Windows 盘符及 UNC 路径、旧版 Pack 迁移与只读设置;Host 测试覆盖 pack target 返回的默认目录。 +- 完整的 `pnpm run verify`、`pnpm run verify:plugins`、`pnpm run verify:docs` 与 `pnpm run release:intent` 均通过。 +- WebUI:脚本化走查截取 4 个浅色、2 个深色与 2 个窄屏状态,控制台无错误;同一流程也在应用内浏览器中手动操作过。 + +## 限制 + +范围改动应用之前,只有默认目录会显示将使用的位置;“工作区”与“集中存储”由“应用”一行说明应用后的效果。ZIP 区域与状态页仍按各自的措辞显示当前根目录。 diff --git a/dsh/docs/pr-assets/desktop-live-activation/README.md b/dsh/docs/pr-assets/desktop-live-activation/README.md new file mode 100644 index 000000000..afafb1be2 --- /dev/null +++ b/dsh/docs/pr-assets/desktop-live-activation/README.md @@ -0,0 +1,65 @@ +# Desktop and official WebUI activation + +[中文](README.zh-CN.md) + +Verified on 2026-09-28 using isolated profiles and synthetic data. All changes are in dsh-mnemon; neither DSH nor the user's Desktop installation was modified. + +## Reproduction and fix + +The baseline is published dsh-mnemon `0.5.17` on unmodified DSH `0.1.7-rc.2`. The profile exposes only the root bundle; its 17 component packages exist in a separate installation generation. Start DSH without selecting Mnemon, then enable it through the official plugin page. Runtime, Documents, Memory Spaces and the default Strategy fail with `ERR_MODULE_NOT_FOUND` because the running host still has its startup resolution table. + +The candidate prepares Starter dependency resolution before native group activation. A caller-owned Web transport scope also registers Mnemon RPC routes when the official Connection is already running. Without this second fix, a fresh WebUI install activates the components but its page RPC returns HTTP 405. + +Existing Entry IDs, the core gate, independent component choices, native configuration schemas and private Provider children remain intact. Previously loaded bundles retain their dependency routes even when disabled. Changes to packages Node has already loaded can still require a host restart. + +| Published baseline: missing components and settings | Candidate: components running and settings available | +| --- | --- | +| ![Before](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/desktop-live-activation/before.png) | ![After](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/desktop-live-activation/after.png) | + +## Candidate and environments + +- Base: `accd87513c6330fbf8af64f4352657dcca390991` plus this PR. The candidate retains the unreleased package version `0.5.17`; the changeset requests a patch release. +- Official WebUI: npm DSH `0.1.7-rc.2`, Node `24.20.0`, clean profile, all 18 candidate tarballs installed through the official **Add plugin** flow. A loopback registry serves the candidate packages; no source checkout is linked into the profile. +- Desktop: unmodified DSH `0.1.7-rc.2` bundled with Desktop `0.10.0`, run in a real Electron `44.0.0` main process with Node `24.18.1` using the repository's Electron fixture. `ELECTRON_RUN_AS_NODE` is unset. This checks the bundled host and official WebUI, not a complete Desktop installer update. +- Native CLI: Mnemon `0.2.9` with isolated data. No remote model or third-party Provider was needed for this activation regression. +- [Sanitized verification record](validation.json) records the candidate digest, CLI result and unchanged WebUI host PID. + +## Real WebUI acceptance + +1. Start the official WebUI with no Mnemon installed. The [clean baseline](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/desktop-live-activation/web-before-install.png) is a separate fresh profile with the same host version. +2. Install the candidate root tarball through **Add plugin**, then choose **Enable now** in the [installation result](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/desktop-live-activation/web-installed.png). The host remains running throughout installation, activation and the operations below. +3. Open Memory System: [Status is healthy](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/desktop-live-activation/web-status.png), with the three Sources and default Strategy available. +4. Create a Runtime entry and read it back: [Runtime result](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/desktop-live-activation/web-runtime.png). +5. Create and read a Project Document: [Documents result](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/desktop-live-activation/web-documents.png). +6. Create and activate a Native Memory Space in the UI. Write a synthetic fact with `mnemon remember`, confirm it with CLI `recall`, then retrieve that exact fact using **Recall → Basic match** in the [official WebUI](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/desktop-live-activation/web-recall.png). +7. Disable and re-enable the bundle. The sidebar returns, Runtime and Documents retain their content, and the [same active Native space and memory remain](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/desktop-live-activation/web-reenabled.png). The original host PID is still alive; the fixture launched it only once. + +CLI commands used, with `MNEMON_DATA_DIR` pointing to the isolated test directory: + +```bash +mnemon --data-dir "$MNEMON_DATA_DIR" --store default remember \ + 'LIVE_INSTALL_ACCEPTANCE: Native CLI and official WebUI share durable memory immediately after installation.' \ + --cat fact --no-diff +mnemon --data-dir "$MNEMON_DATA_DIR" --store default recall LIVE_INSTALL_ACCEPTANCE --basic +``` + +![Official WebUI retrieves the fact written by the native CLI](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/desktop-live-activation/web-recall.png) + +## Electron acceptance + +Start the bundled DSH host inside Electron with the installed bundle unselected, then enable it using the official WebUI. The [component list](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/desktop-live-activation/after-components.png), [healthy Status](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/desktop-live-activation/electron-status.png) and [new Native store](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/desktop-live-activation/electron-native.png) show successful activation and CLI subprocess execution. The existing upstream `cordis:group` display issue remains documented in [compatibility notes](../../en/reference/compatibility.md#dsh-017-bundle-component-list); it does not represent a stopped Source or Strategy. + +## Automated verification + +```bash +pnpm run verify +pnpm run verify:plugins +``` + +Both passed on Node `24.20.0`: + +- Root suite: 1,513 passed, 6 conditional integration tests skipped. The skipped cases require opt-in Flash stress/quality runs or a dedicated Native capacity fixture; the real Native activation path was checked above. +- Build, type checks, documentation links, package contents, public entry imports, `publint` and `attw` passed. The package contains 55 files and is 1,471,089 bytes unpacked. +- Headless activation exposes 37 tools, including 8 representative Mnemon tools. Settings migration, restart and the legacy core-disable gate pass. +- All 17 independent plugin packages pass standalone installation/build/type checks and their tests, with environment-gated Native/Windows/OpenViking integrations skipped. All 18 packed artifacts pass SDK/Client composition checks, optional Strategy composition and root-only Starter installation. +- Published-host lifecycle regressions cover late activation with isolated dependencies, another bundle disabled since startup, independent component choices, native configuration schema discovery, core/bundle toggles and restart persistence. The official Connection regression confirms late RPC registration and removal when Mnemon is disposed. diff --git a/dsh/docs/pr-assets/desktop-live-activation/README.zh-CN.md b/dsh/docs/pr-assets/desktop-live-activation/README.zh-CN.md new file mode 100644 index 000000000..6cf859936 --- /dev/null +++ b/dsh/docs/pr-assets/desktop-live-activation/README.zh-CN.md @@ -0,0 +1,65 @@ +# Desktop 与官方 WebUI 启用验收 + +[English](README.md) + +2026-09-28 使用独立 profile 和测试数据验收。全部修改位于 dsh-mnemon,未修改 DSH 或用户实际的 Desktop 安装。 + +## 复现与修复 + +基线为已发布的 dsh-mnemon `0.5.17` 和未修改的 DSH `0.1.7-rc.2`。profile 只暴露根 bundle,17 个组件包实际位于独立安装 generation 中。宿主启动时不选中 Mnemon,再通过官方插件页启用:Runtime、Documents、Memory Spaces 和默认 Strategy 抛出 `ERR_MODULE_NOT_FOUND`,原因是运行中的宿主仍使用启动时的解析表。 + +候选修复先准备 Starter 的依赖解析,再启用原生分组。同时,Mnemon 自己持有 Web 传输作用域,即使官方 Connection 已运行,也能注册所需 RPC。缺少第二处修复时,官方 WebUI 首次安装后组件虽然启动,但页面 RPC 会返回 HTTP 405。 + +已有 Entry ID、核心总开关、组件独立选择、原生配置 schema 和私有 Provider 子项均保留。其他启动时已加载的 bundle 即使已停用,其依赖路径也继续保留。更新 Node 已加载的包仍可能需要重启宿主。 + +| 发布版:组件与设置不可用 | 候选修复:组件运行、设置正常 | +| --- | --- | +| ![修复前](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/desktop-live-activation/before.png) | ![修复后](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/desktop-live-activation/after.png) | + +## 候选制品与环境 + +- 基于 `accd87513c6330fbf8af64f4352657dcca390991` 加本 PR 修改。候选包仍标记为 `0.5.17`,尚未发布;changeset 请求补丁版本发布。 +- 官方 WebUI:npm DSH `0.1.7-rc.2`、Node `24.20.0`、全新 profile,通过官方“添加插件”流程安装全部 18 个候选 tarball。本地回环 registry 提供候选包,profile 中没有链接源码 checkout。 +- Desktop:Desktop `0.10.0` 随附的未修改 DSH `0.1.7-rc.2`,通过仓库 Electron fixture 在真实 Electron `44.0.0` 主进程、Node `24.18.1` 内运行,未设置 `ELECTRON_RUN_AS_NODE`。验收覆盖随附宿主与官方 WebUI,不代表完成 Desktop 安装程序的整体升级测试。 +- Native CLI:Mnemon `0.2.9`,数据目录独立。本次启动回归不需要远程模型或第三方 Provider。 +- [脱敏验收记录](validation.json)包含候选包摘要、CLI 结果和保持不变的 WebUI 宿主 PID。 + +## 真实 WebUI 验收 + +1. 启动未安装 Mnemon 的官方 WebUI。[空白安装基线](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/desktop-live-activation/web-before-install.png)来自同宿主版本的另一全新 profile。 +2. 通过“添加插件”安装候选根 tarball,在[安装结果](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/desktop-live-activation/web-installed.png)中点击“立即启用”。安装、启用及以下操作期间,宿主始终保持运行。 +3. 打开记忆系统:[状态正常](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/desktop-live-activation/web-status.png),三个 Source 和默认 Strategy 均可用。 +4. 新建并读取 Runtime 条目:[运行时记忆结果](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/desktop-live-activation/web-runtime.png)。 +5. 新建并读取项目档案:[档案结果](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/desktop-live-activation/web-documents.png)。 +6. 在界面中创建、激活 Native 记忆空间。通过 `mnemon remember` 写入测试事实,用 CLI `recall` 确认,再在[官方 WebUI](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/desktop-live-activation/web-recall.png)的“检索 → 基础匹配”中读取同一条事实。 +7. 停用再启用 bundle。侧栏恢复,Runtime 与 Documents 内容保留,[同一个已激活 Native 空间及记忆仍在](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/desktop-live-activation/web-reenabled.png)。原宿主 PID 仍存活,fixture 仅启动过一次宿主。 + +实际执行的 CLI 命令如下,`MNEMON_DATA_DIR` 指向独立测试目录: + +```bash +mnemon --data-dir "$MNEMON_DATA_DIR" --store default remember \ + 'LIVE_INSTALL_ACCEPTANCE: Native CLI and official WebUI share durable memory immediately after installation.' \ + --cat fact --no-diff +mnemon --data-dir "$MNEMON_DATA_DIR" --store default recall LIVE_INSTALL_ACCEPTANCE --basic +``` + +![官方 WebUI 读取 Native CLI 写入的事实](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/desktop-live-activation/web-recall.png) + +## Electron 验收 + +在 Electron 中启动随附 DSH 宿主,初始不选中已安装 bundle,再通过官方 WebUI 启用。[组件列表](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/desktop-live-activation/after-components.png)、[正常的状态页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/desktop-live-activation/electron-status.png)和[新建 Native Store](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/desktop-live-activation/electron-native.png)证明启用成功,并实际执行了 CLI 子进程。既有的上游 `cordis:group` 显示问题仍记录在[兼容性说明](../../zh-CN/reference/compatibility.md#dsh-017-bundle-组件列表)中,它不代表 Source 或 Strategy 停止运行。 + +## 自动化验证 + +```bash +pnpm run verify +pnpm run verify:plugins +``` + +两项均在 Node `24.20.0` 上通过: + +- 根包测试:1,513 项通过,6 项条件集成测试跳过。跳过项需要主动开启 Flash 压力/质量测试或专用 Native 容量 fixture;真实 Native 启用链路已按上文单独验收。 +- 构建、类型、文档链接、包内容、公开入口导入、`publint` 和 `attw` 均通过。发布包包含 55 个文件,解包后为 1,471,089 字节。 +- Headless 激活暴露 37 个工具,包含 8 个代表性 Mnemon 工具。设置迁移、重启和旧版核心停用开关均通过。 +- 17 个独立插件包均通过独立安装/构建/类型及各自测试,环境限定的 Native/Windows/OpenViking 集成项跳过。18 个打包制品通过 SDK/Client 组合、可选 Strategy 组合和只安装根 Starter 的验收。 +- 正式宿主生命周期回归覆盖隔离依赖下的后启用、其他启动后已停用 bundle、组件独立选择、原生配置 schema、核心/bundle 开关及重启持久化。官方 Connection 回归确认后启用时注册 RPC,并在 Mnemon 释放时移除路由。 diff --git a/dsh/docs/pr-assets/desktop-live-activation/validation.json b/dsh/docs/pr-assets/desktop-live-activation/validation.json new file mode 100644 index 000000000..38e095795 --- /dev/null +++ b/dsh/docs/pr-assets/desktop-live-activation/validation.json @@ -0,0 +1,58 @@ +{ + "verifiedAt": "2026-09-28T12:41:22.857393+00:00", + "baseCommit": "accd87513c6330fbf8af64f4352657dcca390991", + "candidate": { + "name": "dsh-mnemon", + "version": "0.5.17", + "status": "unreleased PR candidate", + "sha256": "e9445d4950d8f3a3cc4c02803d3903b6ef992d0f48b0ec9ea0cbeb1fb1f5273f" + }, + "officialWebUI": { + "dsh": "0.1.7-rc.2", + "node": "24.20.0", + "initialState": "fresh profile without Mnemon", + "installFlow": "Add plugin -> Install -> Enable now", + "hostPidAtLaunch": 76815, + "hostPidAfterAcceptance": 76815, + "hostAlive": true, + "hostLaunchCount": 1, + "processStartedAt": "Mon Sep 28 20:23:30 2026", + "dependencyLayout": "pnpm isolated; root tarball installed through official WebUI" + }, + "electron": { + "desktopBundledDSH": "0.1.7-rc.2", + "desktopPackage": "0.10.0", + "electron": "44.0.0", + "node": "24.18.1", + "processType": "browser", + "ELECTRON_RUN_AS_NODE": null, + "acceptance": "Enable previously unselected bundle; Status healthy; create Native store using real CLI subprocess" + }, + "nativeCLI": { + "version": "0.2.9", + "rememberAction": "added", + "memoryId": "5b4caa9f-2334-4474-8c35-aab634006f12", + "content": "LIVE_INSTALL_ACCEPTANCE: Native CLI and official WebUI share durable memory immediately after installation.", + "cliRecallMatches": true, + "officialWebUIBasicRecallMatches": true + }, + "observedViaRealUI": { + "runtimeCreateAndRead": true, + "documentCreateAndRead": true, + "nativeSpaceCreateAndActivate": true, + "cliMemoryRecall": true, + "bundleDisableAndReenable": true, + "runtimeDocumentAndNativeMemoryRetained": true + }, + "checks": { + "pnpm run verify": "passed", + "rootTestsPassed": 1513, + "rootTestsSkipped": 6, + "pnpm run verify:plugins": "passed", + "independentPluginPackages": 17, + "packedArtifacts": 18, + "headlessTools": 37, + "representativeMnemonTools": 8 + }, + "scope": "Isolated profiles and synthetic data; unmodified published DSH and Desktop host files; no user profile updates or release publishing." +} diff --git a/dsh/docs/pr-assets/dialog-surfaces/README.md b/dsh/docs/pr-assets/dialog-surfaces/README.md new file mode 100644 index 000000000..16b1e1f82 --- /dev/null +++ b/dsh/docs/pr-assets/dialog-surfaces/README.md @@ -0,0 +1,76 @@ +# 弹窗背景回归证据 / Dialog surface regression evidence + +本目录是本次修复的 PR 验证材料,不替换发布截图。基线为 `540630e`,验证于 2026-08-30 至 2026-08-31。修复后构建仅修改两个 Client CSS 文件;[measurements.json](./measurements.json) 保存源码 SHA-256、浏览器观测值与像素检查结果。 + +These are PR regression artifacts, not replacement release screenshots. The baseline is `540630e`, verified on August 30–31, 2026. The patched build changes only two Client CSS files; [measurements.json](./measurements.json) records their SHA-256 hashes, browser observations, and pixel checks. + +## 原因与修复 / Cause and fix + +侧栏弹窗和页脚直接使用 `--dsw-alias-bg-overlay`,而宿主原生 Modal 使用 `--dsw-alias-bg-layer-2`。前者在默认深色主题中是明显偏亮的灰色。共用弹窗还缺少透明皮肤所需的画布底层。 + +Sidebar dialogs and footers used `--dsw-alias-bg-overlay`, whereas the host's native Modal uses `--dsw-alias-bg-layer-2`. The former produces the conspicuously bright gray in the default dark theme. The shared dialog also lacked a canvas backing for translucent skins. + +| 默认主题 / Default theme | 修复前侧栏 / Sidebar before | 修复后及原生 Modal / After and native Modal | +| --- | --- | --- | +| 深色 / Dark | `#61666b` | `#2c2c2e` | +| 浅色 / Light | `#e9ecf2` | `#ffffff` | + +共用弹窗现在将 layer-2 叠加在已有 `--mn-surface` 画布上,侧栏不再覆盖它,页脚保持透明以避免重复叠色。Portal、焦点、动画、滚动、移动端抽屉及所有写入行为不变。会话内“存入记忆”本来就使用原生 Modal,已核对且未修改;页面画布和 Tooltip 的合法主题色也未修改。 + +The shared dialog now paints layer-2 over the existing `--mn-surface` canvas. Sidebar overrides are removed, and the footer stays transparent to avoid compositing the same translucent fill twice. Portals, focus, animation, scrolling, mobile sheets, and write behavior are unchanged. Conversation Save to memory already uses the native Modal and was checked without modification; legitimate canvas and Tooltip theme colors are unchanged too. + +## 实机截图 / Live screenshots + +截图来自独立 worktree 的真实 npm WebUI:DSH `0.1.1-rc.2`、`@linxin666/dsh-web-all@0.3.6`、Mnemon `0.3.5` 加本补丁。使用隔离的 home、workspace、记忆目录与本机固定模型响应,仅包含合成数据;未连接私人记忆或付费模型服务。前后运行时弹窗使用相同的空数据状态。 + +Screenshots come from the real npm WebUI in an independent worktree: DSH `0.1.1-rc.2`, `@linxin666/dsh-web-all@0.3.6`, and Mnemon `0.3.5` plus this patch. Home, workspace, memory storage, and the local fixed model response are isolated; all data is synthetic, with no private memory or paid model service connected. Runtime before/after pairs use the same empty data state. + +| 场景 / Scenario | 修复前 / Before | 修复后 / After | +| --- | --- | --- | +| 深色移动端添加热记忆 / Dark mobile runtime, 680 × 900 | ![Before dark mobile runtime](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dialog-surfaces/before-dark-mobile-runtime.jpg) | ![After dark mobile runtime](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dialog-surfaces/after-dark-mobile-runtime.jpg) | +| 深色桌面添加热记忆 / Dark desktop runtime, 1280 × 900 | ![Before dark desktop runtime](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dialog-surfaces/before-dark-desktop-runtime.jpg) | ![After dark desktop runtime](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dialog-surfaces/after-dark-desktop-runtime.jpg) | +| 浅色桌面添加热记忆 / Light desktop runtime, 1280 × 900 | ![Before light desktop runtime](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dialog-surfaces/before-light-desktop-runtime.jpg) | ![After light desktop runtime](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dialog-surfaces/after-light-desktop-runtime.jpg) | + +其他修复后截图 / Additional patched screenshots: + +| 创建档案桌面 / Desktop document creation, 1280 × 900 | 创建档案窄屏 / Narrow document creation, 390 × 844 | 沉淀策略 / Persistence strategy, 1280 × 900 | +| --- | --- | --- | +| ![After desktop document creation](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dialog-surfaces/after-dark-desktop-document.jpg) | ![After narrow document creation](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dialog-surfaces/after-dark-mobile-document.jpg) | ![After persistence strategy](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dialog-surfaces/after-dark-strategy.jpg) | + +## 排查范围 / Audit coverage + +真实页面逐一打开全部 15 个 `SidebarModal` 调用入口,记录计算样式并检查横向溢出;另核对一个原生会话弹窗。共保留 23 次观测,含主题和视口的重复验证。破坏性确认仅打开后取消,没有执行删除、遗忘、归档或版本更新。 + +All 15 `SidebarModal` call sites were opened in the live UI, with computed styles and horizontal overflow checked. One native conversation dialog was checked separately. The 23 observations include repeated theme/viewport checks. Destructive confirmations were opened and canceled; deletion, forgetting, archiving, and version updates were not executed. + +| 区域 / Area | 已打开入口 / Opened dialogs | 数量 / Count | +| --- | --- | --- | +| 运行时记忆 / Runtime memory | 添加、编辑、移除确认 / Add, edit, remove confirmation | 3 | +| 记忆空间 / Memory spaces | 创建、编辑、删除确认 / Create, edit, delete confirmation | 3 | +| 记忆维护 / Memory maintenance | AI 元信息、沉淀策略、沉淀记忆 / AI metadata, persistence strategy, supervised write | 3 | +| 项目档案 / Project documents | 创建、编辑、归档确认 / Create, edit, archive confirmation | 3 | +| 其他 / Other | 版本检查、遗忘确认、全文预览 / Version check, forget confirmation, full-content preview | 3 | + +外部 Provider 的“断开连接”与已验证的删除确认共用同一入口和样式;未单独配置远程 Provider。`buildin` 与侧栏的共用样式通过下面的渲染实验验证,不声称逐一测试所有第三方皮肤。 + +External Provider disconnect shares the tested deletion call site and styles; no remote Provider was provisioned separately. Shared styles for `buildin` and sidebar surfaces were also checked by the rendering experiment below; this is not a claim that every third-party skin was tested. + +## 渲染与工程验证 / Rendering and engineering validation + +本地临时浏览器夹具使用基线和修复后的真实 CSS Modules,分别渲染侧栏与 `buildin` 弹窗。背景使用彩色棋盘以暴露穿透,并对主体、页脚分别采样。8 种 token 组合覆盖默认明暗、透明层、半透明层、半透明画布及缺少 overlay token;32 个采样点修复前 10 个符合预期,修复后 32 个全部符合预期。JPEG 每通道容差为 3。此项是本次手动视觉回归实验,不是新增 CI 测试。 + +A temporary local browser fixture rendered the actual baseline and patched CSS Modules for sidebar and `buildin` dialogs. A colored checkerboard exposed bleed-through, with body and footer pixels sampled separately. Eight token combinations cover default light/dark, transparent layers, translucent layers, a translucent canvas, and a missing overlay token. Of 32 samples, 10 matched before and all 32 matched after, allowing a JPEG tolerance of 3 per channel. This is a manual visual regression experiment for this change, not a new CI test. + +| 检查 / Check | 结果 / Result | +| --- | --- | +| `pnpm run verify` | 通过 / Passed | +| Vitest | 541 通过,1 个 Windows 专用测试在 macOS 跳过 / 541 passed; 1 Windows-only test skipped on macOS | +| 确定性构建 / Deterministic build | 106 个文件一致 / 106 matching files | +| Headless 激活 / Headless activation | 35 个工具,核对 5 个 Mnemon 代表工具 / 35 tools; 5 representative Mnemon tools asserted | +| 包与入口 / Package and entry checks | 内容检查、10 个 Node 入口、publint 与 attw 通过 / Contents, 10 Node entries, publint, and attw passed | +| 浏览器 / Browser | 15 个共用弹窗无横向溢出,控制台无错误 / No horizontal overflow in the 15 shared dialogs; no console errors | +| `git diff --check` | 通过 / Passed | + +复核时可运行 `pnpm run verify`,再用 `node scripts/serve-web-regression.mjs --cli ` 启动隔离 WebUI,切换官方明暗主题并按上表打开弹窗。工作流、文案、配置和存储契约未变,因此现有中英文使用指南无需改写。 + +To recheck, run `pnpm run verify`, then start the isolated WebUI with `node scripts/serve-web-regression.mjs --cli `, select the official light/dark themes, and open the dialogs listed above. Workflows, copy, configuration, and storage contracts are unchanged, so the existing English and Chinese usage guides need no revision. diff --git a/dsh/docs/pr-assets/dialog-surfaces/measurements.json b/dsh/docs/pr-assets/dialog-surfaces/measurements.json new file mode 100644 index 000000000..08f56160e --- /dev/null +++ b/dsh/docs/pr-assets/dialog-surfaces/measurements.json @@ -0,0 +1,97 @@ +{ + "baselineCommit": "540630e", + "environment": {"DSH": "0.1.1-rc.2", "WebUI": "@linxin666/dsh-web-all@0.3.6", "Mnemon": "0.3.5 + this CSS patch", "data": "isolated synthetic data only"}, + "cssSha256": {"src/client/MnemonView.module.css": "3281f34fccf221544ec554ed9e19d763db02562bd52bab1434e262d54315d0d1", "src/client/MnemonSidebarView.module.css": "41d934e557806c5b58b73bf416ba17c3a8b803dc6522684f4b65b6917add3aa7"}, + "dialogObservations": [ + {"name": "after-light-runtime-add", "background": "linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255)) repeat scroll 0% 0% / auto padding-box border-box, linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255)) repeat scroll 0% 0% / auto padding-box border-box, rgb(255, 255, 255) linear-gradient(rgb(233, 236, 242), rgb(233, 236, 242)) repeat scroll 0% 0% / auto padding-box border-box", "body": "rgba(0, 0, 0, 0) none repeat scroll 0% 0% / auto padding-box border-box", "color": "rgb(15, 17, 21)", "footer": "rgba(0, 0, 0, 0) none repeat scroll 0px 0px / auto padding-box border-box", "height": 320.875, "layer2": "#fff", "overflow": false, "title": "添加热记忆", "viewport": [1280, 900], "width": 680}, + {"name": "after-dark-runtime-add-desktop", "background": "linear-gradient(rgb(44, 44, 46), rgb(44, 44, 46)) repeat scroll 0% 0% / auto padding-box border-box, linear-gradient(rgb(21, 21, 23), rgb(21, 21, 23)) repeat scroll 0% 0% / auto padding-box border-box, rgb(21, 21, 23) linear-gradient(rgb(97, 102, 107), rgb(97, 102, 107)) repeat scroll 0% 0% / auto padding-box border-box", "body": "rgba(0, 0, 0, 0) none repeat scroll 0% 0% / auto padding-box border-box", "color": "rgb(249, 250, 251)", "footer": "rgba(0, 0, 0, 0) none repeat scroll 0px 0px / auto padding-box border-box", "height": 320.875, "layer2": "#2c2c2e", "overflow": false, "title": "添加热记忆", "viewport": [1280, 900], "width": 680}, + {"name": "after-dark-runtime-add-mobile", "background": "linear-gradient(rgb(44, 44, 46), rgb(44, 44, 46)) repeat scroll 0% 0% / auto padding-box border-box, linear-gradient(rgb(21, 21, 23), rgb(21, 21, 23)) repeat scroll 0% 0% / auto padding-box border-box, rgb(21, 21, 23) linear-gradient(rgb(97, 102, 107), rgb(97, 102, 107)) repeat scroll 0% 0% / auto padding-box border-box", "body": "rgba(0, 0, 0, 0) none repeat scroll 0% 0% / auto padding-box border-box", "color": "rgb(249, 250, 251)", "footer": "rgba(0, 0, 0, 0) none repeat scroll 0px 0px / auto padding-box border-box", "height": 476.9140625, "layer2": "#2c2c2e", "overflow": false, "title": "添加热记忆", "viewport": [680, 900], "width": 680}, + {"name": "after-dark-space-create", "background": "linear-gradient(rgb(44, 44, 46), rgb(44, 44, 46)) repeat scroll 0% 0% / auto padding-box border-box, linear-gradient(rgb(21, 21, 23), rgb(21, 21, 23)) repeat scroll 0% 0% / auto padding-box border-box, rgb(21, 21, 23) linear-gradient(rgb(97, 102, 107), rgb(97, 102, 107)) repeat scroll 0% 0% / auto padding-box border-box", "body": "rgba(0, 0, 0, 0) none repeat scroll 0% 0% / auto padding-box border-box", "color": "rgb(249, 250, 251)", "footer": "rgba(0, 0, 0, 0) none repeat scroll 0px 0px / auto padding-box border-box", "height": 838.3984375, "layer2": "#2c2c2e", "overflow": false, "title": "创建记忆体", "viewport": [1280, 900], "width": 680}, + {"name": "after-dark-space-edit", "background": "linear-gradient(rgb(44, 44, 46), rgb(44, 44, 46)) repeat scroll 0% 0% / auto padding-box border-box, linear-gradient(rgb(21, 21, 23), rgb(21, 21, 23)) repeat scroll 0% 0% / auto padding-box border-box, rgb(21, 21, 23) linear-gradient(rgb(97, 102, 107), rgb(97, 102, 107)) repeat scroll 0% 0% / auto padding-box border-box", "body": "rgba(0, 0, 0, 0) none repeat scroll 0% 0% / auto padding-box border-box", "color": "rgb(249, 250, 251)", "footer": "rgba(0, 0, 0, 0) none repeat scroll 0px 0px / auto padding-box border-box", "height": 356.8359375, "layer2": "#2c2c2e", "overflow": false, "title": "编辑Surface audit", "viewport": [1280, 900], "width": 680}, + {"name": "after-dark-metadata", "background": "linear-gradient(rgb(44, 44, 46), rgb(44, 44, 46)) repeat scroll 0% 0% / auto padding-box border-box, linear-gradient(rgb(21, 21, 23), rgb(21, 21, 23)) repeat scroll 0% 0% / auto padding-box border-box, rgb(21, 21, 23) linear-gradient(rgb(97, 102, 107), rgb(97, 102, 107)) repeat scroll 0% 0% / auto padding-box border-box", "body": "rgba(0, 0, 0, 0) none repeat scroll 0% 0% / auto padding-box border-box", "color": "rgb(249, 250, 251)", "footer": "rgba(0, 0, 0, 0) none repeat scroll 0px 0px / auto padding-box border-box", "height": 284.6484375, "layer2": "#2c2c2e", "overflow": false, "title": "AI 维护记忆体元信息", "viewport": [1280, 900], "width": 680}, + {"name": "after-dark-strategy", "background": "linear-gradient(rgb(44, 44, 46), rgb(44, 44, 46)) repeat scroll 0% 0% / auto padding-box border-box, linear-gradient(rgb(21, 21, 23), rgb(21, 21, 23)) repeat scroll 0% 0% / auto padding-box border-box, rgb(21, 21, 23) linear-gradient(rgb(97, 102, 107), rgb(97, 102, 107)) repeat scroll 0% 0% / auto padding-box border-box", "body": "rgba(0, 0, 0, 0) none repeat scroll 0% 0% / auto padding-box border-box", "color": "rgb(249, 250, 251)", "footer": "rgba(0, 0, 0, 0) none repeat scroll 0px 0px / auto padding-box border-box", "height": 746.8046875, "layer2": "#2c2c2e", "overflow": false, "title": "沉淀策略", "viewport": [1280, 900], "width": 680}, + {"name": "after-dark-supervised-write", "background": "linear-gradient(rgb(44, 44, 46), rgb(44, 44, 46)) repeat scroll 0% 0% / auto padding-box border-box, linear-gradient(rgb(21, 21, 23), rgb(21, 21, 23)) repeat scroll 0% 0% / auto padding-box border-box, rgb(21, 21, 23) linear-gradient(rgb(97, 102, 107), rgb(97, 102, 107)) repeat scroll 0% 0% / auto padding-box border-box", "body": "rgba(0, 0, 0, 0) none repeat scroll 0% 0% / auto padding-box border-box", "color": "rgb(249, 250, 251)", "footer": "rgba(0, 0, 0, 0) none repeat scroll 0px 0px / auto padding-box border-box", "height": 502.8044738769531, "layer2": "#2c2c2e", "overflow": false, "title": "沉淀记忆", "viewport": [1280, 900], "width": 679.999755859375}, + {"name": "after-dark-space-delete", "background": "linear-gradient(rgb(44, 44, 46), rgb(44, 44, 46)) repeat scroll 0% 0% / auto padding-box border-box, linear-gradient(rgb(21, 21, 23), rgb(21, 21, 23)) repeat scroll 0% 0% / auto padding-box border-box, rgb(21, 21, 23) linear-gradient(rgb(97, 102, 107), rgb(97, 102, 107)) repeat scroll 0% 0% / auto padding-box border-box", "body": "rgba(0, 0, 0, 0) none repeat scroll 0% 0% / auto padding-box border-box", "color": "rgb(249, 250, 251)", "footer": "rgba(0, 0, 0, 0) none repeat scroll 0px 0px / auto padding-box border-box", "height": 289.1875, "layer2": "#2c2c2e", "overflow": false, "title": "删除“Surface audit secondary”?", "viewport": [1280, 900], "width": 680}, + {"name": "after-dark-runtime-edit", "background": "linear-gradient(rgb(44, 44, 46), rgb(44, 44, 46)) repeat scroll 0% 0% / auto padding-box border-box, linear-gradient(rgb(21, 21, 23), rgb(21, 21, 23)) repeat scroll 0% 0% / auto padding-box border-box, rgb(21, 21, 23) linear-gradient(rgb(97, 102, 107), rgb(97, 102, 107)) repeat scroll 0% 0% / auto padding-box border-box", "body": "rgba(0, 0, 0, 0) none repeat scroll 0% 0% / auto padding-box border-box", "color": "rgb(249, 250, 251)", "footer": "rgba(0, 0, 0, 0) none repeat scroll 0px 0px / auto padding-box border-box", "height": 500.874755859375, "layer2": "#2c2c2e", "overflow": false, "title": "编辑运行时记忆", "viewport": [1280, 900], "width": 679.999755859375}, + {"name": "after-dark-runtime-remove", "background": "linear-gradient(rgb(44, 44, 46), rgb(44, 44, 46)) repeat scroll 0% 0% / auto padding-box border-box, linear-gradient(rgb(21, 21, 23), rgb(21, 21, 23)) repeat scroll 0% 0% / auto padding-box border-box, rgb(21, 21, 23) linear-gradient(rgb(97, 102, 107), rgb(97, 102, 107)) repeat scroll 0% 0% / auto padding-box border-box", "body": "rgba(0, 0, 0, 0) none repeat scroll 0% 0% / auto padding-box border-box", "color": "rgb(249, 250, 251)", "footer": "rgba(0, 0, 0, 0) none repeat scroll 0px 0px / auto padding-box border-box", "height": 272.140625, "layer2": "#2c2c2e", "overflow": false, "title": "移除运行时记忆?", "viewport": [1280, 900], "width": 680}, + {"name": "after-dark-versions", "background": "linear-gradient(rgb(44, 44, 46), rgb(44, 44, 46)) repeat scroll 0% 0% / auto padding-box border-box, linear-gradient(rgb(21, 21, 23), rgb(21, 21, 23)) repeat scroll 0% 0% / auto padding-box border-box, rgb(21, 21, 23) linear-gradient(rgb(97, 102, 107), rgb(97, 102, 107)) repeat scroll 0% 0% / auto padding-box border-box", "body": "rgba(0, 0, 0, 0) none repeat scroll 0% 0% / auto padding-box border-box", "color": "rgb(249, 250, 251)", "footer": "rgba(0, 0, 0, 0) none repeat scroll 0px 0px / auto padding-box border-box", "height": 281, "layer2": "#2c2c2e", "overflow": false, "title": "检查与更新版本", "viewport": [1280, 900], "width": 680}, + {"name": "audited-native-conversation-save", "background": "rgb(44, 44, 46) none repeat scroll 0% 0% / auto padding-box border-box", "body": null, "color": "rgb(249, 250, 251)", "footer": null, "height": 494, "layer2": "#2c2c2e", "overflow": false, "title": "确认存入记忆", "viewport": [1280, 900], "width": 642}, + {"name": "after-dark-document-create", "background": "linear-gradient(rgb(44, 44, 46), rgb(44, 44, 46)) repeat scroll 0% 0% / auto padding-box border-box, linear-gradient(rgb(21, 21, 23), rgb(21, 21, 23)) repeat scroll 0% 0% / auto padding-box border-box, rgb(21, 21, 23) linear-gradient(rgb(97, 102, 107), rgb(97, 102, 107)) repeat scroll 0% 0% / auto padding-box border-box", "body": "rgba(0, 0, 0, 0) none repeat scroll 0% 0% / auto padding-box border-box", "color": "rgb(249, 250, 251)", "footer": "rgba(0, 0, 0, 0) none repeat scroll 0px 0px / auto padding-box border-box", "height": 560.679443359375, "layer2": "#2c2c2e", "overflow": false, "title": "创建托管档案", "viewport": [1280, 900], "width": 679.999755859375}, + {"name": "after-dark-document-create-desktop-filled", "background": "linear-gradient(rgb(44, 44, 46), rgb(44, 44, 46)) repeat scroll 0% 0% / auto padding-box border-box, linear-gradient(rgb(21, 21, 23), rgb(21, 21, 23)) repeat scroll 0% 0% / auto padding-box border-box, rgb(21, 21, 23) linear-gradient(rgb(97, 102, 107), rgb(97, 102, 107)) repeat scroll 0% 0% / auto padding-box border-box", "body": "rgba(0, 0, 0, 0) none repeat scroll 0% 0% / auto padding-box border-box", "color": "rgb(249, 250, 251)", "footer": "rgba(0, 0, 0, 0) none repeat scroll 0px 0px / auto padding-box border-box", "height": 560.6796875, "layer2": "#2c2c2e", "overflow": false, "title": "创建托管档案", "viewport": [1280, 900], "width": 680}, + {"name": "after-dark-document-edit", "background": "linear-gradient(rgb(44, 44, 46), rgb(44, 44, 46)) repeat scroll 0% 0% / auto padding-box border-box, linear-gradient(rgb(21, 21, 23), rgb(21, 21, 23)) repeat scroll 0% 0% / auto padding-box border-box, rgb(21, 21, 23) linear-gradient(rgb(97, 102, 107), rgb(97, 102, 107)) repeat scroll 0% 0% / auto padding-box border-box", "body": "rgba(0, 0, 0, 0) none repeat scroll 0% 0% / auto padding-box border-box", "color": "rgb(249, 250, 251)", "footer": "rgba(0, 0, 0, 0) none repeat scroll 0px 0px / auto padding-box border-box", "height": 732.2421875, "layer2": "#2c2c2e", "overflow": false, "title": "编辑活跃档案", "viewport": [1280, 900], "width": 680}, + {"name": "after-dark-document-edit", "background": "linear-gradient(rgb(44, 44, 46), rgb(44, 44, 46)) repeat scroll 0% 0% / auto padding-box border-box, linear-gradient(rgb(21, 21, 23), rgb(21, 21, 23)) repeat scroll 0% 0% / auto padding-box border-box, rgb(21, 21, 23) linear-gradient(rgb(97, 102, 107), rgb(97, 102, 107)) repeat scroll 0% 0% / auto padding-box border-box", "body": "rgba(0, 0, 0, 0) none repeat scroll 0% 0% / auto padding-box border-box", "color": "rgb(249, 250, 251)", "footer": "rgba(0, 0, 0, 0) none repeat scroll 0px 0px / auto padding-box border-box", "height": 732.2421875, "layer2": "#2c2c2e", "overflow": false, "title": "编辑活跃档案", "viewport": [1280, 900], "width": 680}, + {"name": "after-dark-document-edit", "background": "linear-gradient(rgb(44, 44, 46), rgb(44, 44, 46)) repeat scroll 0% 0% / auto padding-box border-box, linear-gradient(rgb(21, 21, 23), rgb(21, 21, 23)) repeat scroll 0% 0% / auto padding-box border-box, rgb(21, 21, 23) linear-gradient(rgb(97, 102, 107), rgb(97, 102, 107)) repeat scroll 0% 0% / auto padding-box border-box", "body": "rgba(0, 0, 0, 0) none repeat scroll 0% 0% / auto padding-box border-box", "color": "rgb(249, 250, 251)", "footer": "rgba(0, 0, 0, 0) none repeat scroll 0px 0px / auto padding-box border-box", "height": 732.2421875, "layer2": "#2c2c2e", "overflow": false, "title": "编辑活跃档案", "viewport": [1280, 900], "width": 680}, + {"name": "after-dark-document-edit", "background": "linear-gradient(rgb(44, 44, 46), rgb(44, 44, 46)) repeat scroll 0% 0% / auto padding-box border-box, linear-gradient(rgb(21, 21, 23), rgb(21, 21, 23)) repeat scroll 0% 0% / auto padding-box border-box, rgb(21, 21, 23) linear-gradient(rgb(97, 102, 107), rgb(97, 102, 107)) repeat scroll 0% 0% / auto padding-box border-box", "body": "rgba(0, 0, 0, 0) none repeat scroll 0% 0% / auto padding-box border-box", "color": "rgb(249, 250, 251)", "footer": "rgba(0, 0, 0, 0) none repeat scroll 0px 0px / auto padding-box border-box", "height": 732.2421875, "layer2": "#2c2c2e", "overflow": false, "title": "编辑活跃档案", "viewport": [1280, 900], "width": 680}, + {"name": "after-dark-document-create-390", "background": "linear-gradient(rgb(44, 44, 46), rgb(44, 44, 46)) repeat scroll 0% 0% / auto padding-box border-box, linear-gradient(rgb(21, 21, 23), rgb(21, 21, 23)) repeat scroll 0% 0% / auto padding-box border-box, rgb(21, 21, 23) linear-gradient(rgb(97, 102, 107), rgb(97, 102, 107)) repeat scroll 0% 0% / auto padding-box border-box", "footer": "rgba(0, 0, 0, 0) none repeat scroll 0px 0px / auto padding-box border-box", "height": 670.2734375, "layer2": "#2c2c2e", "overflow": false, "title": "创建托管档案", "viewport": [390, 844], "width": 390}, + {"name": "after-dark-document-archive", "background": "linear-gradient(rgb(44, 44, 46), rgb(44, 44, 46)) repeat scroll 0% 0% / auto padding-box border-box, linear-gradient(rgb(21, 21, 23), rgb(21, 21, 23)) repeat scroll 0% 0% / auto padding-box border-box, rgb(21, 21, 23) linear-gradient(rgb(97, 102, 107), rgb(97, 102, 107)) repeat scroll 0% 0% / auto padding-box border-box", "footer": "rgba(0, 0, 0, 0) none repeat scroll 0px 0px / auto padding-box border-box", "height": 272.140625, "layer2": "#2c2c2e", "overflow": false, "title": "确认建立 Mnemon 索引并迁移这份档案?", "viewport": [1280, 900], "width": 680}, + {"name": "after-dark-insight-forget", "background": "linear-gradient(rgb(44, 44, 46), rgb(44, 44, 46)) repeat scroll 0% 0% / auto padding-box border-box, linear-gradient(rgb(21, 21, 23), rgb(21, 21, 23)) repeat scroll 0% 0% / auto padding-box border-box, rgb(21, 21, 23) linear-gradient(rgb(97, 102, 107), rgb(97, 102, 107)) repeat scroll 0% 0% / auto padding-box border-box", "footer": "rgba(0, 0, 0, 0) none repeat scroll 0px 0px / auto padding-box border-box", "height": 235.34375, "layer2": "#2c2c2e", "overflow": false, "title": "软删除这条记忆?", "viewport": [1280, 900], "width": 680}, + {"name": "after-dark-content-preview", "background": "linear-gradient(rgb(44, 44, 46), rgb(44, 44, 46)) repeat scroll 0% 0% / auto padding-box border-box, linear-gradient(rgb(21, 21, 23), rgb(21, 21, 23)) repeat scroll 0% 0% / auto padding-box border-box, rgb(21, 21, 23) linear-gradient(rgb(97, 102, 107), rgb(97, 102, 107)) repeat scroll 0% 0% / auto padding-box border-box", "footer": null, "height": 399.21875, "layer2": "#2c2c2e", "overflow": false, "title": "内容全文", "viewport": [1280, 900], "width": 680} + ], + "themeMatrix": [ + {"mode": "before", "case": "official-light-sidebar", "part": "body", "expected": [255, 255, 255], "actual": [235, 236, 241], "passed": false}, + {"mode": "before", "case": "official-light-sidebar", "part": "footer", "expected": [255, 255, 255], "actual": [235, 236, 241], "passed": false}, + {"mode": "before", "case": "official-light-buildin", "part": "body", "expected": [255, 255, 255], "actual": [255, 255, 255], "passed": true}, + {"mode": "before", "case": "official-light-buildin", "part": "footer", "expected": [255, 255, 255], "actual": [255, 255, 255], "passed": true}, + {"mode": "before", "case": "official-dark-sidebar", "part": "body", "expected": [44, 44, 46], "actual": [98, 101, 106], "passed": false}, + {"mode": "before", "case": "official-dark-sidebar", "part": "footer", "expected": [44, 44, 46], "actual": [98, 101, 106], "passed": false}, + {"mode": "before", "case": "official-dark-buildin", "part": "body", "expected": [44, 44, 46], "actual": [44, 44, 46], "passed": true}, + {"mode": "before", "case": "official-dark-buildin", "part": "footer", "expected": [44, 44, 46], "actual": [44, 44, 46], "passed": true}, + {"mode": "before", "case": "transparent-light-sidebar", "part": "body", "expected": [244, 242, 232], "actual": [245, 242, 233], "passed": true}, + {"mode": "before", "case": "transparent-light-sidebar", "part": "footer", "expected": [244, 242, 232], "actual": [245, 242, 233], "passed": true}, + {"mode": "before", "case": "transparent-light-buildin", "part": "body", "expected": [244, 242, 232], "actual": [112, 245, 244], "passed": false}, + {"mode": "before", "case": "transparent-light-buildin", "part": "footer", "expected": [244, 242, 232], "actual": [165, 205, 255], "passed": false}, + {"mode": "before", "case": "transparent-dark-sidebar", "part": "body", "expected": [37, 35, 50], "actual": [37, 35, 49], "passed": true}, + {"mode": "before", "case": "transparent-dark-sidebar", "part": "footer", "expected": [37, 35, 50], "actual": [37, 35, 49], "passed": true}, + {"mode": "before", "case": "transparent-dark-buildin", "part": "body", "expected": [37, 35, 50], "actual": [113, 251, 254], "passed": false}, + {"mode": "before", "case": "transparent-dark-buildin", "part": "footer", "expected": [37, 35, 50], "actual": [185, 217, 255], "passed": false}, + {"mode": "before", "case": "alpha-layer-light-sidebar", "part": "body", "expected": [240, 236, 228], "actual": [223, 215, 202], "passed": false}, + {"mode": "before", "case": "alpha-layer-light-sidebar", "part": "footer", "expected": [240, 236, 228], "actual": [223, 215, 202], "passed": false}, + {"mode": "before", "case": "alpha-layer-light-buildin", "part": "body", "expected": [240, 236, 228], "actual": [179, 255, 252], "passed": false}, + {"mode": "before", "case": "alpha-layer-light-buildin", "part": "footer", "expected": [240, 236, 228], "actual": [232, 238, 254], "passed": false}, + {"mode": "before", "case": "alpha-layer-dark-sidebar", "part": "body", "expected": [40, 55, 70], "actual": [22, 31, 40], "passed": false}, + {"mode": "before", "case": "alpha-layer-dark-sidebar", "part": "footer", "expected": [40, 55, 70], "actual": [22, 31, 40], "passed": false}, + {"mode": "before", "case": "alpha-layer-dark-buildin", "part": "body", "expected": [40, 55, 70], "actual": [92, 176, 178], "passed": false}, + {"mode": "before", "case": "alpha-layer-dark-buildin", "part": "footer", "expected": [40, 55, 70], "actual": [88, 110, 134], "passed": false}, + {"mode": "before", "case": "alpha-base-layer-sidebar", "part": "body", "expected": [208, 209, 210], "actual": [98, 101, 106], "passed": false}, + {"mode": "before", "case": "alpha-base-layer-sidebar", "part": "footer", "expected": [208, 209, 210], "actual": [98, 101, 106], "passed": false}, + {"mode": "before", "case": "alpha-base-layer-buildin", "part": "body", "expected": [208, 209, 210], "actual": [182, 250, 249], "passed": false}, + {"mode": "before", "case": "alpha-base-layer-buildin", "part": "footer", "expected": [208, 209, 210], "actual": [231, 237, 255], "passed": false}, + {"mode": "before", "case": "no-overlay-token-sidebar", "part": "body", "expected": [255, 255, 255], "actual": [255, 255, 255], "passed": true}, + {"mode": "before", "case": "no-overlay-token-sidebar", "part": "footer", "expected": [255, 255, 255], "actual": [255, 255, 255], "passed": true}, + {"mode": "before", "case": "no-overlay-token-buildin", "part": "body", "expected": [255, 255, 255], "actual": [118, 249, 255], "passed": false}, + {"mode": "before", "case": "no-overlay-token-buildin", "part": "footer", "expected": [255, 255, 255], "actual": [158, 208, 255], "passed": false}, + {"mode": "after", "case": "official-light-sidebar", "part": "body", "expected": [255, 255, 255], "actual": [255, 255, 255], "passed": true}, + {"mode": "after", "case": "official-light-sidebar", "part": "footer", "expected": [255, 255, 255], "actual": [255, 255, 255], "passed": true}, + {"mode": "after", "case": "official-light-buildin", "part": "body", "expected": [255, 255, 255], "actual": [255, 255, 255], "passed": true}, + {"mode": "after", "case": "official-light-buildin", "part": "footer", "expected": [255, 255, 255], "actual": [255, 255, 255], "passed": true}, + {"mode": "after", "case": "official-dark-sidebar", "part": "body", "expected": [44, 44, 46], "actual": [44, 44, 46], "passed": true}, + {"mode": "after", "case": "official-dark-sidebar", "part": "footer", "expected": [44, 44, 46], "actual": [44, 44, 46], "passed": true}, + {"mode": "after", "case": "official-dark-buildin", "part": "body", "expected": [44, 44, 46], "actual": [44, 44, 46], "passed": true}, + {"mode": "after", "case": "official-dark-buildin", "part": "footer", "expected": [44, 44, 46], "actual": [44, 44, 46], "passed": true}, + {"mode": "after", "case": "transparent-light-sidebar", "part": "body", "expected": [244, 242, 232], "actual": [245, 242, 233], "passed": true}, + {"mode": "after", "case": "transparent-light-sidebar", "part": "footer", "expected": [244, 242, 232], "actual": [245, 242, 233], "passed": true}, + {"mode": "after", "case": "transparent-light-buildin", "part": "body", "expected": [244, 242, 232], "actual": [245, 242, 233], "passed": true}, + {"mode": "after", "case": "transparent-light-buildin", "part": "footer", "expected": [244, 242, 232], "actual": [245, 242, 233], "passed": true}, + {"mode": "after", "case": "transparent-dark-sidebar", "part": "body", "expected": [37, 35, 50], "actual": [37, 35, 49], "passed": true}, + {"mode": "after", "case": "transparent-dark-sidebar", "part": "footer", "expected": [37, 35, 50], "actual": [37, 35, 49], "passed": true}, + {"mode": "after", "case": "transparent-dark-buildin", "part": "body", "expected": [37, 35, 50], "actual": [37, 35, 49], "passed": true}, + {"mode": "after", "case": "transparent-dark-buildin", "part": "footer", "expected": [37, 35, 50], "actual": [37, 35, 49], "passed": true}, + {"mode": "after", "case": "alpha-layer-light-sidebar", "part": "body", "expected": [240, 236, 228], "actual": [239, 236, 229], "passed": true}, + {"mode": "after", "case": "alpha-layer-light-sidebar", "part": "footer", "expected": [240, 236, 228], "actual": [239, 236, 229], "passed": true}, + {"mode": "after", "case": "alpha-layer-light-buildin", "part": "body", "expected": [240, 236, 228], "actual": [239, 236, 229], "passed": true}, + {"mode": "after", "case": "alpha-layer-light-buildin", "part": "footer", "expected": [240, 236, 228], "actual": [239, 236, 229], "passed": true}, + {"mode": "after", "case": "alpha-layer-dark-sidebar", "part": "body", "expected": [40, 55, 70], "actual": [43, 55, 69], "passed": true}, + {"mode": "after", "case": "alpha-layer-dark-sidebar", "part": "footer", "expected": [40, 55, 70], "actual": [43, 55, 69], "passed": true}, + {"mode": "after", "case": "alpha-layer-dark-buildin", "part": "body", "expected": [40, 55, 70], "actual": [43, 55, 69], "passed": true}, + {"mode": "after", "case": "alpha-layer-dark-buildin", "part": "footer", "expected": [40, 55, 70], "actual": [43, 55, 69], "passed": true}, + {"mode": "after", "case": "alpha-base-layer-sidebar", "part": "body", "expected": [208, 209, 210], "actual": [208, 209, 211], "passed": true}, + {"mode": "after", "case": "alpha-base-layer-sidebar", "part": "footer", "expected": [208, 209, 210], "actual": [208, 209, 211], "passed": true}, + {"mode": "after", "case": "alpha-base-layer-buildin", "part": "body", "expected": [208, 209, 210], "actual": [208, 209, 211], "passed": true}, + {"mode": "after", "case": "alpha-base-layer-buildin", "part": "footer", "expected": [208, 209, 210], "actual": [208, 209, 211], "passed": true}, + {"mode": "after", "case": "no-overlay-token-sidebar", "part": "body", "expected": [255, 255, 255], "actual": [255, 255, 255], "passed": true}, + {"mode": "after", "case": "no-overlay-token-sidebar", "part": "footer", "expected": [255, 255, 255], "actual": [255, 255, 255], "passed": true}, + {"mode": "after", "case": "no-overlay-token-buildin", "part": "body", "expected": [255, 255, 255], "actual": [255, 255, 255], "passed": true}, + {"mode": "after", "case": "no-overlay-token-buildin", "part": "footer", "expected": [255, 255, 255], "actual": [255, 255, 255], "passed": true} + ], + "validation": {"testsPassed": 541, "testsSkipped": 1, "reasonSkipped": "Windows-only smoke test on macOS", "deterministicBuildFiles": 106, "headlessTools": 35, "publicNodeEntries": 10, "browserConsoleErrors": 0, "matrixSamplesAfterPassed": 32, "matrixSamplesAfterTotal": 32} +} diff --git a/dsh/docs/pr-assets/documentation-refresh/README.md b/dsh/docs/pr-assets/documentation-refresh/README.md new file mode 100644 index 000000000..039931bcc --- /dev/null +++ b/dsh/docs/pr-assets/documentation-refresh/README.md @@ -0,0 +1,68 @@ +# Documentation refresh verification / 文档整理验证 + +Current documentation media: [capture provenance](../../assets/showcase/README.md). + +## Initial verification + +Production code, loader patch, lockfile and package versions are unchanged from main `d94dfec`. The new executable work is limited to the documentation guard and its tests. Verification used Node `24.19.0` and pnpm `10.13.1` on macOS. + +| Check | Result | +|---|---| +| `MNEMON_NATIVE_TEST_CLI=/absolute/path/to/mnemon pnpm verify` | Passed: 122 test files / 1,058 tests, including 8 documentation regressions and the real Native create/write/recall/forget case; 1 Windows-only file/test skipped | +| Deterministic build | 39 generated files matched | +| Headless | 38 tools / 7 representative Mnemon tools; legacy entry spelling and complete Starter disabling verified | +| Package verification | 46 Starter files; 11 public runtime imports / 26 public type dependencies; strict publint and attw passed | +| `pnpm verify:plugins` | 16 independent plugin repositories and 17 packed artifacts passed installation, public-consumer checks, typecheck, tests and builds; real DSH activated the packed Starter and all three optional enhancements | +| `pnpm release:check` / `pnpm release:intent` | Existing exact composition valid; changeset covers all 17 packages whose published README/metadata changed; no publication or version bump performed | +| Documentation | Local file/anchor links, English/Chinese counterpart paths and the full official plugin catalog checked; external network availability is not a CI prerequisite | +| README/media | Both languages rendered through GitHub's Markdown API and inspected at desktop/390px widths; overview and Runtime image URLs returned HTTP 200; 1440×960 H.264 video loaded and played for 22.72 seconds | +| Real desktop WebUI | Runtime and Document creation; Native space creation/activation/synchronization/content; three enhancement switches persisted after reopening Settings and returned to Off in both languages; no page-level JS exceptions | +| Narrow product WebUI | **Failed visual inspection**, described below; not conflated with the narrow README rendering check | + +The first verification was stopped by the existing absolute-README-image gate, which caught the interim relative media links. The links now use an immutable media revision. An overlapping standalone package check also ran during a rebuild and saw missing generated declarations; final verification was rerun serially and passed. Upstream packages emit missing-sourcemap warnings; no runtime fix or suppression is included here. + +No paid-model evaluation, cloud-Provider account test, physical-device test or new release-upgrade scenario was run. The Native binary used was the installed 0.2.7 executable identified in the media provenance, not a newly downloaded checksum-verified release. Historical evidence and benchmark scores are retained, not replaced by these checks. + +本次只改变文档与文档检查,不改变生产代码、配置、数据格式或版本。完整验证共 1,058 项通过,真实 Native 用例已运行,仅跳过 Windows 专用测试;16 个独立插件仓库和 17 个产物验证通过。中英文桌面与 README 展示检查通过,产品 390px 窄屏失败单独保留。未执行付费模型、真实云账号或物理手机测试,也未发布新版本。 + +## Follow-up: Chinese copy, centered headers and links + +The 2026-09-06 follow-up restores the original centered title, language selector, seven badges, introduction and navigation. GitHub release/version, stars and both dshfind badges are retained. The Stars badge points to the repository homepage because the public stargazers page returned 404. No Git tag is created, deleted or moved: the 43 local tags and 43 remote tags (58 ref lines including peeled annotated tags) match the pre-change snapshots exactly. + +Chinese entry-point copy, the overview, ownership/data-flow/sequence labels and workflow explanations are localized; package names, API identifiers and executable configuration stay unchanged. Previous heading anchors remain available. The two overview SVG/PNG pairs now live in language-specific directories; historical media and tagged release links remain intact. Rendering also caught three English sequence-message semicolons interpreted as Mermaid statement separators; commas fix that syntax without changing the sequence. + +The self-contained documentation revision is `8466e3560a3b9de4e9f4b7302cbf005c84e8e69f`: all 769 local links and 35 Markdown anchors passed before the npm-facing READMEs were changed to absolute permalinks into that revision. Internal navigation in the snapshot stays relative, so changing language or returning to its homepage does not fall back to an older README. This fixes the 20 new-directory/media URLs that returned 404 on the not-yet-merged `main`. + +| Follow-up check | Result | +|---|---| +| Full `pnpm verify` with the existing Native CLI | 122 files / **1,063 tests passed**; Windows-only 1 file/test skipped; deterministic build, Headless, public exports, package contents, publint and attw passed | +| Documentation regressions | **13 passed**; 3 newly reproduced failures cover HTML headings, URL entities/autolinks and query parameters; both headers guard the seven badges and localized overview | +| `pnpm verify:plugins` | **16 independent plugin repositories / 17 packed artifacts passed**, including real DSH packed-Starter and three-enhancement activation | +| Release metadata | All 17 package versions unchanged; release check and changeset coverage passed; no publication | +| README rendering | GitHub Markdown API + github-markdown-css 5.8.1 in Chrome 152; both languages at 1280px and 390px, centered headers, 9 loaded images per page, no horizontal page overflow or page-level JS exceptions | +| Diagrams | Both SVG overviews rendered at 2×; all 6 architecture Mermaid diagrams parsed and rendered with Mermaid 11.12.0 | +| External URLs | 143 unique HTTP(S) targets across 164 Markdown files: **141 returned 200**, with the two qualified cases below | + +- The upstream `dsh-external/issues#603` is in a **private** repository. Its authenticated GitHub API lookup succeeded; unauthenticated HTTP returned 404. The roadmap retains the link and states the access requirement. +- The npm package page returned a **403 anti-bot challenge**, including in Chrome. `npm view dsh-mnemon name version dist-tags --json` confirmed the public package and `latest: 0.5.2`. This verifies the package identity, not successful page rendering; no challenge was bypassed. + +Network checks are a dated manual audit, not a promise of future availability or a network dependency for local CI. README-only permalink changes were followed by the documentation and package checks. The initial browser assertion was also corrected to accept Chromium's `-webkit-center` spelling for HTML paragraph alignment; it was a harness mismatch, not a layout defect. No model calls or new product UI changes are part of this follow-up. + +| README preview | Desktop | Narrow | +|---|---|---| +| English | [1280px](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/documentation-refresh/readme-en-1280.png) | [390px](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/documentation-refresh/readme-en-390.png) | +| 简体中文 | [1280px](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/documentation-refresh/readme-zh-CN-1280.png) | [390px](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/documentation-refresh/readme-zh-CN-390.png) | + +本轮恢复中英文标题区居中与原有 7 枚徽章,保留全部 Git tags;补齐中文图示与流程说明,并保留旧锚点。完整验证更新为 1,063 项通过,独立插件及产物再次通过。143 个外链中 141 个直接返回 200,私有 Issue 经登录 API 核实,npm 防机器人限制单列,不冒充页面访问通过。上方四张截图是 **README 渲染证据**,不是新一次产品 WebUI 测试;下方既有产品窄屏失败仍原样保留。 + +## Narrow-screen finding + +The unmodified production implementation at `d94dfecddc0976b9d0620d1e0e387a5340bd6526`, running on published DSH `0.1.2-rc.1`, showed clipping after resizing an open Memory System settings dialog from 1440×960 to 390×844. The dialog container remained inside the viewport, but the controls and workbench stacking were not visually usable. This is **not a passing responsive-layout result**. It was found during documentation capture; this documentation PR does not change runtime or DSH UI code. + +[Original 390px resize screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/documentation-refresh/narrow-resize-390.png) + +A fresh 390×844 touch-enabled browser context was checked separately: open the Sidebar, then Settings → Memory System. The resize-specific overlapping workbench was absent, but the fixed-width settings navigation left the content column approximately 106px wide, producing excessively wrapped text. [Fresh narrow-context screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/documentation-refresh/narrow-fresh-390.png). This is browser emulation, not a physical mobile-device test, and is also not accepted as a usable layout. + +真实当前实现缩到 390px 后,虽然弹窗外框仍在视口内,设置内容和工作台层叠却出现裁切。因此不能仅凭 bounding-box 断言就宣布窄屏通过。本次保留原始失败截图,不修改业务界面,也不将其作为主展示素材。桌面中英文操作结果与这一失败分别记录。 + +另用新的 390px 触控浏览器上下文复核:没有同样的工作台层叠,但固定导航让内容列只剩约 106px,正文过度换行;仍不作为可用的窄屏布局通过。 diff --git a/dsh/docs/pr-assets/dsh-rc1-compat/README.md b/dsh/docs/pr-assets/dsh-rc1-compat/README.md new file mode 100644 index 000000000..071a5a4f4 --- /dev/null +++ b/dsh/docs/pr-assets/dsh-rc1-compat/README.md @@ -0,0 +1,29 @@ +# DSH prerelease compatibility evidence + +The WebUI screenshots were captured on 2026-09-03 from implementation commit `3c5b28919649f6b2aa6d1696497c234f0784d657`, based on `origin/main` commit `f6cb0c7f13f346f07fa03afed4fd07917ed61059`. + +Every WebUI run used a fresh `DSH_HOME`, Mnemon data root, and workspace. Model traffic stayed on localhost and returned a deterministic synthetic SSE response; memory writes were disabled. The fixture used Mnemon CLI 0.2.6 with SHA-256 `b1bf3e8e315474bb71e83f94893df6247db7d2fbcad685bee2b914e6d1110609`. No credentials, personal memory, or private workspace content are present in these artifacts. + +## Verification matrix + +| DSH version | Dependency mode | Result | +| --- | --- | --- | +| `0.1.2-rc.1` | Exact registry packages | A pnpm 11.7 frozen install passed the 936-entry supply-chain policy check with an exact, lockfile-enforced rc.1 exemption set. Full `pnpm run verify` passed: typecheck, 654 tests with one Windows-only skip, deterministic build, isolated Headless smoke, package-content checks, publint, and attw. The independent WebUI passed plugin/status loading, Runtime/Documents/Memory Spaces navigation, a synthetic conversation, the read-only save-to-memory dialog, and reload persistence. Browser warning/error logs and matching Host diagnostics were empty. | +| `0.1.2-alpha.5` | Built upstream source overlay at `db6bdc3576c2d4e7c965e8e3ed0c2a731eed87f5` | The same full verification chain passed after linking all 23 direct build-time packages, including Store and Invariants. Registry links were restored afterward. | +| `0.1.1-rc.2` | Exact registry package | The backward-regression WebUI passed memory-system loading, a synthetic conversation, and reload persistence in a separate profile. Browser warning/error logs and matching Host diagnostics were empty. | + +## DSH 0.1.2-rc.1 + +The Mnemon status page reports `系统正常` and plugin version 0.4.6: + +![DSH 0.1.2-rc.1 Mnemon status](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-rc1-compat/rc1-status.png) + +The isolated conversation completed and remained present after a full page reload: + +![DSH 0.1.2-rc.1 isolated conversation](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-rc1-compat/rc1-conversation.png) + +## DSH 0.1.1-rc.2 backward regression + +The previous registry baseline still completes the same isolated conversation flow: + +![DSH 0.1.1-rc.2 isolated conversation](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-rc1-compat/rc2-conversation.png) diff --git a/dsh/docs/pr-assets/dsh-style-ui-20260927/README.md b/dsh/docs/pr-assets/dsh-style-ui-20260927/README.md new file mode 100644 index 000000000..ec8ad991f --- /dev/null +++ b/dsh/docs/pr-assets/dsh-style-ui-20260927/README.md @@ -0,0 +1,68 @@ +# DSH-style memory interface + +[简体中文](./README.zh-CN.md) + +Tested implementation: `0213857a7f7d`, stacked on the optional Mnemon CLI change (`adeb9f701374`). macOS 15.6, Node 25.1.0, pnpm 11.19.0, published DSH 0.1.7-rc.2 and Mnemon CLI 0.2.7, headless Chrome 153 at 1280 × 860 unless noted. The WebUI fixture holds the Provider Lab data from the [optional Mnemon CLI record](../optional-mnemon-cli-20260927/README.md): Mnemon Native plus Docker services for OpenViking, Honcho, Mem0, Hindsight, RetainDB and Supermemory, and a local Holographic store. No personal memory or credentials were used. + +"Before" captures come from the base revision in the same fixture; the turn memory bar before this change is the one recorded for #293, which later changes left untouched. + +## Settings → Memory System + +| Before | After | +|---|---| +| ![Settings before](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/settings-before.png) | ![Settings after](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/settings-after.png) | + +Settings is now a column of DSH preference rows in six groups. Placement, storage scope and user profile scope are DSH Menu selectors instead of choice cards, and every switch is the DSH Switch. + +| Storage | Memory providers | +|---|---| +| ![Storage group](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/settings-storage.png) | ![Provider list](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/settings-providers.png) | + +Storage holds the scope, the single Data directory field (empty means the default location), the user profile scope and ZIP backup and migration; none of them sit in the Mnemon Native panel any more. Mnemon Native is the first Provider card. Expanded, it holds only its embedding settings, in the DSH editor block: + +![Mnemon Native expanded](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/settings-native.png) + +![Background tasks](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/settings-background.png) + +The task Agent model and idle review share Background tasks; the review options appear while review is on, with its limits folded. + +## Status + +| Before | After | +|---|---| +| ![Status before](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/status-before.png) | ![Status after](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/status-after.png) | + +Mnemon Native is the first row of the one Memory providers list; the Provider count includes it. The dark theme uses the same tokens: + +![Status in the dark theme](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/status-dark.png) + +## Memory Spaces + +| Before | After | +|---|---| +| ![Spaces before](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/spaces-before.png) | ![Spaces after](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/spaces-after.png) | +| ![Entities before](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/entities-before.png) | ![Entities after](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/entities-after.png) | + +Cards drop the provider stripes and gradients, use DSH Tags, state dots and switches, and show delete and disconnect as DSH card delete actions. Pages under the tabs title their sections at the group heading scale. Entity capability cards mark unsupported Providers as idle instead of failed. + +## Plugins page and conversation + +| Before | After | +|---|---| +| ![Plugins page before](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/plugins-before.png) | ![Plugins page after](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/plugins-after.png) | +| ![Turn memory before](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/turn-before.png) | ![Turn memory after](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/turn-after.png) | + +The main Strategy is a selector row and the enhancements are switch rows, as on DSH's own component list below them. The turn memory bar uses the DSH data icon and chevron with neutral tool Tags. The save dialog edits its candidate in a DSH settings field: + +![Save to memory dialog](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/save-dialog.png) + +## Validation + +- `pnpm run verify`: documentation 2,144 local links and 81 anchors; deterministic build of 42 files; build, typecheck and tests for the workspace packages (Memory Spaces 174 passed with 2 conditional skips, Runtime 61, Documents 39); root suite 1,419 passed with 6 conditional skips; real Headless activation (37 tools), legacy settings import with an idempotent restart and the root disable gate; 52 packed files, publint strict and attw. +- `pnpm run verify:plugins --skip-build`: 17 independent plugin repositories and 18 packed artifacts, including real DSH activation of the packed Starter and of the three optional Strategies. +- `pnpm run release:intent`: change intents cover every changed package. +- WebUI: 16 states each in the light and dark themes at 1280 × 860 and in the light theme at 700 × 900, every Settings group in both themes, a driven conversation turn with the save dialog, and a memory-tool turn; no console errors in any run. Builtin placement was switched on in Settings, opened from a conversation tab, and switched back. + +## Limits + +Screenshots come from one seeded fixture and show layout and styling, not Provider behavior. The Native Provider still labels its spaces "mnemon", from its descriptor. The documentation media for v0.5.4 predate this change and are refreshed with the next release media. Builtin placement keeps the DSH conversation tabs above the Memory System tabs, as before. diff --git a/dsh/docs/pr-assets/dsh-style-ui-20260927/README.zh-CN.md b/dsh/docs/pr-assets/dsh-style-ui-20260927/README.zh-CN.md new file mode 100644 index 000000000..4fb5f1487 --- /dev/null +++ b/dsh/docs/pr-assets/dsh-style-ui-20260927/README.zh-CN.md @@ -0,0 +1,68 @@ +# 与 DSH 统一的记忆界面 + +[English](./README.md) + +实测实现:`0213857a7f7d`,叠加在 Mnemon CLI 可选化(`adeb9f701374`)之上。环境为 macOS 15.6、Node 25.1.0、pnpm 11.19.0、正式 DSH 0.1.7-rc.2 与 Mnemon CLI 0.2.7,无头 Chrome 153,除特别说明外为 1280 × 860。WebUI 夹具使用 [Mnemon CLI 可选化记录](../optional-mnemon-cli-20260927/README.zh-CN.md)中的 Provider Lab 数据:Mnemon Native,加上 Docker 中的 OpenViking、Honcho、Mem0、Hindsight、RetainDB、Supermemory,以及本地 Holographic。未使用个人记忆或凭据。 + +“改版前”截图取自同一夹具中的 base 版本;回合记忆栏的改版前截图来自 #293 的记录,之后的改动没有涉及它。 + +## 设置 → 记忆系统 + +| 改版前 | 改版后 | +|---|---| +| ![改版前的设置页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/settings-before.png) | ![改版后的设置页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/settings-after.png) | + +设置页改为 DSH 的偏好行,分成六组。展示位置、存储范围和用户画像范围由选择卡片改为 DSH Menu 下拉,所有开关都是 DSH Switch。 + +| 存储 | 记忆 Provider | +|---|---| +| ![存储分组](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/settings-storage.png) | ![Provider 列表](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/settings-providers.png) | + +“存储”包含存储范围、唯一的“数据目录”输入框(留空即默认目录)、用户画像范围以及 ZIP 备份与迁移,这些设置都不再放在 Mnemon Native 面板里。Mnemon Native 是第一张 Provider 卡片,展开后只包含它自己的嵌入设置,使用 DSH 的编辑区样式: + +![展开的 Mnemon Native](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/settings-native.png) + +![后台任务](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/settings-background.png) + +任务 Agent 模型与空闲审查同属“后台任务”;空闲审查开启时才显示其选项,间隔与预算收在“审查参数”中。 + +## 状态 + +| 改版前 | 改版后 | +|---|---| +| ![改版前的状态页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/status-before.png) | ![改版后的状态页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/status-after.png) | + +Mnemon Native 是同一个“记忆 Provider”列表的第一行,已启用数量也把它计算在内。深色主题使用同一组令牌: + +![深色主题的状态页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/status-dark.png) + +## 记忆空间 + +| 改版前 | 改版后 | +|---|---| +| ![改版前的记忆空间](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/spaces-before.png) | ![改版后的记忆空间](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/spaces-after.png) | +| ![改版前的实体页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/entities-before.png) | ![改版后的实体页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/entities-after.png) | + +卡片去掉了 Provider 彩色边条与渐变,改用 DSH 标签、状态点与开关,删除与断开使用 DSH 卡片删除样式。Tab 下的页面以分组标题的层级命名区块。实体能力卡片把不支持的 Provider 标为闲置,而不是故障。 + +## 插件页与对话 + +| 改版前 | 改版后 | +|---|---| +| ![改版前的插件页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/plugins-before.png) | ![改版后的插件页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/plugins-after.png) | +| ![改版前的回合记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/turn-before.png) | ![改版后的回合记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/turn-after.png) | + +主策略是一行下拉选择,增强是开关行,与下方 DSH 自带的组件列表一致。回合记忆栏使用 DSH 数据图标与展开箭头,工具名显示为中性标签。“存入记忆”对话框在 DSH 设置字段中编辑候选内容: + +![存入记忆对话框](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/save-dialog.png) + +## 验证 + +- `pnpm run verify`:文档 2,144 个本地链接与 81 个锚点;确定性构建 42 个文件;各 workspace 包的构建、类型检查与测试(记忆空间 174 通过、2 个条件跳过,运行时 61,档案 39);根测试 1,419 通过、6 个条件跳过;真实 Headless 激活(37 个工具)、旧设置导入与幂等重启、根停用门;打包 52 个文件,publint strict 与 attw 通过。 +- `pnpm run verify:plugins --skip-build`:17 个独立插件仓库与 18 个打包制品,包括真实 DSH 激活打包的 Starter 与三个可选 Strategy。 +- `pnpm run release:intent`:变更说明覆盖所有改动的包。 +- WebUI:浅色与深色主题下 1280 × 860 各 16 个状态,浅色主题下 700 × 900 的 16 个状态,两种主题下设置页的每个分组,一次驱动的对话回合(含存入记忆对话框)与一次记忆工具回合;所有运行均无控制台错误。另在设置中开启 Builtin,从会话标签打开记忆系统,再切回侧边栏。 + +## 限制 + +截图来自一个预置数据的夹具,只说明布局与样式,不代表 Provider 行为。Native Provider 在空间上的标签仍来自其描述符,显示为“mnemon”。v0.5.4 的文档媒体早于本次改版,随下一次发布媒体一起更新。Builtin 模式下,DSH 的会话标签仍位于记忆系统标签上方,与改版前相同。 diff --git a/dsh/docs/pr-assets/entities-complete-index/README.md b/dsh/docs/pr-assets/entities-complete-index/README.md new file mode 100644 index 000000000..778101c68 --- /dev/null +++ b/dsh/docs/pr-assets/entities-complete-index/README.md @@ -0,0 +1,79 @@ +# Entities page: counts and lists from every memory + +[简体中文](./README.zh-CN.md) | [Verification data](./verification.json) + +On **Memory System → Memory Spaces → Entities**, the number next to an entity did not match what selecting it listed. On the export behind the report, `dsh-mnemon` counted 22 and listed 5, while `UI` counted 5 and listed 9. Both numbers came from different reads: +- **The rail** added up each space's top 20 entities from `mnemon status`. An entity outside a space's top 20 lost that space's count or vanished: the export has 129 distinct entities, and the rail showed 62. +- **A selection** ran Agent recall (`intent: ENTITY`) through the strict quality policy, which keeps every result scoring 0.6 or more but at most four between 0.25 and 0.6. Recall scored most `dsh-mnemon` memories near 0.5, so 17 of 22 were cut. For `UI`, memories that do not carry it filled the free places. + +Baseline: `main` `2729237a`, the published dsh-mnemon 0.5.23. Fix: `a0ded7ec`. The runs took place on 2026-10-04 (Asia/Shanghai): +- macOS 15.6 arm64, Node 24.19.0 and Mnemon CLI 0.2.9; +- headless Chrome 154 at 1280×800, zh-CN, light; +- the repository's disposable WebUI fixture on DSH 0.2.0-rc.2 and on 0.1.7-rc.2. + +No model is called, and all memories in the screenshots are synthetic. + +## Reproduction + +Four Native spaces about a fictional project, seeded with explicit entities: +- `Atlas` is carried by 22 memories in three spaces; +- `WebUI` is carried by 4, one of them in a space with 23 entities, where it falls outside the top 20; +- 平台实验室 mentions Atlas in three memories without the tag. + +| Before: Atlas | Before: WebUI | +|---|---| +| ![Atlas counted 22, listed 20 including memories without the entity](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/entities-complete-index/before-atlas.jpg) | ![WebUI counted 3, listed 8](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/entities-complete-index/before-webui.jpg) | + +Each space showed "20 observable", and the rail held 52 of the 63 entities. Selecting `Atlas` listed 20 results: the recall limit cut two memories that carry it, and three that only mention it took their places. `WebUI` counted 3 and listed 8. + +## Fix + +- **One index per space.** The Source builds each space's entity index from the memories its Provider lists, so an entity's count is the number of memories that carry it, and the rail holds every entity. Spellings are merged by the key the Overview graph already uses, and each memory counts once. The same key now serves both, so the graph and the rail agree. +- **The selection lists those memories.** Every memory that carries the entity is listed, most important first, six at a time, with further pages read as the list is revealed. +- **Related memories on demand.** Below the list, **Find related memories** runs recall for the entity, with the carrying memories left out before the quality policy selects, so they cannot take its places. Recall is the slow part, about 0.5 s per space, so it stays collapsed until asked for. Once opened, it stays open for the next entities until hidden, for the rest of the browser session. Hiding and opening it again shows what was found without another recall. When no memory carries a name the user entered, related memories open by themselves, since they are all the page can offer. +- **Progressive loading.** Pressing an entity highlights it and shows its count from the rail at once, and the carrying memories come from the index. A placeholder appears only when a read outlasts 160 ms. The spaces show from the directory while their indexes are read, and a late answer for an earlier selection is dropped. +- **Freshness.** An index stays while its Provider statistics stay the same; Mnemon counts every write in its operation log. A write through the Source drops the index at once. The rail checks every space again; a selection reuses that check for ten seconds. When the same page selects another entity, the related recall it was still waiting for is cancelled. Mnemon runs one CLI call at a time per data directory, so this keeps a new selection from queueing behind the old one. +- **Providers.** A Provider whose `list()` stops short of a space, or cannot list, can implement the optional `entityIndex(body)` and return `{ memories, complete }`. An incomplete index is marked partial. An entity Provider with neither appears as query-only and contributes only related memories. The first-party Providers need no change: Mnemon Native lists a whole store, and Holographic and Hindsight list up to 1,000 memories. + +| After: Atlas | After: related memories, collapsed | After: related memories, found | +|---|---|---| +| ![Atlas counted 22 and listed 22](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/entities-complete-index/after-atlas.jpg) | ![WebUI 4 of 4, related memories collapsed with Find related memories](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/entities-complete-index/after-collapsed.jpg) | ![Related memories found for WebUI](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/entities-complete-index/after-related.jpg) | + +On both hosts: + +| | Before | After | +|---|---|---| +| Entities on the rail | 52, each space "20 observable" | 63, each space's own count (29, 23, 8, 7) | +| Atlas: count / listed | 22 / 20, with 3 that do not carry it | 22 / 22; related memories collapsed, no recall run | +| WebUI: count / listed | 3 / 8 | 4 / 4; **Find related memories** shows 4 after 0.7–0.8 s | +| The next entity after related memories were opened | — | Atlas loads its 5 related memories by itself | +| First card after a click | 400–1,891 ms | 91–99 ms | +| Switching entities at once | settles on the second | settles on the second | + +There were no console errors. + +## Scale + +Four synthetic Native spaces with 500 memories each and 139 entities, read through the real Source and Mnemon Native Provider with Mnemon 0.2.9 (medians of three clicks): + +| | Before | After | +|---|---|---| +| Rail, first / again | 94 / 79 ms, 24 entities | 158 / 92 ms, 139 entities | +| `Release`, carried by 855 | 20 results after 1,718 ms | all 855 (paged) after 7 ms; related memories, when asked, 1.8 s | +| `Atlas`, carried by 211 | 20 results after 1,608 ms | all 211 after 7 ms; related memories, when asked, 1.5 s | +| `Component010`, carried by 86 | 20 results after 1,547 ms | all 86 after 10 ms; related memories, when asked, 1.7 s | +| With related memories open: `Atlas`, then `Component010` 0.3 s later | — | 2 ms to the second list; the first related recall cancelled; related after 1.3 s | + +The rail now reads each space once, which costs about 60 ms more on first open. Recall, about 0.5 s per space, runs only for **Find related memories**; the related timings above were read through the Source directly. + +## The export behind the report + +The same build, on a copy of that export (4 active Native spaces, 31 memories; counts only, with no memory content in this record): +- the rail holds 126 entities instead of 62, and every count equals the Overview graph's; +- `dsh-mnemon` counts 22 and lists 22; **Find related memories** finds 4 more; +- `UI` counts 6 instead of 5: one space's top 20 had left it out. It lists 6; **Find related memories** finds 4 more. + +## Tests + +- Memory Spaces: the entity index (spelling merge, per-memory counting, order), every carrying memory listed with its count, related memories without carrying ones, one status and one listing for a rail and a selection together, index reuse and rebuild after statistics change or a Source write, the selection's ten-second reuse, cancellation per page view, a Provider `entityIndex` marked partial, unavailable spaces, input limits; and on the page, the carrying list with related memories found only when asked, the choice carried to the next entity until hidden, related memories by themselves when nothing carries a name, a forgotten memory leaving at once while the others stay until the refresh replaces them, directory-first spaces, a late answer dropped, paging, placeholders only for slow reads, and a related retry that keeps the list. +- Root: the reviewed presentation baseline takes the new entity classes and copy. diff --git a/dsh/docs/pr-assets/entities-complete-index/README.zh-CN.md b/dsh/docs/pr-assets/entities-complete-index/README.zh-CN.md new file mode 100644 index 000000000..07194f9dc --- /dev/null +++ b/dsh/docs/pr-assets/entities-complete-index/README.zh-CN.md @@ -0,0 +1,79 @@ +# 实体页:计数与列表都来自全部记忆 + +**简体中文** | [English](./README.md) | [验证数据](./verification.json) + +在**记忆系统 → 记忆空间 → 实体**中,实体旁的数字与选中后列出的内容对不上。报告所用的导出数据里,`dsh-mnemon` 计数 22、只列出 5 条,`UI` 计数 5、却列出 9 条。两个数字来自不同的读取: +- **左侧列表**把每个空间 `mnemon status` 返回的前 20 个实体相加。实体不在某个空间的前 20 名时,会丢掉该空间的计数,甚至整个消失:这份导出共有 129 个不同实体,左侧只显示 62 个。 +- **选中实体**走的是给 Agent 用的召回(`intent: ENTITY`)及严格质量策略:0.6 分以上的结果全部保留,0.25–0.6 分之间最多保留四条。召回给大多数 `dsh-mnemon` 记忆打了 0.5 分左右,于是 22 条中有 17 条被截掉;对 `UI`,空出的名额被不带它的记忆补上。 + +基线:`main` `2729237a`,即已发布的 dsh-mnemon 0.5.23。修复:`a0ded7ec`。运行时间为 2026-10-04(北京时间): +- macOS 15.6 arm64、Node 24.19.0 与 Mnemon CLI 0.2.9; +- 无头 Chrome 154,1280×800,zh-CN,浅色; +- 仓库自带的一次性 WebUI 测试环境,分别运行在 DSH 0.2.0-rc.2 与 0.1.7-rc.2 上。 + +全程不调用模型,截图中的记忆均为合成数据。 + +## 复现 + +四个围绕虚构项目的 Native 空间,实体均显式指定: +- `Atlas` 由三个空间中的 22 条记忆带有; +- `WebUI` 由 4 条记忆带有,其中一条所在的空间有 23 个实体,它排在前 20 名之外; +- 平台实验室有三条记忆在正文中提到 Atlas,但不带该实体。 + +| 修复前:Atlas | 修复前:WebUI | +|---|---| +| ![Atlas 计数 22,列出 20 条,其中混有不带该实体的记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/entities-complete-index/before-atlas.jpg) | ![WebUI 计数 3,列出 8 条](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/entities-complete-index/before-webui.jpg) | + +每个空间都显示“20 条可观察”,左侧只有 63 个实体中的 52 个。选中 `Atlas` 列出 20 条:召回上限截掉了两条带有它的记忆,三条只是提到它的记忆补了进来。`WebUI` 计数 3,却列出 8 条。 + +## 修复 + +- **每个空间一个索引。** Source 用 Provider 列出的记忆为每个空间建立实体索引,实体的计数就是带有它的记忆数,左侧包含全部实体。不同写法按概览图谱原有的键合并,每条记忆只计一次。两处现在共用同一个键,因此图谱与左侧列表一致。 +- **选中后列出这些记忆。** 带有该实体的记忆全部列出,按重要性排序,每次显示六条,展开到已读取的末尾时再读取下一页。 +- **相关记忆按需查找。** 列表下方的**查找相关记忆**会针对该实体执行召回。带有该实体的记忆在质量策略挑选之前就被排除,不会占用它的名额。召回是最慢的一步,每个空间约 0.5 秒,因此默认折叠,需要时才运行。展开后,在本次浏览器会话中切换到其他实体时保持展开,直到收起为止。收起后再展开,直接显示已找到的结果,不再重新召回。用户输入的名称没有任何记忆带有时,相关记忆会自动显示,因为这时页面只能提供它们。 +- **逐步加载。** 点击实体后立即高亮,并直接用左侧计数显示数量;带有该实体的记忆从索引读取。读取超过 160 毫秒才显示占位。各空间在索引读取期间先按目录显示,对较早选择的迟到响应会被丢弃。 +- **数据新鲜度。** 空间的 Provider 统计不变时沿用其索引;Mnemon 的操作日志记录每一次写入。经 Source 写入会立即使索引失效。左侧列表每次都重新检查各空间,选中实体时在十秒内沿用这次检查。同一页面改选其他实体时,会取消仍在等待的相关召回。Mnemon 对同一数据目录逐个执行 CLI 调用,这样新选择不必排在旧召回之后。 +- **Provider。** `list()` 取不全整个空间或不能列举的 Provider,可以实现可选的 `entityIndex(body)`,返回 `{ memories, complete }`,索引不完整时会标为部分。两者都没有的实体 Provider 显示为仅查询,只贡献相关记忆。第一方 Provider 无需改动:Mnemon Native 会列出整个 Store,Holographic 与 Hindsight 最多列出 1,000 条记忆。 + +| 修复后:Atlas | 修复后:相关记忆折叠 | 修复后:查找到的相关记忆 | +|---|---|---| +| ![Atlas 计数 22,列出 22 条](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/entities-complete-index/after-atlas.jpg) | ![WebUI 4/4,相关记忆折叠,显示查找相关记忆按钮](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/entities-complete-index/after-collapsed.jpg) | ![为 WebUI 查找到的相关记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/entities-complete-index/after-related.jpg) | + +两个宿主上的结果相同: + +| | 修复前 | 修复后 | +|---|---|---| +| 左侧实体数 | 52,各空间“20 条可观察” | 63,各空间各自的实体数(29、23、8、7) | +| Atlas:计数 / 列出 | 22 / 20,其中 3 条不带该实体 | 22 / 22;相关记忆折叠,未运行召回 | +| WebUI:计数 / 列出 | 3 / 8 | 4 / 4;**查找相关记忆**在 0.7–0.8 秒后显示 4 条 | +| 展开相关记忆后选中下一个实体 | — | Atlas 自动加载其 5 条相关记忆 | +| 点击后第一张卡片出现 | 400–1,891 毫秒 | 91–99 毫秒 | +| 连续快速切换实体 | 停在第二个 | 停在第二个 | + +控制台没有错误。 + +## 规模 + +四个合成 Native 空间,每个 500 条记忆,共 139 个实体,经真实的 Source 与 Mnemon Native Provider 读取,Mnemon 0.2.9,取三次点击的中位数: + +| | 修复前 | 修复后 | +|---|---|---| +| 左侧列表,首次 / 再次 | 94 / 79 毫秒,24 个实体 | 158 / 92 毫秒,139 个实体 | +| `Release`,855 条带有 | 1,718 毫秒后显示 20 条 | 7 毫秒后显示全部 855 条(分页);查找相关记忆 1.8 秒 | +| `Atlas`,211 条带有 | 1,608 毫秒后显示 20 条 | 7 毫秒后显示全部 211 条;查找相关记忆 1.5 秒 | +| `Component010`,86 条带有 | 1,547 毫秒后显示 20 条 | 10 毫秒后显示全部 86 条;查找相关记忆 1.7 秒 | +| 相关记忆展开时,选中 `Atlas`,0.3 秒后改选 `Component010` | — | 第二个列表 2 毫秒;第一次相关召回被取消;相关记忆 1.3 秒 | + +左侧列表现在会把每个空间读取一次,首次打开约多 60 毫秒。召回每个空间约 0.5 秒,只在**查找相关记忆**时运行;上表中的相关记忆耗时是直接经 Source 读取的结果。 + +## 报告所用的导出数据 + +同一构建,在那份导出数据的副本上运行(4 个激活的 Native 空间,31 条记忆;本记录只含计数,不含任何记忆内容): +- 左侧有 126 个实体,而不是 62 个,每个计数都与概览图谱一致; +- `dsh-mnemon` 计数 22、列出 22 条;**查找相关记忆**另找到 4 条; +- `UI` 计数 6 而不是 5:某个空间的前 20 名漏掉了它。它列出 6 条;**查找相关记忆**另找到 4 条。 + +## 测试 + +- 记忆空间:实体索引(写法合并、按记忆计数、排序),列出带有实体的全部记忆且与计数一致,相关记忆不含带有实体的记忆,左侧列表与选中同时发生时每个空间只读一次状态与列表,统计变化或经 Source 写入后重建索引,选中时十秒内沿用检查,按页面视图取消相关召回,Provider `entityIndex` 标为部分,不可用的空间,输入上限;页面上覆盖带有实体的列表与只在需要时查找的相关记忆、展开状态延续到下一个实体直到收起、名称无记忆带有时自动显示相关记忆、删除的记忆立即移除而其余记忆保留到刷新结果替换、先按目录显示空间、丢弃迟到的响应、分页、只在读取较慢时显示占位,以及重试相关记忆时保留已显示的列表。 +- 仓库根目录:经审核的展示基线纳入新的实体样式与文案。 diff --git a/dsh/docs/pr-assets/entities-complete-index/verification.json b/dsh/docs/pr-assets/entities-complete-index/verification.json new file mode 100644 index 000000000..6dd4231d1 --- /dev/null +++ b/dsh/docs/pr-assets/entities-complete-index/verification.json @@ -0,0 +1,314 @@ +{ + "verifiedAt": "2026-10-04", + "revision": { + "baseline": "2729237a4871a01c66d3bc2fc8ce56a1eec0a602", + "fix": "a0ded7ec2e49ba2489bfc1ee960a85812151a05e" + }, + "environment": { + "os": "macOS 15.6 arm64", + "node": "24.19.0", + "mnemonCli": "0.2.9", + "browser": "headless Google Chrome 154, 1280x800, zh-CN, light", + "hosts": [ + "DSH 0.2.0-rc.2", + "DSH 0.1.7-rc.2" + ], + "fixture": "scripts/serve-e2e.mjs with synthetic Native spaces copied into its MNEMON_DATA_DIR", + "dshModified": false + }, + "syntheticSpaces": { + "Atlas 架构决策": "14 memories, 12 carry Atlas, 29 entities", + "Atlas 发布与环境": "12 memories, 5 carry Atlas, 1 carries WebUI outside the space's top 20, 23 entities", + "Atlas 前端约定": "5 memories, all carry Atlas, 3 carry WebUI, 8 entities", + "平台实验室": "4 memories, 3 mention Atlas without carrying it, 7 entities" + }, + "webui": { + "before": { + "020": { + "railEntities": 52, + "spaces": [ + "Atlas 架构决策 Mnemon Native 事实、实体与类型关系 实体索引 20 条可观察", + "Atlas 发布与环境 Mnemon Native 事实、实体与类型关系 实体索引 20 条可观察", + "Atlas 前端约定 Mnemon Native 事实、实体与类型关系 实体索引 8 条可观察", + "平台实验室 Mnemon Native 事实、实体与类型关系 实体索引 7 条可观察" + ], + "rapidSwitchSettlesOn": "WebUI", + "consoleErrors": [], + "Atlas": { + "railCount": 22, + "listCount": 20, + "related": null, + "footers": [ + "当前显示 6 / 20" + ], + "firstCardMs": 614 + }, + "WebUI": { + "railCount": 3, + "listCount": 8, + "related": null, + "footers": [ + "当前显示 6 / 8" + ], + "firstCardMs": 400 + } + }, + "017": { + "railEntities": 52, + "spaces": [ + "Atlas 架构决策 Mnemon Native 事实、实体与类型关系 实体索引 20 条可观察", + "Atlas 发布与环境 Mnemon Native 事实、实体与类型关系 实体索引 20 条可观察", + "Atlas 前端约定 Mnemon Native 事实、实体与类型关系 实体索引 8 条可观察", + "平台实验室 Mnemon Native 事实、实体与类型关系 实体索引 7 条可观察" + ], + "rapidSwitchSettlesOn": "WebUI", + "consoleErrors": [], + "Atlas": { + "railCount": 22, + "listCount": 20, + "related": null, + "footers": [ + "当前显示 6 / 20" + ], + "firstCardMs": 1891 + }, + "WebUI": { + "railCount": 3, + "listCount": 8, + "related": null, + "footers": [ + "当前显示 6 / 8" + ], + "firstCardMs": 783 + } + } + }, + "after": { + "020": { + "railEntities": 63, + "spaces": [ + "Atlas 架构决策 Mnemon Native 事实、实体与类型关系 实体索引 29 个实体", + "Atlas 发布与环境 Mnemon Native 事实、实体与类型关系 实体索引 23 个实体", + "Atlas 前端约定 Mnemon Native 事实、实体与类型关系 实体索引 8 个实体", + "平台实验室 Mnemon Native 事实、实体与类型关系 实体索引 7 个实体" + ], + "rapidSwitchSettlesOn": "WebUI", + "consoleErrors": [], + "Atlas": { + "railCount": 22, + "listCount": 22, + "related": null, + "footers": [ + "当前显示 6 / 22" + ], + "firstCardMs": 97, + "relatedSection": { + "button": "查找相关记忆", + "expanded": false + } + }, + "WebUI": { + "railCount": 4, + "listCount": 4, + "related": null, + "footers": [ + "当前显示 4 / 4" + ], + "firstCardMs": 91, + "relatedSection": { + "button": "查找相关记忆", + "expanded": false + }, + "findRelated": { + "related": 4, + "relatedMs": 829, + "button": "收起" + } + }, + "nextEntityAfterFindRelated": { + "entity": "Atlas", + "listCount": 22, + "related": 5, + "expanded": true + } + }, + "017": { + "railEntities": 63, + "spaces": [ + "Atlas 架构决策 Mnemon Native 事实、实体与类型关系 实体索引 29 个实体", + "Atlas 发布与环境 Mnemon Native 事实、实体与类型关系 实体索引 23 个实体", + "Atlas 前端约定 Mnemon Native 事实、实体与类型关系 实体索引 8 个实体", + "平台实验室 Mnemon Native 事实、实体与类型关系 实体索引 7 个实体" + ], + "rapidSwitchSettlesOn": "WebUI", + "consoleErrors": [], + "Atlas": { + "railCount": 22, + "listCount": 22, + "related": null, + "footers": [ + "当前显示 6 / 22" + ], + "firstCardMs": 99, + "relatedSection": { + "button": "查找相关记忆", + "expanded": false + } + }, + "WebUI": { + "railCount": 4, + "listCount": 4, + "related": null, + "footers": [ + "当前显示 4 / 4" + ], + "firstCardMs": 95, + "relatedSection": { + "button": "查找相关记忆", + "expanded": false + }, + "findRelated": { + "related": 4, + "relatedMs": 719, + "button": "收起" + } + }, + "nextEntityAfterFindRelated": { + "entity": "Atlas", + "listCount": 22, + "related": 5, + "expanded": true + } + } + } + }, + "scale": { + "data": "4 synthetic Native spaces x 500 memories, 139 entities; real Source and Mnemon Native Provider; medians of 3", + "before": { + "label": "before", + "rail": { + "coldMs": 94, + "warmMs": 79, + "entities": 24, + "listed": 24 + }, + "clicks": { + "Release": { + "railCount": 855, + "listed": 20, + "related": 0, + "firstContentMs": 1718, + "allMs": 1718 + }, + "Atlas": { + "railCount": 211, + "listed": 20, + "related": 0, + "firstContentMs": 1608, + "allMs": 1608 + }, + "Component010": { + "railCount": 86, + "listed": 20, + "related": 0, + "firstContentMs": 1547, + "allMs": 1547 + } + }, + "api": "entities(entity)" + }, + "after": { + "label": "after", + "rail": { + "coldMs": 158, + "warmMs": 92, + "entities": 139, + "listed": 139 + }, + "clicks": { + "Release": { + "railCount": 855, + "listed": 855, + "related": 0, + "firstContentMs": 7, + "allMs": 1833 + }, + "Atlas": { + "railCount": 211, + "listed": 211, + "related": 2, + "firstContentMs": 7, + "allMs": 1495 + }, + "Component010": { + "railCount": 86, + "listed": 86, + "related": 4, + "firstContentMs": 10, + "allMs": 1674 + } + }, + "api": "entity-memories + entity-related", + "rapid": { + "from": "Atlas", + "to": "Component010", + "toMemoriesMs": 2, + "toRelatedMs": 1342, + "firstRelated": "superseded by a newer entity selection", + "totalMs": 1796 + } + } + }, + "reportedExport": { + "data": "a copy of the export behind the report: 4 active Native spaces, 31 memories; counts only, no content recorded", + "before": { + "railEntities": 62, + "distinctEntities": 129, + "dsh-mnemon": { + "railCount": 22, + "listed": 5 + }, + "UI": { + "railCount": 5, + "carrying": 6, + "listed": 10, + "listedWithEmbeddings": 9 + } + }, + "after": { + "railEntities": 126, + "railMatchesOverviewGraph": true, + "dsh-mnemon": { + "railCount": 22, + "listed": 22, + "foundRelated": 4 + }, + "UI": { + "railCount": 6, + "listed": 6, + "foundRelated": 4 + }, + "DSH": { + "railCount": 11, + "listed": 11 + }, + "WebUI": { + "railCount": 6, + "listed": 6 + } + }, + "note": "126 merges spellings that differ only in case; the 129 distinct entities count them separately" + }, + "tests": { + "memorySpaces": "216 passed, 2 skipped; new: 10 entity-index and 9 Entities page tests", + "root": "1,611 passed, 6 skipped; presentation baseline updated", + "verify": "pnpm run verify passes; package 1,524,506 unpacked bytes" + }, + "screenshots": { + "before-atlas.jpg": "5dd1497e1e37d8d468b71efbcc896dd8616c0f080f02a1bed20c915b4af6d630", + "before-webui.jpg": "ddd4f6f6d8fdc9b1c0e93a2478ecb3f7aef3b21880c05a09a4535df021a0010a", + "after-atlas.jpg": "3c6d33a79a9582fc18886205d0aebd796a54fda5766eeb62c54bd68dfd92118f", + "after-collapsed.jpg": "47d128b7b324e215be01405d325c6b3b1086860bf6018537c761f38fef02a92e", + "after-related.jpg": "0a3871de447ed296e408018d8a59c96695065efedd55ee80cb4a11e10bd98e7f" + } +} diff --git a/dsh/docs/pr-assets/idle-review-agent-team/README.md b/dsh/docs/pr-assets/idle-review-agent-team/README.md new file mode 100644 index 000000000..2f6d13e74 --- /dev/null +++ b/dsh/docs/pr-assets/idle-review-agent-team/README.md @@ -0,0 +1,85 @@ +# Idle review limits and Agent Teams compatibility + +[简体中文](./README.zh-CN.md) | **English** + +Issues [#100](https://github.com/omdsh-dev/dsh-mnemon/issues/100) and [#255](https://github.com/omdsh-dev/dsh-mnemon/issues/255), reproduced from `1363ebffaf19c9ab4badf0137f6fe87acacaf989` on 2026-09-16. The implementation is committed as `2ca95ee1953f1cadd589a17255f21f87375e6038`; this evidence commit changes documentation and images only. Browser checks use the real DSH WebUI, published Host packages, disposable local storage, and all three Strategy enhancements. Model replies are deterministic loopback fixtures; these checks do not measure model judgment or external API reliability. + +## Before and after + +| Check | Before | With this change | +| --- | --- | --- | +| 30 eligible turns, successful review, configured cap 2 | 15 child reviews | 2 child reviews; clearing/compacting context does not reset the loaded Agent's budget | +| 30 turns over 150 seconds, failed review | 29 attempts | 1 attempt within the configured five-minute minimum interval | +| Missing fork provider | Review rejects | Default bounded spawn works; explicit fork can fall back to spawn or skip before startup | +| Published Agent Teams service and tools | Both fork and spawn fail with `TEAM_NOT_MEMBER` after the first child model step | Review pauses before child creation and explains the incompatible composition | +| Document and Runtime writes followed by a review failure | Failure does not carry both committed write receipts to the workspace | Failure remains a failure; the workspace lists both receipts for reconciliation and does not replay the run | + +The two cadence regressions were run against the baseline implementation before the fix. The matching assertions now pass in `tests/lifecycle.spec.ts`. The production defaults are five minutes between attempts and at most 20 attempts per loaded parent Agent; the smaller test cap makes the bound observable. + +| Actual Team error on the baseline | Compatibility pause on the changed build | Partial failure with committed receipts | +| --- | --- | --- | +| [Screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/idle-review-agent-team/before-team-error.png) | [Screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/idle-review-agent-team/after-team-paused.png) | [Screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/idle-review-agent-team/after-final-partial-receipts.png) | + +The Team WebUI workload has two completed parent turns and five complete overview chunks containing 335 synthetic entries. Its normal eligibility threshold remains 5; the second substantive turn raises the accumulated score to 6. The baseline creates one review child, hits the Team error, and disposes the resident child while retaining its inactive catalog row. The changed build creates no child and makes no review model request during the recorded 190-second quiet interval. [Sanitized WebUI observations](./webui-evidence.json) include package versions, workload, build hashes, disposal evidence, and the telemetry differences between runs. + +The recorded Team build predates the final Settings layout refinement and the cleanup-success counter correction. The public compatibility predicate and its scheduling gate are unchanged; the standalone guarded matrix was rerun against the final source. Screenshots contain only synthetic memory and opaque test identifiers. + +## Published Agent Teams reproduction + +The optional fixture uses only npm DSH `0.1.5-rc.2` and the two Agent Teams `0.1.5-alpha.2` packages. It does not modify DSH sources or add those experimental packages to this repository's dependencies. Use Node with TypeScript stripping (verified with Node 25.1.0): + +```sh +team_profile=$(mktemp -d) +npm install --prefix "$team_profile" --save-exact \ + @deepseek-ai/dsh@0.1.5-rc.2 \ + @deepseek-ai/dsh-experimental-agent-team@0.1.5-alpha.2 \ + @deepseek-ai/dsh-experimental-tool-agent-team@0.1.5-alpha.2 + +node --experimental-strip-types scripts/fixtures/reproduce-published-team.mjs \ + --profile "$team_profile" --output "$team_profile/baseline.json" +node --experimental-strip-types scripts/fixtures/reproduce-published-team.mjs \ + --profile "$team_profile" --guard --output "$team_profile/guarded.json" +``` + +The explicit profile path is required. The script asserts resolved package versions and removes each case's temporary sessions. `--guard` imports the real `idleReviewBlockReason` from this repository; it does not duplicate its predicate. Both commands exit zero when all six expected outcomes match. An asserted baseline error is evidence of the defect, not a successful review. + +| Composition | Fork | Spawn | Child model calls / probe executions | +| --- | --- | --- | --- | +| No Team plugins | Completed | Completed | 2 / 1 | +| Team service only | Completed | Completed | 2 / 1 | +| Team service and tools | Failed | Failed | 1 / 1 | +| Team service and tools with the guard | Skipped before child creation | Skipped before child creation | 0 / 0 | + +Each parent completes a turn. The child uses a bounded persona, `maxDepth: 1`, the idle-review label, and an allowlist containing one synthetic probe. With either provider, the child is first published without its descriptor. Team tools install their policy while membership reports lead; the shared driver later appends the one-shot descriptor and membership disappears. The first probe still executes, but the next prompt's Team policy throws `TEAM_NOT_MEMBER`. + +The guard checks only public `ctx.get('agentTeams')` and `tools.get('spawn_teammate', parent)` capabilities. The explicit parent scope matters. Service-only controls still work. Guarded Team cases create no child or catalog row, then successfully execute the parent's official `team_task_create` tool. Results: [baseline matrix](./published-team-baseline.json), [guarded matrix](./published-team-guarded.json). + +This pauses automatic review for the affected composition. It does not repair the upstream Team policy or claim that manual delegation under that composition works. Removing Team tools allows the next eligible completed turn to schedule a review. + +## Partial failure and Settings reproduction + +Build the repository, point `MNEMON_CLI_PATH` to an official Mnemon 0.2.8 binary, and run: + +```sh +pnpm run build +pnpm --workspace-concurrency=4 -r build +MNEMON_CLI_PATH=/path/to/mnemon pnpm e2e:serve --idle-review --strategy-extensions +``` + +Choose the Mnemon E2E preset. Send two substantive user messages of at least 150 characters to pass the ordinary activity threshold, then wait at least five seconds. The fixture has `idleReview.minIntervalMs: 5000` and `idleReview.maxPerSession: 1`. It performs a real Document create and a real project Runtime add, verifies both receipts, then returns the intentional HTTP 400 error `SYNTHETIC_REVIEW_FAILURE_AFTER_COMMIT`. Refresh the Memory System status to see the failed run and both receipts. Additional eligible turns do not create another review child while that loaded Agent has exhausted its budget. + +The first recorded partial-failure run used the default five-minute minimum interval; its later quiet turn proves cooldown and no immediate replay. After loading the final build, the browser changed the interval to 5,000 ms and opened a fresh parent session. Two eligible turns produced one failed review. A third eligible turn followed by more than 30 seconds produced no second review, isolating the one-attempt cap. The browser then disabled review and raised the cap to 2; a fourth eligible turn followed by more than 10 seconds still created no child, isolating the independent switch. These are two sessions in one fixture log, not one child across the entire log. [Sanitized partial-failure observations](./partial-failure-evidence.json) distinguish them. The final status shows two stored Documents because the earlier session's committed Document is retained. + +| Enabled with a five-second interval and cap 1 | Disabled with available budget (cap 2) | Four completed turns and one child | +| --- | --- | --- | +| [Screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/idle-review-agent-team/after-review-budget.png) | [Screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/idle-review-agent-team/after-review-disabled.png) | [Screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/idle-review-agent-team/after-bounded-conversation.png) | + +In Settings, the independent automatic-review switch leaves recall and writeback settings unchanged. Numeric limits are validated before saving; the existing authorized Settings RPC enforces the same bounds and rejects writes in read-only mode. Automated coverage checks those rejection paths, cancellation, missing guard support, foreign child receipts, and denied own-scope plugin tools. + +## Validation and limits + +`MNEMON_NATIVE_TEST_CLI=/path/to/mnemon pnpm run verify` passed on macOS arm64, Node 25.1.0 and pnpm 11.19.0: 1,156 root tests passed, five real-model opt-in checks skipped in two files, the Windows-only plugin smoke check skipped on macOS, all standard plugin suites passed, and the real Native Source create/write/recall/forget integration passed. Types, deterministic builds, Headless activation with 39 tools, public entries, publint and attw passed. The root package contains 47 files, 298,457 packed bytes and 1,335,573 unpacked bytes; the intentional checkpoint, receipt and Settings additions fit the reviewed 1,338,000-byte ceiling without adding package files or exposing implementation sources. + +The real Native CLI reports 0.2.8 and SHA-256 `f8f21151ce9777983b8d2dfb9cbea1a00b223de6063659af232d04aa57cedad0`. Real Host evidence tests cover fork/spawn with native tools and Code Mode, including complete 19,112-character overview evidence and blocked foreign tools. + +The attempt budget is in memory per loaded Agent and resets on Agent unload/reopen or Host restart. Public disposal removes resident children, but the published Host exposes no plugin-scoped archive/TTL API for retained session history. Existing history is preserved, and no private persistence files or unrelated agents are deleted. Bounded spawn may omit oversized or older messages; fork is explicit and inherits the complete parent context. `maxTokens` limits each model response, not total multi-step usage. diff --git a/dsh/docs/pr-assets/idle-review-agent-team/README.zh-CN.md b/dsh/docs/pr-assets/idle-review-agent-team/README.zh-CN.md new file mode 100644 index 000000000..b76a612ea --- /dev/null +++ b/dsh/docs/pr-assets/idle-review-agent-team/README.zh-CN.md @@ -0,0 +1,85 @@ +# 空闲审查限额与 Agent Teams 兼容性 + +**简体中文** | [English](./README.md) + +关联 [#100](https://github.com/omdsh-dev/dsh-mnemon/issues/100) 和 [#255](https://github.com/omdsh-dev/dsh-mnemon/issues/255),于 2026-09-16 基于 `1363ebffaf19c9ab4badf0137f6fe87acacaf989` 复现。实现已提交为 `2ca95ee1953f1cadd589a17255f21f87375e6038`,本证据提交仅改变文档和图片。浏览器验证使用真实 DSH WebUI、已发布的 Host 包、临时本地存储,并开启全部三个 Strategy 增强。模型回复来自确定性的本地回环夹具;这些检查不衡量模型判断质量或外部 API 可靠性。 + +## 修改前后 + +| 检查 | 修改前 | 本次修改后 | +| --- | --- | --- | +| 30 轮符合条件的对话,审查成功,次数上限设为 2 | 创建 15 个审查子代理 | 创建 2 个;清空或压缩上下文不会重置已加载 Agent 的次数预算 | +| 150 秒内 30 轮对话,审查失败 | 尝试 29 次 | 在配置的五分钟最小间隔内仅尝试 1 次 | +| 缺少 fork Provider | 审查拒绝执行 | 默认使用有界 spawn;显式 fork 可在启动前降级为 spawn 或跳过 | +| 已发布的 Agent Teams 服务和工具同时启用 | fork 与 spawn 都在子代理首步后出现 `TEAM_NOT_MEMBER` | 创建子代理前暂停审查,并说明不兼容组合 | +| Document 与 Runtime 写入后审查失败 | 工作区的失败状态没有同时携带两项已提交回执 | 仍明确报告失败,并列出回执供核对,不会重放本次运行 | + +两项频率回归均先在基线实现上运行并复现失败,修复后 `tests/lifecycle.spec.ts` 的对应断言通过。产品默认尝试间隔为五分钟,每个已加载父 Agent 最多尝试 20 次;测试采用较小上限以直接观察边界。 + +| 基线上的真实 Team 错误 | 修改后的兼容性暂停 | 携带已提交回执的部分失败 | +| --- | --- | --- | +| [截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/idle-review-agent-team/before-team-error.png) | [截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/idle-review-agent-team/after-team-paused.png) | [截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/idle-review-agent-team/after-final-partial-receipts.png) | + +Team WebUI 工作负载包含两个已完成父轮次和五段完整概览,共 335 条合成条目。常规准入阈值仍为 5,第二条实质性消息使累计分数达到 6。基线创建一个审查子代理,发生 Team 错误后释放驻留实例,但保留非活跃目录记录。修改后的构建在记录的 190 秒安静期内没有创建子代理,也没有请求审查模型。[脱敏 WebUI 观测](./webui-evidence.json) 包含包版本、工作负载、构建哈希、释放证据及两次遥测的差异。 + +Team 记录使用的构建早于最终 Settings 布局优化及清理成功后再增加成功计数的调整;公开兼容性检查和调度准入逻辑未变,独立的受保护矩阵也已针对最终源码重新执行。截图仅包含合成记忆和不透明的测试标识。 + +## 已发布 Agent Teams 复现 + +可选夹具仅使用 npm DSH `0.1.5-rc.2` 及两个 Agent Teams `0.1.5-alpha.2` 包,不修改 DSH 源码,也不把实验包加入本仓库依赖。使用支持 TypeScript stripping 的 Node(已用 Node 25.1.0 验证): + +```sh +team_profile=$(mktemp -d) +npm install --prefix "$team_profile" --save-exact \ + @deepseek-ai/dsh@0.1.5-rc.2 \ + @deepseek-ai/dsh-experimental-agent-team@0.1.5-alpha.2 \ + @deepseek-ai/dsh-experimental-tool-agent-team@0.1.5-alpha.2 + +node --experimental-strip-types scripts/fixtures/reproduce-published-team.mjs \ + --profile "$team_profile" --output "$team_profile/baseline.json" +node --experimental-strip-types scripts/fixtures/reproduce-published-team.mjs \ + --profile "$team_profile" --guard --output "$team_profile/guarded.json" +``` + +必须显式指定 Profile 路径。脚本断言实际解析的包版本,并删除每个用例的临时会话。`--guard` 导入本仓库真实的 `idleReviewBlockReason`,没有复制检查逻辑。六种预期结果均符合时,两条命令都以零状态退出;基线中的预期错误是缺陷证据,不代表审查成功。 + +| 组合 | Fork | Spawn | 子代理模型调用数 / 探针执行数 | +| --- | --- | --- | --- | +| 无 Team 插件 | 完成 | 完成 | 2 / 1 | +| 仅 Team 服务 | 完成 | 完成 | 2 / 1 | +| Team 服务与工具 | 失败 | 失败 | 1 / 1 | +| Team 服务与工具,加兼容性检查 | 创建子代理前跳过 | 创建子代理前跳过 | 0 / 0 | + +各父代理先完成一轮对话。子代理采用有界 persona、`maxDepth: 1`、空闲审查标签,以及只允许一个合成探针的工具列表。两种 Provider 都先发布尚无 descriptor 的子代理;此时 Team 认为其为 lead 并安装策略。共用 driver 随后追加一次性 descriptor,成员资格随即消失。首个探针仍执行,但下一步组装提示词时 Team 策略抛出 `TEAM_NOT_MEMBER`。 + +检查仅使用公开的 `ctx.get('agentTeams')` 与 `tools.get('spawn_teammate', parent)` 能力,必须明确传入父代理作用域。仅启用服务的对照组仍正常工作。受保护的 Team 组合不创建子代理或目录记录,之后父代理仍成功执行官方 `team_task_create` 工具。结果:[基线矩阵](./published-team-baseline.json)、[受保护矩阵](./published-team-guarded.json)。 + +此处理会暂停受影响组合的自动审查,不修复上游 Team 策略,也不声称该组合下的手动委派已可正常工作。移除 Team 工具后,下一个符合条件的已完成轮次可重新调度审查。 + +## 部分失败与 Settings 复现 + +构建仓库,将 `MNEMON_CLI_PATH` 指向官方 Mnemon 0.2.8 可执行文件,再运行: + +```sh +pnpm run build +pnpm --workspace-concurrency=4 -r build +MNEMON_CLI_PATH=/path/to/mnemon pnpm e2e:serve --idle-review --strategy-extensions +``` + +选择 Mnemon E2E 预设,发送两条至少 150 字符的实质性用户消息以达到常规活动阈值,等待至少五秒。该夹具设有 `idleReview.minIntervalMs: 5000` 与 `idleReview.maxPerSession: 1`。它执行真实的 Document 创建与项目 Runtime 添加,确认两项回执后故意返回 HTTP 400 错误 `SYNTHETIC_REVIEW_FAILURE_AFTER_COMMIT`。刷新记忆系统状态即可看到失败运行及两项回执。该已加载 Agent 的预算耗尽后,继续发送符合条件的消息也不会再创建审查子代理。 + +第一次部分失败记录采用默认五分钟最小间隔,其后安静轮次只证明冷却和不立即重放。载入最终构建后,浏览器将间隔改为 5,000 毫秒,并打开新的父会话。两轮符合条件的对话产生一个失败审查;第三轮符合条件的对话结束后等待超过 30 秒,仍无第二个审查,单独验证了一次上限。随后关闭审查并将上限提高至 2,第四轮符合条件的对话后等待超过 10 秒仍无新增子代理,单独验证了独立开关。同一夹具日志包含两个会话,不能宣称整份日志只有一个子代理。[脱敏部分失败观测](./partial-failure-evidence.json) 区分了两次记录。最终状态显示两份档案,是因为前一会话已提交的档案仍被保留。 + +| 启用,五秒间隔,上限 1 | 关闭,尚有剩余预算(上限 2) | 四个已完成轮次与一个子代理 | +| --- | --- | --- | +| [截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/idle-review-agent-team/after-review-budget.png) | [截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/idle-review-agent-team/after-review-disabled.png) | [截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/idle-review-agent-team/after-bounded-conversation.png) | + +Settings 中的自动审查开关独立于召回和主动写入设置。数值限额在保存前校验,现有授权 Settings RPC 同样校验范围并拒绝只读模式下的写入。自动化覆盖这些拒绝路径、取消、缺少保护能力、其他子代理回执,以及其他插件自身作用域工具被拒绝执行的情况。 + +## 验证与边界 + +在 macOS arm64、Node 25.1.0、pnpm 11.19.0 上,`MNEMON_NATIVE_TEST_CLI=/path/to/mnemon pnpm run verify` 通过:根目录 1,156 项测试通过,两个文件中的五项真实模型可选检查跳过,Windows 专用插件冒烟检查在 macOS 上跳过;所有常规插件测试及真实 Native Source 的创建、写入、召回、遗忘集成均通过。类型、确定性构建、39 个工具的 Headless 激活、公开入口、publint 和 attw 均通过。根包共 47 个文件,压缩后 298,457 字节,解包后 1,335,573 字节;新增检查点、回执与 Settings 实现仍处于已审查的 1,338,000 字节上限内,没有新增包文件或暴露实现源码。 + +真实 Native CLI 版本为 0.2.8,SHA-256 为 `f8f21151ce9777983b8d2dfb9cbea1a00b223de6063659af232d04aa57cedad0`。真实 Host 证据测试覆盖 fork/spawn 与原生工具、Code Mode 的组合,包括完整 19,112 字符概览证据和其他工具被拒绝的情况。 + +尝试次数存于已加载 Agent 的内存中,卸载后重新打开 Agent 或重启 Host 会重置。公开释放 API 移除驻留子代理,但已发布 Host 没有面向插件的历史归档或 TTL API。因此保留既有历史,不删除私有持久化文件或无关代理。有界 spawn 可省略过大或较早消息;显式 fork 继承完整父上下文。`maxTokens` 限制每次模型回复,不代表整次多步运行的总限额。 diff --git a/dsh/docs/pr-assets/idle-review-agent-team/partial-failure-evidence.json b/dsh/docs/pr-assets/idle-review-agent-team/partial-failure-evidence.json new file mode 100644 index 000000000..b1aa13494 --- /dev/null +++ b/dsh/docs/pr-assets/idle-review-agent-team/partial-failure-evidence.json @@ -0,0 +1,63 @@ +{ + "date": "2026-09-16", + "implementationCommit": "2ca95ee1953f1cadd589a17255f21f87375e6038", + "host": "Published DSH 0.1.5-rc.1 WebUI from the repository dependency cohort", + "nativeCli": "Official Mnemon 0.2.8", + "model": "Deterministic loopback fixture. Model labels in the UI are preset metadata; no external model API was contacted.", + "strategies": [ + "scoped", + "light-context", + "auto-capture" + ], + "firstSession": { + "minimumIntervalMs": 300000, + "attemptCap": 1, + "reviewEnabled": true, + "fixtureReviewOrdinal": 1, + "documentId": "28b1e0e2-f3e8-4a67-b070-aa5aaa8e27bc", + "runtimeRevision": "eb9630ea084410335e336d32908376d78f2b1319276be70b0ddd15c34cbdcca9", + "outcome": "Two confirmed Source receipts followed by the intentional HTTP 400 error.", + "laterTurn": "No immediate replay. The five-minute interval prevents using this observation alone as proof of the one-attempt cap." + }, + "finalSession": { + "loadedFinalBuildAfterHostRestart": true, + "settingsSavedThroughWebUi": true, + "minimumIntervalMs": 5000, + "initialAttemptCap": 1, + "initialReviewEnabled": true, + "fixtureReviewOrdinal": 2, + "childRunId": "65b6f016-bf42-48e1-bb90-897192369e57", + "documentId": "2331330a-dc58-46f6-886d-711cab7d8603", + "runtimeRevision": "eb9630ea084410335e336d32908376d78f2b1319276be70b0ddd15c34cbdcca9", + "firstTwoTurns": "Two substantive synthetic user turns reached the normal eligibility threshold and created one review child.", + "reviewOutcome": "Document create and Runtime add returned committed receipts, then the fixture returned INVALID_REQUEST: SYNTHETIC_REVIEW_FAILURE_AFTER_COMMIT.", + "thirdTurn": { + "fixtureModelRequest": 16, + "eligible": true, + "observedQuietSecondsAtLeast": 30, + "reviewChildCount": 1, + "meaning": "One-attempt session cap holds after the five-second interval elapsed." + }, + "fourthTurn": { + "fixtureModelRequest": 17, + "eligible": true, + "reviewEnabled": false, + "attemptCap": 2, + "observedQuietSecondsAtLeast": 10, + "reviewChildCount": 1, + "meaning": "Independent disabled switch holds with unused attempt budget." + }, + "finalVisibleParentTurns": 4, + "finalVisibleReviewChildren": 1, + "finalStatus": "Failure and both committed receipt identifiers remain visible after ordinary turns and Settings changes. No rollback or automatic replay.", + "retainedStorage": "The same disposable Profile keeps both independent sessions. Final status shows two Documents and one deduplicated synthetic Runtime entry; prior session/catalog history is retained." + }, + "screenshots": [ + "after-final-partial-receipts.png", + "after-review-budget.png", + "after-review-disabled.png", + "after-bounded-conversation.png" + ], + "loggingBoundary": "The outer model fixture remains alive across the Host restart, so review ordinals and model request numbers span both independent sessions. Total review-start events in the complete log are two, one per parent session.", + "fixtureReproduction": "The committed --idle-review fixture now sets both idleReviewMs and idleReview.minIntervalMs to 5000, with maxPerSession 1. Settings controls can change these values live." +} diff --git a/dsh/docs/pr-assets/idle-review-agent-team/published-team-baseline.json b/dsh/docs/pr-assets/idle-review-agent-team/published-team-baseline.json new file mode 100644 index 000000000..f06bb4214 --- /dev/null +++ b/dsh/docs/pr-assets/idle-review-agent-team/published-team-baseline.json @@ -0,0 +1,1236 @@ +{ + "versions": { + "dsh": "0.1.5-rc.2", + "dsh-agent": "0.1.5-rc.2", + "dsh-agent-loop": "0.1.5-rc.2", + "dsh-subagent": "0.1.5-rc.2", + "dsh-subagent-in-process-driver": "0.1.5-rc.2", + "dsh-subagent-fork-in-process": "0.1.5-rc.2", + "dsh-subagent-spawn-in-process": "0.1.5-rc.2", + "dsh-experimental-agent-team": "0.1.5-alpha.2", + "dsh-experimental-tool-agent-team": "0.1.5-alpha.2" + }, + "results": [ + { + "provider": "fork", + "teams": "off", + "guarded": false, + "parentModelCalls": 1, + "childModelCalls": 2, + "probeExecutions": [ + "051a0364-1530-4b4e-a0de-a5f26cfd1e37" + ], + "events": [ + { + "ordinal": 1, + "stage": "created", + "subject": "parent", + "descriptor": null, + "teamRole": null, + "tools": [ + "lifecycle_probe" + ] + }, + { + "ordinal": 2, + "stage": "created", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": null, + "teamRole": null, + "tools": [ + "lifecycle_probe" + ] + }, + { + "ordinal": 3, + "stage": "start-returned", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": null, + "teamRole": null + }, + { + "ordinal": 4, + "stage": "descriptor-appended", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "fork", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + }, + { + "ordinal": 5, + "stage": "model-request", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "fork", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null, + "tools": [ + "lifecycle_probe" + ], + "promptCallsChildLead": false + }, + { + "ordinal": 6, + "stage": "model-request", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "fork", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null, + "tools": [ + "lifecycle_probe" + ], + "promptCallsChildLead": false + }, + { + "ordinal": 7, + "stage": "result-settled", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "fork", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + }, + { + "ordinal": 8, + "stage": "disposed", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "fork", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + } + ], + "preflight": { + "hasTeamService": false, + "hasScopedTeamSpawnTool": false, + "hasGlobalTeamSpawnTool": false + }, + "childModelToolNames": [ + "lifecycle_probe" + ], + "result": { + "output": [ + { + "type": "text", + "text": "Synthetic child completed." + } + ], + "stopReason": "completed" + }, + "residentBeforeDispose": true, + "catalogBeforeDispose": { + "entries": [ + { + "kind": "child", + "id": "051a0364-1530-4b4e-a0de-a5f26cfd1e37", + "mode": "one-shot", + "label": "Mnemon idle checkpoint review", + "activity": "inactive", + "hasChildren": false + } + ], + "parentAvailable": true + }, + "childEventTypes": [ + "agent/inbox/spliced", + "turn/start", + "agent/inbox/spliced", + "step/start", + "system/message", + "user/message", + "request/header", + "request/context", + "assistant/message", + "step/end", + "turn/end", + "session/end-seed", + "agent/inbox/spliced", + "turn/start", + "agent/inbox/spliced", + "subagent/descriptor", + "step/start", + "system/message", + "user/message", + "user/message", + "request/header", + "assistant/message", + "tool/call", + "tool/result", + "step/end", + "step/start", + "assistant/message", + "step/end", + "turn/end" + ], + "residentAfterDispose": false, + "catalogAfterDispose": { + "entries": [ + { + "kind": "child", + "id": "051a0364-1530-4b4e-a0de-a5f26cfd1e37", + "mode": "one-shot", + "label": "Mnemon idle checkpoint review", + "activity": "inactive", + "hasChildren": false + } + ], + "parentAvailable": true + } + }, + { + "provider": "spawn", + "teams": "off", + "guarded": false, + "parentModelCalls": 1, + "childModelCalls": 2, + "probeExecutions": [ + "bef33933-3870-433c-b172-7ae62d743f61" + ], + "events": [ + { + "ordinal": 1, + "stage": "created", + "subject": "parent", + "descriptor": null, + "teamRole": null, + "tools": [ + "lifecycle_probe" + ] + }, + { + "ordinal": 2, + "stage": "created", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": null, + "teamRole": null, + "tools": [ + "lifecycle_probe" + ] + }, + { + "ordinal": 3, + "stage": "start-returned", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": null, + "teamRole": null + }, + { + "ordinal": 4, + "stage": "descriptor-appended", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "spawn", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + }, + { + "ordinal": 5, + "stage": "model-request", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "spawn", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null, + "tools": [ + "lifecycle_probe" + ], + "promptCallsChildLead": false + }, + { + "ordinal": 6, + "stage": "model-request", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "spawn", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null, + "tools": [ + "lifecycle_probe" + ], + "promptCallsChildLead": false + }, + { + "ordinal": 7, + "stage": "result-settled", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "spawn", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + }, + { + "ordinal": 8, + "stage": "disposed", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "spawn", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + } + ], + "preflight": { + "hasTeamService": false, + "hasScopedTeamSpawnTool": false, + "hasGlobalTeamSpawnTool": false + }, + "childModelToolNames": [ + "lifecycle_probe" + ], + "result": { + "output": [ + { + "type": "text", + "text": "Synthetic child completed." + } + ], + "stopReason": "completed" + }, + "residentBeforeDispose": true, + "catalogBeforeDispose": { + "entries": [ + { + "kind": "child", + "id": "bef33933-3870-433c-b172-7ae62d743f61", + "mode": "one-shot", + "label": "Mnemon idle checkpoint review", + "activity": "inactive", + "hasChildren": false + } + ], + "parentAvailable": true + }, + "childEventTypes": [ + "agent/inbox/spliced", + "turn/start", + "agent/inbox/spliced", + "subagent/descriptor", + "step/start", + "system/message", + "user/message", + "user/message", + "request/header", + "request/context", + "assistant/message", + "tool/call", + "tool/result", + "step/end", + "step/start", + "assistant/message", + "step/end", + "turn/end" + ], + "residentAfterDispose": false, + "catalogAfterDispose": { + "entries": [ + { + "kind": "child", + "id": "bef33933-3870-433c-b172-7ae62d743f61", + "mode": "one-shot", + "label": "Mnemon idle checkpoint review", + "activity": "inactive", + "hasChildren": false + } + ], + "parentAvailable": true + } + }, + { + "provider": "fork", + "teams": "service", + "guarded": false, + "parentModelCalls": 1, + "childModelCalls": 2, + "probeExecutions": [ + "4e05b0c5-cade-40c2-8785-899cbf10c559" + ], + "events": [ + { + "ordinal": 1, + "stage": "created", + "subject": "parent", + "descriptor": null, + "teamRole": "lead", + "tools": [ + "lifecycle_probe" + ] + }, + { + "ordinal": 2, + "stage": "created", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": null, + "teamRole": "lead", + "tools": [ + "lifecycle_probe" + ] + }, + { + "ordinal": 3, + "stage": "start-returned", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": null, + "teamRole": "lead" + }, + { + "ordinal": 4, + "stage": "descriptor-appended", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "fork", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + }, + { + "ordinal": 5, + "stage": "model-request", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "fork", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null, + "tools": [ + "lifecycle_probe" + ], + "promptCallsChildLead": false + }, + { + "ordinal": 6, + "stage": "model-request", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "fork", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null, + "tools": [ + "lifecycle_probe" + ], + "promptCallsChildLead": false + }, + { + "ordinal": 7, + "stage": "result-settled", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "fork", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + }, + { + "ordinal": 8, + "stage": "disposed", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "fork", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + } + ], + "preflight": { + "hasTeamService": true, + "hasScopedTeamSpawnTool": false, + "hasGlobalTeamSpawnTool": false + }, + "childModelToolNames": [ + "lifecycle_probe" + ], + "result": { + "output": [ + { + "type": "text", + "text": "Synthetic child completed." + } + ], + "stopReason": "completed" + }, + "residentBeforeDispose": true, + "catalogBeforeDispose": { + "entries": [ + { + "kind": "child", + "id": "4e05b0c5-cade-40c2-8785-899cbf10c559", + "mode": "one-shot", + "label": "Mnemon idle checkpoint review", + "activity": "inactive", + "hasChildren": false + } + ], + "parentAvailable": true + }, + "childEventTypes": [ + "agent/inbox/spliced", + "turn/start", + "agent/inbox/spliced", + "step/start", + "system/message", + "user/message", + "request/header", + "request/context", + "assistant/message", + "step/end", + "turn/end", + "session/end-seed", + "agent/inbox/spliced", + "turn/start", + "agent/inbox/spliced", + "subagent/descriptor", + "step/start", + "system/message", + "user/message", + "user/message", + "request/header", + "assistant/message", + "tool/call", + "tool/result", + "step/end", + "step/start", + "assistant/message", + "step/end", + "turn/end" + ], + "residentAfterDispose": false, + "catalogAfterDispose": { + "entries": [ + { + "kind": "child", + "id": "4e05b0c5-cade-40c2-8785-899cbf10c559", + "mode": "one-shot", + "label": "Mnemon idle checkpoint review", + "activity": "inactive", + "hasChildren": false + } + ], + "parentAvailable": true + } + }, + { + "provider": "spawn", + "teams": "service", + "guarded": false, + "parentModelCalls": 1, + "childModelCalls": 2, + "probeExecutions": [ + "38da4168-8b07-4fa5-9451-4ee7a505cc69" + ], + "events": [ + { + "ordinal": 1, + "stage": "created", + "subject": "parent", + "descriptor": null, + "teamRole": "lead", + "tools": [ + "lifecycle_probe" + ] + }, + { + "ordinal": 2, + "stage": "created", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": null, + "teamRole": "lead", + "tools": [ + "lifecycle_probe" + ] + }, + { + "ordinal": 3, + "stage": "start-returned", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": null, + "teamRole": "lead" + }, + { + "ordinal": 4, + "stage": "descriptor-appended", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "spawn", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + }, + { + "ordinal": 5, + "stage": "model-request", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "spawn", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null, + "tools": [ + "lifecycle_probe" + ], + "promptCallsChildLead": false + }, + { + "ordinal": 6, + "stage": "model-request", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "spawn", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null, + "tools": [ + "lifecycle_probe" + ], + "promptCallsChildLead": false + }, + { + "ordinal": 7, + "stage": "result-settled", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "spawn", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + }, + { + "ordinal": 8, + "stage": "disposed", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "spawn", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + } + ], + "preflight": { + "hasTeamService": true, + "hasScopedTeamSpawnTool": false, + "hasGlobalTeamSpawnTool": false + }, + "childModelToolNames": [ + "lifecycle_probe" + ], + "result": { + "output": [ + { + "type": "text", + "text": "Synthetic child completed." + } + ], + "stopReason": "completed" + }, + "residentBeforeDispose": true, + "catalogBeforeDispose": { + "entries": [ + { + "kind": "child", + "id": "38da4168-8b07-4fa5-9451-4ee7a505cc69", + "mode": "one-shot", + "label": "Mnemon idle checkpoint review", + "activity": "inactive", + "hasChildren": false + } + ], + "parentAvailable": true + }, + "childEventTypes": [ + "agent/inbox/spliced", + "turn/start", + "agent/inbox/spliced", + "subagent/descriptor", + "step/start", + "system/message", + "user/message", + "user/message", + "request/header", + "request/context", + "assistant/message", + "tool/call", + "tool/result", + "step/end", + "step/start", + "assistant/message", + "step/end", + "turn/end" + ], + "residentAfterDispose": false, + "catalogAfterDispose": { + "entries": [ + { + "kind": "child", + "id": "38da4168-8b07-4fa5-9451-4ee7a505cc69", + "mode": "one-shot", + "label": "Mnemon idle checkpoint review", + "activity": "inactive", + "hasChildren": false + } + ], + "parentAvailable": true + } + }, + { + "provider": "fork", + "teams": "tools", + "guarded": false, + "parentModelCalls": 1, + "childModelCalls": 1, + "probeExecutions": [ + "1b20a07c-a3ab-41bc-8e94-7ae0f6de69b6" + ], + "events": [ + { + "ordinal": 1, + "stage": "created", + "subject": "parent", + "descriptor": null, + "teamRole": "lead", + "tools": [ + "lifecycle_probe", + "spawn_teammate", + "send_message", + "list_agents", + "wait_agent", + "interrupt_agent", + "team_task_create", + "team_task_list", + "team_task_get", + "team_task_update" + ] + }, + { + "ordinal": 2, + "stage": "created", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": null, + "teamRole": "lead", + "tools": [ + "lifecycle_probe", + "spawn_teammate", + "send_message", + "list_agents", + "wait_agent", + "interrupt_agent", + "team_task_create", + "team_task_list", + "team_task_get", + "team_task_update" + ] + }, + { + "ordinal": 3, + "stage": "start-returned", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": null, + "teamRole": "lead" + }, + { + "ordinal": 4, + "stage": "descriptor-appended", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "fork", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + }, + { + "ordinal": 5, + "stage": "model-request", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "fork", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null, + "tools": [ + "interrupt_agent", + "lifecycle_probe", + "list_agents", + "send_message", + "spawn_teammate", + "team_task_create", + "team_task_get", + "team_task_list", + "team_task_update", + "wait_agent" + ], + "promptCallsChildLead": true + }, + { + "ordinal": 6, + "stage": "error", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "fork", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null, + "error": { + "name": "TeamError", + "code": "TEAM_NOT_MEMBER", + "message": "agent \"1b20a07c-a3ab-41bc-8e94-7ae0f6de69b6\" is not a member of an active Agent Team" + } + }, + { + "ordinal": 7, + "stage": "result-settled", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "fork", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + }, + { + "ordinal": 8, + "stage": "disposed", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "fork", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + } + ], + "preflight": { + "hasTeamService": true, + "hasScopedTeamSpawnTool": true, + "hasGlobalTeamSpawnTool": false + }, + "childModelToolNames": [ + "interrupt_agent", + "lifecycle_probe", + "list_agents", + "send_message", + "spawn_teammate", + "team_task_create", + "team_task_get", + "team_task_list", + "team_task_update", + "wait_agent" + ], + "result": { + "output": [ + { + "type": "tool-call", + "id": "lifecycle-probe-2", + "name": "lifecycle_probe", + "arguments": "{}" + } + ], + "stopReason": "error" + }, + "residentBeforeDispose": true, + "catalogBeforeDispose": { + "entries": [ + { + "kind": "child", + "id": "1b20a07c-a3ab-41bc-8e94-7ae0f6de69b6", + "mode": "one-shot", + "label": "Mnemon idle checkpoint review", + "activity": "inactive", + "hasChildren": false + } + ], + "parentAvailable": true + }, + "childEventTypes": [ + "agent/inbox/spliced", + "turn/start", + "agent/inbox/spliced", + "step/start", + "system/message", + "user/message", + "request/header", + "request/context", + "assistant/message", + "step/end", + "turn/end", + "session/end-seed", + "agent/inbox/spliced", + "turn/start", + "agent/inbox/spliced", + "subagent/descriptor", + "step/start", + "system/message", + "user/message", + "user/message", + "request/header", + "assistant/message", + "tool/call", + "tool/result", + "step/end", + "turn/end" + ], + "residentAfterDispose": false, + "catalogAfterDispose": { + "entries": [ + { + "kind": "child", + "id": "1b20a07c-a3ab-41bc-8e94-7ae0f6de69b6", + "mode": "one-shot", + "label": "Mnemon idle checkpoint review", + "activity": "inactive", + "hasChildren": false + } + ], + "parentAvailable": true + } + }, + { + "provider": "spawn", + "teams": "tools", + "guarded": false, + "parentModelCalls": 1, + "childModelCalls": 1, + "probeExecutions": [ + "db940301-602e-4956-b89b-2da38103cbf3" + ], + "events": [ + { + "ordinal": 1, + "stage": "created", + "subject": "parent", + "descriptor": null, + "teamRole": "lead", + "tools": [ + "lifecycle_probe", + "spawn_teammate", + "send_message", + "list_agents", + "wait_agent", + "interrupt_agent", + "team_task_create", + "team_task_list", + "team_task_get", + "team_task_update" + ] + }, + { + "ordinal": 2, + "stage": "created", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": null, + "teamRole": "lead", + "tools": [ + "lifecycle_probe", + "spawn_teammate", + "send_message", + "list_agents", + "wait_agent", + "interrupt_agent", + "team_task_create", + "team_task_list", + "team_task_get", + "team_task_update" + ] + }, + { + "ordinal": 3, + "stage": "start-returned", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": null, + "teamRole": "lead" + }, + { + "ordinal": 4, + "stage": "descriptor-appended", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "spawn", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + }, + { + "ordinal": 5, + "stage": "model-request", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "spawn", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null, + "tools": [ + "interrupt_agent", + "lifecycle_probe", + "list_agents", + "send_message", + "spawn_teammate", + "team_task_create", + "team_task_get", + "team_task_list", + "team_task_update", + "wait_agent" + ], + "promptCallsChildLead": true + }, + { + "ordinal": 6, + "stage": "error", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "spawn", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null, + "error": { + "name": "TeamError", + "code": "TEAM_NOT_MEMBER", + "message": "agent \"db940301-602e-4956-b89b-2da38103cbf3\" is not a member of an active Agent Team" + } + }, + { + "ordinal": 7, + "stage": "result-settled", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "spawn", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + }, + { + "ordinal": 8, + "stage": "disposed", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "spawn", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + } + ], + "preflight": { + "hasTeamService": true, + "hasScopedTeamSpawnTool": true, + "hasGlobalTeamSpawnTool": false + }, + "childModelToolNames": [ + "interrupt_agent", + "lifecycle_probe", + "list_agents", + "send_message", + "spawn_teammate", + "team_task_create", + "team_task_get", + "team_task_list", + "team_task_update", + "wait_agent" + ], + "result": { + "output": [ + { + "type": "tool-call", + "id": "lifecycle-probe-2", + "name": "lifecycle_probe", + "arguments": "{}" + } + ], + "stopReason": "error" + }, + "residentBeforeDispose": true, + "catalogBeforeDispose": { + "entries": [ + { + "kind": "child", + "id": "db940301-602e-4956-b89b-2da38103cbf3", + "mode": "one-shot", + "label": "Mnemon idle checkpoint review", + "activity": "inactive", + "hasChildren": false + } + ], + "parentAvailable": true + }, + "childEventTypes": [ + "agent/inbox/spliced", + "turn/start", + "agent/inbox/spliced", + "subagent/descriptor", + "step/start", + "system/message", + "user/message", + "user/message", + "request/header", + "request/context", + "assistant/message", + "tool/call", + "tool/result", + "step/end", + "turn/end" + ], + "residentAfterDispose": false, + "catalogAfterDispose": { + "entries": [ + { + "kind": "child", + "id": "db940301-602e-4956-b89b-2da38103cbf3", + "mode": "one-shot", + "label": "Mnemon idle checkpoint review", + "activity": "inactive", + "hasChildren": false + } + ], + "parentAvailable": true + } + } + ] +} diff --git a/dsh/docs/pr-assets/idle-review-agent-team/published-team-guarded.json b/dsh/docs/pr-assets/idle-review-agent-team/published-team-guarded.json new file mode 100644 index 000000000..686a2894c --- /dev/null +++ b/dsh/docs/pr-assets/idle-review-agent-team/published-team-guarded.json @@ -0,0 +1,865 @@ +{ + "versions": { + "dsh": "0.1.5-rc.2", + "dsh-agent": "0.1.5-rc.2", + "dsh-agent-loop": "0.1.5-rc.2", + "dsh-subagent": "0.1.5-rc.2", + "dsh-subagent-in-process-driver": "0.1.5-rc.2", + "dsh-subagent-fork-in-process": "0.1.5-rc.2", + "dsh-subagent-spawn-in-process": "0.1.5-rc.2", + "dsh-experimental-agent-team": "0.1.5-alpha.2", + "dsh-experimental-tool-agent-team": "0.1.5-alpha.2" + }, + "results": [ + { + "provider": "fork", + "teams": "off", + "guarded": true, + "parentModelCalls": 1, + "childModelCalls": 2, + "probeExecutions": [ + "35ac2446-8cac-4e0d-a398-eefeccddfbab" + ], + "events": [ + { + "ordinal": 1, + "stage": "created", + "subject": "parent", + "descriptor": null, + "teamRole": null, + "tools": [ + "lifecycle_probe" + ] + }, + { + "ordinal": 2, + "stage": "created", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": null, + "teamRole": null, + "tools": [ + "lifecycle_probe" + ] + }, + { + "ordinal": 3, + "stage": "start-returned", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": null, + "teamRole": null + }, + { + "ordinal": 4, + "stage": "descriptor-appended", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "fork", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + }, + { + "ordinal": 5, + "stage": "model-request", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "fork", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null, + "tools": [ + "lifecycle_probe" + ], + "promptCallsChildLead": false + }, + { + "ordinal": 6, + "stage": "model-request", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "fork", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null, + "tools": [ + "lifecycle_probe" + ], + "promptCallsChildLead": false + }, + { + "ordinal": 7, + "stage": "result-settled", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "fork", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + }, + { + "ordinal": 8, + "stage": "disposed", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "fork", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + } + ], + "preflight": { + "hasTeamService": false, + "hasScopedTeamSpawnTool": false, + "hasGlobalTeamSpawnTool": false + }, + "childModelToolNames": [ + "lifecycle_probe" + ], + "result": { + "output": [ + { + "type": "text", + "text": "Synthetic child completed." + } + ], + "stopReason": "completed" + }, + "residentBeforeDispose": true, + "catalogBeforeDispose": { + "entries": [ + { + "kind": "child", + "id": "35ac2446-8cac-4e0d-a398-eefeccddfbab", + "mode": "one-shot", + "label": "Mnemon idle checkpoint review", + "activity": "inactive", + "hasChildren": false + } + ], + "parentAvailable": true + }, + "childEventTypes": [ + "agent/inbox/spliced", + "turn/start", + "agent/inbox/spliced", + "step/start", + "system/message", + "user/message", + "request/header", + "request/context", + "assistant/message", + "step/end", + "turn/end", + "session/end-seed", + "agent/inbox/spliced", + "turn/start", + "agent/inbox/spliced", + "subagent/descriptor", + "step/start", + "system/message", + "user/message", + "user/message", + "request/header", + "assistant/message", + "tool/call", + "tool/result", + "step/end", + "step/start", + "assistant/message", + "step/end", + "turn/end" + ], + "residentAfterDispose": false, + "catalogAfterDispose": { + "entries": [ + { + "kind": "child", + "id": "35ac2446-8cac-4e0d-a398-eefeccddfbab", + "mode": "one-shot", + "label": "Mnemon idle checkpoint review", + "activity": "inactive", + "hasChildren": false + } + ], + "parentAvailable": true + } + }, + { + "provider": "spawn", + "teams": "off", + "guarded": true, + "parentModelCalls": 1, + "childModelCalls": 2, + "probeExecutions": [ + "95925b41-583d-485d-aa4a-001e19b793e4" + ], + "events": [ + { + "ordinal": 1, + "stage": "created", + "subject": "parent", + "descriptor": null, + "teamRole": null, + "tools": [ + "lifecycle_probe" + ] + }, + { + "ordinal": 2, + "stage": "created", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": null, + "teamRole": null, + "tools": [ + "lifecycle_probe" + ] + }, + { + "ordinal": 3, + "stage": "start-returned", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": null, + "teamRole": null + }, + { + "ordinal": 4, + "stage": "descriptor-appended", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "spawn", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + }, + { + "ordinal": 5, + "stage": "model-request", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "spawn", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null, + "tools": [ + "lifecycle_probe" + ], + "promptCallsChildLead": false + }, + { + "ordinal": 6, + "stage": "model-request", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "spawn", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null, + "tools": [ + "lifecycle_probe" + ], + "promptCallsChildLead": false + }, + { + "ordinal": 7, + "stage": "result-settled", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "spawn", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + }, + { + "ordinal": 8, + "stage": "disposed", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "spawn", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + } + ], + "preflight": { + "hasTeamService": false, + "hasScopedTeamSpawnTool": false, + "hasGlobalTeamSpawnTool": false + }, + "childModelToolNames": [ + "lifecycle_probe" + ], + "result": { + "output": [ + { + "type": "text", + "text": "Synthetic child completed." + } + ], + "stopReason": "completed" + }, + "residentBeforeDispose": true, + "catalogBeforeDispose": { + "entries": [ + { + "kind": "child", + "id": "95925b41-583d-485d-aa4a-001e19b793e4", + "mode": "one-shot", + "label": "Mnemon idle checkpoint review", + "activity": "inactive", + "hasChildren": false + } + ], + "parentAvailable": true + }, + "childEventTypes": [ + "agent/inbox/spliced", + "turn/start", + "agent/inbox/spliced", + "subagent/descriptor", + "step/start", + "system/message", + "user/message", + "user/message", + "request/header", + "request/context", + "assistant/message", + "tool/call", + "tool/result", + "step/end", + "step/start", + "assistant/message", + "step/end", + "turn/end" + ], + "residentAfterDispose": false, + "catalogAfterDispose": { + "entries": [ + { + "kind": "child", + "id": "95925b41-583d-485d-aa4a-001e19b793e4", + "mode": "one-shot", + "label": "Mnemon idle checkpoint review", + "activity": "inactive", + "hasChildren": false + } + ], + "parentAvailable": true + } + }, + { + "provider": "fork", + "teams": "service", + "guarded": true, + "parentModelCalls": 1, + "childModelCalls": 2, + "probeExecutions": [ + "05b98284-1710-47bb-ae50-1339016d3910" + ], + "events": [ + { + "ordinal": 1, + "stage": "created", + "subject": "parent", + "descriptor": null, + "teamRole": "lead", + "tools": [ + "lifecycle_probe" + ] + }, + { + "ordinal": 2, + "stage": "created", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": null, + "teamRole": "lead", + "tools": [ + "lifecycle_probe" + ] + }, + { + "ordinal": 3, + "stage": "start-returned", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": null, + "teamRole": "lead" + }, + { + "ordinal": 4, + "stage": "descriptor-appended", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "fork", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + }, + { + "ordinal": 5, + "stage": "model-request", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "fork", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null, + "tools": [ + "lifecycle_probe" + ], + "promptCallsChildLead": false + }, + { + "ordinal": 6, + "stage": "model-request", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "fork", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null, + "tools": [ + "lifecycle_probe" + ], + "promptCallsChildLead": false + }, + { + "ordinal": 7, + "stage": "result-settled", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "fork", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + }, + { + "ordinal": 8, + "stage": "disposed", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "fork", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + } + ], + "preflight": { + "hasTeamService": true, + "hasScopedTeamSpawnTool": false, + "hasGlobalTeamSpawnTool": false + }, + "childModelToolNames": [ + "lifecycle_probe" + ], + "result": { + "output": [ + { + "type": "text", + "text": "Synthetic child completed." + } + ], + "stopReason": "completed" + }, + "residentBeforeDispose": true, + "catalogBeforeDispose": { + "entries": [ + { + "kind": "child", + "id": "05b98284-1710-47bb-ae50-1339016d3910", + "mode": "one-shot", + "label": "Mnemon idle checkpoint review", + "activity": "inactive", + "hasChildren": false + } + ], + "parentAvailable": true + }, + "childEventTypes": [ + "agent/inbox/spliced", + "turn/start", + "agent/inbox/spliced", + "step/start", + "system/message", + "user/message", + "request/header", + "request/context", + "assistant/message", + "step/end", + "turn/end", + "session/end-seed", + "agent/inbox/spliced", + "turn/start", + "agent/inbox/spliced", + "subagent/descriptor", + "step/start", + "system/message", + "user/message", + "user/message", + "request/header", + "assistant/message", + "tool/call", + "tool/result", + "step/end", + "step/start", + "assistant/message", + "step/end", + "turn/end" + ], + "residentAfterDispose": false, + "catalogAfterDispose": { + "entries": [ + { + "kind": "child", + "id": "05b98284-1710-47bb-ae50-1339016d3910", + "mode": "one-shot", + "label": "Mnemon idle checkpoint review", + "activity": "inactive", + "hasChildren": false + } + ], + "parentAvailable": true + } + }, + { + "provider": "spawn", + "teams": "service", + "guarded": true, + "parentModelCalls": 1, + "childModelCalls": 2, + "probeExecutions": [ + "2a1131f6-c40a-4a4f-ad57-7b8d2df426c9" + ], + "events": [ + { + "ordinal": 1, + "stage": "created", + "subject": "parent", + "descriptor": null, + "teamRole": "lead", + "tools": [ + "lifecycle_probe" + ] + }, + { + "ordinal": 2, + "stage": "created", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": null, + "teamRole": "lead", + "tools": [ + "lifecycle_probe" + ] + }, + { + "ordinal": 3, + "stage": "start-returned", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": null, + "teamRole": "lead" + }, + { + "ordinal": 4, + "stage": "descriptor-appended", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "spawn", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + }, + { + "ordinal": 5, + "stage": "model-request", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "spawn", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null, + "tools": [ + "lifecycle_probe" + ], + "promptCallsChildLead": false + }, + { + "ordinal": 6, + "stage": "model-request", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "spawn", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null, + "tools": [ + "lifecycle_probe" + ], + "promptCallsChildLead": false + }, + { + "ordinal": 7, + "stage": "result-settled", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "spawn", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + }, + { + "ordinal": 8, + "stage": "disposed", + "subject": "child", + "origin": "subagent", + "parentSession": "parent", + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "spawn", + "label": "Mnemon idle checkpoint review" + }, + "teamRole": null + } + ], + "preflight": { + "hasTeamService": true, + "hasScopedTeamSpawnTool": false, + "hasGlobalTeamSpawnTool": false + }, + "childModelToolNames": [ + "lifecycle_probe" + ], + "result": { + "output": [ + { + "type": "text", + "text": "Synthetic child completed." + } + ], + "stopReason": "completed" + }, + "residentBeforeDispose": true, + "catalogBeforeDispose": { + "entries": [ + { + "kind": "child", + "id": "2a1131f6-c40a-4a4f-ad57-7b8d2df426c9", + "mode": "one-shot", + "label": "Mnemon idle checkpoint review", + "activity": "inactive", + "hasChildren": false + } + ], + "parentAvailable": true + }, + "childEventTypes": [ + "agent/inbox/spliced", + "turn/start", + "agent/inbox/spliced", + "subagent/descriptor", + "step/start", + "system/message", + "user/message", + "user/message", + "request/header", + "request/context", + "assistant/message", + "tool/call", + "tool/result", + "step/end", + "step/start", + "assistant/message", + "step/end", + "turn/end" + ], + "residentAfterDispose": false, + "catalogAfterDispose": { + "entries": [ + { + "kind": "child", + "id": "2a1131f6-c40a-4a4f-ad57-7b8d2df426c9", + "mode": "one-shot", + "label": "Mnemon idle checkpoint review", + "activity": "inactive", + "hasChildren": false + } + ], + "parentAvailable": true + } + }, + { + "provider": "fork", + "teams": "tools", + "guarded": true, + "parentModelCalls": 1, + "childModelCalls": 0, + "probeExecutions": [], + "events": [ + { + "ordinal": 1, + "stage": "created", + "subject": "parent", + "descriptor": null, + "teamRole": "lead", + "tools": [ + "lifecycle_probe", + "spawn_teammate", + "send_message", + "list_agents", + "wait_agent", + "interrupt_agent", + "team_task_create", + "team_task_list", + "team_task_get", + "team_task_update" + ] + } + ], + "preflight": { + "hasTeamService": true, + "hasScopedTeamSpawnTool": true, + "hasGlobalTeamSpawnTool": false, + "blockReason": "agent-team" + }, + "guardOutcome": "skipped before child creation", + "catalogAfterSkip": { + "entries": [], + "parentAvailable": true + }, + "parentTeamToolSucceeded": true, + "parentTeamTaskCount": 1 + }, + { + "provider": "spawn", + "teams": "tools", + "guarded": true, + "parentModelCalls": 1, + "childModelCalls": 0, + "probeExecutions": [], + "events": [ + { + "ordinal": 1, + "stage": "created", + "subject": "parent", + "descriptor": null, + "teamRole": "lead", + "tools": [ + "lifecycle_probe", + "spawn_teammate", + "send_message", + "list_agents", + "wait_agent", + "interrupt_agent", + "team_task_create", + "team_task_list", + "team_task_get", + "team_task_update" + ] + } + ], + "preflight": { + "hasTeamService": true, + "hasScopedTeamSpawnTool": true, + "hasGlobalTeamSpawnTool": false, + "blockReason": "agent-team" + }, + "guardOutcome": "skipped before child creation", + "catalogAfterSkip": { + "entries": [], + "parentAvailable": true + }, + "parentTeamToolSucceeded": true, + "parentTeamTaskCount": 1 + } + ] +} diff --git a/dsh/docs/pr-assets/idle-review-agent-team/webui-evidence.json b/dsh/docs/pr-assets/idle-review-agent-team/webui-evidence.json new file mode 100644 index 000000000..0a947f718 --- /dev/null +++ b/dsh/docs/pr-assets/idle-review-agent-team/webui-evidence.json @@ -0,0 +1,284 @@ +{ + "inspectedAt": "2026-09-16T04:35:27.869Z", + "model": "Deterministic loopback fixture; no external model API and no model-quality claim.", + "hostVersion": "0.1.5-rc.2", + "nativeCli": { + "version": "mnemon version 0.2.8", + "sha256": "f8f21151ce9777983b8d2dfb9cbea1a00b223de6063659af232d04aa57cedad0" + }, + "workload": { + "messages": [ + "review-team-255", + "Please continue inspecting the complete synthetic module overview already returned in five chunks. Check the module index carefully and explain the result using those existing chunks. Keep the discussion focused on this disposable test checkpoint." + ], + "overviewChunks": 5, + "overviewEntries": 335, + "eligibility": "Normal threshold 5 retained. First short turn scores 2; substantive second turn brings the accumulated activity score to 6." + }, + "baseline": { + "packageVersions": { + "dsh-experimental-agent-team": "0.1.5-alpha.2", + "dsh-experimental-tool-agent-team": "0.1.5-alpha.2" + }, + "mnemonBaseCommit": "1363ebffaf19c9ab4badf0137f6fe87acacaf989", + "mnemonRootBuildSha256": "8f177a306962439d6f16fe86d2ca1a7c9fdecdbc0825509e6e3d95f59df0dc4a", + "enabledStrategies": [ + "scoped", + "light-context", + "auto-capture" + ], + "idleReviewMs": 5000, + "secondsSinceLastLogWriteAtInspection": 582, + "counts": { + "completedParentTurns": 2, + "parentOverviewToolCalls": 5, + "childOverviewToolCalls": 0, + "childCreated": 1, + "childDescriptors": 1, + "reviewModelStarts": 1, + "teamNotMemberErrors": 1, + "childDisposed": 1, + "modelHttpRequests": 9 + }, + "observations": [ + { + "source": "team-lifecycle", + "event": "mounted", + "providers": [], + "teamService": true + }, + { + "source": "team-lifecycle", + "event": "created", + "agentId": "parent", + "origin": "root", + "hasDescriptor": false, + "teamRole": "lead", + "hasTeamSpawnTool": false + }, + { + "source": "overview-tool", + "actor": "parent", + "chunk": 0 + }, + { + "source": "overview-tool", + "actor": "parent", + "chunk": 1 + }, + { + "source": "overview-tool", + "actor": "parent", + "chunk": 2 + }, + { + "source": "overview-tool", + "actor": "parent", + "chunk": 3 + }, + { + "source": "overview-tool", + "actor": "parent", + "chunk": 4 + }, + { + "source": "review-model", + "event": "parent-complete", + "overviewReads": 5 + }, + { + "source": "review-model", + "event": "parent-complete", + "overviewReads": 5 + }, + { + "source": "team-lifecycle", + "event": "created", + "agentId": "child-1", + "parentId": "parent", + "origin": "subagent", + "hasDescriptor": false, + "teamRole": "lead", + "hasTeamSpawnTool": false + }, + { + "source": "team-lifecycle", + "event": "descriptor-appended", + "agentId": "child-1", + "parentId": "parent", + "origin": "subagent", + "hasDescriptor": true, + "teamRole": null, + "hasTeamSpawnTool": true, + "descriptor": { + "version": 3, + "mode": "one-shot", + "provider": "fork", + "label": "Mnemon idle checkpoint review" + } + }, + { + "source": "review-model", + "event": "review-start", + "inheritedChunks": 5, + "entries": 335, + "characters": 19112, + "foreignToolAdvertised": true + }, + { + "source": "team-lifecycle", + "event": "error", + "agentId": "child-1", + "parentId": "parent", + "origin": "subagent", + "hasDescriptor": true, + "teamRole": null, + "hasTeamSpawnTool": true, + "error": { + "name": "TeamError", + "code": "TEAM_NOT_MEMBER", + "message": "agent \"child-1\" is not a member of an active Agent Team" + } + }, + { + "source": "team-lifecycle", + "event": "subagent-end", + "id": "child-1", + "provider": "fork" + }, + { + "source": "team-lifecycle", + "event": "disposed", + "agentId": "child-1", + "parentId": "parent", + "origin": "subagent", + "hasDescriptor": true, + "teamRole": null, + "hasTeamSpawnTool": false + }, + { + "source": "team-lifecycle", + "event": "catalog-after-dispose", + "childId": "child-1", + "resident": false, + "entries": [ + { + "id": "child-1", + "mode": "one-shot", + "activity": "inactive", + "label": "Mnemon idle checkpoint review" + } + ] + } + ] + }, + "fixed": { + "packageVersions": { + "dsh-experimental-agent-team": "0.1.5-alpha.2", + "dsh-experimental-tool-agent-team": "0.1.5-alpha.2" + }, + "mnemonBaseCommit": "1363ebffaf19c9ab4badf0137f6fe87acacaf989", + "mnemonRootBuildSha256": "4953e8a3d11673bcd8b933a18860d3e1c1362974427680b807ed7c5463fee46e", + "enabledStrategies": [ + "scoped", + "light-context", + "auto-capture" + ], + "idleReviewMs": 5000, + "secondsSinceLastLogWriteAtInspection": 190, + "counts": { + "completedParentTurns": 2, + "parentOverviewToolCalls": 5, + "childOverviewToolCalls": 0, + "childCreated": 0, + "childDescriptors": 0, + "reviewModelStarts": 0, + "teamNotMemberErrors": 0, + "childDisposed": 0, + "modelHttpRequests": 8 + }, + "observations": [ + { + "source": "team-lifecycle", + "event": "mounted", + "providers": [], + "teamService": true + }, + { + "source": "team-lifecycle", + "event": "created", + "agentId": "parent", + "origin": "root", + "hasDescriptor": false, + "teamRole": "lead", + "hasTeamSpawnTool": false + }, + { + "source": "team-lifecycle", + "event": "session-start", + "agentId": "parent", + "origin": "root", + "hasDescriptor": false, + "teamRole": "lead", + "hasTeamSpawnTool": true + }, + { + "source": "overview-tool", + "actor": "parent", + "chunk": 0 + }, + { + "source": "overview-tool", + "actor": "parent", + "chunk": 1 + }, + { + "source": "overview-tool", + "actor": "parent", + "chunk": 2 + }, + { + "source": "overview-tool", + "actor": "parent", + "chunk": 3 + }, + { + "source": "overview-tool", + "actor": "parent", + "chunk": 4 + }, + { + "source": "review-model", + "event": "parent-complete", + "overviewReads": 5 + }, + { + "source": "team-lifecycle", + "event": "turn-stopping", + "agentId": "parent", + "origin": "root", + "hasDescriptor": false, + "teamRole": "lead", + "hasTeamSpawnTool": true, + "turn": 1 + }, + { + "source": "review-model", + "event": "parent-complete", + "overviewReads": 5 + }, + { + "source": "team-lifecycle", + "event": "turn-stopping", + "agentId": "parent", + "origin": "root", + "hasDescriptor": false, + "teamRole": "lead", + "hasTeamSpawnTool": true, + "turn": 2 + } + ] + }, + "limitation": "Agent Teams alpha.2 still fails for both official rc.2 providers without the Mnemon compatibility skip. The fix pauses automatic memory review for the affected composition before creating a child. It does not repair upstream Team policy or delete historical catalog rows.", + "recordingNote": "The baseline telemetry records created, descriptor, error, disposal and catalog events. Session-start and turn-stopping observations were added to the same read-only telemetry plugin for the fixed run. Its original baseline subagent-end observation did not record a stop reason; the separate published-package fixture asserts that result directly." +} diff --git a/dsh/docs/pr-assets/issue-189-core-workspaces/README.md b/dsh/docs/pr-assets/issue-189-core-workspaces/README.md new file mode 100644 index 000000000..2ee3df791 --- /dev/null +++ b/dsh/docs/pr-assets/issue-189-core-workspaces/README.md @@ -0,0 +1,65 @@ +# Built-in centralized workspaces — issue #189 + +[中文](./README.zh-CN.md) · [Evidence index](../README.md) + +Tested on 2026-09-09 with published DSH 0.1.2-rc.1, Node 22.19.0, pnpm 10.13.1 and Mnemon Native 0.2.7 on macOS. This is a fresh implementation based on `main` at `820b0b092fe68eb7ce6fbf1eaa12e9cf39087e31`, replacing the separate storage-plugin approach in PR #215. The original checkout and old branch were retained. + +The functional run used `2314d6a44ee21b74f1e3bb22a0533d90285c5eea`; final layout verification and the complete automated checks used `51446b51ed636eace6f6ca4d206969561ada194d`. Screenshots 01–03 and 05–07 record the functional revision; 04, 08 and 09 record the final layout. None were copied from the old plugin PR. + +## Implementation boundary + +The root package's Host resolves all four layouts and canonical workspace identities; Core carries the operation scope and retains View authority. Existing Sources receive their directory through the current configuration contract. There is no new package, dependency, storage contribution API or SDK export. The existing Memory Spaces package has a presentation-only fix for long paths. + +## Automated verification + +```bash +MNEMON_NATIVE_TEST_CLI=/opt/homebrew/bin/mnemon npx --yes --package=node@22.19.0 --package=pnpm@10.13.1 --call 'pnpm run verify' +npx --yes --package=node@22.19.0 --package=pnpm@10.13.1 --call 'node scripts/verify-plugin-artifacts.mjs --skip-build' +CHANGESET_BASE_REVISION=820b0b092fe68eb7ce6fbf1eaa12e9cf39087e31 npx --yes --package=node@22.19.0 --package=pnpm@10.13.1 --call 'pnpm run release:intent' +``` + +| Check | Result | +|---|---| +| Root tests | 852 passed; 5 opt-in Flash checks skipped | +| Independent plugin tests in `verify` | 324 passed; 1 Windows-only smoke check skipped | +| Deterministic build, TypeScript, package exports and package lint | Passed | +| Real Headless profile | 39 tools; 8 representative Mnemon tools; restart and legacy-disable checks passed | +| Real Native CLI | Temporary View write, recall and deletion passed | +| Packed artifacts | 16 independent plugin repositories and 17 tarballs passed, including root-only Starter installation into real DSH | + +Coverage includes all four storage scopes, legacy `dataDir`, registry rejection, session-owned Headless/Builtin routing, real Source writes to all four areas, global versus storage USER.md, symlink aliases, missing path descendants, Unicode, same-named directories, rename behavior and non-destructive switching. The latest main's 23 capacity-workflow tests also passed. + +The initial package-size check exposed a tight pre-existing ceiling. A separate build of the base revision measured 1,249,360 unpacked bytes; the final package is 1,256,247 bytes (+6,887), still 46 files. Growth is confined to Host/UI, public scope types and README copy. The bounded ceiling is now 1,260,000 bytes; no Source or Provider implementation was bundled into the root. + +## Real WebUI workflow + +Run `pnpm e2e:serve` after building. The existing harness uses a disposable Profile, global data root, two temporary workspaces, an explicit temporary central root and a loopback-only scripted model. The browser exercised the real DSH settings, RPC and Source stack. + +1. Select centralized workspace storage and global USER.md. A relative root blocks Save; an absolute root saves and takes effect live. +2. In A, add a project fact and a global preference, create a Document, enable Holographic and create a real Native space (88 KB database). +3. In B, confirm zero A project facts, Documents or Provider mappings. Only the global preference is visible. Add B's distinct project fact. +4. Clear the root and save: the workspace uses the default global-root subtree, with the global preference still visible. Restore the custom root: the original facts return. Five control-file SHA-256 values remain identical across these switches and Sidebar/Builtin changes. +5. Create real A/B DSH sessions with the scripted model. Builtin follows each owning session and omits the workspace picker. Restart the Host: both facts, the global preference, A's Document and its Provider mappings survive. +6. Inspect A while the active conversation belongs to B. The difference indicator appears; Align returns to B. Test Chinese Dark, English Light and a 390×844 Sidebar. + +Disk assertions found `runtime`, `data`, `documents` and `state` under A's single hashed subtree, and no `.mnemon` directory in either project. Desktop Provider canvas measurements were 992/992 pixels (client/scroll width); narrow Runtime and Memory Spaces were 326/326. Final browser console inspection returned no errors. + +Screenshot review caught and fixed two layout defects: long directory paths caused horizontal overflow, and the English alignment notice squeezed out the workspace selector. The final screenshots show constrained directory text and wrapping header controls; the existing presentation fingerprint was updated after visual verification. + +## Screenshots + +| Evidence | Shows | +|---|---| +| [01 — Chinese Dark settings](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-189-core-workspaces/01-settings-zh-dark.png) | Scope, optional root and independent global profile | +| [02 — A Runtime](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-189-core-workspaces/02-workspace-a-runtime.png) | A project fact plus global preference | +| [03 — A Document](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-189-core-workspaces/03-workspace-a-document.png) | Successful managed Document creation | +| [04 — A Providers](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-189-core-workspaces/04-workspace-a-providers.png) | Real Native database and Holographic mapping; corrected long-path layout | +| [05 — B isolation](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-189-core-workspaces/05-workspace-b-isolation.png) | Global preference present; A project fact absent | +| [06 — English Light settings](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-189-core-workspaces/06-settings-en-light.png) | Full centralized label and root control | +| [07 — Builtin after restart](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-189-core-workspaces/07-builtin-b-after-restart.png) | B's recovered fact in its owning session | +| [08 — Workspace alignment](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-189-core-workspaces/08-workspace-alignment.png) | A inspection versus B execution, with visible selector and alignment action | +| [09 — 390px Sidebar](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-189-core-workspaces/09-sidebar-390px.png) | Narrow Memory Spaces, long path and alignment controls | + +## Limits + +This run validates local persistence and UI/transport behavior, not live model distillation quality or remote Provider accounts. Five real-Flash checks were not enabled; the Windows-only binary smoke check requires Windows. Moving or renaming a workspace selects a new path-derived ID; old data is preserved without migration. Remote Provider namespaces keep their own sharing semantics. Test data and browser tabs were removed after verification; screenshots contain only synthetic content and temporary paths. diff --git a/dsh/docs/pr-assets/issue-189-core-workspaces/README.zh-CN.md b/dsh/docs/pr-assets/issue-189-core-workspaces/README.zh-CN.md new file mode 100644 index 000000000..60f7c2aa8 --- /dev/null +++ b/dsh/docs/pr-assets/issue-189-core-workspaces/README.zh-CN.md @@ -0,0 +1,61 @@ +# 内置集中工作区存储 — issue #189 + +[English](./README.md) · [证据索引](../README.md) + +2026-09-09 在 macOS 上使用正式发布的 DSH 0.1.2-rc.1、Node 22.19.0、pnpm 10.13.1 和 Mnemon Native 0.2.7 验证。新实现从 `main` 的 `820b0b092fe68eb7ce6fbf1eaa12e9cf39087e31` 分出,替代 PR #215 的独立存储插件方案。原工作目录和旧分支均保留。 + +功能验收 revision 为 `2314d6a44ee21b74f1e3bb22a0533d90285c5eea`,最终布局和完整自动化验证对应 `51446b51ed636eace6f6ca4d206969561ada194d`。截图 01–03、05–07 对应功能版本,04、08、09 对应最终布局;均为本次重新采集,没有复用旧插件 PR 的截图。 + +## 实现边界 + +根包 Host 统一解析四种布局和规范化工作区标识;Core 传递操作范围并保持 View 权限边界。现有 Source 通过原配置契约接收目录。没有新增包、依赖、存储贡献 API 或 SDK 导出。现有 Memory Spaces 包仅调整了长路径展示样式。 + +## 自动化验证 + +精确复现命令见[英文记录](./README.md#automated-verification),使用固定版本的 Node/pnpm 环境。 + +| 检查 | 结果 | +|---|---| +| 根包测试 | 852 通过;5 个 opt-in Flash 检查跳过 | +| `verify` 中的独立插件测试 | 324 通过;1 个 Windows 专属冒烟测试跳过 | +| 确定性构建、TypeScript、包出口与发布包检查 | 通过 | +| 真实 Headless Profile | 39 个工具、8 个代表性 Mnemon 工具;重启与旧 Entry 停用检查通过 | +| 真实 Native CLI | 临时 View 写入、召回、删除通过 | +| 独立制品 | 16 个独立插件仓库、17 个 tarball 通过;真实 DSH 仅安装根包 Starter 后可正常激活 | + +覆盖四种存储模式、旧 `dataDir` 配置、registry 拒绝路径、Headless/Builtin 会话归属、真实 Source 的四区域写入、USER.md 两种范围、符号链接别名、尚未创建的子路径、Unicode、同名目录、重命名和无破坏切换。最新 main 的 23 个容量工作流测试也全部通过。 + +最初发布包检查触发现有体积上限。单独构建 base revision 后,基线为 1,249,360 字节,最终为 1,256,247 字节,增加 6,887 字节,文件数仍为 46。增量仅来自 Host/UI、公开范围类型和 README 文案;没有打入 Source 或 Provider 实现。上限小幅调整为 1,260,000 字节。 + +## 真实 WebUI 验收 + +构建后运行已有的 `pnpm e2e:serve`。夹具使用临时 Profile、全局根、两个临时工作区、自定义集中根和仅监听 loopback 的固定回复模型;操作经过真实 DSH 设置、RPC 和 Source。 + +1. 选择集中工作区存储和全局 USER.md;相对根目录无法保存,绝对目录保存后实时生效。 +2. 在 A 写入项目事实和全局偏好,创建档案,启用 Holographic,并通过真实 Native CLI 创建 88 KB 数据库。 +3. B 看不到 A 的项目事实、档案或 Provider 映射,只能看到全局偏好;另写入 B 的独立项目事实。 +4. 清空集中根并保存后使用默认根的工作区子目录,全局偏好仍可见;恢复自定义根后原有事实恢复。切换根目录及 Sidebar/Builtin 前后,5 个控制文件的 SHA-256 一致。 +5. 建立真实 A/B 会话,Builtin 跟随所属会话且不显示工作区选择器。重启 Host 后,两份项目事实、全局偏好、A 的档案和 Provider 映射均保留。 +6. 当前会话属于 B 时查看 A,差异提示出现;对齐操作返回 B。完成中文深色、英文浅色及 390×844 Sidebar 验证。 + +磁盘断言确认 A 的 `runtime`、`data`、`documents`、`state` 同属一个哈希子目录,两个项目均没有 `.mnemon`。桌面 Provider 画布的可视/滚动宽度均为 992px;窄屏 Runtime 和 Memory Spaces 均为 326px。最终浏览器控制台未发现错误。 + +截图复查发现并修正了长目录导致的横向溢出、英文差异提示挤压工作区选择器两处布局问题。最终截图中的路径受列宽约束,标题栏控件可换行;视觉验收后同步更新了已有样式指纹。 + +## 截图 + +| 证据 | 内容 | +|---|---| +| [01 中文深色设置](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-189-core-workspaces/01-settings-zh-dark.png) | 范围、可选集中根和独立全局用户档案 | +| [02 A 运行时](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-189-core-workspaces/02-workspace-a-runtime.png) | A 项目事实与全局偏好 | +| [03 A 档案](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-189-core-workspaces/03-workspace-a-document.png) | 托管档案创建成功 | +| [04 A Provider](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-189-core-workspaces/04-workspace-a-providers.png) | 真实 Native 数据库、Holographic 映射及修正后的长路径布局 | +| [05 B 隔离](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-189-core-workspaces/05-workspace-b-isolation.png) | 全局偏好可见,A 项目事实不可见 | +| [06 英文浅色设置](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-189-core-workspaces/06-settings-en-light.png) | 完整的集中存储标签与目录控件 | +| [07 重启后的 Builtin](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-189-core-workspaces/07-builtin-b-after-restart.png) | B 所属会话读取恢复后的 B 事实 | +| [08 工作区对齐](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-189-core-workspaces/08-workspace-alignment.png) | 查看 A、执行 B,选择器和对齐操作均可见 | +| [09 390px Sidebar](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-189-core-workspaces/09-sidebar-390px.png) | 窄屏记忆空间、长路径和对齐控件 | + +## 验证限制 + +本次验证本地持久化和 UI/传输,不代表真实模型沉淀质量或远端 Provider 账号验收。5 个真实 Flash 检查未开启,Windows 二进制冒烟需要 Windows 环境。移动或重命名工作区会使用新的路径哈希,旧数据保留且不自动迁移;远端 Provider 仍遵循自身命名空间共享语义。验证结束后清理临时数据与浏览器标签页,截图仅含合成内容和临时路径。 diff --git a/dsh/docs/pr-assets/issue-201-electron-cli/README.md b/dsh/docs/pr-assets/issue-201-electron-cli/README.md new file mode 100644 index 000000000..60bd636fb --- /dev/null +++ b/dsh/docs/pr-assets/issue-201-electron-cli/README.md @@ -0,0 +1,78 @@ +# Issue #201: Electron npm CLI verification + +## Revision and environment / 版本与环境 + +- Issue: [#201](https://github.com/omdsh-dev/dsh-mnemon/issues/201). +- Baseline: `main` at `0d5f5fa`; source tested after repair: `1dc3a21018a9918188bf15224d2f9fa59ac719d3`. Evidence-only commits do not change that implementation. +- Date: 2026-09-09. macOS 15.6 arm64; Electron 44.3.0 with Node 24.20.0; ordinary Node Host 25.1.0; pnpm 11.19.0; Playwright 1.63.0 / Chromium 153.0.8010.12, 1440 × 1050. +- Published DSH 0.1.2-rc.1, locally built dsh-mnemon 0.5.5 and Source 0.5.4 artifacts. Each Profile, memory root, browser state and npm installation was disposable. npm prefixes deliberately contained spaces. Absolute checkout paths were masked when capturing version dialogs. + +本记录对应从 `main@0d5f5fa` 创建的独立 worktree,修复代码为 `1dc3a21`。DSH 使用正式发布包,未修改其源码或依赖制品。Electron 主进程中的 `process.type` 为 `browser`,`process.execPath` 为 Electron GUI 可执行文件,Host 的 `ELECTRON_RUN_AS_NODE` 未设置。npm、Profile、记忆根目录和浏览器均使用隔离测试数据;截图中的 checkout 路径在拍摄时遮盖。 + +## Failure and repair / 复现与修复 + +On the baseline Electron Host, the version dialog found the npm launcher but could not read its version. Creating a Memory Space timed out after 10,000 ms because the child was launched as another Electron application. The Windows report's exact `invalid JSON` text was not reproduced on macOS; the same incorrect GUI launch was reproduced with a real Electron main process. + +The repair sets `ELECTRON_RUN_AS_NODE=1` only on verified JavaScript launcher invocations: memory commands, `--version`, the Windows npm ownership probe and the Mnemon update launcher. Saved embedding overrides retain priority; `auto` does not regain an inherited protocol. Native executables keep their direct invocation, and the Host environment is not mutated. + +修复前,真实 Electron Host 的版本弹窗无法读取 npm CLI 版本,创建空间在 10 秒后超时。macOS 未复现 Windows 报告中的完全相同错误文本,但复现了相同的错误 GUI 启动路径。修复后仅为 JavaScript 启动器子进程设置 Node 模式;记忆调用、版本检查、npm 归属检查和更新均覆盖,embedding 配置、原生二进制调用及 Host 环境保持原有语义。 + +## Automated checks / 自动验证 + +| Check / 检查 | Result / 结果 | +|---|---| +| Regression tests before the fix | 5 expected failures: 3 real child-environment cases and 2 Electron version/update cases; 48 passed | +| Targeted runner, runner configuration and version suites after the fix | 63 passed, including case-insensitive environment keys, paths with spaces, embedding disabled/auto/explicit protocol, unchanged Host environment and native process options | +| `pnpm verify` | Passed: 1,121 test executions, 2 opt-in skips; types, deterministic builds, plugin builds, real Headless activation and package validation passed | +| `pnpm verify:plugins` | Passed: 16 independent plugin repositories / 17 packed artifacts, external SDK/Client consumer and real packed DSH composition | +| `MNEMON_NATIVE_TEST_CLI= pnpm exec vitest run plugins/dsh-mnemon-source-memory-spaces/tests/native-integration.spec.ts` | Passed: real Source/Provider create, View write, recall and forget; this separately exercised one of the full-suite opt-in skips | +| Real Electron main-process smoke | npm symlink, Windows-shaped `.cmd`, and native binary each passed status, write, recall, forget and version checks; Host Node-mode variable remained absent | +| Actual npm launcher `update` in Electron | Passed against the isolated npm prefix; upstream 0.2.8 was already current, so this was a real reinstall/current-version check | +| Documentation and whitespace | `pnpm verify:docs` and `git diff --check` passed | + +The selected, credential-free Electron process results are retained in [electron-smoke.txt](./electron-smoke.txt). The existing rejection tests for unrelated npm roots, missing npm, broken launchers and updates that do not activate the expected version also passed. + +完整验证通过 1,121 项测试执行,2 项按需测试默认跳过;随后单独启用了真实 Native Source/Provider 集成测试并通过。真实 Electron 冒烟覆盖 npm 链接、Windows 形式的 `.cmd` 和原生二进制三条路径。Windows 专用冒烟仍未运行。错误 npm 归属、npm 缺失、启动器故障及更新后版本未变化的拒绝路径均通过已有回归测试。 + +## WebUI checks / WebUI 验证 + +All actions below used the real published DSH Web stack and the locally built plugin. The ordinary memory scenarios used the published Mnemon npm 0.2.8 package and its native binary. The model endpoint returned a fixed response; the no-session Sidebar's constrained write used the real Source mutation path, so these checks do not claim LLM distillation quality. + +| Scenario / 场景 | Observed result / 观察结果 | Screenshot / 截图 | +|---|---|---| +| Baseline Electron version check | npm launcher found, version unreadable | [Before](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-201-electron-cli/01-before-versions.png) | +| Repaired Electron npm version check | 0.2.8, npm ownership recognized; Check again and all 16 subpackage entries worked | [Versions](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-201-electron-cli/03-after-versions.png) | +| Electron npm write and browse | Created `Electron issue 201`, activated it, deactivated `default`, wrote a synthetic fact with tags/entities and read it back | [Content](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-201-electron-cli/04-after-write-content.png) | +| Electron npm keyword recall | Retrieved the exact persisted sentinel with its space and Provider provenance | [Recall](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-201-electron-cli/05-after-recall.png) | +| Electron npm entity and graph reads | Entity lookup returned the sentinel; related lookup completed; both stores reported healthy storage | [Space health](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-201-electron-cli/06-after-space-health.png) | +| Forget cancellation and confirmation | Cancel retained the item; Confirm forget removed it and showed the empty state | [After forget](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-201-electron-cli/07-after-forget.png) | +| Electron update dialog with a controlled npm package | Real RPC and process execution advanced a test version from 0.2.7 to 0.2.8; success shown and Update removed | [Update fixture](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-201-electron-cli/08-update-fixture-success.png) | +| Ordinary Node npm version check | 0.2.8 and npm ownership recognized | [Node versions](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-201-electron-cli/09-node-host-versions.png) | +| Ordinary Node npm memory workflow | Create, activate, write, browse, keyword recall, empty filter, filter recovery and confirmed forget passed | [Node recall](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-201-electron-cli/10-node-host-recall.png) | +| Electron with explicit native `cliPath` | Existing spaces survived restart; version 0.2.8, write and keyword recall passed; automatic npm update remained unavailable for the direct binary | [Native path](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-201-electron-cli/11-electron-native-binary.png) | + +真实 WebUI 已验证修复前故障、修复后版本识别与重新检查、子包展开、空间创建和激活、写入和内容浏览、关键词召回、实体和关联读取、删除取消与确认、Node Host 回归,以及显式原生二进制路径。上述记忆操作均写入隔离的真实 Mnemon 数据库。 + +The update-dialog scenario intentionally used a separate controlled package because npm had only the current 0.2.8 stable release. Its launcher recorded Electron 44.3.0 and `ELECTRON_RUN_AS_NODE=1` for `--version`, `update`, and the post-update recheck. It changed a test version marker; it was not an actual upstream 0.2.7-to-0.2.8 upgrade. The actual published npm update command was exercised separately as described above. + +更新弹窗使用独立受控包验证 0.2.7 → 0.2.8 的状态转换,真实 RPC 和 Electron 子进程均参与,但变化的是测试版本标记,并非真实上游升级。实际官方 npm 启动器的自更新已另行执行,当时 0.2.8 已是最新版本。 + +## Reproduction / 复测 + +Build with `pnpm verify`. Install test Electron and Mnemon into disposable directories, then run the [Electron WebUI fixture](../../../scripts/fixtures/electron-dsh-host.cjs) through the normal E2E server: + +```sh +MNEMON_CLI_PATH="/bin/mnemon" \ +npm_config_prefix="" \ +pnpm e2e:serve --electron="/Electron.app/Contents/MacOS/Electron" +``` + +Use the printed loopback URL. Repeat the table's actions. Omit `--electron` for the ordinary Node Host. For the direct native path, set `mnemon.cliPath` in the disposable Profile settings to the installed platform binary. The fixture enables `--expose-internals` for the published Cordis loader; it runs DSH inside Electron rather than spawning a separate Node core. Ctrl-C removes the disposable Profile and memory data. + +运行 `pnpm verify` 后,在临时目录安装测试 Electron 与 Mnemon,按上方命令启动并打开输出的本地地址。移除 `--electron` 即可验证普通 Node Host;原生路径通过临时 Profile 的 `mnemon.cliPath` 设置。Ctrl-C 清理临时 Profile 和记忆数据。 + +## Limits / 边界 + +Windows 11 and the specific anywhere-labs desktop shell were not available locally. The Windows `.cmd` layout and npm invocation branches were exercised by automated tests, and a Windows-shaped shim executed successfully in real Electron on macOS. A desktop build with Electron's `runAsNode` fuse disabled still requires the documented native-binary path. No live embedding service or LLM quality was tested; embedding environment merging was verified through actual child-process regression tests. + +本地未实测 Windows 11 或用户指定的 anywhere-labs 桌面壳。`.cmd` 布局、npm 调用分支已有自动化覆盖,并在 macOS 的真实 Electron 中成功执行 Windows 形式的 shim。关闭 `runAsNode` fuse 的桌面构建仍需文档所述原生路径。本次没有验证真实 embedding 服务或模型质量;embedding 环境合并通过真实子进程测试验证。 diff --git a/dsh/docs/pr-assets/issue-201-electron-cli/electron-smoke.txt b/dsh/docs/pr-assets/issue-201-electron-cli/electron-smoke.txt new file mode 100644 index 000000000..73a8a23b7 --- /dev/null +++ b/dsh/docs/pr-assets/issue-201-electron-cli/electron-smoke.txt @@ -0,0 +1,4 @@ +{"label":"npm symlink","electron":"44.3.0","node":"24.20.0","hostType":"browser","hostRunAsNode":null,"status":true,"write":true,"recall":true,"forget":true,"version":"0.2.8","mode":"npm"} +Actual npm launcher self-update completed in the isolated prefix; 0.2.8 was already latest. +{"label":"Windows-shaped .cmd","electron":"44.3.0","node":"24.20.0","hostType":"browser","hostRunAsNode":null,"status":true,"write":true,"recall":true,"forget":true,"version":"0.2.8","mode":"npm"} +{"label":"native binary","electron":"44.3.0","node":"24.20.0","hostType":"browser","hostRunAsNode":null,"status":true,"write":true,"recall":true,"forget":true,"version":"0.2.8","mode":"manual"} diff --git a/dsh/docs/pr-assets/issue-202-created-order/README.md b/dsh/docs/pr-assets/issue-202-created-order/README.md new file mode 100644 index 000000000..80c331226 --- /dev/null +++ b/dsh/docs/pr-assets/issue-202-created-order/README.md @@ -0,0 +1,62 @@ +# Issue #202: newest creations first / 创建时间倒序 + +Captured on 2026-09-09 using implementation `690a46b17526d4ad38f58b23f2f8d3d6896834e1`, based on `main` revision `0d5f5fafe4e8d7ec5e299c637baa1a05d3020c84`. Screenshots 01–02 use the main build; screenshots 03–08 use the fixed build. DSH `0.1.2-rc.1`, Starter `0.5.5`, Runtime/Documents Sources `0.5.4`, Node `25.1.0`, pnpm `11.19.0`; the browser uses its default 1280 × 720 viewport and Chinese locale. + +截图日期为 2026-09-09,实现和 main 基线版本如上。01–02 为 main 构建,03–08 为修复后构建。使用正式发布的 DSH 契约、本次本地 Source 构建、浏览器默认 1280 × 720 视口和中文界面。 + +## Setup and scope / 环境与范围 + +`pnpm e2e:serve` provides a real DSH WebUI with a disposable `DSH_HOME`, data root, workspace and loopback model endpoint. Seeded through real Source management: 12 Runtime entries (six USER, six MEMORY) and 12 Documents (ten active, two archived). Creation dates span August 1–12, with insertion order 03, 01, 02, 04…12; a separate seeding process controls fixture dates. Runtime 02 and Document 01 were edited on a later fixture date. Subsequent browser edits and creations use the real clock. + +`pnpm e2e:serve` 启动真实 DSH WebUI,使用一次性的配置、数据根、工作区和本机模型端点。通过真实 Source management 写入 12 条运行时记忆(USER、MEMORY 各六条)和 12 份档案(十份活跃、两份归档)。创建日期为 8 月 1–12 日,写入顺序为 03、01、02、04…12;仅独立播种进程控制夹具日期。运行时 02 和档案 01 随后在更晚的夹具日期编辑,浏览器里的新增和编辑使用实际时间。 + +All records are synthetic. Model replies are scripted; Source persistence, page loading, filtering, search, edits, creation and reloads are real. Archive records were seeded locally, so this does not test model-driven cold-index creation. This change only sorts the displayed matches: search eligibility, result limits and engine relevance ranking are unchanged. The shared Source pages serve Sidebar and Builtin; this browser run covers Sidebar. + +全部资料为合成测试数据。只有模型回复被脚本化,Source 持久化、页面加载、筛选、检索、编辑、新增和刷新均走真实链路。归档记录由本地播种,因此不验证模型驱动的冷索引创建。改动仅调整返回结果的页面顺序,检索范围、结果数量上限和引擎相关性排序保持不变。Sidebar 与 Builtin 共享 Source 页面,本次浏览器验收覆盖 Sidebar。 + +## Results / 结果 + +| Check / 检查 | Result / 结果 | +|---|---| +| Before/after regression / 修复前后回归 | Both new page tests fail against main and pass against the fixed pages. / 两项新页面用例在 main 页面实现上失败,在修复后通过。 | +| Runtime / 运行时 | First page 12…03; Show more continues with 02, 01. MEMORY and text filters retain descending creation order. / 首屏 12…03,“再显示”追加 02、01,工作记忆和文本筛选保持倒序。 | +| Documents / 档案 | Active list and search show 10…03, then 02, 01; archived list and archived search show 12, 11. / 活跃目录及检索先显示 10…03,再显示 02、01;归档目录和归档检索显示 12、11。 | +| Actual UI writes / 实际界面写入 | Editing Runtime 02 and Document 01 preserves their creation times and positions. Document 01 reaches revision 3. New Runtime 13 and Document 13 appear first and remain first after reload. / 编辑运行时 02、档案 01 保持创建时间与顺序,档案 01 成为第 3 版。新增运行时 13、档案 13 置顶,刷新后保持。 | +| Data / 数据 | Disk assertions confirm original storage order and creation timestamps remain unchanged; final totals are 13 Runtime, 11 active Documents and two archived Documents. / 磁盘断言确认原存储顺序和创建时间保留,最终为 13 条运行时、11 份活跃和两份归档。 | +| Browser console / 浏览器控制台 | Zero errors or warnings during the fixed-build run. Four connection-retry warnings came from intentionally stopping the baseline server. / 修复后构建验收期间无错误或警告;四条连接重试警告来自主动停止基线服务。 | +| Full verification / 完整验证 | PASS: 1116 tests, two opt-in Native/Windows skips; types, deterministic builds, Headless and package checks. / 通过:1116 项测试,跳过两项需额外启用的 Native/Windows 测试;类型、确定性构建、Headless 与包校验通过。 | +| Independent packages / 独立发布包 | PASS: 16 independent plugin repositories, 17 packed artifacts, external consumer and real DSH activation with four workers. / 通过:四路并行验证 16 个独立插件仓库、17 个发布制品、外部消费方和真实 DSH 激活。 | + +[Browser and disk assertions / 浏览器及磁盘断言](./browser-assertions.json) + +The published upstream UI package has an existing missing-source-map warning during Vitest. It does not prevent the checks from passing. / 已发布上游 UI 包在 Vitest 中存在缺少 source map 的现有警告,不影响检查通过。 + +The first independent-package attempt exposed timeouts in the new tests while seeding repeatedly through Source management under parallel load. The final regressions use frozen page snapshots without changing the global clock. The first remote artifact run also exposed repeated whole-list accessibility visibility scans under CI load; ordering assertions now read the list nodes directly and use scoped labels/text for interaction. Existing real Source mutation tests, timeouts and actual WebUI writes remain in place. + +首次独立包检查中,新增测试在并行负载下通过 Source management 反复播种时超时。最终回归使用冻结的页面快照,不再修改全局时钟。首次远端制品检查还暴露了 CI 负载下反复扫描整份列表可访问性与可见性的开销;排序断言现直接读取列表节点,交互使用有范围的标签/文本定位。原有真实 Source 写入测试、超时限制和本次实际 WebUI 写入验收均保留。 + +## Reproduce / 复现 + +```sh +pnpm install --frozen-lockfile +pnpm run verify +pnpm run verify:plugins --skip-build +pnpm e2e:serve +``` + +Select the printed disposable workspace and the **Mnemon E2E** preset, send a test message, then open **Memory system**. Create multiple Runtime entries across both targets and multiple Documents. Verify newest-first order, edit an older record, filter/search, load more, and reload. The page regression fixtures contain the out-of-order dates and pagination cases. Stop the fixture with Ctrl-C when finished. + +选择打印的一次性工作区和 **Mnemon E2E** 预设,发送测试消息后打开**记忆系统**。分别创建两种目标的多条运行时记忆和多份档案,检查倒序、编辑旧记录、筛选/检索、加载更多和刷新。页面回归夹具覆盖乱序日期与分页场景。结束后用 Ctrl-C 停止夹具。 + +## Screenshots / 截图 + +| Evidence / 证据 | Screenshot / 截图 | +|---|---| +| Runtime before / 修复前运行时 | [01](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-202-created-order/01-runtime-before.png) | +| Documents before / 修复前档案 | [02](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-202-created-order/02-documents-before.png) | +| Runtime newest first / 运行时倒序 | [03](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-202-created-order/03-runtime-newest-first.png) | +| Runtime UI creation first / 界面新增运行时置顶 | [04](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-202-created-order/04-runtime-ui-create.png) | +| Active Documents newest first / 活跃档案倒序 | [05](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-202-created-order/05-documents-newest-first.png) | +| Archived Documents newest first / 归档倒序 | [06](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-202-created-order/06-archived-newest-first.png) | +| Edited old document stays last / 编辑旧档案后仍在末尾 | [07](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-202-created-order/07-documents-edit-preserved.png) | +| New document remains first after reload / 新档案刷新后仍置顶 | [08](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-202-created-order/08-documents-ui-create.png) | diff --git a/dsh/docs/pr-assets/issue-202-created-order/browser-assertions.json b/dsh/docs/pr-assets/issue-202-created-order/browser-assertions.json new file mode 100644 index 000000000..006ff6d7b --- /dev/null +++ b/dsh/docs/pr-assets/issue-202-created-order/browser-assertions.json @@ -0,0 +1,507 @@ +{ + "date": "2026-09-09", + "implementationRevision": "690a46b17526d4ad38f58b23f2f8d3d6896834e1", + "baseRevision": "0d5f5fafe4e8d7ec5e299c637baa1a05d3020c84", + "runtimeFirstPage": [ + { + "content": "运行时排序验证 12:创建于 8 月 12 日。", + "createdAt": "2026-08-12T08:00:00.000Z" + }, + { + "content": "运行时排序验证 11:创建于 8 月 11 日。", + "createdAt": "2026-08-11T08:00:00.000Z" + }, + { + "content": "运行时排序验证 10:创建于 8 月 10 日。", + "createdAt": "2026-08-10T08:00:00.000Z" + }, + { + "content": "运行时排序验证 09:创建于 8 月 9 日。", + "createdAt": "2026-08-09T08:00:00.000Z" + }, + { + "content": "运行时排序验证 08:创建于 8 月 8 日。", + "createdAt": "2026-08-08T08:00:00.000Z" + }, + { + "content": "运行时排序验证 07:创建于 8 月 7 日。", + "createdAt": "2026-08-07T08:00:00.000Z" + }, + { + "content": "运行时排序验证 06:创建于 8 月 6 日。", + "createdAt": "2026-08-06T08:00:00.000Z" + }, + { + "content": "运行时排序验证 05:创建于 8 月 5 日。", + "createdAt": "2026-08-05T08:00:00.000Z" + }, + { + "content": "运行时排序验证 04:创建于 8 月 4 日。", + "createdAt": "2026-08-04T08:00:00.000Z" + }, + { + "content": "运行时排序验证 03:创建于 8 月 3 日。", + "createdAt": "2026-08-03T08:00:00.000Z" + } + ], + "runtimeExpanded": [ + { + "content": "运行时排序验证 12:创建于 8 月 12 日。", + "createdAt": "2026-08-12T08:00:00.000Z" + }, + { + "content": "运行时排序验证 11:创建于 8 月 11 日。", + "createdAt": "2026-08-11T08:00:00.000Z" + }, + { + "content": "运行时排序验证 10:创建于 8 月 10 日。", + "createdAt": "2026-08-10T08:00:00.000Z" + }, + { + "content": "运行时排序验证 09:创建于 8 月 9 日。", + "createdAt": "2026-08-09T08:00:00.000Z" + }, + { + "content": "运行时排序验证 08:创建于 8 月 8 日。", + "createdAt": "2026-08-08T08:00:00.000Z" + }, + { + "content": "运行时排序验证 07:创建于 8 月 7 日。", + "createdAt": "2026-08-07T08:00:00.000Z" + }, + { + "content": "运行时排序验证 06:创建于 8 月 6 日。", + "createdAt": "2026-08-06T08:00:00.000Z" + }, + { + "content": "运行时排序验证 05:创建于 8 月 5 日。", + "createdAt": "2026-08-05T08:00:00.000Z" + }, + { + "content": "运行时排序验证 04:创建于 8 月 4 日。", + "createdAt": "2026-08-04T08:00:00.000Z" + }, + { + "content": "运行时排序验证 03:创建于 8 月 3 日。", + "createdAt": "2026-08-03T08:00:00.000Z" + }, + { + "content": "运行时排序验证 02:创建于 8 月 2 日,9 月 1 日编辑;应保留原顺序。", + "createdAt": "2026-08-02T08:00:00.000Z" + }, + { + "content": "运行时排序验证 01:创建于 8 月 1 日。", + "createdAt": "2026-08-01T08:00:00.000Z" + } + ], + "runtimeMemoryFilter": [ + { + "content": "运行时排序验证 12:创建于 8 月 12 日。", + "createdAt": "2026-08-12T08:00:00.000Z" + }, + { + "content": "运行时排序验证 10:创建于 8 月 10 日。", + "createdAt": "2026-08-10T08:00:00.000Z" + }, + { + "content": "运行时排序验证 08:创建于 8 月 8 日。", + "createdAt": "2026-08-08T08:00:00.000Z" + }, + { + "content": "运行时排序验证 06:创建于 8 月 6 日。", + "createdAt": "2026-08-06T08:00:00.000Z" + }, + { + "content": "运行时排序验证 04:创建于 8 月 4 日。", + "createdAt": "2026-08-04T08:00:00.000Z" + }, + { + "content": "运行时排序验证 02:创建于 8 月 2 日,9 月 1 日编辑;应保留原顺序。", + "createdAt": "2026-08-02T08:00:00.000Z" + } + ], + "runtimeTextFilter": [ + { + "content": "运行时排序验证 12:创建于 8 月 12 日。", + "createdAt": "2026-08-12T08:00:00.000Z" + }, + { + "content": "运行时排序验证 10:创建于 8 月 10 日。", + "createdAt": "2026-08-10T08:00:00.000Z" + } + ], + "runtimeEdited": [ + { + "content": "运行时排序验证 02:创建于 8 月 2 日;已通过 Web UI 再次编辑,仍按创建时间排序。", + "createdAt": "2026-08-02T08:00:00.000Z" + } + ], + "runtimeAfterCreate": [ + { + "content": "运行时排序验证 13:通过 Web UI 新增,创建时间最新,应显示在顶部。", + "createdAt": "2026-09-09T07:35:36.391Z" + }, + { + "content": "运行时排序验证 12:创建于 8 月 12 日。", + "createdAt": "2026-08-12T08:00:00.000Z" + }, + { + "content": "运行时排序验证 11:创建于 8 月 11 日。", + "createdAt": "2026-08-11T08:00:00.000Z" + }, + { + "content": "运行时排序验证 10:创建于 8 月 10 日。", + "createdAt": "2026-08-10T08:00:00.000Z" + }, + { + "content": "运行时排序验证 09:创建于 8 月 9 日。", + "createdAt": "2026-08-09T08:00:00.000Z" + }, + { + "content": "运行时排序验证 08:创建于 8 月 8 日。", + "createdAt": "2026-08-08T08:00:00.000Z" + }, + { + "content": "运行时排序验证 07:创建于 8 月 7 日。", + "createdAt": "2026-08-07T08:00:00.000Z" + }, + { + "content": "运行时排序验证 06:创建于 8 月 6 日。", + "createdAt": "2026-08-06T08:00:00.000Z" + }, + { + "content": "运行时排序验证 05:创建于 8 月 5 日。", + "createdAt": "2026-08-05T08:00:00.000Z" + }, + { + "content": "运行时排序验证 04:创建于 8 月 4 日。", + "createdAt": "2026-08-04T08:00:00.000Z" + } + ], + "documentsFirstPage": [ + { + "createdAt": "2026-08-10T08:00:00.000Z", + "title": "排序档案 10" + }, + { + "createdAt": "2026-08-09T08:00:00.000Z", + "title": "排序档案 09" + }, + { + "createdAt": "2026-08-08T08:00:00.000Z", + "title": "排序档案 08" + }, + { + "createdAt": "2026-08-07T08:00:00.000Z", + "title": "排序档案 07" + }, + { + "createdAt": "2026-08-06T08:00:00.000Z", + "title": "排序档案 06" + }, + { + "createdAt": "2026-08-05T08:00:00.000Z", + "title": "排序档案 05" + }, + { + "createdAt": "2026-08-04T08:00:00.000Z", + "title": "排序档案 04" + }, + { + "createdAt": "2026-08-03T08:00:00.000Z", + "title": "排序档案 03" + } + ], + "documentsExpanded": [ + { + "createdAt": "2026-08-10T08:00:00.000Z", + "title": "排序档案 10" + }, + { + "createdAt": "2026-08-09T08:00:00.000Z", + "title": "排序档案 09" + }, + { + "createdAt": "2026-08-08T08:00:00.000Z", + "title": "排序档案 08" + }, + { + "createdAt": "2026-08-07T08:00:00.000Z", + "title": "排序档案 07" + }, + { + "createdAt": "2026-08-06T08:00:00.000Z", + "title": "排序档案 06" + }, + { + "createdAt": "2026-08-05T08:00:00.000Z", + "title": "排序档案 05" + }, + { + "createdAt": "2026-08-04T08:00:00.000Z", + "title": "排序档案 04" + }, + { + "createdAt": "2026-08-03T08:00:00.000Z", + "title": "排序档案 03" + }, + { + "createdAt": "2026-08-02T08:00:00.000Z", + "title": "排序档案 02" + }, + { + "createdAt": "2026-08-01T08:00:00.000Z", + "title": "排序档案 01" + } + ], + "documentsSearch": [ + { + "createdAt": "2026-08-10T08:00:00.000Z", + "title": "排序档案 10" + }, + { + "createdAt": "2026-08-09T08:00:00.000Z", + "title": "排序档案 09" + }, + { + "createdAt": "2026-08-08T08:00:00.000Z", + "title": "排序档案 08" + }, + { + "createdAt": "2026-08-07T08:00:00.000Z", + "title": "排序档案 07" + }, + { + "createdAt": "2026-08-06T08:00:00.000Z", + "title": "排序档案 06" + }, + { + "createdAt": "2026-08-05T08:00:00.000Z", + "title": "排序档案 05" + }, + { + "createdAt": "2026-08-04T08:00:00.000Z", + "title": "排序档案 04" + }, + { + "createdAt": "2026-08-03T08:00:00.000Z", + "title": "排序档案 03" + } + ], + "archivedSearch": [ + { + "createdAt": "2026-08-12T08:00:00.000Z", + "title": "排序档案 12" + }, + { + "createdAt": "2026-08-11T08:00:00.000Z", + "title": "排序档案 11" + } + ], + "archivedDocuments": [ + { + "createdAt": "2026-08-12T08:00:00.000Z", + "title": "排序档案 12" + }, + { + "createdAt": "2026-08-11T08:00:00.000Z", + "title": "排序档案 11" + } + ], + "documentsAfterEdit": [ + { + "createdAt": "2026-08-10T08:00:00.000Z", + "title": "排序档案 10" + }, + { + "createdAt": "2026-08-09T08:00:00.000Z", + "title": "排序档案 09" + }, + { + "createdAt": "2026-08-08T08:00:00.000Z", + "title": "排序档案 08" + }, + { + "createdAt": "2026-08-07T08:00:00.000Z", + "title": "排序档案 07" + }, + { + "createdAt": "2026-08-06T08:00:00.000Z", + "title": "排序档案 06" + }, + { + "createdAt": "2026-08-05T08:00:00.000Z", + "title": "排序档案 05" + }, + { + "createdAt": "2026-08-04T08:00:00.000Z", + "title": "排序档案 04" + }, + { + "createdAt": "2026-08-03T08:00:00.000Z", + "title": "排序档案 03" + }, + { + "createdAt": "2026-08-02T08:00:00.000Z", + "title": "排序档案 02" + }, + { + "createdAt": "2026-08-01T08:00:00.000Z", + "title": "排序档案 01" + } + ], + "documentsAfterCreate": [ + { + "createdAt": "2026-09-09T07:37:20.783Z", + "title": "排序档案 13:Web UI 新建" + }, + { + "createdAt": "2026-08-10T08:00:00.000Z", + "title": "排序档案 10" + }, + { + "createdAt": "2026-08-09T08:00:00.000Z", + "title": "排序档案 09" + }, + { + "createdAt": "2026-08-08T08:00:00.000Z", + "title": "排序档案 08" + }, + { + "createdAt": "2026-08-07T08:00:00.000Z", + "title": "排序档案 07" + }, + { + "createdAt": "2026-08-06T08:00:00.000Z", + "title": "排序档案 06" + }, + { + "createdAt": "2026-08-05T08:00:00.000Z", + "title": "排序档案 05" + }, + { + "createdAt": "2026-08-04T08:00:00.000Z", + "title": "排序档案 04" + } + ], + "runtimeReload": [ + { + "content": "运行时排序验证 13:通过 Web UI 新增,创建时间最新,应显示在顶部。", + "createdAt": "2026-09-09T07:35:36.391Z" + }, + { + "content": "运行时排序验证 12:创建于 8 月 12 日。", + "createdAt": "2026-08-12T08:00:00.000Z" + }, + { + "content": "运行时排序验证 11:创建于 8 月 11 日。", + "createdAt": "2026-08-11T08:00:00.000Z" + }, + { + "content": "运行时排序验证 10:创建于 8 月 10 日。", + "createdAt": "2026-08-10T08:00:00.000Z" + }, + { + "content": "运行时排序验证 09:创建于 8 月 9 日。", + "createdAt": "2026-08-09T08:00:00.000Z" + }, + { + "content": "运行时排序验证 08:创建于 8 月 8 日。", + "createdAt": "2026-08-08T08:00:00.000Z" + }, + { + "content": "运行时排序验证 07:创建于 8 月 7 日。", + "createdAt": "2026-08-07T08:00:00.000Z" + }, + { + "content": "运行时排序验证 06:创建于 8 月 6 日。", + "createdAt": "2026-08-06T08:00:00.000Z" + }, + { + "content": "运行时排序验证 05:创建于 8 月 5 日。", + "createdAt": "2026-08-05T08:00:00.000Z" + }, + { + "content": "运行时排序验证 04:创建于 8 月 4 日。", + "createdAt": "2026-08-04T08:00:00.000Z" + } + ], + "documentsReload": [ + { + "createdAt": "2026-09-09T07:37:20.783Z", + "title": "排序档案 13:Web UI 新建" + }, + { + "createdAt": "2026-08-10T08:00:00.000Z", + "title": "排序档案 10" + }, + { + "createdAt": "2026-08-09T08:00:00.000Z", + "title": "排序档案 09" + }, + { + "createdAt": "2026-08-08T08:00:00.000Z", + "title": "排序档案 08" + }, + { + "createdAt": "2026-08-07T08:00:00.000Z", + "title": "排序档案 07" + }, + { + "createdAt": "2026-08-06T08:00:00.000Z", + "title": "排序档案 06" + }, + { + "createdAt": "2026-08-05T08:00:00.000Z", + "title": "排序档案 05" + }, + { + "createdAt": "2026-08-04T08:00:00.000Z", + "title": "排序档案 04" + } + ], + "console": { + "errors": [], + "warningsDuringIntentionalRestart": [ + { + "level": "warn", + "message": "[connection] connection lost, retry #1", + "timestamp": "2026-09-09T07:33:42.786Z" + }, + { + "level": "warn", + "message": "[connection] connection lost, retry #2", + "timestamp": "2026-09-09T07:33:43.603Z" + }, + { + "level": "warn", + "message": "[connection] connection lost, retry #3", + "timestamp": "2026-09-09T07:33:45.251Z" + }, + { + "level": "warn", + "message": "[connection] connection lost, retry #4", + "timestamp": "2026-09-09T07:33:47.921Z" + } + ], + "warningsAfterRestart": [] + }, + "persisted": { + "originalStorageOrderPreserved": true, + "originalCreationTimestampsPreserved": true, + "runtimeCount": 13, + "activeDocumentCount": 11, + "archivedDocumentCount": 2, + "editedRuntime": { + "content": "运行时排序验证 02:创建于 8 月 2 日;已通过 Web UI 再次编辑,仍按创建时间排序。", + "created_at": "2026-08-02T08:00:00.000Z", + "updated_at": "2026-09-09T07:35:24.174Z" + }, + "editedDocument": { + "title": "排序档案 01", + "createdAt": "2026-08-01T08:00:00.000Z", + "updatedAt": "2026-09-09T07:36:56.371Z", + "revision": 3 + } + } +} diff --git a/dsh/docs/pr-assets/issue-210-review-warning/README.md b/dsh/docs/pr-assets/issue-210-review-warning/README.md new file mode 100644 index 000000000..52b03f378 --- /dev/null +++ b/dsh/docs/pr-assets/issue-210-review-warning/README.md @@ -0,0 +1,33 @@ +# Idle-review failure visibility — issue #210 + +English / 中文 · 2026-09-11 · Base `1e19caf` · DSH `0.1.5-rc.1`, Node `25.1.0`, pnpm `11.19.0`, Mnemon CLI `0.2.7`. + +The real DSH WebUI ran with disposable Host/data/workspace directories and all three Strategy enhancements enabled. Only the loopback model endpoint was controlled: after two real conversation turns, the inherited fork worker received HTTP 400 with `CONTEXT_WINDOW_EXCEEDED` and synthetic token counts (145508 / 98304). This reproduces the error path; it does not measure a real 145K-token model request or a paid provider. + +Before the fix, the new lifecycle/UI regressions failed and the memory page showed “System nominal” after the rejected review. Afterward, the Host logged the bounded error and the workspace showed a warning with task-model context-window guidance. A real UI runtime-memory write succeeded while the warning remained visible. The read-only UI regression also displays the diagnostic. Aborted reviews stay quiet; ordinary turns retain the warning and a successful review clears it. Fork inheritance, dirty candidates, and the existing retry eligibility remain intact. The warning is loaded with workspace status or its Refresh action, not pushed as a toast; it does not wake the parent model. + +真实 DSH WebUI 使用临时 Host、数据与工作区目录,并同时启用三个策略增强。仅模型端点受控:两轮真实会话完成后,继承父会话的 fork worker 收到含 `CONTEXT_WINDOW_EXCEEDED` 的 HTTP 400,token 数(145508 / 98304)为合成值。该测试复现错误处理路径,不代表实际发送了 145K token 或调用付费服务。 + +修复前,新增生命周期与 UI 回归测试失败,审查报错后记忆页仍显示“系统正常”。修复后,Host 记录有界错误,工作区展示警告和任务模型窗口指引;保留警告时,真实 UI 热记忆写入成功。只读 UI 也可查看错误。取消审查不报警,普通轮次保留警告,成功审查后清除。fork 继承、候选内容及原有重试资格规则保持不变。警告随工作区状态加载或刷新显示,不会启动父模型或弹出通知。 + +## Reproduce / 复现 + +```sh +pnpm build +pnpm --workspace-concurrency=4 -r build +MNEMON_CLI_PATH=/absolute/path/to/mnemon pnpm e2e:serve --review-failure --strategy-extensions +``` + +Choose the printed disposable workspace and Mnemon E2E preset. Send a durable “Please remember” architecture rationale longer than 150 characters, then confirm it in a second turn. Wait five seconds and open Memory System. / 选择日志中的临时工作区和 Mnemon E2E 预设,发送超过 150 字符的长期决策与 “Please remember”,第二轮确认,等待五秒后打开记忆系统。 + +## Verification / 验证 + +- `pnpm run verify`: passed; 930 root tests, 5 expected skips, full plugin checks, deterministic builds, real isolated Headless activation, package/public-entry validation. +- `pnpm verify:plugins`: passed; 16 independent plugin repositories and 17 tarballs, including isolated Starter activation and all three enhancements. +- `MNEMON_NATIVE_TEST_CLI=/opt/homebrew/bin/mnemon pnpm --filter dsh-mnemon-source-memory-spaces exec vitest run tests/native-integration.spec.ts`: passed; real CLI create/write/recall/forget in a disposable store. +- Bilingual documentation links and `git diff --check`: passed. Upstream missing source-map notices are non-failing. +- Composition follow-up: the real loopback fixture returns HTTP 200 for a parent that advertises the stable result tool, and HTTP 400 only for a child completion persona (stable or legacy tool name). A user message quoting the protocol also stays HTTP 200. / 组合验证:真实本地端点对公开固定结果工具的父任务返回 HTTP 200,仅对子任务完成 persona 返回 HTTP 400(兼容固定和旧工具名);用户消息引用协议仍返回 HTTP 200。 + +![Before: silent failure](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-210-review-warning/before.png) +![After: warning and guidance](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-210-review-warning/after.png) +![After: runtime write succeeds](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-210-review-warning/runtime-write.png) diff --git a/dsh/docs/pr-assets/issue-211-20260911/README.md b/dsh/docs/pr-assets/issue-211-20260911/README.md new file mode 100644 index 000000000..44a62da0d --- /dev/null +++ b/dsh/docs/pr-assets/issue-211-20260911/README.md @@ -0,0 +1,47 @@ +# Idle review evidence reuse and tool composition + +**English** | [简体中文](./README.zh-CN.md) + +Issue [#211](https://github.com/omdsh-dev/dsh-mnemon/issues/211), verified 2026-09-11 against base `1e19caf40f0bf37edf678c3d829f2398ab28792a` and this PR's Host changes. [verification.json](./verification.json) records the source hashes, environment, measured results, and limits. + +The baseline already excludes AOCI from `REVIEW_TOOLS`. The reproduced gap is more specific: DSH's `tools.restrict()` filters inherited tools but preserves tools registered directly on a child Agent. An independently composed plugin that registers an overview tool on every Agent can therefore expose it to an idle reviewer. The real baseline reviewer executed that tool even though all five complete overview chunks were inherited. + +The fix attaches DSH's monotonic `tools.guard()` during child publication. An exact asynchronous start identity and `agents.isOwnedBy` keep concurrent and nested reviews separate. The guard stays installed until owned child disposal settles and admits the review allowlist plus the `run_code` transport; each Code Mode subcall is checked again. The persona tells the reviewer to reuse complete inherited evidence and skip candidates that bounded Document search cannot establish. Ordinary parent tools and manual memory actions remain available. + +## Reproduction and results + +Only model choices and the synthetic overview provider are fixtures. Agent publication, inherited history, tool dispatch, the fork provider, memory composition, persistence, CLI, HTTP transport, and WebUI are real published implementations. The fixture has 335 synthetic entries in five chunks, totaling 19,112 characters. It makes the same attempted child read before and after the fix; it does not choose its behavior based on the new persona. + +| Observation | Baseline | Fixed | +|---|---:|---:| +| Complete inherited overview chunks | 5 | 5 | +| Authorized parent overview reads | 5 | 5 | +| Attempted child overview reads | 1 | 1 | +| Executed child overview reads | 1 | 0 | +| Successful bounded Document searches | 1 | 1 | +| Completed review result | skipped | skipped | + +Run `pnpm exec vitest run tests/review-evidence-host.spec.ts tests/review-tools.spec.ts tests/subagent.spec.ts`. The real-host test covers native tools and the published worker-thread Code Mode runtime, including a tool attempt before the coordinator receives its run handle. Copy only the new real-host test and browser fixture into a baseline worktree to reproduce the failure; leave baseline Host source unchanged. + +For the browser run, build Root and plugins, then use `pnpm e2e:serve --review-evidence --strategy-extensions`. Choose the printed disposable workspace and Mnemon E2E preset. Send these two turns: + +1. `Read the complete synthetic overview once, then review the inherited checkpoint.` +2. `The five overview chunks are complete, covering all 335 synthetic modules. Finish this bounded checkpoint review using the evidence already present. This isolated fixture contains no new durable user preference and no missing overview section.` + +The normal activity gate is unchanged; the fixture shortens only the idle debounce to five seconds. Open the review child from the conversation's subagent menu. All three optional Strategy extensions were enabled in both browser runs. + +![Baseline child repeats an already inherited overview read](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-211-20260911/before-redundant-read.png) + +![Fixed child receives a denial and completes the review through allowed tools](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-211-20260911/after-read-denied.png) + +## Verification and limits + +The Root suite passed 935 tests and the plugin suites passed 323, with seven opt-in skips. The targeted suite passed 94 tests. Type checks, deterministic builds, Headless activation/restart/disable checks, package contents, public entries, and package lint passed. The initial package check measured 1,271,378 unpacked bytes against a 1,270,000-byte cap; the audited Host-only guard raised the cap to 1,275,000, and the package check then passed. + +The opt-in real Native Source suite passed 169 tests with one Windows-only skip, using `MNEMON_NATIVE_TEST_CLI=/opt/homebrew/bin/mnemon`. In the real WebUI, a new Native space was created after review, CLI 0.2.7 wrote an exact canary with `--no-diff`, the UI read it, and the normal Forget action returned the list to zero items. + +![Real Native CLI canary remains readable through normal WebUI controls](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-211-20260911/after-native-cli-read.png) + +Published DSH 0.1.1-rc.1 and 0.1.2-rc.1 tarballs were inspected for `isOwnedBy`, own-scope filter behavior, and `tools.guard`; the executable host/browser tests use 0.1.5-rc.1 on macOS arm64, Node 25.1.0, and pnpm 11.19.0. Missing guard support fails review explicitly. No persistence format, credentials, RPC authority, or personal memory changed. + +This does **not** reproduce the reporter's exact Windows/AOCI profile, six historical reads, or approximately 37K token usage. There was no live model call, AOCI server, or measured token-saving claim. The evidence establishes the current composition gap and its execution-level fix, not model judgment quality. The HTTP fixture and real-host test accept both the legacy unique-result-tool protocol and the stable result tool with a request envelope. diff --git a/dsh/docs/pr-assets/issue-211-20260911/README.zh-CN.md b/dsh/docs/pr-assets/issue-211-20260911/README.zh-CN.md new file mode 100644 index 000000000..8458bbd4a --- /dev/null +++ b/dsh/docs/pr-assets/issue-211-20260911/README.zh-CN.md @@ -0,0 +1,47 @@ +# 空闲审查的证据复用与工具组合 + +**简体中文** | [English](./README.md) + +关联 [Issue #211](https://github.com/omdsh-dev/dsh-mnemon/issues/211)。2026-09-11 基于 `1e19caf40f0bf37edf678c3d829f2398ab28792a` 及本 PR 的 Host 修改进行验证。[verification.json](./verification.json) 记录源码哈希、环境、实测结果和限制。 + +基线的 `REVIEW_TOOLS` 已排除 AOCI。本次复现的是更具体的组合缺口:DSH 的 `tools.restrict()` 过滤继承工具,却保留直接注册在子 Agent 作用域中的工具。如果另一个插件为每个 Agent 都注册概览工具,空闲审查仍能看到并执行它。真实基线中的审查已继承五个完整概览分块,却成功再次读取其中一个分块。 + +修复在子 Agent 发布时安装 DSH 的单调 `tools.guard()`。精确的异步启动标识配合 `agents.isOwnedBy` 区分并发及嵌套审查;保护持续到受管理的子 Agent 清理结束。它允许既有审查工具集及 `run_code` 传输,并继续检查每个 Code Mode 子调用。Persona 明确要求复用完整的继承证据;有界档案搜索仍不足以确认的候选直接跳过。父会话工具和正常手动记忆操作继续可用。 + +## 复现与结果 + +只有模型决策和合成概览 Provider 由夹具控制。Agent 发布、继承历史、工具执行、fork Provider、记忆组合、持久化、CLI、HTTP 与 WebUI 均使用真实已发布实现。夹具包含 335 个合成条目、五个分块,共 19,112 个字符。修复前后执行相同的子 Agent 读取尝试,不根据新 Persona 改变模型选择。 + +| 观测项 | 基线 | 修复后 | +|---|---:|---:| +| 完整继承的概览分块 | 5 | 5 | +| 父会话授权读取次数 | 5 | 5 | +| 子 Agent 尝试读取次数 | 1 | 1 | +| 子 Agent 实际读取次数 | 1 | 0 | +| 成功的有界档案搜索 | 1 | 1 | +| 审查完成结果 | skipped | skipped | + +运行 `pnpm exec vitest run tests/review-evidence-host.spec.ts tests/review-tools.spec.ts tests/subagent.spec.ts`。真实 Host 测试覆盖 native 工具、已发布的 worker-thread Code Mode runtime,以及协调器尚未收到运行句柄时就发生的工具调用。向基线 worktree 仅复制新的真实 Host 测试及浏览器夹具,保留原 Host 源码,即可复现失败。 + +浏览器验证先构建 Root 和插件,再运行 `pnpm e2e:serve --review-evidence --strategy-extensions`。选择打印出的临时工作区与 Mnemon E2E preset,依次发送: + +1. `Read the complete synthetic overview once, then review the inherited checkpoint.` +2. `The five overview chunks are complete, covering all 335 synthetic modules. Finish this bounded checkpoint review using the evidence already present. This isolated fixture contains no new durable user preference and no missing overview section.` + +活动评分门槛不变,夹具仅将空闲等待缩短至五秒。通过会话中的子 Agent 菜单打开审查记录。两次浏览器验证均同时启用三个可选 Strategy 增强。 + +![基线子 Agent 重复读取已继承概览](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-211-20260911/before-redundant-read.png) + +![修复后拒绝读取,并通过授权工具完成审查](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-211-20260911/after-read-denied.png) + +## 验证与限制 + +Root 测试通过 935 项,插件测试通过 323 项,共跳过七项 opt-in 测试;定向测试通过 94 项。类型检查、可复现构建、Headless 激活 / 重启 / 禁用、包内容、公开入口与包 lint 通过。首次包检查测得解包大小 1,271,378 字节,超过原 1,270,000 上限;为审计过的 Host guard 将上限调整为 1,275,000 后,包检查通过。 + +通过 `MNEMON_NATIVE_TEST_CLI=/opt/homebrew/bin/mnemon` 启用真实 Native Source 测试,169 项通过、仅跳过一项 Windows 测试。真实 WebUI 在审查后成功新建 Native 空间;CLI 0.2.7 通过 `--no-diff` 写入精确 canary,UI 读取成功,再通过正常“忘记”操作恢复零条内容。 + +![真实 Native CLI 写入内容仍可通过正常 WebUI 操作读取](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-211-20260911/after-native-cli-read.png) + +检查了已发布 DSH 0.1.1-rc.1 和 0.1.2-rc.1 tarball 中的 `isOwnedBy`、作用域工具过滤与 `tools.guard`;实际 Host / 浏览器执行使用 0.1.5-rc.1、macOS arm64、Node 25.1.0 和 pnpm 11.19.0。缺少 guard 能力时审查明确失败。没有修改持久化格式、凭据、RPC authority 或个人记忆。 + +本记录**没有**复现报告者的完整 Windows / AOCI 配置、历史六次读取或约 37K token 消耗,也没有调用在线模型、连接 AOCI 服务或声称测得 token 节省。证据验证的是当前工具组合缺口及执行层修复,不评价模型判断质量。HTTP 夹具与真实 Host 测试同时支持旧的独立结果工具协议,以及稳定结果工具的 request envelope 协议。 diff --git a/dsh/docs/pr-assets/issue-211-20260911/verification.json b/dsh/docs/pr-assets/issue-211-20260911/verification.json new file mode 100644 index 000000000..e286b986b --- /dev/null +++ b/dsh/docs/pr-assets/issue-211-20260911/verification.json @@ -0,0 +1,97 @@ +{ + "date": "2026-09-11", + "baseCommit": "1e19caf40f0bf37edf678c3d829f2398ab28792a", + "node": "25.1.0", + "pnpm": "11.19.0", + "dsh": "0.1.5-rc.1", + "mnemonCli": "0.2.7", + "platform": "macOS arm64", + "sourceSha256": { + "src/host/review-tools.ts": "ae99d95b375b2c9f5b7b8c0cb754806bcfd5fd2470a379921903c2444f8219cf", + "src/host/subagent.ts": "5f038cc53855871293047eee5781a5f7f5c7694206769b85a1003796bd3e67aa", + "src/host/dsh.ts": "893f6083b95eeb92bb07603d395b7762a074c612fed5e79aed1f027c94278f62", + "scripts/fixtures/review-evidence-model.mjs": "35abd2b6011f90db17f7365e8c67096273441a2c8611e89eb6ffe829e2b18748", + "tests/review-evidence-host.spec.ts": "ee9e9021329a53cf02faa5c4b20491c02cbb563b0da52e8346aebc033780a083", + "tests/review-tools.spec.ts": "3102861779248aee95f1241c9cdf3289d311bfa1298382360bd9c9941ef45782" + }, + "web": { + "before": [ + { + "event": "parent-complete", + "overviewReads": 5 + }, + { + "event": "parent-complete", + "overviewReads": 5 + }, + { + "event": "review-start", + "inheritedChunks": 5, + "entries": 335, + "characters": 19112, + "foreignToolAdvertised": true + }, + { + "event": "foreign-attempt-result", + "executed": true, + "denied": false + }, + { + "event": "review-finish", + "action": "skipped", + "boundedDocumentSearch": true + } + ], + "after": [ + { + "event": "parent-complete", + "overviewReads": 5 + }, + { + "event": "parent-complete", + "overviewReads": 5 + }, + { + "event": "review-start", + "inheritedChunks": 5, + "entries": 335, + "characters": 19112, + "foreignToolAdvertised": true + }, + { + "event": "foreign-attempt-result", + "executed": false, + "denied": true + }, + { + "event": "review-finish", + "action": "skipped", + "boundedDocumentSearch": true + } + ] + }, + "unit": { + "targetedPassed": 94, + "rootPassed": 935, + "pluginsPassed": 323, + "optInSkipped": 7 + }, + "native": { + "passed": 169, + "windowsSkipped": 1, + "uiCreatedSpace": true, + "cliWriteEmbedded": false, + "uiReadExactCanary": true, + "uiForgetRemainingItems": 0 + }, + "package": { + "unpackedBytes": 1271378, + "previousCap": 1270000, + "auditedCap": 1275000 + }, + "limits": [ + "No exact historical Windows/AOCI profile reproduction", + "No live model calls or token savings measurement", + "0.1.1-rc.1 and 0.1.2-rc.1 public contract inspection; execution cohort is 0.1.5-rc.1" + ] +} diff --git a/dsh/docs/pr-assets/issue-217-exact-runtime-match/README.md b/dsh/docs/pr-assets/issue-217-exact-runtime-match/README.md new file mode 100644 index 000000000..1e94701f3 --- /dev/null +++ b/dsh/docs/pr-assets/issue-217-exact-runtime-match/README.md @@ -0,0 +1,31 @@ +# Runtime exact entry matching — issue #217 + +[简体中文](./README.zh-CN.md) + +On 2026-09-16, the unmodified main revision `1363ebffaf19c9ab4badf0137f6fe87acacaf989` reproduced the reported collision in the actual DSH WebUI. The fixed revision is `098b8127444c67145e77a4e39f6a6f160280f5ed`. Both ran in separate worktrees and disposable profiles on macOS arm64, Node 25.1.0, pnpm 11.19.0, published DSH 0.1.5-rc.1 and the official checksum-verified Mnemon CLI 0.2.8. The Scoped, Light context and Active capture Strategy plugins were enabled together. + +## Browser reproduction and verification + +Build each revision, then run `MNEMON_CLI_PATH=/path/to/mnemon pnpm e2e:serve --strategy-extensions`. In Chrome, open Memory → Runtime and add two working-memory entries: `X` and `EGO_LINUX_CHROME`. All mutations below were performed through the visible UI, with its ordinary confirmation dialog. + +| Operation | Main | Fixed | +| --- | --- | --- | +| Remove `X` | Reports `Multiple memory entries contain "X"; use a unique substring.`; both entries remain | Removes only `X` | +| Edit `X` to `LINUX` | Reports the same ambiguity and retains `X` | Succeeds and retains the containing entry | +| Remove the resulting `LINUX` | Not reachable after the rejected edit | Succeeds even though `LINUX` is part of `EGO_LINUX_CHROME` | + +The fixed run additionally added a fresh `X` and removed it directly, repeating the original reported operation. Direct inspection of the disposable `memories.json` and `MEMORY.md` confirmed the final content is exactly `EGO_LINUX_CHROME`, with its original creation/update timestamps unchanged. The baseline files still contained both original entries after its rejected operations. + +![Baseline: rejected exact removal](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-217-exact-runtime-match/before-remove.png) + +![Fixed: exact replacement succeeds](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-217-exact-runtime-match/after-edit.png) + +![Fixed: exact removal leaves the containing entry](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-217-exact-runtime-match/after-remove.png) + +## Automated checks and limits + +Before the production edit, five controller regressions and two Source/client regressions failed on the old implementation. Afterward, all 51 Runtime package tests passed. Coverage includes both targets and actions, duplicate exact rejection, substring fallback, branch projection, byte-preserving refusal, capacity maintenance, management confirmation/revision checks, read-only UI and independent Source instances. + +`MNEMON_NATIVE_TEST_CLI=/path/to/mnemon pnpm run verify` passed: all plugin builds/typechecks/tests, 1,128 root tests with five expected opt-in skips, deterministic builds, actual isolated Headless activation, package/export checks and release-intent validation. The real Native integration exercised write, recall and forget; the root suite also ran the actual Native capacity workflow. The Memory Spaces package had one expected opt-in skip. No model API was needed for these direct UI operations; the E2E profile's model endpoint is a deterministic loopback fixture. + +The in-app browser failed to load the DSH client bundle before testing began. Chrome loaded the same unchanged Host normally; no browser or Host security setting was changed. This is macOS/Chrome evidence, not a claim of testing the reporter's Windows/WSL browser environment. No persistence format or migration is introduced by this fix. diff --git a/dsh/docs/pr-assets/issue-217-exact-runtime-match/README.zh-CN.md b/dsh/docs/pr-assets/issue-217-exact-runtime-match/README.zh-CN.md new file mode 100644 index 000000000..2789c6333 --- /dev/null +++ b/dsh/docs/pr-assets/issue-217-exact-runtime-match/README.zh-CN.md @@ -0,0 +1,31 @@ +# Runtime 精确条目匹配 — Issue #217 + +[English](./README.md) + +2026-09-16,在真实 DSH WebUI 中,未修改的 main `1363ebffaf19c9ab4badf0137f6fe87acacaf989` 复现了报告中的匹配冲突,修复版本为 `098b8127444c67145e77a4e39f6a6f160280f5ed`。两者分别使用独立 worktree 和临时 Profile;环境为 macOS arm64、Node 25.1.0、pnpm 11.19.0、正式发布的 DSH 0.1.5-rc.1,以及已核验官方校验和的 Mnemon CLI 0.2.8。Scoped、Light context、Active capture 三个 Strategy 插件同时启用。 + +## 浏览器复现与验证 + +分别构建对应版本,再运行 `MNEMON_CLI_PATH=/path/to/mnemon pnpm e2e:serve --strategy-extensions`。在 Chrome 的「记忆系统 → 运行时」中添加工作记忆 `X` 和 `EGO_LINUX_CHROME`。以下修改均通过可见 UI 操作,并经过正常确认弹窗。 + +| 操作 | main | 修复后 | +| --- | --- | --- | +| 移除 `X` | 提示 `Multiple memory entries contain "X"; use a unique substring.`,两条记录均保留 | 仅移除 `X` | +| 将 `X` 编辑为 `LINUX` | 同样报歧义,原 `X` 保留 | 成功,包含它的另一条记录保持原样 | +| 移除编辑后的 `LINUX` | 编辑失败,未进入此步骤 | 即使它也是 `EGO_LINUX_CHROME` 的子串,仍成功移除 | + +修复后又添加新的 `X` 并直接移除,重复了报告中的原始操作。直接读取临时目录中的 `memories.json` 和 `MEMORY.md`,确认最终内容只有 `EGO_LINUX_CHROME`,其创建和更新时间均未改变。基线在两次失败操作后仍保留两条原始记录。 + +![基线:精确移除被拒绝](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-217-exact-runtime-match/before-remove.png) + +![修复后:精确替换成功](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-217-exact-runtime-match/after-edit.png) + +![修复后:精确移除,包含该子串的另一条记录保留](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-217-exact-runtime-match/after-remove.png) + +## 自动验证与边界 + +修改生产代码前,新增的五个 controller 用例和两个 Source/client 用例在旧实现上失败。修复后 Runtime 包的 51 个测试全部通过,覆盖两种 target 和两种操作、重复精确条目拒绝、子串回退、分支投影、拒绝时文件不变、容量维护、管理操作的确认/修订检查、只读 UI 和 Source 实例隔离。 + +`MNEMON_NATIVE_TEST_CLI=/path/to/mnemon pnpm run verify` 通过:所有插件构建、类型和测试,1,128 个根测试(五个预期的可选测试跳过),确定性构建、真实隔离 Headless 激活、包与入口检查,以及发布意图验证。真实 Native 集成覆盖写入、召回和遗忘,根测试还执行了实际 Native 容量工作流;Memory Spaces 包另有一个预期的可选测试跳过。以上直接 UI 操作不需要模型 API;E2E Profile 的模型端点是确定性回环夹具。 + +内置浏览器在测试开始前未能加载 DSH 客户端 bundle;Chrome 正常加载同一未修改的 Host,未调整浏览器或 Host 安全设置。这份记录对应 macOS/Chrome,不代表已验证报告者的 Windows/WSL 浏览器环境。修复不引入持久化格式变更或数据迁移。 diff --git a/dsh/docs/pr-assets/issue-222-document-archive/README.md b/dsh/docs/pr-assets/issue-222-document-archive/README.md new file mode 100644 index 000000000..1168915b8 --- /dev/null +++ b/dsh/docs/pr-assets/issue-222-document-archive/README.md @@ -0,0 +1,76 @@ +# Documents archive verification — issue #222 + +[Evidence index](../README.md) · [Issue #222](https://github.com/omdsh-dev/dsh-mnemon/issues/222) + +## English + +Verified on 2026-09-10 in an independent worktree based on `main` at `1790251919ec731f9dd49b43da78f78b37890a00`. The full WebUI journey (screenshots 01–12) tested `179589d152b3220ce32f2401de9f757ae421fe06`. Final automated checks and the additional WebUI archive smoke (screenshot 13) tested `90eb1d21eaf3d251937dafe6232a72c2509cc60c`, which also rechecks the live global read-only policy before planning and indexing. + +Environment: macOS 15.6 arm64, Node 25.1.0, pnpm 11.19.0, published DSH 0.1.2-rc.1 Web profile, local dsh-mnemon 0.5.6 build, official npm Mnemon CLI 0.2.8, Chromium 153.0.8010.12 / Playwright 1.63.0, 1440×1050 viewport. Every profile, workspace, memory store and model endpoint was disposable. No live model API or personal memory was used. + +### Reproduction and result + +The legacy build was exercised through the real WebUI, DSH archive task and Native Provider. The scripted worker made an empty recall, actually wrote a cold index, then incorrectly referenced receipt 1 as its destination. The Host returned the exact reported error, `migration lineage destination does not match its recall receipt`. The document stayed active while one new index remained searchable. This reproduces the reported failure mode; the reporter's private model trace was not available to establish its exact sequence. + +The fixed worker has only its terminal result tool. It proposes a summary and destination; the Host validates that plan before any index write, appends the exact cold path and hash, obtains the durable receipt, builds lineage, then commits the document archive. Invalid plans cannot write indexes. Exact existing references can be reused. On a later document commit failure, compensation targets only the newly created index; it preserves existing or ambiguous entries and reports an unsuccessful cleanup with the destination/id. + +### Automated checks + +| Command / boundary | Result | +|---|---| +| Targeted regressions against the legacy Host | 7 expected failures, 7 passes in the selected subset | +| Live read-only policy regressions before the final guard | 2 expected failures; both pass with the guard | +| `pnpm exec vitest run tests/subagent.spec.ts` | 85 passed, including 25 added cases | +| `pnpm verify` | 1,199 passed executions: 876 Root + 323 plugins; 7 opt-in skips; types, deterministic builds, real Headless activation and package validation passed | +| `pnpm verify:plugins` | 16 independent plugin repositories, 17 packed artifacts and external SDK/Client/real DSH composition passed | +| `MNEMON_NATIVE_TEST_CLI=... pnpm --filter dsh-mnemon-source-memory-spaces exec vitest run tests/native-integration.spec.ts` | 1 passed; real isolated Native write, recall and forget | +| `pnpm release:intent`, `pnpm verify:docs`, `git diff --check` | Passed | + +The regression suite covers live global read-only changes before and during planning, malformed/failed plans, invalid or changed destinations, immutable View namespace grants, LRU capacity archival, revision conflicts before and after indexing, caller cancellation, deduplication readback, existing-index reuse, wrong-space/near-match recall, asynchronous or ambiguous receipts, failed cleanup, and preserving an index after the document has already committed. A composed Documents + Holographic test performs real persistence and verifies that compensation removes the new index while retaining the original document and an earlier index. The seven ordinary-suite skips are optional live-model/Native/Windows cases; the portable Native integration was run separately above. + +### WebUI evidence + +| Case | Observed result / screenshot | +|---|---| +| Legacy failure | [Exact error, active original](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-222-document-archive/01-before-archive-error.png); [one orphan index](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-222-document-archive/02-before-orphan-index.png) | +| Successful archive | [Archived original and receipt](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-222-document-archive/03-after-archived-original.png); [exact cold index](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-222-document-archive/04-after-cold-index.png) | +| Discoverability | [Real keyword recall returns the index](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-222-document-archive/05-after-keyword-recall.png) | +| Revision-2 invalid proposal | [Rejected, document remains active](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-222-document-archive/06-after-rejected-plan.png); [index count remains one](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-222-document-archive/07-after-rejected-no-index.png) | +| Corrected retry | [Rename and retry succeeds; two archives](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-222-document-archive/08-after-retry-success.png); exactly two indexes after the retry | +| No eligible destination | [Inactive space rejected before model work or indexing](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-222-document-archive/09-after-inactive-rejected.png) | +| Restart | [Two archived originals and one active rejected document survive](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-222-document-archive/10-after-restart.png); inactive space state also persists | +| Final build after reactivation | [Previously rejected document archives at revision 2](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-222-document-archive/13-after-final-build-archive.png); exactly three indexes and three archived originals in that profile | +| Actual named tools | [Three conversation turns: create, update, archive](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-222-document-archive/11-after-tool-roundtrip.png); [revision-2 content archived at revision 3](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-222-document-archive/12-after-tool-archived-original.png) | + +Dialog cancellation preserved the active document. All four final document bodies were read from disk and matched their stored SHA-256. After the final smoke all four were archived, across two disposable profiles. The named-tool case asserted the actual returned lineage's source revision/digest and Provider destination. See [minimal structured evidence](./verification.json) for exact synthetic ids, hashes and selected model-fixture events. All thirteen screenshots were visually reviewed. + +### Repeat locally + +```sh +pnpm install --frozen-lockfile +pnpm verify +pnpm verify:plugins +MNEMON_CLI_PATH=/absolute/path/to/mnemon pnpm e2e:serve --document-archive +``` + +Choose the printed disposable workspace and the Mnemon E2E preset. Create and activate a Native Memory Space, then create/archive a document in Memory System. A title containing `REJECT` deliberately proposes an invalid destination; check the active original and index count, rename it, and retry. Deactivate the destination to test preflight refusal. Send `archive-tool-222 prepare`, `archive-tool-222 update`, and `archive-tool-222` in three separate conversation turns for the named-tool path. The fixture scripts only model decisions; all tools and storage operations remain real. Stop with Ctrl-C to remove the temporary data. + +### Limits and compatibility + +Windows and the reporter's original desktop installation were not available. This does not measure live LLM summary quality or certify every remote Provider. Archive now requires an existing active destination with exact writes and safe forget; asynchronous extraction and non-reversible destinations are rejected before writing. There is no storage-format migration. Native compensation uses its normal soft-delete semantics, so tombstones/audit history can remain. Provider timeouts, process crashes, unidentifiable receipts or failed cleanup cannot provide a distributed transaction guarantee; the implementation fails closed and avoids deleting pre-existing or uncertain data. Older orphan indexes are not bulk-deleted on upgrade. + +## 中文 + +2026-09-10,从 `main` 的 `1790251` 创建独立 worktree,完整 WebUI 流程(截图 01–12)实测提交为 `179589d`;最终自动校验和追加 WebUI 归档复验(截图 13)实测 `90eb1d2`,后者增加规划前及索引写入前的实时全局只读检查。环境为 macOS 15.6 arm64、Node 25.1.0、pnpm 11.19.0、正式 DSH 0.1.2-rc.1 Web profile、本地 dsh-mnemon 0.5.6 构建、官方 npm Mnemon CLI 0.2.8,以及 Chromium 153 / Playwright 1.63。所有工作区、记忆和模型端点均为临时夹具,没有调用真实模型服务或读取私人记忆。 + +旧构建通过真实 WebUI、归档任务和 Native Provider 复现了报告中的完整现象:脚本子代理先空召回、再实际写入冷索引,随后错误引用第 1 份回执;Host 抛出同样的 lineage 匹配错误,档案仍为 active,空间却多出一条索引。用户原始模型轨迹不可得,因此这证明的是同类失败路径,而不是声称还原了用户模型的具体思考过程。 + +修复后,子代理只拟定摘要和目标空间,Host 在写入前校验方案,补齐精确路径与哈希,根据自己的持久回执生成 lineage,再移动原文。无效方案没有索引副作用;旧的精确索引可复用;文档提交失败时仅补偿本次新建的索引,不删除已有或结果不确定的数据。 + +`pnpm verify` 共通过 1,199 次测试执行(Root 876、插件 323),另有 7 项 opt-in 跳过;类型、确定性构建、真实 Headless 激活及包校验全部通过。独立验证通过 16 个插件仓库、17 个制品和外部组合;真实 Native 写入、召回、删除冒烟另行通过。子代理套件共 85 项,新增 25 项;追加的两项实时只读回归在补充检查前均失败、修复后均通过。套件覆盖实时只读变化、无效方案、修订冲突、取消、重复索引、清理失败、已提交状态保护、受限 View 与 LRU 归档;真实 Documents + Holographic 组合验证了补偿清理和原文保留。 + +上表保留了 13 张实测截图:旧错误与孤儿索引、正常归档和召回、revision 2 的拒绝路径与索引数量不变、修正后重试、停用目标后的提前拒绝、重启持久化,以及三个真实用户回合中的 `mnemon_document_manage` 新建、更新和归档。最终构建重启后重新启用目标空间,原先被拒绝的文档成功归档,该 Profile 恰好有三个归档原文和三条索引。两个临时 Profile 的四个文档最终均已归档,磁盘正文 SHA-256 与记录的内容哈希均一致。真实工具回执包含指向 revision 2 的 source lineage,归档后文档 revision 为 3。精简事件、id 和哈希见 [verification.json](./verification.json)。 + +复跑命令和测试指令见上方;`REJECT` 标题用于拒绝路径,`archive-tool-222 prepare`、`archive-tool-222 update`、`archive-tool-222` 分别触发三个真实工具回合。夹具只控制模型决策,不模拟工具或存储。停止服务会清理临时数据。 + +未实测 Windows 原环境、真实 LLM 摘要质量或所有远程 Provider。归档要求已有、已启用且支持精确写入和安全删除的目标空间;异步或不可补偿目标在写入前拒绝。持久化格式未变,升级不会批量删除旧孤儿索引。Native 清理沿用软删除语义,可能保留墓碑和审计历史;超时、崩溃、无法识别的回执及清理失败不具备分布式事务保证,需根据返回的目标信息核对状态。 diff --git a/dsh/docs/pr-assets/issue-222-document-archive/verification.json b/dsh/docs/pr-assets/issue-222-document-archive/verification.json new file mode 100644 index 000000000..e83736701 --- /dev/null +++ b/dsh/docs/pr-assets/issue-222-document-archive/verification.json @@ -0,0 +1,135 @@ +{ + "date": "2026-09-10", + "baseRevision": "1790251919ec731f9dd49b43da78f78b37890a00", + "testedRevision": "90eb1d21eaf3d251937dafe6232a72c2509cc60c", + "checks": { + "verify": { + "passed": 1199, + "rootPassed": 876, + "pluginPassed": 323, + "optInSkipped": 7 + }, + "independentPluginRepositories": 16, + "packedArtifacts": 17, + "nativeIntegrationPassed": 1, + "subagentTestsPassed": 85, + "liveReadOnlyRegressions": { + "failedBeforeGuard": 2, + "passedAfterGuard": 2 + }, + "screenshotsReviewed": 13 + }, + "browser": { + "viewport": "1440x1050", + "model": "loopback scripted decisions; no live model API", + "chromium": "153.0.8010.12", + "playwright": "1.63.0" + }, + "events": { + "before": [ + { + "event": "legacy-lineage-mismatch", + "destinationId": "91efb069-2418-4d61-9f72-ab77cd28e0e8", + "memoryBodyId": "default", + "indexed": true + } + ], + "after": [ + { + "event": "archive-plan", + "title": "Issue 222 Release Design", + "dataPlaneTools": 0 + }, + { + "event": "archive-plan", + "title": "Issue 222 REJECT Proposal", + "dataPlaneTools": 0 + }, + { + "event": "archive-plan", + "title": "Issue 222 Retry Accepted", + "dataPlaneTools": 0 + }, + { + "event": "archive-plan", + "title": "Issue 222 Inactive Destination", + "dataPlaneTools": 0, + "testedRevision": "90eb1d21eaf3d251937dafe6232a72c2509cc60c" + } + ], + "tool": [ + { + "event": "archive-plan", + "title": "Issue 222 Tool Roundtrip", + "dataPlaneTools": 0 + }, + { + "event": "tool-archive-completed", + "revision": 3, + "lineage": [ + { + "source": { + "layerId": "documents", + "reference": "document:74a12f52-33de-460a-9745-82cde7e6e2ea:2", + "digest": "aab7cb2607dd7befbfa6f64859d2f3c328ec97daa565d4300822b423ec52d8d9" + }, + "destination": { + "layerId": "memory-spaces", + "reference": "memory-space:default/item:6095c40f-d8db-4c7b-b38e-fd9b2ec0baf0", + "digest": "d1d784d522b427003f8e6c2ee0eb979121c87dbc50db8d14514601230d8d7618" + } + } + ] + } + ] + }, + "persistedDocuments": [ + { + "id": "08cb440a-3b1b-4aff-a2d0-025b38f2b120", + "title": "Issue 222 Release Design", + "status": "archived", + "revision": 2, + "contentHash": "f3afa39a97e0ab992b6585cadfdcbafefab347f52dbac92d5559b6a9d5f6865a", + "exactBodyHash": true + }, + { + "id": "9e63c709-34c9-40ac-972c-a173bcbcfd3b", + "title": "Issue 222 Retry Accepted", + "status": "archived", + "revision": 4, + "contentHash": "ee8adac042f49cece862f09119040a81c814c0ea713486faa25b219ac5abe26a", + "exactBodyHash": true + }, + { + "id": "33ee0f61-eba1-4b74-80b0-48577665e95d", + "title": "Issue 222 Inactive Destination", + "status": "archived", + "revision": 2, + "contentHash": "754fe448c2659bfeafd3f1feb75bc68e1c37d86fbaaca2eac5fb2ef213e1a4f6", + "exactBodyHash": true + }, + { + "id": "74a12f52-33de-460a-9745-82cde7e6e2ea", + "title": "Issue 222 Tool Roundtrip", + "status": "archived", + "revision": 3, + "contentHash": "aab7cb2607dd7befbfa6f64859d2f3c328ec97daa565d4300822b423ec52d8d9", + "exactBodyHash": true + } + ], + "environment": { + "node": "v25.1.0", + "pnpm": "11.19.0", + "os": "macOS 15.6", + "arch": "arm64", + "dsh": "0.1.2-rc.1", + "mnemon": "0.2.8" + }, + "webuiInitialRevision": "179589d152b3220ce32f2401de9f757ae421fe06", + "webuiFinalSmokeRevision": "90eb1d21eaf3d251937dafe6232a72c2509cc60c", + "finalSmoke": { + "destinationReactivated": true, + "primaryProfileArchivedDocuments": 3, + "primaryProfileMemoryIndexes": 3 + } +} diff --git a/dsh/docs/pr-assets/issue-223-dsh-015/README.md b/dsh/docs/pr-assets/issue-223-dsh-015/README.md new file mode 100644 index 000000000..5b303f35a --- /dev/null +++ b/dsh/docs/pr-assets/issue-223-dsh-015/README.md @@ -0,0 +1,63 @@ +# DSH 0.1.5 compatibility — issue 223 + +[简体中文](./README.zh-CN.md) | [Issue 223](https://github.com/omdsh-dev/dsh-mnemon/issues/223) + +Verification record for the isolated `codex/fix-223-dsh-015` worktree based on main `1790251919ec731f9dd49b43da78f78b37890a00`. The tested DSH release is the published npm `latest`, `0.1.5-rc.1`; no DSH source is modified. + +The main build reproduced HTTP 405 in the real WebUI. With the Starter connection dependency patch, all seven Mnemon RPC channels register and Status loads. Lifecycle messages omit invalid summary metadata. A synthetic conversation emitted by the published 0.1.2 Agent loop reproduces the official migration refusal; the repair command preserves the original, then a deliberately installed copy migrates to v3 and cold-reopens under the published 0.1.5 persistence backend, for JSONL and Zstandard. + +The reusable fixture is [issue-223-legacy-v0.jsonl](../../../tests/fixtures/issue-223-legacy-v0.jsonl). It contains only a synthetic user request, the two historical Mnemon pre-step messages and a fixed assistant response. [Regression tests](../../../tests/legacy-session-repair.spec.ts) check byte preservation, exclusive output publication, unrelated metadata, malformed input, frame truncation, bounds, and official migration. See [recovery instructions](../../en/guides/operations.md#dsh-015-compatibility-and-legacy-session-recovery). + + +## Result + +Command entrypoint follow-up: `a389438a4e12a77f953c80ce79828e221653ee8f`. The final full local verification passes at that revision, including a regression that invokes the npm-style executable symlink for help and repair preview. The WebUI captures below cover the unchanged Runtime/UI implementation. + +Implementation: `595e197670574f6a731e445b896dbc5c78271027`; base: `1790251919ec731f9dd49b43da78f78b37890a00`. Captured on 2026-09-10 with Node 25.1.0, pnpm 11.19.0, Playwright 1.63.0 / Chromium 153, macOS arm64, and Mnemon Native 0.2.8. The UI shows package version 0.5.6 because the changeset has not yet performed the release version bump. [Machine-readable results and screenshot hashes](./verification.json). + +| Check | Result | +|---|---| +| Frozen install and `pnpm verify` | Passed: 1194 tests, 7 opt-in skips; types, deterministic build, docs, Headless, strict package checks | +| `pnpm verify:plugins` | Passed: 16 standalone plugin repositories, 17 packed artifacts, external SDK/Client consumer, real DSH packed upgrade and optional Strategy composition | +| Real Native integration | Passed: 1 opt-in test, disposable write/recall/delete | +| Repair regression | Passed: 17 tests including real v0 → v3 loaders, both encodings, cold reopen, byte preservation, exclusive output, checksum/truncation and rejection paths | +| Node 20 repair preview | Passed on the same v0 JSONL fixture; 2 repairs, original preserved | +| DSH 0.1.2-rc.1 Headless | Passed with the current plugin: 39 tools / 8 representative Mnemon tools, settings migration, restart, disable whole Starter | +| DSH 0.1.5-rc.1 Headless | Passed: 38 tools / 8 representative Mnemon tools, the same restart and disable checks | +| Browser authorization | Cookie-authenticated pages work; unauthenticated status RPC returns 401; read-only Runtime and Save-to-memory controls reject writes | + +The real WebUI sequence used only disposable data: + +1. Open Memory System without a session, then Settings: the main build fails with HTTP 405; the fixed build loads Status, the Source catalog and Provider settings. +2. Create USER and MEMORY entries, edit Working Memory, filter it, and verify the exact text. Create a disposable entry in Builtin, cancel removal, then remove it after restoring write access; the original two entries remain. +3. Select the disposable workspace and create a real DSH conversation through the Mnemon E2E preset. Create and search a Document, read its body, then read it again after restart. +4. Create a Native Memory Space through the UI. Seed one synthetic fact with the real Native CLI, activate the space in the UI, run keyword Recall, inspect Entities and Content, restart and read the same fact. Cancel Forget, then confirm soft deletion and verify Content is empty. This is a CLI-seeded read/delete check, not an AI-distillation quality claim. +5. Save Sidebar → Builtin and Builtin → Sidebar through Settings; verify exactly one entry is mounted and both display the same data. Check English and Chinese settings. Export a backup ZIP through the real pack RPC. +6. Restart with `writeEnabled: false`: Runtime has no mutation controls and the conversation Save-to-memory confirmation stays disabled. Restore write access only in the disposable fixture. +7. Use the copy-repair command on a compressed v0 Session, install that copy in the disposable Session root, load its original user/assistant history, send a second turn, restart DSH, and cold-open both turns. The final log contains 30 events, 6 user messages, and 3 valid Mnemon context messages without summary. The original backup SHA-256 is unchanged. + +For the WebUI legacy scenario only, the generated fixture header was assigned the disposable workspace and `mnemon-e2e` preset, and the synthetic model route was mapped to the loopback provider. Those are fixture setup changes; the repair command only removes the two known summary properties. DSH itself publishes and validates the v3 successor. + +## Screenshots + +| Evidence | Image | +|---|---| +| Main reproduces both HTTP 405 errors | [Before](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-223-dsh-015/00-before-http-405.png) | +| No-session Status connects | [Status](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-223-dsh-015/01-status-connected.png) | +| Runtime create/edit result | [Runtime](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-223-dsh-015/02-runtime-crud.png) | +| Document creation and search | [Documents](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-223-dsh-015/03-documents-search.png) | +| Native keyword Recall | [Recall](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-223-dsh-015/04-native-recall.png) | +| Settings save and connected catalog | [Settings](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-223-dsh-015/05-settings-connected.png) | +| Builtin uses the same Runtime data | [Builtin](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-223-dsh-015/06-builtin-runtime.png) | +| Read-only mode after restart | [Read-only](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-223-dsh-015/07-readonly-restart.png) | +| Chinese Settings | [中文](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-223-dsh-015/10-chinese-settings.png) | +| Repaired legacy conversation after resume and restart | [Cold reopen](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-223-dsh-015/11-legacy-cold-reopen.png) | +| Native content retained through restart, before soft deletion | [Content](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-223-dsh-015/12-content-after-restart.png) | + +## Reproduction and limits + +Run `pnpm install --frozen-lockfile`, `pnpm verify`, and `pnpm verify:plugins`. For the browser, build first, then run `MNEMON_CLI_PATH=/absolute/path/to/mnemon pnpm e2e:serve`; select the printed disposable workspace and Mnemon E2E. The fixture uses the current persona `prefix` and disables Open-in-App together with its disabled subprocess dependency. Use the checked-in legacy fixture with `node bin/repair-legacy-session.mjs --input tests/fixtures/issue-223-legacy-v0.jsonl` for a non-writing preview. The real loader regression creates and cleans its own storage roots. + +This verifies published DSH APIs and desktop WebUI behavior. It does not retest Windows, Electron, physical mobile devices, live third-party accounts, model quality, or every historical DSH release. DSH 0.1.2 received Headless regression only. Older peer ranges remain, but historical reports keep their own revisions. The copy utility deliberately refuses unrelated corruption and does not overwrite live Sessions. DSH v3 cannot be treated as an old-version rollback artifact. + +Sources: [official DSH 0.1.5-rc.1 release](https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.1.5-rc.1), [Node Zstandard APIs](https://nodejs.org/api/zlib.html#zlibzstddecompresssyncbuffer-options), [Zstandard framing, RFC 8878](https://www.rfc-editor.org/rfc/rfc8878.html#section-3.1.1). CI results belong to the final PR revision and are linked from the PR, separately from this local capture. diff --git a/dsh/docs/pr-assets/issue-223-dsh-015/README.zh-CN.md b/dsh/docs/pr-assets/issue-223-dsh-015/README.zh-CN.md new file mode 100644 index 000000000..cd1d5fce4 --- /dev/null +++ b/dsh/docs/pr-assets/issue-223-dsh-015/README.zh-CN.md @@ -0,0 +1,63 @@ +# DSH 0.1.5 兼容 — issue 223 + +[English](./README.md) | [Issue 223](https://github.com/omdsh-dev/dsh-mnemon/issues/223) + +本记录来自独立 `codex/fix-223-dsh-015` worktree,基于 main `1790251919ec731f9dd49b43da78f78b37890a00`。测试目标为 npm `latest` 发布的 DSH `0.1.5-rc.1`,未修改 DSH 官方源码。 + +使用 main 制品在真实 WebUI 复现 HTTP 405。Starter 连接依赖补丁恢复七个 Mnemon RPC 通道,状态页正常加载。生命周期消息移除不合法的 summary。通过已发布的 0.1.2 Agent loop 生成的合成会话复现官方迁移拒绝;修复命令保留原件,明确安装副本后,已发布的 0.1.5 持久化后端可将其升级到 v3 并冷启动重读,JSONL 和 Zstandard 均覆盖。 + +可复用夹具为 [issue-223-legacy-v0.jsonl](../../../tests/fixtures/issue-223-legacy-v0.jsonl),仅包含合成用户请求、两类历史 Mnemon pre-step 消息及固定回复。[回归测试](../../../tests/legacy-session-repair.spec.ts)检查逐字节保留、拒绝覆盖、其他元数据、损坏输入、帧截断、大小上限与官方迁移。操作方法见[恢复流程](../../zh-CN/guides/operations.md#dsh-015-兼容与旧会话恢复)。 + + +## 结果 + +命令入口补充修复 revision:`a389438a4e12a77f953c80ce79828e221653ee8f`。最终完整本地验证在此 revision 通过,额外回归验证 npm 安装生成的可执行符号链接能显示帮助并预览修复。下列 WebUI 截图对应此前未改变的 Runtime/UI 实现。 + +实现 revision:`595e197670574f6a731e445b896dbc5c78271027`;基线:`1790251919ec731f9dd49b43da78f78b37890a00`。采集日期为 2026-09-10,使用 macOS arm64、Node 25.1.0、pnpm 11.19.0、Playwright 1.63.0 / Chromium 153,以及 Mnemon Native 0.2.8。截图显示包版本 0.5.6,因为 changeset 尚未执行发布升版。[机器可读结果与截图哈希](./verification.json)。 + +| 检查 | 结果 | +|---|---| +| 冻结安装与 `pnpm verify` | 通过:1194 项测试、7 项 opt-in 跳过;类型、确定性构建、文档、Headless、严格包检查全部通过 | +| `pnpm verify:plugins` | 通过:16 个独立插件仓库、17 个 tarball、外部 SDK/Client 消费者、真实 DSH 打包升级与可选 Strategy 组合 | +| 真实 Native 集成 | 1 项 opt-in 测试通过,覆盖临时数据写入、召回、删除 | +| 修复工具回归 | 17 项通过,覆盖真实 v0 → v3 加载、两种编码、冷启动重读、字节保留、拒绝覆盖、校验和/截断与拒绝路径 | +| Node 20 修复预览 | 同一 v0 JSONL 夹具通过:预览 2 项修复,原件保留 | +| DSH 0.1.2-rc.1 Headless | 当前插件通过:39 个工具中核对 8 个代表性 Mnemon 工具,以及设置迁移、重启、整体停用 Starter | +| DSH 0.1.5-rc.1 Headless | 通过:38 个工具中核对相同 8 个代表性工具,以及相同的重启、停用检查 | +| 浏览器授权 | Cookie 认证页面正常;未认证状态 RPC 返回 401;只读 Runtime 和“存入记忆”入口禁止写入 | + +真实 WebUI 全程使用一次性数据,依次验证: + +1. 无会话打开记忆系统及设置:main 制品复现 HTTP 405;修复制品正常加载状态、Source 目录与 Provider 配置。 +2. 新增 USER 和 MEMORY 条目,编辑、筛选并核对原文;在 Builtin 新增一次性条目,取消删除,恢复写权限后实际删除,原有两条保留。 +3. 选择临时工作区,以 Mnemon E2E 预设创建真实 DSH 会话;创建档案、搜索并读取正文,重启后再次读取。 +4. 在 UI 创建 Native 记忆空间,用真实 Native CLI 写入一条合成事实;在 UI 激活、关键词召回、查看实体和内容,重启后重读。取消遗忘后核对仍在,再确认软删除,内容页变为空。这是 CLI 预置数据的读取/删除检查,不是 AI 沉淀质量评测。 +5. 在设置中保存 Sidebar → Builtin → Sidebar,确认始终只有一个入口且数据相同;检查中英文设置,通过真实 pack RPC 导出备份 ZIP。 +6. 以 `writeEnabled: false` 重启:Runtime 不显示修改入口,会话“存入记忆”确认按钮保持禁用。只在一次性夹具内恢复写权限。 +7. 对压缩 v0 会话运行副本修复命令,在临时会话根安装副本;打开原有用户/助手历史,发送第二轮,重启 DSH 后再次读取两轮内容。最终日志有 30 个事件、6 条 user/message,其中 3 条 Mnemon 上下文消息均不含 summary;原备份 SHA-256 未改变。 + +WebUI 旧会话夹具额外设置了临时工作区和 `mnemon-e2e` 预设,并将合成模型路由映射到 loopback Provider。这些属于夹具准备;修复命令仍只删除两个已知 summary 属性。v3 后继文件由 DSH 自己发布与校验。 + +## 截图 + +| 证据 | 图片 | +|---|---| +| main 复现两个 HTTP 405 | [修复前](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-223-dsh-015/00-before-http-405.png) | +| 无会话状态页正常连接 | [状态](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-223-dsh-015/01-status-connected.png) | +| Runtime 新增、编辑结果 | [Runtime](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-223-dsh-015/02-runtime-crud.png) | +| 档案创建与搜索 | [档案](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-223-dsh-015/03-documents-search.png) | +| Native 关键词召回 | [召回](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-223-dsh-015/04-native-recall.png) | +| 设置保存及目录正常连接 | [设置](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-223-dsh-015/05-settings-connected.png) | +| Builtin 使用同一份 Runtime | [Builtin](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-223-dsh-015/06-builtin-runtime.png) | +| 重启后的只读模式 | [只读](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-223-dsh-015/07-readonly-restart.png) | +| 中文设置 | [中文](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-223-dsh-015/10-chinese-settings.png) | +| 旧会话续写后重启重读 | [冷启动重读](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-223-dsh-015/11-legacy-cold-reopen.png) | +| Native 内容重启后仍在,随后完成软删除 | [内容](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-223-dsh-015/12-content-after-restart.png) | + +## 复现与边界 + +执行 `pnpm install --frozen-lockfile`、`pnpm verify` 和 `pnpm verify:plugins`。浏览器检查先构建,再运行 `MNEMON_CLI_PATH=/absolute/path/to/mnemon pnpm e2e:serve`,选择其打印的临时工作区及 Mnemon E2E。夹具使用新版 persona `prefix`,并随已停用的 subprocess 一起停用 Open-in-App。用 `node bin/repair-legacy-session.mjs --input tests/fixtures/issue-223-legacy-v0.jsonl` 可对仓库夹具执行不写入的预览。真实加载器回归自行创建并清理存储根。 + +本次验证正式发布的 DSH API 与桌面 WebUI,未重测 Windows、Electron、真实手机、三方真实账号、模型质量或所有旧版 DSH。DSH 0.1.2 本次仅做 Headless 回归;保留旧 peer 范围不代表历史记录对应代码变为本次验证。副本工具明确拒绝其他损坏,不覆盖正在使用的会话;DSH v3 不能直接用于回滚旧版。 + +来源:[DSH 0.1.5-rc.1 官方发布](https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.1.5-rc.1)、[Node Zstandard API](https://nodejs.org/api/zlib.html#zlibzstddecompresssyncbuffer-options)、[RFC 8878 帧规范](https://www.rfc-editor.org/rfc/rfc8878.html#section-3.1.1)。最终 CI 结果在 PR 中关联到最终 revision,与本次本地截图分别记录。 diff --git a/dsh/docs/pr-assets/issue-223-dsh-015/verification.json b/dsh/docs/pr-assets/issue-223-dsh-015/verification.json new file mode 100644 index 000000000..43f3df193 --- /dev/null +++ b/dsh/docs/pr-assets/issue-223-dsh-015/verification.json @@ -0,0 +1,169 @@ +{ + "date": "2026-09-10", + "sourceRevision": "595e197670574f6a731e445b896dbc5c78271027", + "baseRevision": "1790251919ec731f9dd49b43da78f78b37890a00", + "dsh": "0.1.5-rc.1", + "dshNpmLatestRechecked": true, + "node": "25.1.0", + "pnpm": "11.19.0", + "playwright": "1.63.0", + "chromium": "153", + "platform": "macOS arm64", + "nativeCli": "0.2.8", + "tests": { + "passed": 1194, + "skipped": 7, + "rootPassed": 871, + "pluginPassed": 323, + "repairPassed": 17, + "nativeOptInPassed": 1, + "independentPluginRepositories": 16, + "packedArtifacts": 17, + "npmExecutableSymlinkPassed": true + }, + "headless": { + "current": { + "version": "0.1.5-rc.1", + "totalTools": 38, + "representativeMnemonTools": 8 + }, + "previous": { + "version": "0.1.2-rc.1", + "totalTools": 39, + "representativeMnemonTools": 8 + } + }, + "browser": { + "status": "passed", + "runtimeCreateEditFilterDelete": true, + "documentCreateSearchReadAndRestart": true, + "nativeSpaceCreatedAndActivatedInUi": true, + "nativeFactSeededBy": "real CLI; no LLM", + "nativeRecallContentEntitiesRestartAndSoftDelete": true, + "sidebarBuiltinSwitchBothDirections": true, + "englishChineseSettings": true, + "readOnlyMutationControlsDisabled": true, + "unauthenticatedStatus": 401, + "backupExport": { + "bytes": 5369, + "sha256": "21696dc1f68aa0048a612dc387b27cc2f94c7146e36e2a7589902115ea0270e0" + }, + "coldReopenPageErrors": [] + }, + "legacyRepair": { + "mode": "copy", + "format": 0, + "encoding": "zstd", + "repairedMessages": 2, + "inputSha256": "09da22890e09eb0ddde51bfd4c87ab878d30fe35a1231148320a286f996e7102", + "outputSha256": "7222c6379e7c28296bb93d636c163169130293c5bdc46745588796349bded511", + "originalPreserved": true + }, + "legacyFinal": { + "migratedV3": true, + "coldRead": true, + "totalEvents": 30, + "userMessages": 6, + "validMnemonContextMessages": 3, + "originalBackupSha256": "09da22890e09eb0ddde51bfd4c87ab878d30fe35a1231148320a286f996e7102", + "originalBackupPreserved": true + }, + "screenshots": [ + { + "path": "00-before-http-405.png", + "bytes": 94134, + "width": 1440, + "height": 1050, + "sha256": "b5237d132a43bfb7b0b57ae9377e419e37fe0a0721015dfbe6cc2af92fe2203a", + "sourceRevision": "1790251919ec731f9dd49b43da78f78b37890a00" + }, + { + "path": "01-status-connected.png", + "bytes": 135775, + "width": 1440, + "height": 1050, + "sha256": "ad81cc354c8189b9d08f00e9c1b0a5d204b8eccd7acfc824df70d1282b346bc6", + "sourceRevision": "595e197670574f6a731e445b896dbc5c78271027" + }, + { + "path": "02-runtime-crud.png", + "bytes": 168690, + "width": 1440, + "height": 1050, + "sha256": "c5ce922ee0944efed6a10091bf3b816bda45a94c59336428bcefb9ad28b3257c", + "sourceRevision": "595e197670574f6a731e445b896dbc5c78271027" + }, + { + "path": "03-documents-search.png", + "bytes": 142984, + "width": 1440, + "height": 1050, + "sha256": "aa6dd99cfa5ccf89a6aa67e1d871e09ee486ba3254c4258df25902d3fd90cafc", + "sourceRevision": "595e197670574f6a731e445b896dbc5c78271027" + }, + { + "path": "04-native-recall.png", + "bytes": 127348, + "width": 1440, + "height": 1050, + "sha256": "df9d09e0cb41fa22f27ef39b305849f28ddcffcaa159940808113999bd1c8f6c", + "sourceRevision": "595e197670574f6a731e445b896dbc5c78271027" + }, + { + "path": "05-settings-connected.png", + "bytes": 180477, + "width": 1440, + "height": 1050, + "sha256": "9b323d36a34940afda04754652e949af29ab72ac4b83f4c0ec87dafbe2bf1a46", + "sourceRevision": "595e197670574f6a731e445b896dbc5c78271027" + }, + { + "path": "06-builtin-runtime.png", + "bytes": 187648, + "width": 1440, + "height": 1050, + "sha256": "8e162983e877352cc4c0d9c27718c0488dbf0453c403a43d2a639d09b4385d04", + "sourceRevision": "595e197670574f6a731e445b896dbc5c78271027" + }, + { + "path": "07-readonly-restart.png", + "bytes": 205441, + "width": 1440, + "height": 1050, + "sha256": "87fc47d985056ab286ddd591c325125c0dd9b5767392063144c8bef9f03f8a5e", + "sourceRevision": "595e197670574f6a731e445b896dbc5c78271027" + }, + { + "path": "10-chinese-settings.png", + "bytes": 195658, + "width": 1440, + "height": 1050, + "sha256": "e8a11e10499dd8b5e51c0ee5f697856b8daa0d2529e17e0ccea68184028a8705", + "sourceRevision": "595e197670574f6a731e445b896dbc5c78271027" + }, + { + "path": "11-legacy-cold-reopen.png", + "bytes": 87157, + "width": 1440, + "height": 1050, + "sha256": "80cbb93829da50544fe76de6187d91b246acf7b499c251dba8f02dd265445ce0", + "sourceRevision": "595e197670574f6a731e445b896dbc5c78271027" + }, + { + "path": "12-content-after-restart.png", + "bytes": 134565, + "width": 1440, + "height": 1050, + "sha256": "8c6e54ae74d0df3a591a6419e7e71177b4591123a6503390fb587181e1f66769", + "sourceRevision": "595e197670574f6a731e445b896dbc5c78271027" + } + ], + "limits": [ + "No Windows, Electron or physical-mobile retest", + "Loopback fixed model responses; no model-quality claim", + "No live third-party Provider accounts", + "DSH 0.1.2 Headless only; older releases retain historical evidence" + ], + "cliEntrypointRevision": "a389438a4e12a77f953c80ce79828e221653ee8f", + "finalLocalVerificationRevision": "a389438a4e12a77f953c80ce79828e221653ee8f" +} diff --git a/dsh/docs/pr-assets/issue-232-orca-sidebar/README.md b/dsh/docs/pr-assets/issue-232-orca-sidebar/README.md new file mode 100644 index 000000000..cc05eefce --- /dev/null +++ b/dsh/docs/pr-assets/issue-232-orca-sidebar/README.md @@ -0,0 +1,43 @@ +# Issue #232: ORCA LINK sidebar entry + +[Issue](https://github.com/omdsh-dev/dsh-mnemon/issues/232) · [Validation data and hashes](./validation.json) + +On 2026-09-11, a real DSH 0.1.5-rc.1 WebUI reproduced the invisible Mnemon launcher on main `1e19caf`. The ORCA LINK skin treated the unmarked button as New Session: its text color became transparent and both child spans had opacity 0. Adding the shared `data-dsh-part="sidebar-entry"` and `data-dsh-plugin="dsh-mnemon"` attributes restored the icon, label and skin's z-index 3. No skin-specific CSS override is needed. + +2026-09-11 在真实 DSH 0.1.5-rc.1 WebUI 中复现:main `1e19caf` 的入口被 ORCA LINK 当作新建会话按钮,文字透明、两个子元素的 opacity 都为 0。补齐通用语义标记后,图标、文字与皮肤规定的 z-index 3 恢复,无需覆盖皮肤样式。 + +| Before / 修复前 | After / 修复后 | +|---|---| +| ![Invisible entry](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/issues/232/before-orca-wide.png) | ![Visible entry](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/issues/232/after-orca-wide.png) | + +The browser clicks exercised the actual built Starter, all three Sources, Native Provider, default Strategy and the scoped/light-context/auto-capture enhancements together. The icon also worked in the collapsed sidebar. From the restored entry, Runtime add and edit persisted, returning to chat worked, and creating/activating a Native space produced a connected status with Mnemon CLI 0.2.7. A restart with the skin disabled retained the data and working default launcher. + +浏览器实操使用构建后的 Starter、三个 Source、Native Provider、默认 Strategy,并同时启用 scoped、light-context、auto-capture。窄侧栏入口同样可见且可点击;从入口新增、编辑热记忆并返回会话成功,创建并激活 Native 空间后显示 CLI 0.2.7 已连接。关闭皮肤并重启后,数据与默认入口保持正常。 + +- [Runtime write / 热记忆写入](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/issues/232/after-orca-runtime-write.png) +- [Collapsed sidebar / 折叠侧栏](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/issues/232/after-orca-collapsed.png) +- [Native CLI status / Native CLI 状态](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/issues/232/after-orca-native-status.png) +- [Default skin after restart / 重启后的默认皮肤](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/issues/232/after-default-status.png) + +Reproduction uses the existing disposable WebUI fixture. Its optional extra-package argument is named `--better-sidebar`; here it installs the actual ORCA LINK bundle. Clone the external skin at the recorded revision, then run these commands in the isolated issue worktree: + +复现使用现有临时 WebUI 夹具。`--better-sidebar` 参数接收额外插件目录,本次传入真实 ORCA LINK bundle。将皮肤检出到记录中的 revision 后,在独立 worktree 中执行: + +```sh +pnpm install --frozen-lockfile +pnpm build +pnpm --workspace-concurrency=2 -r build +MNEMON_CLI_PATH=/absolute/path/to/mnemon node scripts/serve-e2e.mjs --strategy-extensions --better-sidebar=/absolute/path/to/dsh-deep-whale/orca-link +``` + +Open the printed loopback URL, keep the sidebar wide and the skin's status character enabled. Capture the launcher, apply the two-attribute fix, rebuild and restart the same fixture (`SIGUSR2` to its printed PID), then open the new URL and repeat. The fixture isolates DSH home, memory and workspace. All browser inputs were synthetic. The native integration test additionally verified create → View write → recall → forget with the real CLI. No live model API was needed. Full `pnpm verify` passed, including 926 root tests, all plugin tests, types, deterministic builds, Headless and package checks. Exact check results are in the validation file. + +打开夹具给出的本地地址,保持宽侧栏与状态角色开启;截图后应用两个属性的修复,重新构建并通过夹具输出的 PID 发送 `SIGUSR2` 重启,再打开新地址重复操作。夹具隔离 DSH home、记忆与工作区,输入全部为合成测试数据。Native 集成测试还通过真实 CLI 验证了创建 → View 写入 → 召回 → 删除。无需真实模型调用。完整 `pnpm verify` 通过,含 926 个 root 测试、全部 plugin 测试、类型、确定性构建、Headless 与包检查;具体结果见 validation 文件。 + +This records macOS/Chromium verification, not Windows/Edge certification. The published DSH UI primitive package emitted missing source-map warnings; verification exited successfully. + +本记录覆盖 macOS/Chromium,未验证 Windows/Edge。DSH 已发布 UI 包有缺少 source map 的提示,验证命令正常退出。 + +The ORCA LINK screenshots include third-party artwork: original whale-girl character by [上善](https://www.pixiv.net/users/62155430), derived skin scenes and status artwork by [Small-tailqwq](https://github.com/Small-tailqwq/dsh-deep-whale/tree/a0c9573c508e463441d804f03f116ae606f5f5ea/orca-link). That artwork remains under [CC BY-NC-SA 4.0](https://github.com/Small-tailqwq/dsh-deep-whale/blob/a0c9573c508e463441d804f03f116ae606f5f5ea/orca-link/LICENSE-ARTWORK), with the [upstream attribution notice](https://github.com/Small-tailqwq/dsh-deep-whale/blob/a0c9573c508e463441d804f03f116ae606f5f5ea/orca-link/NOTICE); it is not relicensed under this repository's software license. Screenshots are unretouched technical evidence. + +ORCA LINK 截图中的角色原作归属上善,皮肤场景与状态角色衍生设计归属 Small-tailqwq;美术保留上游 CC BY-NC-SA 4.0 授权与完整署名链,不适用本仓库的软件许可。截图为未修饰的技术验证记录。 diff --git a/dsh/docs/pr-assets/issue-232-orca-sidebar/validation.json b/dsh/docs/pr-assets/issue-232-orca-sidebar/validation.json new file mode 100644 index 000000000..805c99a9c --- /dev/null +++ b/dsh/docs/pr-assets/issue-232-orca-sidebar/validation.json @@ -0,0 +1,91 @@ +{ + "issue": 232, + "date": "2026-09-11", + "baseRevision": "1e19caf40f0bf37edf678c3d829f2398ab28792a", + "change": "src/client/sidebar-entry.ts: add data-dsh-plugin=dsh-mnemon and data-dsh-part=sidebar-entry", + "sourceSha256": "2943652859be52df7b8f4099aa802ed2d9e55ac7b4cdfb3e726aa367e5188bf1", + "environment": { + "dsh": "0.1.5-rc.1", + "starter": "0.5.7 plus this patch", + "node": "25.1.0", + "pnpm": "11.19.0", + "mnemon": "0.2.7", + "browser": "Codex in-app Chromium", + "viewport": [ + 1280, + 720 + ], + "skin": "@dsh-external/dsh-client-ui-skin-orca-link@0.0.1", + "skinRevision": "a0c9573c508e463441d804f03f116ae606f5f5ea", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ] + }, + "before": { + "data-dsh-part": null, + "data-dsh-plugin": null, + "color": "rgba(0, 0, 0, 0)", + "iconOpacity": "0", + "labelOpacity": "0", + "wide": true + }, + "after": { + "data-dsh-part": "sidebar-entry", + "data-dsh-plugin": "dsh-mnemon", + "color": "rgb(200, 210, 226)", + "iconOpacity": "1", + "labelOpacity": "1", + "zIndex": "3", + "wide": true + }, + "browserChecks": [ + "ORCA wide entry visible and clickable", + "ORCA collapsed entry visible and clickable", + "open Runtime before creating a conversation", + "add synthetic runtime memory", + "edit synthetic runtime memory", + "return to conversation", + "create and activate Native memory space", + "status shows Native 0.2.7 connected", + "restart with skin disabled retains data and working entry" + ], + "checks": { + "pnpm verify": "passed, root 926 passed / 5 opt-in skipped; all workspace packages passed", + "realNativeIntegration": "passed: create, write via View, recall, forget", + "verifyPlugins": "passed: 16 independent plugin repositories / 17 packed artifacts, external public SDK Client, real DSH packed Starter and three simultaneous strategy enhancements", + "skinRegression": "fails against base main; patched complete sidebar suite 21/21 passed" + }, + "screenshots": [ + { + "file": "../../assets/issues/232/after-default-status.png", + "sha256": "258189d94cf3c3660042cf6630499576322cd0f6167f6b506a192bdc6b8a734c" + }, + { + "file": "../../assets/issues/232/after-orca-collapsed.png", + "sha256": "ab9319e5c851e8dcea036b652a4f3dd0712d0c10fafd4dcf0bf2e57778701e9a" + }, + { + "file": "../../assets/issues/232/after-orca-native-status.png", + "sha256": "fe52fa3ee1cf53c3f861bcb94d633747a506de1ffa64a1e09413779021dfb3d7" + }, + { + "file": "../../assets/issues/232/after-orca-runtime-write.png", + "sha256": "f825e2360f537ea80d5973d77ed705d1d46a72343c6f78130273e6225a3e7793" + }, + { + "file": "../../assets/issues/232/after-orca-wide.png", + "sha256": "45b9a0c1ae3a50dfaeaa87d675c09f49ff6df44ba11a4251574970927d703fdf" + }, + { + "file": "../../assets/issues/232/before-orca-wide.png", + "sha256": "7bc57b2a90a1747162d3297f7d9966391200908e4296f12abcbe7ee19c6ca25f" + } + ], + "limits": [ + "macOS only; Windows and reported Edge version not tested", + "No live model API was necessary for this DOM/navigation fix", + "Upstream DSH UI primitive package missing sourcemap warnings do not fail verification" + ] +} diff --git a/dsh/docs/pr-assets/issue-233-openviking-20260916/README.md b/dsh/docs/pr-assets/issue-233-openviking-20260916/README.md new file mode 100644 index 000000000..098208e14 --- /dev/null +++ b/dsh/docs/pr-assets/issue-233-openviking-20260916/README.md @@ -0,0 +1,86 @@ +# OpenViking exact writes — issue #233 + +[简体中文](./README.zh-CN.md) | [Issue #233](https://github.com/omdsh-dev/dsh-mnemon/issues/233) | [Original contribution #234](https://github.com/omdsh-dev/dsh-mnemon/pull/234) | [Verification data](./verification.json) + +The baseline can report a stored memory after an extraction task that saves none of the supplied text. The fixed Provider creates the original content, verifies its index status and reads it back before returning a stored receipt. A controlled HTTP fixture reproduces the false receipt through the real WebUI and Host; a separate published OpenViking v0.4.20 service verifies actual storage, search and deletion. + +Baseline: `1363ebffaf19c9ab4badf0137f6fe87acacaf989`. Tested implementation and reusable fixtures: `11c5534cb916351f6f2ad22cd6d0dc22b8237e1b`. Izgenlre's original commit `bf83a4240f505e22713f20393d9b5cfa97fda05f` remains an ancestor, integrated by merge `2c5de91f0c9864de2d1f1918c4377dba7ea35a80`; it was not rewritten or squashed. + +The 2026-09-16 environment uses macOS arm64, Node 25.1.0, pnpm 11.19.0, DSH 0.1.5-rc.1 and official Native CLI 0.2.8. Browser runs use disposable Host state and synthetic content. Model decisions are scripted; actual Host tools, the delegated writer, Source/Provider composition, transport and WebUI run normally. No external model API or personal memory is used. + +## Baseline and controlled comparison + +The [regression test](../../../plugins/dsh-mnemon-provider-openviking/tests/write-regression.spec.ts) failed on main before the contributor branch was merged: + +```text +receipt.action: stored +expected persisted files: ["Release approvals require a completed canary.\n保留原文和换行。"] +actual persisted files: [] +``` + +The contributor change saved the body but still failed the next assertion because its receipt omitted `id`, which the Host needs for lineage and compensation. Both failures were retained before completing the fix. + +For the browser comparison, start the [protocol fixture](../../../scripts/fixtures/openviking-protocol.mjs), then the [scripted model](../../../scripts/fixtures/openviking-write-model.mjs) with the selected product revision: + +```sh +node scripts/fixtures/openviking-protocol.mjs +MNEMON_CLI_PATH=/absolute/path/to/mnemon pnpm e2e:serve --openviking-write --strategy-extensions +``` + +The browser runs use official Native CLI 0.2.8 and enable Scoped, Light context and Active capture together. Configure OpenViking at `http://127.0.0.1:19335`, account/user `default`, no API key, and send `openviking-write-233` in Mnemon E2E. Only fixture code is copied into the baseline checkout. The model selects the unique enabled default OpenViking namespace and drives its real delegated writer once. The fixture deliberately returns a cosmetic extraction update without storing the candidate; this is controlled protocol evidence, not a claim about live LLM extraction. + +| Same running HTTP fixture | Baseline | Fixed | +|---|---:|---:| +| Session creations / commits | 1 / 1 | 0 / 0 | +| Direct content writes | 0 | 1 | +| Persisted memories | 0 | 1 | +| Exact UTF-8 body | absent | 117 bytes | +| Writes to the other owner | 0 | 0 | + +The baseline displays `stored`, one extracted update and a committed Host receipt while the Content tab is empty. The fixed build sends `mode: create`, `wait: true`, performs exact readback and displays the original bilingual body. Counts for the fixed run exclude the baseline requests already retained by the same fixture. + +| Baseline stored receipt | Baseline empty content | +|---|---| +| ![Baseline reports a stored memory](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-233-openviking-20260916/before-stored-receipt.png) | ![Baseline contains no memory](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-233-openviking-20260916/before-empty-content.png) | + +![Fixed content appears in the intended namespace](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-233-openviking-20260916/after-protocol-content.png) + +## Real backend acceptance + +The separate service at `http://127.0.0.1:19333` uses the published `openviking==0.4.20` Python wheel and Python 3.12.13. Its audited source tag resolves to `b54001e2e5c974ffd7a09ba543813fa104a99561`. Storage, HTTP handling and vector indexing are real OpenViking. A local 128-dimensional token-hash embedding endpoint makes the test deterministic; chat-model requests fail explicitly. Authentication uses an isolated account-admin key omitted from this record. + +The [opt-in integration test](../../../plugins/dsh-mnemon-provider-openviking/tests/integration.spec.ts) creates a unique 137-byte bilingual memory, reads the exact original, finds it through search and browsing, deletes its exact URI, then confirms its absence. It passes against the authenticated server. The recorded receipt contains the matching `id`/`uri`, `writtenBytes: 137` and `vectorStatus: complete`; no session extraction endpoint is called. + +```sh +# Supply an account-admin key through the environment when required. +MNEMON_OPENVIKING_TEST_ENDPOINT=http://127.0.0.1:19333 \ + pnpm --filter dsh-mnemon-provider-openviking exec vitest run tests/integration.spec.ts +``` + +The service must be disposable. The test accepts loopback endpoints only and removes its own canary. On v0.4.20, namespace discovery needs an explicit account and an account-admin key; a ROOT bootstrap key cannot access tenant content, and unauthenticated dev mode rejects admin discovery. + +The fixed WebUI was then switched to this real service through its ordinary settings and discovery flow. The same tool command stored the 117-byte browser canary with a committed Host receipt, and an independent HTTP read matched it byte-for-byte. The Content page displayed it in `default`; direct search for `release approval staged rollout` returned the full original. The synthetic embedding score of 0.707 is recorded only as observed output. Clicking Forget and confirming removed it; a separate HTTP read returned `404 NOT_FOUND`. The compact [WebUI readback record](./real-webui-readback.json) includes the exact receipt and deletion result. + +| Real OpenViking content | Real OpenViking direct search | +|---|---| +| ![Actual backend contains the full original](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-233-openviking-20260916/after-real-content.png) | ![Direct search returns the full original](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-233-openviking-20260916/after-real-search.png) | + +![Confirmed deletion leaves the real namespace empty](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-233-openviking-20260916/after-real-forget.png) + +## Contract and failure handling + +The implementation was checked against the official [content writer](https://github.com/volcengine/OpenViking/blob/v0.4.20/openviking/storage/content_write.py), [HTTP content routes](https://github.com/volcengine/OpenViking/blob/v0.4.20/openviking/server/routers/content.py) and [namespace rules](https://github.com/volcengine/OpenViking/blob/v0.4.20/openviking/core/namespace.py). A successful HTTP response alone is insufficient: the write must report the requested URI, mode, memory type, updated content, correct byte count, completed vector status, and completed or skipped semantic status without queue errors. Public content readback must then equal the input exactly. Search and browse likewise hydrate full public content. + +New files use UUID identities and `create`, so collisions cannot replace existing memory. The Provider validates owner roots, discovered owner IDs and exact deletion scope. The persisted shorthand `viking://user/memories` resolves to the configured or authenticated owner without rewriting settings. A failed, pending, canceled or uncertain write produces an error naming the candidate URI; it is not retried or automatically deleted. Older servers without the required write/read contract fail explicitly. Existing content is retained on downgrade. + +The [Host composition tests](../../../tests/openviking-host.spec.mjs) exercise separate OpenViking instances alongside Native, Holographic, Runtime and Documents. They verify exact receipts and full reads, scoped writes/deletes, archive lineage, compensation of only the newly created index after a local archive conflict, and retention of original documents when indexing fails. + +## Validation and limits + +- `pnpm install --frozen-lockfile` passes in the isolated worktree. +- `MNEMON_NATIVE_TEST_CLI=/absolute/path/to/mnemon pnpm run verify` passes: root 1,134 tests / 5 opt-in skips; OpenViking 49 / 1 opt-in skip; Memory Spaces 169 / 1 Windows-only skip. Documentation, types, deterministic builds, plugin suites, real Native integration, real Headless activation and package checks pass. +- `pnpm run verify:plugins` passes for 16 independently installed plugins and 17 packed artifacts, including public SDK composition and actual DSH activation. +- Regression cases cover malformed receipts, missing/incomplete/failed indexing, readback mismatch, HTTP errors, timeout/cancellation, unsafe roots/IDs, inherited object-property category names, account isolation and rollback. The separate real OpenViking integration passes; ordinary CI intentionally skips it. +- The changeset covers the aggregate Host, OpenViking Provider and Memory Spaces Source. Release-intent validation passes. + +The deterministic backend model validates storage and index contracts, not extraction quality or semantic recall quality. Live Flash, Windows and Electron were not exercised. This record contains only synthetic screenshots, compact counts and hashes; credentials, full sessions and personal data are excluded. diff --git a/dsh/docs/pr-assets/issue-233-openviking-20260916/README.zh-CN.md b/dsh/docs/pr-assets/issue-233-openviking-20260916/README.zh-CN.md new file mode 100644 index 000000000..0bdf682fd --- /dev/null +++ b/dsh/docs/pr-assets/issue-233-openviking-20260916/README.zh-CN.md @@ -0,0 +1,86 @@ +# OpenViking 精确写入 — issue #233 + +[English](./README.md) | [Issue #233](https://github.com/omdsh-dev/dsh-mnemon/issues/233) | [原始贡献 #234](https://github.com/omdsh-dev/dsh-mnemon/pull/234) | [核验数据](./verification.json) + +基线可能在抽取任务没有保存传入正文时仍返回已存储。修复后的 Provider 直接创建原文,确认索引状态并精确回读,最后才返回存储回执。可控 HTTP fixture 通过真实 WebUI 与 Host 复现错误回执;另用官方发布的 OpenViking v0.4.20 服务验证实际存储、检索和删除。 + +基线为 `1363ebffaf19c9ab4badf0137f6fe87acacaf989`,实测实现与可复用 fixture 为 `11c5534cb916351f6f2ad22cd6d0dc22b8237e1b`。Izgenlre 的原始提交 `bf83a4240f505e22713f20393d9b5cfa97fda05f` 经 merge `2c5de91f0c9864de2d1f1918c4377dba7ea35a80` 保留为祖先,没有改写或 squash。 + +2026-09-16 的环境为 macOS arm64、Node 25.1.0、pnpm 11.19.0、DSH 0.1.5-rc.1 和官方 Native CLI 0.2.8。浏览器运行使用独立临时 Host 状态和合成资料。模型决策由脚本提供,Host 工具、委派写入 Agent、Source/Provider 组合、传输及 WebUI 均实际执行;没有调用外部模型 API 或使用个人记忆。 + +## 基线与受控对照 + +合并贡献者分支前,[回归测试](../../../plugins/dsh-mnemon-provider-openviking/tests/write-regression.spec.ts) 已在 main 上失败: + +```text +receipt.action: stored +期望持久化内容:["Release approvals require a completed canary.\n保留原文和换行。"] +实际持久化内容:[] +``` + +原贡献能够保存正文,但随后仍因回执缺少 Host 建立 lineage 和补偿删除所需的 `id` 而失败。两次失败结果均在完成修复前保存。 + +浏览器对照分别构建目标产品 revision,运行[协议 fixture](../../../scripts/fixtures/openviking-protocol.mjs)与[模型脚本](../../../scripts/fixtures/openviking-write-model.mjs): + +```sh +node scripts/fixtures/openviking-protocol.mjs +MNEMON_CLI_PATH=/absolute/path/to/mnemon pnpm e2e:serve --openviking-write --strategy-extensions +``` + +浏览器运行使用官方 Native CLI 0.2.8,同时启用 Scoped、Light context 和 Active capture。将 OpenViking 配为 `http://127.0.0.1:19335`,account/user 均为 `default`,不填 API key,在 Mnemon E2E 会话发送 `openviking-write-233`。基线 checkout 只复制 fixture 代码。模型选择唯一启用的默认 OpenViking 命名空间,经实际委派写入 Agent 执行一次。fixture 刻意返回一次外观性抽取更新但不保存候选正文;这是受控协议复现,不代表实测了在线 LLM 抽取质量。 + +| 同一运行中的 HTTP fixture | 基线 | 修复后 | +|---|---:|---:| +| Session 创建 / commit | 1 / 1 | 0 / 0 | +| 直接正文写入 | 0 | 1 | +| 已持久化记忆 | 0 | 1 | +| 精确 UTF-8 正文 | 不存在 | 117 字节 | +| 其他 owner 写入 | 0 | 0 | + +基线显示 `stored`、一次抽取更新以及已提交 Host 回执,内容页却为空。修复版发送 `mode: create`、`wait: true`,精确回读后显示完整双语正文。修复后计数扣除了同一 fixture 中已有的基线请求。 + +| 基线已存储回执 | 基线空内容页 | +|---|---| +| ![基线报告记忆已存储](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-233-openviking-20260916/before-stored-receipt.png) | ![基线没有已保存的记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-233-openviking-20260916/before-empty-content.png) | + +![修复后原文出现在目标命名空间](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-233-openviking-20260916/after-protocol-content.png) + +## 真实后端验收 + +独立服务 `http://127.0.0.1:19333` 使用正式发布的 `openviking==0.4.20` Python wheel 和 Python 3.12.13。审计源码 tag 对应 `b54001e2e5c974ffd7a09ba543813fa104a99561`。HTTP、存储和向量索引均为真实 OpenViking;本地 128 维 token-hash embedding 服务保证确定性,chat 模型请求明确失败。服务使用隔离的 account-admin key,本记录不保存凭据。 + +[可选集成测试](../../../plugins/dsh-mnemon-provider-openviking/tests/integration.spec.ts) 在认证服务上通过:创建唯一的 137 字节双语记忆,精确读取原文,经检索和浏览找到同一内容,删除其准确 URI,再确认已不存在。回执包含一致的 `id`/`uri`、`writtenBytes: 137` 和 `vectorStatus: complete`,没有调用 Session 抽取端点。 + +```sh +# 如服务要求认证,通过环境提供 account-admin key。 +MNEMON_OPENVIKING_TEST_ENDPOINT=http://127.0.0.1:19333 \ + pnpm --filter dsh-mnemon-provider-openviking exec vitest run tests/integration.spec.ts +``` + +必须使用可丢弃后端。测试仅允许 loopback 地址并清理自己的 canary。v0.4.20 的命名空间发现需要显式 account 和 account-admin key;ROOT 初始化 key 不能访问租户内容,免认证 dev 模式不允许 admin discovery。 + +随后通过正常设置与发现流程,将修复版 WebUI 切换到此真实服务。同一工具命令写入 117 字节浏览器 canary 并返回已提交 Host 回执,独立 HTTP 读取逐字节一致。内容页在 `default` 显示正文,直接检索 `release approval staged rollout` 返回完整原文。0.707 是合成 embedding 的观察分数,仅如实记录。点击忘记并确认后内容消失,独立 HTTP 读取返回 `404 NOT_FOUND`。[WebUI 回读记录](./real-webui-readback.json) 保留精确回执和删除结果。 + +| 真实 OpenViking 内容 | 真实 OpenViking 直接检索 | +|---|---| +| ![真实后端保存完整原文](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-233-openviking-20260916/after-real-content.png) | ![直接检索返回完整原文](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-233-openviking-20260916/after-real-search.png) | + +![确认删除后真实命名空间为空](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-233-openviking-20260916/after-real-forget.png) + +## 契约与失败处理 + +实现对照了官方[正文写入器](https://github.com/volcengine/OpenViking/blob/v0.4.20/openviking/storage/content_write.py)、[HTTP 正文路由](https://github.com/volcengine/OpenViking/blob/v0.4.20/openviking/server/routers/content.py)和[命名空间规则](https://github.com/volcengine/OpenViking/blob/v0.4.20/openviking/core/namespace.py)。HTTP 成功本身不足以证明存储成功:回执必须对应请求 URI、mode 和 memory 类型,正文已更新、字节数准确,向量索引已完成,语义状态为完成或跳过,且没有队列错误;随后公共正文读取必须与输入完全一致。检索和浏览也读取完整正文。 + +新文件采用 UUID 与 `create`,碰撞时不会覆盖已有记忆。Provider 校验 owner 根路径、发现的用户 ID 及精确删除范围。已有简写 `viking://user/memories` 解析为配置或认证 owner,不改写配置。失败、待处理、取消或不确定写入均返回包含候选 URI 的错误,不自动重试或删除。缺少所需写入/读取契约的旧服务明确失败;降级保留既有内容。 + +[Host 组合测试](../../../tests/openviking-host.spec.mjs) 将独立的 OpenViking 实例与 Native、Holographic、Runtime 和 Documents 一同运行,验证精确回执及原文读取、范围内写入/删除、归档 lineage、本地归档冲突后只补偿删除新建索引,以及索引失败时保留原始档案。 + +## 验证及限制 + +- 隔离 worktree 中 `pnpm install --frozen-lockfile` 通过。 +- `MNEMON_NATIVE_TEST_CLI=/absolute/path/to/mnemon pnpm run verify` 通过:root 1,134 个测试通过 / 5 个可选跳过;OpenViking 49 / 1 个可选跳过;Memory Spaces 169 / 1 个 Windows 专属跳过。文档、类型、确定性构建、插件测试、真实 Native 集成、真实 Headless 激活与 package 检查均通过。 +- `pnpm run verify:plugins` 通过:16 个独立安装的插件、17 个打包制品,包括公共 SDK 组合及实际 DSH 激活。 +- 回归覆盖异常回执、缺失/未完成/失败的索引、回读不一致、HTTP 错误、超时/取消、不安全根路径/ID、对象原型属性分类名、账户隔离及回滚。单独的真实 OpenViking 集成测试通过;常规 CI 有意跳过该可选测试。 +- Changeset 覆盖聚合 Host、OpenViking Provider 及 Memory Spaces Source,release-intent 检查通过。 + +确定性模型只验证存储与索引契约,不证明抽取质量或语义召回质量。未执行在线 Flash、Windows 或 Electron 验收。本记录只含合成截图、精简计数和 hash,不含凭据、完整会话或个人数据。 diff --git a/dsh/docs/pr-assets/issue-233-openviking-20260916/real-webui-readback.json b/dsh/docs/pr-assets/issue-233-openviking-20260916/real-webui-readback.json new file mode 100644 index 000000000..e654b405c --- /dev/null +++ b/dsh/docs/pr-assets/issue-233-openviking-20260916/real-webui-readback.json @@ -0,0 +1,32 @@ +{ + "backend": "OpenViking 0.4.20", + "scope": "isolated default/default", + "model": "scripted tool decisions, synthetic embeddings, no live LLM", + "receipt": { + "action": "stored", + "provider": "openviking", + "id": "viking://user/default/memories/experiences/20260916043728-issue-233-canary-release-approval-requires-a-com-0f28a6e3-d100-43df-ad23-bb99623cbb61.md", + "uri": "viking://user/default/memories/experiences/20260916043728-issue-233-canary-release-approval-requires-a-com-0f28a6e3-d100-43df-ad23-bb99623cbb61.md", + "externalUri": "viking://user/default/memories/experiences/20260916043728-issue-233-canary-release-approval-requires-a-com-0f28a6e3-d100-43df-ad23-bb99623cbb61.md", + "summary": "OpenViking stored the memory at viking://user/default/memories/experiences/20260916043728-issue-233-canary-release-approval-requires-a-com-0f28a6e3-d100-43df-ad23-bb99623cbb61.md (category decision).", + "writtenBytes": 117, + "vectorStatus": "complete", + "memoryBodyId": "openviking-265db573b8e128c231453ecc", + "memoryBodyName": "default", + "memoryProviderId": "openviking", + "memoryProviderLabel": "OpenViking", + "memoryReceipt": { + "status": "succeeded", + "completion": "committed", + "committedAt": "2026-09-16T04:37:29.197Z" + } + }, + "readback": "Issue 233 canary: release approval requires a completed staged rollout.\n合成回归资料:保留原文与换行。", + "utf8Bytes": 117, + "byteExact": true, + "forget": { + "trigger": "real WebUI confirmation", + "readAfterDeleteHttpStatus": 404, + "backendCode": "NOT_FOUND" + } +} \ No newline at end of file diff --git a/dsh/docs/pr-assets/issue-233-openviking-20260916/verification.json b/dsh/docs/pr-assets/issue-233-openviking-20260916/verification.json new file mode 100644 index 000000000..5274e1bee --- /dev/null +++ b/dsh/docs/pr-assets/issue-233-openviking-20260916/verification.json @@ -0,0 +1,175 @@ +{ + "date": "2026-09-16", + "baselineRevision": "1363ebffaf19c9ab4badf0137f6fe87acacaf989", + "contributorRevision": "bf83a4240f505e22713f20393d9b5cfa97fda05f", + "contributorMergeRevision": "2c5de91f0c9864de2d1f1918c4377dba7ea35a80", + "testedRevision": "11c5534cb916351f6f2ad22cd6d0dc22b8237e1b", + "environment": { + "platform": "macOS arm64", + "node": "25.1.0", + "pnpm": "11.19.0", + "dsh": "0.1.5-rc.1", + "mnemonNative": "0.2.8", + "python": "3.12.13", + "openviking": "0.4.20", + "openvikingSourceRevision": "b54001e2e5c974ffd7a09ba543813fa104a99561" + }, + "controlledProtocol": { + "endpoint": "http://127.0.0.1:19335", + "isRealOpenViking": false, + "before": { + "storedFileCount": 0, + "sessionCreates": 1, + "sessionCommits": 1, + "contentWrites": 0, + "reportedAction": "stored", + "reportedMemoryUpdates": 1 + }, + "after": { + "storedFileCount": 1, + "sessionCreates": 0, + "sessionCommits": 0, + "contentWrites": 1, + "writeMode": "create", + "wait": true, + "utf8Bytes": 117, + "otherOwnerWrites": 0, + "canary": "Issue 233 canary: release approval requires a completed staged rollout.\n合成回归资料:保留原文与换行。" + } + }, + "realOpenVikingIntegration": { + "endpoint": "http://127.0.0.1:19333", + "canary": "OpenViking integration 27cd9a6a-0d57-4b8c-9998-d132e2e071b9: release approval requires a completed canary.\n保留完整中文和换行。", + "receipt": { + "action": "stored", + "provider": "openviking", + "id": "viking://user/default/memories/experiences/20260916042919-openviking-integration-27cd9a6a-0d57-4b8c-9998-d-73e8e735-6f16-4e52-957f-5b41a342537a.md", + "uri": "viking://user/default/memories/experiences/20260916042919-openviking-integration-27cd9a6a-0d57-4b8c-9998-d-73e8e735-6f16-4e52-957f-5b41a342537a.md", + "externalUri": "viking://user/default/memories/experiences/20260916042919-openviking-integration-27cd9a6a-0d57-4b8c-9998-d-73e8e735-6f16-4e52-957f-5b41a342537a.md", + "summary": "OpenViking stored the memory at viking://user/default/memories/experiences/20260916042919-openviking-integration-27cd9a6a-0d57-4b8c-9998-d-73e8e735-6f16-4e52-957f-5b41a342537a.md (category decision).", + "writtenBytes": 137, + "vectorStatus": "complete" + }, + "removed": { + "action": "deleted", + "provider": "openviking", + "id": "viking://user/default/memories/experiences/20260916042919-openviking-integration-27cd9a6a-0d57-4b8c-9998-d-73e8e735-6f16-4e52-957f-5b41a342537a.md", + "uri": "viking://user/default/memories/experiences/20260916042919-openviking-integration-27cd9a6a-0d57-4b8c-9998-d-73e8e735-6f16-4e52-957f-5b41a342537a.md", + "estimatedDeletedCount": 1 + }, + "exactReadback": true, + "recalled": true, + "browsed": true, + "deleted": true, + "requests": [ + { + "method": "POST", + "path": "/api/v1/content/write" + }, + { + "method": "GET", + "path": "/api/v1/content/read" + }, + { + "method": "POST", + "path": "/api/v1/search/find" + }, + { + "method": "GET", + "path": "/api/v1/content/read" + }, + { + "method": "GET", + "path": "/api/v1/fs/ls" + }, + { + "method": "GET", + "path": "/api/v1/content/read" + }, + { + "method": "DELETE", + "path": "/api/v1/fs" + }, + { + "method": "GET", + "path": "/api/v1/fs/ls" + } + ], + "embedding": "Local deterministic 128-dimensional token-hash embeddings; no live LLM", + "auth": "Disposable account-admin key, omitted from evidence" + }, + "automated": { + "verify": "passed", + "root": { + "passed": 1134, + "skipped": 5, + "filesPassed": 87, + "filesSkipped": 2 + }, + "openvikingProvider": { + "passed": 49, + "skipped": 1 + }, + "memorySpacesSource": { + "passed": 169, + "skipped": 1 + }, + "plugins": { + "independentPlugins": 16, + "packedArtifacts": 17, + "result": "passed" + } + }, + "sha256": { + "main-red.log": "cb6eda625f9fe69d1ea374e69e7026fd1477c20843e2056574f01b97baf33400", + "draft-red.log": "969941eef21f38bd5ceb0efcac02758dbfa340755149c0ef527ed884b4ea32d8", + "real-openviking-auth-report.json": "a32d20b20721854859f0bb32b720731687187b21c57e59e381a0ed0545de6ddc", + "verify.log": "698d1afa8b9ee302b3ff5641a30989427d7026d39371fb036ea165a5b15005b1", + "verify-plugins.log": "cacc1fcc62733a3d8ad311465b4b459985b83c5f3df57a2c4aa5910c774b4283", + "before-stored-receipt.png": "7b08526fffa8e5e5dca673ad1412395412966db3904652ebd161288e14f5f9c0", + "before-empty-content.png": "2170fa5d59b9abdc4fd38a705caaaaaf173009030561c1216b30c3fa7280a836", + "after-protocol-content.png": "b3e7827b1f47669e1f2db85a20b16038cdc0608d05e639c5f04c376fa2081db3", + "after-real-content.png": "44d2532c1ef5e206f582df78121e9af6ef0dfd3a1e4893df0db606d4e873ced7", + "after-real-search.png": "8b62620e4a1b5deca59fd3005beb96c1fd621854989f2bb7a7e06202a8fa7f1b", + "after-real-forget.png": "242466dd915c856c69729ff3e13b35735f244c4496f82518c529e578db0ce0bd", + "real-webui-readback.json": "3019b81883c7737637883e3c186167813300d9f903ab0d27aa646cf950d8d99b" + }, + "realOpenVikingWebUI": { + "backend": "OpenViking 0.4.20", + "scope": "isolated default/default", + "model": "scripted tool decisions, synthetic embeddings, no live LLM", + "receipt": { + "action": "stored", + "provider": "openviking", + "id": "viking://user/default/memories/experiences/20260916043728-issue-233-canary-release-approval-requires-a-com-0f28a6e3-d100-43df-ad23-bb99623cbb61.md", + "uri": "viking://user/default/memories/experiences/20260916043728-issue-233-canary-release-approval-requires-a-com-0f28a6e3-d100-43df-ad23-bb99623cbb61.md", + "externalUri": "viking://user/default/memories/experiences/20260916043728-issue-233-canary-release-approval-requires-a-com-0f28a6e3-d100-43df-ad23-bb99623cbb61.md", + "summary": "OpenViking stored the memory at viking://user/default/memories/experiences/20260916043728-issue-233-canary-release-approval-requires-a-com-0f28a6e3-d100-43df-ad23-bb99623cbb61.md (category decision).", + "writtenBytes": 117, + "vectorStatus": "complete", + "memoryBodyId": "openviking-265db573b8e128c231453ecc", + "memoryBodyName": "default", + "memoryProviderId": "openviking", + "memoryProviderLabel": "OpenViking", + "memoryReceipt": { + "status": "succeeded", + "completion": "committed", + "committedAt": "2026-09-16T04:37:29.197Z" + } + }, + "readback": "Issue 233 canary: release approval requires a completed staged rollout.\n合成回归资料:保留原文与换行。", + "utf8Bytes": 117, + "byteExact": true, + "forget": { + "trigger": "real WebUI confirmation", + "readAfterDeleteHttpStatus": 404, + "backendCode": "NOT_FOUND" + }, + "search": { + "query": "release approval staged rollout", + "resultCount": 1, + "fullOriginalVisible": true, + "syntheticEmbeddingScore": 0.707 + } + } +} diff --git a/dsh/docs/pr-assets/issue-235-runtime-routing/README.md b/dsh/docs/pr-assets/issue-235-runtime-routing/README.md new file mode 100644 index 000000000..ac827966f --- /dev/null +++ b/dsh/docs/pr-assets/issue-235-runtime-routing/README.md @@ -0,0 +1,76 @@ +# Runtime archive routing fallback — issue #235 + +Base: `1e19caf` (main). Fixed branch: `codex/fix-235-runtime-route-fallback`. +Environment: macOS arm64, Node 25.1.0, pnpm 11.19.0, published DSH 0.1.5-rc.1, +real Mnemon CLI 0.2.7. All data is synthetic and disposable. + +## Reproduction and browser verification + +Run `pnpm build`, `pnpm --workspace-concurrency=4 -r build`, then +`pnpm e2e:serve --runtime-routing --strategy-extensions`. Create two Native +Memory Spaces named `Routing fallback default` and `Routing release notes`, and +activate both. All three optional Strategy enhancements are enabled together. +The actual Host, DSH WebUI, transport, Source plugins, Native Provider and CLI +are used. Only the routing model response is scripted: it returns indexes local +to each chunk, deliberately reproducing the invalid second proposal. + +1. Add three Runtime entries, one each for A, B and C: `Routing source A: ` + followed by fourteen copies of `Archive exact release notes. ` (substitute + B or C in the label). The committed projection uses 1,278/1,600 bytes. +2. Add `Pending routing entry: ` followed by sixteen copies of + `Preserve the new commitment. `. +3. Before the fix, the first model batch receives `[1, 2]` and returns `[1, 2]`; + the second receives `[3]` and returns `[1]`. The WebUI reports + `runtime memory migration route coverage is invalid`. No archive is written + and the three original Runtime entries remain unchanged. The projection SHA-256 + is `32e152148433964015a4835c8856932c732da6af9fa3bc177c4491f3ffa44d87`. +4. Restart the same fixture with the fixed Host and retry the exact pending + entry. The same malformed routing responses now succeed through fallback. + Runtime shows 913/1,600 bytes and two entries, including the pending entry. + The Content page shows all three complete original entries in Native + `default`; the other space stays empty. + +| Before: routing validation aborts | After: complete archive and pending write | +| --- | --- | +| ![Before](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-235-runtime-routing/before-webui.png) | ![After](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-235-runtime-routing/after-webui.png) | + +## Automated checks and limits + +- The added regression fails on the base with the original coverage error. +- Coordinator tests cover omitted sources, empty routes, unknown or empty + destinations, empty indexes, duplicates within and across routes, out-of-range + and noninteger indexes, and an explicit failed model proposal. +- A valid first chunk routed to a second destination stays intact when a later + chunk restarts numbering. Assertions check complete original source text and + destination distribution, independently of Provider batching. Model excerpts + stay within 384 characters, including the truncation marker. +- Caller cancellation still aborts before archival writes. Existing dense-source + and exact-archive tests retain complete content outside model excerpts. +- `pnpm verify` passes: 936 root tests pass and five opt-in live-model tests skip; + workspace tests, types, builds, real Headless activation, exports and package + checks pass. The final coordinator run passes all 95 tests. +- `pnpm verify:plugins` passes for 16 independent plugin repositories and 17 + packed artifacts, including all three optional Strategy enhancements together. +- The real Native integration creates a disposable store, writes through a View, + recalls through the real CLI, and forgets the result. +- This change handles advisory routing failures. Provider-write recovery is + covered separately by issue #240. Scripted routing verifies deterministic Host + behavior; it is not a live-model routing-quality evaluation. + +## 中文记录 + +基于 `1e19caf`,在真实 DSH WebUI、独立 Source/Provider 制品及 Mnemon CLI 中验收, +同时启用三个 Strategy 增强。创建并激活两个 Native 空间,使用 1,600 字节的 Runtime +上限。仅模型的路由响应由脚本控制:第一批合法返回全局索引 `[1, 2]`,第二批本应 +返回 `[3]`,却重新从 `[1]` 编号。 + +修复前,同一新增操作触发原始覆盖校验错误,没有归档写入,1,278 字节的三个原始 +热记忆条目保持不变。修复后重启同一个临时实例,以相同输入和错误路由响应重试, +操作成功;Runtime 为 913/1,600 字节、两个条目,并包含待新增条目。内容页可查看 +`default` 空间中的三个完整原文,另一个空间为空。 + +自动化覆盖缺失条目、空路由、错误目标、重复或非法索引以及失败提案,验证整个 +错误批次回退、此前合法批次保留、截断摘要不会截断实际归档原文,以及用户取消 +仍会停止写入。完整 workspace 验证、独立制品组合与真实 CLI 的创建、写入、召回、 +删除均通过。本改动处理建议路由失败;Provider 写入恢复由 #240 单独覆盖,不将 +脚本模型验收表述为真实模型的路由质量评测。 diff --git a/dsh/docs/pr-assets/issue-239-result-tool/README.md b/dsh/docs/pr-assets/issue-239-result-tool/README.md new file mode 100644 index 000000000..00b0bd7bb --- /dev/null +++ b/dsh/docs/pr-assets/issue-239-result-tool/README.md @@ -0,0 +1,37 @@ +# Stable delegated result tool — issue #239 + +English / 中文 · 2026-09-11 · Base `1e19caf` · DSH `0.1.5-rc.1`, Node `25.1.0`, pnpm `11.19.0`, Mnemon CLI `0.2.7`. + +Two consecutive writes in the real DSH WebUI reproduced different UUID result-tool names and different serialized tool definitions on the baseline. The fix registers `mnemon_subagent_result` once for the coordinator lifetime. A revocable `requestId` is passed in the child instructions and tool arguments; each request retains its own operation schema and child ownership checks. The same disposable profile ran both builds with all three Strategy enhancements enabled. + +真实 DSH WebUI 在修复前连续执行两次写入委派,观察到不同的 UUID 结果工具名和工具定义。修复后,协调器仅注册一次 `mnemon_subagent_result`,通过子任务指令和工具参数传递可撤销的 `requestId`,并保留每次操作的结果 schema 与子任务归属校验。前后使用同一临时 Profile,且同时启用三个策略增强。 + +| Build / 版本 | Child run 1 / 子任务 1 | Child run 2 / 子任务 2 | +| --- | --- | --- | +| Baseline / 修复前 | `mnemon_subagent_result_ee8cebfce93847a78c93ba21fbaabf39` | `mnemon_subagent_result_c04cad1861f84ecd9af5fa32edf465af` | +| Fixed / 修复后 | `mnemon_subagent_result` | `mnemon_subagent_result` | + +The full child `tools` array hash changed from `43791240…294b` to `51b1596e…4f6f` before the fix; both fixed child requests have `b01c520da60b2e631963e1e8f4b8f0ba26026cd13b57e7a92ff17d20503dd28e`. Parent requests on the fixed build also have one stable hash. [Exact tool names and hashes](./tools-fingerprints.json) are captured at the loopback model endpoint. This proves tool-prefix stability for the reproduced operations; no paid model, large-context cache hit rate, or monetary savings were measured. The child safely skips the memory-space write because the fixture has no active space. A separate real WebUI runtime write and real CLI create/write/recall/forget check succeed. + +修复前,完整子任务 `tools` 数组的哈希由 `43791240…294b` 变为 `51b1596e…4f6f`;修复后两次均为 `b01c520da60b2e631963e1e8f4b8f0ba26026cd13b57e7a92ff17d20503dd28e`,父任务的工具定义也保持一致。[完整工具名与哈希](./tools-fingerprints.json) 来自本地模型端点。该验证证明所复现操作的工具前缀稳定,不涉及付费模型、大上下文缓存命中率或费用测量。临时环境未激活记忆空间,子任务安全跳过该写入;另行通过真实 WebUI 热记忆写入和真实 CLI 创建、写入、召回、遗忘验证基本功能。 + +## Reproduce / 复现 + +```sh +pnpm build +pnpm --workspace-concurrency=4 -r build +MNEMON_CLI_PATH=/absolute/path/to/mnemon pnpm e2e:serve --result-tool-cache --strategy-extensions +``` + +Open the printed workspace and choose the Mnemon E2E preset. Send `cache-round-239 first`, then `cache-round-239 second`. Inspect the two child conversations and the endpoint's `Result tool cache` lines. The responder supports both the baseline UUID protocol and the fixed request envelope. / 打开日志中的工作区并选择 Mnemon E2E 预设,依次发送 `cache-round-239 first` 和 `cache-round-239 second`,查看两次子任务及端点输出的 `Result tool cache`。受控响应器同时兼容修复前的 UUID 协议和修复后的请求封装。 + +## Verification / 验证 + +- `pnpm run verify`: passed; 935 root tests, 5 expected live-model skips, deterministic builds, plugin checks, isolated Headless activation, package/public-entry validation. / 通过;根测试 935 项,5 项实时模型测试按预期跳过。 +- `node scripts/verify-plugin-artifacts.mjs --skip-build`: passed after the full build; 16 independent plugin repositories and 17 tarballs, including real DSH Starter activation and three combined Strategy enhancements. / 完整构建后通过;16 个独立插件仓库、17 个发布包及真实 DSH 组合激活。 +- `MNEMON_NATIVE_TEST_CLI=/opt/homebrew/bin/mnemon pnpm --filter dsh-mnemon-source-memory-spaces exec vitest run tests/native-integration.spec.ts`: passed; real CLI round trip in a disposable store. / 真实 CLI 临时存储读写闭环通过。 +- Regressions cover overlapping requests, cross-child/root attempts, duplicate and expired results, rejected startup, cancelled/disposed requests, disposal during workflow preparation, revocation before asynchronous cleanup, operation-specific schema validation, and authoritative tool/outer `run_code` success. / 回归覆盖并发、跨子任务与父任务越权、重复及过期结果、启动失败、取消与销毁、工作流准备期间销毁、异步清理前撤销、操作专用 schema 和权威执行事件。 + +![Before: UUID result tool](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-239-result-tool/before.png) +![After: stable tool and request envelope](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-239-result-tool/after.png) +![After: real runtime write succeeds](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-239-result-tool/runtime-write.png) diff --git a/dsh/docs/pr-assets/issue-239-result-tool/tools-fingerprints.json b/dsh/docs/pr-assets/issue-239-result-tool/tools-fingerprints.json new file mode 100644 index 000000000..707ff621b --- /dev/null +++ b/dsh/docs/pr-assets/issue-239-result-tool/tools-fingerprints.json @@ -0,0 +1,247 @@ +{ + "source": "Controlled loopback model endpoint; real DSH WebUI requests. SHA256 covers JSON.stringify(request.tools).", + "baseline": [ + { + "event": "parent-tools", + "names": [ + "mnemon_document_create", + "mnemon_document_manage", + "mnemon_document_search", + "mnemon_forget", + "mnemon_link", + "mnemon_memory_bodies", + "mnemon_memory_body_create", + "mnemon_memory_body_merge", + "mnemon_memory_body_update", + "mnemon_recall", + "mnemon_related", + "mnemon_remember", + "mnemon_runtime_memory", + "mnemon_status", + "mnemon_view_action", + "mnemon_view_route" + ], + "sha256": "9a76b5225a98bfda5c239d8a0c4d8c2d90c654b150a6b1c2c40ac2f7fa1e0026" + }, + { + "event": "child-tools", + "names": [ + "mnemon_link", + "mnemon_memory_bodies", + "mnemon_memory_body_create", + "mnemon_memory_body_merge", + "mnemon_memory_body_update", + "mnemon_recall", + "mnemon_related", + "mnemon_remember", + "mnemon_subagent_result_ee8cebfce93847a78c93ba21fbaabf39" + ], + "sha256": "43791240edf2dd263242adc807ceef4c2f5d528a6df2bc1f11029c4283d0294b" + }, + { + "event": "parent-tools", + "names": [ + "mnemon_document_create", + "mnemon_document_manage", + "mnemon_document_search", + "mnemon_forget", + "mnemon_link", + "mnemon_memory_bodies", + "mnemon_memory_body_create", + "mnemon_memory_body_merge", + "mnemon_memory_body_update", + "mnemon_recall", + "mnemon_related", + "mnemon_remember", + "mnemon_runtime_memory", + "mnemon_status", + "mnemon_view_action", + "mnemon_view_route" + ], + "sha256": "9a76b5225a98bfda5c239d8a0c4d8c2d90c654b150a6b1c2c40ac2f7fa1e0026" + }, + { + "event": "parent-tools", + "names": [ + "mnemon_document_create", + "mnemon_document_manage", + "mnemon_document_search", + "mnemon_forget", + "mnemon_link", + "mnemon_memory_bodies", + "mnemon_memory_body_create", + "mnemon_memory_body_merge", + "mnemon_memory_body_update", + "mnemon_recall", + "mnemon_related", + "mnemon_remember", + "mnemon_runtime_memory", + "mnemon_status", + "mnemon_view_action", + "mnemon_view_route" + ], + "sha256": "9a76b5225a98bfda5c239d8a0c4d8c2d90c654b150a6b1c2c40ac2f7fa1e0026" + }, + { + "event": "child-tools", + "names": [ + "mnemon_link", + "mnemon_memory_bodies", + "mnemon_memory_body_create", + "mnemon_memory_body_merge", + "mnemon_memory_body_update", + "mnemon_recall", + "mnemon_related", + "mnemon_remember", + "mnemon_subagent_result_c04cad1861f84ecd9af5fa32edf465af" + ], + "sha256": "51b1596e83a43ce7f9cf4b3c62161bb4641cd601881d7aba61219c10e76f4f6f" + }, + { + "event": "parent-tools", + "names": [ + "mnemon_document_create", + "mnemon_document_manage", + "mnemon_document_search", + "mnemon_forget", + "mnemon_link", + "mnemon_memory_bodies", + "mnemon_memory_body_create", + "mnemon_memory_body_merge", + "mnemon_memory_body_update", + "mnemon_recall", + "mnemon_related", + "mnemon_remember", + "mnemon_runtime_memory", + "mnemon_status", + "mnemon_view_action", + "mnemon_view_route" + ], + "sha256": "9a76b5225a98bfda5c239d8a0c4d8c2d90c654b150a6b1c2c40ac2f7fa1e0026" + } + ], + "fixed": [ + { + "event": "parent-tools", + "names": [ + "mnemon_document_create", + "mnemon_document_manage", + "mnemon_document_search", + "mnemon_forget", + "mnemon_link", + "mnemon_memory_bodies", + "mnemon_memory_body_create", + "mnemon_memory_body_merge", + "mnemon_memory_body_update", + "mnemon_recall", + "mnemon_related", + "mnemon_remember", + "mnemon_runtime_memory", + "mnemon_status", + "mnemon_subagent_result", + "mnemon_view_action", + "mnemon_view_route" + ], + "sha256": "bdd050ee49e46a614f74e46913e9658a705246b308c0df7407c22a4c65f897df" + }, + { + "event": "child-tools", + "names": [ + "mnemon_link", + "mnemon_memory_bodies", + "mnemon_memory_body_create", + "mnemon_memory_body_merge", + "mnemon_memory_body_update", + "mnemon_recall", + "mnemon_related", + "mnemon_remember", + "mnemon_subagent_result" + ], + "sha256": "b01c520da60b2e631963e1e8f4b8f0ba26026cd13b57e7a92ff17d20503dd28e" + }, + { + "event": "parent-tools", + "names": [ + "mnemon_document_create", + "mnemon_document_manage", + "mnemon_document_search", + "mnemon_forget", + "mnemon_link", + "mnemon_memory_bodies", + "mnemon_memory_body_create", + "mnemon_memory_body_merge", + "mnemon_memory_body_update", + "mnemon_recall", + "mnemon_related", + "mnemon_remember", + "mnemon_runtime_memory", + "mnemon_status", + "mnemon_subagent_result", + "mnemon_view_action", + "mnemon_view_route" + ], + "sha256": "bdd050ee49e46a614f74e46913e9658a705246b308c0df7407c22a4c65f897df" + }, + { + "event": "parent-tools", + "names": [ + "mnemon_document_create", + "mnemon_document_manage", + "mnemon_document_search", + "mnemon_forget", + "mnemon_link", + "mnemon_memory_bodies", + "mnemon_memory_body_create", + "mnemon_memory_body_merge", + "mnemon_memory_body_update", + "mnemon_recall", + "mnemon_related", + "mnemon_remember", + "mnemon_runtime_memory", + "mnemon_status", + "mnemon_subagent_result", + "mnemon_view_action", + "mnemon_view_route" + ], + "sha256": "bdd050ee49e46a614f74e46913e9658a705246b308c0df7407c22a4c65f897df" + }, + { + "event": "child-tools", + "names": [ + "mnemon_link", + "mnemon_memory_bodies", + "mnemon_memory_body_create", + "mnemon_memory_body_merge", + "mnemon_memory_body_update", + "mnemon_recall", + "mnemon_related", + "mnemon_remember", + "mnemon_subagent_result" + ], + "sha256": "b01c520da60b2e631963e1e8f4b8f0ba26026cd13b57e7a92ff17d20503dd28e" + }, + { + "event": "parent-tools", + "names": [ + "mnemon_document_create", + "mnemon_document_manage", + "mnemon_document_search", + "mnemon_forget", + "mnemon_link", + "mnemon_memory_bodies", + "mnemon_memory_body_create", + "mnemon_memory_body_merge", + "mnemon_memory_body_update", + "mnemon_recall", + "mnemon_related", + "mnemon_remember", + "mnemon_runtime_memory", + "mnemon_status", + "mnemon_subagent_result", + "mnemon_view_action", + "mnemon_view_route" + ], + "sha256": "bdd050ee49e46a614f74e46913e9658a705246b308c0df7407c22a4c65f897df" + } + ] +} diff --git a/dsh/docs/pr-assets/issue-240-runtime-archive/README.md b/dsh/docs/pr-assets/issue-240-runtime-archive/README.md new file mode 100644 index 000000000..db2dbed1e --- /dev/null +++ b/dsh/docs/pr-assets/issue-240-runtime-archive/README.md @@ -0,0 +1,69 @@ +# Runtime archive destination safety — issue #240 + +Base: `1e19caf` (main). Environment: macOS arm64, Node 25.1.0, pnpm 11.19.0, +published DSH 0.1.5-rc.1, real Mnemon CLI 0.2.7. All data is synthetic and disposable. + +## Reproduction and browser verification + +Run `pnpm build`, `pnpm --workspace-concurrency=4 -r build`, then +`pnpm e2e:serve --runtime-archive --strategy-extensions`. The harness prints a +loopback Hindsight endpoint. Enable Hindsight with that endpoint in Settings, +then open Runtime. All three optional Strategy enhancements are enabled together. +The actual Hindsight plugin, Source plugins, Host, transport, WebUI and storage +are used; the Hindsight HTTP service is controlled and records accepted writes. +No live model or external service is needed for the one-safe-destination path. + +1. Add `Archive fixture: ` followed by five copies of + `Keep the release gate and rollback notes. `: Runtime uses 226/300 bytes. +2. Add `Pending archive entry: ` followed by four copies of + `Keep exact source text. `. +3. Before the fix, the exact synchronous-commit error appears, the source remains + at 226 bytes, and Hindsight accepts one write. +4. Restart the same fixture with the fixed Host and retry twice: an actionable + preflight error appears, Runtime stays at 226 bytes, and the accepted-write + counter remains one (zero new writes). +5. In Memory Spaces, create and activate a Native space named `Safe runtime archive` + while keeping Hindsight active. Retry the pending entry: Native archives the + exact original and Runtime commits the pending entry at 118/300 bytes. + The Content page shows the original in Native. Hindsight still has no new writes. + +| Before | After preflight | After activating Native | +| --- | --- | --- | +| ![Before](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-240-runtime-archive/before-webui.png) | ![Preflight](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-240-runtime-archive/after-preflight-webui.png) | ![Native success](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-240-runtime-archive/after-native-webui.png) | + +## Automated checks and limits + +- The new unsafe-provider regression fails on the base with the original + `runtime archive write did not commit synchronously` error. +- Coordinator coverage includes async and no-forget preflight, mixed providers, + destination removal before writing, receipt failure, a later Provider failure, + cancellation, skipped-entry preservation, failed cleanup, and uncertain local commits. +- A composed Runtime + Holographic test verifies the original Runtime revision + survives a local commit failure and newly created cold content is removed. +- The real Native integration creates a disposable store, writes through a View, + recalls through the real CLI and forgets the result. +- Complete workspace verification and the standalone 16-plugin / 17-artifact + suite cover the published package composition and simultaneous enhancements. +- The Host package remains bounded at 1.28 MB unpacked (the recovery code exceeded + the previous 1.27 MB limit by approximately 1.8 KB). +- This does not promise a distributed transaction. Provider failures without + receipts can have unknown remote effects. Cleanup only owns proven-new ids; + skipped entries and possibly committed archives are retained. No live Hindsight + server, Windows build, or model-routing quality claim is made. + +## 中文记录 + +基于 `1e19caf`,使用真实 DSH WebUI、独立 Source/Provider 制品和 Mnemon CLI, +同时启用三个 Strategy 增强。Hindsight 使用记录写入次数的本地 HTTP 测试服务, +所有内容均为临时合成数据。 + +修复前,226/300 字节的热记忆在新增时触发原始同步提交错误,Hindsight 已接收一条 +写入,热记忆未变。修复后在相同数据上重试两次,前置校验拒绝异步目标,写入计数 +仍为一。保留 Hindsight 激活状态并新增、激活 Native 空间后,同一次操作成功归档 +完整原文,热记忆降至 118/300 字节;内容页可读取 Native 中的原文。 + +自动化覆盖异步与不可删除目标、混合 Provider、目标消失、回执错误、后续 Provider +失败、取消、既有条目保留、清理失败及本地提交结果不确定。真实 Runtime 与 +Holographic 组合验证本地提交失败后热记忆修订不变,新增冷条目被补偿清理。 +完整 workspace 验证、独立制品组合和真实 CLI 的创建、写入、召回、删除均有检查。 +远端请求未返回回执时不能承诺分布式原子性;既有条目和可能已提交的归档会被保留。 diff --git a/dsh/docs/pr-assets/issue-250-runtime-write-scope/README.md b/dsh/docs/pr-assets/issue-250-runtime-write-scope/README.md new file mode 100644 index 000000000..aea20501f --- /dev/null +++ b/dsh/docs/pr-assets/issue-250-runtime-write-scope/README.md @@ -0,0 +1,69 @@ +# Runtime archive write authority — issue #250 + +[简体中文](./README.zh-CN.md) | [Issue #250](https://github.com/omdsh-dev/dsh-mnemon/issues/250) | [Verification data](./verification.json) + +The baseline reproduces the reported sequence: an overflowing add fails while two eligible Native spaces are active, and retrying the same input in the next turn succeeds. The fixed build succeeds on the first turn, with both archived originals and the pending Runtime entry verified byte-for-byte. Product baseline: `6ad99cc1890714355e1bb0e9230f3fce674bfb73`. The implementation and reusable fixture are at `246a50e2ae3cd63ef2fa05354c1297c7c8de5d57`; only fixture code was copied into the baseline checkout. + +The 2026-09-14 environment uses macOS arm64, Node 25.1.0, pnpm 11.19.0, published DSH 0.1.5-rc.1 packages and the checksum-verified official Mnemon CLI 0.2.8. The Native release archive SHA-256 is `96159ad2fe8f0531b5a00ab009849614c234032146b95fa2db5aa726a8922e2d`. Each browser run owns disposable `DSH_HOME`, `MNEMON_DATA_DIR`, workspace and loopback model endpoint. All three optional Strategy plugins are enabled together: Scoped, Light context and Active capture. The script supplies model decisions; the actual View, tools, Source/Provider plugins, Native CLI, transport and WebUI execute normally. The displayed model selector does not imply a live model API call. + +## Reproduction + +Build the selected revision and run: + +```sh +pnpm build +pnpm --workspace-concurrency=4 -r build +MNEMON_CLI_PATH=/absolute/path/to/mnemon pnpm e2e:serve --runtime-write-scope --strategy-extensions +``` + +In a fresh Mnemon E2E conversation, send `archive-scope-250`. The [fixture](../../../scripts/fixtures/runtime-write-scope-model.mjs) performs seven real root tool calls: two Runtime adds, two delegated Native space creations, two delegated activations, and the overflowing pending add. Delegated tools inherit the initiating View. The fixture uses a 512-byte Working Memory projection limit and synthetic checkpoints; it never loads personal memory. Child requests complete only after the actual Source receipt, with a bounded request count. + +The first six calls succeed on the baseline. The seventh returns the exact reported error, without an unsupported-destinations suffix: + +```text +runtime memory archival requires an existing active writable Memory Space with exact writes and safe forget; activate a supported Memory Space or increase runtimeMemory.memoryLimitBytes +``` + +The status page confirms two Runtime entries, two of two Native spaces active, and zero archived memories. The pending add has not committed; both original Runtime entries remain. Sending `archive-scope-250 retry` in the next turn commits the pending checkpoint after archival. No space or Provider configuration changes between these attempts. + +| Failed first-turn add | Two active Native spaces, no archived memories | +|---|---| +| ![Original capacity error](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-250-runtime-write-scope/before-capacity-error.jpg) | ![Both Native spaces active](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-250-runtime-write-scope/before-two-active-spaces.jpg) | + +![The same pending input succeeds in the next turn](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-250-runtime-write-scope/before-next-turn-retry.jpg) + +## Cause and implementation + +The baseline Host filters archive destinations using `memoryBodyIds` from the View's recall grant. These active namespace pins are empty when the two spaces are created later in the same View. The owning Memory Spaces Source already permits `remember` into known namespaces and namespaces created by that View, so the Host's archive filter rejects otherwise authorized writes. + +The fix lets `body-directory` return an optional Source-owned write scope bound to the exact View and Source. It shares the Source's existing `remember` authority, validates the returned identity and namespace array, and rechecks authority and live capabilities before archive writes. An empty scope remains deny-all. Foreign-created namespaces are excluded until a new turn. Older Sources that omit the response retain the narrower pinned-read fallback. Error messages distinguish an empty directory, an empty or excluding write scope, and unsupported capabilities, report scope/counts, and explain that the pending input must be retried. + +There is no persistence-format, credential or storage-path change. Rollback retains all stored entries. Only the aggregate Host and Memory Spaces Source need patch releases; the changeset names both packages. + +## Fixed-side verification + +The same fixture on the fixed build succeeds in its first turn: seven root tool calls, four delegated children, one turn and eight steps. It needs no second-turn retry. The status page reports one Runtime entry, two active Native spaces and two archived memories. Runtime shows only the pending checkpoint at 269/512 projection bytes; the Content page shows one original checkpoint in each Native space. + +| First-turn success | Two archived memories in two active spaces | +|---|---| +| ![The first capacity add succeeds](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-250-runtime-write-scope/after-first-turn-success.jpg) | ![Two Native archives and one Runtime entry](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-250-runtime-write-scope/after-two-archived-spaces.jpg) | + +| Only the pending checkpoint remains in Runtime | Both exact originals are visible in Native | +|---|---| +| ![Pending Runtime checkpoint](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-250-runtime-write-scope/after-pending-runtime.jpg) | ![Original architecture and release checkpoints](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-250-runtime-write-scope/after-exact-native-content.jpg) | + +Readback with Native CLI 0.2.8 in `--readonly` mode returns exactly one item from each of the two stores. Their UTF-8 content bytes match the fixture's two `archiveScopeSaved` strings. Runtime contains exactly one entry whose UTF-8 bytes match `archiveScopePending`. The [verification data](./verification.json) records the aggregate counts, equality checks and hashes of the checked content and screenshots. This confirms the persisted result separately from the scripted model's success message. + +Automated verification on the implementation passes: + +- `MNEMON_NATIVE_TEST_CLI=/absolute/path/to/mnemon pnpm run verify`: Root 1,003 passed / 5 opt-in skips; Memory Spaces 169 passed / 1 Windows-only skip. Workspace types, deterministic builds, plugin suites, real Headless activation and package validation pass. +- `pnpm run verify:plugins`: 16 independent plugin repositories and 17 packed artifacts pass standalone checks, public SDK composition, real packed-Starter activation and simultaneous activation of the three enhancements. +- The Native 0.2.8 Host test archives two exact checkpoints to two same-View-created spaces and commits the pending add. No external model API is used. +- Regression cases cover known spaces activated after pinning, same-View creations, foreign creations and next-turn retry, explicit empty attenuation, malformed or foreign returned scopes, scope revocation, exact Source instance selection, cancellation, ended turns, unload with an existing lease, and conservative older-Source fallback. +- Four fixture tests prevent repeated child writes after a successful receipt, complete failed calls, bound missing-receipt attempts and use returned namespace ids for activation. + +The initial full verification reached the package-size guard after all earlier checks passed. The final artifact is 1,281,433 unpacked bytes versus 1,279,223 on the baseline, a 2,210-byte increase. The guard remains enabled at 1,284,000 bytes. The subsequent complete `verify` run passes. + +## Limits + +This proves a deterministic temporal authority mismatch and the baseline's next-turn recovery. It does not establish the exact timing of the original Windows report or prove a transient directory-read race. Model decisions are scripted, so these checks do not evaluate routing quality. No Windows, Electron or live third-party service is claimed. The record includes only synthetic screenshots and compact verification data; no full sessions, credentials or personal memories are copied. diff --git a/dsh/docs/pr-assets/issue-250-runtime-write-scope/README.zh-CN.md b/dsh/docs/pr-assets/issue-250-runtime-write-scope/README.zh-CN.md new file mode 100644 index 000000000..5384239bc --- /dev/null +++ b/dsh/docs/pr-assets/issue-250-runtime-write-scope/README.zh-CN.md @@ -0,0 +1,69 @@ +# Runtime 归档写入权限 — issue #250 + +[English](./README.md) | [Issue #250](https://github.com/omdsh-dev/dsh-mnemon/issues/250) | [验证数据](./verification.json) + +基线已复现报告中的失败顺序:两个符合能力要求的 Native 空间已激活,但越限新增失败;在下一回合重试相同输入后成功。修复版本在首回合即可成功,两条归档原文和待新增 Runtime 条目都通过逐字节核验。产品基线为 `6ad99cc1890714355e1bb0e9230f3fce674bfb73`。修复实现与可复用夹具位于 `246a50e2ae3cd63ef2fa05354c1297c7c8de5d57`;基线 checkout 仅复制了夹具代码。 + +2026-09-14 的环境为 macOS arm64、Node 25.1.0、pnpm 11.19.0、已发布的 DSH 0.1.5-rc.1 包,以及经过官方校验和验证的 Mnemon CLI 0.2.8。Native 发布压缩包 SHA-256 为 `96159ad2fe8f0531b5a00ab009849614c234032146b95fa2db5aa726a8922e2d`。每次浏览器运行使用独立的临时 `DSH_HOME`、`MNEMON_DATA_DIR`、工作区和本地模型端点,同时启用 Scoped、Light context、Active capture 三个可选 Strategy 插件。脚本只提供模型决策;View、工具、Source/Provider 插件、Native CLI、传输与 WebUI 均正常执行真实实现。界面显示的模型选择不代表调用了真实模型 API。 + +## 复现 + +构建所选 revision 后执行: + +```sh +pnpm build +pnpm --workspace-concurrency=4 -r build +MNEMON_CLI_PATH=/absolute/path/to/mnemon pnpm e2e:serve --runtime-write-scope --strategy-extensions +``` + +在全新的 Mnemon E2E 对话中发送 `archive-scope-250`。[夹具](../../../scripts/fixtures/runtime-write-scope-model.mjs) 执行七次真实根 Agent 工具调用:两次 Runtime 新增、两次委派 Native 空间创建、两次委派激活,以及一次越限新增。委派工具继承发起回合的 View。夹具采用 512 字节的工作记忆投影上限与合成检查点,不读取个人记忆。子 Agent 必须收到真实 Source 回执后才完成,且调用次数有上限。 + +基线的前六次调用成功,第七次返回报告中的原始错误,且没有 unsupported-destinations 后缀: + +```text +runtime memory archival requires an existing active writable Memory Space with exact writes and safe forget; activate a supported Memory Space or increase runtimeMemory.memoryLimitBytes +``` + +状态页确认 Runtime 保留两条条目、两个 Native 空间全部激活,归档记忆数量为零。待新增内容尚未提交,两条原有 Runtime 条目仍在。下一回合发送 `archive-scope-250 retry` 后,待新增检查点在完成归档后成功提交。两次尝试之间没有修改空间或 Provider 配置。 + +| 首回合越限新增失败 | 两个 Native 空间均激活,归档数量为零 | +|---|---| +| ![原始容量错误](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-250-runtime-write-scope/before-capacity-error.jpg) | ![两个 Native 空间均激活](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-250-runtime-write-scope/before-two-active-spaces.jpg) | + +![下一回合重试同一份待新增输入成功](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-250-runtime-write-scope/before-next-turn-retry.jpg) + +## 原因与实现 + +基线 Host 使用 View 召回授权中的 `memoryBodyIds` 过滤归档目标。当两个空间在同一 View 内稍后创建时,回合开始时固定的已激活命名空间范围仍为空。所属 Memory Spaces Source 的 `remember` 本来已经允许写入已知命名空间,以及该 View 创建的命名空间,因此 Host 的归档过滤拒绝了实际有写入权限的目标。 + +修复通过 `body-directory` 可选返回由 Source 定义、绑定精确 View 与 Source 的写入范围,与 Source 现有 `remember` 权限共用实现。Host 校验返回身份与命名空间数组,并在归档写入前复查权限和当前能力。空范围始终不授权任何目标;其他操作在回合开始后创建的命名空间仍被排除,需要新回合才能使用。未返回新字段的旧 Source 保留较窄的召回固定范围。错误会区分目录为空、写入范围为空或排除目标、Provider 能力不支持,附带范围与数量,并明确提示重试尚未提交的输入。 + +没有改变持久化格式、凭据或存储路径,回滚后保留所有已存条目。仅聚合 Host 与 Memory Spaces Source 需要 patch 发布,changeset 同时列出了这两个包。 + +## 修复侧验证 + +相同夹具在修复版本的首回合即成功:七次根工具调用、四个委派子 Agent、一个回合、八个步骤,无需下一回合重试。状态页显示一条 Runtime 条目、两个已激活 Native 空间和两条归档记忆。Runtime 仅保留待新增检查点,投影占用为 269/512 字节;内容页显示每个 Native 空间各有一条原始检查点。 + +| 首回合成功 | 两个已激活空间中各有一条归档 | +|---|---| +| ![第一次越限新增成功](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-250-runtime-write-scope/after-first-turn-success.jpg) | ![两条 Native 归档与一条 Runtime 条目](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-250-runtime-write-scope/after-two-archived-spaces.jpg) | + +| Runtime 仅保留待新增检查点 | Native 中显示两条完整原文 | +|---|---| +| ![待新增 Runtime 检查点](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-250-runtime-write-scope/after-pending-runtime.jpg) | ![架构与发布检查点原文](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-250-runtime-write-scope/after-exact-native-content.jpg) | + +使用 Native CLI 0.2.8 的 `--readonly` 模式从两个 store 各读取到且仅读取到一条内容,其 UTF-8 字节分别与夹具的两条 `archiveScopeSaved` 字符串完全一致。Runtime 恰有一条条目,其 UTF-8 字节与 `archiveScopePending` 完全一致。[验证数据](./verification.json) 保留汇总数量、相等性断言,以及所核验内容和截图的哈希。这项检查独立验证实际持久化结果,不依赖脚本模型的成功回复。 + +修复实现的自动化验证已通过: + +- `MNEMON_NATIVE_TEST_CLI=/absolute/path/to/mnemon pnpm run verify`:Root 1,003 项通过、5 项显式开启用例跳过;Memory Spaces 169 项通过、1 项 Windows 专属用例跳过。workspace 类型、确定性构建、插件测试、真实 Headless 激活与制品验证全部通过。 +- `pnpm run verify:plugins`:16 个独立插件仓库、17 个打包制品通过独立检查、公开 SDK 组合、真实 packed Starter 激活,以及三个增强同时激活验证。 +- Native 0.2.8 Host 测试将两条完整检查点分别归档到同一 View 创建的两个空间,并提交待新增内容,不调用外部模型 API。 +- 回归覆盖已知空间在固定后激活、同 View 创建、外部创建及下一回合重试、显式空范围收窄、返回范围格式错误或身份不符、权限撤销、精确 Source 实例选择、取消、回合结束、已有租约下卸载,以及旧 Source 的保守回退。 +- 四项夹具测试验证收到成功回执后不重复子 Agent 写入、失败调用能够结束、无回执尝试有次数上限,以及激活时使用实际返回的命名空间 id。 + +首次完整验证在前面所有检查通过后触发了制品大小上限。最终制品解包后为 1,281,433 字节,基线为 1,279,223 字节,增加 2,210 字节。大小检查仍启用,上限小幅调整到 1,284,000 字节。随后完整 `verify` 运行通过。 + +## 限制 + +本记录证明了可确定复现的时间顺序与权限范围不一致,以及基线在下一回合恢复的行为;不代表已经确定原始 Windows 报告中的具体时序,也不证明存在瞬时目录读取竞态。模型决策由脚本控制,不评估语义路由质量。不宣称已测试 Windows、Electron 或真实第三方服务。记录仅包含合成截图与精简验证数据,没有复制完整会话、凭据或个人记忆。 diff --git a/dsh/docs/pr-assets/issue-250-runtime-write-scope/verification.json b/dsh/docs/pr-assets/issue-250-runtime-write-scope/verification.json new file mode 100644 index 000000000..2ad547c78 --- /dev/null +++ b/dsh/docs/pr-assets/issue-250-runtime-write-scope/verification.json @@ -0,0 +1,115 @@ +{ + "date": "2026-09-14", + "issue": 250, + "status": "baseline-and-fixed-browser-cli-verified", + "baselineRevision": "6ad99cc1890714355e1bb0e9230f3fce674bfb73", + "implementationRevision": "246a50e2ae3cd63ef2fa05354c1297c7c8de5d57", + "environment": { + "os": "macOS arm64", + "node": "25.1.0", + "pnpm": "11.19.0", + "dsh": "0.1.5-rc.1", + "mnemon": "0.2.8", + "nativeReleaseArchiveSha256": "96159ad2fe8f0531b5a00ab009849614c234032146b95fa2db5aa726a8922e2d", + "model": "loopback fixture only", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "memoryLimitBytes": 512 + }, + "baseline": { + "rootToolCalls": 7, + "initialAdd": "rejected", + "runtimeEntriesAfterRejection": 2, + "activeNativeSpaces": 2, + "archivedMemoriesAfterRejection": 0, + "sameInputNextTurn": "committed-after-archival" + }, + "automated": { + "verify": "passed", + "rootTests": { + "passed": 1003, + "skipped": 5 + }, + "memorySpacesTests": { + "passed": 169, + "skipped": 1 + }, + "nativeTwoDestinationHostTest": "passed", + "standalonePluginRepositories": 16, + "packedArtifacts": 17, + "fixtureTestsPassed": 4, + "unpackedBytes": 1281433, + "baselineUnpackedBytes": 1279223, + "maximumUnpackedBytes": 1284000 + }, + "screenshots": [ + { + "file": "after-exact-native-content.jpg", + "bytes": 89486, + "sha256": "395d3d38c8321210b6dc1b1e3552ce3e70bc6b1b91a908115f511580021f906d" + }, + { + "file": "after-first-turn-success.jpg", + "bytes": 44149, + "sha256": "daccc762fb171c04876ac4e491bd4e8d37be38d0a9a6fa1a19ae7580aca92281" + }, + { + "file": "after-pending-runtime.jpg", + "bytes": 73965, + "sha256": "78dece5eec47abc0018ccae4fb493ac3c999760b3e8e74b3a1536f13a82ff8a2" + }, + { + "file": "after-two-archived-spaces.jpg", + "bytes": 64753, + "sha256": "47e4af0e667b0ca8fef960d2efec3e977bebd6900e89cb150f2f188aa1dacac4" + }, + { + "file": "before-capacity-error.jpg", + "bytes": 58511, + "sha256": "17d7e9cbd4932da7eb6f7ade1f300ce2b5764e32686b488d4390cbd681f0eaad" + }, + { + "file": "before-next-turn-retry.jpg", + "bytes": 62849, + "sha256": "47c91856a845bcb9f5f87d4b87f434467203d48122cbd7e60460b494b998b2d6" + }, + { + "file": "before-two-active-spaces.jpg", + "bytes": 64771, + "sha256": "dec5a3fe7dc9b6ff6fbcbe48d4d13d6d4d9c81c1fc0c7829357d496dde9b152e" + } + ], + "fixed": { + "revision": "246a50e2ae3cd63ef2fa05354c1297c7c8de5d57", + "rootToolCalls": 7, + "delegatedChildren": 4, + "turns": 1, + "steps": 8, + "firstTurnSucceeded": true, + "activeNativeSpaces": 2, + "archivedEntries": 2, + "runtimeEntries": 1, + "runtimeProjectionBytes": 269, + "cliReadOnly": true, + "savedContentByteExact": true, + "pendingContentByteExact": true, + "archivedContent": [ + { + "utf8Bytes": 209, + "sha256": "8d7834b2d1320bc0f8fd28db05a53c1263a6912d7b7f444428830d86e65499de" + }, + { + "utf8Bytes": 179, + "sha256": "477ddb25e7b7e6f0729f65c4dd637d64eeb1c0a0fc0cb2c76c95fa6451aab47b" + } + ], + "runtimeContent": { + "utf8Bytes": 269, + "sha256": "c9410b0ef083d5c0c4cd398fd8e057ed796fd667c0f6cffc57690e6f15547a10" + }, + "readbackJsonSha256": "888fe9e74435ede178ff002ce7a2f958aaf53b3f7f8bb6fb5acc2505ff805fe3" + } +} diff --git a/dsh/docs/pr-assets/issue-251-legacy-repair/README.md b/dsh/docs/pr-assets/issue-251-legacy-repair/README.md new file mode 100644 index 000000000..cc52c2686 --- /dev/null +++ b/dsh/docs/pr-assets/issue-251-legacy-repair/README.md @@ -0,0 +1,107 @@ +# Issue #251: explicit legacy Session recovery + +[简体中文](./README.zh-CN.md) | [Issue #251](https://github.com/omdsh-dev/dsh-mnemon/issues/251) | [Recovery procedure](../../en/guides/operations.md#dsh-015-compatibility-and-legacy-session-recovery) + +Reproduced from main `6ad99cc1890714355e1bb0e9230f3fce674bfb73` on 2026-09-14, using macOS arm64, Node 25.1.0, pnpm 11.19.0 and the published DSH 0.1.5-rc.1 packages. Only synthetic Session logs and disposable Profile, workspace and memory directories were used. The repair stays in the Starter's explicit maintenance CLI; it does not change Core/Source/Provider ownership, intercept history loading or modify DSH's frozen validator. + +## Before and after in the actual WebUI + +The [runtime fixture](../../../tests/fixtures/issue-251-legacy-v0.jsonl) extends the synthetic 0.1.2-produced history with the missing historical Runtime summary and an independent plugin's snapshot. Its session id, workspace, preset and model route were adapted to the disposable `scripts/serve-e2e.mjs` harness, then each line was encoded as a checksummed Zstandard frame. + +The old CLI reported two repaired messages. Opening its copy in the actual WebUI still failed with `user/message 7 source summary requires notice form`: `Runtime memory snapshot` remained. Repository commit `000ecc1568899a794ecb5f8ccf6f28a90c13894e` confirms this exact summary was emitted as `dsh-mnemon` / `recall` by `memorySnapshotMessage()`. + +The updated CLI reported three repairs. The original historical user request and assistant answer loaded. A new canary turn completed in the WebUI; after restarting the Host and reopening the Session, both turns remained readable. The selected configured model route used a loopback response server; no external model API was called. The original compressed backup SHA-256 remained `415d0a9470f9eb7301bcd02333d8be428b29082fe7b0ae74e43da43336022d3e`; the repaired copy was `2e69362ea5284683b933ef6b8f2e480486a723930a0fa262b657b80c63fa87e0`. + +| Before: old repair leaves Runtime summary | After: resume, restart and cold reopen | +|---|---| +| ![Historical summary refusal](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-before-runtime-summary.jpg) | ![Both conversation turns retained](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-after-cold-reopen.jpg) | + +A second WebUI artifact uses the [combined fixture](../../../tests/fixtures/issue-251-repairable-v0.jsonl): all three summaries, a compatible v2 descriptor and a packed row with empty string ID/name. The old repair still failed at Runtime summary 7. The updated CLI reported three summary repairs, one descriptor promotion and one packed row expanded into three chunks, with no blockers. The actual DSH loader migrated the copy, and the UI displayed the historical user request, assistant answer and expanded `synthetic_lookup {}` call with `Synthetic tool response.` Selecting the configured loopback `DeepSeek-V4-Flash` route restored the interactive composer. The preserved original hash is `94f7da957d213c896c4794603580c9e982670bcd725ecffeee241eab3ad709e5`; the repaired copy is `07db52eaacf6d45fc2d4c0a2e8931604674ce332c3a0f823aeed4d4aacaeed3b`. + +| Combined artifact before repair | After migration and tool-history rendering | +|---|---| +| ![Combined legacy history refused](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-combined-before.jpg) | ![Historical messages and expanded tool result](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-combined-after.jpg) | + +A canary turn also completed through the combined Session's actual WebUI. Cold reopening after a `SIGUSR2` Host restart retained the historical messages, expanded tool result and new exchange. The physical v3 log contained 43 rows. Structural comparison retained all five historical user/plugin messages apart from the three removed summary fields, both historical assistant messages, the durable tool call/result and the expanded stream. The original and repaired v0 hashes remained unchanged; external model calls remained zero. + +| Historical messages and tool output after cold reopen | Canary exchange retained after cold reopen | +|---|---| +| ![Combined history after Host restart](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-combined-cold-reopen.jpg) | ![Tool output and continued exchange after Host restart](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-combined-cold-new-turn.jpg) | + +Two further fixtures isolate null streaming names: [raw deltas](../../../tests/fixtures/issue-251-null-name-v0.jsonl) and [packed deltas](../../../tests/fixtures/issue-251-null-name-packed-v0.jsonl). Both retain valid durable tool identities and start with an empty argument fragment. The actual WebUI rejected the originals at the v1→v2 accumulator and packed decoder respectively; the preceding CLI revision `83d0c40` also refused copies. The updated CLI normalized three logical names in each, expanding only the packed row. Both histories then loaded, displayed the original user/assistant messages and tool result, and accepted a new canary turn through the loopback route. Each v3 log had 42 physical rows; structural comparison retained historical messages, durable calls/results and exact timed replay after the intended name normalization. Original and repaired v0 hashes remained unchanged, as recorded in [machine-readable evidence](./verification.json). + +| Null-name artifact before repair | After migration and history rendering | +|---|---| +| ![Raw null names refused](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-null-raw-before.jpg) | ![Raw null names repaired](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-null-raw-after.jpg) | +| ![Packed null names refused](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-null-packed-before.jpg) | ![Packed null names repaired](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-null-packed-after.jpg) | + +After a Host restart, both Sessions cold reopened in an independent Chrome test window against the same disposable Profile. Historical tool output and the earlier canary exchanges remained visible. The in-app browser had encountered a client-bundle load failure before Session selection; the bundle returned HTTP 200 and passed syntax checking. Chrome loaded the same Host normally, without changing Host or browser security configuration. + +| Raw null-name history after cold reopen | Packed null-name history after cold reopen | +|---|---| +| ![Raw history, tool result and canary after restart](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-null-raw-cold-reopen.jpg) | ![Packed history, tool result and canary after restart](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-null-packed-cold-reopen.jpg) | + +Two final fixtures cover the completed empty-ID chain: [recorded ID](../../../tests/fixtures/issue-251-existing-id-v0.jsonl) and [all four legacy families](../../../tests/fixtures/issue-251-all-legacy-v0.jsonl). They use consistent `deepseek-official` writer metadata, complete usage records and an independent plugin snapshot. The ID-only WebUI input expands the fixture's packed placeholders into equivalent raw deltas to isolate the durable-ID failure. The full combination retains packed null-name placeholders, all three summaries and descriptor v2. Session id, workspace and preset are adapted to the disposable Profile; original inputs are kept separately. + +CLI revision `2612856` refused both inputs without publishing output. The actual WebUI rejected the ID-only history at `assistant/message 27 ... id must be a non-empty string`; the full combination failed at summary 5. The updated CLI restored one chain and seven identity fields in each. The combination additionally repaired three summaries and one descriptor, expanded one packed row into two chunks, and normalized two names. Both histories displayed the original user/assistant messages and expanded tool result in Chrome. + +| Before the recorded-ID repair | After history and tool-output recovery | +|---|---| +| ![Empty durable tool identity](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-existing-id-before.jpg) | ![Recorded identity recovered](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-existing-id-after.jpg) | +| ![All four legacy shapes](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-all-legacy-before.jpg) | ![All four shapes repaired together](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-all-legacy-after.jpg) | + +Each Session then received a `legacy-replay-251` canary through the real WebUI with the configured loopback Flash route. The [test model](../../../scripts/fixtures/legacy-session-replay-model.mjs) validates the actual HTTP request: the historical `tool_calls[].id` and `tool_call_id` must equal the recorded `provider-existing-id-251`, the tool name and arguments must remain `synthetic_lookup` / `{}`, and the output must remain `Synthetic tool response with the original provider ID.` Missing, conflicting or freshly invented identities cannot produce the success response. + +Both canaries completed. After a Host restart, the historical messages, expanded tool result and new exchange reopened. The 42 physical v3 rows present before restart remained structurally identical; DSH appended one normal `session/end-seed` boundary, resulting in 43 rows. Original and repaired v0 hashes remained unchanged. The full combination's original SHA-256 is `02918e5bf3ddc83a1c7087fce7622ad46b91bd8c1987ffb423d2669ce276b281`; its repaired copy is `1e30311e7422a4eb6c6b3f5c679901b8dfe902205e6b287f9a7a49ab885ad7f6`. The [machine-readable record](./verification.json) includes both cases, cold artifact hashes and wire payload hashes. + +| ID-only history and canary after restart | All-four history and canary after restart | +|---|---| +| ![Recorded-ID cold reopen](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-existing-id-cold-reopen.jpg) | ![All-four cold reopen](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-all-legacy-cold-reopen.jpg) | + +The shared Starter baseline also loaded all three optional strategy extensions and reported the installed Native CLI 0.2.8. A separate disposable real-CLI create/write/keyword-recall/forget smoke passed. [Native status screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/baseline-native-status.jpg). These checks do not imply that this patch changes Native storage. + +## Audited transformations and boundaries + +| Shape | Verified behavior | +|---|---| +| Three historical Mnemon summaries | Remove only the recognized summary members; preserve bodies and other plugin sources. | +| Compatible descriptor v2 | Change only version 2 to 3 after checking the exact historical keys and every applicable frozen v3 constraint. | +| Packed tool deltas with empty string ID or name | Expand to their exact raw delta events, preserving logical sequence, timing, names and arguments. | +| Already raw empty string deltas outside a proven identity repair | Keep byte-identical; the actual released migration supports them. | +| Own null names in exact raw or packed tool deltas | Retain the field as `name:""`, preserving assembly and token timing; expand packed rows without changing IDs, logical coordinates or arguments. | +| Cleared durable identities with one original provider ID retained in the exact completed stream | Restore only that recorded identity after stream, advertisement, execution, result and related-reference checks. | +| Missing/conflicting identity candidates, unresolved owner references, incompatible descriptors, unknown or unsafe affected delta shapes | Report bounded line/event/path diagnostics, exit 1 and publish no output. | + +Descriptor v2 was found in published `dsh-subagent@0.1.1-rc.2`; the audited `0.1.2-alpha.2` and `0.1.2-rc.1` artifacts already use v3. Comparing `lib/types/descriptor.js` and the cold-resume code in `continuation.js` shows v3 adds optional `agentReasoningEffort`. Leaving that absent preserves the old declared composition. One-shot records permit only version/mode/provider and optional label. Continuable records permit label, a paired agentProvider/agentModel, persona and closed allow/deny tool filters. No field is trimmed, synthesized or discarded. Runtime defaults across different DSH releases are outside this equivalence claim. + +Historical `dsh-session@0.1.2-rc.1/lib/types/chunk-rows.js` accepts string placeholders and defines their exact expansion. The current physical decoder rejects packed empty IDs. Packed empty names can pass migration but then fail `expandAssistantStream`. In contrast, the actual v0→v3 path preserves raw empty-string deltas through `AssistantStreamAccumulator`. Tests therefore validate complete migration **and timed stream replay**, not just the exported standalone payload validator. + +For null names, the official `dsh-llm-deepseek@0.1.2-rc.1` emitter copies a non-undefined transport name without a string check. Both the old and current `BlockAssembler` assign names only when truthy, so null→empty produces the same state transition at every prefix. Both versions count a token when arguments are nonempty **or the name property is present**; retaining the property preserves TTFT, including an initial empty argument fragment. The current accumulator keeps empty-string names as raw records. A separate audit executed the published old and new assemblers across 36 prefixes and five variants, compared actual old/current Session statistics, and verified v3 publication, cold reopen and durable/owner metadata preservation. Packed null names were outside the old codec, but normalization followed by expansion matches that codec's decoding of the normalized row exactly. The repair uses closed field sets, safe coordinates and duplicate-key checks, without matching a later call or inventing a name. + +The published `dsh-llm-deepseek@0.1.2-rc.1` adapter can clear an earlier nonempty provider ID when a continuation explicitly supplies an empty or null ID. The current adapter treats those continuation values as no update. Independent probes executed both published adapters against the same synthetic SSE frames: restoring the earlier recorded ID made the old chunks equal the current output. The old official Session hot-write path also accepted the resulting broken durable chain, while cold seeding rejected it. The old agent loop records `tool/result.sourceEventSeqs` pointing to its `tool/call` event. + +Recovery selects the exact assistant stream through ordered provenance, checks block order, completion, usage, names and argument assembly, and pairs every affected advertisement with its execution and result. A single-call fallback can prove the result association when result provenance is absent; conflicting explicit provenance is refused. Text/reasoning and multiple independently provable calls are supported. Approval, PTC, copied-message, replacement and plugin identity references must be accounted for; unresolved ownership causes whole-file refusal. Nonempty chains that share the step are checked for internal consistency too. No ID is generated, and empty deltas before the first recorded candidate remain unchanged. + +Dropping deltas would lose timestamps, arguments and provenance. Deleting a null name property can change token timing; replacing its value with an empty string does not. Logs with no retained provider identity or an ambiguous association cannot be reconstructed from a new synthetic ID, so those inputs still refuse output. These limits are distinct from the four reported shape families covered by the positive migration and WebUI fixtures. + +The published v0→v1 migration `lib/index.js` is byte-identical in 0.1.5-rc.1 and 0.1.5-rc.2 (SHA-256 `15ae26b90310d83b1b90a5e7cad9e2f34282fddaba2f19f2fd2232382065603d`). Full execution here uses rc.1. No published `dsh@0.1.2-rc.2` was found in the registry; the reported source build cannot be identified without its commit. + +## Regression and reproduction + +[Repair tests](../../../tests/legacy-session-repair.spec.ts) exercise published JSONL persistence, v3 publication, cold reopen and exact timed stream expansion; strict descriptor gates; combined repairs; null-name prefix assembly and token timing; unrelated plugin preservation; raw and compressed idempotence; exclusive output creation; diagnostics and refusal; duplicate fields, malformed frames, unsafe coordinates and expanded-size limits. The [combined fixture](../../../tests/fixtures/issue-251-repairable-v0.jsonl) covers summaries, descriptors and string placeholders; the separate raw/packed null-name fixtures isolate that failure. [Machine-readable evidence](./verification.json). + +Final `pnpm verify` passed: 1,104 root tests and 323 independent plugin tests, with seven opt-in skips. The repair suite has 132 cases; ten fixture tests ensure invalid continuation identities or changed tool payloads cannot return a false WebUI success. Deterministic builds, types, docs, public entries, publint/attw and real Headless activation passed. The package contains 47 files: 293,317 packed bytes and 1,313,898 unpacked bytes, within the 1,318,000-byte guard. Host and Client bundle code is unchanged by this compatibility repair. + +Independent review checked 144 descriptor combinations, 125 packed coordinate boundaries, five null-name variants across 36 prefixes, and 28 recorded-ID boundary cases. All eight accepted identity cases also passed separate real migration, cold reopen and embedded-stream checks. A separate 300-chain preview smoke recovered 2,100 identity fields; its final output hash was stable. This scale preview is not a separate migration assertion. + +```sh +pnpm install --frozen-lockfile +pnpm exec vitest run tests/legacy-session-repair.spec.ts tests/lifecycle.spec.ts +pnpm run verify +node bin/repair-legacy-session.mjs --input tests/fixtures/issue-251-repairable-v0.jsonl +node bin/repair-legacy-session.mjs --input tests/fixtures/issue-251-null-name-v0.jsonl +node bin/repair-legacy-session.mjs --input tests/fixtures/issue-251-all-legacy-v0.jsonl +MNEMON_CLI_PATH=/absolute/path/to/mnemon pnpm e2e:serve --strategy-extensions --legacy-session-replay +``` + +The WebUI screenshots cover Runtime summary recovery, summary/descriptor/packed-string combinations, both null-name forms, recorded durable IDs and all four families together. Exact timed delta preservation is additionally verified by the published loader and stream replay tests. No Windows source build, live third-party Provider or production Session was tested. diff --git a/dsh/docs/pr-assets/issue-251-legacy-repair/README.zh-CN.md b/dsh/docs/pr-assets/issue-251-legacy-repair/README.zh-CN.md new file mode 100644 index 000000000..0ea62ac73 --- /dev/null +++ b/dsh/docs/pr-assets/issue-251-legacy-repair/README.zh-CN.md @@ -0,0 +1,107 @@ +# Issue #251:显式恢复旧会话 + +[English](./README.md) | [Issue #251](https://github.com/omdsh-dev/dsh-mnemon/issues/251) | [恢复流程](../../zh-CN/guides/operations.md#dsh-015-兼容与旧会话恢复) + +2026-09-14 从 main `6ad99cc1890714355e1bb0e9230f3fce674bfb73` 复现,环境为 macOS arm64、Node 25.1.0、pnpm 11.19.0 和已发布 DSH 0.1.5-rc.1。仅使用合成会话日志、一次性 Profile、工作区与记忆目录。修复位于 Starter 的显式维护 CLI,不改变 Core/Source/Provider 的职责,不拦截历史加载,不修改 DSH 冻结校验器。 + +## 真实 WebUI 前后验证 + +[Runtime 夹具](../../../tests/fixtures/issue-251-legacy-v0.jsonl) 在 0.1.2 生成的合成历史上增加遗漏的旧 Runtime summary 和另一插件的 snapshot。为一次性 `scripts/serve-e2e.mjs` 环境调整 session id、工作区、preset、模型路由,再将每行编码为带校验和的 Zstandard 帧。 + +旧 CLI 报告修复两条消息,但实际 WebUI 打开其副本仍报 `user/message 7 source summary requires notice form`,因为 `Runtime memory snapshot` 未被移除。仓库提交 `000ecc1568899a794ecb5f8ccf6f28a90c13894e` 的 `memorySnapshotMessage()` 确认该字符串曾以 `dsh-mnemon` / `recall` 写出。 + +新 CLI 报告修复三条消息,原始用户请求与助手回复正常加载。通过实际 WebUI 完成新的 canary 对话,重启 Host 并冷打开后两轮均保留。选择配置好的回环响应服务路由,未调用外部模型 API。原始压缩备份 SHA-256 始终为 `415d0a9470f9eb7301bcd02333d8be428b29082fe7b0ae74e43da43336022d3e`;修复副本为 `2e69362ea5284683b933ef6b8f2e480486a723930a0fa262b657b80c63fa87e0`。 + +| 修复前:旧工具遗漏 Runtime summary | 修复后:续写、重启、冷打开 | +|---|---| +| ![历史 summary 校验失败](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-before-runtime-summary.jpg) | ![两轮会话均保留](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-after-cold-reopen.jpg) | + +第二个 WebUI 制品使用[组合夹具](../../../tests/fixtures/issue-251-repairable-v0.jsonl),包含全部三个 summary、兼容 v2 descriptor,以及 ID/name 为空字符串的 packed 行。旧工具仍在 Runtime summary 7 处失败;新 CLI 报告修复三个 summary、提升一个 descriptor、将一条 packed 行展开为三个 chunk,无 blocker。真实 DSH 加载器迁移副本后,界面显示历史用户请求、助手回复,以及展开的 `synthetic_lookup {}` 与 `Synthetic tool response.`。选择配置好的回环 `DeepSeek-V4-Flash` 路由后恢复可交互输入框。原件 hash 保持为 `94f7da957d213c896c4794603580c9e982670bcd725ecffeee241eab3ad709e5`;修复副本为 `07db52eaacf6d45fc2d4c0a2e8931604674ce332c3a0f823aeed4d4aacaeed3b`。 + +| 组合制品修复前 | 迁移并渲染工具历史后 | +|---|---| +| ![组合旧历史被拒绝](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-combined-before.jpg) | ![历史消息与展开的工具结果](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-combined-after.jpg) | + +组合会话也通过真实 WebUI 完成 canary 续写。Host 经 `SIGUSR2` 重启后冷打开,历史消息、展开的工具结果与新对话均保留。物理 v3 日志包含 43 行;结构比较确认五条历史 user/plugin 消息仅删除三个 summary 字段,两条历史 assistant 消息、持久 tool/call 与 tool/result、展开后的 stream 均一致。原始及修复 v0 的 hash 均未改变,外部模型调用为零。 + +| 冷打开后的历史消息和工具输出 | 冷打开后保留的 canary 对话 | +|---|---| +| ![Host 重启后的组合历史](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-combined-cold-reopen.jpg) | ![Host 重启后的工具输出与续写对话](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-combined-cold-new-turn.jpg) | + +另两个夹具单独复现流式 null name:[raw delta](../../../tests/fixtures/issue-251-null-name-v0.jsonl) 与 [packed delta](../../../tests/fixtures/issue-251-null-name-packed-v0.jsonl)。两者保留有效持久工具身份,并以空参数片段开始。真实 WebUI 分别在 v1→v2 accumulator 与 packed decoder 拒绝原件;前一 CLI 版本 `83d0c40` 同样拒绝发布副本。新 CLI 各规范化三个逻辑 name,仅展开 packed 行。两段历史均正常加载,显示原始用户/助手消息与工具结果,并通过回环路由完成新 canary 对话。各 v3 日志有 42 个物理行;结构比较确认历史消息、持久调用/结果及按预期规范化后的精确带时间回放均保留。原始与修复 v0 的 hash 未变,详见[机器可读记录](./verification.json)。 + +| null-name 制品修复前 | 迁移并渲染历史后 | +|---|---| +| ![raw null name 被拒绝](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-null-raw-before.jpg) | ![raw null name 修复后](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-null-raw-after.jpg) | +| ![packed null name 被拒绝](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-null-packed-before.jpg) | ![packed null name 修复后](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-null-packed-after.jpg) | + +Host 重启后,独立 Chrome 测试窗口通过同一个一次性 Profile 冷打开了两段会话,历史工具输出与此前的 canary 对话均保留。内置浏览器在选择会话之前遇到 client bundle 加载错误;该 bundle 返回 HTTP 200,语法检查通过。Chrome 正常加载同一个 Host,未改动 Host 或浏览器安全配置。 + +| raw null-name 历史冷打开后 | packed null-name 历史冷打开后 | +|---|---| +| ![重启后的 raw 历史、工具结果与 canary](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-null-raw-cold-reopen.jpg) | ![重启后的 packed 历史、工具结果与 canary](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-null-packed-cold-reopen.jpg) | + +最后两个夹具覆盖完成后的空 ID 链:[已有 ID 链](../../../tests/fixtures/issue-251-existing-id-v0.jsonl)与[四类旧格式组合](../../../tests/fixtures/issue-251-all-legacy-v0.jsonl)。它们使用一致的 `deepseek-official` 写入器元数据、完整 usage 记录与另一插件的 snapshot。仅 ID 故障的 WebUI 输入将 packed 占位值等价展开为 raw delta,以单独复现持久 ID 错误;完整组合保留 packed null-name 占位值、全部三个 summary 和 descriptor v2。为一次性 Profile 调整 session id、工作区和 preset,原始输入另行保留。 + +CLI 版本 `2612856` 对两份输入均拒绝发布副本。真实 WebUI 在仅 ID 样本中报 `assistant/message 27 ... id must be a non-empty string`,完整组合则在 summary 5 处失败。更新后的 CLI 各恢复一条调用链、七个身份字段;组合样本还修复三个 summary、一个 descriptor,将一条 packed 行展开为两个 chunk,并规范化两个 name。Chrome 中两段历史均显示原始用户/助手消息与展开的工具结果。 + +| 已有 ID 恢复前 | 历史和工具输出恢复后 | +|---|---| +| ![持久工具身份为空](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-existing-id-before.jpg) | ![已有身份恢复后](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-existing-id-after.jpg) | +| ![四类旧格式组合](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-all-legacy-before.jpg) | ![四类格式一并修复](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-all-legacy-after.jpg) | + +随后在真实 WebUI 选择已配置的回环 Flash 路由,分别发送 `legacy-replay-251` canary。[测试模型](../../../scripts/fixtures/legacy-session-replay-model.mjs)核对实际 HTTP 请求:历史 `tool_calls[].id` 和 `tool_call_id` 必须等于原有 `provider-existing-id-251`,工具名与参数必须保持 `synthetic_lookup` / `{}`,输出必须保持 `Synthetic tool response with the original provider ID.`。身份缺失、冲突或临时生成时,测试不会返回成功响应。 + +两次 canary 均完成。重启 Host 后,历史消息、展开的工具结果和新对话都能重新打开。重启前的 42 个物理 v3 行逐结构保持相同;DSH 正常追加一条 `session/end-seed` 边界,因此冷打开后共 43 行。原始与修复 v0 的 hash 均未改变。四类组合的原始 SHA-256 为 `02918e5bf3ddc83a1c7087fce7622ad46b91bd8c1987ffb423d2669ce276b281`,修复副本为 `1e30311e7422a4eb6c6b3f5c679901b8dfe902205e6b287f9a7a49ab885ad7f6`;[机器可读记录](./verification.json)包含两个样本、冷打开制品与请求 payload 的 hash。 + +| 重启后的 ID 样本与 canary | 重启后的四类组合与 canary | +|---|---| +| ![已有 ID 冷打开](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-existing-id-cold-reopen.jpg) | ![四类组合冷打开](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/251-all-legacy-cold-reopen.jpg) | + +共享 Starter 基线还启用了三个可选 Strategy 扩展,实际状态页显示已安装 Native CLI 0.2.8。一次性真实 CLI 的创建、写入、关键词召回与删除冒烟通过。[Native 状态截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-251-legacy-repair/baseline-native-status.jpg)。这些检查不表示此补丁修改了 Native 存储。 + +## 契约审计与修复边界 + +| 形状 | 已验证行为 | +|---|---| +| 三个历史 Mnemon summary 字符串 | 仅删除匹配的 summary 成员;保留正文和其他插件 source。 | +| 兼容的 descriptor v2 | 检查严格的旧版字段集合及全部适用冻结 v3 约束后,仅将版本值由 2 改为 3。 | +| ID 或 name 为空字符串的 packed 工具 delta | 展开为原有 raw delta 事件,保留逻辑序号、时间、name 和参数。 | +| 不属于已证明身份恢复链的 raw 空字符串 delta | 保持字节不变;实际已发布迁移支持此形状。 | +| 严格 raw 或 packed 工具 delta 中自有的 null name | 保留字段为 `name:""`,维持组装和 token 计时;展开 packed 行时不改 ID、逻辑坐标或参数。 | +| 完整流中仍保留唯一原始 provider ID 的持久空身份链 | 验证流、广告调用、执行、结果和关联引用后,仅恢复该已有身份。 | +| 原始身份缺失或冲突、owner 引用未闭合、不兼容 descriptor、待修 delta 的未知结构或不安全坐标 | 输出有数量上限的行号/事件/字段路径诊断,退出码 1,不发布输出。 | + +已发布 `dsh-subagent@0.1.1-rc.2` 使用 descriptor v2;审计的 `0.1.2-alpha.2` 与 `0.1.2-rc.1` 已使用 v3。比较 `lib/types/descriptor.js` 和 `continuation.js` 的冷恢复代码可见,v3 增加可选 `agentReasoningEffort`;保持其缺省即可保留旧版声明的组合参数。One-shot 只允许 version/mode/provider 和可选 label;continuable 允许 label、成对 agentProvider/agentModel、persona 以及封闭 allow/deny toolFilter。不裁剪、生成或丢弃字段;不承诺不同 DSH 版本的运行时默认值相同。 + +旧版 `dsh-session@0.1.2-rc.1/lib/types/chunk-rows.js` 接受字符串占位值,并定义无损展开。当前物理解码器拒绝 packed 空 ID;packed 空 name 可能通过迁移却在 `expandAssistantStream` 回放时报错。实际 v0→v3 流程则通过 `AssistantStreamAccumulator` 保留 raw 空字符串 delta,因此测试同时检查完整迁移和带时间戳的 stream 回放,而不是仅调用独立 payload 校验函数。 + +官方 `dsh-llm-deepseek@0.1.2-rc.1` emitter 会直接复制非 undefined 的 transport name,未要求字符串。旧版与当前 `BlockAssembler` 均仅在 name 为真值时赋值,因此 null→空字符串在每个前缀上保持相同状态转换。两版均在参数非空或 name 属性存在时计入 token;保留属性可维持 TTFT,包括首个空参数片段。当前 accumulator 会将空字符串 name 保留为 raw 记录。独立审计执行了已发布新旧 assembler 的五种变体、36 个前缀,比较真实新旧会话统计,并验证 v3 发布、冷打开以及 durable/owner metadata 保留。旧 codec 本身拒绝 packed null name,但规范化后再展开的结果与旧 codec 对规范化行的解码完全相同。修复仅使用封闭字段集合、安全坐标与重复键检查,不关联后续调用或推测工具名。 + +已发布的 `dsh-llm-deepseek@0.1.2-rc.1` adapter 在后续片段显式提供空或 null ID 时,会清空此前的非空 provider ID;当前 adapter 将这类后续值视为不更新。独立探针将相同合成 SSE 帧送入两版正式 adapter,恢复此前记录的 ID 后,旧 chunk 与当前输出完全一致。旧官方 Session 的热写入路径也接受了这条损坏持久链,而冷 seed 会拒绝它。旧 agent loop 会用 `tool/result.sourceEventSeqs` 指向对应 `tool/call` 事件。 + +恢复通过有序 provenance 选择精确的助手流,核对块顺序、完成状态、usage、工具名和参数组装,并逐一对应广告调用、执行与结果。结果 provenance 缺省时,可用完整的唯一工具调用证明关联;已有但冲突的 provenance 则拒绝。允许 text/reasoning 与多条分别可证明的调用。Approval、PTC、复制消息、替换以及插件中的身份引用必须明确归属,否则整份副本拒绝输出;同一步内的非空调用链也检查内部一致性。不会生成 ID,首次有效候选出现前的空 delta 仍保持原值。 + +删除 delta 会丢失时间、参数和 provenance;删除 null name 属性可能改变 token 计时,将其值改为空字符串则不会。日志若未保留 provider 原始身份,或关联本身存在歧义,无法用新造 ID 还原,因此仍拒绝输出。这些限制与迁移及 WebUI 正例已覆盖的四类报告形状分别记录。 + +已发布 0.1.5-rc.1 与 0.1.5-rc.2 的 v0→v1 迁移 `lib/index.js` 字节一致,SHA-256 为 `15ae26b90310d83b1b90a5e7cad9e2f34282fddaba2f19f2fd2232382065603d`。完整执行环境使用 rc.1。注册表未找到已发布的 `dsh@0.1.2-rc.2`;报告中的 source build 仍需其提交号才能确定。 + +## 回归与复现 + +[修复测试](../../../tests/legacy-session-repair.spec.ts)覆盖已发布 JSONL 持久层、v3 发布、冷打开与精确的带时间 stream 回放、严格 descriptor 条件、组合修复、null name 各前缀组装与 token 计时、其他插件保留、普通/压缩幂等、独占输出、诊断与拒绝、重复字段、损坏帧、不安全坐标及展开大小上限。[组合夹具](../../../tests/fixtures/issue-251-repairable-v0.jsonl)覆盖 summary、descriptor 和字符串占位值;独立 raw/packed null-name 夹具单独复现该错误。[机器可读验证记录](./verification.json)。 + +最终 `pnpm verify` 通过:root 1,104 项与独立插件 323 项通过,七项 opt-in 测试跳过。修复专项有 132 项;另有十项夹具测试,确保错误的续写身份或被改动的工具正文不会得到虚假成功响应。确定性构建、类型、文档、公开入口、publint/attw 与真实 Headless 激活均通过。包包含 47 个文件,压缩大小 293,317 字节、展开大小 1,313,898 字节,位于 1,318,000 字节上限内。本兼容性修复未改动 Host 或 Client bundle 代码。 + +独立复审检查了 144 组 descriptor、125 组 packed 坐标边界、五种 null-name 变体的 36 个前缀,以及 28 组已有 ID 边界用例。八个被接受的身份正例又全部通过独立的真实迁移、冷打开与嵌入 stream 比对。另一次 300 调用链 preview 冒烟恢复了 2,100 个身份字段,最终产物 hash 稳定;这项规模 preview 不代表另一次迁移验证。 + +```sh +pnpm install --frozen-lockfile +pnpm exec vitest run tests/legacy-session-repair.spec.ts tests/lifecycle.spec.ts +pnpm run verify +node bin/repair-legacy-session.mjs --input tests/fixtures/issue-251-repairable-v0.jsonl +node bin/repair-legacy-session.mjs --input tests/fixtures/issue-251-null-name-v0.jsonl +node bin/repair-legacy-session.mjs --input tests/fixtures/issue-251-all-legacy-v0.jsonl +MNEMON_CLI_PATH=/absolute/path/to/mnemon pnpm e2e:serve --strategy-extensions --legacy-session-replay +``` + +WebUI 截图覆盖 Runtime summary 恢复、summary/descriptor/packed 字符串组合、两种 null-name 形状、已有持久 ID 与全部四类形状的组合;精确的 delta 时间保留另由真实已发布加载器与 stream 回放测试验证。未测试 Windows source build、外部真实 Provider 或生产会话。 diff --git a/dsh/docs/pr-assets/issue-251-legacy-repair/verification.json b/dsh/docs/pr-assets/issue-251-legacy-repair/verification.json new file mode 100644 index 000000000..3b84a5dd2 --- /dev/null +++ b/dsh/docs/pr-assets/issue-251-legacy-repair/verification.json @@ -0,0 +1,379 @@ +{ + "date": "2026-09-14", + "baseline": "6ad99cc1890714355e1bb0e9230f3fce674bfb73", + "dsh": "0.1.5-rc.1", + "node": "25.1.0", + "pnpm": "11.19.0", + "platform": "macOS arm64", + "nativeCli": "0.2.8", + "webui": { + "fixture": "tests/fixtures/issue-251-legacy-v0.jsonl", + "beforeOldCliRepairs": 2, + "afterCliRepairs": 3, + "beforeError": "user/message 7 source summary requires notice form", + "historyRestored": true, + "resumedThroughActualWebUI": true, + "hostRestartedAndColdReopened": true, + "loopbackModelOnly": true, + "originalSha256": "415d0a9470f9eb7301bcd02333d8be428b29082fe7b0ae74e43da43336022d3e", + "repairedSha256": "2e69362ea5284683b933ef6b8f2e480486a723930a0fa262b657b80c63fa87e0" + }, + "native": { + "realCliCreateWriteRecallForget": true, + "optionalStrategies": [ + "scoped", + "light-context", + "auto-capture" + ] + }, + "screenshots": [ + { + "path": "251-before-runtime-summary.jpg", + "bytes": 44155, + "sha256": "3d1cde0ad35ae361ce3082d1b6a11378b110c2e94982fd2d7fd43b60d49d1ac1" + }, + { + "path": "251-after-cold-reopen.jpg", + "bytes": 48254, + "sha256": "7f4bf72465a6f5ccfb436d45071af0bde3388bf093dfd1f60ef21f37395115e2" + }, + { + "path": "baseline-native-status.jpg", + "bytes": 62005, + "sha256": "77c67c5bcf180d3ae483e2ab49f718f8870a582ef2f168958ded6332d1d7ae93" + }, + { + "path": "251-combined-before.jpg", + "bytes": 46956, + "sha256": "c1e997bea6b1003e43e0fbd844474cc0fe5008e557d7230cffe8ef50410a5272" + }, + { + "path": "251-combined-after.jpg", + "bytes": 40096, + "sha256": "ac9e2f58e51d87ede85a117502a0c6c512ac8a48ab6118bea71b583d6efb79f6" + }, + { + "path": "251-combined-cold-reopen.jpg", + "bytes": 45923, + "sha256": "b56cd813c105fd634725b5f22085d93ebb3456a4e089041096b90a7e48b9f603" + }, + { + "path": "251-combined-cold-new-turn.jpg", + "bytes": 50060, + "sha256": "ce4b4beb4552830e5ddfdcdba0e028c9cebcbbff30b32a58c9646d3e1502be2c" + }, + { + "path": "251-null-raw-before.jpg", + "bytes": 44913, + "sha256": "b1b584b67c48dde589b5a06f77cb1a20fde637c93fb26a666456731c11a28bf7" + }, + { + "path": "251-null-raw-after.jpg", + "bytes": 40096, + "sha256": "90b4f73b57a555c15a58d691e0245a7ad53cbdf9aab346850da61da25525f926" + }, + { + "path": "251-null-packed-before.jpg", + "bytes": 45481, + "sha256": "9a975d864b35ec81814915e30a9019306c1656704b7e0904aec4d3935b6a9c88" + }, + { + "path": "251-null-packed-after.jpg", + "bytes": 39174, + "sha256": "1c23ad8259c03cb9d2ed3b5f003c8dc6f509c6214c459e5bb1ea55903221c339" + }, + { + "path": "251-null-raw-cold-reopen.jpg", + "bytes": 61308, + "sha256": "d23fdec11b157e46c4da66d9746523aa39adc4bc5778b12544152182a7442f15" + }, + { + "path": "251-null-packed-cold-reopen.jpg", + "bytes": 62199, + "sha256": "3b9660279b1475980a28f7be03b0dabff0925769758d2eb35e1d1761bc2afed6" + }, + { + "path": "251-existing-id-before.jpg", + "bytes": 57760, + "sha256": "d08399743517285597b8f0173d9e090b6d79ca38927a1024246c1dcc2c4397b9" + }, + { + "path": "251-existing-id-after.jpg", + "bytes": 48602, + "sha256": "edef105224310ca7bbcbcb21e1392d9878a585c7abf6d802199ba9b38c6e8284" + }, + { + "path": "251-existing-id-cold-reopen.jpg", + "bytes": 69786, + "sha256": "495d2843f4d3bdbafddc05a5741a7e536c3f315c2a082664d32baad62a45f3cb" + }, + { + "path": "251-all-legacy-before.jpg", + "bytes": 56817, + "sha256": "4dcf9266aa5304b584c25d40b53674a15f50c593eacb2fa29e507932eccf3180" + }, + { + "path": "251-all-legacy-after.jpg", + "bytes": 47571, + "sha256": "7e1fc7cf446bf4b3c403a40bf6a133386d9f23f597abc299c0e7d21cf9b18d57" + }, + { + "path": "251-all-legacy-cold-reopen.jpg", + "bytes": 69694, + "sha256": "2d9f9120f0cdb8ec2c08b37b3e63e132bc6cc1fa7d00b365d58d12f5e43c36d8" + } + ], + "limits": [ + "Real WebUI evidence covers all four reported legacy shape families, including combined repair, recorded-ID continuation wire validation and cold reopen. Exact timed delta preservation is additionally checked against the released loader and replay implementation.", + "Recovery requires a unique ID already recorded in the exact completed stream and proven call/result associations. Missing/conflicting identities, unsupported or unsafe shapes and unresolved related owner references refuse output.", + "Frozen v0 migration rc.1 and rc.2 artifacts are byte-identical; full execution used rc.1.", + "No production Sessions, external model APIs, Windows source build or live third-party Provider was tested." + ], + "validation": { + "fullVerifyPassed": true, + "rootPassed": 1104, + "rootSkipped": 5, + "pluginPassed": 323, + "pluginSkipped": 2, + "totalPassed": 1427, + "totalSkipped": 7, + "repairPassed": 132, + "headlessTotalTools": 39, + "headlessRepresentativeMnemonTools": 8, + "deterministicBuildFiles": 39, + "packageFiles": 47, + "packedBytes": 293317, + "unpackedBytes": 1313898, + "maximumUnpackedBytes": 1318000, + "publicNodeEntries": 12, + "publicTypeDependencies": 27, + "executableHelpEntries": 1, + "independentAudit": { + "descriptorCombinations": 144, + "packedBigIntBoundaryCases": 125, + "remainingFindings": 0, + "nullNameVariants": 5, + "nullNameComparedPrefixes": 36, + "oldAndCurrentStatsEquivalent": true, + "recordedIdentityCases": 28, + "recordedIdentityRealLoaderColdReopens": 8, + "recordedIdentityRemainingFindings": 0 + }, + "wireFixturePassed": 10 + }, + "combinedWebui": { + "fixture": "tests/fixtures/issue-251-repairable-v0.jsonl", + "beforeError": "user/message 7 source summary requires notice form", + "repair": { + "mode": "copy", + "format": 0, + "encoding": "zstd", + "repairedMessages": 3, + "repairedDescriptors": 1, + "expandedToolChunkRows": 1, + "expandedToolChunks": 3, + "inputSha256": "94f7da957d213c896c4794603580c9e982670bcd725ecffeee241eab3ad709e5", + "outputSha256": "07db52eaacf6d45fc2d4c0a2e8931604674ce332c3a0f823aeed4d4aacaeed3b", + "originalPreserved": true, + "migrationValidated": false, + "blockers": [] + }, + "originalHashVerified": true, + "repairedHashVerified": true, + "dshMigrationAndReplaySucceeded": true, + "historicalUserAndAssistantVisible": true, + "expandedToolCall": { + "name": "synthetic_lookup", + "arguments": "{}", + "result": "Synthetic tool response." + }, + "configuredLoopbackRouteSelected": "DeepSeek-V4-Flash", + "finalValidation": { + "baseRevision": "6ad99cc1890714355e1bb0e9230f3fce674bfb73", + "repairRevision": "e82735158ed763d3dcfd8d9052b678f6c96963e8", + "originalSha256": "94f7da957d213c896c4794603580c9e982670bcd725ecffeee241eab3ad709e5", + "repairedSha256": "07db52eaacf6d45fc2d4c0a2e8931604674ce332c3a0f823aeed4d4aacaeed3b", + "originalPreserved": true, + "repairedV0PreservedAfterMigration": true, + "migratedFormat": 3, + "physicalV3Rows": 43, + "historicalUserAndPluginMessagesPreservedApartFromThreeSummaryFields": 5, + "historicalAssistantMessagesPreserved": 2, + "durableToolCallAndResultPreserved": true, + "expandedStreamPreserved": true, + "newTurnContinued": true, + "coldReopenVerifiedInWebUI": true, + "externalModelCalls": 0, + "hostRestartSignal": "SIGUSR2" + } + }, + "nullNameWebui": { + "results": [ + { + "variant": "raw", + "originalSha256": "04890736fdc5e8d3c0e8331df219c3d7d625647165b4b0fbe5ec23a8bde06ded", + "repairedSha256": "90cd9f4e18ebad3db883c5c9b35a2ce7bedfe9d4c0487d14ee340083d9d52b8c", + "physicalV3Rows": 42, + "normalizedToolChunkNames": 3, + "firstDeltaHasOwnEmptyName": true, + "firstArgumentFragment": "", + "timedReplayMatchesNormalizedCopy": true, + "durableIdentityAndMessagesPreserved": true, + "continuedThroughRealWebUI": true, + "originalPreserved": true, + "coldReopenedThroughRealWebUI": true, + "coldWebUiBrowser": "Chrome", + "coldV3Sha256": "1f0c14019e06afde01a89c5ce806890f54ea5bb5b633d8ff5543e8a599f8e592", + "coldScreenshot": "251-null-raw-cold-reopen.jpg" + }, + { + "variant": "packed", + "originalSha256": "af9c1c6eee90336df0a27e079d2dce9ac028d53f3ee002b9d7e79bbd9a5d306e", + "repairedSha256": "ff79820f088acffdba3a42b5bce2cdf5af5312a2539f20761a06881252cda20f", + "physicalV3Rows": 42, + "normalizedToolChunkNames": 3, + "firstDeltaHasOwnEmptyName": true, + "firstArgumentFragment": "", + "timedReplayMatchesNormalizedCopy": true, + "durableIdentityAndMessagesPreserved": true, + "continuedThroughRealWebUI": true, + "originalPreserved": true, + "coldReopenedThroughRealWebUI": true, + "coldWebUiBrowser": "Chrome", + "coldV3Sha256": "84c21218ae381cbf43641b4409b76f01f82b4f23d5e44037cdc3965ddc03077f", + "coldScreenshot": "251-null-packed-cold-reopen.jpg" + } + ], + "environmentNote": "After a DSH Host restart preserving the disposable profile, the in-app browser reported a client bundle load error before session selection. The exact bundle returned HTTP 200 and passed syntax checking. A separate Chrome test tab loaded the same Host normally; both migrated histories, tool output and prior continuation canaries were reopened there. No Host or browser security configuration was changed.", + "beforeCliRevision": "83d0c40fe7d9e62d60e283a56d1a0a63e31018db" + }, + "recordedIdentityWebui": { + "results": [ + { + "variant": "existing-id", + "originalSha256": "5c2b140d9c60e8a71c5fde2ae2d150e1bdd77fae53c7641dc0efaf1717278983", + "repairedSha256": "85739141506083d559aec9a237c3f27b12295e3f55e3ddda9e28546eb8d0209a", + "v3Sha256": "4fa5ab121d5dbf534f688f4a9d5ac0e2eb8a250fd5afb895ce6ba887d83b95e1", + "physicalV3Rows": 42, + "existingProviderId": "provider-existing-id-251", + "originalPreserved": true, + "repairedV0Preserved": true, + "historicalUserAndPluginBodiesPreserved": true, + "historicalAssistantMessagesAndToolResultsPreserved": true, + "timedReplayMatchesRepairedCopy": true, + "continuedThroughRealWebUI": true, + "modelWireIdentityAndPayloadValidated": true, + "coldReopenedThroughRealWebUI": true, + "coldBrowser": "Chrome", + "coldHistoricalRowsUnchanged": true, + "coldAppendedBoundary": "session/end-seed", + "coldPhysicalV3Rows": 43, + "coldV3Sha256": "1a5d43a5d330098663972429670eb8ab71a8bb2311cf4c19110b81f76a1118ee", + "coldScreenshot": "251-existing-id-cold-reopen.jpg", + "repair": { + "mode": "copy", + "format": 0, + "encoding": "zstd", + "repairedMessages": 0, + "repairedDescriptors": 0, + "expandedToolChunkRows": 0, + "expandedToolChunks": 0, + "normalizedToolChunkNames": 0, + "recoveredToolIdentityChains": 1, + "recoveredToolIdentityFields": 7, + "toolIdentityRecoveryRefusals": { + "occurrences": 0, + "samples": [] + }, + "inputSha256": "5c2b140d9c60e8a71c5fde2ae2d150e1bdd77fae53c7641dc0efaf1717278983", + "outputSha256": "85739141506083d559aec9a237c3f27b12295e3f55e3ddda9e28546eb8d0209a", + "originalPreserved": true, + "migrationValidated": false, + "blockers": [] + }, + "beforeError": "assistant/message 27 message content[0] id must be a non-empty string" + }, + { + "variant": "all-legacy", + "originalSha256": "02918e5bf3ddc83a1c7087fce7622ad46b91bd8c1987ffb423d2669ce276b281", + "repairedSha256": "1e30311e7422a4eb6c6b3f5c679901b8dfe902205e6b287f9a7a49ab885ad7f6", + "v3Sha256": "aff2908436f065c25289a69d34ebe08d6b18ce7b7b71941baaa78c34eaef05a9", + "physicalV3Rows": 42, + "existingProviderId": "provider-existing-id-251", + "originalPreserved": true, + "repairedV0Preserved": true, + "historicalUserAndPluginBodiesPreserved": true, + "historicalAssistantMessagesAndToolResultsPreserved": true, + "timedReplayMatchesRepairedCopy": true, + "continuedThroughRealWebUI": true, + "modelWireIdentityAndPayloadValidated": true, + "coldReopenedThroughRealWebUI": true, + "coldBrowser": "Chrome", + "coldHistoricalRowsUnchanged": true, + "coldAppendedBoundary": "session/end-seed", + "coldPhysicalV3Rows": 43, + "coldV3Sha256": "4d5fdf7a0f9d0be7f8bbe23570a96c804f6de26efb058bed7edc06da881c5fc1", + "coldScreenshot": "251-all-legacy-cold-reopen.jpg", + "repair": { + "mode": "copy", + "format": 0, + "encoding": "zstd", + "repairedMessages": 3, + "repairedDescriptors": 1, + "expandedToolChunkRows": 1, + "expandedToolChunks": 2, + "normalizedToolChunkNames": 2, + "recoveredToolIdentityChains": 1, + "recoveredToolIdentityFields": 7, + "toolIdentityRecoveryRefusals": { + "occurrences": 0, + "samples": [] + }, + "inputSha256": "02918e5bf3ddc83a1c7087fce7622ad46b91bd8c1987ffb423d2669ce276b281", + "outputSha256": "1e30311e7422a4eb6c6b3f5c679901b8dfe902205e6b287f9a7a49ab885ad7f6", + "originalPreserved": true, + "migrationValidated": false, + "blockers": [] + }, + "beforeError": "user/message 5 source summary requires notice form" + } + ], + "wireReports": [ + { + "event": "legacy-replay-verified", + "callId": "provider-existing-id-251", + "toolCallCount": 1, + "toolResultCount": 1, + "argumentsSha256": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", + "resultSha256": "8306ed2cf00d4ad1484f5f155e24086b2196f6775dc0ba3a5734edb3035a0ed6" + }, + { + "event": "legacy-replay-verified", + "callId": "provider-existing-id-251", + "toolCallCount": 1, + "toolResultCount": 1, + "argumentsSha256": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", + "resultSha256": "8306ed2cf00d4ad1484f5f155e24086b2196f6775dc0ba3a5734edb3035a0ed6" + } + ], + "beforeCliRevision": "2612856624bd78cebcf02991e5313742df148f12", + "fixtures": [ + "tests/fixtures/issue-251-existing-id-v0.jsonl", + "tests/fixtures/issue-251-all-legacy-v0.jsonl" + ], + "existingIdPackedExpandedForIsolation": true, + "loopbackModelOnly": true, + "finalAuditedOutputsMatchTestedCopies": true, + "finalCliSha256": "98a0b2cd1b007f6f92db177793c5aee7d852b1bdeefa01f1846fef6e14d6086c", + "finalPreviewAndExclusiveCopiesMatchWebuiHashes": true, + "scaleSmoke": { + "chains": 300, + "inputBytes": 709334, + "recoveredFields": 2100, + "outputSha256": "2918324ec821ea9dbfe3a8ec4eccd60a1cd000ad9a0edc88186eda2bfd20f193", + "finalElapsedMs": 681, + "finalOutputHashMatchesEarlierPreview": true, + "previewOnly": true + } + } +} diff --git a/dsh/docs/pr-assets/issue-259-windows-git-console/README.md b/dsh/docs/pr-assets/issue-259-windows-git-console/README.md new file mode 100644 index 000000000..d28badf7a --- /dev/null +++ b/dsh/docs/pr-assets/issue-259-windows-git-console/README.md @@ -0,0 +1,80 @@ +# Windows Git probe console — issue #259 + +[简体中文](./README.zh-CN.md) | [Issue #259](https://github.com/omdsh-dev/dsh-mnemon/issues/259) | [Verification data](./verification.json) | [Windows observations](./windows-results.json) + +On 2026-09-17, the baseline Runtime Git probe produced visible console windows from a console-free Windows Node process. Adding `windowsHide: true` eliminated them in four measured trials. The command, timeout, branch result and failure fallback remain unchanged. Baseline: `6da061e8fa51a30ed50c6fff2a4cadcddf7a4a6a`; implementation: `1a9d455004c9204a74b87c920d81b67b3f7b954f`. + +## Windows reproduction + +The observer ran on Windows Server 2025, build 26100, with Node 22.19.0 / libuv 1.51.0 and Git 2.55.0.windows.5. A GUI executable starts Node with `DETACHED_PROCESS`; `AttachConsole` fails with error 6 for every A/B parent, confirming there is no inherited console. A positive control starts a visible console. Continuous `EnumWindows` observation counts new console-class windows only when visible, non-minimized, uncloaked and nonempty. + +| Run | Positive control | Baseline trial 1 / 2 | Fixed trial 1 / 2 | +|---|---:|---:|---:| +| [Primary observation](https://github.com/omdsh-dev/dsh-mnemon/actions/runs/35240525857) | 1 | 105 / 104 | 0 / 0 | +| [Screenshot observation](https://github.com/omdsh-dev/dsh-mnemon/actions/runs/35241111560) | 1 | 105 / 105 | 0 / 0 | + +Each trial makes 100 normal branch queries plus four Git calls for a padded path, detached HEAD, a non-repository and a missing path. All return-value assertions pass; absent/blank roots also retain the no-launch fallback. Counts are distinct visible windows, not process counts: each baseline trial shows 104 Terminal windows, with one additional classic console window in three trials. + +| Baseline: actual opening frame | Fixed: Git queries running | +|---|---| +| ![Visible baseline terminal opening frame](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-259-windows-git-console/windows-before.png) | ![No console window during fixed Git queries](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-259-windows-git-console/windows-after.png) | + +Git was not delayed to obtain the screenshot. The baseline frame precedes completed Terminal painting. The screenshot run temporarily minimized one preexisting runner console and restored it afterward. Capture sets focus, so these images do not independently prove focus theft. The zero-window result comes from continuous observation of the entire fixed phase, rather than one screenshot. + +The [pinned workflow and harness](https://github.com/omdsh-dev/dsh-mnemon/tree/b81ccd541e93644d3ae2106a8163acdce654534c/scripts/issue-259-windows) are on a validation-only branch. The workflow provides PowerShell 7, Node 22.19.0, Git, full Git history and `RUNNER_TEMP`. A manual run also needs an interactive Windows desktop and the Framework64 .NET C# compiler used by the script. Check out that revision with its baseline history, open PowerShell 7 at the checkout root, and provide a fresh temporary directory: + +```powershell +$env:RUNNER_TEMP = Join-Path $env:TEMP ("mnemon-259-" + [guid]::NewGuid()) +New-Item -ItemType Directory -Path $env:RUNNER_TEMP | Out-Null +./scripts/issue-259-windows/run.ps1 +``` + +The archive also contains the reusable observer, worker, runner and workflow. Both runs verified the downloaded source bytes against Git blobs `eca1a05ed7fc1b382ed31ccdcbadfbc800afba64` (baseline) and `340e4817a695d4807142e12d09bd70e792cec847` (fixed). + +The [downloadable evidence archive](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-259-windows-git-console/windows-evidence.tar.gz) preserves both successful runs beyond Actions retention. Its SHA-256 is `f976d55ec68f6b18322e2c9e7b391f3a9d4ab4c965f15db9da6a60ff3a996dea`; the [44-file manifest](./windows-archive-manifest.json) identifies every member. It contains synthetic observations, source blobs, screenshots and harness files; full Actions logs and unsuccessful harness setup attempts are excluded. + +## Real WebUI and CLI + +Separate clean worktrees ran both revisions on macOS arm64, Node 25.1.0, pnpm 11.19.0 and published DSH 0.1.5-rc.1. The official Mnemon CLI was 0.2.8; its executable SHA-256 is recorded in the verification data. Scoped, Light context and Active capture were enabled together. Each run used disposable DSH home, data and Git workspace directories. The loopback model fixture returns deterministic responses; the real Host, plugins, transport, Git, CLI and WebUI execute normally. The displayed Flash model name does not imply a live model API call. + +Build each revision and use the existing server with the [pass-through trace helper](./trace-git-probe.mjs): + +```sh +pnpm build +pnpm --workspace-concurrency=4 -r build +MNEMON_CLI_PATH=/absolute/path/to/mnemon \ +MNEMON_GIT_PROBE_TRACE=/tmp/git-probes.jsonl \ +MNEMON_GIT_PROBE_PHASE=before \ +NODE_OPTIONS=--import=/absolute/path/to/trace-git-probe.mjs \ +node scripts/serve-e2e.mjs --strategy-extensions +``` + +Use `after` for the fixed run. Initialize the printed disposable workspace as a Git repository with an empty commit on `webui-main`, then create `webui-other`. In the WebUI: + +1. Select that workspace, send a synthetic turn and rename the session `Git projection A`. +2. Add the two synthetic Runtime entries shown below, scoped respectively to `webui-main` and `webui-other`. +3. Create `Git projection B`, send a turn, switch B → A → B and reload the browser. +4. On the fixed build, check out `webui-other` and send another turn; detach HEAD and send another turn; restore `webui-main`, reload, and check the CLI version from Memory System → Status. + +Both revisions created the entries, completed conversations, switched sessions and restored the selected conversation after reload. The fixed build also completed the other-branch and detached-HEAD turns. The version dialog returned the actual CLI version 0.2.8; no update was installed. + +| Baseline Runtime writes | Fixed Runtime writes | +|---|---| +| ![Two baseline Runtime entries](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-259-windows-git-console/before-runtime.png) | ![Two fixed Runtime entries](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-259-windows-git-console/after-runtime.png) | + +| Baseline conversation switch | Fixed conversation switch | +|---|---| +| ![Baseline session B after switching](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-259-windows-git-console/before-conversation-switch.png) | ![Fixed session B after switching](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-259-windows-git-console/after-conversation-switch.png) | + +[Fixed detached-HEAD turn after reload](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-259-windows-git-console/after-detached-reload.png) · [Actual CLI version check](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-259-windows-git-console/after-cli-version.png) + +The [trace](./webui-git-probes.json) records two baseline calls without `windowsHide` and four fixed calls with `windowsHide: true`, including `webui-other` and detached HEAD. It passes the original arguments, options, return values and errors through. In this DSH/macOS fixture, actual probes occur on conversation turns; hot A/B switching and browser reload do not add probes. Runtime screenshots show persisted branch labels, not proof of model-context filtering; the existing projection tests cover that behavior. + +## Checks and scope + +- The new launch-options regression first failed against unchanged baseline source: 1 failed / 11 passed. After the change, all 12 focused tests and all 59 Runtime tests pass. +- `MNEMON_NATIVE_TEST_CLI=/absolute/path/to/mnemon pnpm run verify` passes: 1,162 root tests, all plugin suites, deterministic builds, types, real CLI/Native capacity checks, Headless startup/restart and package checks. Five root live-Flash checks, one live-OpenViking check and one macOS-inapplicable Windows test are skipped. +- `MNEMON_PLUGIN_VERIFY_CONCURRENCY=4 pnpm run verify:plugins --skip-build` passes for 16 independent plugin repositories and 17 artifacts, public SDK-only consumers, real packed-Starter activation and all three optional Strategies together. +- Documentation and changeset coverage pass. The Runtime patch changeset also schedules the aggregate Starter patch through its exact dependency. Host and Memory Spaces process runners already hide Windows consoles. + +Only Runtime's launch option changes in production. There is no DSH-source, protocol, Provider, storage, credential or migration change. This evidence establishes real Windows process behavior and macOS WebUI compatibility; it does not replay the reporter's complete Windows 11 Electron session-switch environment. No external model API or personal data is used. diff --git a/dsh/docs/pr-assets/issue-259-windows-git-console/README.zh-CN.md b/dsh/docs/pr-assets/issue-259-windows-git-console/README.zh-CN.md new file mode 100644 index 000000000..7db5a636a --- /dev/null +++ b/dsh/docs/pr-assets/issue-259-windows-git-console/README.zh-CN.md @@ -0,0 +1,80 @@ +# Windows Git 查询窗口 — Issue #259 + +[English](./README.md) | [Issue #259](https://github.com/omdsh-dev/dsh-mnemon/issues/259) | [验证数据](./verification.json) | [Windows 观测数据](./windows-results.json) + +2026-09-17 实测:无控制台的 Windows Node 进程执行原版 Runtime Git 查询时,会产生可见控制台窗口;添加 `windowsHide: true` 后,四轮观测均为零。命令、超时、分支结果与失败回退保持不变。基线为 `6da061e8fa51a30ed50c6fff2a4cadcddf7a4a6a`,修复实现为 `1a9d455004c9204a74b87c920d81b67b3f7b954f`。 + +## Windows 复现 + +环境为 Windows Server 2025 build 26100、Node 22.19.0 / libuv 1.51.0、Git 2.55.0.windows.5。GUI 观测程序以 `DETACHED_PROCESS` 启动 Node;每个 A/B 父进程的 `AttachConsole` 均返回错误 6,确认未继承控制台。阳性对照显式创建可见窗口。持续 `EnumWindows` 枚举只统计可见、未最小化、未被 DWM 隐藏且尺寸非零的新增控制台类窗口。 + +| 实验 | 阳性对照 | 修复前第 1 / 2 轮 | 修复后第 1 / 2 轮 | +|---|---:|---:|---:| +| [主观测](https://github.com/omdsh-dev/dsh-mnemon/actions/runs/35240525857) | 1 | 105 / 104 | 0 / 0 | +| [截图复验](https://github.com/omdsh-dev/dsh-mnemon/actions/runs/35241111560) | 1 | 105 / 105 | 0 / 0 | + +每轮执行 100 次正常分支查询,以及带空白的路径、分离 HEAD、非仓库目录、不存在目录共 4 次 Git 调用。所有返回值断言通过;空值与空白路径继续直接回退,不启动进程。计数是不同的可见窗口数,不是进程数:每轮基线都有 104 个 Terminal 窗口,其中三轮还捕获到 1 个传统控制台窗口。 + +| 修复前:真实窗口打开帧 | 修复后:Git 查询进行中 | +|---|---| +| ![原版 Git 查询产生的可见窗口](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-259-windows-git-console/windows-before.png) | ![修复后的 Git 查询没有可见控制台窗口](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-259-windows-git-console/windows-after.png) | + +未人为延长 Git 执行时间。修复前截图捕获的是 Terminal 尚未绘制完成的打开帧。截图复验临时最小化了一个既有 runner 控制台,完成后恢复。截图时设置了焦点,因此图片不能独立证明抢焦点;零窗口结论来自修复后整个阶段的持续枚举,而非单张截图。 + +[固定版本的工作流与验证脚本](https://github.com/omdsh-dev/dsh-mnemon/tree/b81ccd541e93644d3ae2106a8163acdce654534c/scripts/issue-259-windows)保存在独立验证分支。工作流提供 PowerShell 7、Node 22.19.0、Git、完整 Git 历史及 `RUNNER_TEMP`。手动运行还需要交互式 Windows 桌面,以及脚本使用的 Framework64 .NET C# 编译器。检出该版本并保留基线历史,在 checkout 根目录打开 PowerShell 7,指定全新的临时目录后运行: + +```powershell +$env:RUNNER_TEMP = Join-Path $env:TEMP ("mnemon-259-" + [guid]::NewGuid()) +New-Item -ItemType Directory -Path $env:RUNNER_TEMP | Out-Null +./scripts/issue-259-windows/run.ps1 +``` + +归档同时保留可复用的观测器、worker、runner 和工作流。两次实验都根据下载的源码字节校验了 Git blob:基线 `eca1a05ed7fc1b382ed31ccdcbadfbc800afba64`,修复后 `340e4817a695d4807142e12d09bd70e792cec847`。 + +[可下载证据归档](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-259-windows-git-console/windows-evidence.tar.gz)随 PR 保留两次成功实验,不依赖 Actions 的保留期限。SHA-256 为 `f976d55ec68f6b18322e2c9e7b391f3a9d4ab4c965f15db9da6a60ff3a996dea`;[44 个文件的清单](./windows-archive-manifest.json)逐项列出归档成员。内容为合成观测数据、源码、截图与验证脚本,不包含完整 Actions 日志或早期未成功的验证环境尝试。 + +## 真实 WebUI 与 CLI + +两个干净的独立 worktree 分别运行基线和修复版本,环境为 macOS arm64、Node 25.1.0、pnpm 11.19.0、正式发布的 DSH 0.1.5-rc.1。使用官方 Mnemon CLI 0.2.8,其可执行文件 SHA-256 记录在验证数据中。Scoped、Light context、Active capture 三个可选 Strategy 同时开启;DSH home、数据目录和 Git 工作区均为一次性目录。回环模型端点返回确定性内容,Host、插件、传输、Git、CLI 和 WebUI 均真实执行。界面中的 Flash 模型名称不代表调用了外部模型 API。 + +分别构建两个版本,使用现有服务器和[透传记录脚本](./trace-git-probe.mjs): + +```sh +pnpm build +pnpm --workspace-concurrency=4 -r build +MNEMON_CLI_PATH=/absolute/path/to/mnemon \ +MNEMON_GIT_PROBE_TRACE=/tmp/git-probes.jsonl \ +MNEMON_GIT_PROBE_PHASE=before \ +NODE_OPTIONS=--import=/absolute/path/to/trace-git-probe.mjs \ +node scripts/serve-e2e.mjs --strategy-extensions +``` + +修复后改用 `after`。将服务器输出的一次性 workspace 初始化为 Git 仓库,在 `webui-main` 建立空提交,再创建 `webui-other`。通过 WebUI 执行: + +1. 选择该工作区,发送合成内容,将会话命名为 `Git projection A`。 +2. 添加下图中的两条 Runtime 记忆,分别指定 `webui-main` 和 `webui-other` 分支。 +3. 创建 `Git projection B`,发送一轮内容,执行 B → A → B 切换并刷新浏览器。 +4. 在修复版本中检出 `webui-other` 后发送一轮,再分离 HEAD 并发送一轮;恢复 `webui-main`、刷新,通过“记忆系统 → 状态”检查 CLI 版本。 + +两版均成功写入记忆、完成对话、切换会话,并在刷新后恢复所选会话。修复版还完成了其他分支及分离 HEAD 下的对话。版本弹窗返回真实 CLI 版本 0.2.8,没有执行更新安装。 + +| 修复前 Runtime 写入 | 修复后 Runtime 写入 | +|---|---| +| ![基线的两条 Runtime 记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-259-windows-git-console/before-runtime.png) | ![修复后的两条 Runtime 记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-259-windows-git-console/after-runtime.png) | + +| 修复前会话切换 | 修复后会话切换 | +|---|---| +| ![基线切换回会话 B](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-259-windows-git-console/before-conversation-switch.png) | ![修复后切换回会话 B](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-259-windows-git-console/after-conversation-switch.png) | + +[分离 HEAD 对话刷新后仍保留](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-259-windows-git-console/after-detached-reload.png) · [真实 CLI 版本检查](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-259-windows-git-console/after-cli-version.png) + +[查询记录](./webui-git-probes.json)包含基线两次未设置 `windowsHide` 的调用,以及修复后四次 `windowsHide: true` 的调用,覆盖 `webui-other` 与分离 HEAD。记录脚本透传原始参数、选项、返回值和错误。在该 DSH/macOS 环境中,实际查询发生于对话回合;热切换 A/B 和浏览器刷新未增加查询次数。Runtime 截图证明分支标签已持久化,不用于证明模型上下文过滤;该行为由已有投影测试覆盖。 + +## 检查与范围 + +- 新增启动选项回归测试先在未改动的基线源码上失败:1 项失败、11 项通过。修改后 12 项专项测试及 59 项完整 Runtime 测试全部通过。 +- `MNEMON_NATIVE_TEST_CLI=/absolute/path/to/mnemon pnpm run verify` 通过:根目录 1,162 项测试、所有插件测试、确定性构建、类型检查、真实 CLI/Native 容量检查、Headless 启动及重启、包检查。跳过 5 项需要显式开启的真实 Flash 测试、1 项真实 OpenViking 测试及 1 项不适用于 macOS 的 Windows 测试。 +- `MNEMON_PLUGIN_VERIFY_CONCURRENCY=4 pnpm run verify:plugins --skip-build` 通过:16 个独立插件仓库、17 个制品、仅使用公开 SDK 的消费者、真实 Starter 制品激活及三个可选 Strategy 同时激活。 +- 文档和 changeset 覆盖检查通过。Runtime 的 patch changeset 会通过精确依赖联动 Starter patch。Host 与 Memory Spaces 的进程 runner 原本已隐藏 Windows 控制台。 + +生产代码仅改变 Runtime 的启动选项,不涉及 DSH 源码、协议、Provider、存储、凭据或迁移。本记录证明真实 Windows 进程行为与 macOS WebUI 兼容性,不声称已完整重放报告者的 Windows 11 Electron 会话切换环境。未使用外部模型 API 或个人数据。 diff --git a/dsh/docs/pr-assets/issue-259-windows-git-console/trace-git-probe.mjs b/dsh/docs/pr-assets/issue-259-windows-git-console/trace-git-probe.mjs new file mode 100644 index 000000000..99df92e98 --- /dev/null +++ b/dsh/docs/pr-assets/issue-259-windows-git-console/trace-git-probe.mjs @@ -0,0 +1,36 @@ +import childProcess from 'node:child_process' +import { appendFileSync } from 'node:fs' +import { syncBuiltinESMExports } from 'node:module' + +const execute = childProcess.execFileSync +childProcess.execFileSync = function (file, args, options) { + const observed = file === 'git' && Array.isArray(args) + && args.length === 4 && args[0] === '-C' + && args[2] === 'branch' && args[3] === '--show-current' + const record = { + phase: process.env.MNEMON_GIT_PROBE_PHASE, + platform: process.platform, + pid: process.pid, + timestamp: new Date().toISOString(), + windowsHide: options?.windowsHide ?? null, + timeout: options?.timeout ?? null, + stdio: options?.stdio ?? null, + } + try { + const output = Reflect.apply(execute, this, [file, args, options]) + if (observed && process.env.MNEMON_GIT_PROBE_TRACE) { + appendFileSync(process.env.MNEMON_GIT_PROBE_TRACE, JSON.stringify({ + ...record, outcome: 'success', branch: String(output).trim(), + }) + '\n') + } + return output + } catch (error) { + if (observed && process.env.MNEMON_GIT_PROBE_TRACE) { + appendFileSync(process.env.MNEMON_GIT_PROBE_TRACE, JSON.stringify({ + ...record, outcome: 'error', code: error?.code ?? null, + }) + '\n') + } + throw error + } +} +syncBuiltinESMExports() diff --git a/dsh/docs/pr-assets/issue-259-windows-git-console/verification.json b/dsh/docs/pr-assets/issue-259-windows-git-console/verification.json new file mode 100644 index 000000000..8b8722638 --- /dev/null +++ b/dsh/docs/pr-assets/issue-259-windows-git-console/verification.json @@ -0,0 +1,139 @@ +{ + "date": "2026-09-17", + "issue": 259, + "baseline": "6da061e8fa51a30ed50c6fff2a4cadcddf7a4a6a", + "implementation": "1a9d455004c9204a74b87c920d81b67b3f7b954f", + "webui": { + "platform": "macOS arm64", + "node": "25.1.0", + "pnpm": "11.19.0", + "dsh": "0.1.5-rc.1", + "model": "loopback deterministic fixture; no external model API", + "cli": { + "version": "0.2.8", + "sha256": "f8f21151ce9777983b8d2dfb9cbea1a00b223de6063659af232d04aa57cedad0" + }, + "optionalStrategiesEnabled": [ + "scoped", + "light-context", + "auto-capture" + ], + "bothRevisions": { + "conversationsCreated": 2, + "runtimeEntriesCreated": 2, + "switchSequence": [ + "B", + "A", + "B" + ], + "browserReloadPassed": true + }, + "fixedAdditionalBranches": [ + "webui-other", + "detached HEAD" + ], + "fixedCliVersionCheckPassed": true, + "gitProbesBefore": 2, + "gitProbesAfter": 4, + "caveat": "Actual Git probes occurred on conversation turns. Hot A/B switching and browser reload did not add probes in this DSH/macOS fixture. Windows console reproduction is separately observed in the Windows harness." + }, + "checks": { + "newRegressionBefore": { + "passed": 11, + "failed": 1 + }, + "newRegressionAfter": { + "passed": 12, + "failed": 0 + }, + "runtimeSuite": { + "passed": 59, + "failed": 0 + }, + "rootSuite": { + "passed": 1162, + "skipped": 5, + "failed": 0 + }, + "rootSkips": "opt-in live Flash quality/stress", + "pluginSkips": [ + "one Memory Spaces Windows-only test on macOS", + "one opt-in live OpenViking test" + ], + "nativeCapacityTests": 43, + "independentPluginRepositories": 16, + "packedArtifacts": 17, + "packedStarterActivation": true, + "allThreeOptionalStrategiesTogether": true, + "headlessTools": 39, + "package": { + "files": 47, + "packedBytes": 298499, + "unpackedBytes": 1335667, + "unpackedBudget": 1338000 + } + }, + "changeScope": "Runtime process launch option only; no protocol, storage or DSH-source change", + "files": { + "README.md": { + "bytes": 8106, + "sha256": "c9d6449afd4ebc29d7271ffc8a6ca1e6150807f6955f07903bc1d448f7a277d3" + }, + "README.zh-CN.md": { + "bytes": 7764, + "sha256": "0e7996eb6fbda19e6ac7c29044c3e261e9ab4120e68f90a31d5d22c215e98151" + }, + "after-cli-version.png": { + "bytes": 97421, + "sha256": "f23b3776d3f3165b567f70152c4aacbeec26312c3850537124e7af63f96292d3" + }, + "after-conversation-switch.png": { + "bytes": 44293, + "sha256": "5e179450679bb4641c1e3226d2d091ca23bf87d348fbae4f83e793f5bb08a824" + }, + "after-detached-reload.png": { + "bytes": 64461, + "sha256": "bef9c9903ffe736f5089ded3ed43d6a04b1965b39f71755fadb85b55dd916291" + }, + "after-runtime.png": { + "bytes": 80407, + "sha256": "cc6a547d8470aef8ae0edb59c112696b320d5788afa5e080ca961203decd7bf1" + }, + "before-conversation-switch.png": { + "bytes": 44958, + "sha256": "d1901089d310710be4a3a2ed8b3f7fe718969d298682f7993c94a61d525fd2aa" + }, + "before-runtime.png": { + "bytes": 81454, + "sha256": "123e8d4994e268e3c83cfe566b62dc29783564e1d1b10b3f41aff2083e6f7cd9" + }, + "trace-git-probe.mjs": { + "bytes": 1298, + "sha256": "b524a9bada30937c308ff6cf3f54b34185f5e5ccfb122259981be0c1da834242" + }, + "webui-git-probes.json": { + "bytes": 1712, + "sha256": "280d90c9f3627f85fb9523883611651c32fbd6517acce12c15cc7acfc8a8d235" + }, + "windows-after.png": { + "bytes": 786057, + "sha256": "011de9e35297b8c14a2112e0e7b17f9b7d9bf1f06c1d1ca396b58059f43b4134" + }, + "windows-archive-manifest.json": { + "bytes": 3722, + "sha256": "de4f2584209651d3647827ab1a7f95cc4cc8a9cb7f4df285cd74c9f1c49749cd" + }, + "windows-before.png": { + "bytes": 786383, + "sha256": "760c89932db104c71f4a07424f3bcd3eb2ef2273cdc2e553e401d39b854b10cb" + }, + "windows-evidence.tar.gz": { + "bytes": 3308741, + "sha256": "f976d55ec68f6b18322e2c9e7b391f3a9d4ab4c965f15db9da6a60ff3a996dea" + }, + "windows-results.json": { + "bytes": 12164, + "sha256": "c0f810298ef0a0a63e380f6a5e831f9caa12f21782e61a6d09c2de0c4eb07f62" + } + } +} diff --git a/dsh/docs/pr-assets/issue-259-windows-git-console/webui-git-probes.json b/dsh/docs/pr-assets/issue-259-windows-git-console/webui-git-probes.json new file mode 100644 index 000000000..ad4a71c43 --- /dev/null +++ b/dsh/docs/pr-assets/issue-259-windows-git-console/webui-git-probes.json @@ -0,0 +1,92 @@ +[ + { + "phase": "before", + "platform": "darwin", + "pid": 74494, + "timestamp": "2026-09-17T15:21:11.231Z", + "windowsHide": null, + "timeout": 2000, + "stdio": [ + "ignore", + "pipe", + "ignore" + ], + "outcome": "success", + "branch": "webui-main" + }, + { + "phase": "before", + "platform": "darwin", + "pid": 74494, + "timestamp": "2026-09-17T15:24:44.214Z", + "windowsHide": null, + "timeout": 2000, + "stdio": [ + "ignore", + "pipe", + "ignore" + ], + "outcome": "success", + "branch": "webui-main" + }, + { + "phase": "after", + "platform": "darwin", + "pid": 92855, + "timestamp": "2026-09-17T15:30:29.562Z", + "windowsHide": true, + "timeout": 2000, + "stdio": [ + "ignore", + "pipe", + "ignore" + ], + "outcome": "success", + "branch": "webui-main" + }, + { + "phase": "after", + "platform": "darwin", + "pid": 92855, + "timestamp": "2026-09-17T15:31:43.478Z", + "windowsHide": true, + "timeout": 2000, + "stdio": [ + "ignore", + "pipe", + "ignore" + ], + "outcome": "success", + "branch": "webui-main" + }, + { + "phase": "after", + "platform": "darwin", + "pid": 92855, + "timestamp": "2026-09-17T15:33:10.714Z", + "windowsHide": true, + "timeout": 2000, + "stdio": [ + "ignore", + "pipe", + "ignore" + ], + "outcome": "success", + "branch": "webui-other" + }, + { + "phase": "after", + "platform": "darwin", + "pid": 92855, + "timestamp": "2026-09-17T15:33:36.977Z", + "windowsHide": true, + "timeout": 2000, + "stdio": [ + "ignore", + "pipe", + "ignore" + ], + "outcome": "success", + "branch": "" + } +] diff --git a/dsh/docs/pr-assets/issue-259-windows-git-console/windows-archive-manifest.json b/dsh/docs/pr-assets/issue-259-windows-git-console/windows-archive-manifest.json new file mode 100644 index 000000000..ab8641fec --- /dev/null +++ b/dsh/docs/pr-assets/issue-259-windows-git-console/windows-archive-manifest.json @@ -0,0 +1,182 @@ +{ + "path": "windows-evidence.tar.gz", + "sha256": "f976d55ec68f6b18322e2c9e7b391f3a9d4ab4c965f15db9da6a60ff3a996dea", + "files": [ + { + "path": "report.md", + "bytes": 7264 + }, + { + "path": "README.md", + "bytes": 3548 + }, + { + "path": "compact-results.json", + "bytes": 12374 + }, + { + "path": "summarize.py", + "bytes": 2721 + }, + { + "path": "harness/Observer.cs", + "bytes": 13165 + }, + { + "path": "harness/issue-259-windows-validation.yml", + "bytes": 859 + }, + { + "path": "harness/run.ps1", + "bytes": 5272 + }, + { + "path": "harness/worker.mjs", + "bytes": 2008 + }, + { + "path": "run-35240525857/baseline-1-ready", + "bytes": 4 + }, + { + "path": "run-35240525857/baseline-1-result.json", + "bytes": 395 + }, + { + "path": "run-35240525857/baseline-2-ready", + "bytes": 4 + }, + { + "path": "run-35240525857/baseline-2-result.json", + "bytes": 387 + }, + { + "path": "run-35240525857/baseline.ts", + "bytes": 871 + }, + { + "path": "run-35240525857/fixed-1-ready", + "bytes": 4 + }, + { + "path": "run-35240525857/fixed-1-result.json", + "bytes": 380 + }, + { + "path": "run-35240525857/fixed-2-ready", + "bytes": 4 + }, + { + "path": "run-35240525857/fixed-2-result.json", + "bytes": 380 + }, + { + "path": "run-35240525857/fixed.ts", + "bytes": 896 + }, + { + "path": "run-35240525857/metadata.json", + "bytes": 996 + }, + { + "path": "run-35240525857/positive-control-first-window.png", + "bytes": 24611 + }, + { + "path": "run-35240525857/positive-control-ready", + "bytes": 4 + }, + { + "path": "run-35240525857/positive-control-result.json", + "bytes": 130 + }, + { + "path": "run-35240525857/summary.txt", + "bytes": 671 + }, + { + "path": "run-35240525857/windows-observation.json", + "bytes": 55846 + }, + { + "path": "run-35241111560/baseline-1-desktop.png", + "bytes": 786383 + }, + { + "path": "run-35241111560/baseline-1-ready", + "bytes": 4 + }, + { + "path": "run-35241111560/baseline-1-result.json", + "bytes": 387 + }, + { + "path": "run-35241111560/baseline-2-desktop.png", + "bytes": 786397 + }, + { + "path": "run-35241111560/baseline-2-ready", + "bytes": 4 + }, + { + "path": "run-35241111560/baseline-2-result.json", + "bytes": 387 + }, + { + "path": "run-35241111560/baseline.ts", + "bytes": 871 + }, + { + "path": "run-35241111560/fixed-1-desktop.png", + "bytes": 786057 + }, + { + "path": "run-35241111560/fixed-1-ready", + "bytes": 4 + }, + { + "path": "run-35241111560/fixed-1-result.json", + "bytes": 380 + }, + { + "path": "run-35241111560/fixed-2-desktop.png", + "bytes": 786057 + }, + { + "path": "run-35241111560/fixed-2-ready", + "bytes": 4 + }, + { + "path": "run-35241111560/fixed-2-result.json", + "bytes": 389 + }, + { + "path": "run-35241111560/fixed.ts", + "bytes": 896 + }, + { + "path": "run-35241111560/metadata.json", + "bytes": 996 + }, + { + "path": "run-35241111560/positive-control-desktop.png", + "bytes": 150320 + }, + { + "path": "run-35241111560/positive-control-ready", + "bytes": 4 + }, + { + "path": "run-35241111560/positive-control-result.json", + "bytes": 130 + }, + { + "path": "run-35241111560/summary.txt", + "bytes": 671 + }, + { + "path": "run-35241111560/windows-observation.json", + "bytes": 56066 + } + ] +} diff --git a/dsh/docs/pr-assets/issue-259-windows-git-console/windows-results.json b/dsh/docs/pr-assets/issue-259-windows-git-console/windows-results.json new file mode 100644 index 000000000..2eac2cee1 --- /dev/null +++ b/dsh/docs/pr-assets/issue-259-windows-git-console/windows-results.json @@ -0,0 +1,377 @@ +{ + "issue": 259, + "runs": [ + { + "id": 35240525857, + "url": "https://github.com/omdsh-dev/dsh-mnemon/actions/runs/35240525857", + "metadata": { + "git": "git version 2.55.0.windows.5", + "baselineSha256": "1CAFEEB141E84721FD1E42AFB1A7F546593C32C6535CF5BFEDA12FA1ABB34473", + "baselineBlob": "eca1a05ed7fc1b382ed31ccdcbadfbc800afba64", + "fixedRevision": "1a9d455004c9204a74b87c920d81b67b3f7b954f", + "runnerImageVersion": "20260907.229.1", + "baselineRevision": "6da061e8fa51a30ed50c6fff2a4cadcddf7a4a6a", + "fixedBlob": "340e4817a695d4807142e12d09bd70e792cec847", + "validationRevision": "f5979b854b22a47c1dd77434bb7cacc2a40e5ce7", + "runnerImage": "win25-vs2026", + "gitPath": "C:\\Program Files\\Git\\bin\\git.exe", + "node": "v22.19.0", + "nodePath": "C:\\hostedtoolcache\\windows\\node\\22.19.0\\x64\\node.exe", + "operatingSystem": { + "Caption": "Microsoft Windows Server 2025 Datacenter", + "Version": "10.0.26100", + "BuildNumber": "26100" + }, + "fixedSha256": "B66C292271C41361CA21214B673D89BB5FA208BC8DDCA683ADF5F8FC5487A00F", + "sourcePath": "plugins/dsh-mnemon-source-runtime/src/git-branch.ts" + }, + "observerConsoleHandle": 0, + "userInteractive": true, + "screenBounds": "{X=0,Y=0,Width=1024,Height=768}", + "sourceBlobsVerifiedFromArtifactBytes": true, + "phases": [ + { + "phase": "positive-control", + "flags": "CREATE_NEW_CONSOLE", + "checkedConsole": true, + "nodeHadConsole": true, + "attachError": 0, + "exitCode": 0, + "samples": 859, + "newVisibleConsoleWindowCount": 1, + "windowClasses": { + "CASCADIA_HOSTING_WINDOW_CLASS": 1 + }, + "probes": null, + "branch": null, + "behaviors": null, + "success": true, + "screenshot": null, + "screenshotObservation": null + }, + { + "phase": "baseline-1", + "flags": "DETACHED_PROCESS", + "checkedConsole": true, + "nodeHadConsole": false, + "attachError": 6, + "exitCode": 0, + "samples": 7594, + "newVisibleConsoleWindowCount": 105, + "windowClasses": { + "CASCADIA_HOSTING_WINDOW_CLASS": 104, + "ConsoleWindowClass": 1 + }, + "probes": 100, + "branch": "console-probe", + "behaviors": { + "padded": "console-probe", + "absent": null, + "blank": null, + "detached": null, + "nonRepository": null, + "nonexistent": null + }, + "success": true, + "screenshot": null, + "screenshotObservation": null + }, + { + "phase": "fixed-1", + "flags": "DETACHED_PROCESS", + "checkedConsole": true, + "nodeHadConsole": false, + "attachError": 6, + "exitCode": 0, + "samples": 1937, + "newVisibleConsoleWindowCount": 0, + "windowClasses": {}, + "probes": 100, + "branch": "console-probe", + "behaviors": { + "padded": "console-probe", + "absent": null, + "blank": null, + "detached": null, + "nonRepository": null, + "nonexistent": null + }, + "success": true, + "screenshot": null, + "screenshotObservation": null + }, + { + "phase": "baseline-2", + "flags": "DETACHED_PROCESS", + "checkedConsole": true, + "nodeHadConsole": false, + "attachError": 6, + "exitCode": 0, + "samples": 7359, + "newVisibleConsoleWindowCount": 104, + "windowClasses": { + "CASCADIA_HOSTING_WINDOW_CLASS": 104 + }, + "probes": 100, + "branch": "console-probe", + "behaviors": { + "padded": "console-probe", + "absent": null, + "blank": null, + "detached": null, + "nonRepository": null, + "nonexistent": null + }, + "success": true, + "screenshot": null, + "screenshotObservation": null + }, + { + "phase": "fixed-2", + "flags": "DETACHED_PROCESS", + "checkedConsole": true, + "nodeHadConsole": false, + "attachError": 6, + "exitCode": 0, + "samples": 1942, + "newVisibleConsoleWindowCount": 0, + "windowClasses": {}, + "probes": 100, + "branch": "console-probe", + "behaviors": { + "padded": "console-probe", + "absent": null, + "blank": null, + "detached": null, + "nonRepository": null, + "nonexistent": null + }, + "success": true, + "screenshot": null, + "screenshotObservation": null + } + ] + }, + { + "id": 35241111560, + "url": "https://github.com/omdsh-dev/dsh-mnemon/actions/runs/35241111560", + "metadata": { + "fixedSha256": "B66C292271C41361CA21214B673D89BB5FA208BC8DDCA683ADF5F8FC5487A00F", + "git": "git version 2.55.0.windows.5", + "fixedBlob": "340e4817a695d4807142e12d09bd70e792cec847", + "gitPath": "C:\\Program Files\\Git\\bin\\git.exe", + "node": "v22.19.0", + "baselineRevision": "6da061e8fa51a30ed50c6fff2a4cadcddf7a4a6a", + "baselineBlob": "eca1a05ed7fc1b382ed31ccdcbadfbc800afba64", + "validationRevision": "b81ccd541e93644d3ae2106a8163acdce654534c", + "runnerImageVersion": "20260907.229.1", + "runnerImage": "win25-vs2026", + "nodePath": "C:\\hostedtoolcache\\windows\\node\\22.19.0\\x64\\node.exe", + "sourcePath": "plugins/dsh-mnemon-source-runtime/src/git-branch.ts", + "baselineSha256": "1CAFEEB141E84721FD1E42AFB1A7F546593C32C6535CF5BFEDA12FA1ABB34473", + "fixedRevision": "1a9d455004c9204a74b87c920d81b67b3f7b954f", + "operatingSystem": { + "Caption": "Microsoft Windows Server 2025 Datacenter", + "Version": "10.0.26100", + "BuildNumber": "26100" + } + }, + "observerConsoleHandle": 0, + "userInteractive": true, + "screenBounds": "{X=0,Y=0,Width=1024,Height=768}", + "sourceBlobsVerifiedFromArtifactBytes": true, + "phases": [ + { + "phase": "positive-control", + "flags": "CREATE_NEW_CONSOLE", + "checkedConsole": true, + "nodeHadConsole": true, + "attachError": 0, + "exitCode": 0, + "samples": 879, + "newVisibleConsoleWindowCount": 1, + "windowClasses": { + "CASCADIA_HOSTING_WINDOW_CLASS": 1 + }, + "probes": null, + "branch": null, + "behaviors": null, + "success": true, + "screenshot": "run-35241111560/positive-control-desktop.png", + "screenshotObservation": { + "elapsedMs": 391.458, + "window": { + "handle": "2017E", + "pid": 6444, + "class": "CASCADIA_HOSTING_WINDOW_CLASS", + "process": "WindowsTerminal", + "title": "Windows Terminal", + "visible": true, + "minimized": false, + "cloaked": 0, + "bounds": [ + 44, + 52, + 1088, + 687 + ], + "firstMs": 107.9269, + "samples": 829, + "lastMs": 2074.918 + } + } + }, + { + "phase": "baseline-1", + "flags": "DETACHED_PROCESS", + "checkedConsole": true, + "nodeHadConsole": false, + "attachError": 6, + "exitCode": 0, + "samples": 3293, + "newVisibleConsoleWindowCount": 105, + "windowClasses": { + "CASCADIA_HOSTING_WINDOW_CLASS": 104, + "ConsoleWindowClass": 1 + }, + "probes": 100, + "branch": "console-probe", + "behaviors": { + "padded": "console-probe", + "absent": null, + "blank": null, + "detached": null, + "nonRepository": null, + "nonexistent": null + }, + "success": true, + "screenshot": "run-35241111560/baseline-1-desktop.png", + "screenshotObservation": { + "elapsedMs": 650.4541, + "window": { + "handle": "70150", + "pid": 6444, + "class": "CASCADIA_HOSTING_WINDOW_CLASS", + "process": "WindowsTerminal", + "title": "Terminal", + "visible": true, + "minimized": false, + "cloaked": 0, + "bounds": [ + 70, + 78, + 1114, + 713 + ], + "firstMs": 581.1235, + "samples": 10, + "lastMs": 684.8246 + } + } + }, + { + "phase": "fixed-1", + "flags": "DETACHED_PROCESS", + "checkedConsole": true, + "nodeHadConsole": false, + "attachError": 6, + "exitCode": 0, + "samples": 459, + "newVisibleConsoleWindowCount": 0, + "windowClasses": {}, + "probes": 100, + "branch": "console-probe", + "behaviors": { + "padded": "console-probe", + "absent": null, + "blank": null, + "detached": null, + "nonRepository": null, + "nonexistent": null + }, + "success": true, + "screenshot": "run-35241111560/fixed-1-desktop.png", + "screenshotObservation": { + "elapsedMs": 1097.5217, + "visibleConsoleWindows": [] + } + }, + { + "phase": "baseline-2", + "flags": "DETACHED_PROCESS", + "checkedConsole": true, + "nodeHadConsole": false, + "attachError": 6, + "exitCode": 0, + "samples": 2093, + "newVisibleConsoleWindowCount": 105, + "windowClasses": { + "CASCADIA_HOSTING_WINDOW_CLASS": 104, + "ConsoleWindowClass": 1 + }, + "probes": 100, + "branch": "console-probe", + "behaviors": { + "padded": "console-probe", + "absent": null, + "blank": null, + "detached": null, + "nonRepository": null, + "nonexistent": null + }, + "success": true, + "screenshot": "run-35241111560/baseline-2-desktop.png", + "screenshotObservation": { + "elapsedMs": 650.9873, + "window": { + "handle": "6D0178", + "pid": 6444, + "class": "CASCADIA_HOSTING_WINDOW_CLASS", + "process": "WindowsTerminal", + "title": "Terminal", + "visible": true, + "minimized": false, + "cloaked": 0, + "bounds": [ + 70, + 78, + 1114, + 713 + ], + "firstMs": 571.1632000000001, + "samples": 4, + "lastMs": 680.7619000000001 + } + } + }, + { + "phase": "fixed-2", + "flags": "DETACHED_PROCESS", + "checkedConsole": true, + "nodeHadConsole": false, + "attachError": 6, + "exitCode": 0, + "samples": 451, + "newVisibleConsoleWindowCount": 0, + "windowClasses": {}, + "probes": 100, + "branch": "console-probe", + "behaviors": { + "padded": "console-probe", + "absent": null, + "blank": null, + "detached": null, + "nonRepository": null, + "nonexistent": null + }, + "success": true, + "screenshot": "run-35241111560/fixed-2-desktop.png", + "screenshotObservation": { + "elapsedMs": 1066.5727, + "visibleConsoleWindows": [] + } + } + ] + } + ] +} diff --git a/dsh/docs/pr-assets/issue-261-dsh-slots/README.md b/dsh/docs/pr-assets/issue-261-dsh-slots/README.md new file mode 100644 index 000000000..de2f97a78 --- /dev/null +++ b/dsh/docs/pr-assets/issue-261-dsh-slots/README.md @@ -0,0 +1,50 @@ +# Issue 261: published DSH RC and alpha compatibility + +**English** | [简体中文](./README.zh-CN.md) | [Evidence index](../README.md) + +Recorded on 2026-09-18, macOS arm64, Node 25.1.0, Mnemon Native CLI 0.2.8. Issue: [#261](https://github.com/omdsh-dev/dsh-mnemon/issues/261). + +The baseline is the v0.5.11 release tree at `02ce994c8f05b6bf83a6b5872bd018bbfc8970ce`, merged into main as `245c9e2f6a50e6a77e7ba3c679ce07ba80458925`. The isolated fix branch started from that main commit. Production code tested here is `2a593c9a6f6510afc0e12704ff2103abc1a1f597` (including `60a481f`). The patched package still has development version 0.5.11; its integrity identifies the tested artifact, not a new npm release. The root-only patch changeset controls the next publication. + +## Reproduction and change + +On published DSH `0.1.6-alpha.2`, v0.5.11 completes actual Runtime add/status tools, but the browser reports `list slot "conversation.chat.turnTail" requires options.id`. Neither the turn memory bar nor the later-registered save action appears. See [original browser error](./baseline-browser-error.json) and the screenshots below. + +The fix supplies stable ID `dsh-mnemon/turn-tail`, retains the RC chain selector, and guards activity reads/rendering inside the component for the alpha list renderer. A full production type check also found three accesses to the removed `SessionListState.current`. Sidebar and reactive Settings now use the public default/main UI Session binding; Builtin and Better Sidebar preserve their explicit owning session, including an explicitly absent session. Source, Strategy and Provider implementations remain in their own packages. + +The two root DSH peer ranges now require `^0.1.5-rc.1 || ^0.1.6-alpha.2`. Older DSH releases without the required public UI Session service are not claimed compatible. Rolling back such a host requires its previously verified Mnemon release. No stored-data or configuration migration is introduced. + +## Observed results + +| Check | Release baseline | Fixed RC `0.1.5-rc.2` | Fixed alpha `0.1.6-alpha.2` | +|---|---|---|---| +| Full production public type graph | RC: 196 files, 0 errors; alpha: 196 files, 4 errors | 198 files, 0 errors | 198 files, 0 errors | +| Normal packed npm install / `npm ls --all` | Alpha needs the documented baseline-only bypass | Pass; 234 exact-cohort DSH records | Pass; 251 exact-cohort DSH records | +| Real WebUI Runtime add and status | Alpha tools succeed; bar/save action missing | Pass | Pass | +| Turn bar off / restored / after page reload | Missing on alpha | 0 / 1 / 1 | 0 / 1 / 1 | +| Other turn-tail contributions | No peer fixture | Two chain selectors decline, as configured | A and B each remain present while Mnemon is off, and each appears once after reload | +| Sidebar and Builtin tool-chip navigation | Not accepted on baseline | Correct Runtime record | Correct Runtime record | +| Save action | Absent on alpha | Correct final-answer candidate; cancelled | Correct final-answer candidate; cancelled | +| Packed Headless add/status, new-process persistence, Root disable | Alpha passes after protocol correction | 17 enabled tools, persisted bytes, 0 disabled tools | 17 enabled tools, persisted bytes, 0 disabled tools | + +Alpha also passed an actual session-scope check: after selecting workspace storage, a second session wrote one record in `workspace-b`; switching back to the original session showed zero records in `workspace`. The earlier global record remains in its original global store. The new-session screen had no Builtin tab. Scoped/absent session, pending cwd, stale activity responses, and ambiguous/foreign Builtin anchors have focused component regression tests; this browser record does not claim an exhaustive third-party split-pane test. + +`pnpm run verify` passed, including 1,183 root tests, the actual Native CLI create/write/read/forget integration, actual same-View Native archival through Host tools, deterministic builds, public entries and the RC1 Headless profile. `pnpm run verify:plugins --skip-build` passed all 16 independent plugins, 17 packed artifacts, external consumers, Starter upgrade from 0.4.7 and the three enabled Strategy extensions. Five opt-in live Flash tests, one live OpenViking integration and one Windows-only smoke test were skipped in this macOS run. Root package contents: 47 files, 299,146 packed bytes, 1,338,363 unpacked bytes; the checked budget is 1,339,000 bytes. + +## Screenshots and retained evidence + +Screenshots are original browser captures, without editing. All visible memory is synthetic test data. + +| Before: alpha registration failure | After: alpha, both peer contributions and Mnemon | +|---|---| +| ![Alpha before](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-261-dsh-slots/01-alpha-before-missing-turn-bar.png) | ![Alpha after](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-261-dsh-slots/02-alpha-after-peer-turn-bar.png) | + +Additional captures: [Sidebar Runtime record](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-261-dsh-slots/03-alpha-runtime-navigation.png), [Mnemon off with peers retained](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-261-dsh-slots/04-alpha-bar-off-peers-retained.png), [Builtin navigation](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-261-dsh-slots/05-alpha-builtin-navigation.png), [workspace B record](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-261-dsh-slots/06-alpha-workspace-b-memory.png), [workspace A isolated](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-261-dsh-slots/07-alpha-workspace-a-isolated.png), [RC completed turn](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-261-dsh-slots/08-rc-after-turn-bar.png). + +- [Browser observations](./browser-results.json), [type-check summary](./typecheck-results.json), and [verification/provenance summary](./verification.json). +- [Packed harness and reproduction commands](./harness/HARNESS.md), [artifact integrity and cohort results](./harness/evidence-summary.json). +- [Production type-check script](./check-published-types.mjs): run against a normal, exact-cohort public SDK installation. Install matching `dsh-client-store` and `dsh-client-ui-slots` declarations too. The check includes root and all plugin production sources and rejects foreign DSH declarations; no DSH source checkout is used. + +The loopback model fixture speaks OpenAI and Anthropic protocols and requests actual installed Mnemon tools. No external model API was called. The UI's model label is a fixture setting, not evidence of live Flash inference. Runtime add writes real Runtime Source JSON; it is distinct from the separately passed Native memory-space CLI integration. Saving through a distillation Agent was not exercised beyond opening/cancelling its confirmation. + +Only baseline alpha uses `--legacy-peer-deps`, because released 0.5.11 excludes alpha in its peers. Fixed installations use ordinary npm resolution. The first OpenAI-only alpha attempt failed because the fixture lacked Anthropic support; it is not classified as a product failure. Original public SlotCore, Chat, Renderer and Conversation bytes were verified unchanged. Unrelated subprocess services were disabled in the test profile; alpha consequently shows a terminal-restore warning. Extension-origin console messages were excluded explicitly; neither fixed cohort produced an application warning/error in the observed runs. This evidence covers mechanism compatibility, not model quality or live third-party Provider conformance. diff --git a/dsh/docs/pr-assets/issue-261-dsh-slots/README.zh-CN.md b/dsh/docs/pr-assets/issue-261-dsh-slots/README.zh-CN.md new file mode 100644 index 000000000..2c9c78037 --- /dev/null +++ b/dsh/docs/pr-assets/issue-261-dsh-slots/README.zh-CN.md @@ -0,0 +1,50 @@ +# Issue 261:正式发布的 DSH RC 与 alpha 兼容验证 + +**简体中文** | [English](./README.md) | [证据索引](../README.md) + +采集日期:2026-09-18。环境:macOS arm64、Node 25.1.0、Mnemon Native CLI 0.2.8。关联 [Issue #261](https://github.com/omdsh-dev/dsh-mnemon/issues/261)。 + +基线为 v0.5.11 发布树 `02ce994c8f05b6bf83a6b5872bd018bbfc8970ce`,对应 main 合并提交 `245c9e2f6a50e6a77e7ba3c679ce07ba80458925`。修复从该 main 创建独立 worktree。本记录实测生产代码为 `2a593c9a6f6510afc0e12704ff2103abc1a1f597`(包含 `60a481f`)。修复制品的开发版本仍为 0.5.11,以完整性哈希区分,不能将其视为新 npm 发布;下一次发布由仅涉及 Root 的 patch changeset 控制。 + +## 复现与修复 + +在正式 npm 的 DSH `0.1.6-alpha.2` 上,基线能够完成真实 Runtime 写入和状态工具,但浏览器报出 `list slot "conversation.chat.turnTail" requires options.id`;记忆条与随后注册的存入记忆入口均不出现。保留了[原始错误摘要](./baseline-browser-error.json)与下方对比截图。 + +修复为插槽增加稳定 ID `dsh-mnemon/turn-tail`,保留 RC 的 chain selector,并在组件内限制仅已完成回合读取、显示记忆活动。完整生产类型检查还发现三处旧 `SessionListState.current` 访问,因此 Sidebar 与响应式设置页改用公开的默认/主 UI Session binding;Builtin 和 Better Sidebar 保留显式所属会话,包括显式无会话状态。Source、Strategy 与 Provider 实现仍由各自独立包拥有。 + +Root 的两个 DSH peer 范围调整为 `^0.1.5-rc.1 || ^0.1.6-alpha.2`。缺少公开 UI Session 服务的旧版 DSH 不在本次支持范围;回滚旧宿主需要同时使用之前验证过的 Mnemon 版本。此修复不迁移配置或已有数据。 + +## 实测结果 + +| 检查 | 发布基线 | 修复后 RC `0.1.5-rc.2` | 修复后 alpha `0.1.6-alpha.2` | +|---|---|---|---| +| 完整生产源码公开类型图 | RC:196 文件、0 错误;alpha:196 文件、4 错误 | 198 文件、0 错误 | 198 文件、0 错误 | +| 正常 npm 制品安装与 `npm ls --all` | alpha 需要下文注明的基线专用绕过 | 通过;234 个同版本 DSH 记录 | 通过;251 个同版本 DSH 记录 | +| 真实 WebUI Runtime 写入与状态读取 | alpha 工具成功,但无记忆条/保存入口 | 通过 | 通过 | +| 关闭/恢复/页面重载后的记忆条数 | alpha 缺失 | 0 / 1 / 1 | 0 / 1 / 1 | +| 其他回合组件 | 未加载 peer 夹具 | 按夹具配置,两个 chain selector 均不展示 | Mnemon 关闭时 A、B 仍在;重载后各一份 | +| Sidebar 与 Builtin 工具入口跳转 | 基线不作为通过项 | 正确显示 Runtime 记录 | 正确显示 Runtime 记录 | +| 存入记忆入口 | alpha 缺失 | 候选为完整最终回复,随后取消 | 候选为完整最终回复,随后取消 | +| 制品 Headless 写入/状态、新进程持久化、禁用 Root | 修正夹具协议后 alpha 通过 | 启用 17 工具、数据字节不变、禁用后 0 工具 | 启用 17 工具、数据字节不变、禁用后 0 工具 | + +alpha 还进行了真实会话范围切换:设置为工作区存储后,在第二个会话的 `workspace-b` 写入一条记录,切回原会话的 `workspace` 显示零条,未借用 B 的数据。较早的全局记录仍保存在全局存储。新会话起始页面不显示 Builtin 标签。显式会话/无会话、等待 cwd、过期活动响应与歧义/其他会话的 Builtin 挂载点由针对性组件测试覆盖;本记录不声称穷尽了第三方分屏布局。 + +`pnpm run verify` 通过,包含 Root 1,183 项测试、真实 Native CLI 创建/写入/读取/遗忘、真实 Host 工具在同一 View 向 Native 归档、确定性构建、公开入口及 RC1 Headless。`pnpm run verify:plugins --skip-build` 通过 16 个独立插件、17 个制品、外部消费者、从 0.4.7 升级 Starter 与三个同时启用的 Strategy 扩展。此次 macOS 验证跳过 5 项需显式开启的真实 Flash 测试、1 项真实 OpenViking 集成及 1 项 Windows 专用 smoke。Root 制品为 47 文件、压缩后 299,146 字节、解压后 1,338,363 字节;大小预算为 1,339,000 字节。 + +## 截图与保留证据 + +截图均为未经编辑的浏览器原始采集,画面中的记忆均为合成测试数据。 + +| 修复前:alpha 注册失败 | 修复后:alpha 的两个 peer 与 Mnemon 同时存在 | +|---|---| +| ![alpha 修复前](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-261-dsh-slots/01-alpha-before-missing-turn-bar.png) | ![alpha 修复后](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-261-dsh-slots/02-alpha-after-peer-turn-bar.png) | + +补充截图:[Sidebar Runtime 记录](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-261-dsh-slots/03-alpha-runtime-navigation.png)、[关闭 Mnemon 后保留 peer](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-261-dsh-slots/04-alpha-bar-off-peers-retained.png)、[Builtin 跳转](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-261-dsh-slots/05-alpha-builtin-navigation.png)、[工作区 B 记录](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-261-dsh-slots/06-alpha-workspace-b-memory.png)、[工作区 A 隔离](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-261-dsh-slots/07-alpha-workspace-a-isolated.png)、[RC 完成回合](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-261-dsh-slots/08-rc-after-turn-bar.png)。 + +- [浏览器观测](./browser-results.json)、[类型检查摘要](./typecheck-results.json)、[验证与版本摘要](./verification.json)。 +- [制品验证脚本与复现命令](./harness/HARNESS.md)、[制品完整性与版本组合结果](./harness/evidence-summary.json)。 +- [生产类型检查脚本](./check-published-types.mjs):输入正常安装、版本完全一致的公开 SDK,另安装同版本 `dsh-client-store` 与 `dsh-client-ui-slots` 声明;覆盖 Root 和全部插件生产源码,并拒绝混入其他 DSH 声明,不引用 DSH 源码 checkout。 + +本地模型夹具支持 OpenAI 与 Anthropic 协议,并请求实际安装的 Mnemon 工具;未调用外部模型 API。界面的模型名称来自夹具配置,不代表真实 Flash 推理。Runtime 写入由真实 Runtime Source JSON 存储完成,与另行通过的 Native 记忆空间 CLI 集成有所区别;沉淀 Agent 仅验证确认入口及取消,没有执行模型沉淀。 + +仅基线 alpha 使用 `--legacy-peer-deps`,因为已发布 0.5.11 的 peer 不包含 alpha;修复版均正常解析 npm peer。最早仅支持 OpenAI 的 alpha 尝试因夹具不支持 Anthropic 而失败,未将其算作产品缺陷。公开 SlotCore、Chat、Renderer 与 Conversation 的字节均验证未改动。测试 Profile 关闭了无关子进程服务,因此 alpha 页头有终端恢复提示。明确排除了浏览器扩展来源的控制台消息;两个修复版本的观测均无应用来源警告或错误。本记录验证兼容机制,不评估模型质量或真实第三方 Provider 账号一致性。 diff --git a/dsh/docs/pr-assets/issue-261-dsh-slots/baseline-browser-error.json b/dsh/docs/pr-assets/issue-261-dsh-slots/baseline-browser-error.json new file mode 100644 index 000000000..4b79ebbb8 --- /dev/null +++ b/dsh/docs/pr-assets/issue-261-dsh-slots/baseline-browser-error.json @@ -0,0 +1,12 @@ +[ + { + "level": "warn", + "message": "dsh-mnemon: settings listener failed; keeping the published Host snapshot Error: list slot \"conversation.chat.turnTail\" requires options.id", + "timestamp": "2026-09-18T03:18:31.970Z" + }, + { + "level": "warn", + "message": "dsh-mnemon: settings listener failed; keeping the published Host snapshot Error: list slot \"conversation.chat.turnTail\" requires options.id", + "timestamp": "2026-09-18T03:28:34.522Z" + } +] diff --git a/dsh/docs/pr-assets/issue-261-dsh-slots/browser-results.json b/dsh/docs/pr-assets/issue-261-dsh-slots/browser-results.json new file mode 100644 index 000000000..ca636a6b5 --- /dev/null +++ b/dsh/docs/pr-assets/issue-261-dsh-slots/browser-results.json @@ -0,0 +1,55 @@ +{ + "recordedAt": "2026-09-18T03:41:21.480Z", + "baselineAlpha": { + "counts": { + "turnBars": 0, + "saveActions": 0 + }, + "realRuntimeAddAndStatus": true, + "missingIdError": "list slot \"conversation.chat.turnTail\" requires options.id" + }, + "fixedAlpha": { + "realRuntimeAddAndStatus": true, + "sidebarRuntimeNavigation": true, + "builtinRuntimeNavigation": true, + "saveActionCandidateVerifiedAndCancelled": true, + "offCounts": { + "turnBars": 0, + "peerA": 1, + "peerB": 1 + }, + "reloadCounts": { + "turnBars": 1, + "peerA": 1, + "peerB": 1 + }, + "noActiveSessionBuiltinTabAbsent": true, + "workspaceIsolation": { + "storage": "workspace", + "workspaceBRecordCount": 1, + "workspaceARecordCountAfterSwitch": 0 + }, + "applicationWarningsAndErrors": [], + "ignoredExtensionWarningCount": 6 + }, + "fixedRc": { + "realRuntimeAddAndStatus": true, + "sidebarRuntimeNavigation": true, + "builtinRuntimeNavigation": true, + "saveActionCandidateVerifiedAndCancelled": true, + "offTurnBars": 0, + "reloadCounts": { + "turnBars": 1, + "peerA": 0, + "peerB": 0 + }, + "applicationWarningsAndErrors": [], + "ignoredExtensionWarningCount": 13 + }, + "limits": [ + "Model responses use an explicit loopback fixture; tools, installed plugins, storage, and browser UI are real.", + "The save-action confirmation was opened and cancelled; no live model distillation was claimed.", + "The RC chain peer selectors deliberately decline rendering; alpha list peers render independently.", + "The alpha test profile disables unrelated subprocess services, causing a terminal restore warning in its header." + ] +} diff --git a/dsh/docs/pr-assets/issue-261-dsh-slots/check-published-types.mjs b/dsh/docs/pr-assets/issue-261-dsh-slots/check-published-types.mjs new file mode 100644 index 000000000..5d3ec572b --- /dev/null +++ b/dsh/docs/pr-assets/issue-261-dsh-slots/check-published-types.mjs @@ -0,0 +1,44 @@ +// Validate production sources against a clean, exact published DSH cohort. +// Usage: node check-published-types.mjs +// Install the matching dsh-client-store and dsh-client-ui-slots contracts too. +import assert from 'node:assert/strict' +import { readdir, readFile, writeFile, realpath } from 'node:fs/promises' +import { createRequire } from 'node:module' +import { resolve, join } from 'node:path' +assert.equal(process.argv.length, 5, 'Expected Mnemon checkout, public SDK installation root, and JSON report path') +const [rootArg, sdkArg, report] = process.argv.slice(2).map(value => resolve(value)) +const root = await realpath(rootArg) +const sdkRoot = await realpath(sdkArg) +const require = createRequire(join(root, 'package.json')) +const ts = require('typescript') +const base = JSON.parse(await readFile(join(root, 'tsconfig.json'), 'utf8')) +const paths = Object.fromEntries(Object.entries(base.compilerOptions.paths).map(([key, values]) => [key, values.map(value => resolve(root, value))])) +const packages = [] +for (const name of await readdir(join(sdkRoot, 'node_modules/@deepseek-ai'))) { + const directory = join(sdkRoot, 'node_modules/@deepseek-ai', name) + const pkg = JSON.parse(await readFile(join(directory, 'package.json'), 'utf8')) + packages.push({name:pkg.name, version:pkg.version}) + for (const [key, target] of Object.entries(pkg.exports ?? {})) { + if (typeof target !== 'object' || target === null || typeof target.types !== 'string') continue + assert(!target.types.includes('/src/'), 'Use only published declarations') + paths[pkg.name + (key === '.' ? '' : key.slice(1))] = [resolve(directory, target.types)] + } + if (!paths[pkg.name] && pkg.types) paths[pkg.name] = [resolve(directory, pkg.types)] +} +const cohort = packages.find(pkg => pkg.name === '@deepseek-ai/dsh')?.version +assert(cohort, 'The SDK root must include the published DSH CLI') +assert(packages.every(pkg => !pkg.name.startsWith('@deepseek-ai/dsh') || pkg.version === cohort), 'The SDK root contains mixed DSH cohorts') +const config = ts.parseJsonConfigFileContent({ + ...base, + compilerOptions: {...base.compilerOptions, paths, types:['node'], noEmit:true, typeRoots:[join(root,'node_modules/@types')]}, + include:['src/**/*.ts','src/**/*.tsx','plugins/*/src/**/*.ts','plugins/*/src/**/*.tsx'] +}, ts.sys, root) +const program = ts.createProgram(config.fileNames, config.options) +const diagnostics = ts.getPreEmitDiagnostics(program) +const sourceFiles = program.getSourceFiles().map(file => file.fileName) +const leaked = sourceFiles.filter(file => file.includes('node_modules') && /@deepseek-ai(?:\+|\/)(?:dsh|cordis)/.test(file) && !file.startsWith(sdkRoot + '/')) +const rendered = ts.formatDiagnosticsWithColorAndContext(diagnostics, {getCanonicalFileName:value=>value,getCurrentDirectory:()=>root,getNewLine:()=> '\n'}) +await writeFile(report, JSON.stringify({root,sdkRoot,packages,diagnosticCount:diagnostics.length,diagnostics:ts.formatDiagnostics(diagnostics,{getCanonicalFileName:value=>value,getCurrentDirectory:()=>root,getNewLine:()=> '\n'}), leaked,sourceFiles},null,2)+'\n') +console.log(rendered) +console.log(JSON.stringify({sdkRoot, productionFiles:config.fileNames.length,diagnostics:diagnostics.length, foreignDshDeclarations:leaked.length,report})) +process.exitCode = diagnostics.length || leaked.length ? 1 : 0 diff --git a/dsh/docs/pr-assets/issue-261-dsh-slots/harness/HARNESS.md b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/HARNESS.md new file mode 100644 index 000000000..d47528701 --- /dev/null +++ b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/HARNESS.md @@ -0,0 +1,66 @@ +# Issue 261 published-package compatibility harness + +[English](HARNESS.md) | [简体中文](HARNESS.zh-CN.md) + +This directory contains external verification scripts. They never alter a DSH package or alias a package to a source checkout. Supply built Mnemon source only to the pack command; the installed test consumers resolve all 17 Mnemon packages from a test-owned loopback registry with checked SHA-512 integrity. The Root bundle composes all 16 companions and enables scoped, light-context, and auto-capture strategy extensions. + +Requirements: Node and npm, tar, Git, and the actual Mnemon Native CLI. Node v25.1.0 and Native v0.2.8 were used for the recorded run. Run these commands from this harness directory. Set `MNEMON_SOURCE` to a built Mnemon checkout, `NATIVE_CLI` to the Native executable, and `VERIFICATION_ROOT` to an empty external directory. Keep each run root unique for initial installs. + +```sh +node pack-artifacts.mjs --source "$MNEMON_SOURCE" --output "$VERIFICATION_ROOT/artifacts/fixed" +node pack-peer-fixture.mjs --output "$VERIFICATION_ROOT/artifacts/peer-fixture" +node packed-e2e.mjs --mode fixed --cohort alpha --artifacts "$VERIFICATION_ROOT/artifacts/fixed" --peer-artifact "$VERIFICATION_ROOT/artifacts/peer-fixture/artifact.json" --peers true --run-root "$VERIFICATION_ROOT/fixed-alpha" --native-cli "$NATIVE_CLI" --serve true +``` + +Use `--cohort rc` for published DSH 0.1.5-rc.2; `alpha` pins 0.1.6-alpha.2. Fixed consumers use ordinary npm peer resolution. More than 200 DSH package records must all have the exact cohort version. Installed public SlotCore, Chat, Renderer, and Conversation bytes must match unmodified public tarballs; missing reference tarballs are fetched from the public npm registry with their public integrity checked. + +For either mode, `--framework-root /path/to/previously-verified-consumer` pins its physically installed public framework cohort and supporting peers as exact consumer dependencies. The selected DSH version must match; the pins are recorded in `framework-pins.json`. This keeps later prereleases from entering a historical reproduction through upstream peer ranges, while retaining ordinary npm resolution for fixed consumers. No public package metadata or bytes are patched. Issue #265 used this option after a fresh unpinned RC2 install selected an incomplete RC3 transitive release. + +`--serve false` prepares and verifies a consumer without starting DSH. `--reuse-install true` restarts a previously prepared consumer without npm installation, preserving its home, workspace, and Mnemon data. It still checks the lock graph, local package integrity, dependency resolution, and original public module bytes. This option is for a previously verified test run, never initial installation. The loopback registry may get a new port; historical resolved tarball URLs remain in its lockfile as provenance. + +The WebUI bootstrap URL, harness PID, DSH PID, CLI paths, and isolated directories are recorded in `server.json`. A bootstrap URL may be consumed by the browser; do not prefetch it as an HTTP readiness check. `SIGUSR2` to the harness PID restarts its DSH child and preserves the isolated data; it generates a new bootstrap URL. `SIGTERM` stops the DSH child and the local registry/model services. No background service is installed. + +## Real model/tool sequence + +Send the exact human message `compatibility-261` in the WebUI or Headless CLI. The local model fixture calls the actual `mnemon_runtime_memory` add tool, requires its successful receipt, calls actual `mnemon_status`, then emits the fixed completion text. Memory is written by the real installed Runtime Source into its JSON storage. The real Native CLI is configured and version-checked; this Runtime add does not claim a Native memory-space backend write. Title-generation requests are answered as text and cannot initiate memory writes. Child maintenance requests use their offered completion tool to report a skipped action. Other human turns receive a fixed readiness response. The fixture accepts OpenAI and Anthropic request formats and emits the matching SSE wire protocol. It has a request budget and performs no network model requests. + +`model-requests.jsonl` retains request paths, detected protocols, real tool inventories, prompts, and tool receipts. `model-events.jsonl` retains the bounded fixture decisions. These are disposable test inputs and outputs. + +The optional peer plugin is ordinary source under `e2e/peer-fixture`, packed by `pack-peer-fixture.mjs` and loaded through DSH's official bundle/client loader. It contributes distinct IDs `issue261-peer-a:tail` and `issue261-peer-b:tail`, with text “Compatibility peer A” and “Compatibility peer B”. Its selector declines both RC chain entries; the alpha list renderer renders both. No registry internals or framework modules are patched. Toggle Mnemon's turn-bar control off/on and reload to check that the peers coexist independently. + +## Headless checks against the same packed graph + +```sh +node headless-check.mjs --consumer "$VERIFICATION_ROOT/fixed-alpha/dsh-home/profiles/web" --output "$VERIFICATION_ROOT/headless/fixed-alpha" --native-cli "$NATIVE_CLI" +``` + +The Headless profile has its own home/data/workspace. Its only `node_modules` link points to the test-owned packed consumer above. The script asserts the exact DSH cohort, all 17 local artifact records, the full Root composition, all three enabled strategies, the real add/status sequence, persisted Runtime memory across a new CLI process, and zero `mnemon_*` tools after disabling Root. Each command has a 90-second bound. The result and logs remain under the output directory. Use a new output directory for each run. + +## Baseline alpha caveat + +Released Root 0.5.11 excludes alpha2 in its two published framework peer ranges. Only `--mode baseline --cohort alpha` uses `--legacy-peer-deps` to reach the original browser registration failure. Because this option also suppresses DSH's automatic peer installation, baseline reproduction uses exact public framework pins. The bundled `e2e/framework-peers-alpha.json` and `e2e/framework-peers-rc.json` snapshots were captured on macOS ARM64. The implicit alpha snapshot fallback is rejected on other platforms. For a fresh host, set `FRAMEWORK_ROOT` to a normal-installed, exact-alpha public profile and derive its physically installed platform packages. Set `BASELINE_SOURCE` to the built released Root 0.5.11 checkout before packing the baseline: + +```sh +node pack-artifacts.mjs --source "$BASELINE_SOURCE" --output "$VERIFICATION_ROOT/artifacts/baseline" +node packed-e2e.mjs --mode baseline --cohort alpha --artifacts "$VERIFICATION_ROOT/artifacts/baseline" --framework-root "$FRAMEWORK_ROOT" --run-root "$VERIFICATION_ROOT/baseline-alpha" --native-cli "$NATIVE_CLI" --serve true +``` + +`framework-pins.mjs` derives exact pins from physically installed public packages, so foreign-platform optional package records are not pinned. The snapshot is retained in `baseline-framework-pins.json`. `install-policy.json` records this baseline-only bypass; it is not evidence of a normal compatible install. Both fixed cohorts must install without the bypass. + +The original alpha browser missing-ID failure was independently reproduced. Prior OpenAI-only fixture failures in the first browser/Headless attempts were fixture protocol mismatches, not product failures. Corrected dual-protocol runs were recorded separately. `evidence-summary.json` preserves the result and artifact identities. Raw run logs, dependency trees, model requests, bootstrap URLs, and generated tarballs remain outside the repository and are intentionally excluded from this bundle. + +## Artifact reuse during a long verification run + +`pack-artifacts.mjs` packs all artifacts from a built source tree. For the recorded fix, Root was packed before a concurrent full clean build; `assemble-fixed-artifacts.mjs` then verified that every companion's tracked source and manifest matched the release baseline and reused its byte-identical tarball. It accepts `--source`, `--baseline`, and `--output`; the output must already contain Root's tarball and `npm pack --ignore-scripts --json` result as `root-pack.json`. All 17 tarball identities and source provenance are retained in `artifacts.json`. + +## Reuse limits and copy audit + +- The bundle starts local model/registry services and the official DSH CLI; it does not automate a browser. Browser screenshots and observations are documented in the surrounding evidence report. +- Build the selected Mnemon source before packing. The 17 generated tarballs and optional peer tarball are inputs generated by the commands above, not missing checked-in fixtures. `assemble-fixed-artifacts.mjs` is optional and requires the baseline Git commit to be available locally. +- Fresh installs require public npm access. DSH versions and selected Mnemon artifact bytes are checked exactly; unrelated transitive dependencies still use their public semver ranges, and their resolved versions may change. Retain each generated lockfile when repeating an exact historical run. +- The baseline alpha legacy-peer bypass is deliberately confined to reproducing released Root 0.5.11. Both fixed cohorts use ordinary peer resolution. Platform-specific public packages and Native executables must be available on the host; other operating systems were not exercised in this evidence. +- Headless output must be a new directory. Reusing WebUI installs requires the original verified consumer and artifact set; it is not an installer migration path. +- The bounded model fixture implements the named add/status sequence, title text, and skipped child maintenance. It is not a general language-model simulator or a Native memory-space backend-write test. +- The harness executes official npm package install scripts and inherits the caller's environment. It is not an OS sandbox. Its DeepSeek key is explicitly fake and its model endpoint binds to loopback. The reusable copy sets the supported `DSH_TELEMETRY_MODE=DISABLED` for both public cohorts. + +`bundle-audit.json` records SHA-256 hashes of every copied source file and its committed counterpart. The two executable changes from the exported bundle add the baseline platform guard and explicit telemetry mode; documentation and the sanitized summary explain portability and excluded runtime files. The recorded artifact/result hashes remain unchanged. Repository-authored translations are recorded separately without an export-source hash. diff --git a/dsh/docs/pr-assets/issue-261-dsh-slots/harness/HARNESS.zh-CN.md b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/HARNESS.zh-CN.md new file mode 100644 index 000000000..2fddbbaf3 --- /dev/null +++ b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/HARNESS.zh-CN.md @@ -0,0 +1,66 @@ +# Issue 261 已发布包兼容性验证工具 + +[English](HARNESS.md) | [简体中文](HARNESS.zh-CN.md) + +本目录包含独立的验证脚本。脚本不会修改 DSH 包,也不会通过别名将包指向源码检出目录。已构建的 Mnemon 源码只作为打包命令的输入;安装后的测试环境从测试专用的本机回环 registry 解析全部 17 个 Mnemon 包,并校验 SHA-512 完整性。Root bundle 组合全部 16 个配套插件,并启用 scoped、light-context 和 auto-capture 策略扩展。 + +运行环境需要 Node、npm、tar、Git,以及真实的 Mnemon Native CLI。本次记录使用 Node v25.1.0 和 Native v0.2.8。请在本验证工具目录中运行以下命令。将 `MNEMON_SOURCE` 设置为已构建的 Mnemon 检出目录,`NATIVE_CLI` 设置为 Native 可执行文件,`VERIFICATION_ROOT` 设置为空的外部目录。首次安装时,每次运行应使用独立的根目录。 + +```sh +node pack-artifacts.mjs --source "$MNEMON_SOURCE" --output "$VERIFICATION_ROOT/artifacts/fixed" +node pack-peer-fixture.mjs --output "$VERIFICATION_ROOT/artifacts/peer-fixture" +node packed-e2e.mjs --mode fixed --cohort alpha --artifacts "$VERIFICATION_ROOT/artifacts/fixed" --peer-artifact "$VERIFICATION_ROOT/artifacts/peer-fixture/artifact.json" --peers true --run-root "$VERIFICATION_ROOT/fixed-alpha" --native-cli "$NATIVE_CLI" --serve true +``` + +`--cohort rc` 使用已发布的 DSH 0.1.5-rc.2;`alpha` 固定使用 0.1.6-alpha.2。修复后的测试环境采用正常的 npm peer 依赖解析。超过 200 条 DSH 包记录必须全部使用所选版本组的精确版本。已安装的公开 SlotCore、Chat、Renderer 和 Conversation 模块必须与未经修改的公开 tarball 字节一致;缺少参照 tarball 时,脚本会从公开 npm registry 获取并校验其公开完整性值。 + +两种模式均可使用 `--framework-root /path/to/previously-verified-consumer`,把已验证环境中实际安装的公开框架版本组和配套 peer 固定为测试环境的精确依赖。DSH 版本必须与所选版本组一致,固定结果记录在 `framework-pins.json`。这样可避免上游 peer 范围把较新的预发布版本带入历史复现;修复模式仍采用正常 npm 解析,不修改公开包的元数据或字节。Issue #265 的未固定 RC2 首次安装遇到不完整的 RC3 传递发布后,采用了此选项。 + +`--serve false` 只准备并验证测试环境,不启动 DSH。`--reuse-install true` 跳过 npm 安装,重新启动之前准备好的测试环境,并保留其 home、workspace 和 Mnemon 数据。它仍会检查锁定依赖图、本地包完整性、依赖解析以及原始公开模块的字节。此选项仅用于已经验证过的运行目录,不能用于首次安装。本机回环 registry 可能使用新端口;锁文件中保留的历史 tarball 解析 URL 用于记录制品来源。 + +`server.json` 记录 WebUI 启动认证 URL、验证进程 PID、DSH PID、CLI 路径以及隔离目录。启动 URL 可能在浏览器访问后失效,不要提前请求它来判断 HTTP 服务是否就绪。向验证进程 PID 发送 `SIGUSR2` 会重启其 DSH 子进程,保留隔离数据,并生成新的启动 URL。`SIGTERM` 会停止 DSH 子进程以及本地 registry 和模型服务。脚本不会安装后台服务。 + +## 真实模型与工具调用流程 + +在 WebUI 或 Headless CLI 中发送内容完全为 `compatibility-261` 的用户消息。本地模型测试桩会调用真实的 `mnemon_runtime_memory` add 工具,要求返回成功回执,再调用真实的 `mnemon_status`,最后输出固定的完成文本。记忆由实际安装的 Runtime Source 写入其 JSON 存储。真实的 Native CLI 已配置并通过版本检查,但此 Runtime add 不代表执行了 Native 记忆空间后端写入。标题生成请求只返回文本,不会发起记忆写入。子级维护请求通过其提供的完成工具报告跳过操作。其他用户回合返回固定的就绪文本。测试桩接收 OpenAI 和 Anthropic 请求格式,并输出对应的 SSE 传输格式。它设置了请求次数上限,不调用外部网络模型。 + +`model-requests.jsonl` 保留请求路径、检测到的协议、真实工具列表、提示词和工具回执。`model-events.jsonl` 保留受限测试桩的决策记录。这些文件都是可丢弃的测试输入和输出。 + +可选的共存测试插件使用 `e2e/peer-fixture` 中的普通源码,由 `pack-peer-fixture.mjs` 打包,并通过 DSH 官方的 bundle/客户端加载器加载。它注册两个不同的 ID:`issue261-peer-a:tail` 和 `issue261-peer-b:tail`,显示文本分别为 “Compatibility peer A” 和 “Compatibility peer B”。在 RC 的 chain 模式中,这两项的 selector 均不命中;alpha 的 list 渲染器则同时渲染两项。测试不会修改 registry 内部状态或框架模块。关闭再开启 Mnemon 的回合栏控件,并重载页面,可检查其他贡献项是否仍能独立共存。 + +## 使用同一打包依赖图进行 Headless 检查 + +```sh +node headless-check.mjs --consumer "$VERIFICATION_ROOT/fixed-alpha/dsh-home/profiles/web" --output "$VERIFICATION_ROOT/headless/fixed-alpha" --native-cli "$NATIVE_CLI" +``` + +Headless profile 使用独立的 home、data 和 workspace。它唯一的 `node_modules` 链接指向上述测试专用的打包安装环境。脚本会断言:DSH 使用精确版本组;全部 17 个本地制品记录存在;Root 完整组合已生效;三项策略扩展全部启用;真实 add/status 流程完成;启动新的 CLI 进程后 Runtime 记忆仍被保留;禁用 Root 后不再暴露任何 `mnemon_*` 工具。每条命令的超时上限为 90 秒。结果与日志保留在输出目录中。每次运行都应使用新的输出目录。 + +## Alpha 基线的特殊处理 + +已发布的 Root 0.5.11 在两个框架 peer 依赖范围中都未包含 alpha2。只有 `--mode baseline --cohort alpha` 会使用 `--legacy-peer-deps`,以便复现原始的浏览器注册失败。由于该选项也会阻止 DSH 自动安装 peer 依赖,基线复现需要精确固定公开框架包的版本。随工具提供的 `e2e/framework-peers-alpha.json` 和 `e2e/framework-peers-rc.json` 快照采集自 macOS ARM64;在其他平台上,脚本会拒绝隐式回退到此 alpha 快照。在新主机上,请将 `FRAMEWORK_ROOT` 指向通过正常安装获得、使用精确 alpha 版本的公开 profile,以便从实际已安装的包推导该平台所需依赖。打包基线前,请将 `BASELINE_SOURCE` 设置为已构建的已发布 Root 0.5.11 检出目录: + +```sh +node pack-artifacts.mjs --source "$BASELINE_SOURCE" --output "$VERIFICATION_ROOT/artifacts/baseline" +node packed-e2e.mjs --mode baseline --cohort alpha --artifacts "$VERIFICATION_ROOT/artifacts/baseline" --framework-root "$FRAMEWORK_ROOT" --run-root "$VERIFICATION_ROOT/baseline-alpha" --native-cli "$NATIVE_CLI" --serve true +``` + +`framework-pins.mjs` 根据实际已安装的公开包生成精确版本列表,因此不会将其他平台的可选包记录固定为直接依赖。快照保留在 `baseline-framework-pins.json` 中。`install-policy.json` 记录此仅限基线的依赖检查绕过;它不能证明正常安装兼容。两个修复后的版本组都必须在不绕过检查的情况下完成安装。 + +原始 alpha 浏览器中的缺少 ID 错误已独立复现。首次浏览器和 Headless 尝试中,仅支持 OpenAI 的测试桩因协议不匹配而失败;这是测试桩问题,不是产品错误。修正后的双协议运行已单独记录。`evidence-summary.json` 保留结果和制品标识。原始运行日志、依赖树、模型请求、启动认证 URL 和生成的 tarball 保留在仓库之外,不纳入本工具包。 + +## 长时间验证中的制品复用 + +`pack-artifacts.mjs` 从已构建的源码树打包全部制品。本次修复在并行执行完整清理构建前先打包 Root;随后,`assemble-fixed-artifacts.mjs` 验证每个配套插件的 Git 跟踪源码与 manifest 均与发布基线一致,并复用字节完全相同的 tarball。该脚本接受 `--source`、`--baseline` 和 `--output`;输出目录必须已包含 Root tarball,以及保存为 `root-pack.json` 的 `npm pack --ignore-scripts --json` 结果。全部 17 个 tarball 的标识与源码来源记录在 `artifacts.json` 中。 + +## 复用限制与复制审计 + +- 本工具包启动本地模型服务、registry 服务和官方 DSH CLI,不自动操作浏览器。浏览器截图与观察结果记录在上级证据报告中。 +- 打包前必须构建所选 Mnemon 源码。17 个生成的 tarball 和可选的 peer tarball 由上述命令生成,并非缺失的仓库测试文件。`assemble-fixed-artifacts.mjs` 是可选脚本,要求基线 Git 提交在本地可访问。 +- 首次安装需要访问公开 npm registry。脚本精确检查 DSH 版本和所选 Mnemon 制品的字节;其他间接依赖仍遵循公开的 semver 范围,实际解析版本可能变化。若要精确重现历史运行,应保留每次生成的锁文件。 +- Alpha 基线的 legacy-peer 绕过仅用于复现已发布 Root 0.5.11。两个修复后的版本组均使用正常的 peer 依赖解析。主机必须能够使用相应平台的公开包和 Native 可执行文件;本次证据未覆盖其他操作系统。 +- Headless 输出目录必须是新目录。复用 WebUI 安装要求保留原先已经验证的安装环境和制品集;该选项不用于安装器迁移。 +- 受限模型测试桩只实现指定的 add/status 流程、标题文本和跳过子级维护的行为。它不是通用语言模型模拟器,也不是 Native 记忆空间后端写入测试。 +- 工具会执行官方 npm 包的安装脚本,并继承调用者的环境。它不是操作系统沙箱。DeepSeek key 明确为测试用假值,模型端点绑定在本机回环地址。可复用副本为两个公开版本组都设置了受支持的 `DSH_TELEMETRY_MODE=DISABLED`。 + +`bundle-audit.json` 记录每个复制源文件及其仓库副本的 SHA-256。相较于原始导出包,可执行脚本有两项调整:添加基线平台检查,并显式设置遥测模式;文档和清理后的摘要说明可移植性及未纳入仓库的本地运行文件。已记录的制品与结果哈希保持不变。在仓库中新编写的译文单独记录,不虚构原始导出文件的哈希。 diff --git a/dsh/docs/pr-assets/issue-261-dsh-slots/harness/assemble-fixed-artifacts.mjs b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/assemble-fixed-artifacts.mjs new file mode 100644 index 000000000..70a8a7b2f --- /dev/null +++ b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/assemble-fixed-artifacts.mjs @@ -0,0 +1,37 @@ +import assert from 'node:assert/strict'; +import { readFile, writeFile, copyFile } from 'node:fs/promises'; +import { spawnSync } from 'node:child_process'; +import { createHash } from 'node:crypto'; +import { resolve, join } from 'node:path'; +const args = new Map(); +for (let i = 2; i < process.argv.length; i += 2) args.set(process.argv[i], process.argv[i + 1]); +const source = resolve(args.get('--source')); +const baseline = resolve(args.get('--baseline')); +const output = resolve(args.get('--output')); +const git = (...argv) => { const result = spawnSync('git', argv, { cwd: source, encoding: 'utf8' }); assert.equal(result.status, 0, result.stderr); return result.stdout.trim(); }; +const original = JSON.parse(await readFile(join(baseline, 'artifacts.json'), 'utf8')); +const [packed] = JSON.parse(await readFile(join(output, 'root-pack.json'), 'utf8')); +const rootManifestResult = spawnSync('tar', ['-xOf', join(output, packed.filename), 'package/package.json'], { encoding: 'utf8' }); +assert.equal(rootManifestResult.status, 0); +const manifest = JSON.parse(rootManifestResult.stdout); +const bytes = await readFile(join(output, packed.filename)); +assert.equal(packed.integrity, 'sha512-' + createHash('sha512').update(bytes).digest('base64')); +const artifacts = [{ name: manifest.name, version: manifest.version, filename: packed.filename, integrity: packed.integrity, shasum: createHash('sha1').update(bytes).digest('hex'), sha256: createHash('sha256').update(bytes).digest('hex'), source, manifest, files: packed.files }]; +const reuse = []; +for (const item of original.artifacts.filter(item => item.name !== 'dsh-mnemon')) { + const relative = 'plugins/' + item.name; + assert.equal(git('diff', original.provenance.commit, 'HEAD', '--', relative), '', `${relative} tracked source changed`); + assert.equal(git('diff', 'HEAD', '--', relative), '', `${relative} working source changed`); + const current = JSON.parse(await readFile(join(source, relative, 'package.json'), 'utf8')); + assert.deepEqual(current, item.manifest, `${item.name} manifest changed`); + const oldBytes = await readFile(join(baseline, item.filename)); + assert.equal(item.integrity, 'sha512-' + createHash('sha512').update(oldBytes).digest('base64')); + await copyFile(join(baseline, item.filename), join(output, item.filename)); + artifacts.push({ ...item, reusedFrom: baseline, unchangedSourceComparedAt: source }); + reuse.push({ name: item.name, integrity: item.integrity, trackedSourceIdentical: true, manifestIdentical: true }); +} +assert.equal(artifacts.length, 17); +const provenance = { source, commit: git('rev-parse', 'HEAD'), status: git('status', '--porcelain=v1'), capturedAt: new Date().toISOString(), node: process.version, companionBaseline: original.provenance, reuse }; +await writeFile(join(output, 'source-diff.patch'), git('diff', '--binary', 'HEAD') + '\n'); +await writeFile(join(output, 'artifacts.json'), JSON.stringify({ provenance, artifacts }, null, 2) + '\n'); +console.log(JSON.stringify({ source, commit: provenance.commit, artifactCount: artifacts.length, rootIntegrity: artifacts[0].integrity, reusedCompanions: reuse.length, output })); diff --git a/dsh/docs/pr-assets/issue-261-dsh-slots/harness/bundle-audit.json b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/bundle-audit.json new file mode 100644 index 000000000..08b024aae --- /dev/null +++ b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/bundle-audit.json @@ -0,0 +1,142 @@ +{ + "purpose": "Source-to-repository copy audit of the external portable Issue 261 harness bundle.", + "sourceIdentifier": "Exported portable-harness bundle used for the recorded external verification", + "recordedRuntimeNode": "v25.1.0", + "recordedPlatform": { + "platform": "darwin", + "arch": "arm64" + }, + "validation": { + "javascriptSyntaxChecks": 9, + "relativeModuleImports": "all included", + "peerManifestFiles": "all included", + "jsonFiles": "all parse", + "personalPaths": "none", + "bootstrapOrAuthUrls": "none", + "personalOrApiCredentials": "none; only explicitly fake local-model keys", + "headless": [ + { + "cohort": "alpha", + "version": "0.1.6-alpha.2", + "result": "pass", + "nativeVersion": "mnemon version 0.2.8", + "enabledMnemonToolCount": 17, + "disabledMnemonToolCount": 0, + "completedAt": "2026-09-18T03:41:00.148Z" + }, + { + "cohort": "rc", + "version": "0.1.5-rc.2", + "result": "pass", + "nativeVersion": "mnemon version 0.2.8", + "enabledMnemonToolCount": 17, + "disabledMnemonToolCount": 0, + "completedAt": "2026-09-18T03:41:00.515Z" + } + ], + "peerRepackIntegrityMatchesRecordedRun": true + }, + "sourceFiles": [ + { + "file": "HARNESS.md", + "sourceSha256": "ba8fb1f2e08d01a45b22f8779fb19d6a0f81d2cebdfeb56710b57bfbb9f34011", + "copiedSha256": "316b6b47970825af906317c87c430388b9d2c649574825b3dcc334e0c3ae6d93", + "identical": false, + "reason": "Document host requirements, generated inputs, platform limits, environment inheritance, excluded local runtime files, copy audit, and language cross-links." + }, + { + "file": "assemble-fixed-artifacts.mjs", + "sourceSha256": "a101bef2ba8d41d4eaeb8b298fca92a95cdcb8a578535efd089d89a47b32db3f", + "copiedSha256": "a101bef2ba8d41d4eaeb8b298fca92a95cdcb8a578535efd089d89a47b32db3f", + "identical": true + }, + { + "file": "compatibility-model.mjs", + "sourceSha256": "844f685eb57190e2f414add7cf0bf3af66fb98ef397a82d03cb92a12683207bb", + "copiedSha256": "844f685eb57190e2f414add7cf0bf3af66fb98ef397a82d03cb92a12683207bb", + "identical": true + }, + { + "file": "e2e/framework-peers-alpha.json", + "sourceSha256": "25d0bc36224cb75a8b1ba08bbab8f43f2e16966b96e59150c5c3f2395025ef80", + "copiedSha256": "25d0bc36224cb75a8b1ba08bbab8f43f2e16966b96e59150c5c3f2395025ef80", + "identical": true + }, + { + "file": "e2e/framework-peers-rc.json", + "sourceSha256": "30bf5ba3e730a6383220b38bebc81d4cd738706559537e8fbd48cef70c11bcde", + "copiedSha256": "30bf5ba3e730a6383220b38bebc81d4cd738706559537e8fbd48cef70c11bcde", + "identical": true + }, + { + "file": "e2e/peer-fixture/client.js", + "sourceSha256": "bc6da826562a49d28065859616632da2d21f8aa66fd528860f2aee07a27238aa", + "copiedSha256": "bc6da826562a49d28065859616632da2d21f8aa66fd528860f2aee07a27238aa", + "identical": true + }, + { + "file": "e2e/peer-fixture/cordis.patch.yml", + "sourceSha256": "f59e407c0b76f5270386deeeeb3734560b0699b9933e14ea62e00b029a13b2b6", + "copiedSha256": "f59e407c0b76f5270386deeeeb3734560b0699b9933e14ea62e00b029a13b2b6", + "identical": true + }, + { + "file": "e2e/peer-fixture/index.js", + "sourceSha256": "4af587d107a43bf9a985ae2769357fc25a481c2057e3374840d24e5d0502ca06", + "copiedSha256": "4af587d107a43bf9a985ae2769357fc25a481c2057e3374840d24e5d0502ca06", + "identical": true + }, + { + "file": "e2e/peer-fixture/package.json", + "sourceSha256": "4cf063a361808a92df9bcd308f74b77bb4117615aca301730494ae82dd84fadd", + "copiedSha256": "4cf063a361808a92df9bcd308f74b77bb4117615aca301730494ae82dd84fadd", + "identical": true + }, + { + "file": "evidence-summary.json", + "sourceSha256": "f7bb7cb40e4d9ee142e343dd9546e7b902975d6698fc8325b870ffe72cbb774a", + "copiedSha256": "fafb4f56dfcfd2724bbad28a8046299eb7f21b8e80215194457a13e7dc76ef7b", + "identical": false, + "reason": "Preserve results/artifact identities, clarify original source hashes, remove references to excluded raw local files, and add copied-harness validation." + }, + { + "file": "framework-pins.mjs", + "sourceSha256": "9fc56d492618e9db1fcf637b2b9dac69a1acfdeb13b48dab73127e9807117db2", + "copiedSha256": "9fc56d492618e9db1fcf637b2b9dac69a1acfdeb13b48dab73127e9807117db2", + "identical": true + }, + { + "file": "headless-check.mjs", + "sourceSha256": "5b1a38435d9cc781fc238dd66ab5437a10baeb0fbe73250d18e2bb4ce80bfd32", + "copiedSha256": "bbf4d9e13ba8a3ce3e01263708af912c5a7a4672f8095874da699b8da0bc46a1", + "identical": false, + "reason": "Add explicit supported telemetry mode." + }, + { + "file": "pack-artifacts.mjs", + "sourceSha256": "e695a91b01fa17ef55b8954b24481aee3beea0eb9ad8476d429000c2d47677b0", + "copiedSha256": "e695a91b01fa17ef55b8954b24481aee3beea0eb9ad8476d429000c2d47677b0", + "identical": true + }, + { + "file": "pack-peer-fixture.mjs", + "sourceSha256": "66f71ecf5086ec1611e1299d020d34cfde49547760ff027eb8a8a58d6b7c931e", + "copiedSha256": "66f71ecf5086ec1611e1299d020d34cfde49547760ff027eb8a8a58d6b7c931e", + "identical": true + }, + { + "file": "packed-e2e.mjs", + "sourceSha256": "bb41816d293e369bea096d50b800c814d58c0de3def66fe4c4a65218868d3b80", + "copiedSha256": "288c79642f06a3874d6590d086b23370dd4bb29c277f118950764b76622a4e58", + "identical": false, + "reason": "Add explicit supported telemetry mode and reject implicit macOS ARM64 baseline pins on other platforms." + } + ], + "repositoryAdditions": [ + { + "file": "HARNESS.zh-CN.md", + "sha256": "2f2d61df7efb9afe03d344e1dfd83e54221af559690e32a510e9ee4795282dd1", + "provenance": "Chinese translation authored in the repository from HARNESS.md; no source file exists in the original exported bundle." + } + ] +} diff --git a/dsh/docs/pr-assets/issue-261-dsh-slots/harness/compatibility-model.mjs b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/compatibility-model.mjs new file mode 100644 index 000000000..bac098bf0 --- /dev/null +++ b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/compatibility-model.mjs @@ -0,0 +1,89 @@ +import assert from 'node:assert/strict'; + +export const fixtureMemory = 'Issue 261 compatibility: the isolated published DSH cohort uses real Mnemon runtime memory.'; +const ready = 'Isolated Issue 261 compatibility fixture ready. Send compatibility-261 to add runtime memory and inspect Mnemon status.'; +export const contentText = content => typeof content === 'string' ? content : Array.isArray(content) ? content.filter(block => block.type === 'text' || typeof block.text === 'string').map(block => block.text ?? '').join('\n') : ''; +const parseReceipt = content => { const value = contentText(content); try { return JSON.parse(value); } catch { return { error: value }; } }; + +export function normalizeRequest(request) { + const messages = request.messages ?? []; + const system = [contentText(request.system), ...messages.filter(message => message.role === 'system').map(message => contentText(message.content))].filter(Boolean).join('\n'); + const toolNames = (request.tools ?? []).map(tool => tool.function?.name ?? tool.name).filter(Boolean); + const isMarker = message => message.role === 'user' && (typeof message.content === 'string' ? [message.content] : (message.content ?? []).filter(block => block.type === 'text').map(block => block.text ?? '')).some(value => /^compatibility-261(?:\s|$)/u.test(value.trim())); + const userIndex = messages.findLastIndex(isMarker); + const receipts = messages.slice(userIndex + 1).flatMap(message => message.role === 'tool' ? [parseReceipt(message.content)] : message.role === 'user' && Array.isArray(message.content) ? message.content.filter(block => block.type === 'tool_result').map(block => parseReceipt(block.content)) : []); + return { messages, system, toolNames, userIndex, receipts }; +} + +export function compatibilityModel(report) { + let calls = 0; + const childCounts = new Map(); + return request => { + calls += 1; + if (calls > 200) return 'Compatibility fixture request budget reached. Stop this test run.'; + const { system, toolNames: names, userIndex, receipts } = normalizeRequest(request); + const toolNames = new Set(names); + // Title requests quote the marker, but have no tool inventory or authority to write memory. + if (toolNames.size === 0) return /title/iu.test(system) ? 'Issue 261 compatibility' : ready; + const completionName = system.match(/Completion protocol: call `([^`]+)`/u)?.[1]; + if (completionName !== undefined) { + assert(toolNames.has(completionName), 'child completion tool is visible'); + const requestId = system.match(/requestId `([^`]+)`/u)?.[1]; + const key = requestId ?? completionName; + const count = (childCounts.get(key) ?? 0) + 1; + childCounts.set(key, count); + if (count > 3) return 'No further maintenance actions are permitted in this bounded compatibility fixture.'; + const result = { action: 'skipped', summary: 'No child maintenance writes in the Issue 261 compatibility fixture.', memoryBodyIds: [] }; + report({ event: 'child-skipped', completionName, requestId, count }); + return { name: completionName, args: requestId === undefined ? result : { requestId, result } }; + } + if (userIndex < 0) return ready; + assert(toolNames.has('mnemon_runtime_memory') && toolNames.has('mnemon_status'), 'real Mnemon runtime and status tools are visible'); + if (receipts.length === 0) { + report({ event: 'runtime-add-requested', tools: [...toolNames].filter(name => name.startsWith('mnemon_')).sort() }); + return { name: 'mnemon_runtime_memory', args: { action: 'add', target: 'memory', content: fixtureMemory, importance: 'normal' } }; + } + if (receipts.length === 1) { + const first = receipts[0]; + if (first?.success !== true) { + report({ event: 'runtime-add-failed', receipt: first }); + return 'Compatibility fixture stopped because the real runtime-memory tool did not report success: ' + JSON.stringify(first); + } + report({ event: 'runtime-add-committed', receipt: first }); + return { name: 'mnemon_status', args: {} }; + } + report({ event: 'compatibility-complete', receipts: receipts.slice(0, 2) }); + return 'Issue 261 compatibility complete: real runtime memory added and Mnemon status inspected.'; + }; +} + +export function responseProtocol(request, requestPath = '') { + return requestPath.includes('/anthropic/') || /\/messages(?:\?|$)/u.test(requestPath) || request.system !== undefined || request.tools?.some(tool => tool.input_schema !== undefined) ? 'anthropic' : 'openai'; +} + +export function sendModelReply(response, request, requestPath, reply, id) { + const protocol = responseProtocol(request, requestPath); + const isText = typeof reply === 'string'; + const model = request.model ?? 'issue261-fixture'; + if (protocol === 'anthropic') { + const message = { id, type: 'message', role: 'assistant', model, content: isText ? [{ type: 'text', text: reply }] : [{ type: 'tool_use', id: `tool-${id}`, name: reply.name, input: reply.args }], stop_reason: isText ? 'end_turn' : 'tool_use', stop_sequence: null, usage: { input_tokens: 100, output_tokens: 20 } }; + if (request.stream === false) { response.writeHead(200, { 'content-type': 'application/json' }); response.end(JSON.stringify(message)); return; } + response.writeHead(200, { 'content-type': 'text/event-stream', 'cache-control': 'no-cache' }); + const event = value => response.write(`event: ${value.type}\ndata: ${JSON.stringify(value)}\n\n`); + event({ type: 'message_start', message: { ...message, content: [], stop_reason: null, usage: { input_tokens: 100, output_tokens: 0 } } }); + event({ type: 'content_block_start', index: 0, content_block: isText ? { type: 'text', text: '' } : { type: 'tool_use', id: `tool-${id}`, name: reply.name, input: {} } }); + event({ type: 'content_block_delta', index: 0, delta: isText ? { type: 'text_delta', text: reply } : { type: 'input_json_delta', partial_json: JSON.stringify(reply.args) } }); + event({ type: 'content_block_stop', index: 0 }); + event({ type: 'message_delta', delta: { stop_reason: message.stop_reason, stop_sequence: null }, usage: { output_tokens: 20 } }); + event({ type: 'message_stop' }); + response.end(); + return; + } + const delta = isText ? { role: 'assistant', content: reply } : { role: 'assistant', tool_calls: [{ index: 0, id: `tool-${id}`, type: 'function', function: { name: reply.name, arguments: JSON.stringify(reply.args) } }] }; + if (request.stream === false) { response.writeHead(200, { 'content-type': 'application/json' }); response.end(JSON.stringify({ id, object: 'chat.completion', model, choices: [{ index: 0, message: delta, finish_reason: isText ? 'stop' : 'tool_calls' }], usage: { prompt_tokens: 100, completion_tokens: 20, total_tokens: 120 } })); return; } + response.writeHead(200, { 'content-type': 'text/event-stream', 'cache-control': 'no-cache' }); + response.write(`data: ${JSON.stringify({ id, object: 'chat.completion.chunk', model, choices: [{ index: 0, delta, finish_reason: null }] })}\n\n`); + response.write(`data: ${JSON.stringify({ id, object: 'chat.completion.chunk', model, choices: [{ index: 0, delta: {}, finish_reason: isText ? 'stop' : 'tool_calls' }] })}\n\n`); + response.write(`data: ${JSON.stringify({ id, object: 'chat.completion.chunk', model, choices: [], usage: { prompt_tokens: 100, completion_tokens: 20, total_tokens: 120 } })}\n\n`); + response.end('data: [DONE]\n\n'); +} diff --git a/dsh/docs/pr-assets/issue-261-dsh-slots/harness/e2e/framework-peers-alpha.json b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/e2e/framework-peers-alpha.json new file mode 100644 index 000000000..0b1398e96 --- /dev/null +++ b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/e2e/framework-peers-alpha.json @@ -0,0 +1,267 @@ +{ + "@deepseek-ai/cordis": "4.0.2", + "@deepseek-ai/cordis-plugin-group": "1.0.2", + "@deepseek-ai/cordis-plugin-include": "1.0.7", + "@deepseek-ai/cordis-plugin-loader": "1.0.3", + "@deepseek-ai/cordis-plugin-timer": "1.1.4", + "@deepseek-ai/cosmokit": "1.8.3", + "@deepseek-ai/dsh": "0.1.6-alpha.2", + "@deepseek-ai/dsh-acp": "0.1.6-alpha.2", + "@deepseek-ai/dsh-acp-app": "0.1.6-alpha.2", + "@deepseek-ai/dsh-agent": "0.1.6-alpha.2", + "@deepseek-ai/dsh-agent-default-model": "0.1.6-alpha.2", + "@deepseek-ai/dsh-agent-instructions": "0.1.6-alpha.2", + "@deepseek-ai/dsh-agent-loop": "0.1.6-alpha.2", + "@deepseek-ai/dsh-agent-presets": "0.1.6-alpha.2", + "@deepseek-ai/dsh-agent-tool-presentation": "0.1.6-alpha.2", + "@deepseek-ai/dsh-anonymous-user-id": "0.1.6-alpha.2", + "@deepseek-ai/dsh-api-gateway": "0.1.6-alpha.2", + "@deepseek-ai/dsh-api-remotes": "0.1.6-alpha.2", + "@deepseek-ai/dsh-api-session-controller": "0.1.6-alpha.2", + "@deepseek-ai/dsh-api-settings-controller": "0.1.6-alpha.2", + "@deepseek-ai/dsh-api-terminal-controller": "0.1.6-alpha.2", + "@deepseek-ai/dsh-api-workspace-controller": "0.1.6-alpha.2", + "@deepseek-ai/dsh-api-workspace-files": "0.1.6-alpha.2", + "@deepseek-ai/dsh-app-boot": "0.1.6-alpha.2", + "@deepseek-ai/dsh-atomic-write": "0.1.6-alpha.2", + "@deepseek-ai/dsh-attachment": "0.1.6-alpha.2", + "@deepseek-ai/dsh-attachment-local": "0.1.6-alpha.2", + "@deepseek-ai/dsh-authorization": "0.1.6-alpha.2", + "@deepseek-ai/dsh-base": "0.1.6-alpha.2", + "@deepseek-ai/dsh-bash-local": "0.1.6-alpha.2", + "@deepseek-ai/dsh-bash-sandbox": "0.1.6-alpha.2", + "@deepseek-ai/dsh-brand": "0.1.6-alpha.2", + "@deepseek-ai/dsh-chunked-list": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-connection": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-file-upload": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-hmr": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-locale": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-modules": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-resources": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-store": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-agent-preset": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-approval": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-attachment": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-brand-official": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-chat": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-commands": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-conversation": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-cordis": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-deliverables": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-directory-picker-browse": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-directory-picker-native": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-goal": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-input-trigger": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-jobs": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-layout": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-message-feedback": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-model-selection": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-open-in-app": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-permission-presets": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-plan": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-plugin-manager": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-primitives": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-reference": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-renderer": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-schedule": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-session": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-settings": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-settings-general": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-settings-models": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-settings-plugin-inventory": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-settings-plugins": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-settings-unarchive-sessions": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-sidebar": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-sidebar-browser": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-sidebar-documentpreview": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-sidebar-files": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-sidebar-right": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-sidebar-terminal": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-skill": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-slots": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-subagent": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-theme": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-tool": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-trajectory": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-user-questions": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-workflow-run": "0.1.6-alpha.2", + "@deepseek-ai/dsh-client-ui-workspace": "0.1.6-alpha.2", + "@deepseek-ai/dsh-cmdline": "0.1.6-alpha.2", + "@deepseek-ai/dsh-command-compact": "0.1.6-alpha.2", + "@deepseek-ai/dsh-command-feedback": "0.1.6-alpha.2", + "@deepseek-ai/dsh-command-goal": "0.1.6-alpha.2", + "@deepseek-ai/dsh-commands": "0.1.6-alpha.2", + "@deepseek-ai/dsh-compaction": "0.1.6-alpha.2", + "@deepseek-ai/dsh-compaction-basic": "0.1.6-alpha.2", + "@deepseek-ai/dsh-compaction-image-offload": "0.1.6-alpha.2", + "@deepseek-ai/dsh-compaction-tool-result-pruner": "0.1.6-alpha.2", + "@deepseek-ai/dsh-cordis-client-runner": "0.1.6-alpha.2", + "@deepseek-ai/dsh-cordis-host-runner": "0.1.6-alpha.2", + "@deepseek-ai/dsh-credentials": "0.1.6-alpha.2", + "@deepseek-ai/dsh-credentials-local": "0.1.6-alpha.2", + "@deepseek-ai/dsh-deepseek-llm-api-extensions": "0.1.6-alpha.2", + "@deepseek-ai/dsh-deque": "0.1.6-alpha.2", + "@deepseek-ai/dsh-experimental-agent-team": "0.1.6-alpha.2", + "@deepseek-ai/dsh-experimental-agent-team-profile": "0.1.6-alpha.2", + "@deepseek-ai/dsh-experimental-agent-team-web-profile": "0.1.6-alpha.2", + "@deepseek-ai/dsh-experimental-client-ui-agent-team": "0.1.6-alpha.2", + "@deepseek-ai/dsh-experimental-tool-agent-team": "0.1.6-alpha.2", + "@deepseek-ai/dsh-file-reference": "0.1.6-alpha.2", + "@deepseek-ai/dsh-file-reference-local": "0.1.6-alpha.2", + "@deepseek-ai/dsh-fs": "0.1.6-alpha.2", + "@deepseek-ai/dsh-fs-local": "0.1.6-alpha.2", + "@deepseek-ai/dsh-fs-observation-policy": "0.1.6-alpha.2", + "@deepseek-ai/dsh-fs-sandbox": "0.1.6-alpha.2", + "@deepseek-ai/dsh-goal": "0.1.6-alpha.2", + "@deepseek-ai/dsh-goal-round-driver": "0.1.6-alpha.2", + "@deepseek-ai/dsh-headless": "0.1.6-alpha.2", + "@deepseek-ai/dsh-hmr": "0.1.6-alpha.2", + "@deepseek-ai/dsh-home-paths": "0.1.6-alpha.2", + "@deepseek-ai/dsh-hook-protocol": "0.1.6-alpha.2", + "@deepseek-ai/dsh-hooks-claude-code": "0.1.6-alpha.2", + "@deepseek-ai/dsh-hooks-codex": "0.1.6-alpha.2", + "@deepseek-ai/dsh-host-directory-picker": "0.1.6-alpha.2", + "@deepseek-ai/dsh-host-directory-picker-auto": "0.1.6-alpha.2", + "@deepseek-ai/dsh-host-directory-picker-browse": "0.1.6-alpha.2", + "@deepseek-ai/dsh-host-directory-picker-native": "0.1.6-alpha.2", + "@deepseek-ai/dsh-host-frontend-static": "0.1.6-alpha.2", + "@deepseek-ai/dsh-host-open-in-app": "0.1.6-alpha.2", + "@deepseek-ai/dsh-host-plugin-inventory": "0.1.6-alpha.2", + "@deepseek-ai/dsh-host-webserver": "0.1.6-alpha.2", + "@deepseek-ai/dsh-http-proxy": "0.1.6-alpha.2", + "@deepseek-ai/dsh-invariants": "0.1.6-alpha.2", + "@deepseek-ai/dsh-jobs": "0.1.6-alpha.2", + "@deepseek-ai/dsh-jobs-local": "0.1.6-alpha.2", + "@deepseek-ai/dsh-launch-environment": "0.1.6-alpha.2", + "@deepseek-ai/dsh-lazy-require": "0.1.6-alpha.2", + "@deepseek-ai/dsh-llm": "0.1.6-alpha.2", + "@deepseek-ai/dsh-llm-deepseek": "0.1.6-alpha.2", + "@deepseek-ai/dsh-llm-pi-ai": "0.1.6-alpha.2", + "@deepseek-ai/dsh-llm-retry": "0.1.6-alpha.2", + "@deepseek-ai/dsh-mcp-client": "0.1.6-alpha.2", + "@deepseek-ai/dsh-mcp-resources": "0.1.6-alpha.2", + "@deepseek-ai/dsh-message-feedback": "0.1.6-alpha.2", + "@deepseek-ai/dsh-native-command": "0.1.6-alpha.2", + "@deepseek-ai/dsh-office-to-pdf": "0.1.6-alpha.2", + "@deepseek-ai/dsh-output-retention": "0.1.6-alpha.2", + "@deepseek-ai/dsh-package-manifest": "0.1.6-alpha.2", + "@deepseek-ai/dsh-permission-presets": "0.1.6-alpha.2", + "@deepseek-ai/dsh-persona": "0.1.6-alpha.2", + "@deepseek-ai/dsh-plan-mode": "0.1.6-alpha.2", + "@deepseek-ai/dsh-plugin-manager": "0.1.6-alpha.2", + "@deepseek-ai/dsh-plugin-package-inventory-deepseek": "0.1.6-alpha.2", + "@deepseek-ai/dsh-ptc-runtime": "0.1.6-alpha.2", + "@deepseek-ai/dsh-ptc-runtime-node": "0.1.6-alpha.2", + "@deepseek-ai/dsh-pwsh-local": "0.1.6-alpha.2", + "@deepseek-ai/dsh-pwsh-sandbox": "0.1.6-alpha.2", + "@deepseek-ai/dsh-repeat-tool-reminder": "0.1.6-alpha.2", + "@deepseek-ai/dsh-sandbox": "0.1.6-alpha.2", + "@deepseek-ai/dsh-sandbox-local": "0.1.6-alpha.2", + "@deepseek-ai/dsh-sandbox-policy": "0.1.6-alpha.2", + "@deepseek-ai/dsh-sandbox-windows-acl": "0.1.6-alpha.2", + "@deepseek-ai/dsh-schedule": "0.1.6-alpha.2", + "@deepseek-ai/dsh-scope": "0.1.6-alpha.2", + "@deepseek-ai/dsh-sdk-app": "0.1.6-alpha.2", + "@deepseek-ai/dsh-sdk-jsonrpc-server": "0.1.6-alpha.2", + "@deepseek-ai/dsh-sdk-minimal": "0.1.6-alpha.2", + "@deepseek-ai/dsh-sdk-protocol": "0.1.6-alpha.2", + "@deepseek-ai/dsh-session": "0.1.6-alpha.2", + "@deepseek-ai/dsh-session-checkpoint-policy": "0.1.6-alpha.2", + "@deepseek-ai/dsh-session-format": "0.1.6-alpha.2", + "@deepseek-ai/dsh-session-format-catalog": "0.1.6-alpha.2", + "@deepseek-ai/dsh-session-format-v0-to-v1": "0.1.6-alpha.2", + "@deepseek-ai/dsh-session-format-v1-to-v2": "0.1.6-alpha.2", + "@deepseek-ai/dsh-session-format-v2-to-v3": "0.1.6-alpha.2", + "@deepseek-ai/dsh-session-log-deepseek": "0.1.6-alpha.2", + "@deepseek-ai/dsh-session-log-export": "0.1.6-alpha.2", + "@deepseek-ai/dsh-session-persistence": "0.1.6-alpha.2", + "@deepseek-ai/dsh-session-persistence-jsonl": "0.1.6-alpha.2", + "@deepseek-ai/dsh-session-projection": "0.1.6-alpha.2", + "@deepseek-ai/dsh-session-projection-cache": "0.1.6-alpha.2", + "@deepseek-ai/dsh-session-query": "0.1.6-alpha.2", + "@deepseek-ai/dsh-session-query-sqlite": "0.1.6-alpha.2", + "@deepseek-ai/dsh-session-reference": "0.1.6-alpha.2", + "@deepseek-ai/dsh-session-stats": "0.1.6-alpha.2", + "@deepseek-ai/dsh-session-telemetry": "0.1.6-alpha.2", + "@deepseek-ai/dsh-session-telemetry-otel": "0.1.6-alpha.2", + "@deepseek-ai/dsh-session-title": "0.1.6-alpha.2", + "@deepseek-ai/dsh-session-title-first-prompt-llm": "0.1.6-alpha.2", + "@deepseek-ai/dsh-session-title-llm": "0.1.6-alpha.2", + "@deepseek-ai/dsh-session-turn-outline": "0.1.6-alpha.2", + "@deepseek-ai/dsh-settings": "0.1.6-alpha.2", + "@deepseek-ai/dsh-settings-file": "0.1.6-alpha.2", + "@deepseek-ai/dsh-shell": "0.1.6-alpha.2", + "@deepseek-ai/dsh-shell-env": "0.1.6-alpha.2", + "@deepseek-ai/dsh-skill": "0.1.6-alpha.2", + "@deepseek-ai/dsh-skill-badge": "0.1.6-alpha.2", + "@deepseek-ai/dsh-skill-filesystem": "0.1.6-alpha.2", + "@deepseek-ai/dsh-spill": "0.1.6-alpha.2", + "@deepseek-ai/dsh-spill-local": "0.1.6-alpha.2", + "@deepseek-ai/dsh-spill-policy": "0.1.6-alpha.2", + "@deepseek-ai/dsh-storage": "0.1.6-alpha.2", + "@deepseek-ai/dsh-storage-domain": "0.1.6-alpha.2", + "@deepseek-ai/dsh-storage-json": "0.1.6-alpha.2", + "@deepseek-ai/dsh-subagent": "0.1.6-alpha.2", + "@deepseek-ai/dsh-subagent-fork-in-process": "0.1.6-alpha.2", + "@deepseek-ai/dsh-subagent-in-process-driver": "0.1.6-alpha.2", + "@deepseek-ai/dsh-subagent-spawn-in-process": "0.1.6-alpha.2", + "@deepseek-ai/dsh-subprocess": "0.1.6-alpha.2", + "@deepseek-ai/dsh-subprocess-local": "0.1.6-alpha.2", + "@deepseek-ai/dsh-system-prompt": "0.1.6-alpha.2", + "@deepseek-ai/dsh-terminal": "0.1.6-alpha.2", + "@deepseek-ai/dsh-terminal-bash": "0.1.6-alpha.2", + "@deepseek-ai/dsh-time-context": "0.1.6-alpha.2", + "@deepseek-ai/dsh-timeout": "0.1.6-alpha.2", + "@deepseek-ai/dsh-tmux-context": "0.1.6-alpha.2", + "@deepseek-ai/dsh-token-meter": "0.1.6-alpha.2", + "@deepseek-ai/dsh-tool-ask-user": "0.1.6-alpha.2", + "@deepseek-ai/dsh-tool-bash": "0.1.6-alpha.2", + "@deepseek-ai/dsh-tool-bash-persistent": "0.1.6-alpha.2", + "@deepseek-ai/dsh-tool-call-timeout-policy": "0.1.6-alpha.2", + "@deepseek-ai/dsh-tool-cordis": "0.1.6-alpha.2", + "@deepseek-ai/dsh-tool-fs": "0.1.6-alpha.2", + "@deepseek-ai/dsh-tool-fs-search": "0.1.6-alpha.2", + "@deepseek-ai/dsh-tool-goal": "0.1.6-alpha.2", + "@deepseek-ai/dsh-tool-jobs": "0.1.6-alpha.2", + "@deepseek-ai/dsh-tool-present": "0.1.6-alpha.2", + "@deepseek-ai/dsh-tool-pwsh": "0.1.6-alpha.2", + "@deepseek-ai/dsh-tool-pwsh-persistent": "0.1.6-alpha.2", + "@deepseek-ai/dsh-tool-ralph": "0.1.6-alpha.2", + "@deepseek-ai/dsh-tool-skill": "0.1.6-alpha.2", + "@deepseek-ai/dsh-tool-str-replace-editor": "0.1.6-alpha.2", + "@deepseek-ai/dsh-tool-subagent": "0.1.6-alpha.2", + "@deepseek-ai/dsh-tool-subagent-control": "0.1.6-alpha.2", + "@deepseek-ai/dsh-tool-todo": "0.1.6-alpha.2", + "@deepseek-ai/dsh-tool-web": "0.1.6-alpha.2", + "@deepseek-ai/dsh-tool-workflow": "0.1.6-alpha.2", + "@deepseek-ai/dsh-tools": "0.1.6-alpha.2", + "@deepseek-ai/dsh-typert-loader": "0.1.6-alpha.2", + "@deepseek-ai/dsh-typert-protocol": "0.1.6-alpha.2", + "@deepseek-ai/dsh-typert-registry": "0.1.6-alpha.2", + "@deepseek-ai/dsh-user-approval": "0.1.6-alpha.2", + "@deepseek-ai/dsh-user-questions": "0.1.6-alpha.2", + "@deepseek-ai/dsh-util-crypto": "0.1.6-alpha.2", + "@deepseek-ai/dsh-util-time": "0.1.6-alpha.2", + "@deepseek-ai/dsh-util-values": "0.1.6-alpha.2", + "@deepseek-ai/dsh-util-workspace-path": "0.1.6-alpha.2", + "@deepseek-ai/dsh-web": "0.1.6-alpha.2", + "@deepseek-ai/dsh-web-app": "0.1.6-alpha.2", + "@deepseek-ai/dsh-web-fetch-http": "0.1.6-alpha.2", + "@deepseek-ai/dsh-web-frontend": "0.1.6-alpha.2", + "@deepseek-ai/dsh-web-search-deepseek": "0.1.6-alpha.2", + "@deepseek-ai/dsh-webhook": "0.1.6-alpha.2", + "@deepseek-ai/dsh-webhook-github": "0.1.6-alpha.2", + "@deepseek-ai/dsh-win32-process": "0.1.6-alpha.2", + "@deepseek-ai/dsh-workflow": "0.1.6-alpha.2", + "@deepseek-ai/dsh-workflow-ptc": "0.1.6-alpha.2", + "@deepseek-ai/dsh-workspace": "0.1.6-alpha.2", + "@deepseek-ai/dsh-workspace-changes": "0.1.6-alpha.2", + "@deepseek-ai/libreoffice-kit": "0.0.1", + "@deepseek-ai/libreoffice-kit-darwin-arm64": "0.0.1", + "@deepseek-ai/node-addon-system": "0.1.2", + "@deepseek-ai/node-addon-system-darwin-arm64": "0.1.2", + "@deepseek-ai/schemastery": "3.18.2", + "@opentelemetry/api": "1.9.1", + "react": "18.3.1", + "zod": "4.6.5" +} diff --git a/dsh/docs/pr-assets/issue-261-dsh-slots/harness/e2e/framework-peers-rc.json b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/e2e/framework-peers-rc.json new file mode 100644 index 000000000..5537ed97e --- /dev/null +++ b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/e2e/framework-peers-rc.json @@ -0,0 +1,251 @@ +{ + "@deepseek-ai/cordis": "4.0.2", + "@deepseek-ai/cordis-plugin-group": "1.0.2", + "@deepseek-ai/cordis-plugin-hmr": "1.0.17", + "@deepseek-ai/cordis-plugin-include": "1.0.7", + "@deepseek-ai/cordis-plugin-loader": "1.0.3", + "@deepseek-ai/cordis-plugin-timer": "1.1.4", + "@deepseek-ai/cosmokit": "1.8.3", + "@deepseek-ai/dsh": "0.1.5-rc.2", + "@deepseek-ai/dsh-acp": "0.1.5-rc.2", + "@deepseek-ai/dsh-acp-app": "0.1.5-rc.2", + "@deepseek-ai/dsh-agent": "0.1.5-rc.2", + "@deepseek-ai/dsh-agent-default-model": "0.1.5-rc.2", + "@deepseek-ai/dsh-agent-instructions": "0.1.5-rc.2", + "@deepseek-ai/dsh-agent-loop": "0.1.5-rc.2", + "@deepseek-ai/dsh-agent-presets": "0.1.5-rc.2", + "@deepseek-ai/dsh-agent-tool-presentation": "0.1.5-rc.2", + "@deepseek-ai/dsh-anonymous-user-id": "0.1.5-rc.2", + "@deepseek-ai/dsh-api-gateway": "0.1.5-rc.2", + "@deepseek-ai/dsh-api-remotes": "0.1.5-rc.2", + "@deepseek-ai/dsh-api-session-controller": "0.1.5-rc.2", + "@deepseek-ai/dsh-api-settings-controller": "0.1.5-rc.2", + "@deepseek-ai/dsh-api-workspace-controller": "0.1.5-rc.2", + "@deepseek-ai/dsh-api-workspace-files": "0.1.5-rc.2", + "@deepseek-ai/dsh-app-boot": "0.1.5-rc.2", + "@deepseek-ai/dsh-atomic-write": "0.1.5-rc.2", + "@deepseek-ai/dsh-attachment": "0.1.5-rc.2", + "@deepseek-ai/dsh-attachment-local": "0.1.5-rc.2", + "@deepseek-ai/dsh-authorization": "0.1.5-rc.2", + "@deepseek-ai/dsh-base": "0.1.5-rc.2", + "@deepseek-ai/dsh-bash-local": "0.1.5-rc.2", + "@deepseek-ai/dsh-bash-sandbox": "0.1.5-rc.2", + "@deepseek-ai/dsh-brand": "0.1.5-rc.2", + "@deepseek-ai/dsh-chunked-list": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-connection": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-file-upload": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-hmr": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-locale": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-modules": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-resources": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-store": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-agent-preset": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-approval": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-attachment": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-brand-official": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-chat": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-commands": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-conversation": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-cordis": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-deliverables": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-directory-picker-browse": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-directory-picker-native": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-goal": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-input-trigger": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-jobs": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-layout": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-message-feedback": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-model-selection": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-open-in-app": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-permission-presets": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-plan": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-primitives": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-reference": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-renderer": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-schedule": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-session": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-settings": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-settings-general": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-settings-models": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-settings-plugin-inventory": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-settings-plugins": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-sidebar": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-sidebar-documentpreview": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-sidebar-files": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-sidebar-right": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-skill": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-slots": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-subagent": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-theme": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-tool": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-trajectory": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-user-questions": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-workflow-run": "0.1.5-rc.2", + "@deepseek-ai/dsh-client-ui-workspace": "0.1.5-rc.2", + "@deepseek-ai/dsh-cmdline": "0.1.5-rc.2", + "@deepseek-ai/dsh-code-runtime": "0.1.5-rc.2", + "@deepseek-ai/dsh-code-runtime-worker-thread": "0.1.5-rc.2", + "@deepseek-ai/dsh-command-compact": "0.1.5-rc.2", + "@deepseek-ai/dsh-command-feedback": "0.1.5-rc.2", + "@deepseek-ai/dsh-command-goal": "0.1.5-rc.2", + "@deepseek-ai/dsh-commands": "0.1.5-rc.2", + "@deepseek-ai/dsh-compaction": "0.1.5-rc.2", + "@deepseek-ai/dsh-compaction-basic": "0.1.5-rc.2", + "@deepseek-ai/dsh-compaction-tool-result-pruner": "0.1.5-rc.2", + "@deepseek-ai/dsh-cordis-client-runner": "0.1.5-rc.2", + "@deepseek-ai/dsh-cordis-host-runner": "0.1.5-rc.2", + "@deepseek-ai/dsh-credentials": "0.1.5-rc.2", + "@deepseek-ai/dsh-credentials-local": "0.1.5-rc.2", + "@deepseek-ai/dsh-deepseek-llm-api-extensions": "0.1.5-rc.2", + "@deepseek-ai/dsh-deque": "0.1.5-rc.2", + "@deepseek-ai/dsh-file-reference": "0.1.5-rc.2", + "@deepseek-ai/dsh-file-reference-local": "0.1.5-rc.2", + "@deepseek-ai/dsh-fs": "0.1.5-rc.2", + "@deepseek-ai/dsh-fs-local": "0.1.5-rc.2", + "@deepseek-ai/dsh-fs-observation-policy": "0.1.5-rc.2", + "@deepseek-ai/dsh-fs-sandbox": "0.1.5-rc.2", + "@deepseek-ai/dsh-goal": "0.1.5-rc.2", + "@deepseek-ai/dsh-goal-round-driver": "0.1.5-rc.2", + "@deepseek-ai/dsh-headless": "0.1.5-rc.2", + "@deepseek-ai/dsh-home-paths": "0.1.5-rc.2", + "@deepseek-ai/dsh-hook-protocol": "0.1.5-rc.2", + "@deepseek-ai/dsh-hooks-claude-code": "0.1.5-rc.2", + "@deepseek-ai/dsh-hooks-codex": "0.1.5-rc.2", + "@deepseek-ai/dsh-host-directory-picker": "0.1.5-rc.2", + "@deepseek-ai/dsh-host-directory-picker-auto": "0.1.5-rc.2", + "@deepseek-ai/dsh-host-directory-picker-browse": "0.1.5-rc.2", + "@deepseek-ai/dsh-host-directory-picker-native": "0.1.5-rc.2", + "@deepseek-ai/dsh-host-frontend-static": "0.1.5-rc.2", + "@deepseek-ai/dsh-host-open-in-app": "0.1.5-rc.2", + "@deepseek-ai/dsh-host-plugin-inventory": "0.1.5-rc.2", + "@deepseek-ai/dsh-host-webserver": "0.1.5-rc.2", + "@deepseek-ai/dsh-http-proxy": "0.1.5-rc.2", + "@deepseek-ai/dsh-invariants": "0.1.5-rc.2", + "@deepseek-ai/dsh-jobs": "0.1.5-rc.2", + "@deepseek-ai/dsh-jobs-local": "0.1.5-rc.2", + "@deepseek-ai/dsh-launch-environment": "0.1.5-rc.2", + "@deepseek-ai/dsh-llm": "0.1.5-rc.2", + "@deepseek-ai/dsh-llm-deepseek": "0.1.5-rc.2", + "@deepseek-ai/dsh-llm-pi-ai": "0.1.5-rc.2", + "@deepseek-ai/dsh-llm-retry": "0.1.5-rc.2", + "@deepseek-ai/dsh-mcp-client": "0.1.5-rc.2", + "@deepseek-ai/dsh-message-feedback": "0.1.5-rc.2", + "@deepseek-ai/dsh-native-command": "0.1.5-rc.2", + "@deepseek-ai/dsh-output-retention": "0.1.5-rc.2", + "@deepseek-ai/dsh-package-manifest": "0.1.5-rc.2", + "@deepseek-ai/dsh-permission-presets": "0.1.5-rc.2", + "@deepseek-ai/dsh-persona": "0.1.5-rc.2", + "@deepseek-ai/dsh-plan-mode": "0.1.5-rc.2", + "@deepseek-ai/dsh-plugin-package-inventory-deepseek": "0.1.5-rc.2", + "@deepseek-ai/dsh-pwsh-local": "0.1.5-rc.2", + "@deepseek-ai/dsh-pwsh-sandbox": "0.1.5-rc.2", + "@deepseek-ai/dsh-repeat-tool-reminder": "0.1.5-rc.2", + "@deepseek-ai/dsh-sandbox": "0.1.5-rc.2", + "@deepseek-ai/dsh-sandbox-local": "0.1.5-rc.2", + "@deepseek-ai/dsh-sandbox-policy": "0.1.5-rc.2", + "@deepseek-ai/dsh-sandbox-windows-acl": "0.1.5-rc.2", + "@deepseek-ai/dsh-schedule": "0.1.5-rc.2", + "@deepseek-ai/dsh-scope": "0.1.5-rc.2", + "@deepseek-ai/dsh-sdk-app": "0.1.5-rc.2", + "@deepseek-ai/dsh-sdk-jsonrpc-server": "0.1.5-rc.2", + "@deepseek-ai/dsh-sdk-minimal": "0.1.5-rc.2", + "@deepseek-ai/dsh-sdk-protocol": "0.1.5-rc.2", + "@deepseek-ai/dsh-session": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-checkpoint-policy": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-format": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-format-catalog": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-format-v0-to-v1": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-format-v1-to-v2": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-format-v2-to-v3": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-log-deepseek": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-log-export": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-persistence": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-persistence-jsonl": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-projection": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-projection-cache": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-query": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-query-sqlite": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-reference": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-stats": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-telemetry": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-telemetry-otel": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-title": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-title-first-prompt-llm": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-title-llm": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-turn-outline": "0.1.5-rc.2", + "@deepseek-ai/dsh-settings": "0.1.5-rc.2", + "@deepseek-ai/dsh-settings-file": "0.1.5-rc.2", + "@deepseek-ai/dsh-shell": "0.1.5-rc.2", + "@deepseek-ai/dsh-shell-env": "0.1.5-rc.2", + "@deepseek-ai/dsh-skill": "0.1.5-rc.2", + "@deepseek-ai/dsh-skill-badge": "0.1.5-rc.2", + "@deepseek-ai/dsh-skill-filesystem": "0.1.5-rc.2", + "@deepseek-ai/dsh-spill": "0.1.5-rc.2", + "@deepseek-ai/dsh-spill-local": "0.1.5-rc.2", + "@deepseek-ai/dsh-spill-policy": "0.1.5-rc.2", + "@deepseek-ai/dsh-storage": "0.1.5-rc.2", + "@deepseek-ai/dsh-storage-domain": "0.1.5-rc.2", + "@deepseek-ai/dsh-storage-json": "0.1.5-rc.2", + "@deepseek-ai/dsh-subagent": "0.1.5-rc.2", + "@deepseek-ai/dsh-subagent-fork-in-process": "0.1.5-rc.2", + "@deepseek-ai/dsh-subagent-in-process-driver": "0.1.5-rc.2", + "@deepseek-ai/dsh-subagent-spawn-in-process": "0.1.5-rc.2", + "@deepseek-ai/dsh-subprocess": "0.1.5-rc.2", + "@deepseek-ai/dsh-subprocess-local": "0.1.5-rc.2", + "@deepseek-ai/dsh-system-prompt": "0.1.5-rc.2", + "@deepseek-ai/dsh-terminal": "0.1.5-rc.2", + "@deepseek-ai/dsh-terminal-bash": "0.1.5-rc.2", + "@deepseek-ai/dsh-time-context": "0.1.5-rc.2", + "@deepseek-ai/dsh-timeout": "0.1.5-rc.2", + "@deepseek-ai/dsh-tmux-context": "0.1.5-rc.2", + "@deepseek-ai/dsh-token-meter": "0.1.5-rc.2", + "@deepseek-ai/dsh-tool-ask-user": "0.1.5-rc.2", + "@deepseek-ai/dsh-tool-bash": "0.1.5-rc.2", + "@deepseek-ai/dsh-tool-bash-persistent": "0.1.5-rc.2", + "@deepseek-ai/dsh-tool-call-timeout-policy": "0.1.5-rc.2", + "@deepseek-ai/dsh-tool-cordis": "0.1.5-rc.2", + "@deepseek-ai/dsh-tool-fs": "0.1.5-rc.2", + "@deepseek-ai/dsh-tool-fs-search": "0.1.5-rc.2", + "@deepseek-ai/dsh-tool-goal": "0.1.5-rc.2", + "@deepseek-ai/dsh-tool-jobs": "0.1.5-rc.2", + "@deepseek-ai/dsh-tool-present": "0.1.5-rc.2", + "@deepseek-ai/dsh-tool-pwsh": "0.1.5-rc.2", + "@deepseek-ai/dsh-tool-pwsh-persistent": "0.1.5-rc.2", + "@deepseek-ai/dsh-tool-ralph": "0.1.5-rc.2", + "@deepseek-ai/dsh-tool-skill": "0.1.5-rc.2", + "@deepseek-ai/dsh-tool-str-replace-editor": "0.1.5-rc.2", + "@deepseek-ai/dsh-tool-subagent": "0.1.5-rc.2", + "@deepseek-ai/dsh-tool-subagent-control": "0.1.5-rc.2", + "@deepseek-ai/dsh-tool-todo": "0.1.5-rc.2", + "@deepseek-ai/dsh-tool-web": "0.1.5-rc.2", + "@deepseek-ai/dsh-tool-workflow": "0.1.5-rc.2", + "@deepseek-ai/dsh-tools": "0.1.5-rc.2", + "@deepseek-ai/dsh-typert-loader": "0.1.5-rc.2", + "@deepseek-ai/dsh-typert-protocol": "0.1.5-rc.2", + "@deepseek-ai/dsh-typert-registry": "0.1.5-rc.2", + "@deepseek-ai/dsh-user-approval": "0.1.5-rc.2", + "@deepseek-ai/dsh-user-questions": "0.1.5-rc.2", + "@deepseek-ai/dsh-util-crypto": "0.1.5-rc.2", + "@deepseek-ai/dsh-util-time": "0.1.5-rc.2", + "@deepseek-ai/dsh-util-values": "0.1.5-rc.2", + "@deepseek-ai/dsh-util-workspace-path": "0.1.5-rc.2", + "@deepseek-ai/dsh-web": "0.1.5-rc.2", + "@deepseek-ai/dsh-web-app": "0.1.5-rc.2", + "@deepseek-ai/dsh-web-fetch-http": "0.1.5-rc.2", + "@deepseek-ai/dsh-web-frontend": "0.1.5-rc.2", + "@deepseek-ai/dsh-web-search-deepseek": "0.1.5-rc.2", + "@deepseek-ai/dsh-webhook": "0.1.5-rc.2", + "@deepseek-ai/dsh-webhook-github": "0.1.5-rc.2", + "@deepseek-ai/dsh-win32-process": "0.1.5-rc.2", + "@deepseek-ai/dsh-workflow": "0.1.5-rc.2", + "@deepseek-ai/dsh-workflow-worker-thread": "0.1.5-rc.2", + "@deepseek-ai/dsh-workspace": "0.1.5-rc.2", + "@deepseek-ai/node-addon-system": "0.1.2", + "@deepseek-ai/node-addon-system-darwin-arm64": "0.1.2", + "@deepseek-ai/schemastery": "3.18.2", + "@modelcontextprotocol/sdk": "1.30.0", + "@opentelemetry/api": "1.9.1", + "hono": "4.13.8", + "react": "18.3.1", + "zod": "4.6.5" +} diff --git a/dsh/docs/pr-assets/issue-261-dsh-slots/harness/e2e/peer-fixture/client.js b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/e2e/peer-fixture/client.js new file mode 100644 index 000000000..1e66bf598 --- /dev/null +++ b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/e2e/peer-fixture/client.js @@ -0,0 +1,19 @@ +window.__ModuleLoader__.load({ + id: 'issue261-turn-tail-peers', + factory: require => { + const React = require('react'); + return { + inject: ['slots'], + apply(ctx) { + for (const [id, label, order] of [ + ['issue261-peer-a:tail', 'Compatibility peer A', -20], + ['issue261-peer-b:tail', 'Compatibility peer B', -10], + ]) { + ctx.slots.inject('conversation.chat.turnTail', () => ctx.slots.register({ + name: 'conversation.chat.turnTail', id, order, select: () => null, + }, () => React.createElement('div', { 'data-compatibility-peer': id }, label))); + } + }, + }; + }, +}); diff --git a/dsh/docs/pr-assets/issue-261-dsh-slots/harness/e2e/peer-fixture/cordis.patch.yml b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/e2e/peer-fixture/cordis.patch.yml new file mode 100644 index 000000000..3e9e16869 --- /dev/null +++ b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/e2e/peer-fixture/cordis.patch.yml @@ -0,0 +1,3 @@ +- insert: + - id: issue261-turn-tail-peers + name: issue261-turn-tail-peers diff --git a/dsh/docs/pr-assets/issue-261-dsh-slots/harness/e2e/peer-fixture/index.js b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/e2e/peer-fixture/index.js new file mode 100644 index 000000000..1d0cfcdad --- /dev/null +++ b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/e2e/peer-fixture/index.js @@ -0,0 +1,2 @@ +export const name = 'issue261-turn-tail-peers'; +export function apply() {} diff --git a/dsh/docs/pr-assets/issue-261-dsh-slots/harness/e2e/peer-fixture/package.json b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/e2e/peer-fixture/package.json new file mode 100644 index 000000000..70e09b864 --- /dev/null +++ b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/e2e/peer-fixture/package.json @@ -0,0 +1,14 @@ +{ + "name": "issue261-turn-tail-peers", + "version": "0.0.0", + "private": true, + "type": "module", + "main": "./index.js", + "exports": { ".": "./index.js", "./client": "./client.js", "./package.json": "./package.json" }, + "files": ["index.js", "client.js", "cordis.patch.yml"], + "peerDependencies": { "@deepseek-ai/cordis": "^4.0.2", "react": "^18.2.0" }, + "dsh": { + "bundle": { "patch": "./cordis.patch.yml" }, + "client": { "inject": ["@deepseek-ai/dsh-client-ui-renderer", "@deepseek-ai/dsh-client-ui-chat"], "platform": "web" } + } +} diff --git a/dsh/docs/pr-assets/issue-261-dsh-slots/harness/evidence-summary.json b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/evidence-summary.json new file mode 100644 index 000000000..032378038 --- /dev/null +++ b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/evidence-summary.json @@ -0,0 +1,350 @@ +{ + "issue": 261, + "recordedAt": "2026-09-18T03:34:30.164Z", + "baselineCommit": "02ce994c8f05b6bf83a6b5872bd018bbfc8970ce", + "fixedCommit": "2a593c9a6f6510afc0e12704ff2103abc1a1f597", + "sourceAliases": false, + "frameworkSourceModifications": false, + "nativeRuntimeMemoryDistinction": "The fixture writes through the real Runtime Source JSON storage; the Native CLI is configured and version-checked. It does not claim a Native memory-space backend write.", + "artifactCount": 17, + "artifacts": [ + { + "name": "dsh-mnemon", + "version": "0.5.11", + "filename": "dsh-mnemon-0.5.11.tgz", + "integrity": "sha512-FFzaR68Ita2O08qveSa2i8hofy3yqOF2qNnr5pfOOUQWQHkLdYvtnUeTBJ6kvFgnzskz/azo/spCFRRJJzFNPQ==", + "sha256": "57976643ee3577a8b4c3cd9dacd6eea49b3814ffb6fc33f31161802a10297336", + "reusedUnchangedBaselineCompanion": false + }, + { + "name": "dsh-mnemon-provider-byterover", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-byterover-0.5.4.tgz", + "integrity": "sha512-0/ru18uVKNILv3Q2Dx7soOpm+9SrARnbxCsewmQKPy8fvj14zwd7TNLlI9/lV0/vN5SgjKq95PnOvsJZVD/krw==", + "sha256": "94677bb5a27261dd2051f67c9ae5bc41c17ea86a4ea95cbd0baee1728203633f", + "reusedUnchangedBaselineCompanion": true + }, + { + "name": "dsh-mnemon-provider-hindsight", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-hindsight-0.5.4.tgz", + "integrity": "sha512-nQ3rQ83J2ugNLXDkG1eYPjpibFANoe3n9OkxfyPCxXkAHHKYdmIZ35mzCFG8sMWsDeSt8y1RezulWjima4ZAVw==", + "sha256": "bf527bc83d51bd7b2e510ed3be59665ae6b3ddbd4e1c3beee16c32235fa0afbb", + "reusedUnchangedBaselineCompanion": true + }, + { + "name": "dsh-mnemon-provider-holographic", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-holographic-0.5.4.tgz", + "integrity": "sha512-ULO6MBVq8rmRMlMnDcqJ26XPsk1K5j3HPjtTXHoBlEy6GdYQ7/2vZLtDQZ9FNjMZ9VT/W6Qj+RAc1HfgIj3bZA==", + "sha256": "67f9cc593a7bcf33001e0817a4932e23cf9f0927e90b6bc2b2ccd5ef49b91259", + "reusedUnchangedBaselineCompanion": true + }, + { + "name": "dsh-mnemon-provider-honcho", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-honcho-0.5.4.tgz", + "integrity": "sha512-xbbDeshwpiFZHlIUFveMbv6o6xTRY1sjNUvkcXKN/oInxCYdV4sC7Q6pBMRoSoSXYoAgUVExLLXxG+p9Z7uDZA==", + "sha256": "c102952aa1b9fdd953b25068663635a9ffad606bb39c68b56eafa538c8fab72d", + "reusedUnchangedBaselineCompanion": true + }, + { + "name": "dsh-mnemon-provider-mem0", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-mem0-0.5.4.tgz", + "integrity": "sha512-jriF73KpQp6DoH0RbdfJx+6HzUGEGq1/zy1Y8DmyRaRF62OhBmySGSTHZHasVu1kw61NWzSqIBFWFVn6La0wYQ==", + "sha256": "42cd225bb376d387d8e7f5503d64b6a00ddae4f5aea26b495cb9d5aa44dab3b0", + "reusedUnchangedBaselineCompanion": true + }, + { + "name": "dsh-mnemon-provider-mnemon-native", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-mnemon-native-0.5.5.tgz", + "integrity": "sha512-M6v7W2MomcPaPEcf5vUVfJa0j1HUdEmHwdCYR+jmK05doyuP/Vfg/+S2ZnpySKWEGu7jDlgRQbd5hb8RANKahg==", + "sha256": "cb1532ec04e1bc2550456340b453717686c2bc399e742e61be529bdae5e6f812", + "reusedUnchangedBaselineCompanion": true + }, + { + "name": "dsh-mnemon-provider-openviking", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-openviking-0.5.5.tgz", + "integrity": "sha512-7XP4we0/Zl9QasILR5SvEGEONk7YSXj1X9PTpPZ7eS6IgCV3uauhTV5aqZ0d2VPgSR7MYaq1nWnc0MuEZfVbXA==", + "sha256": "da3505e93462a7860d331e830930a04d4e2406be5b540333f340f9351346920f", + "reusedUnchangedBaselineCompanion": true + }, + { + "name": "dsh-mnemon-provider-retaindb", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-retaindb-0.5.4.tgz", + "integrity": "sha512-wztasXBFvV0M4xnJM0G0K819EMWLB0gVE0rvc4CY7XkmnqTAPoZ/26pZT9WY6ENHj84onkC2ck58xy7eqiX70A==", + "sha256": "a7b5f1efb4e68df2d7ccc7e6078cd8c3229c82cf14f0162a196c975cbe19727f", + "reusedUnchangedBaselineCompanion": true + }, + { + "name": "dsh-mnemon-provider-supermemory", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-supermemory-0.5.4.tgz", + "integrity": "sha512-hB6G2B5hu1ktMyqP5Al7b9uLVfV876Fs5zrgh5URlaE25e4s3VrJBkzebq5ZuhSocTDYFKMsZL4AWQGLFkb0Rg==", + "sha256": "d04b900f7f9e9359ba53b349bdbc4550fc19b350771e61df3aa451e8f67f9afd", + "reusedUnchangedBaselineCompanion": true + }, + { + "name": "dsh-mnemon-source-documents", + "version": "0.5.6", + "filename": "dsh-mnemon-source-documents-0.5.6.tgz", + "integrity": "sha512-1wVN68FNzsAuiS3msECGC2RtEC2m+aTmGPCdjvYxvIJFncEyJbJ56mdf8mwiJNt8lul5Kuzvyr3G6c0OxCmQUw==", + "sha256": "d34ba96c9e1de64dcdec661b5b1e9a5bf84166b8b261e6ddf4f9756c7d50c0ce", + "reusedUnchangedBaselineCompanion": true + }, + { + "name": "dsh-mnemon-source-memory-spaces", + "version": "0.5.8", + "filename": "dsh-mnemon-source-memory-spaces-0.5.8.tgz", + "integrity": "sha512-2NNbKuMnbWcGQ0gXFjY7u6C/m+fIkjh9QscOf2Rc+LjdOHtVZUduOOV2EPFMMgOHiz8eqtvuqY9Wu6S0jj75aA==", + "sha256": "c3ea9dd2cb5b4ff50a914d904156dd4028551e9544672198263a01095bde9bc3", + "reusedUnchangedBaselineCompanion": true + }, + { + "name": "dsh-mnemon-source-runtime", + "version": "0.5.8", + "filename": "dsh-mnemon-source-runtime-0.5.8.tgz", + "integrity": "sha512-kOoHYl8pF1LVzABQcWpAMy0d/H6YuyVJCajyHVC7i5yPDyJpG0zKwTSL2UI+ncvPfvqKng+yOcdDVRAUfs0/mg==", + "sha256": "e9c7313b640c367d0f9fdee9638d93cc755396e59f2c88d6b3c9adaecf1c95ff", + "reusedUnchangedBaselineCompanion": true + }, + { + "name": "dsh-mnemon-strategy-auto-capture", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-auto-capture-0.5.4.tgz", + "integrity": "sha512-SYHrc0H2Pxgs5dqa48AgoYudnAlkB450H8dqAz7ILiFGEo4V2u6RQuBsF5A7QOJXwV9v3fd0YQ9eAcc5tFds9Q==", + "sha256": "3816601abd83f0c5c679f3e4c9ae96c8f697e8e15f6546e975a96fd7f86b8c02", + "reusedUnchangedBaselineCompanion": true + }, + { + "name": "dsh-mnemon-strategy-default-three-tier", + "version": "0.5.5", + "filename": "dsh-mnemon-strategy-default-three-tier-0.5.5.tgz", + "integrity": "sha512-6cg2mz8QyYKYjdk0YnW+IYp2TEQQsZatj89CwsDXU50sN2D8dDal5TZwBl5+qRa5MZj11mIN5ZENWCDcBqTFxw==", + "sha256": "52af5caf635f2cf2b165feff1ff3d35d241e6d073771b7b92b3ea37b36ff589f", + "reusedUnchangedBaselineCompanion": true + }, + { + "name": "dsh-mnemon-strategy-light-context", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-light-context-0.5.4.tgz", + "integrity": "sha512-hLhVwtVC7s9Zf0xyvm8GfY/IYbK+2o98WMBBo2gZQ+5CCzx2kAQzxPCkTaxFstfxS9IZlBrfmkzI7Z+8zC+KDw==", + "sha256": "46261e60c2c720ec8ccca5fe13e2d36d0125bbffcfeb2c09cf5f941009dca7f0", + "reusedUnchangedBaselineCompanion": true + }, + { + "name": "dsh-mnemon-strategy-scoped", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-scoped-0.5.4.tgz", + "integrity": "sha512-MA2LxRmNPwgy1hZE8X4uZXvEcOuhUmVNbLvjWXRw2dlZj3OABO2BbuU49ZDpQtULJO3V/FzZO7IWECTC+xv+Qw==", + "sha256": "d4f09db97300a81d64446739cd99b777acfc46892080f3935f81c2bcc5f97910", + "reusedUnchangedBaselineCompanion": true + } + ], + "cases": [ + { + "name": "baseline-alpha", + "version": "0.1.6-alpha.2", + "dshPackageCount": 251, + "mnemonPackageCount": 17, + "testClientPackages": [], + "normalNpmInstall": false, + "baselineOnlyLegacyPeerBypass": true, + "originalPublicModulesChecked": [ + { + "package": "@deepseek-ai/dsh-client-ui-slots", + "file": "lib/index.js", + "sha256": "57e1314e31a2015f30231e1e7596da658f1bb5cb64c103c4cf13381647412c99" + }, + { + "package": "@deepseek-ai/dsh-client-ui-chat", + "file": "lib/client.js", + "sha256": "a022200a86e6ba0301c861f6114214ee5fe0a849401332d1962276f142641f9e" + }, + { + "package": "@deepseek-ai/dsh-client-ui-renderer", + "file": "lib/client.js", + "sha256": "ed156076ac2a5c26e6db40d8fab165a0ec8dd4f6d36f6cf0e595111bf4afea34" + }, + { + "package": "@deepseek-ai/dsh-client-ui-conversation", + "file": "lib/client.js", + "sha256": "c02a68ebd98c7f32f70ab3302a6a6f5304c61717f28e87f616b070cbf105dabe" + } + ], + "headless": { + "result": "pass", + "nativeVersion": "mnemon version 0.2.8", + "enabledMnemonToolCount": 17, + "disabledMnemonToolCount": 0, + "persistence": [ + { + "file": "runtime/memories.json", + "sha256": "f401893f81deb16cae6e15beeb5df2f3555534bb24a5049a1ee5fb230351e6dc" + } + ] + } + }, + { + "name": "fixed-alpha", + "version": "0.1.6-alpha.2", + "dshPackageCount": 251, + "mnemonPackageCount": 17, + "testClientPackages": [ + "issue261-turn-tail-peers" + ], + "normalNpmInstall": true, + "baselineOnlyLegacyPeerBypass": false, + "originalPublicModulesChecked": [ + { + "package": "@deepseek-ai/dsh-client-ui-slots", + "file": "lib/index.js", + "sha256": "57e1314e31a2015f30231e1e7596da658f1bb5cb64c103c4cf13381647412c99" + }, + { + "package": "@deepseek-ai/dsh-client-ui-chat", + "file": "lib/client.js", + "sha256": "a022200a86e6ba0301c861f6114214ee5fe0a849401332d1962276f142641f9e" + }, + { + "package": "@deepseek-ai/dsh-client-ui-renderer", + "file": "lib/client.js", + "sha256": "ed156076ac2a5c26e6db40d8fab165a0ec8dd4f6d36f6cf0e595111bf4afea34" + }, + { + "package": "@deepseek-ai/dsh-client-ui-conversation", + "file": "lib/client.js", + "sha256": "c02a68ebd98c7f32f70ab3302a6a6f5304c61717f28e87f616b070cbf105dabe" + } + ], + "headless": { + "result": "pass", + "nativeVersion": "mnemon version 0.2.8", + "enabledMnemonToolCount": 17, + "disabledMnemonToolCount": 0, + "persistence": [ + { + "file": "runtime/memories.json", + "sha256": "6de65bc0bae079790fd8614b3fb4704466faa35c0e435d5e3e084aa00272a8b6" + } + ] + } + }, + { + "name": "fixed-rc", + "version": "0.1.5-rc.2", + "dshPackageCount": 234, + "mnemonPackageCount": 17, + "testClientPackages": [ + "issue261-turn-tail-peers" + ], + "normalNpmInstall": true, + "baselineOnlyLegacyPeerBypass": false, + "originalPublicModulesChecked": [ + { + "package": "@deepseek-ai/dsh-client-ui-slots", + "file": "lib/index.js", + "sha256": "5433ef2cb74699a59000052c4b1899808c83544f45ba6176f297efc68414c588" + }, + { + "package": "@deepseek-ai/dsh-client-ui-chat", + "file": "lib/client.js", + "sha256": "6527556d25c7b2a27f2e3bfb96cc2ae3d9244f1213147e245b17684ad8d9e396" + }, + { + "package": "@deepseek-ai/dsh-client-ui-renderer", + "file": "lib/client.js", + "sha256": "859d0a187e436d682c71d18f0266d588f923d7e1628157ad73000f99a377d151" + }, + { + "package": "@deepseek-ai/dsh-client-ui-conversation", + "file": "lib/client.js", + "sha256": "81314dfd95864f2522f8edb812e3f8e08b04a8ef2141913e6e8cbdaae1ffc37f" + } + ], + "headless": { + "result": "pass", + "nativeVersion": "mnemon version 0.2.8", + "enabledMnemonToolCount": 17, + "disabledMnemonToolCount": 0, + "persistence": [ + { + "file": "runtime/memories.json", + "sha256": "ab147c515b60abe3513f710f134d96126c642f3e1ec2d0af7e9ef7fc5280b95a" + } + ] + } + } + ], + "fixtureProtocols": [ + "OpenAI chat completions SSE", + "Anthropic Messages SSE" + ], + "fixtureFailureClassification": "The initial OpenAI-only fixture failed on alpha Anthropic requests; those retained attempts are fixture failures. The missing turnTail ID was independently reproduced by the parent in the real browser.", + "browserOwner": "The surrounding evidence report and screenshots document browser verification. This reusable harness does not automate the browser.", + "sourceBundleScriptHashes": [ + { + "file": "pack-artifacts.mjs", + "sha256": "e695a91b01fa17ef55b8954b24481aee3beea0eb9ad8476d429000c2d47677b0" + }, + { + "file": "assemble-fixed-artifacts.mjs", + "sha256": "a101bef2ba8d41d4eaeb8b298fca92a95cdcb8a578535efd089d89a47b32db3f" + }, + { + "file": "packed-e2e.mjs", + "sha256": "bb41816d293e369bea096d50b800c814d58c0de3def66fe4c4a65218868d3b80" + }, + { + "file": "compatibility-model.mjs", + "sha256": "844f685eb57190e2f414add7cf0bf3af66fb98ef397a82d03cb92a12683207bb" + }, + { + "file": "headless-check.mjs", + "sha256": "5b1a38435d9cc781fc238dd66ab5437a10baeb0fbe73250d18e2bb4ce80bfd32" + }, + { + "file": "framework-pins.mjs", + "sha256": "9fc56d492618e9db1fcf637b2b9dac69a1acfdeb13b48dab73127e9807117db2" + }, + { + "file": "pack-peer-fixture.mjs", + "sha256": "66f71ecf5086ec1611e1299d020d34cfde49547760ff027eb8a8a58d6b7c931e" + }, + { + "file": "HARNESS.md", + "sha256": "ba8fb1f2e08d01a45b22f8779fb19d6a0f81d2cebdfeb56710b57bfbb9f34011" + } + ], + "bundledHarnessAudit": "bundle-audit.json", + "rawRunArtifactsIncluded": false, + "rawRunArtifactsNote": "Detailed local logs, dependency trees, model requests, bootstrap URLs, and tarballs are excluded; summarized results and artifact hashes are retained.", + "copiedHarnessValidation": { + "headless": [ + { + "cohort": "alpha", + "version": "0.1.6-alpha.2", + "result": "pass", + "nativeVersion": "mnemon version 0.2.8", + "enabledMnemonToolCount": 17, + "disabledMnemonToolCount": 0, + "completedAt": "2026-09-18T03:41:00.148Z" + }, + { + "cohort": "rc", + "version": "0.1.5-rc.2", + "result": "pass", + "nativeVersion": "mnemon version 0.2.8", + "enabledMnemonToolCount": 17, + "disabledMnemonToolCount": 0, + "completedAt": "2026-09-18T03:41:00.515Z" + } + ], + "peerRepackIntegrityMatchesRecordedRun": true, + "peerIntegrity": "sha512-u4Y8TtOwKETc0dn+tldH5FdU6thrydv0qMaUY1Ly2h5KZgcgbF++Qpt/eZKuEUMlUTgCerx576gimkwMabXkiA==" + } +} diff --git a/dsh/docs/pr-assets/issue-261-dsh-slots/harness/framework-pins.mjs b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/framework-pins.mjs new file mode 100644 index 000000000..9f7fa0ada --- /dev/null +++ b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/framework-pins.mjs @@ -0,0 +1,24 @@ +import assert from 'node:assert/strict'; +import { readFile, readdir } from 'node:fs/promises'; +import { join } from 'node:path'; + +/** Snapshot physically installed public framework packages; never optional platforms from a lockfile. */ +export async function frameworkPins(root, expectedVersion) { + const pins = {}; + for (const directory of await readdir(join(root, 'node_modules/@deepseek-ai'))) { + let manifest; + try { manifest = JSON.parse(await readFile(join(root, 'node_modules/@deepseek-ai', directory, 'package.json'), 'utf8')); } + catch (error) { if (error.code === 'ENOENT') continue; throw error; } + if (manifest.name.startsWith('@deepseek-ai/dsh')) assert.equal(manifest.version, expectedVersion, `mixed framework package: ${directory}`); + pins[`@deepseek-ai/${directory}`] = manifest.version; + } + const lock = JSON.parse(await readFile(join(root, 'package-lock.json'), 'utf8')); + for (const [path, entry] of Object.entries(lock.packages)) { + if (!path.startsWith('node_modules/') || path.includes('/node_modules/') || !entry.peer) continue; + const name = path.slice('node_modules/'.length); + if (name.startsWith('@deepseek-ai/')) continue; + try { pins[name] = JSON.parse(await readFile(join(root, path, 'package.json'), 'utf8')).version; } + catch (error) { if (error.code !== 'ENOENT') throw error; } + } + return pins; +} diff --git a/dsh/docs/pr-assets/issue-261-dsh-slots/harness/headless-check.mjs b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/headless-check.mjs new file mode 100644 index 000000000..5294dc2b6 --- /dev/null +++ b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/headless-check.mjs @@ -0,0 +1,140 @@ +import assert from 'node:assert/strict'; +import { createServer } from 'node:http'; +import { readFile, writeFile, appendFile, mkdir, symlink, realpath, readdir } from 'node:fs/promises'; +import { spawn } from 'node:child_process'; +import { createHash } from 'node:crypto'; +import { resolve, join } from 'node:path'; +import { compatibilityModel, sendModelReply, normalizeRequest, responseProtocol } from './compatibility-model.mjs'; + +const args = new Map(); +for (let i = 2; i < process.argv.length; i += 2) args.set(process.argv[i], process.argv[i + 1]); +assert(args.has('--consumer') && args.has('--output'), 'supply --consumer and --output'); +const consumer = await realpath(resolve(args.get('--consumer'))); +const output = resolve(args.get('--output')); +const nativeInput = args.get('--native-cli') ?? process.env.MNEMON_CLI_PATH; +assert(nativeInput, 'supply --native-cli or MNEMON_CLI_PATH'); +const nativeCli = await realpath(resolve(nativeInput)); +const sourceManifest = JSON.parse(await readFile(join(consumer, 'package.json'), 'utf8')); +const lock = JSON.parse(await readFile(join(consumer, 'package-lock.json'), 'utf8')); +const dshManifest = JSON.parse(await readFile(join(consumer, 'node_modules/@deepseek-ai/dsh/package.json'), 'utf8')); +const version = dshManifest.version; +assert(['0.1.5-rc.2', '0.1.6-alpha.2'].includes(version)); +const dshPackages = Object.entries(lock.packages).flatMap(([path, entry]) => /(?:^|\/)node_modules\/@deepseek-ai\/dsh(?:-[^/]+)?$/.test(path) ? [{ path, version: entry.version }] : []); +assert(dshPackages.length > 200 && dshPackages.every(entry => entry.version === version), 'headless must reuse one exact published DSH cohort'); +const mnemonPackages = Object.entries(lock.packages).filter(([path]) => /^node_modules\/dsh-mnemon(?:-[^/]+)?$/.test(path)); +assert.equal(mnemonPackages.length, 17); +assert(mnemonPackages.every(([, entry]) => /^http:\/\/127\.0\.0\.1:\d+\/artifacts\//.test(entry.resolved) && typeof entry.integrity === 'string'), 'all Mnemon packages must have packed loopback provenance'); +const home = join(output, 'dsh-home'); +const profile = join(home, 'profiles/headless'); +const workspace = join(output, 'workspace'); +const data = join(output, 'mnemon-data'); +await Promise.all([profile, workspace, data].map(path => mkdir(path, { recursive: true }))); +const profileManifest = { name: 'issue261-packed-headless', private: true, type: 'module', dependencies: sourceManifest.dependencies, dsh: { profile: { bundles: ['@deepseek-ai/dsh-base', '@deepseek-ai/dsh-headless', 'dsh-mnemon'] } } }; +await writeFile(join(profile, 'package.json'), JSON.stringify(profileManifest, null, 2) + '\n'); +await symlink(join(consumer, 'node_modules'), join(profile, 'node_modules'), 'dir'); +const disabled = ['subprocess', 'bash-sandbox', 'pwsh-sandbox', 'tool-bash', 'tool-pwsh', 'permission', 'tool-fs-search']; +const extensions = ['mnemon-strategy-scoped', 'mnemon-strategy-light-context', 'mnemon-strategy-auto-capture']; +const patch = disabled.map(id => `- id: ${id}\n disabled: true\n`).join('') + extensions.map(id => `- id: ${id}\n disabled: false\n`).join(''); +const patchFile = join(profile, 'cordis.patch.yml'); +await writeFile(patchFile, patch); +await writeFile(join(home, 'settings.yaml'), `mnemon:\n cliPath: ${JSON.stringify(nativeCli)}\n idleReview:\n enabled: false\n persistenceStrategy:\n mode: manual\n`); +await writeFile(join(workspace, 'README.md'), '# Issue 261 isolated Headless fixture\n'); +const requests = []; +let phase = 'setup'; +const decide = compatibilityModel(event => { void appendFile(join(output, 'model-events.jsonl'), JSON.stringify({ phase, ...event }) + '\n'); }); +const model = createServer(async (request, response) => { + try { + let raw = ''; + for await (const chunk of request) raw += chunk; + const body = JSON.parse(raw); + const number = requests.push({ phase, path: request.url, body }); + await appendFile(join(output, 'model-requests.jsonl'), JSON.stringify({ phase, number, path: request.url, protocol: responseProtocol(body, request.url), body }) + '\n'); + const reply = decide(body); + sendModelReply(response, body, request.url, reply, `issue261-headless-${number}`); + } catch (error) { + await appendFile(join(output, 'model-errors.log'), String(error.stack ?? error) + '\n'); + response.writeHead(500, { 'content-type': 'application/json' }); + response.end(JSON.stringify({ error: { message: String(error) } })); + } +}); +await new Promise(done => model.listen(0, '127.0.0.1', done)); +const env = { ...process.env, DSH_HOME: home, DSH_TELEMETRY_DISABLED: '1', DSH_TELEMETRY_MODE: 'DISABLED', MNEMON_DATA_DIR: data, MNEMON_CLI_PATH: nativeCli, DEEPSEEK_API_KEY: 'isolated-issue261-headless-key', DEEPSEEK_BASE_URL: `http://127.0.0.1:${model.address().port}` }; +for (const key of ['NODE_PATH', 'NODE_OPTIONS', 'npm_config_legacy_peer_deps', 'NPM_CONFIG_LEGACY_PEER_DEPS']) delete env[key]; +const cli = join(consumer, 'node_modules/@deepseek-ai/dsh/lib/bin.js'); +function run(label, argv, executable = process.execPath) { + return new Promise((done, reject) => { + const child = spawn(executable, argv, { cwd: workspace, env, stdio: ['ignore', 'pipe', 'pipe'] }); + let stdout = ''; + let stderr = ''; + child.stdout.on('data', chunk => { stdout += chunk; void appendFile(join(output, `${label}.stdout.log`), chunk); }); + child.stderr.on('data', chunk => { stderr += chunk; void appendFile(join(output, `${label}.stderr.log`), chunk); }); + const timer = setTimeout(() => { child.kill('SIGTERM'); reject(new Error(`${label} timed out after 90000 ms`)); }, 90000); + child.once('error', reject); + child.once('exit', code => { clearTimeout(timer); code === 0 ? done({ code, stdout, stderr }) : reject(new Error(`${label} exited ${code}: ${stderr}`)); }); + }); +} +async function files(root) { + const output = []; + for (const entry of await readdir(root, { withFileTypes: true })) { + const path = join(root, entry.name); + if (entry.isDirectory()) output.push(...await files(path)); + else if (entry.isFile()) output.push(path); + } + return output; +} +const inventories = {}; +const required = ['mnemon_status', 'mnemon_recall', 'mnemon_document_search', 'mnemon_document_create', 'mnemon_runtime_memory', 'mnemon_remember', 'mnemon_view_route', 'mnemon_view_action']; +function inventory(label, requireMnemon) { + const request = requests.find(item => item.phase === label && Array.isArray(item.body.tools)); + assert(request, `${label} reached the local model with a tool inventory`); + const names = normalizeRequest(request.body).toolNames.sort(); + if (requireMnemon) for (const name of required) assert(names.includes(name), `${label} missing ${name}`); + else assert(!names.some(name => name.startsWith('mnemon_')), 'disabled root leaked Mnemon tools'); + inventories[label] = names; + return request.body; +} + +try { + const nativeVersion = await run('native-version', ['--version'], nativeCli); + const dshVersion = await run('dsh-version', [cli, '--version']); + const config = await run('effective-config', [cli, '--profile', 'headless', '--dump-config']); + for (const [path] of mnemonPackages) assert(config.stdout.includes(path.slice('node_modules/'.length)), `Root composition missing ${path}`); + for (const id of extensions) assert(new RegExp(`- id: ${id}\\n[\\s\\S]*?disabled: false`).test(config.stdout), `strategy extension not enabled: ${id}`); + phase = 'enabled'; + const enabled = await run('enabled', [cli, '--profile', 'headless', 'compatibility-261']); + assert(enabled.stdout.includes('Issue 261 compatibility complete'), 'real runtime add/status sequence must complete'); + const enabledRequest = inventory('enabled', true); + const prompt = normalizeRequest(enabledRequest).system; + for (const phrase of ['Source order expresses preference', 'MNEMON OPTIONAL AUTO CAPTURE']) assert(prompt.includes(phrase), `enabled strategy extension missing: ${phrase}`); + const runtimeFiles = (await files(data)).filter(path => path.endsWith('/runtime/memories.json')); + assert(runtimeFiles.length > 0, 'real Runtime storage was initialized'); + const before = new Map(); + const content = 'Issue 261 compatibility: the isolated published DSH cohort uses real Mnemon runtime memory.'; + for (const path of runtimeFiles) before.set(path, await readFile(path, 'utf8')); + assert([...before.values()].some(text => text.includes(content)), 'real Runtime add persisted the fixture memory'); + phase = 'restart'; + const restarted = await run('restart', [cli, '--profile', 'headless', 'Verify the isolated Headless restart.']); + assert(restarted.stdout.includes('Isolated Issue 261 compatibility fixture ready')); + inventory('restart', true); + const persistence = []; + for (const [path, bytes] of before) { + const after = await readFile(path, 'utf8'); + assert.equal(after, bytes, 'restart must preserve Runtime bytes'); + persistence.push({ path, sha256: createHash('sha256').update(after).digest('hex') }); + } + await writeFile(patchFile, patch + '- id: mnemon\n disabled: true\n'); + phase = 'disabled'; + const disabledRun = await run('disabled', [cli, '--profile', 'headless', 'Verify the disabled Root tool surface.']); + assert(disabledRun.stdout.includes('Isolated Issue 261 compatibility fixture ready')); + inventory('disabled', false); + const result = { result: 'pass', consumer, output, version, dshPackageCount: dshPackages.length, mnemonPackageCount: mnemonPackages.length, enabledStrategyExtensions: extensions, nativeCli, nativeVersion: nativeVersion.stdout.trim(), dshVersion: dshVersion.stdout.trim(), inventories, persistence, completedAt: new Date().toISOString() }; + await writeFile(join(output, 'result.json'), JSON.stringify(result, null, 2) + '\n'); + console.log(JSON.stringify({ result: result.result, output, version, nativeVersion: result.nativeVersion, enabledMnemonTools: inventories.enabled.filter(name => name.startsWith('mnemon_')).length, disabledMnemonTools: inventories.disabled.filter(name => name.startsWith('mnemon_')).length })); +} catch (error) { + await writeFile(join(output, 'result.json'), JSON.stringify({ result: 'fail', consumer, output, version, phase, error: String(error.stack ?? error), inventories, completedAt: new Date().toISOString() }, null, 2) + '\n'); + console.error(error); + process.exitCode = 1; +} finally { + model.closeAllConnections(); + await new Promise(done => model.close(done)); +} diff --git a/dsh/docs/pr-assets/issue-261-dsh-slots/harness/pack-artifacts.mjs b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/pack-artifacts.mjs new file mode 100644 index 000000000..6559cc1a5 --- /dev/null +++ b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/pack-artifacts.mjs @@ -0,0 +1,29 @@ +import { readFile, writeFile, mkdir, readdir } from 'node:fs/promises'; +import { spawnSync } from 'node:child_process'; +import { createHash } from 'node:crypto'; +import { resolve, join } from 'node:path'; + +const args = new Map(); +for (let i = 2; i < process.argv.length; i += 2) args.set(process.argv[i], process.argv[i + 1]); +const source = resolve(args.get('--source')); +const output = resolve(args.get('--output')); +await mkdir(output, { recursive: true }); +const git = (...argv) => spawnSync('git', argv, { cwd: source, encoding: 'utf8' }).stdout.trim(); +const provenance = { source, commit: git('rev-parse', 'HEAD'), status: git('status', '--porcelain=v1'), capturedAt: new Date().toISOString(), node: process.version }; +await writeFile(join(output, 'source-diff.patch'), git('diff', '--binary', 'HEAD') + '\n'); +const dirs = [source, ...(await readdir(join(source, 'plugins'))).filter(name => name.startsWith('dsh-mnemon-')).sort().map(name => join(source, 'plugins', name))]; +const artifacts = []; +for (const dir of dirs) { + const manifest = JSON.parse(await readFile(join(dir, 'package.json'), 'utf8')); + const packed = spawnSync('npm', ['pack', '--ignore-scripts', '--json', '--pack-destination', output], { cwd: dir, encoding: 'utf8', maxBuffer: 8 * 1024 * 1024 }); + if (packed.status !== 0) throw new Error(`npm pack ${manifest.name} failed: ${packed.stderr}`); + const [result] = JSON.parse(packed.stdout); + const bytes = await readFile(join(output, result.filename)); + const integrity = 'sha512-' + createHash('sha512').update(bytes).digest('base64'); + if (integrity !== result.integrity) throw new Error(`pack integrity mismatch: ${manifest.name}`); + artifacts.push({ name: manifest.name, version: manifest.version, filename: result.filename, integrity, shasum: createHash('sha1').update(bytes).digest('hex'), sha256: createHash('sha256').update(bytes).digest('hex'), source: dir, manifest, files: result.files }); + console.log(`${manifest.name}@${manifest.version} ${result.filename}`); +} +if (artifacts.length !== 17) throw new Error(`expected 17 artifacts, got ${artifacts.length}`); +await writeFile(join(output, 'artifacts.json'), JSON.stringify({ provenance, artifacts }, null, 2) + '\n'); +console.log(`Packed ${artifacts.length} artifacts at ${output}`); diff --git a/dsh/docs/pr-assets/issue-261-dsh-slots/harness/pack-peer-fixture.mjs b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/pack-peer-fixture.mjs new file mode 100644 index 000000000..ba787cd88 --- /dev/null +++ b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/pack-peer-fixture.mjs @@ -0,0 +1,20 @@ +import assert from 'node:assert/strict'; +import { readFile, writeFile, mkdir } from 'node:fs/promises'; +import { spawnSync } from 'node:child_process'; +import { createHash } from 'node:crypto'; +import { dirname, join, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +const args = new Map(); +for (let i = 2; i < process.argv.length; i += 2) args.set(process.argv[i], process.argv[i + 1]); +const source = resolve(args.get('--source') ?? join(dirname(fileURLToPath(import.meta.url)), 'e2e/peer-fixture')); +const output = resolve(args.get('--output') ?? join(dirname(fileURLToPath(import.meta.url)), 'e2e/artifacts/peer-fixture')); +await mkdir(output, { recursive: true }); +const manifest = JSON.parse(await readFile(join(source, 'package.json'), 'utf8')); +const result = spawnSync('npm', ['pack', '--ignore-scripts', '--json', '--pack-destination', output], { cwd: source, encoding: 'utf8' }); +assert.equal(result.status, 0, result.stderr); +const [packed] = JSON.parse(result.stdout); +const bytes = await readFile(join(output, packed.filename)); +assert.equal(packed.integrity, 'sha512-' + createHash('sha512').update(bytes).digest('base64')); +const artifact = { name: manifest.name, version: manifest.version, filename: packed.filename, integrity: packed.integrity, shasum: packed.shasum, sha256: createHash('sha256').update(bytes).digest('hex'), manifest, source, provenance: 'Test-owned client plugin, loaded through the official DSH bundle/client loader.' }; +await writeFile(join(output, 'artifact.json'), JSON.stringify(artifact, null, 2) + '\n'); +console.log(join(output, 'artifact.json')); diff --git a/dsh/docs/pr-assets/issue-261-dsh-slots/harness/packed-e2e.mjs b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/packed-e2e.mjs new file mode 100644 index 000000000..eefc0146e --- /dev/null +++ b/dsh/docs/pr-assets/issue-261-dsh-slots/harness/packed-e2e.mjs @@ -0,0 +1,253 @@ +import assert from 'node:assert/strict'; +import { createServer } from 'node:http'; +import { readFile, writeFile, appendFile, mkdir, readdir, realpath, stat } from 'node:fs/promises'; +import { createReadStream } from 'node:fs'; +import { spawn } from 'node:child_process'; +import { createHash } from 'node:crypto'; +import { createRequire } from 'node:module'; +import { resolve, join, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { compatibilityModel, sendModelReply, responseProtocol } from './compatibility-model.mjs'; +import { frameworkPins } from './framework-pins.mjs'; + +const args = new Map(); +for (let i = 2; i < process.argv.length; i += 2) args.set(process.argv[i], process.argv[i + 1]); +const mode = args.get('--mode'); +const cohort = args.get('--cohort'); +assert(['baseline', 'fixed'].includes(mode), '--mode must be baseline or fixed'); +assert(['rc', 'alpha'].includes(cohort), '--cohort must be rc or alpha'); +const version = cohort === 'alpha' ? '0.1.6-alpha.2' : '0.1.5-rc.2'; +const artifactsRoot = resolve(args.get('--artifacts')); +const runRoot = resolve(args.get('--run-root') ?? `./e2e/runs/${mode}-${cohort}`); +const serve = args.get('--serve') === 'true'; +const dshHome = join(runRoot, 'dsh-home'); +const consumer = join(dshHome, 'profiles/web'); +const workspace = join(runRoot, 'workspace'); +const data = join(runRoot, 'mnemon-data'); +const nativeCliInput = args.get('--native-cli') ?? process.env.MNEMON_CLI_PATH; +assert(nativeCliInput, 'supply --native-cli or MNEMON_CLI_PATH'); +const nativeCli = resolve(nativeCliInput); +await Promise.all([consumer, workspace, data].map(path => mkdir(path, { recursive: true }))); +const artifactSet = JSON.parse(await readFile(join(artifactsRoot, 'artifacts.json'), 'utf8')); +assert.equal(artifactSet.artifacts.length, 17); +const localPackages = new Map(artifactSet.artifacts.map(item => [item.name, item])); +const peersEnabled = args.get('--peers') === 'true'; +if (peersEnabled) { + const peerArtifactPath = resolve(args.get('--peer-artifact') ?? fileURLToPath(new URL('./e2e/artifacts/peer-fixture/artifact.json', import.meta.url))); + const peerArtifact = { ...JSON.parse(await readFile(peerArtifactPath, 'utf8')), artifactRoot: dirname(peerArtifactPath) }; + localPackages.set(peerArtifact.name, peerArtifact); + await writeFile(join(runRoot, 'peer-fixture-provenance.json'), JSON.stringify(peerArtifact, null, 2) + '\n'); +} +const artifactPath = item => join(item.artifactRoot ?? artifactsRoot, item.filename); +for (const item of localPackages.values()) { + const bytes = await readFile(artifactPath(item)); + assert.equal('sha512-' + createHash('sha512').update(bytes).digest('base64'), item.integrity); +} +await writeFile(join(runRoot, 'artifact-provenance.json'), JSON.stringify({ artifactsRoot, ...artifactSet.provenance, artifacts: artifactSet.artifacts.map(({ name, version, filename, integrity, sha256, source }) => ({ name, version, filename, integrity, sha256, source })) }, null, 2) + '\n'); +const legacyPeers = mode === 'baseline' && cohort === 'alpha'; +if (legacyPeers && !args.has('--framework-root')) assert(process.platform === 'darwin' && process.arch === 'arm64', 'Bundled baseline pins are macOS ARM64 only; supply --framework-root from a normal exact-cohort install on this platform.'); +const frameworkPeers = args.has('--framework-root') + ? await frameworkPins(resolve(args.get('--framework-root')), version) + : legacyPeers + ? JSON.parse(await readFile(new URL(`./e2e/framework-peers-${cohort}.json`, import.meta.url), 'utf8')) + : {}; +if (args.has('--framework-root')) await writeFile(join(runRoot, 'framework-pins.json'), JSON.stringify(frameworkPeers, null, 2) + '\n'); +if (legacyPeers) await writeFile(join(runRoot, 'baseline-framework-pins.json'), JSON.stringify(frameworkPeers, null, 2) + '\n'); +for (const [name, pinned] of Object.entries(frameworkPeers)) if (name.startsWith('@deepseek-ai/dsh')) assert.equal(pinned, version); +const env = { ...process.env, DSH_HOME: dshHome, DSH_TELEMETRY_DISABLED: '1', DSH_TELEMETRY_MODE: 'DISABLED', MNEMON_DATA_DIR: data, MNEMON_CLI_PATH: nativeCli, DEEPSEEK_API_KEY: 'isolated-issue261-fixture-key' }; +for (const key of ['NODE_PATH', 'NODE_OPTIONS', 'npm_config_legacy_peer_deps', 'NPM_CONFIG_LEGACY_PEER_DEPS']) delete env[key]; +const registry = createServer(async (request, response) => { + try { + const path = new URL(request.url, 'http://127.0.0.1').pathname; + if (path.startsWith('/artifacts/')) { + const file = decodeURIComponent(path.slice('/artifacts/'.length)); + const item = [...localPackages.values()].find(candidate => candidate.filename === file); + if (!item) { response.writeHead(404); response.end(); return; } + response.writeHead(200, { 'content-type': 'application/octet-stream' }); + createReadStream(artifactPath(item)).pipe(response); + await appendFile(join(runRoot, 'registry.log'), JSON.stringify({ at: new Date().toISOString(), kind: 'local-tarball', package: item.name, version: item.version, integrity: item.integrity }) + '\n'); + return; + } + const name = decodeURIComponent(path.slice(1)); + const item = localPackages.get(name); + if (item) { + const base = `http://127.0.0.1:${registry.address().port}`; + const manifest = { ...item.manifest, dist: { tarball: `${base}/artifacts/${encodeURIComponent(item.filename)}`, integrity: item.integrity, shasum: item.shasum } }; + response.writeHead(200, { 'content-type': 'application/json' }); + response.end(JSON.stringify({ _id: name, name, 'dist-tags': { latest: item.version }, versions: { [item.version]: manifest } })); + return; + } + if (name.startsWith('dsh-mnemon')) { response.writeHead(404); response.end(JSON.stringify({ error: 'All Mnemon packages must come from the selected artifacts' })); return; } + const upstream = await fetch('https://registry.npmjs.org' + path, { headers: { accept: request.headers.accept ?? 'application/json' } }); + response.writeHead(upstream.status, { 'content-type': upstream.headers.get('content-type') ?? 'application/json' }); + response.end(Buffer.from(await upstream.arrayBuffer())); + } catch (error) { response.writeHead(500); response.end(String(error)); } +}); +await new Promise(resolveListen => registry.listen(0, '127.0.0.1', resolveListen)); +const registryUrl = `http://127.0.0.1:${registry.address().port}`; +env.npm_config_registry = registryUrl; +env.NPM_CONFIG_REGISTRY = registryUrl; + +function command(executable, argv, log, cwd = consumer, timeoutMs = 240000) { + return new Promise((resolveCommand, reject) => { + const child = spawn(executable, argv, { cwd, env, stdio: ['ignore', 'pipe', 'pipe'] }); + let out = ''; + child.stdout.on('data', chunk => { out += chunk; void appendFile(join(runRoot, log), chunk); }); + child.stderr.on('data', chunk => { out += chunk; void appendFile(join(runRoot, log), chunk); }); + const timer = setTimeout(() => { child.kill('SIGTERM'); reject(new Error(`${argv[0]} timed out`)); }, timeoutMs); + child.once('error', reject); + child.once('exit', code => { clearTimeout(timer); code === 0 ? resolveCommand(out) : reject(new Error(`${executable} ${argv.join(' ')} exited ${code}; see ${join(runRoot, log)}`)); }); + }); +} + +const manifest = { + name: `issue261-${mode}-${cohort}-web`, version: '0.0.0', private: true, type: 'module', + dependencies: { + ...frameworkPeers, + '@deepseek-ai/dsh': version, '@deepseek-ai/dsh-client-ui-primitives': version, '@deepseek-ai/dsh-typert-protocol': version, + ...Object.fromEntries(['dsh-client-ui-slots', 'dsh-client-ui-chat', 'dsh-client-ui-conversation', 'dsh-client-ui-renderer', 'dsh-client-ui-layout', 'dsh-client-ui-settings', 'dsh-client-store', 'dsh-client-locale'].map(name => [`@deepseek-ai/${name}`, version])), + '@deepseek-ai/cordis': '4.0.2', react: '18.3.1', 'react-dom': '18.3.1', + ...Object.fromEntries([...localPackages.values()].map(item => [item.name, item.version])), + }, + dsh: { profile: { bundles: ['@deepseek-ai/dsh-base', '@deepseek-ai/dsh-web-app', 'dsh-mnemon', ...(peersEnabled ? ['issue261-turn-tail-peers'] : [])] } }, +}; +await writeFile(join(consumer, 'package.json'), JSON.stringify(manifest, null, 2) + '\n'); +await writeFile(join(runRoot, 'install-policy.json'), JSON.stringify({ mode, cohort, version, registryUrl, legacyPeers, reusedExistingInstall: args.get('--reuse-install') === 'true', reason: legacyPeers ? 'Baseline dsh-mnemon 0.5.11 peer ranges exclude 0.1.6-alpha.2; only this baseline reproduction uses --legacy-peer-deps. Fixed consumers must normal-install.' : 'Normal npm peer resolution, no legacy-peer-deps.' }, null, 2) + '\n'); +console.log(`Installing ${mode} ${cohort} from ${artifactsRoot} at ${consumer}`); +if (args.get('--reuse-install') !== 'true') await command('npm', ['install', '--registry', registryUrl, '--no-audit', '--no-fund', ...(legacyPeers ? ['--legacy-peer-deps'] : [])], 'install.log'); + +const lock = JSON.parse(await readFile(join(consumer, 'package-lock.json'), 'utf8')); +const dshPackages = Object.entries(lock.packages).flatMap(([path, info]) => /(?:^|\/)node_modules\/@deepseek-ai\/dsh(?:-[^/]+)?$/.test(path) ? [{ path, version: info.version, resolved: info.resolved, integrity: info.integrity }] : []); +assert(dshPackages.length > 200); +assert(dshPackages.every(item => item.version === version), 'all installed DSH packages must match exact cohort'); +for (const item of localPackages.values()) { + const entry = lock.packages[`node_modules/${item.name}`]; + assert.equal(entry.version, item.version); + assert.equal(entry.integrity, item.integrity); + assert(/^http:\/\/127\.0\.0\.1:\d+\/artifacts\//.test(entry.resolved), 'Mnemon must resolve from the test loopback registry'); + assert.equal(decodeURIComponent(new URL(entry.resolved).pathname), `/artifacts/${item.filename}`); +} +const resolutions = []; +for (const item of localPackages.values()) { + const installed = join(consumer, 'node_modules', item.name, 'package.json'); + const loader = createRequire(installed); + for (const dependency of ['@deepseek-ai/cordis', '@deepseek-ai/dsh-client-ui-primitives', '@deepseek-ai/dsh-typert-protocol']) { + const path = await realpath(loader.resolve(dependency)); + assert(path.startsWith(await realpath(consumer) + '/node_modules/'), `external resolution detected: ${path}`); + resolutions.push({ from: item.name, dependency, path }); + } +} +const bytesChecked = []; +for (const pkg of ['dsh-client-ui-slots', 'dsh-client-ui-chat', 'dsh-client-ui-renderer', 'dsh-client-ui-conversation']) { + const file = pkg === 'dsh-client-ui-slots' ? 'lib/index.js' : 'lib/client.js'; + let reference = resolve(args.get('--contracts-root') ?? dirname(fileURLToPath(import.meta.url)), version, pkg, 'package', file); + try { await stat(reference); } catch (error) { + if (error.code !== 'ENOENT') throw error; + const publicRoot = join(runRoot, 'public-contracts', version, pkg); + await mkdir(publicRoot, { recursive: true }); + const metadata = await (await fetch(`https://registry.npmjs.org/@deepseek-ai/${pkg}/${version}`)).json(); + assert.equal(metadata.version, version); + const archive = Buffer.from(await (await fetch(metadata.dist.tarball)).arrayBuffer()); + assert.equal('sha512-' + createHash('sha512').update(archive).digest('base64'), metadata.dist.integrity); + const tarball = join(publicRoot, 'package.tgz'); + await writeFile(tarball, archive); + await writeFile(join(publicRoot, 'metadata.json'), JSON.stringify(metadata, null, 2) + '\n'); + await command('tar', ['-xzf', tarball, '-C', publicRoot], 'public-contract-extraction.log', runRoot); + reference = join(publicRoot, 'package', file); + } + const installed = join(consumer, 'node_modules/@deepseek-ai', pkg, file); + const original = await readFile(reference); + const actual = await readFile(installed); + assert(actual.equals(original), `public package bytes changed: ${pkg}`); + bytesChecked.push({ package: `@deepseek-ai/${pkg}`, file, sha256: createHash('sha256').update(actual).digest('hex') }); +} +await writeFile(join(runRoot, 'runtime-graph.json'), JSON.stringify({ version, dshPackageCount: dshPackages.length, dshPackages, mnemonPackages: [...localPackages.keys()].filter(name => name.startsWith('dsh-mnemon')), testClientPackages: [...localPackages.keys()].filter(name => !name.startsWith('dsh-mnemon')), resolutions, publicBytesChecked: bytesChecked }, null, 2) + '\n'); +const cli = join(consumer, 'node_modules/@deepseek-ai/dsh/lib/bin.js'); +await command(process.execPath, [cli, '--version'], 'cli-version.log'); +await command(nativeCli, ['--version'], 'mnemon-cli-version.log', workspace); + +const disabled = ['subprocess', 'open-in-app', 'bash-sandbox', 'pwsh-sandbox', 'tool-bash', 'tool-pwsh', 'permission', 'tool-fs-search', 'directory-picker']; +const extensionIds = ['mnemon-strategy-scoped', 'mnemon-strategy-light-context', 'mnemon-strategy-auto-capture']; +await writeFile(join(consumer, 'cordis.patch.yml'), disabled.map(id => `- id: ${id}\n disabled: true\n`).join('') + + '- id: agent-presets\n config:\n default: mnemon-e2e\n' + + '- insert:\n - id: e2e-directory-picker\n name: "@deepseek-ai/dsh-host-directory-picker-browse"\n - id: e2e-directory-picker-ui\n name: "@deepseek-ai/dsh-client-ui-directory-picker-browse"\n' + + extensionIds.map(id => `- id: ${id}\n disabled: false\n`).join('')); +await writeFile(join(dshHome, 'settings.yaml'), `mnemon:\n cliPath: ${JSON.stringify(nativeCli)}\n idleReview:\n enabled: false\n persistenceStrategy:\n mode: manual\n`); +const preset = join(dshHome, '.agent-presets/mnemon-e2e'); +await mkdir(preset, { recursive: true }); +await writeFile(join(preset, 'preset.yml'), 'name: Mnemon E2E\ndescription: Isolated packed-artifact Issue 261 fixture.\norder: 0\n'); +await writeFile(join(preset, 'agent.cordis.yml'), '- id: persona\n name: "@deepseek-ai/dsh-persona"\n config:\n prefix: You are testing packed Mnemon compatibility with an isolated deterministic model.\n'); +await writeFile(join(workspace, 'README.md'), '# Issue 261 disposable workspace\n\nAll memory and model data in this fixture are test-owned.\n'); +await command(process.execPath, [cli, 'web', '--dump-config'], 'effective-config.yml', workspace); +let status = { mode, cohort, version, runRoot, artifactsRoot, consumer, dshHome, workspace, data, cli, nativeCli, registryUrl, legacyPeers, peersEnabled, pid: process.pid, preparedAt: new Date().toISOString(), serverStarted: false }; +await writeFile(join(runRoot, 'server.json'), JSON.stringify(status, null, 2) + '\n'); +if (!serve) { + await new Promise(done => registry.close(done)); + console.log(JSON.stringify(status)); + process.exit(0); +} + +const decide = compatibilityModel(event => { void appendFile(join(runRoot, 'model-events.jsonl'), JSON.stringify({ at: new Date().toISOString(), ...event }) + '\n'); }); +let modelRequests = 0; +const model = createServer(async (request, response) => { + try { + let body = ''; + for await (const chunk of request) body += chunk; + const input = JSON.parse(body); + const number = ++modelRequests; + await appendFile(join(runRoot, 'model-requests.jsonl'), JSON.stringify({ at: new Date().toISOString(), number, path: request.url, protocol: responseProtocol(input, request.url), body: input }) + '\n'); + const reply = decide(input); + sendModelReply(response, input, request.url, reply, `compatibility-261-${number}`); + } catch (error) { + await appendFile(join(runRoot, 'model-errors.log'), String(error.stack ?? error) + '\n'); + response.writeHead(500, { 'content-type': 'application/json' }); + response.end(JSON.stringify({ error: { message: String(error) } })); + } +}); +await new Promise(done => model.listen(0, '127.0.0.1', done)); +env.DEEPSEEK_BASE_URL = `http://127.0.0.1:${model.address().port}`; +let web; +let stopping = false; +let restarting = false; +function launch() { + web = spawn(process.execPath, [cli, 'web', '--no-open', '--host', '127.0.0.1', '--port', '0'], { cwd: workspace, env, stdio: ['ignore', 'pipe', 'pipe'] }); + status = { ...status, serverStarted: true, webPid: web.pid, modelUrl: env.DEEPSEEK_BASE_URL, startedAt: new Date().toISOString() }; + void writeFile(join(runRoot, 'server.json'), JSON.stringify(status, null, 2) + '\n'); + let output = ''; + const record = chunk => { + output += chunk; + process.stdout.write(chunk); + void appendFile(join(runRoot, 'web.log'), chunk); + const urls = [...output.matchAll(/https?:\/\/127\.0\.0\.1:\d+[^\s\x1b]*/g)].map(match => match[0]); + const url = urls.find(candidate => !candidate.startsWith(registryUrl) && !candidate.startsWith(env.DEEPSEEK_BASE_URL)); + if (url && status.url !== url) { + status = { ...status, url }; + void writeFile(join(runRoot, 'server.json'), JSON.stringify(status, null, 2) + '\n'); + } + }; + web.stdout.on('data', record); + web.stderr.on('data', record); + web.once('error', error => { console.error(error); process.exitCode = 1; void stop(); }); + web.once('exit', code => { if (!stopping && !restarting) { process.exitCode = code ?? 1; void stop(); } }); +} +async function stop() { + if (stopping) return; + stopping = true; + if (web && web.exitCode === null) { web.kill('SIGTERM'); await new Promise(done => web.once('exit', done)); } + model.closeAllConnections(); + registry.closeAllConnections(); + await Promise.all([new Promise(done => model.close(done)), new Promise(done => registry.close(done))]); + await writeFile(join(runRoot, 'server.json'), JSON.stringify({ ...status, stoppedAt: new Date().toISOString() }, null, 2) + '\n'); +} +process.once('SIGINT', () => { void stop(); }); +process.once('SIGTERM', () => { void stop(); }); +process.on('SIGUSR2', async () => { + if (stopping || restarting || !web || web.exitCode !== null) return; + restarting = true; + web.kill('SIGTERM'); + await new Promise(done => web.once('exit', done)); + restarting = false; + if (!stopping) launch(); +}); +console.log(JSON.stringify({ ...status, modelUrl: env.DEEPSEEK_BASE_URL })); +launch(); diff --git a/dsh/docs/pr-assets/issue-261-dsh-slots/typecheck-results.json b/dsh/docs/pr-assets/issue-261-dsh-slots/typecheck-results.json new file mode 100644 index 000000000..6e3640acc --- /dev/null +++ b/dsh/docs/pr-assets/issue-261-dsh-slots/typecheck-results.json @@ -0,0 +1,38 @@ +{ + "recordedOn": "2026-09-18", + "scope": "Root and all 16 plugins production sources; published declaration files only", + "cases": [ + { + "case": "baseline-rc", + "cohort": "0.1.5-rc.2", + "productionFileCount": 196, + "diagnosticCount": 0, + "diagnostics": "", + "foreignDshDeclarations": 0 + }, + { + "case": "baseline-alpha", + "cohort": "0.1.6-alpha.2", + "productionFileCount": 196, + "diagnosticCount": 4, + "diagnostics": "src/client/index.ts(50,24): error TS2769: No overload matches this call.\n Overload 1 of 2, '(options: { name: \"conversation.chat.turnTail\"; children?: Record; store?: undefined; locale?: \"mnemon\"; registrant?: string; } & { id: string; order?: number; label?: SlotLabel; priority?: number; } & { ...; }, component: NamedExoticComponent<...> & SlotComponent<...>): () => void', gave the following error.\n Type '(sessionId: unknown) => { sessionId?: string; connection: ClientConnectionHandle; localeRuntime: MnemonClientContext[\"locale\"]; t: (key: MnemonKey, params?: Record) => string; }' is not assignable to type 'undefined'.\n Overload 2 of 2, '(options: { name: \"conversation.chat.turnTail\"; children?: Record; store?: undefined; locale?: \"mnemon\"; registrant?: string; } & { id: string; order?: number; label?: SlotLabel; priority?: number; } & { ...; }, component: NamedExoticComponent<...> & SlotComponent<...>): () => void', gave the following error.\n Object literal may only specify known properties, and 'select' does not exist in type '{ name: \"conversation.chat.turnTail\"; children?: Record; store?: undefined; locale?: \"mnemon\"; registrant?: string; } & { id: string; order?: number; label?: SlotLabel; priority?: number; } & { ...; }'.\nsrc/client/index.ts(177,82): error TS2339: Property 'current' does not exist on type 'SessionListState'.\nsrc/client/index.ts(235,34): error TS2339: Property 'current' does not exist on type 'SessionListState'.\nsrc/client/workspace-mount.tsx(120,49): error TS2339: Property 'current' does not exist on type 'SessionListState'.\n", + "foreignDshDeclarations": 0 + }, + { + "case": "fixed-rc", + "cohort": "0.1.5-rc.2", + "productionFileCount": 198, + "diagnosticCount": 0, + "diagnostics": "", + "foreignDshDeclarations": 0 + }, + { + "case": "fixed-alpha", + "cohort": "0.1.6-alpha.2", + "productionFileCount": 198, + "diagnosticCount": 0, + "diagnostics": "", + "foreignDshDeclarations": 0 + } + ] +} diff --git a/dsh/docs/pr-assets/issue-261-dsh-slots/verification.json b/dsh/docs/pr-assets/issue-261-dsh-slots/verification.json new file mode 100644 index 000000000..0c485259d --- /dev/null +++ b/dsh/docs/pr-assets/issue-261-dsh-slots/verification.json @@ -0,0 +1,107 @@ +{ + "recordedOn": "2026-09-18", + "issue": 261, + "baselineCommit": "02ce994c8f05b6bf83a6b5872bd018bbfc8970ce", + "mainBase": "245c9e2f6a50e6a77e7ba3c679ce07ba80458925", + "productionFixCommit": "2a593c9a6f6510afc0e12704ff2103abc1a1f597", + "environment": { + "os": "macOS", + "arch": "arm64", + "node": "v25.1.0", + "nativeCli": "0.2.8" + }, + "releaseBeforeFix": { + "version": "0.5.11", + "pullRequest": 262, + "workflow": 35301166613, + "result": "success" + }, + "validation": { + "verify": "pass", + "rootTests": { + "passed": 1183, + "skipped": 5, + "skipReason": "Opt-in live Flash tests" + }, + "nativeMemorySpaceIntegration": "pass: real CLI create, write, recall and forget", + "nativeRuntimeArchival": "pass: two same-View Native namespaces through actual Host tools", + "verifyPlugins": { + "result": "pass", + "independentPlugins": 16, + "packedArtifacts": 17, + "externalConsumer": true, + "starterUpgradeFrom": "0.4.7", + "enabledOptionalStrategies": 3 + }, + "otherSkips": [ + "Live OpenViking integration: 1", + "Windows-only smoke on macOS: 1" + ], + "package": { + "files": 47, + "packedBytes": 299146, + "unpackedBytes": 1338363, + "maximumUnpackedBytes": 1339000 + } + }, + "screenshots": [ + { + "file": "01-alpha-before-missing-turn-bar.png", + "bytes": 59306, + "sha256": "8fc4cf43084a7e66a1eb5a221c6e505c050a24820798bb214011983ac74f50d9", + "edited": false + }, + { + "file": "02-alpha-after-peer-turn-bar.png", + "bytes": 64144, + "sha256": "d10cec649958868a4f5ff3c93b62b037a73d0e3bbfe7fc38088fd0f1de724bfd", + "edited": false + }, + { + "file": "03-alpha-runtime-navigation.png", + "bytes": 68643, + "sha256": "07e5b625a26cc1cf904a5878039732ceab080aef95a0b20a3069aaaace729d2c", + "edited": false + }, + { + "file": "04-alpha-bar-off-peers-retained.png", + "bytes": 60057, + "sha256": "8cba336c93b6ebe45d41d072dbec026bb815a299e892176c27356c266148b7e3", + "edited": false + }, + { + "file": "05-alpha-builtin-navigation.png", + "bytes": 78530, + "sha256": "6bf5347d9aef4fe8ddfeddb53f6874b5ed18494e0ac9c3dbe0229b6173518bad", + "edited": false + }, + { + "file": "06-alpha-workspace-b-memory.png", + "bytes": 80924, + "sha256": "38c735c12601187aabf0ac726b582ab3ae8639dc0b1209218b65a26b7f07a147", + "edited": false + }, + { + "file": "07-alpha-workspace-a-isolated.png", + "bytes": 72942, + "sha256": "0afc4e0d0a0bea12810c51e6cb9fc5592e998931c299033f96cfd94b51eea5ce", + "edited": false + }, + { + "file": "08-rc-after-turn-bar.png", + "bytes": 59000, + "sha256": "9f05d7d752523fc187af49e964f59407f63d14c622f63cedcb187f183a94a49b", + "edited": false + } + ], + "verificationLogHashes": [ + { + "name": "verify.log", + "sha256": "5047086535fcd384031b72d472cc9428f836d514bd606afdfa1454b39322d453" + }, + { + "name": "verify-plugins.log", + "sha256": "ee7d40f81b1b0e9c36cf61a405bb6be78cf0f65152ee2d1354443d52c34cbfbf" + } + ] +} diff --git a/dsh/docs/pr-assets/issue-265-builtin-width-handles/README.md b/dsh/docs/pr-assets/issue-265-builtin-width-handles/README.md new file mode 100644 index 000000000..2fce03646 --- /dev/null +++ b/dsh/docs/pr-assets/issue-265-builtin-width-handles/README.md @@ -0,0 +1,50 @@ +# Issue 265: Builtin width-handle evidence + +[English](README.md) | [简体中文](README.zh-CN.md) + +On main `65c0e23ba410993e16c00c8b3adf92d59d853425` (Mnemon 0.5.12), DSH's conversation width handles remain mounted over the Builtin memory page. Hovering the left strip shows a vertical mark; hit testing reaches the native handle instead of the memory page. + +The fix uses a CSS `:has()` rule for the conversation that directly owns Mnemon's Builtin surface. Only that conversation's direct `data-width-handle` children receive `visibility: hidden` and `pointer-events: none`. The Builtin root remains `position: static; z-index: auto`; its resident composer remains usable. Returning to Chat restores native dragging. The direct ownership chain also excludes Sidebar, nested/sibling conversations, and other plugin views. A raised stacking layer was rejected after it occluded the composer; its prototype runs are excluded here. + +## Actual WebUI evidence + +Recorded on 2026-09-22, using public DSH packages, all 17 packed Mnemon packages, and a loopback model fixture on macOS arm64 / Node 25.1.0. The final artifact set is `fixed-scoped`; its working-tree patch was packed on the main commit above. All 47 packed Root files match the final verified build. [verification.json](verification.json) records all 17 tarball hashes, public-module byte checks, observations, and screenshot hashes. + +| Run | Observation | Screenshot | +| --- | --- | --- | +| Main baseline, DSH 0.1.5-rc.2 | Both edge points hit native handles; left hover mark opacity is 1. | [Before](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-265-builtin-width-handles/before-rc2.png) | +| Scoped fix, DSH 0.1.5-rc.2 | Both handles hidden and noninteractive; edge points hit Builtin; composer visible and directly hittable. | [After RC2](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-265-builtin-width-handles/after-rc2.png) | +| Scoped fix, DSH 0.1.6-alpha.2 | Same native hit-test result. | [After alpha2](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-265-builtin-width-handles/after-alpha2.png) | +| RC2 Runtime Source | Actual body-portal Add dialog accepted a new memory and displayed a success receipt. Both entries survived reload and session reselection. | [Dialog](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-265-builtin-width-handles/runtime-dialog-rc2.png), [saved memory](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-265-builtin-width-handles/runtime-write-rc2.png) | +| RC2 resident composer and Chat | Sent a turn while Builtin was open; Chat showed the new turn and completed model reply. Native width dragging also worked. | [Chat](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-265-builtin-width-handles/chat-restored-rc2.png) | + +![RC2 baseline with the native width mark over Builtin](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-265-builtin-width-handles/before-rc2.png) + +![RC2 with scoped suppression and the resident composer visible](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-265-builtin-width-handles/after-rc2.png) + +Actual Chat drags moved the left edge by 60 px in both cohorts and reduced composer width from 943.20 to 823.20 px. The RC2 width was restored with another real drag. After the Runtime write and browser reload, the handle points still hit Builtin and the composer still hit its own input. + +## Validation + +- `npx --yes pnpm@10.13.1 run verify`: passed; 1,189 Root tests and 382 plugin tests passed. Seven existing opt-in tests were skipped: five real-model pressure/quality cases, one Windows Native smoke case, and one configured-endpoint OpenViking integration case. Types, deterministic builds, Headless, package contents, `publint`, and `attw` passed. `MNEMON_NATIVE_TEST_CLI` enabled real Native 0.2.8 space creation, View write, recall, forget and two-namespace capacity workflows in isolated storage. +- `npx --yes pnpm@10.13.1 run verify:plugins --skip-build`: passed for 16 independent plugin repositories and 17 packed artifacts, including public SDK composition, Client tests, all three optional strategies together, and Root-only activation. +- The six selector contract tests pass; running them against main's CSS gives three failures. They cover ownership, composer, peer/Sidebar exclusion, nested/sibling isolation, and automatic restoration. Their JSDOM adapter reads the authored rule; the browser runs above separately verify native CSS painting and hit testing. +- Both final Headless runs expose all 17 `mnemon_*` tools, preserve real Runtime add/status data across a new CLI process, enable three strategy extensions, and expose zero Mnemon tools after Root is disabled. Native Mnemon 0.2.8 is configured and version-checked. +- RC2 contains 234 exact-version DSH package records; alpha2 contains 251. Each resolves all 17 Mnemon packages, passes 51 resolution checks, and verifies four installed public UI modules against official tarball bytes. No test Client package is installed. + +## Repeating the runs + +Use the [published-package harness](../issue-261-dsh-slots/harness/HARNESS.md). Build the selected source first. Set `MNEMON_SOURCE`, `NATIVE_CLI`, and an empty `VERIFICATION_ROOT`; set `RC_FRAMEWORK_ROOT` and `ALPHA_FRAMEWORK_ROOT` to previously verified public consumers of the matching exact cohort on the current platform. From the repository root: + +```sh +HARNESS=docs/pr-assets/issue-261-dsh-slots/harness +node "$HARNESS/pack-artifacts.mjs" --source "$MNEMON_SOURCE" --output "$VERIFICATION_ROOT/artifacts/fixed-scoped" +node "$HARNESS/packed-e2e.mjs" --mode fixed --cohort rc --artifacts "$VERIFICATION_ROOT/artifacts/fixed-scoped" --framework-root "$RC_FRAMEWORK_ROOT" --run-root "$VERIFICATION_ROOT/fixed-scoped-rc" --native-cli "$NATIVE_CLI" --serve true +node "$HARNESS/packed-e2e.mjs" --mode fixed --cohort alpha --artifacts "$VERIFICATION_ROOT/artifacts/fixed-scoped" --framework-root "$ALPHA_FRAMEWORK_ROOT" --run-root "$VERIFICATION_ROOT/fixed-scoped-alpha" --native-cli "$NATIVE_CLI" --serve true +node "$HARNESS/headless-check.mjs" --consumer "$VERIFICATION_ROOT/fixed-scoped-rc/dsh-home/profiles/web" --output "$VERIFICATION_ROOT/headless-scoped-rc" --native-cli "$NATIVE_CLI" +node "$HARNESS/headless-check.mjs" --consumer "$VERIFICATION_ROOT/fixed-scoped-alpha/dsh-home/profiles/web" --output "$VERIFICATION_ROOT/headless-scoped-alpha" --native-cli "$NATIVE_CLI" +``` + +Serve commands remain running; use separate terminals. For the baseline, build and pack the clean main commit and use the same RC2 command with separate artifact/run directories. All three initial installs use ordinary npm peer resolution. Exact framework pins avoid an incomplete upstream RC3 release reached by fresh RC2 transitive ranges; no DSH bytes or metadata are patched and no legacy-peer bypass is used. + +Screenshots retain the reusable fixture title “Issue 261 compatibility.” Alpha's terminal-recovery toast comes from the deliberately disabled terminal fixture. These runs certify the public WebUI behavior shown, not terminal operation, Windows, the native desktop executable, or a Native memory-space backend write. Peer isolation is covered by contract tests; the final browser runs did not load the optional peer fixture. This CSS change does not change persistence, configuration, RPC, or credentials. diff --git a/dsh/docs/pr-assets/issue-265-builtin-width-handles/README.zh-CN.md b/dsh/docs/pr-assets/issue-265-builtin-width-handles/README.zh-CN.md new file mode 100644 index 000000000..b8ff921cc --- /dev/null +++ b/dsh/docs/pr-assets/issue-265-builtin-width-handles/README.zh-CN.md @@ -0,0 +1,50 @@ +# Issue 265:Builtin 宽度手柄验证 + +[English](README.md) | [简体中文](README.zh-CN.md) + +在 main `65c0e23ba410993e16c00c8b3adf92d59d853425`(Mnemon 0.5.12)上,DSH 的对话宽度手柄仍挂载在 Builtin 记忆页上方。鼠标移到左侧条带时会显示竖线;命中测试落到原生手柄,而不是记忆页。 + +修复通过 CSS `:has()` 规则匹配直接承载 Mnemon Builtin 的对话,仅给该对话的直接 `data-width-handle` 子元素设置 `visibility: hidden` 和 `pointer-events: none`。Builtin 根节点保持 `position: static; z-index: auto`,常驻输入框仍可使用;返回 Chat 后原生拖动自动恢复。直接归属链还排除了 Sidebar、嵌套或相邻对话以及其他插件视图。曾尝试提升层级,但实际遮挡了输入框,因此已放弃;本文不将该原型运行计入通过证据。 + +## 实际 WebUI 证据 + +验证日期为 2026-09-22,环境为 macOS arm64 / Node 25.1.0,使用公开发布的 DSH 包、全部 17 个 Mnemon 打包制品和本机回环模型夹具。最终制品集为 `fixed-scoped`,在上述 main 提交上打包工作区补丁。Root 制品内全部 47 个文件与最终验证构建一致。[verification.json](verification.json) 记录全部 17 个 tarball 哈希、公开模块字节核对、观测值及截图哈希。 + +| 运行 | 观测结果 | 截图 | +| --- | --- | --- | +| main 基线,DSH 0.1.5-rc.2 | 两侧命中原生手柄;左侧悬停标记透明度为 1。 | [修复前](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-265-builtin-width-handles/before-rc2.png) | +| 限定归属的修复,DSH 0.1.5-rc.2 | 两侧手柄隐藏且不接收指针事件;原手柄位置命中 Builtin;输入框可见且直接命中自身。 | [RC2 修复后](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-265-builtin-width-handles/after-rc2.png) | +| 限定归属的修复,DSH 0.1.6-alpha.2 | 原生浏览器命中测试结果相同。 | [alpha2 修复后](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-265-builtin-width-handles/after-alpha2.png) | +| RC2 Runtime Source | 实际打开挂载于 body 的新增对话框,写入新记忆并显示成功回执。刷新并重新选择会话后,两条记忆仍存在。 | [对话框](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-265-builtin-width-handles/runtime-dialog-rc2.png)、[保存结果](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-265-builtin-width-handles/runtime-write-rc2.png) | +| RC2 常驻输入框与 Chat | 在 Builtin 打开时发送新消息;Chat 显示新增用户消息与完成的模型回复。原生宽度拖动也正常。 | [Chat](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-265-builtin-width-handles/chat-restored-rc2.png) | + +![RC2 基线:原生宽度标记覆盖 Builtin](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-265-builtin-width-handles/before-rc2.png) + +![RC2 修复后:限定范围隐藏手柄,常驻输入框可见](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-265-builtin-width-handles/after-rc2.png) + +两个版本中都实际拖动了 Chat 左边界 60 px,输入框宽度从 943.20 px 缩至 823.20 px。RC2 再次通过真实拖动恢复原宽度。Runtime 写入并刷新浏览器后,原手柄位置仍命中 Builtin,输入框仍命中自身。 + +## 验证结果 + +- `npx --yes pnpm@10.13.1 run verify`:通过;Root 测试 1,189 项、插件测试 382 项通过。七项既有按需测试跳过:五项真实模型压力或质量测试、一项 Windows Native 冒烟测试、一项需要指定服务端点的 OpenViking 集成测试。类型、确定性构建、Headless、包内容、`publint` 与 `attw` 均通过。通过 `MNEMON_NATIVE_TEST_CLI` 启用真实 Native 0.2.8,在隔离存储中完成空间创建、View 写入、检索、删除及双命名空间容量工作流。 +- `npx --yes pnpm@10.13.1 run verify:plugins --skip-build`:16 个独立插件仓库和 17 个打包制品通过,包括公开 SDK 组合、Client 测试、三个可选策略共同激活,以及仅安装 Root 后激活。 +- 六项选择器契约测试全部通过;换成 main 的 CSS 后出现三项失败。测试覆盖归属、输入框、peer/Sidebar 排除、嵌套与相邻对话隔离及自动恢复。JSDOM 适配器读取实际编写的规则;上述真实浏览器运行另行验证原生 CSS 绘制及命中行为。 +- 两次最终 Headless 运行都暴露全部 17 个 `mnemon_*` 工具;实际执行 Runtime add/status,数据在新 CLI 进程中保留;启用三个策略扩展;禁用 Root 后 Mnemon 工具数为零。实际 Native Mnemon 0.2.8 已配置并核对版本。 +- RC2 包含 234 条严格同版本 DSH 包记录,alpha2 为 251 条。两者都解析全部 17 个 Mnemon 包,通过 51 项解析检查,并将四个已安装公开 UI 模块与官方 tarball 字节核对。未安装测试 Client 包。 + +## 重复验证 + +使用[公开发布包验证脚本](../issue-261-dsh-slots/harness/HARNESS.zh-CN.md)。先构建待测源码,设置 `MNEMON_SOURCE`、`NATIVE_CLI` 和空的 `VERIFICATION_ROOT`;将 `RC_FRAMEWORK_ROOT`、`ALPHA_FRAMEWORK_ROOT` 分别设为当前平台上此前验证过、版本精确匹配的公开包安装目录。在仓库根目录运行: + +```sh +HARNESS=docs/pr-assets/issue-261-dsh-slots/harness +node "$HARNESS/pack-artifacts.mjs" --source "$MNEMON_SOURCE" --output "$VERIFICATION_ROOT/artifacts/fixed-scoped" +node "$HARNESS/packed-e2e.mjs" --mode fixed --cohort rc --artifacts "$VERIFICATION_ROOT/artifacts/fixed-scoped" --framework-root "$RC_FRAMEWORK_ROOT" --run-root "$VERIFICATION_ROOT/fixed-scoped-rc" --native-cli "$NATIVE_CLI" --serve true +node "$HARNESS/packed-e2e.mjs" --mode fixed --cohort alpha --artifacts "$VERIFICATION_ROOT/artifacts/fixed-scoped" --framework-root "$ALPHA_FRAMEWORK_ROOT" --run-root "$VERIFICATION_ROOT/fixed-scoped-alpha" --native-cli "$NATIVE_CLI" --serve true +node "$HARNESS/headless-check.mjs" --consumer "$VERIFICATION_ROOT/fixed-scoped-rc/dsh-home/profiles/web" --output "$VERIFICATION_ROOT/headless-scoped-rc" --native-cli "$NATIVE_CLI" +node "$HARNESS/headless-check.mjs" --consumer "$VERIFICATION_ROOT/fixed-scoped-alpha/dsh-home/profiles/web" --output "$VERIFICATION_ROOT/headless-scoped-alpha" --native-cli "$NATIVE_CLI" +``` + +服务命令持续运行,请使用独立终端。复现基线时,构建并打包干净的 main 提交,以不同制品目录、运行目录执行同一 RC2 命令。三次首次安装均采用正常 npm peer 解析。精确框架固定值避免 RC2 的传递版本范围解析到不完整的上游 RC3 发布;未改写 DSH 字节或元数据,也未绕过 peer 检查。 + +截图保留了复用夹具的会话标题“Issue 261 compatibility”。alpha 的终端恢复提示来自刻意禁用的终端夹具。这些运行验证所示公开 WebUI 行为,不证明终端运行、Windows、原生桌面可执行程序或 Native 记忆空间后端写入。peer 隔离由契约测试覆盖;最终浏览器运行未加载可选 peer 夹具。本次 CSS 修改不涉及持久化、配置、RPC 或凭据。 diff --git a/dsh/docs/pr-assets/issue-265-builtin-width-handles/verification.json b/dsh/docs/pr-assets/issue-265-builtin-width-handles/verification.json new file mode 100644 index 000000000..7ceaef634 --- /dev/null +++ b/dsh/docs/pr-assets/issue-265-builtin-width-handles/verification.json @@ -0,0 +1,1140 @@ +{ + "schemaVersion": 1, + "issue": 265, + "recordedDate": "2026-09-22", + "result": "pass", + "environment": { + "os": "macOS", + "architecture": "arm64", + "node": "v25.1.0", + "pnpm": "10.13.1 (invoked through npx for verification)", + "nativeMnemon": "0.2.8", + "model": "test-owned loopback fixture; no external model API" + }, + "implementation": { + "file": "src/client/MnemonView.module.css", + "sha256": "58cbfc1985cddca9775d5c416bd27afecada9ef1f7862166f9fa048cd1392b52", + "rule": ":has(> [data-conversation-scroll] > [data-slot='conversation.session'] > * > [data-slot='conversation.view'] > .shell[data-mnemon-surface='builtin']) > [data-width-handle]", + "declarations": { + "visibility": "hidden", + "pointer-events": "none" + }, + "builtinPosition": "static", + "builtinZIndex": "auto", + "scope": "Only direct width handles of the conversation that directly owns this Mnemon Builtin surface. Removing that surface restores native Chat handles." + }, + "baseline": { + "mainCommit": "65c0e23ba410993e16c00c8b3adf92d59d853425", + "workingTreeCleanAtPack": true, + "capturedAt": "2026-09-22T10:21:48.369Z", + "run": "baseline-rc-pinned", + "dshVersion": "0.1.5-rc.2", + "installMode": "fixed", + "legacyPeers": false, + "artifactCount": 17, + "totalPackedBytes": 607285, + "rootArtifact": { + "name": "dsh-mnemon", + "version": "0.5.12", + "filename": "dsh-mnemon-0.5.12.tgz", + "integrity": "sha512-RWKR8SDTj9WOR/P59LJGbg0UP3I7Ult92aRFQZRoWdTiKMJProqxh+uPU+oJ/o/YNTK/kkq3nXMyP5MFD1O9EA==", + "sha256": "42889c4a556b9cb6ee4797f3f5cba98a59673c63d20c4cbe848d572c87546de2", + "bytes": 299145 + }, + "client": { + "file": "lib/client.js", + "bytes": 603148, + "sha256": "c5a352edc0d1dbab58f8bbddb73c60283906d5196ad125b28a1801ec1c524818" + }, + "hover": [ + { + "hitBuiltin": false, + "hitClass": "wSkVaW_widthHandle", + "hitHandle": "left", + "hover": true, + "markOpacity": "1", + "point": { + "x": 493.40625, + "y": 511.5 + }, + "side": "left", + "zIndex": "8" + }, + { + "hitBuiltin": false, + "hitClass": "wSkVaW_widthHandle", + "hitHandle": "right", + "hover": false, + "markOpacity": "0", + "point": { + "x": 1496.59375, + "y": 511.5 + }, + "side": "right", + "zIndex": "8" + } + ], + "hoverRecord": { + "filename": "issue265-before-hit-test.json", + "sha256": "e2b45c91464e40e3b99495d6884360e8b170e58089bd1b6deb1b185c393a7680" + } + }, + "fixedBuild": { + "baseCommit": "65c0e23ba410993e16c00c8b3adf92d59d853425", + "workingTreePatchAtPack": true, + "capturedAt": "2026-09-22T10:40:21.760Z", + "artifactSet": "fixed-scoped", + "artifactCount": 17, + "totalPackedBytes": 607358, + "unchangedCompanionCount": 16, + "artifacts": [ + { + "name": "dsh-mnemon", + "version": "0.5.12", + "filename": "dsh-mnemon-0.5.12.tgz", + "integrity": "sha512-bdoOxdC6PaIBWxZjNDUcseelH+ZNyUv7GjjnvgnVS8zxQ2AD11dRN9pGtCxjrcLVfHZt/0XHqRqMlDIQc6fmdA==", + "sha256": "bd3a9c0815bbc077b42e05ad5f6a04a64301042525e7ba6d998d5c45c9cdeee5", + "bytes": 299218, + "identicalToBaseline": false + }, + { + "name": "dsh-mnemon-provider-byterover", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-byterover-0.5.4.tgz", + "integrity": "sha512-0/ru18uVKNILv3Q2Dx7soOpm+9SrARnbxCsewmQKPy8fvj14zwd7TNLlI9/lV0/vN5SgjKq95PnOvsJZVD/krw==", + "sha256": "94677bb5a27261dd2051f67c9ae5bc41c17ea86a4ea95cbd0baee1728203633f", + "bytes": 6336, + "identicalToBaseline": true + }, + { + "name": "dsh-mnemon-provider-hindsight", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-hindsight-0.5.4.tgz", + "integrity": "sha512-nQ3rQ83J2ugNLXDkG1eYPjpibFANoe3n9OkxfyPCxXkAHHKYdmIZ35mzCFG8sMWsDeSt8y1RezulWjima4ZAVw==", + "sha256": "bf527bc83d51bd7b2e510ed3be59665ae6b3ddbd4e1c3beee16c32235fa0afbb", + "bytes": 14797, + "identicalToBaseline": true + }, + { + "name": "dsh-mnemon-provider-holographic", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-holographic-0.5.4.tgz", + "integrity": "sha512-ULO6MBVq8rmRMlMnDcqJ26XPsk1K5j3HPjtTXHoBlEy6GdYQ7/2vZLtDQZ9FNjMZ9VT/W6Qj+RAc1HfgIj3bZA==", + "sha256": "67f9cc593a7bcf33001e0817a4932e23cf9f0927e90b6bc2b2ccd5ef49b91259", + "bytes": 6940, + "identicalToBaseline": true + }, + { + "name": "dsh-mnemon-provider-honcho", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-honcho-0.5.4.tgz", + "integrity": "sha512-xbbDeshwpiFZHlIUFveMbv6o6xTRY1sjNUvkcXKN/oInxCYdV4sC7Q6pBMRoSoSXYoAgUVExLLXxG+p9Z7uDZA==", + "sha256": "c102952aa1b9fdd953b25068663635a9ffad606bb39c68b56eafa538c8fab72d", + "bytes": 7451, + "identicalToBaseline": true + }, + { + "name": "dsh-mnemon-provider-mem0", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-mem0-0.5.4.tgz", + "integrity": "sha512-jriF73KpQp6DoH0RbdfJx+6HzUGEGq1/zy1Y8DmyRaRF62OhBmySGSTHZHasVu1kw61NWzSqIBFWFVn6La0wYQ==", + "sha256": "42cd225bb376d387d8e7f5503d64b6a00ddae4f5aea26b495cb9d5aa44dab3b0", + "bytes": 8105, + "identicalToBaseline": true + }, + { + "name": "dsh-mnemon-provider-mnemon-native", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-mnemon-native-0.5.5.tgz", + "integrity": "sha512-M6v7W2MomcPaPEcf5vUVfJa0j1HUdEmHwdCYR+jmK05doyuP/Vfg/+S2ZnpySKWEGu7jDlgRQbd5hb8RANKahg==", + "sha256": "cb1532ec04e1bc2550456340b453717686c2bc399e742e61be529bdae5e6f812", + "bytes": 7330, + "identicalToBaseline": true + }, + { + "name": "dsh-mnemon-provider-openviking", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-openviking-0.5.5.tgz", + "integrity": "sha512-7XP4we0/Zl9QasILR5SvEGEONk7YSXj1X9PTpPZ7eS6IgCV3uauhTV5aqZ0d2VPgSR7MYaq1nWnc0MuEZfVbXA==", + "sha256": "da3505e93462a7860d331e830930a04d4e2406be5b540333f340f9351346920f", + "bytes": 14523, + "identicalToBaseline": true + }, + { + "name": "dsh-mnemon-provider-retaindb", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-retaindb-0.5.4.tgz", + "integrity": "sha512-wztasXBFvV0M4xnJM0G0K819EMWLB0gVE0rvc4CY7XkmnqTAPoZ/26pZT9WY6ENHj84onkC2ck58xy7eqiX70A==", + "sha256": "a7b5f1efb4e68df2d7ccc7e6078cd8c3229c82cf14f0162a196c975cbe19727f", + "bytes": 5418, + "identicalToBaseline": true + }, + { + "name": "dsh-mnemon-provider-supermemory", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-supermemory-0.5.4.tgz", + "integrity": "sha512-hB6G2B5hu1ktMyqP5Al7b9uLVfV876Fs5zrgh5URlaE25e4s3VrJBkzebq5ZuhSocTDYFKMsZL4AWQGLFkb0Rg==", + "sha256": "d04b900f7f9e9359ba53b349bdbc4550fc19b350771e61df3aa451e8f67f9afd", + "bytes": 5655, + "identicalToBaseline": true + }, + { + "name": "dsh-mnemon-source-documents", + "version": "0.5.6", + "filename": "dsh-mnemon-source-documents-0.5.6.tgz", + "integrity": "sha512-RFagCAtZTGtjgOJ2mEcGv4wDtszF7R2AMVAL3kcAgs86ey2YoAztbr5rpSX3ayYC8B+sShKmq6f385+fJkHZTQ==", + "sha256": "d3f92ffd05b35ace1af629bfb39dccd419aff17f9072530c5ec3bdadc037c397", + "bytes": 39697, + "identicalToBaseline": true + }, + { + "name": "dsh-mnemon-source-memory-spaces", + "version": "0.5.8", + "filename": "dsh-mnemon-source-memory-spaces-0.5.8.tgz", + "integrity": "sha512-fLMyH0I778NKDZ5J1VfzeEss8vLC0wS1QTUaGPa67or/FTdmc3qSYLK+BpVsznN86nOvYeEuDkkovR79Myhq6g==", + "sha256": "fbebb9ad30242548746e01befbc62848cf690f99de0bcbbc98ff6b706d2e55b0", + "bytes": 138592, + "identicalToBaseline": true + }, + { + "name": "dsh-mnemon-source-runtime", + "version": "0.5.8", + "filename": "dsh-mnemon-source-runtime-0.5.8.tgz", + "integrity": "sha512-QNn9Ld9PSJgp3VFolW4+fTOOetIw8od3CDx/9MvbnSC5FwjkwtyG53X/5ROPB9SDe0JNXPAGQcSVNTrfT0Q59A==", + "sha256": "791347aaf03679a8c47ac4db11433adf608aa8174c6e1423daf708e1d9c76f6b", + "bytes": 27502, + "identicalToBaseline": true + }, + { + "name": "dsh-mnemon-strategy-auto-capture", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-auto-capture-0.5.4.tgz", + "integrity": "sha512-SYHrc0H2Pxgs5dqa48AgoYudnAlkB450H8dqAz7ILiFGEo4V2u6RQuBsF5A7QOJXwV9v3fd0YQ9eAcc5tFds9Q==", + "sha256": "3816601abd83f0c5c679f3e4c9ae96c8f697e8e15f6546e975a96fd7f86b8c02", + "bytes": 3767, + "identicalToBaseline": true + }, + { + "name": "dsh-mnemon-strategy-default-three-tier", + "version": "0.5.5", + "filename": "dsh-mnemon-strategy-default-three-tier-0.5.5.tgz", + "integrity": "sha512-6cg2mz8QyYKYjdk0YnW+IYp2TEQQsZatj89CwsDXU50sN2D8dDal5TZwBl5+qRa5MZj11mIN5ZENWCDcBqTFxw==", + "sha256": "52af5caf635f2cf2b165feff1ff3d35d241e6d073771b7b92b3ea37b36ff589f", + "bytes": 15214, + "identicalToBaseline": true + }, + { + "name": "dsh-mnemon-strategy-light-context", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-light-context-0.5.4.tgz", + "integrity": "sha512-7qbo0e9igj0x03ekcBbAunkldWOme07d/R+tI/U+0P6WsyiPGy8EX0UnqYAervd1oZ8G06+srJ0zDX6tcFX0JA==", + "sha256": "62947c3ff777fbb8d42724acf2a9c651d2b2302b387bb8579c59cd79f82a9e0d", + "bytes": 3191, + "identicalToBaseline": true + }, + { + "name": "dsh-mnemon-strategy-scoped", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-scoped-0.5.4.tgz", + "integrity": "sha512-aWPYgkexWdmg8LWFddnlnX9HbFRA6E43pbQxMTnkC8YOFja/EAArpqa0McBlffYMZedvCg7FhGWuQV7Qy7nezw==", + "sha256": "f3f945add93d2d5f8f48f0e70f5de9360814499a552218af7fa93f574dd3457a", + "bytes": 3622, + "identicalToBaseline": true + } + ], + "finalWorktreeMatch": { + "result": "pass", + "comparedFileCount": 47, + "allMatch": true, + "record": { + "filename": "issue265-final-artifact-match.json", + "sha256": "e205609a2923d17307a529c7717fc29c16a9f18c074385bd2f649384bf2432ef" + }, + "files": [ + { + "path": "LICENSE", + "bytes": 1082, + "sha256": "0bc72d9f737802b30a7e43d1b59912e81698e75948759ba07c276732aba0c7a2", + "matchesFinalWorktree": true + }, + { + "path": "lib/client.js", + "bytes": 603352, + "sha256": "77e53602fe48c33f01854453035be60dba03fca6abd7bfc7270a9201cf1d920c", + "matchesFinalWorktree": true + }, + { + "path": "lib/contracts.js", + "bytes": 905, + "sha256": "91ae208755d7142112c7da0ee15226f86e028517e24678cea89b6f7652102b02", + "matchesFinalWorktree": true + }, + { + "path": "lib/core.js", + "bytes": 380, + "sha256": "172d72135a12d9c030573c468d93cfa680b3c8e8c9a5b3ca7414f8ec313f7977", + "matchesFinalWorktree": true + }, + { + "path": "lib/definitions-j0FLgE0-.js", + "bytes": 13849, + "sha256": "ac00484576915414cddb51ac588034a74b8d642c8e19482b34cd374138928d7e", + "matchesFinalWorktree": true + }, + { + "path": "lib/extension-sdk.js", + "bytes": 1867, + "sha256": "15f4464c3efb9ce92abf529af8c81fd8be483492bcbc604b30c8138273236553", + "matchesFinalWorktree": true + }, + { + "path": "lib/index.js", + "bytes": 401453, + "sha256": "a5f4774a2caf21d9950adbcadf9d2e6801af7820936b6a3a2d3baf37c8c30c76", + "matchesFinalWorktree": true + }, + { + "path": "lib/runtime-AtIOTeP1.js", + "bytes": 64943, + "sha256": "9c69bfed94ec57222e4345c7ba1fa2ed1f896763ada5749b1f26dc399c40bd59", + "matchesFinalWorktree": true + }, + { + "path": "lib/strategy-configuration-BTXIWooc.js", + "bytes": 8672, + "sha256": "5dea51c623f16e494c0a3756e4abce21111f5ddf0506748aada877a656801486", + "matchesFinalWorktree": true + }, + { + "path": "lib/testing.js", + "bytes": 8235, + "sha256": "cb4fdc238b399a576d72b1e3d9ea19587bedb1b5b266d8cc9bfcefd19a5c7f7f", + "matchesFinalWorktree": true + }, + { + "path": "package.json", + "bytes": 8310, + "sha256": "2123cd53fb0b9a1c0488831b13570eee13a58e0d0a302db58396233866121ee3", + "matchesFinalWorktree": true + }, + { + "path": "README.md", + "bytes": 13365, + "sha256": "b17bac4bea0f0fc7e11cd4636134eb609ab84b23959f327df948caa5d037b36b", + "matchesFinalWorktree": true + }, + { + "path": "README.zh-CN.md", + "bytes": 12797, + "sha256": "3739843303083d7048a17d97ba56e8bebc9ee7c243edf21f0ad5f321c9bf440e", + "matchesFinalWorktree": true + }, + { + "path": "SECURITY.md", + "bytes": 6034, + "sha256": "c680c9d196d71368b08bfdfd91177dc4df9ffae29e0b3253f6023e1ec7317613", + "matchesFinalWorktree": true + }, + { + "path": "THIRD_PARTY_NOTICES.md", + "bytes": 1097, + "sha256": "32ebd32c67f4316e33e4214a0504ebe6dabeb1fef00106de05e457a97cb80926", + "matchesFinalWorktree": true + }, + { + "path": "bin/repair-legacy-session.mjs", + "bytes": 44022, + "sha256": "98a0b2cd1b007f6f92db177793c5aee7d852b1bdeefa01f1846fef6e14d6086c", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/host/config-values.d.ts", + "bytes": 1001, + "sha256": "00bc86ab887b27524f997afcb87874248b37fb2164e60385fa97acbec900731b", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/host/config.d.ts", + "bytes": 1525, + "sha256": "f823eba8c069a28cfb149fa67f53822aee57d8d1060af9b586bcde3dad19a490", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/core/definitions.d.ts", + "bytes": 1656, + "sha256": "c1426396948e63015b25a8e5aaccdf65c43ddf33055de833ae90a33cb76a2fec", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/host/display-mode.d.ts", + "bytes": 227, + "sha256": "9cfd641ad2162703dea794b613f98a6a42b3007c8aeb4e0f6b65b82ad5a4d678", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/client/extension-sdk.d.ts", + "bytes": 949, + "sha256": "079a1abcaf29b5007457de0cdb5415c2d10438b71cceea4ef63a09da6db0605a", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/client/index.d.ts", + "bytes": 223, + "sha256": "d43c82958f8bb83ef5cff5524813d65e3e0b25b688914b04283d238ce8e13f6f", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/core/contracts/index.d.ts", + "bytes": 1611, + "sha256": "fd893b6fabe17ac00e5847c481b1f1d85b44b73256651bb7838d9831c97b8275", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/index.d.ts", + "bytes": 127, + "sha256": "a30ee3bce8e37249b8461b925cc0e626942d4a0cf6233c13a3f0d010f6d5ad47", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/sdk/index.d.ts", + "bytes": 1689, + "sha256": "0814e66b41dc5d2391d1fa2d724bb766ba89153d1f8101c5b79c9c7987596ed2", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/sdk/input.d.ts", + "bytes": 1272, + "sha256": "3264d99270796e102939d3d412f4c38c220e8c4f79ac6d001bf5fb374248d246", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/sdk/install.d.ts", + "bytes": 432, + "sha256": "2b3e0903128d4b79861233772966fce157bd18bd8234beadcbdef4bcfc7ca93e", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/client/locales.d.ts", + "bytes": 75700, + "sha256": "405f4d8b68f88d70d2f17ae8af97e0b3a62885893a79f841ed653b0be1a45dc1", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/client/MnemonDialog.d.ts", + "bytes": 467, + "sha256": "cf9de6f80a98d6e527860d7077ce5cc5098b2beda55aecf25790403974bd1c34", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/client/MnemonLogo.d.ts", + "bytes": 246, + "sha256": "c1532a4f925703354772392d59b767cba82db7cfa0ede0ed2c0c4ad7f952a405", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/client/page-client.d.ts", + "bytes": 871, + "sha256": "8a39aabfe82b9127f1be2443c35d634ec665613c63464c27434142c5caa89f96", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/client/page-kit.d.ts", + "bytes": 1616, + "sha256": "ce40c66a2409f540adea88e82183bd17cbcbd20d7e0a3796d150283f34082b12", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/core/contracts/plugin.d.ts", + "bytes": 1216, + "sha256": "430b6c765482d75525a62c8515118e2926eb218704a1ebee73743d347f63e406", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/core/plugin.d.ts", + "bytes": 318, + "sha256": "73a8b54a714b0f680507a4c935a3fc9b17918e106bf901883d1226d69729d6a2", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/host/plugin.d.ts", + "bytes": 401, + "sha256": "7cdc0ce523adf64877709eee34f42155c2b6f8367ca8a9788d490a0d0644cefe", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/host/protocol.d.ts", + "bytes": 20250, + "sha256": "7369016112ee86640a0f175c2b7759f21bbef86a14c5ea63edf89aa16e3b675b", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/sdk/service.d.ts", + "bytes": 1400, + "sha256": "7ef3753a9dc3537e852c483bd00055de83c207d7cd2e327d05a9517787cac15d", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/client/source-contracts.d.ts", + "bytes": 2139, + "sha256": "b92fd9f5312c5861b932fde88daa215f534dcedf11efde3eb097e21b6178f788", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/client/source-pages.d.ts", + "bytes": 3332, + "sha256": "4272de97752579c469648a1a1ba223940bea27145209b1ea3e6e1ad5601bdb51", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/sdk/storage-lock.d.ts", + "bytes": 329, + "sha256": "9072c9369822413d48c46c929530e950cdc07cc03a05342e05a0730cea4e44f9", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/sdk/strategy-configuration.d.ts", + "bytes": 2043, + "sha256": "e87fea784b5b29bfcffaabe448fec056eaa3a0a64baf315b3a9f0a647d83308a", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/sdk/testing.d.ts", + "bytes": 3965, + "sha256": "a27d5ce27c21159ea0786078852a68faf214ba59d7f0a744aa44ce9acd238133", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/client/use-request-version.d.ts", + "bytes": 271, + "sha256": "65bb1a8eddc167bdd3c730024b589792e2fe0535a6f770b81585ff85653cd78e", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/host/view-protocol.d.ts", + "bytes": 3842, + "sha256": "528ecab3f74b2541598ef763a534d2ee7ed95028721779266b5f1f54d998aafe", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/client/view-styles.d.ts", + "bytes": 167, + "sha256": "cebd26c73d5ba1bf720d201402380e3c47767141436d6d06b09db29566298637", + "matchesFinalWorktree": true + }, + { + "path": "lib/types/src/core/contracts/view.d.ts", + "bytes": 15221, + "sha256": "848404392f44382804da5d03c36836076966b851c028b57e5e714a3d684f76e0", + "matchesFinalWorktree": true + }, + { + "path": "cordis.patch.yml", + "bytes": 3698, + "sha256": "a5f6d790a84e628335b560c52fb669b0c9717513cf9f94575646e79655cb2964", + "matchesFinalWorktree": true + } + ] + } + }, + "cohorts": [ + { + "run": "fixed-scoped-rc", + "dshVersion": "0.1.5-rc.2", + "dshPackageCount": 234, + "allDshPackagesAtExactVersion": true, + "mnemonPackageCount": 17, + "resolutionCheckCount": 51, + "testClientPackageCount": 0, + "install": { + "mode": "fixed", + "legacyPeers": false, + "reusedExistingInstall": false, + "reason": "Normal npm peer resolution, no legacy-peer-deps." + }, + "publicBytesChecked": [ + { + "package": "@deepseek-ai/dsh-client-ui-slots", + "file": "lib/index.js", + "sha256": "5433ef2cb74699a59000052c4b1899808c83544f45ba6176f297efc68414c588" + }, + { + "package": "@deepseek-ai/dsh-client-ui-chat", + "file": "lib/client.js", + "sha256": "6527556d25c7b2a27f2e3bfb96cc2ae3d9244f1213147e245b17684ad8d9e396" + }, + { + "package": "@deepseek-ai/dsh-client-ui-renderer", + "file": "lib/client.js", + "sha256": "859d0a187e436d682c71d18f0266d588f923d7e1628157ad73000f99a377d151" + }, + { + "package": "@deepseek-ai/dsh-client-ui-conversation", + "file": "lib/client.js", + "sha256": "81314dfd95864f2522f8edb812e3f8e08b04a8ef2141913e6e8cbdaae1ffc37f" + } + ], + "runtimeGraphRecord": { + "filename": "runtime-graph.json", + "sha256": "c8ea4ce1800201a46da0a016be711d96ee413427da8a74c7ece55a7dc3a2c05f" + }, + "headless": { + "run": "headless-scoped-rc", + "result": "pass", + "completedAt": "2026-09-22T10:44:51.084Z", + "nativeVersion": "mnemon version 0.2.8", + "enabledStrategyExtensions": [ + "mnemon-strategy-scoped", + "mnemon-strategy-light-context", + "mnemon-strategy-auto-capture" + ], + "mnemonToolCounts": { + "enabled": 17, + "restart": 17, + "disabled": 0 + }, + "mnemonTools": [ + "mnemon_document_create", + "mnemon_document_manage", + "mnemon_document_search", + "mnemon_forget", + "mnemon_link", + "mnemon_memory_bodies", + "mnemon_memory_body_create", + "mnemon_memory_body_merge", + "mnemon_memory_body_update", + "mnemon_recall", + "mnemon_related", + "mnemon_remember", + "mnemon_runtime_memory", + "mnemon_status", + "mnemon_subagent_result", + "mnemon_view_action", + "mnemon_view_route" + ], + "runtimePersistence": [ + { + "file": "runtime/memories.json", + "sha256": "9164995f92a7406aec5292d86da60858e1939b96400de98a5c1c89c95b8b479a" + } + ], + "resultRecord": { + "filename": "result.json", + "sha256": "d217650edce6255d9c5d0d91a1e0eacf3bcfe10112cad74ec401c8c2bd20b343" + } + } + }, + { + "run": "fixed-scoped-alpha", + "dshVersion": "0.1.6-alpha.2", + "dshPackageCount": 251, + "allDshPackagesAtExactVersion": true, + "mnemonPackageCount": 17, + "resolutionCheckCount": 51, + "testClientPackageCount": 0, + "install": { + "mode": "fixed", + "legacyPeers": false, + "reusedExistingInstall": false, + "reason": "Normal npm peer resolution, no legacy-peer-deps." + }, + "publicBytesChecked": [ + { + "package": "@deepseek-ai/dsh-client-ui-slots", + "file": "lib/index.js", + "sha256": "57e1314e31a2015f30231e1e7596da658f1bb5cb64c103c4cf13381647412c99" + }, + { + "package": "@deepseek-ai/dsh-client-ui-chat", + "file": "lib/client.js", + "sha256": "a022200a86e6ba0301c861f6114214ee5fe0a849401332d1962276f142641f9e" + }, + { + "package": "@deepseek-ai/dsh-client-ui-renderer", + "file": "lib/client.js", + "sha256": "ed156076ac2a5c26e6db40d8fab165a0ec8dd4f6d36f6cf0e595111bf4afea34" + }, + { + "package": "@deepseek-ai/dsh-client-ui-conversation", + "file": "lib/client.js", + "sha256": "c02a68ebd98c7f32f70ab3302a6a6f5304c61717f28e87f616b070cbf105dabe" + } + ], + "runtimeGraphRecord": { + "filename": "runtime-graph.json", + "sha256": "33d83b6b281e5497d08ccc86631009b6cc2615f8d8c9e6c05d371b1164e74b8f" + }, + "headless": { + "run": "headless-scoped-alpha", + "result": "pass", + "completedAt": "2026-09-22T10:44:50.907Z", + "nativeVersion": "mnemon version 0.2.8", + "enabledStrategyExtensions": [ + "mnemon-strategy-scoped", + "mnemon-strategy-light-context", + "mnemon-strategy-auto-capture" + ], + "mnemonToolCounts": { + "enabled": 17, + "restart": 17, + "disabled": 0 + }, + "mnemonTools": [ + "mnemon_document_create", + "mnemon_document_manage", + "mnemon_document_search", + "mnemon_forget", + "mnemon_link", + "mnemon_memory_bodies", + "mnemon_memory_body_create", + "mnemon_memory_body_merge", + "mnemon_memory_body_update", + "mnemon_recall", + "mnemon_related", + "mnemon_remember", + "mnemon_runtime_memory", + "mnemon_status", + "mnemon_subagent_result", + "mnemon_view_action", + "mnemon_view_route" + ], + "runtimePersistence": [ + { + "file": "runtime/memories.json", + "sha256": "3b554457fa52c027e3dbf0ee9edb3ab738ae76f1fd5b32e69cf6cffbe9039d95" + } + ], + "resultRecord": { + "filename": "result.json", + "sha256": "e1c6d1f8400b319532ec794bfcd1345123d65579b0b1e64647221e876a20fac2" + } + } + } + ], + "browser": { + "driver": "actual browser interactions with installed public DSH WebUI and packed Mnemon", + "fixedRc": { + "observation": { + "builtin": { + "position": "static", + "zIndex": "auto" + }, + "composers": [ + { + "hitOwn": true, + "label": "发消息或创建任务, / 调用指令, @ 文件或对话", + "rect": { + "height": 36, + "width": 943.1953125, + "x": 516.3984375, + "y": 771 + }, + "visibility": "visible" + } + ], + "handles": [ + { + "hitBuiltin": true, + "hitHandle": null, + "hover": false, + "markOpacity": "0", + "point": { + "x": 493.40625, + "y": 483.5 + }, + "pointerEvents": "none", + "side": "left", + "visibility": "hidden" + }, + { + "hitBuiltin": true, + "hitHandle": null, + "hover": false, + "markOpacity": "0", + "point": { + "x": 1496.59375, + "y": 483.5 + }, + "pointerEvents": "none", + "side": "right", + "visibility": "hidden" + } + ] + }, + "record": { + "filename": "issue265-final-rc2-hit-test.json", + "sha256": "7f1783906ff7e7c25f2f83a82a24112be04d8d3b97522574888ef46f98bfb218" + } + }, + "fixedAlpha": { + "observation": { + "builtin": { + "position": "static", + "zIndex": "auto" + }, + "composers": [ + { + "hitOwn": true, + "label": "发消息或创建任务, / 调用指令, @ 文件或对话", + "rect": { + "height": 36, + "width": 943.1953125, + "x": 516.3984375, + "y": 771 + }, + "visibility": "visible" + } + ], + "handles": [ + { + "hitBuiltin": true, + "hitHandle": null, + "hover": false, + "markOpacity": "0", + "point": { + "x": 508.40625, + "y": 483.5 + }, + "pointerEvents": "none", + "side": "left", + "visibility": "hidden" + }, + { + "hitBuiltin": true, + "hitHandle": null, + "hover": false, + "markOpacity": "0", + "point": { + "x": 1481.59375, + "y": 483.5 + }, + "pointerEvents": "none", + "side": "right", + "visibility": "hidden" + } + ] + }, + "record": { + "filename": "issue265-final-alpha2-hit-test.json", + "sha256": "6a8ff33e17f93db579c714427d2bb6543b8a073d629cad2a15e1ab1a23dea85a" + } + }, + "chatDrag": { + "rc": { + "leftXBefore": 493.40625, + "leftXAfter": 553.40625, + "rightXBefore": 1496.59375, + "rightXAfter": 1436.59375, + "composerWidthBefore": 943.1953125, + "composerWidthAfter": 823.1953125, + "restoredThroughRealDrag": true, + "observation": { + "builtin": null, + "composers": [ + { + "hitOwn": true, + "label": "发消息或创建任务, / 调用指令, @ 文件或对话", + "rect": { + "height": 36, + "width": 823.1953125, + "x": 576.3984375, + "y": 771 + }, + "visibility": "visible" + } + ], + "handles": [ + { + "hitBuiltin": false, + "hitHandle": "left", + "hover": true, + "markOpacity": "1", + "point": { + "x": 553.40625, + "y": 483.5 + }, + "pointerEvents": "auto", + "side": "left", + "visibility": "visible" + }, + { + "hitBuiltin": false, + "hitHandle": "right", + "hover": false, + "markOpacity": "0", + "point": { + "x": 1436.59375, + "y": 483.5 + }, + "pointerEvents": "auto", + "side": "right", + "visibility": "visible" + } + ] + }, + "record": { + "filename": "issue265-rc2-chat-drag.json", + "sha256": "cd0e4b4c83e15836d5dad7011d0a795324f4017c8bded7b96c88dce20089bd6a" + } + }, + "alpha": { + "leftXBefore": 508.40625, + "leftXAfter": 568.40625, + "rightXBefore": 1481.59375, + "rightXAfter": 1421.59375, + "composerWidthBefore": 943.1953125, + "composerWidthAfter": 823.1953125, + "observation": { + "builtin": null, + "composers": [ + { + "hitOwn": true, + "label": "发消息或创建任务, / 调用指令, @ 文件或对话", + "rect": { + "height": 36, + "width": 823.1953125, + "x": 576.3984375, + "y": 771 + }, + "visibility": "visible" + } + ], + "handles": [ + { + "hitBuiltin": false, + "hitHandle": "left", + "hover": true, + "markOpacity": "1", + "point": { + "x": 568.40625, + "y": 483.5 + }, + "pointerEvents": "auto", + "side": "left", + "visibility": "visible" + }, + { + "hitBuiltin": false, + "hitHandle": "right", + "hover": false, + "markOpacity": "0", + "point": { + "x": 1421.59375, + "y": 483.5 + }, + "pointerEvents": "auto", + "side": "right", + "visibility": "visible" + } + ] + }, + "record": { + "filename": "issue265-alpha2-chat-drag.json", + "sha256": "022e2b94d2cc3d25a225e982bb616cd3a481c6c9073abeee3a821eae6ffb0c6d" + } + } + }, + "residentComposer": { + "cohort": "0.1.5-rc.2", + "sentWhileBuiltinOpen": "Issue 265: the resident composer works while Builtin memory is open.", + "chatShowsNewUserTurnAndCompletedModelReply": true, + "displayedRounds": 2, + "displayedSteps": 4 + }, + "runtimeSourceDialog": { + "cohort": "0.1.5-rc.2", + "openedBodyPortalDialog": true, + "submittedContent": "Issue 265 WebUI: Runtime Source 对话框写入成功,宽度手柄没有截获点击。", + "successReceiptVisible": true, + "entryCount": 2, + "browserReloadAndSessionReselectionPreservedBothEntries": true, + "postReloadHandleHitBuiltin": true, + "postReloadComposerHitOwn": true, + "storage": { + "file": "runtime/memories.json", + "sha256": "341c8c25d54b7812689ae1add3efc46742732ca01f70899c45ab577b9b7011ca", + "version": 1, + "entries": [ + { + "content": "Issue 261 compatibility: the isolated published DSH cohort uses real Mnemon runtime memory.", + "created_at": "2026-09-22T10:46:18.897Z", + "updated_at": "2026-09-22T10:46:18.897Z", + "target": "memory", + "importance": "normal" + }, + { + "content": "Issue 265 WebUI: Runtime Source 对话框写入成功,宽度手柄没有截获点击。", + "created_at": "2026-09-22T10:51:21.954Z", + "updated_at": "2026-09-22T10:51:21.954Z", + "target": "memory", + "importance": "normal" + } + ] + } + } + }, + "validation": { + "fullVerify": { + "command": "MNEMON_NATIVE_TEST_CLI= npx --yes pnpm@10.13.1 run verify", + "result": "pass", + "exitCode": 0, + "rootTests": { + "passed": 1189, + "skipped": 5 + }, + "pluginTests": { + "passed": 382, + "skipped": 2 + }, + "combinedTests": { + "passed": 1571, + "skipped": 7 + }, + "skipReasons": [ + "Five opt-in real DeepSeek V4 Flash pressure/quality cases were not enabled.", + "One opt-in Windows Native smoke case was not enabled on macOS.", + "One OpenViking integration case requires an explicitly configured endpoint." + ], + "typecheck": "pass", + "deterministicBuild": "pass", + "headlessProfile": "pass", + "package": { + "files": 47, + "packedBytes": 299218, + "unpackedBytes": 1338567, + "nodeEntries": 12, + "publicTypeDependencies": 27, + "executableHelpEntries": 1, + "publint": "pass", + "attw": "pass" + }, + "record": { + "filename": "issue265-final-verify.log", + "sha256": "aab15ac40f0c1dc43b128d19686c3770ffd73791dc26ded128aa2e2cd2516510" + } + }, + "independentPluginVerification": { + "command": "npx --yes pnpm@10.13.1 run verify:plugins --skip-build", + "result": "pass", + "exitCode": 0, + "independentRepositories": 16, + "packedArtifacts": 17, + "publicSdkOnlyComposition": true, + "clientTests": true, + "optionalStrategiesActivateTogether": 3, + "rootOnlyInstallAndActivation": "pass", + "record": { + "filename": "issue265-final-verify-plugins.log", + "sha256": "0659f833b6cc77c60c7a8834983aa825436bb8a4d63b58aa899700b3cefab918" + } + }, + "selectorContract": { + "file": "tests/client-builtin-width-handles.spec.ts", + "sha256": "0de16669dbf11a5600cc87499de256d483a65442995cbf5ad2b5625c035a57af", + "fixed": { + "passed": 6, + "failed": 0, + "record": { + "filename": "issue265-selector-final-docs.log", + "sha256": "cae864b0170b90307b2566de172ceefeb53569effdfa95783febb974925de7cc" + } + }, + "baseline": { + "passed": 3, + "failed": 3, + "exitCode": 1, + "cssSha256": "c26e2b0cf5e31f2216c8d0dcc42838214dab7cd465d7d802943fc862e20fc6bf", + "record": { + "filename": "baseline-failure.log", + "sha256": "e7455840965b04294cb70220c1063e8392af47e825beab5fdcf7cabcc1385d8a" + } + }, + "coverage": [ + "owning handles and resident composer", + "Sidebar and peer/foreign surfaces", + "nested and sibling conversations in both directions", + "surface removal, remount, and replacement" + ], + "method": "JSDOM 26 adapter reads the authored CSS rule and resolves its relative selector with :scope because native JSDOM rejects this multi-combinator :has expression. Native browser hit testing is recorded separately." + }, + "nativeCliIntegration": { + "version": "0.2.8", + "enabledBy": "MNEMON_NATIVE_TEST_CLI", + "sourceComposition": { + "file": "plugins/dsh-mnemon-source-memory-spaces/tests/native-integration.spec.ts", + "result": "pass", + "operations": [ + "create space", + "write through View", + "list", + "recall", + "forget" + ] + }, + "capacityWorkflow": { + "file": "tests/runtime-capacity-workflow.spec.ts", + "result": "pass", + "operations": [ + "archive exact checkpoints to two Native namespaces through Host tools" + ] + }, + "scope": "Isolated test-owned storage; these are real CLI operations separate from the WebUI Runtime Source checks." + } + }, + "screenshots": [ + { + "file": "after-alpha2.png", + "bytes": 87252, + "sha256": "9ae6ab1154825e302a0e7a4181e583610b96c28f438ba647bbf3d519bd49a365" + }, + { + "file": "after-rc2.png", + "bytes": 85739, + "sha256": "5ed6d11403e79cd203242a050b4ca34217d6a3b85c97ce1ef8196017f4744631" + }, + { + "file": "before-rc2.png", + "bytes": 89585, + "sha256": "cf384464e7bd0e31965c18234139f1fff8e13e52fcc8b8d38f8ffa32c3dd35e9" + }, + { + "file": "chat-restored-rc2.png", + "bytes": 60440, + "sha256": "5f5ddc61aa942edb51fbc1a1541ba2141fbf22465e7fcbdce2b613b05b793d67" + }, + { + "file": "runtime-dialog-rc2.png", + "bytes": 74047, + "sha256": "e4143303614bdb6a107ed2359bc31d6603dacbabea886be167916a71114bedbd" + }, + { + "file": "runtime-write-rc2.png", + "bytes": 90408, + "sha256": "553587951eeb541649c29dd2b9470a9943a3b10c199183e6ea512e0711501de5" + } + ], + "reproduction": { + "harness": "docs/pr-assets/issue-261-dsh-slots/harness", + "baselineArtifactSet": "baseline", + "fixedArtifactSet": "fixed-scoped", + "initialRuns": [ + "baseline-rc-pinned", + "fixed-scoped-rc", + "fixed-scoped-alpha" + ], + "headlessRuns": [ + "headless-scoped-rc", + "headless-scoped-alpha" + ], + "exactFrameworkPins": true, + "npmPeerResolution": "normal; no legacy-peer-deps", + "frameworkSetupNote": "Fresh unpinned RC2 resolution encountered an incomplete upstream RC3 transitive release, missing @deepseek-ai/dsh-client-ui-sidebar-documentpreview@^0.1.5-rc.3. Exact physically installed public framework/supporting-peer pins were used for both cohorts. No public package metadata or bytes were changed." + }, + "limits": [ + "The screenshots retain the reusable harness session title Issue 261 compatibility.", + "The alpha screenshot terminal-recovery toast comes from the deliberately disabled terminal fixture; terminal shell operation was not certified.", + "This evidence exercises public npm WebUI on macOS arm64, not the native desktop executable or Windows.", + "Peer/nested/sibling isolation is covered by selector contract tests; the final browser runs did not load the optional peer fixture.", + "The WebUI checks exercise Runtime Source add/status and persistence; real Native backend operations are verified separately by the enabled CLI integration and capacity tests.", + "No intermediate stacking-layer prototype artifact or run is counted as passing evidence." + ] +} diff --git a/dsh/docs/pr-assets/issue-266-workspace-ancestor/README.md b/dsh/docs/pr-assets/issue-266-workspace-ancestor/README.md new file mode 100644 index 000000000..493611557 --- /dev/null +++ b/dsh/docs/pr-assets/issue-266-workspace-ancestor/README.md @@ -0,0 +1,59 @@ +# Windows workspace ancestors — issue #266 + +[简体中文](./README.zh-CN.md) | [Issue #266](https://github.com/omdsh-dev/dsh-mnemon/issues/266) | [Windows observations](./windows-results.json) | [WebUI observations](./webui-verification.json) | [Verification](./verification.json) + +On 2026-09-22, real Windows runners reproduced the invalid workspace identity reported in #266. The baseline returned storage hashes for paths below a regular file; the fix rejects both a child and a deeper descendant with `ENOTDIR`. Baseline: `65c0e23ba410993e16c00c8b3adf92d59d853425` (v0.5.12). Implementation: `1750e497e58c24406c0092e4fc1549996283aab8`. + +## Native Windows before and after + +The runners used Windows build 26100, Node 22.19.0 / libuv 1.51.0 and Node 24.20.0 / libuv 1.52.1. Each run creates a disposable regular file, directory and junction, then imports the repository's TypeScript module directly. The native probe observes `realpathSync.native(file/child)` throwing `ENOENT` (errno `-4058`) while `realpathSync.native(file)` succeeds. No filesystem calls are mocked in this observation. + +| Run | Native contract cases, each Node version | Reported test files, each Node version | +|---|---|---| +| [Baseline](https://github.com/omdsh-dev/dsh-mnemon/actions/runs/35715519552) | 10 pass, 2 fail: file child and deeper descendant return hashes | 26 pass, 3 fail | +| [Fixed](https://github.com/omdsh-dev/dsh-mnemon/actions/runs/35715781789) | 12 pass; both file descendants throw `ENOTDIR` | 31 pass, 0 fail | + +The baseline workflow deliberately requires the reported failures; its successful workflow status means reproduction succeeded. Both phases retain existing file identities, missing directory descendants, Unicode names, existing roots and matching junction identities. Invalid relative, empty and NUL-containing inputs remain rejected. The original file's content remains unchanged. + +The three baseline test failures were the actual non-directory ancestor assertion, the Client import boundary's hardcoded POSIX separators, and the delegated workspace scope's hardcoded POSIX identity. The latter two fixes only make test paths portable. A permanent Windows CI matrix now runs all three files on Node 22.19 and 24. + +The validation-only branch contains the [baseline workflow and harness](https://github.com/omdsh-dev/dsh-mnemon/tree/22690919434d18d8537ef74e08a4d200282a199e/scripts/issue-266-windows) and [fixed workflow and harness](https://github.com/omdsh-dev/dsh-mnemon/tree/fde19025ff6452fcfd0643f377593b83279ca232/scripts/issue-266-windows). Check out the corresponding revision on Windows and run: + +```powershell +node --experimental-strip-types scripts/issue-266-windows/observe.mjs baseline +# Use fixed instead of baseline at the fixed validation revision. +pnpm install --frozen-lockfile +pnpm exec vitest run tests/workspace-storage.spec.ts tests/client-platform-boundary.spec.ts tests/async-subagent-memory.spec.ts +``` + +The JSON evidence retains all native case observations and concise test results. Its SHA-256 fields verify the imported source against the stated product revisions, including the runner's LF-to-CRLF checkout conversion. Complete routine Vitest output remains attached to the Actions runs. + +## Portable regression and integration checks + +Before changing production code, the new portable regression failed against the baseline: 1 failed / 5 passed in `workspace-storage.spec.ts`. It replaces only the first native `realpath` call with Windows's `ENOENT`; resolution of the existing file and filesystem type inspection remain real. This models the missing branch on POSIX and is separate from the actual Windows evidence above. All 31 focused tests pass after the fix on macOS too. + +Local verification used macOS arm64, Node 25.1.0, pnpm 10.13.1, published DSH 0.1.5-rc.1 and the official Mnemon CLI 0.2.8. With `MNEMON_NATIVE_TEST_CLI` set, `pnpm run verify` passed 1,185 root tests and 382 plugin tests, deterministic builds, type checks, real Native integration and capacity checks, isolated Headless activation/restart, and package validation. Five root live-Flash tests, one live-OpenViking test and one macOS-inapplicable Windows Native smoke were skipped. + +Then `MNEMON_PLUGIN_VERIFY_CONCURRENCY=4 pnpm run verify:plugins --skip-build` passed for 16 independent plugin repositories, 17 artifacts, the public SDK-only external consumer, packed-Starter activation and all three optional Strategies together. Builds and artifact consumers ran sequentially. Seventeen verified artifacts were packed from the clean implementation revision for the separate macOS WebUI smoke below. + +## macOS WebUI with published RC2 and real CLI + +Chrome on macOS loaded all 17 artifacts from implementation `1750e497`, with published DSH 0.1.5-rc.2 and the real Mnemon CLI 0.2.8. Scoped, Light context and Active capture were enabled together. The isolated profile contains 234 DSH packages, all exactly RC2; all 51 checked peer resolutions stay inside its `node_modules`, and four Client entry files match public package bytes. The Root tarball SHA-256 is `3d5259f8a995a662ecf3e015282f42490a4be13e006e4672715aa05a8f6c353b`; the [WebUI record](./webui-verification.json) contains all 17 artifact hashes and the CLI hash. + +The UI selected centralized workspaces and an existing directory named `项目 with spaces`. Sending `compatibility-261` used a local deterministic model to call the real `mnemon_runtime_memory` add and `mnemon_status` tools. The status page showed a healthy system and one Runtime entry. Clicking Runtime → 添加记忆 then saved `Issue 266 WebUI: 中文与空格工作区使用稳定集中存储,Runtime 写入后可读回。`; the page showed two entries and a success message. + +| Centralized workspace status | Runtime write and readback | +|---|---| +| ![Healthy centralized Unicode workspace](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-266-workspace-ancestor/webui-centralized-status.png) | ![Two Runtime entries after the UI write](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-266-workspace-ancestor/webui-runtime-write.png) | + +Read-only inspection confirmed the canonical workspace hash `b541b67e760240d3e9cf9985777163114eb92d9dad1ce95546b73fd0e04c4c12`, with exactly two entries under `/workspaces//runtime/memories.json` and the exact added text in its `MEMORY.md` projection. The global Runtime remained empty, and no `.mnemon` directory appeared inside the workspace. The JSON and projection hashes are retained in the WebUI record. The screenshots' “Issue 261” session title and first entry are intentionally retained labels from the reused compatibility fixture. + +An unpinned install during this task encountered an incomplete public RC3 transitive release. The smoke therefore used a fresh consumer pinned to the exact published RC2 dependency graph from a previously verified profile, without `--legacy-peer-deps`. An external copy of the [existing setup harness](../issue-261-dsh-slots/harness/packed-e2e.mjs) was adjusted only to honor `--framework-root` in fixed mode; its [small setup delta](./webui-rc2-pins.patch) is retained, and its SHA-256 is `9de47863b19cae46122ab030675879c11b6e598bd46f31c302bd9eeb59b598d2`. No DSH package source or release manifest was changed. + +This smoke checks valid centralized workspace operation on macOS, separately from the native Windows rejection evidence. The browser run checked the Native CLI version but did not create a Native Memory Space; real Native writes, recall, forget and capacity checks belong to the full verification above. Model responses and all stored content were synthetic; no external model API or personal credentials were used. + +## Compatibility and data + +The additional directory check runs only after a successful ancestor resolution with a nonempty missing suffix. Existing file paths without descendants retain their previous identity behavior. Valid workspace IDs, aliases, Unicode names and not-yet-created directory descendants are unchanged. Resolving an identity still writes nothing. + +Paths below regular files are now rejected before hashing on Windows. Any old central directory remains on disk; there is no format change, migration, merge or deletion. Rolling back restores the old acceptance behavior without a data conversion. The patch changeset covers only `dsh-mnemon`; no DSH source, published contract, Provider credential or external service behavior changes. The other storage modes and UI instructions were reviewed and require no changes. diff --git a/dsh/docs/pr-assets/issue-266-workspace-ancestor/README.zh-CN.md b/dsh/docs/pr-assets/issue-266-workspace-ancestor/README.zh-CN.md new file mode 100644 index 000000000..a1b004ab5 --- /dev/null +++ b/dsh/docs/pr-assets/issue-266-workspace-ancestor/README.zh-CN.md @@ -0,0 +1,59 @@ +# Windows 工作区祖先路径 — issue #266 + +[English](./README.md) | [Issue #266](https://github.com/omdsh-dev/dsh-mnemon/issues/266) | [Windows 观测](./windows-results.json) | [WebUI 观测](./webui-verification.json) | [验证记录](./verification.json) + +2026-09-22,真实 Windows runner 复现了 #266 报告的无效工作区身份。基线会为普通文件下的路径返回存储哈希;修复后,直接子路径与更深后代均以 `ENOTDIR` 拒绝。基线:`65c0e23ba410993e16c00c8b3adf92d59d853425`(v0.5.12)。实现:`1750e497e58c24406c0092e4fc1549996283aab8`。 + +## 真实 Windows 前后对比 + +runner 使用 Windows build 26100、Node 22.19.0 / libuv 1.51.0,以及 Node 24.20.0 / libuv 1.52.1。每次运行都创建临时普通文件、目录与 junction,然后直接导入仓库的 TypeScript 模块。原生探针观测到 `realpathSync.native(file/child)` 抛出 `ENOENT`(errno `-4058`),而 `realpathSync.native(file)` 成功。该观测没有 mock 文件系统调用。 + +| 运行 | 每个 Node 版本的原生契约用例 | 每个 Node 版本的报告所涉测试文件 | +|---|---|---| +| [基线](https://github.com/omdsh-dev/dsh-mnemon/actions/runs/35715519552) | 10 通过、2 失败:文件子路径及更深后代返回哈希 | 26 通过、3 失败 | +| [修复](https://github.com/omdsh-dev/dsh-mnemon/actions/runs/35715781789) | 12 通过;两种文件后代均抛出 `ENOTDIR` | 31 通过、0 失败 | + +基线 workflow 特意断言报告中的失败出现;其成功状态代表复现成功。前后两次都保留现存文件身份、尚未创建的目录后代、Unicode 名称、现存根目录以及 junction 的相同身份。相对路径、空字符串和含 NUL 输入仍被拒绝。原文件内容保持不变。 + +基线的三处测试失败分别是:非目录祖先的实际断言、Client 导入边界硬编码 POSIX 分隔符,以及委派工作区范围硬编码 POSIX 身份。后两处修正仅提高测试路径的跨平台兼容性。正式 CI 新增 Windows 矩阵,在 Node 22.19 和 24 上持续运行这三个文件。 + +验证专用分支保留了[基线 workflow 与脚本](https://github.com/omdsh-dev/dsh-mnemon/tree/22690919434d18d8537ef74e08a4d200282a199e/scripts/issue-266-windows)和[修复 workflow 与脚本](https://github.com/omdsh-dev/dsh-mnemon/tree/fde19025ff6452fcfd0643f377593b83279ca232/scripts/issue-266-windows)。在 Windows 上检出相应 revision 后运行: + +```powershell +node --experimental-strip-types scripts/issue-266-windows/observe.mjs baseline +# 在修复后的验证 revision 上,将 baseline 替换为 fixed。 +pnpm install --frozen-lockfile +pnpm exec vitest run tests/workspace-storage.spec.ts tests/client-platform-boundary.spec.ts tests/async-subagent-memory.spec.ts +``` + +JSON 证据保留所有原生用例观测及精简测试结果。SHA-256 字段核对实际导入源码与标注的产品 revision,包括 runner 检出时从 LF 到 CRLF 的转换。完整的常规 Vitest 输出保留为 Actions artifact。 + +## 跨平台回归与集成检查 + +修改产品代码前,新增跨平台回归在基线上失败:`workspace-storage.spec.ts` 为 1 失败、5 通过。它仅将第一次原生 `realpath` 调用替换为 Windows 的 `ENOENT`;现存文件的后续解析及文件系统类型检查仍使用真实文件。该用例在 POSIX 上覆盖缺失分支,与上述真实 Windows 证据相互独立。修复后,macOS 上全部 31 项定向测试也通过。 + +本地验证使用 macOS arm64、Node 25.1.0、pnpm 10.13.1、正式 DSH 0.1.5-rc.1 与官方 Mnemon CLI 0.2.8。设置 `MNEMON_NATIVE_TEST_CLI` 后,`pnpm run verify` 通过 1,185 项根测试、382 项插件测试、确定性构建、类型检查、真实 Native 集成与容量流程、隔离 Headless 激活/重启,以及制品校验。跳过 5 项需要真实 Flash 的根测试、1 项真实 OpenViking 测试,以及 1 项不适用于 macOS 的 Windows Native 冒烟。 + +随后,`MNEMON_PLUGIN_VERIFY_CONCURRENCY=4 pnpm run verify:plugins --skip-build` 通过 16 个独立插件仓库、17 个制品、只用公开 SDK 的外部消费者、打包 Starter 激活,以及三个可选 Strategy 同时启用验证。构建与制品消费者顺序执行。另从干净的实现 revision 打包全部 17 个已验证制品,供下面独立的 macOS WebUI 冒烟使用。 + +## 正式 RC2 与真实 CLI 的 macOS WebUI + +macOS 上的 Chrome 加载了实现 `1750e497` 的全部 17 个制品,使用正式 DSH 0.1.5-rc.2 与真实 Mnemon CLI 0.2.8。Scoped、Light context 和 Active capture 同时启用。隔离 profile 包含 234 个 DSH 包,全部精确为 RC2;51 项已检查的 peer 解析都位于该 profile 的 `node_modules` 内,4 个 Client 入口文件与公开制品字节一致。Root tarball 的 SHA-256 为 `3d5259f8a995a662ecf3e015282f42490a4be13e006e4672715aa05a8f6c353b`;[WebUI 记录](./webui-verification.json)保留全部 17 个制品哈希与 CLI 哈希。 + +在 UI 选择集中工作区,并选中现存的 `项目 with spaces` 目录。发送 `compatibility-261` 后,本地确定性模型调用真实的 `mnemon_runtime_memory` add 与 `mnemon_status` 工具。状态页显示系统正常、Runtime 一条。随后点击 Runtime → 添加记忆,保存 `Issue 266 WebUI: 中文与空格工作区使用稳定集中存储,Runtime 写入后可读回。`;页面显示两条记录及成功提示。 + +| 集中工作区状态 | Runtime 写入与读回 | +|---|---| +| ![Unicode 集中工作区状态正常](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-266-workspace-ancestor/webui-centralized-status.png) | ![UI 写入后显示两条 Runtime 记录](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-266-workspace-ancestor/webui-runtime-write.png) | + +只读检查确认规范工作区哈希为 `b541b67e760240d3e9cf9985777163114eb92d9dad1ce95546b73fd0e04c4c12`,`<集中根>/workspaces/<哈希>/runtime/memories.json` 恰有两条记录,其 `MEMORY.md` 投影也包含精确新增文本。全局 Runtime 仍为空,工作区内没有新增 `.mnemon` 目录。WebUI 记录保留 JSON 与投影哈希。截图中的 “Issue 261” 会话标题和第一条记录是复用兼容性夹具时有意保留的标记。 + +本任务中未固定依赖的安装遇到了不完整的公开 RC3 传递依赖发布。因此,冒烟使用全新的消费者,将依赖固定为此前验证 profile 的精确正式 RC2 图谱,未使用 `--legacy-peer-deps`。[已有安装脚本](../issue-261-dsh-slots/harness/packed-e2e.mjs)的外部副本仅调整为在 fixed 模式也使用 `--framework-root`;已保留该[小范围安装差异](./webui-rc2-pins.patch),副本 SHA-256 为 `9de47863b19cae46122ab030675879c11b6e598bd46f31c302bd9eeb59b598d2`。没有修改 DSH 包源码或发布 manifest。 + +该冒烟验证 macOS 上有效集中工作区的操作,与 Windows 原生拒绝证据独立。浏览器流程检查了 Native CLI 版本,但未创建 Native Memory Space;真实 Native 写入、召回、遗忘和容量流程由上面的完整验证覆盖。模型响应与存储内容均为合成数据,未使用外部模型 API 或个人凭据。 + +## 兼容与数据 + +新增目录检查仅在祖先解析成功、且存在已剥离的缺失后缀时执行。没有后代的现存文件路径保留原有身份行为。有效工作区 ID、别名、Unicode 名称和尚未创建的目录后代保持不变。身份解析仍不写入任何内容。 + +Windows 上位于普通文件下的路径现在会在哈希前被拒绝。任何旧集中目录仍保留在磁盘上;没有格式变更、迁移、合并或删除。回滚恢复原有接受行为,无需转换数据。patch changeset 仅覆盖 `dsh-mnemon`;不修改 DSH 源码、公开契约、Provider 凭据或外部服务行为。已检查其他存储模式与 UI 操作说明,无需修改。 diff --git a/dsh/docs/pr-assets/issue-266-workspace-ancestor/verification.json b/dsh/docs/pr-assets/issue-266-workspace-ancestor/verification.json new file mode 100644 index 000000000..fd441e411 --- /dev/null +++ b/dsh/docs/pr-assets/issue-266-workspace-ancestor/verification.json @@ -0,0 +1,65 @@ +{ + "issue": 266, + "date": "2026-09-22", + "baselineRevision": "65c0e23ba410993e16c00c8b3adf92d59d853425", + "implementationRevision": "1750e497e58c24406c0092e4fc1549996283aab8", + "localEnvironment": { + "os": "macOS", + "architecture": "arm64", + "node": "v25.1.0", + "pnpm": "10.13.1", + "dsh": "0.1.5-rc.1", + "nativeCli": "0.2.8", + "nativeCliSha256": "f8f21151ce9777983b8d2dfb9cbea1a00b223de6063659af232d04aa57cedad0" + }, + "baselineRegression": { + "command": "pnpm exec vitest run tests/workspace-storage.spec.ts", + "testsPassed": 5, + "testsFailed": 1, + "failure": "rejects a file ancestor when native realpath reports ENOENT for its child", + "scope": "Only the first realpath call is mocked to throw ENOENT; subsequent realpath and filesystem type checks use a real temporary file." + }, + "fixedFocusedTests": { + "files": [ + "tests/workspace-storage.spec.ts", + "tests/client-platform-boundary.spec.ts", + "tests/async-subagent-memory.spec.ts" + ], + "passed": 31 + }, + "verify": { + "command": "MNEMON_NATIVE_TEST_CLI=/absolute/path/to/mnemon npx --yes pnpm@10.13.1 run verify", + "exitCode": 0, + "rootTestsPassed": 1185, + "rootTestsSkipped": 5, + "pluginTestsPassed": 382, + "pluginTestsSkipped": 2, + "deterministicBuildFiles": 39, + "nativeIntegrationEnabled": true, + "nativeCapacityWorkflowEnabled": true, + "headlessProfile": "activation, settings persistence, restart and complete Starter disable passed", + "packageValidation": "contents, public imports/types, publint and attw passed" + }, + "plugins": { + "command": "MNEMON_NATIVE_TEST_CLI=/absolute/path/to/mnemon MNEMON_PLUGIN_VERIFY_CONCURRENCY=4 npx --yes pnpm@10.13.1 run verify:plugins --skip-build", + "exitCode": 0, + "independentRepositories": 16, + "packedArtifacts": 17, + "externalConsumer": "public SDK-only composition and Client tests passed", + "headlessProfile": "packed Starter and simultaneous optional Strategies passed" + }, + "skips": [ + "5 live Flash tests require authorized credentials", + "1 live OpenViking integration requires a disposable backend", + "1 Windows Native binary smoke does not apply to macOS" + ], + "scope": "Windows observations use native node:fs calls, separately from the portable simulated-ENOENT regression. All stores are disposable; no personal data, credentials, external model calls or DSH source changes.", + "webui": { + "evidence": "webui-verification.json", + "platform": "macOS Chrome", + "dsh": "0.1.5-rc.2", + "packedArtifacts": 17, + "runtimeEntries": 2, + "status": "passed" + } +} diff --git a/dsh/docs/pr-assets/issue-266-workspace-ancestor/webui-rc2-pins.patch b/dsh/docs/pr-assets/issue-266-workspace-ancestor/webui-rc2-pins.patch new file mode 100644 index 000000000..239b1741d --- /dev/null +++ b/dsh/docs/pr-assets/issue-266-workspace-ancestor/webui-rc2-pins.patch @@ -0,0 +1,20 @@ +--- a/packed-e2e.mjs ++++ b/packed-e2e.mjs +@@ -48,7 +48,7 @@ + if (legacyPeers && !args.has('--framework-root')) assert(process.platform === 'darwin' && process.arch === 'arm64', 'Bundled baseline pins are macOS ARM64 only; supply --framework-root from a normal exact-cohort install on this platform.'); +-const frameworkPeers = legacyPeers +- ? args.has('--framework-root') +- ? await frameworkPins(resolve(args.get('--framework-root')), version) +- : JSON.parse(await readFile(new URL(`./e2e/framework-peers-${cohort}.json`, import.meta.url), 'utf8')) +- : {}; ++const frameworkPeers = args.has('--framework-root') ++ ? await frameworkPins(resolve(args.get('--framework-root')), version) ++ : legacyPeers ++ ? JSON.parse(await readFile(new URL(`./e2e/framework-peers-${cohort}.json`, import.meta.url), 'utf8')) ++ : {}; + if (legacyPeers) await writeFile(join(runRoot, 'baseline-framework-pins.json'), JSON.stringify(frameworkPeers, null, 2) + '\n'); +@@ -104,3 +104,3 @@ + dependencies: { +- ...(legacyPeers ? frameworkPeers : {}), ++ ...frameworkPeers, + '@deepseek-ai/dsh': version, '@deepseek-ai/dsh-client-ui-primitives': version, '@deepseek-ai/dsh-typert-protocol': version, diff --git a/dsh/docs/pr-assets/issue-266-workspace-ancestor/webui-verification.json b/dsh/docs/pr-assets/issue-266-workspace-ancestor/webui-verification.json new file mode 100644 index 000000000..40edd3879 --- /dev/null +++ b/dsh/docs/pr-assets/issue-266-workspace-ancestor/webui-verification.json @@ -0,0 +1,191 @@ +{ + "issue": 266, + "date": "2026-09-22", + "implementationRevision": "1750e497e58c24406c0092e4fc1549996283aab8", + "packedFromCleanWorktree": true, + "environment": { + "platform": "macOS arm64", + "browser": "Chrome", + "node": "v25.1.0", + "dshCli": "0.1.5-rc.2", + "nativeCli": "mnemon version 0.2.8", + "nativeCliSha256": "f8f21151ce9777983b8d2dfb9cbea1a00b223de6063659af232d04aa57cedad0" + }, + "setup": { + "baseHarness": "docs/pr-assets/issue-261-dsh-slots/harness/packed-e2e.mjs", + "externalCopySha256": "9de47863b19cae46122ab030675879c11b6e598bd46f31c302bd9eeb59b598d2", + "delta": "webui-rc2-pins.patch", + "frameworkRootUsed": true, + "reason": "The task encountered an incomplete public rc3 transitive release during unpinned installation. The external harness copy accepts --framework-root in fixed mode to reuse exact published RC2 pins from a previously verified profile.", + "reusedExistingInstall": false, + "legacyPeerDeps": false, + "dshPackageCount": 234, + "allDshPackageVersions": [ + "0.1.5-rc.2" + ], + "publicContractByteChecks": [ + { + "package": "@deepseek-ai/dsh-client-ui-slots", + "file": "lib/index.js", + "sha256": "5433ef2cb74699a59000052c4b1899808c83544f45ba6176f297efc68414c588" + }, + { + "package": "@deepseek-ai/dsh-client-ui-chat", + "file": "lib/client.js", + "sha256": "6527556d25c7b2a27f2e3bfb96cc2ae3d9244f1213147e245b17684ad8d9e396" + }, + { + "package": "@deepseek-ai/dsh-client-ui-renderer", + "file": "lib/client.js", + "sha256": "859d0a187e436d682c71d18f0266d588f923d7e1628157ad73000f99a377d151" + }, + { + "package": "@deepseek-ai/dsh-client-ui-conversation", + "file": "lib/client.js", + "sha256": "81314dfd95864f2522f8edb812e3f8e08b04a8ef2141913e6e8cbdaae1ffc37f" + } + ], + "isolatedPeerResolutions": 51, + "allPeerResolutionsInsideConsumer": true, + "testClientPackages": [] + }, + "composition": { + "packedArtifactCount": 17, + "enabledOptionalStrategies": [ + "mnemon-strategy-scoped", + "mnemon-strategy-light-context", + "mnemon-strategy-auto-capture" + ], + "artifacts": [ + { + "name": "dsh-mnemon", + "version": "0.5.12", + "sha256": "3d5259f8a995a662ecf3e015282f42490a4be13e006e4672715aa05a8f6c353b" + }, + { + "name": "dsh-mnemon-provider-byterover", + "version": "0.5.4", + "sha256": "94677bb5a27261dd2051f67c9ae5bc41c17ea86a4ea95cbd0baee1728203633f" + }, + { + "name": "dsh-mnemon-provider-hindsight", + "version": "0.5.4", + "sha256": "bf527bc83d51bd7b2e510ed3be59665ae6b3ddbd4e1c3beee16c32235fa0afbb" + }, + { + "name": "dsh-mnemon-provider-holographic", + "version": "0.5.4", + "sha256": "67f9cc593a7bcf33001e0817a4932e23cf9f0927e90b6bc2b2ccd5ef49b91259" + }, + { + "name": "dsh-mnemon-provider-honcho", + "version": "0.5.4", + "sha256": "c102952aa1b9fdd953b25068663635a9ffad606bb39c68b56eafa538c8fab72d" + }, + { + "name": "dsh-mnemon-provider-mem0", + "version": "0.5.4", + "sha256": "42cd225bb376d387d8e7f5503d64b6a00ddae4f5aea26b495cb9d5aa44dab3b0" + }, + { + "name": "dsh-mnemon-provider-mnemon-native", + "version": "0.5.5", + "sha256": "cb1532ec04e1bc2550456340b453717686c2bc399e742e61be529bdae5e6f812" + }, + { + "name": "dsh-mnemon-provider-openviking", + "version": "0.5.5", + "sha256": "da3505e93462a7860d331e830930a04d4e2406be5b540333f340f9351346920f" + }, + { + "name": "dsh-mnemon-provider-retaindb", + "version": "0.5.4", + "sha256": "a7b5f1efb4e68df2d7ccc7e6078cd8c3229c82cf14f0162a196c975cbe19727f" + }, + { + "name": "dsh-mnemon-provider-supermemory", + "version": "0.5.4", + "sha256": "d04b900f7f9e9359ba53b349bdbc4550fc19b350771e61df3aa451e8f67f9afd" + }, + { + "name": "dsh-mnemon-source-documents", + "version": "0.5.6", + "sha256": "94ff6441881b73f490ed02fd038234232d119be311703a383ed45a20c8de7126" + }, + { + "name": "dsh-mnemon-source-memory-spaces", + "version": "0.5.8", + "sha256": "18e3d346f5289aeac3944c1be222e1520ae6adf8235fd4da0a9ee900a0c04568" + }, + { + "name": "dsh-mnemon-source-runtime", + "version": "0.5.8", + "sha256": "de1e147ab3437d8c9f377c138a3e4a06d0930b7b6c92986db4b023d3df575d4f" + }, + { + "name": "dsh-mnemon-strategy-auto-capture", + "version": "0.5.4", + "sha256": "3816601abd83f0c5c679f3e4c9ae96c8f697e8e15f6546e975a96fd7f86b8c02" + }, + { + "name": "dsh-mnemon-strategy-default-three-tier", + "version": "0.5.5", + "sha256": "52af5caf635f2cf2b165feff1ff3d35d241e6d073771b7b92b3ea37b36ff589f" + }, + { + "name": "dsh-mnemon-strategy-light-context", + "version": "0.5.4", + "sha256": "62947c3ff777fbb8d42724acf2a9c651d2b2302b387bb8579c59cd79f82a9e0d" + }, + { + "name": "dsh-mnemon-strategy-scoped", + "version": "0.5.4", + "sha256": "f3f945add93d2d5f8f48f0e70f5de9360814499a552218af7fa93f574dd3457a" + } + ] + }, + "workspace": { + "displayName": "项目 with spaces", + "storageScope": "workspaces", + "centralRoot": "/mnemon-data", + "inputPath": "/workspace/项目 with spaces", + "canonicalization": "macOS resolves the temporary /tmp alias to /private/tmp before hashing; both forms identify the same workspace", + "storageId": "b541b67e760240d3e9cf9985777163114eb92d9dad1ce95546b73fd0e04c4c12", + "root": "/workspaces/b541b67e760240d3e9cf9985777163114eb92d9dad1ce95546b73fd0e04c4c12", + "noWorkspaceLocalMnemon": true, + "globalRuntimeEntryCount": 0 + }, + "actions": { + "conversationPrompt": "compatibility-261", + "model": "Local deterministic compatibility fixture; no external model API", + "realHostTools": [ + "mnemon_runtime_memory (add)", + "mnemon_status" + ], + "initialStatus": "healthy; 1 Runtime entry", + "uiAction": "Runtime -> 添加记忆", + "exactAddedContent": "Issue 266 WebUI: 中文与空格工作区使用稳定集中存储,Runtime 写入后可读回。", + "addedContentSha256": "1d93b3626ab20a6ff65703547c559888017309a1ecb56043e8a014645597e64e", + "finalRuntimeEntryCount": 2, + "memoriesJsonSha256": "a27b0da65535b224f77e641fab69c710bd7823921e249591703f0748e5663232", + "memoryMarkdownSha256": "966a59fbb1d25dec88edbb9d5d0bc9bb24a78fadc5acb9494c3e120c51b13ad2", + "exactContentPersistedInJsonAndMarkdown": true, + "fixtureLabelNote": "The existing compatibility harness intentionally retains Issue 261 in its preset/session title and first synthetic Runtime entry." + }, + "screenshots": [ + { + "file": "webui-centralized-status.png", + "sha256": "7bb77b1df3e14001edbf4f3fbe12f51a7966d1119fe6372c241c799a4a4ae58a" + }, + { + "file": "webui-runtime-write.png", + "sha256": "b90ffaf45572ee5f9355288ef63f47148cddff239112ab97085604aa191a87ef" + } + ], + "limits": [ + "This is a macOS Chrome compatibility smoke for valid centralized workspace paths. Actual Windows rejection evidence is the separate Actions before/after run.", + "The Native executable version was checked; this browser smoke did not create a Native Memory Space. Real Native write/recall/forget and capacity checks passed in full verification.", + "All three optional Strategies were enabled together; this smoke did not exercise every Strategy behavior.", + "All data are disposable synthetic fixture content. No personal data or personal credentials, DSH source overlays or external model API calls were used." + ] +} diff --git a/dsh/docs/pr-assets/issue-266-workspace-ancestor/windows-results.json b/dsh/docs/pr-assets/issue-266-workspace-ancestor/windows-results.json new file mode 100644 index 000000000..80b8ee7c1 --- /dev/null +++ b/dsh/docs/pr-assets/issue-266-workspace-ancestor/windows-results.json @@ -0,0 +1,502 @@ +{ + "issue": 266, + "date": "2026-09-22", + "runs": [ + { + "phase": "baseline", + "productRevision": "65c0e23ba410993e16c00c8b3adf92d59d853425", + "validationRevision": "22690919434d18d8537ef74e08a4d200282a199e", + "runUrl": "https://github.com/omdsh-dev/dsh-mnemon/actions/runs/35715519552", + "source": { + "gitBlobSha256": "f78cffb1d777b9da384c4184e1514636392600c055b424a01746e656938d80c1", + "windowsCheckoutSha256": "54a2c24f6fc52f8e75faaf775fcddbca8767552647c885eda906204c47f66f45", + "lineEndings": "Git blob LF; Windows checkout CRLF; observed hashes match the respective checkout bytes" + }, + "observations": [ + { + "artifact": "issue-266-baseline-node-22.19.0", + "filesystem": { + "phase": "baseline", + "platform": "win32", + "osRelease": "10.0.26100", + "node": "v22.19.0", + "libuv": "1.51.0", + "sourceSha256": "54a2c24f6fc52f8e75faaf775fcddbca8767552647c885eda906204c47f66f45", + "native": [ + { + "name": "file-child", + "threw": true, + "code": "ENOENT", + "errno": -4058 + }, + { + "name": "file-ancestor", + "returned": "\\file" + } + ], + "cases": [ + { + "name": "empty", + "shouldThrow": true, + "threw": true, + "code": null, + "errno": null, + "passed": true + }, + { + "name": "relative", + "shouldThrow": true, + "threw": true, + "code": null, + "errno": null, + "passed": true + }, + { + "name": "nul", + "shouldThrow": true, + "threw": true, + "code": null, + "errno": null, + "passed": true + }, + { + "name": "file-child", + "shouldThrow": true, + "returned": "19654417a7b36eb7879b868eb2993b3c5f131cfbfb84b9dac5e277e37ca10a57", + "passed": false + }, + { + "name": "file-deeper", + "shouldThrow": true, + "returned": "a5a375613fc56e2be99b6cf071a62b294bbf8ed128bc9cc98ad18d5aeb9b63f3", + "passed": false + }, + { + "name": "unicode-missing", + "shouldThrow": false, + "returned": "b1acc5e3f5b6dc73f75263ede731409c9aa10105ee2e946b42e0dcebe5b265eb", + "passed": true + }, + { + "name": "existing-file", + "shouldThrow": false, + "returned": "6e847e62f6a759aa29c04728144a08825e217b5328123903c264b4f875d4ccbd", + "passed": true + }, + { + "name": "missing-deeper", + "shouldThrow": false, + "returned": "8952e4a7303cbb4ebc71cbf98dc2d20c1bdb7f8ddb110e791c7d714a2ca73d95", + "passed": true + }, + { + "name": "existing-root", + "shouldThrow": false, + "returned": "5dc50dd6409e963476fb99d7b6f6d90f6d79878a95796d99b3476665db9767f5", + "passed": true + }, + { + "name": "workspace", + "shouldThrow": false, + "returned": "74be816a5cfe75104ccae7bf6bbd3c5b25439deafb181cbfee7e5405d4469557", + "passed": true + }, + { + "name": "junction", + "shouldThrow": false, + "returned": "74be816a5cfe75104ccae7bf6bbd3c5b25439deafb181cbfee7e5405d4469557", + "passed": true + }, + { + "name": "junction-missing", + "shouldThrow": false, + "returned": "f424b2120159e38722e39b636d4cea92b397ce01000e5066a4d3b75d83eec8b6", + "passed": true + } + ], + "contractFailures": [ + "file-child", + "file-deeper" + ], + "junctionAliasesMatch": true, + "originalFileRetained": true + }, + "tests": { + "total": 29, + "passed": 26, + "failed": 3, + "failures": [ + "asynchronous child memory authority keeps the delegated workspace scope when a child header is changed", + "browser bundle platform boundary keeps all default and independent Client imports inside browser code or data-only contracts", + "built-in centralized workspace layout rejects malformed identities and non-directory ancestors while allowing Unicode names" + ] + } + }, + { + "artifact": "issue-266-baseline-node-24", + "filesystem": { + "phase": "baseline", + "platform": "win32", + "osRelease": "10.0.26100", + "node": "v24.20.0", + "libuv": "1.52.1", + "sourceSha256": "54a2c24f6fc52f8e75faaf775fcddbca8767552647c885eda906204c47f66f45", + "native": [ + { + "name": "file-child", + "threw": true, + "code": "ENOENT", + "errno": -4058 + }, + { + "name": "file-ancestor", + "returned": "\\file" + } + ], + "cases": [ + { + "name": "empty", + "shouldThrow": true, + "threw": true, + "code": null, + "errno": null, + "passed": true + }, + { + "name": "relative", + "shouldThrow": true, + "threw": true, + "code": null, + "errno": null, + "passed": true + }, + { + "name": "nul", + "shouldThrow": true, + "threw": true, + "code": null, + "errno": null, + "passed": true + }, + { + "name": "file-child", + "shouldThrow": true, + "returned": "e9904367bb62b5a67601ae37596db3804a6869dd46c4c6083a67ce0c4f1b09bf", + "passed": false + }, + { + "name": "file-deeper", + "shouldThrow": true, + "returned": "8f208910d289032af8a98a868287484f8564a98cb222a1b5488b6cca9434f956", + "passed": false + }, + { + "name": "unicode-missing", + "shouldThrow": false, + "returned": "c311e117b2c88b3add101ed4f08b632093d8f2aa40ac504de35db7dfcb926162", + "passed": true + }, + { + "name": "existing-file", + "shouldThrow": false, + "returned": "4d0cd14d04fbb9da9a0b4d2b5f6a22aee4e73f061d2ed812e6f10b1292f5a161", + "passed": true + }, + { + "name": "missing-deeper", + "shouldThrow": false, + "returned": "88a327e26b0fb53421ec911e6c8b2a33f1a27d1f023a8eada977957e9c689ca5", + "passed": true + }, + { + "name": "existing-root", + "shouldThrow": false, + "returned": "9c63dcbe7f1fa5f9e0d9062157786f5b69a6acbfb21bc0f6d4e8bad2a9329d30", + "passed": true + }, + { + "name": "workspace", + "shouldThrow": false, + "returned": "9389e09d93ba6d2a80cafe42c581b7bb412f498e5657c9970bf5c2d02f11ee89", + "passed": true + }, + { + "name": "junction", + "shouldThrow": false, + "returned": "9389e09d93ba6d2a80cafe42c581b7bb412f498e5657c9970bf5c2d02f11ee89", + "passed": true + }, + { + "name": "junction-missing", + "shouldThrow": false, + "returned": "2062623835ab24b20bf526e8ac35ce2170ba0708dfff7da60c04e0fee79b9f3e", + "passed": true + } + ], + "contractFailures": [ + "file-child", + "file-deeper" + ], + "junctionAliasesMatch": true, + "originalFileRetained": true + }, + "tests": { + "total": 29, + "passed": 26, + "failed": 3, + "failures": [ + "asynchronous child memory authority keeps the delegated workspace scope when a child header is changed", + "browser bundle platform boundary keeps all default and independent Client imports inside browser code or data-only contracts", + "built-in centralized workspace layout rejects malformed identities and non-directory ancestors while allowing Unicode names" + ] + } + } + ] + }, + { + "phase": "fixed", + "productRevision": "1750e497e58c24406c0092e4fc1549996283aab8", + "validationRevision": "fde19025ff6452fcfd0643f377593b83279ca232", + "runUrl": "https://github.com/omdsh-dev/dsh-mnemon/actions/runs/35715781789", + "source": { + "gitBlobSha256": "526da7a29d0ca6e5e0f79f877ddf4b4466644fe0a1599fbaa4f5018d5c637344", + "windowsCheckoutSha256": "7e4dae828209fe2a7d2b1ceea701c74ea6295ba34bce4733480a5d97e3c5e8f7", + "lineEndings": "Git blob LF; Windows checkout CRLF; observed hashes match the respective checkout bytes" + }, + "observations": [ + { + "artifact": "issue-266-fixed-node-22.19.0", + "filesystem": { + "phase": "fixed", + "platform": "win32", + "osRelease": "10.0.26100", + "node": "v22.19.0", + "libuv": "1.51.0", + "sourceSha256": "7e4dae828209fe2a7d2b1ceea701c74ea6295ba34bce4733480a5d97e3c5e8f7", + "native": [ + { + "name": "file-child", + "threw": true, + "code": "ENOENT", + "errno": -4058 + }, + { + "name": "file-ancestor", + "returned": "\\file" + } + ], + "cases": [ + { + "name": "empty", + "shouldThrow": true, + "threw": true, + "code": null, + "errno": null, + "passed": true + }, + { + "name": "relative", + "shouldThrow": true, + "threw": true, + "code": null, + "errno": null, + "passed": true + }, + { + "name": "nul", + "shouldThrow": true, + "threw": true, + "code": null, + "errno": null, + "passed": true + }, + { + "name": "file-child", + "shouldThrow": true, + "threw": true, + "code": "ENOTDIR", + "errno": null, + "passed": true + }, + { + "name": "file-deeper", + "shouldThrow": true, + "threw": true, + "code": "ENOTDIR", + "errno": null, + "passed": true + }, + { + "name": "unicode-missing", + "shouldThrow": false, + "returned": "c4a69a9709588490d5c35a333362c7968c7c57f48f418629c12ee597a382f0a8", + "passed": true + }, + { + "name": "existing-file", + "shouldThrow": false, + "returned": "8c2294e4a64dd689fab6e293fb9c4b46fe62792e0ed83dac2edd1d85853c57fe", + "passed": true + }, + { + "name": "missing-deeper", + "shouldThrow": false, + "returned": "e5a9913f8124c01bb5301ac2fea2b27a637daa1a38477ca0a56f262f1c34ff6f", + "passed": true + }, + { + "name": "existing-root", + "shouldThrow": false, + "returned": "33013274708740d724043c8602f9a317efba2c2ffcd5e0771a5fc8d10d1d4bc6", + "passed": true + }, + { + "name": "workspace", + "shouldThrow": false, + "returned": "80a4cea84a53d989223ee3be8793a57960bdc3da838c4b0ebc2e6506f9e37da0", + "passed": true + }, + { + "name": "junction", + "shouldThrow": false, + "returned": "80a4cea84a53d989223ee3be8793a57960bdc3da838c4b0ebc2e6506f9e37da0", + "passed": true + }, + { + "name": "junction-missing", + "shouldThrow": false, + "returned": "455cbdf080bf0b7346136b2b05fb488a7cdc7991e50af8dd0fb280a65b48ff3f", + "passed": true + } + ], + "contractFailures": [], + "junctionAliasesMatch": true, + "originalFileRetained": true + }, + "tests": { + "total": 31, + "passed": 31, + "failed": 0, + "failures": [] + } + }, + { + "artifact": "issue-266-fixed-node-24", + "filesystem": { + "phase": "fixed", + "platform": "win32", + "osRelease": "10.0.26100", + "node": "v24.20.0", + "libuv": "1.52.1", + "sourceSha256": "7e4dae828209fe2a7d2b1ceea701c74ea6295ba34bce4733480a5d97e3c5e8f7", + "native": [ + { + "name": "file-child", + "threw": true, + "code": "ENOENT", + "errno": -4058 + }, + { + "name": "file-ancestor", + "returned": "\\file" + } + ], + "cases": [ + { + "name": "empty", + "shouldThrow": true, + "threw": true, + "code": null, + "errno": null, + "passed": true + }, + { + "name": "relative", + "shouldThrow": true, + "threw": true, + "code": null, + "errno": null, + "passed": true + }, + { + "name": "nul", + "shouldThrow": true, + "threw": true, + "code": null, + "errno": null, + "passed": true + }, + { + "name": "file-child", + "shouldThrow": true, + "threw": true, + "code": "ENOTDIR", + "errno": null, + "passed": true + }, + { + "name": "file-deeper", + "shouldThrow": true, + "threw": true, + "code": "ENOTDIR", + "errno": null, + "passed": true + }, + { + "name": "unicode-missing", + "shouldThrow": false, + "returned": "3ff949f0692911975b4048bb2b9bea5cd6c9aadf3f70375f364d2b1f76fc7136", + "passed": true + }, + { + "name": "existing-file", + "shouldThrow": false, + "returned": "c0e9a03965e3fd986b5ec5bffcfaea40cde0e79157e645c2dc92073af5684147", + "passed": true + }, + { + "name": "missing-deeper", + "shouldThrow": false, + "returned": "5991542749057cc10fdc8a782bcdfa584399f9b10a4f0a50bb641c4a3dc19a6a", + "passed": true + }, + { + "name": "existing-root", + "shouldThrow": false, + "returned": "9f8e5950e61830b962a383948facb6e244fdf8e0d7acb82236ddc553032aa23a", + "passed": true + }, + { + "name": "workspace", + "shouldThrow": false, + "returned": "76c26bb73693098a1490ac55f9c14d5c293b686445f437d232f37fa85098a5c8", + "passed": true + }, + { + "name": "junction", + "shouldThrow": false, + "returned": "76c26bb73693098a1490ac55f9c14d5c293b686445f437d232f37fa85098a5c8", + "passed": true + }, + { + "name": "junction-missing", + "shouldThrow": false, + "returned": "491a008aba4de8d6f2f2248c841f69084bfdac77b03953dc4f3153c2e2db372c", + "passed": true + } + ], + "contractFailures": [], + "junctionAliasesMatch": true, + "originalFileRetained": true + }, + "tests": { + "total": 31, + "passed": 31, + "failed": 0, + "failures": [] + } + } + ] + } + ] +} diff --git a/dsh/docs/pr-assets/issue-267-settings-migration/README.md b/dsh/docs/pr-assets/issue-267-settings-migration/README.md new file mode 100644 index 000000000..e4551bf76 --- /dev/null +++ b/dsh/docs/pr-assets/issue-267-settings-migration/README.md @@ -0,0 +1,138 @@ +# DSH Settings compatibility and recovery — issue #267 + +[简体中文](./README.zh-CN.md) + +## Baseline reproduction + +On 2026-09-22, unmodified main `65c0e23ba410993e16c00c8b3adf92d59d853425` reproduced the activation failure with published DSH `0.1.7-alpha.1` in a disposable Web profile. The Mnemon root reported: + +```text +mnemon (dsh-mnemon): TypeError: ctx.settings.register is not a function +``` + +The plugin page showed the root in an error state and dependent Sources and Strategies waiting for their dependency. The local activation record identifies this result as `reproduced`; selected installed DSH files were compared with integrity-verified public npm tarballs. + +![Baseline: Mnemon activation fails and dependent plugins wait](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/before-alpha7-activation.png) + +See the [packed-artifact harness](./harness/HARNESS.md) for preparation and launch instructions. Baseline and fixed artifacts use separate disposable profiles. Only the baseline requires `--legacy-peer-deps` to get past its outdated peer range and expose the runtime failure; the fixed consumer uses normal peer resolution. + +## Implementation and public contracts + +DSH `0.1.7-alpha.1` replaces dynamic Settings registrations with forms derived from each plugin's static `Config`. The implementation uses published `SettingsForms.configure/describe/mutate`, `ConfigEditor.edit`, the Cordis `internal/config` waterfall, and the Loader's `loader/volatile-update` event. Official DSH packages, manifests and source are unchanged. No DSH source checkout, workspace alias or replacement Settings service is used by the artifact harness. + +- [Live Config](../../../src/host/live-config.ts) uses real volatile references from the published DeepSeek Schemastery/Cosmokit packages. It preserves the object schema for native form discovery and performs pure cross-field validation. `remoteAccess` remains an ordinary field with the host's normal remount behavior. +- A private static ESM bridge imports the unchanged published fork through the npm alias `schemastery-live`. Its narrow declarations prevent the fork's new global generic from merging into legacy schema builders. The canonical development dependency stays at `3.18.2` for the unchanged RC.1 type cohort, including an upstream declaration that omits this runtime dependency. The alias does not appear in Mnemon's emitted public types; consumer Hosts keep their own framework dependency versions. No official package, TypeScript source path or import order is patched. +- The [settings adapter](../../../src/host/settings-service.ts) retains Mnemon's client namespaces while binding reads, validation and writes to the owning Entry/Fiber. Existing hosts with `settings.register` retain their original service. Profile writes go through DSH's locked editor; the owner-scoped preflight validates the candidate runtime before persistence, and committed volatile updates refresh the runtime. +- Core, UI and View settings share one native revision. A bounded retry is allowed only when that namespace's unredacted effective, inherited and explicit values still match the observed snapshot. Same-namespace conflicts, missing snapshots, read-only state, disposal and Entry replacement are rejected. Remote descriptors remain redacted. +- [Plugin management](../../../src/host/plugin-management.ts) reapplies selected Entry state after whole-profile reconciliation, validates the resulting composition, and compensates failed transactions. +- [Client icon aliases](../../../src/client/ui-icons.ts) support both the old size-suffixed exports and alpha7's weight-suffixed exports. The settings footer now describes DSH-managed live saving in both languages without hardcoding the removed settings file. + +- [Session messages](../../../src/host/lifecycle.ts) use the producer-owned `dsh-mnemon` source kind required by alpha7's V4 codec. Reading and deduplication still recognize both historical wrappers and the official `plugin:dsh-mnemon` migration. A real WebUI turn exposed this additional compatibility failure after activation was repaired. + +## Retained settings and existing profile choices + +Recovery reads `settings.yaml.imported` without modifying its bytes and writes only through `ConfigEditor.edit`. Its [pure planner](../../../src/host/legacy-settings-import.ts) maps the canonical root's retained sections as follows: + +| Retained section | Profile Config destination | +| --- | --- | +| `mnemon` | Root fields missing from the current explicit override | +| `mnemon-ui` | `conversationInteraction` | +| Exact `mnemon-view[-hash]` | `memoryView` | +| Exact `mnemon-plugins[-hash]` | Source activation in `memoryView.entries`, only for confirmed Source Entries | + +Current explicit root/UI choices win. Explicit `memoryView` fields, including an empty `entries` reset, are preserved. Existing Strategy rows take precedence over older saved activation/configuration; Source configuration remains on its native Entry. Local patch IDs are matched to their unambiguous full Loader IDs. Unrelated rows are not replaced. The plan is recomputed inside the editor callback so an intervening explicit edit wins, and `legacySettingsImported` prevents later resets from resurrecting old preferences. + +Native `!!js` expressions are retained as expression data, never evaluated or converted into literal strings by recovery. An expression-controlled activation or Strategy configuration causes that Entry's old View overlay to be skipped. Source configuration expressions and unrelated expression rows stay on their native rows. Expressions in relevant legacy backup sections, malformed data and unsupported YAML are rejected without completing recovery or changing the backup. + +## Migration and rollback limits + +- If the original `settings.yaml` exists at startup, DSH performs its own asynchronous rename/import. Mnemon defers supplemental recovery until the next cold Host start; a page refresh or plugin remount does not clear this gate. +- Recovery accepts only the canonical root Entry `mnemon` and the exact owning profile's historical namespace suffix. It does not guess custom-root ownership, foreign profile hashes or an unsuffixed fallback. Ambiguous or rejected input remains available in the retained backup for manual review. +- Back up the DSH profile configuration, legacy settings and relevant Mnemon data before upgrading. New profile settings are not mirrored back to `settings.yaml`. A downgrade requires the corresponding configuration backup and manual reconciliation of later changes; there is no automatic reverse export. +- This changes preference storage and recovery, not the Runtime, Documents or Memory Spaces data formats. + +## Verification scope + +Focused regressions cover [schema/reference behavior](../../../tests/live-config.spec.ts), [owner isolation and redaction](../../../tests/profile-settings.spec.ts), [revision conflicts and retired owners](../../../tests/profile-settings-revisions.spec.ts), [pure migration planning](../../../tests/legacy-settings-import.spec.ts), [retained-file recovery and expressions](../../../tests/profile-settings-import.spec.ts), and [both client icon generations](https://github.com/omdsh-dev/dsh-mnemon/blob/65a14ddd2d83bd0850948dc915bc410552b545d9/tests/client-primitives-compat.spec.tsx). + +The artifact harness uses isolated test data and a deterministic model endpoint bound to `127.0.0.1`; it requires no external model API key and calls no external model provider. Bootstrap URLs, `server.json`, raw `web.log` and model request logs remain outside the repository. Public evidence must exclude credentials and private data. + +## Measured settings and migration results + +Commit `407eac75ee46e677f694e6c318ca9380357fae27` passed the following browser operations with 17 packed Mnemon artifacts. The later Session source-format follow-up is tracked separately; these runs verify the settings implementation. Machine-readable hashes and results are in [settings-verification.json](./settings-verification.json). + +| Published DSH | Uniform DSH packages | Official installed files checked | Actual WebUI operations | +| --- | ---: | ---: | --- | +| `0.1.5-rc.2` | 234 | 8 | Workbench; disable Light while Auto Capture/Scoped remain enabled; Core/UI save; reload | +| `0.1.6-alpha.2` | 251 | 8 | Same operations on the alpha Settings service | +| `0.1.7-alpha.1` | 267 | 10 | Root and all Sources/Strategies activate; Core/UI consecutive saves; separate UI save; reload and full Host restart | + +On alpha7, the interval remained `310000`, Light stayed off, Auto Capture/Scoped stayed on, and both UI toggles stayed off after restart. On RC.2 and alpha.2 the saved intervals were `320000` and `330000`, with Light/turn bar off and the other toggles on. No false revision conflict occurred. + +The retained-backup fixture restored recall limit `7`, turn bar on/save action off, and Auto Capture on. Current Light activation/configuration (`1200`) and Documents activation won over legacy values; foreign-profile Scoped preferences were ignored. After a full Host restart, the profile patch, root Config, all 21 non-root rows and original backup hashes were unchanged. Before that restart, 20 initial non-root rows matched the harness initialization semantically; the extra row was the browser's welcome-notice acknowledgement. + +The real Mnemon CLI `0.2.8` also answered the WebUI status operation and created/enabled a native memory space with normal storage. The optional default Ollama embedding endpoint was not running; no external embedding or model key was used. + +![Fixed alpha7 root and Source activation](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/after-alpha7-activation.png) +![Fixed alpha7 memory workbench](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/after-alpha7-status.png) +![Core/UI settings saved without a false revision conflict](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/after-alpha7-settings-saved.png) +![Strategy choices survive a complete Host restart](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/after-alpha7-host-restart.png) +![Retained UI preferences after recovery](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/after-alpha7-legacy-settings.png) +![Retained preferences survive restart](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/after-alpha7-legacy-restart.png) +![Native CLI space created and enabled through WebUI](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/after-alpha7-native-cli.png) +![RC.2 settings save](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/after-rc2-settings-saved.png) +![Alpha.2 settings save](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/after-alpha2-settings-saved.png) + +## Session fix: packed verification before type isolation + +The Session fix at `dc10b5b6a2b39bd3093823741e286592dc640cd6` was packed from a clean checkout and installed into three fresh consumers using normal npm peer resolution. All three used the same 17 Mnemon archives; the root archive SHA-256 is `b2204a284db063a128dce845cd115ecac33b5c91e91071d050d2a9d5daf1db16`. [final-verification.json](./final-verification.json) records this stage's artifact hashes, public-package checks, persisted tool receipts and test results. The final type-isolated artifacts are verified below. + +Actual WebUI turns on `0.1.5-rc.2`, `0.1.6-alpha.2` and `0.1.7-alpha.1` called `mnemon_runtime_memory` with `action: add`, then `mnemon_status`. Each turn wrote exactly one fixture memory and returned a healthy status with the native CLI found and writes enabled. Each compressed Session decoded completely and retained two messages with source kind `dsh-mnemon`. RC.2 and alpha.2 wrote Session V3; alpha.7 wrote V4. The fixture prompt and title retain `compatibility-261` / `Issue 261 compatibility` because the deterministic model fixture is reused; the artifacts, profiles and screenshots in this directory belong to Issue 267. + +Before the Session fix, the real alpha.7 turn failed with `format v4 message requires a producer-owned source kind`. The regression uses the actual published alpha.7 `encodeEvent` and `restoreReleasedV4Artifact` contracts, including rejection of the old wrapper. The completed lifecycle suite passed 56 tests. + +On these alpha.7 artifacts, the browser selected Builtin display mode, disabled Light, saved interval `340000`, disabled the turn bar and retained the save action. After a complete Host process restart, the browser confirmed all settings, the restored conversation and its one Runtime memory. Auto Capture and Scoped remained enabled through their native Entries without unwanted View overrides. + +The separate original-file fixture also passed both migration stages: DSH first renamed/imported `settings.yaml`, with Mnemon's supplemental marker and View absent; one full Host restart then restored the expected UI/View preferences and wrote the marker. The retained backup remained byte-for-byte unchanged. That settings-only run used `407eac75`; the final Session commit does not change its settings implementation. + +![Final alpha.7 turn completes both Mnemon tool calls](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/final-alpha7-session-tools.png) +![The Runtime tool writes one visible fixture memory](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/final-alpha7-runtime-write.png) +![RC.2 completes the same real tool calls](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/final-rc2-session-tools.png) +![Alpha.2 completes the same real tool calls](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/final-alpha2-session-tools.png) +![Final alpha.7 settings after a complete Host restart](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/final-alpha7-settings-restart.png) +![Builtin workbench retains the Runtime memory after restart](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/final-alpha7-builtin-restart.png) +![Original-file migration completes on the next Host start](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/after-alpha7-native-import-restart.png) + +## Final type-isolated artifacts and WebUI verification + +Clean CI exposed a global generic declaration collision between legacy Schemastery and the new live fork. The private bridge described above isolates the runtime import and preserves the existing main lockfile package/snapshot values. Implementation commit `d57e14054a4c523097b382ff36ca6bd3a993ff03` was packed from a clean checkout and installed with normal peers into three new consumers. The final root archive SHA-256 is `552eb9c23846e23e958100974888351a74666c471d614d2f098f2426d5a81cb3`; all 17 artifact hashes and installation checks are recorded in [type-isolation-verification.json](./type-isolation-verification.json). + +All three public DSH cohorts completed fresh WebUI turns with real Runtime writes and native CLI status calls. Each installed `schemastery-live` package matched all eight files of the unchanged official `@deepseek-ai/schemastery@3.18.3` archive. The DSH package counts and selected official-file checks remained 234/8, 251/8 and 267/10 for RC.2, alpha.2 and alpha.7 respectively. + +On alpha.7, the browser prepared Core/UI edits, switched Light off through the separate immediate plugin write, and saved the remaining draft without a false conflict. After a complete Host restart, Builtin mode, interval `350000`, turn bar off/save action on and Light off/Auto Capture and Scoped on all persisted. The restored conversation completed loading, and its Builtin workbench displayed the single Runtime memory. The profile and compressed Session checks are retained alongside the browser evidence. + +[Source verification on Node 22.19](https://github.com/omdsh-dev/dsh-mnemon/actions/runs/35733033143/job/106763140386) and [packed plugin verification on Node 24](https://github.com/omdsh-dev/dsh-mnemon/actions/runs/35733033143/job/106763140029) passed for this implementation commit. + +![Type-isolated alpha.7 artifacts complete both tools](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/v4-alpha7-session-tools.png) +![Type-isolated RC.2 artifacts complete both tools](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/v4-rc2-session-tools.png) +![Type-isolated alpha.2 artifacts complete both tools](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/v4-alpha2-session-tools.png) +![Builtin setting persists after full Host restart](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/v4-alpha7-settings-restart.png) +![Interval and composed Strategy choices persist](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/v4-alpha7-strategies-restart.png) +![Final Builtin Runtime memory survives full Host restart](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/v4-alpha7-builtin-restart.png) + +## Complete local checks and limits + +The following commands passed, with the plugin check run after `verify` completed: + +```sh +MNEMON_NATIVE_TEST_CLI=/path/to/mnemon \ +MNEMON_DSH_V4_CONTRACT_ROOT=/path/to/alpha7-consumer/node_modules \ + npx --yes --package=node@22.19.0 --package=pnpm@10.13.1 pnpm run verify +npx --yes --package=node@24.20.0 --package=pnpm@10.13.1 pnpm run verify:plugins --skip-build +``` + +- Root tests: 1,342 passed, 5 skipped; plugin tests: 382 passed, 2 skipped. The skipped cases require an external Flash model, Windows, or a live OpenViking service. The real native CLI and published V4 codec cases were enabled. +- Documentation, type checks, deterministic build, package-content budget, public entry imports, declaration dependencies, `publint` and `attw` passed. The final root package contains 49 files and 1,370,296 unpacked bytes; 12 Node-compatible entries, 28 public type dependencies and the repair CLI help were verified. +- The Headless check passed with 39 tools and 8 representative Mnemon tools. Independent-consumer verification passed for all 16 plugin repositories and 17 tarballs, including an SDK-only external consumer, all three optional Strategies together, and Starter-only activation. +- The browser fixtures ran on macOS with Node `25.1.0`; final source and packed checks ran on Node `22.19.0` and `24.20.0`. This is Mnemon compatibility evidence, not an end-to-end check of every DSH feature. The minimal fixture omits terminal/deliverables/workspace-change services; their waiting entries, general permission-preset request failure and alpha.2 terminal retry button do not represent Mnemon failures. External model quality, external Providers and embeddings are outside this run. +- Repeated loopback fixtures produced a browser HTTP 431 during the final restart check. A synthetic 14 KB cookie reproduced a short request succeeding and the longer plugin-resource request failing. Navigating the same running listener through `localhost` completed the browser check without changing profile/data, framework files, server limits or existing browser cookies. See the [harness note](./harness/HARNESS.md#repeated-loopback-runs) for repeating this setup safely. diff --git a/dsh/docs/pr-assets/issue-267-settings-migration/README.zh-CN.md b/dsh/docs/pr-assets/issue-267-settings-migration/README.zh-CN.md new file mode 100644 index 000000000..01ac184aa --- /dev/null +++ b/dsh/docs/pr-assets/issue-267-settings-migration/README.zh-CN.md @@ -0,0 +1,138 @@ +# DSH Settings 兼容与恢复 — Issue #267 + +[English](./README.md) + +## 基线复现 + +2026-09-22,未修改的 main `65c0e23ba410993e16c00c8b3adf92d59d853425` 在临时 Web Profile 中,使用公开发布的 DSH `0.1.7-alpha.1` 复现了激活失败。Mnemon 根插件报错: + +```text +mnemon (dsh-mnemon): TypeError: ctx.settings.register is not a function +``` + +插件页面显示根插件异常,依赖它的 Source 和 Strategy 等待依赖。本地激活记录将结果标记为 `reproduced`;选定的 DSH 安装文件已与经过完整性校验的公开 npm tarball 比对。 + +![基线:Mnemon 激活异常,依赖插件等待依赖](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/before-alpha7-activation.png) + +准备和启动方法见[打包制品验证夹具](./harness/HARNESS.md)。基线与修复制品使用独立的临时 Profile。仅基线需要 `--legacy-peer-deps`,用于越过其过时的 peer 范围并暴露运行时故障;修复制品使用正常的 peer 解析。 + +## 实现与公开契约 + +DSH `0.1.7-alpha.1` 将动态 Settings 注册改为从各插件的静态 `Config` 生成表单。实现使用公开发布的 `SettingsForms.configure/describe/mutate`、`ConfigEditor.edit`、Cordis 的 `internal/config` waterfall,以及 Loader 的 `loader/volatile-update` 事件。官方 DSH 包、manifest 和源码均未修改;制品夹具不使用 DSH 源码 checkout、工作区 alias 或替代 Settings 服务。 + +- [Live Config](../../../src/host/live-config.ts) 使用公开 DeepSeek Schemastery/Cosmokit 包提供的真实 volatile 引用,保留原生表单可识别的对象 schema,并执行纯跨字段校验。`remoteAccess` 保持普通字段,沿用 Host 正常的重挂载行为。 +- 私有静态 ESM 桥通过 npm alias `schemastery-live` 导入未修改的公开 fork,以窄类型声明阻止其新增全局泛型合并进旧 schema builder。canonical 开发依赖固定为 `3.18.2`,保留 RC.1 类型组合,并满足一处遗漏运行时依赖的上游声明。Mnemon 输出的公开类型不包含 alias;consumer 的 Host 保持自身框架依赖版本。没有修改官方包、TypeScript 源码路径或导入顺序。 +- [Settings 适配器](../../../src/host/settings-service.ts) 保留 Mnemon 客户端命名空间,将读取、校验和写入绑定到所属 Entry/Fiber。仍提供 `settings.register` 的旧 Host 继续使用原服务。Profile 写入经过 DSH 加锁的编辑器;所属实例的 preflight 在持久化前校验候选运行图,提交后的 volatile 更新负责刷新运行时。 +- Core、UI 和 View 共用一个原生 revision。仅当该命名空间未脱敏的生效值、继承值和显式覆盖仍与已观察快照一致时,才允许有限重试。同命名空间冲突、缺少历史快照、只读状态、实例销毁和 Entry 替换均会拒绝写入;远程描述仍保持脱敏。 +- [插件管理](../../../src/host/plugin-management.ts) 在整个 Profile 被重新协调后重放已选择的 Entry 状态,校验最终组合,并对失败事务执行补偿。 +- [客户端图标别名](../../../src/client/ui-icons.ts) 同时支持旧版按尺寸命名的导出和 alpha7 按字重命名的导出。设置页双语说明改为由 DSH 保存并实时生效,不再硬编码已移除的设置文件。 + +- [Session 消息](../../../src/host/lifecycle.ts) 使用 alpha7 V4 codec 要求的生产者来源类型 `dsh-mnemon`。读取和去重仍识别历史 wrapper 与官方迁移后的 `plugin:dsh-mnemon`;修复激活后,真实 WebUI 对话继续复现了这一兼容故障。 + +## 保留设置与现有 Profile 选择 + +恢复过程只读 `settings.yaml.imported`,保持其字节不变,并仅通过 `ConfigEditor.edit` 写入。[纯迁移规划器](../../../src/host/legacy-settings-import.ts) 将规范根插件的保留分区映射如下: + +| 保留分区 | Profile Config 目标 | +| --- | --- | +| `mnemon` | 当前显式覆盖中缺失的根配置字段 | +| `mnemon-ui` | `conversationInteraction` | +| 精确匹配的 `mnemon-view[-hash]` | `memoryView` | +| 精确匹配的 `mnemon-plugins[-hash]` | `memoryView.entries` 中的 Source 启用状态,仅接受已确认的 Source Entry | + +当前显式根配置和 UI 选择优先。已显式设置的 `memoryView` 字段会保留,包括将 `entries` 重置为空的选择。现有 Strategy 行优先于更早保存的启用状态和配置;Source 配置仍由原生 Entry 保存。Profile 的局部 patch ID 会匹配到无歧义的完整 Loader ID,其他行不会被替换。规划器在编辑器回调内重新读取状态,使期间发生的显式编辑优先;`legacySettingsImported` 标记防止后续重置又恢复旧偏好。 + +原生 `!!js` 表达式以表达式数据保留,恢复过程不会执行它们,也不会把它们转成普通字符串。若某个 Entry 的启用状态或 Strategy 配置由表达式控制,将跳过该 Entry 的旧 View 覆盖。Source 配置表达式和无关表达式行继续保留在原生行中。相关旧备份分区中的表达式、损坏数据和不支持的 YAML 会被拒绝,不会完成恢复或修改备份。 + +## 迁移与回滚边界 + +- 若启动时仍有原始 `settings.yaml`,DSH 会自行异步重命名和导入。Mnemon 的补充恢复延后到下一次 Host 冷启动;刷新页面或重挂插件不会解除该限制。 +- 自动恢复仅接受规范根 Entry `mnemon`,以及所属 Profile 精确匹配的历史命名空间后缀。不猜测自定义根归属、其他 Profile 的 hash 或无后缀回退。歧义和被拒绝的数据仍保留在备份中,供人工核对。 +- 升级前备份 DSH Profile 配置、旧设置和相关 Mnemon 数据。新 Profile 设置不会反向同步到 `settings.yaml`;降级需要相应的配置备份,并人工核对升级后的新增修改,没有自动逆向导出。 +- 此变更涉及偏好存储和恢复,不改变 Runtime、Documents 或 Memory Spaces 的数据格式。 + +## 验证范围 + +定向回归覆盖 [schema 与引用行为](../../../tests/live-config.spec.ts)、[所属实例隔离与脱敏](../../../tests/profile-settings.spec.ts)、[revision 冲突与已退役实例](../../../tests/profile-settings-revisions.spec.ts)、[纯迁移规划](../../../tests/legacy-settings-import.spec.ts)、[保留文件恢复与表达式](../../../tests/profile-settings-import.spec.ts),以及[两代客户端图标](https://github.com/omdsh-dev/dsh-mnemon/blob/65a14ddd2d83bd0850948dc915bc410552b545d9/tests/client-primitives-compat.spec.tsx)。 + +制品夹具使用隔离测试数据,确定性模型端点仅绑定 `127.0.0.1`,不需要外部模型 API Key,也不会调用外部模型服务。启动认证 URL、`server.json`、原始 `web.log` 和模型请求日志均留在仓库外;公开证据不得包含凭据或私人数据。 + +## 设置与迁移实测结果 + +提交 `407eac75ee46e677f694e6c318ca9380357fae27` 的 17 件 Mnemon 打包制品通过以下浏览器操作。随后补充的 Session 来源格式修复单独记录;本轮记录验证设置实现。机器可读哈希与结果见 [settings-verification.json](./settings-verification.json)。 + +| 公开 DSH 版本 | 同版本 DSH 包数量 | 核对的官方安装文件 | 实际 WebUI 操作 | +| --- | ---: | ---: | --- | +| `0.1.5-rc.2` | 234 | 8 | 打开工作台;关闭 Light 并保留 Auto Capture/Scoped;保存核心/UI 设置;刷新页面 | +| `0.1.6-alpha.2` | 251 | 8 | 使用该版本 Settings 服务完成相同操作 | +| `0.1.7-alpha.1` | 267 | 10 | Root 与全部 Source/Strategy 激活;核心/UI 连续保存;独立 UI 保存;页面刷新及完整 Host 重启 | + +alpha7 重启后间隔仍为 `310000`,Light 保持关闭,Auto Capture/Scoped 保持开启,两个 UI 开关保持关闭。RC.2、alpha.2 保存的间隔分别为 `320000`、`330000`;Light/回合记忆条关闭,其他开关保持开启。没有出现虚假的 revision 冲突。 + +旧备份夹具恢复召回上限 `7`、回合记忆条开启/存入按钮关闭,以及主动记录开启。当前 Light 启用和配置(`1200`)、档案启用优先于旧值;外部 profile 的 Scoped 偏好被忽略。完整 Host 重启后,profile patch、根 Config、全部 21 个非 root 行和原始备份哈希均不变。重启前,20 个初始非 root 行与夹具初始化语义相同;新增行来自浏览器确认内测声明。 + +真实 Mnemon CLI `0.2.8` 还完成了 WebUI 状态调用,并创建、启用存储正常的原生记忆空间。可选的默认 Ollama 嵌入端点未运行;未使用外部嵌入或模型密钥。 + +![修复后 alpha7 Root 与 Source 激活](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/after-alpha7-activation.png) +![修复后 alpha7 记忆工作台](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/after-alpha7-status.png) +![核心/UI 设置成功保存,无虚假 revision 冲突](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/after-alpha7-settings-saved.png) +![完整 Host 重启后 Strategy 选择保持](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/after-alpha7-host-restart.png) +![旧 UI 偏好恢复](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/after-alpha7-legacy-settings.png) +![旧偏好在重启后保持](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/after-alpha7-legacy-restart.png) +![通过 WebUI 创建并启用原生 CLI 记忆空间](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/after-alpha7-native-cli.png) +![RC.2 设置保存](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/after-rc2-settings-saved.png) +![Alpha.2 设置保存](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/after-alpha2-settings-saved.png) + +## 类型隔离前的 Session 制品验证 + +Session 修复 `dc10b5b6a2b39bd3093823741e286592dc640cd6` 从干净 checkout 打包,在三个全新 consumer 中使用正常 npm peer 解析安装。三组使用相同的 17 件 Mnemon 制品;根包 SHA-256 为 `b2204a284db063a128dce845cd115ecac33b5c91e91071d050d2a9d5daf1db16`。[final-verification.json](./final-verification.json) 记录这一阶段的制品哈希、公开包校验、持久化工具回执及测试结果。最终类型隔离制品的验证见下文。 + +`0.1.5-rc.2`、`0.1.6-alpha.2`、`0.1.7-alpha.1` 的实际 WebUI 回合均调用 `mnemon_runtime_memory` 的 `action: add`,随后调用 `mnemon_status`。每组恰好写入一条夹具记忆,状态回执均为健康、原生 CLI 可用、允许写入。每份压缩 Session 均完整解码,保留两条来源为 `dsh-mnemon` 的消息。RC.2、alpha.2 使用 Session V3,alpha.7 使用 V4。提示词和标题仍为 `compatibility-261` / `Issue 261 compatibility`,因为复用了确定性模型夹具;本目录的制品、Profile 与截图属于 Issue 267。 + +Session 修复前,alpha.7 的真实回合报错 `format v4 message requires a producer-owned source kind`。回归测试使用公开 alpha.7 制品的真实 `encodeEvent`、`restoreReleasedV4Artifact` 契约,并验证旧包装会被拒绝。完整 lifecycle 测试 56 项通过。 + +这组 alpha.7 制品中,通过浏览器选择 Builtin 显示模式、关闭 Light、保存间隔 `340000`、关闭回合记忆条并保留存入按钮。完整重启 Host 进程后,浏览器确认全部设置、恢复的对话及其一条 Runtime 记忆均保留。Auto Capture、Scoped 仍通过原生 Entry 启用,没有多余的 View 覆盖。 + +原始文件夹具也通过了两个迁移阶段:DSH 先重命名、导入 `settings.yaml`,此时 Mnemon 补充标记和 View 尚不存在;完整重启一次 Host 后,预期 UI/View 偏好恢复,迁移标记写入。保留备份字节始终不变。这轮仅设置验证使用 `407eac75`;最终 Session 提交没有改动其设置实现。 + +![最终 alpha.7 回合完成两个 Mnemon 工具调用](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/final-alpha7-session-tools.png) +![Runtime 工具写入一条可见夹具记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/final-alpha7-runtime-write.png) +![RC.2 完成相同的真实工具调用](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/final-rc2-session-tools.png) +![Alpha.2 完成相同的真实工具调用](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/final-alpha2-session-tools.png) +![完整 Host 重启后的最终 alpha.7 设置](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/final-alpha7-settings-restart.png) +![Builtin 工作台在重启后保留 Runtime 记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/final-alpha7-builtin-restart.png) +![原始文件迁移在下一次 Host 启动完成](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/after-alpha7-native-import-restart.png) + +## 最终类型隔离制品与 WebUI 验证 + +干净 CI 暴露了旧 Schemastery 与新版 live fork 的全局泛型声明冲突。前述私有桥隔离运行时导入,并保留 main 已有锁文件的 package/snapshot 值。实现提交 `d57e14054a4c523097b382ff36ca6bd3a993ff03` 从干净 checkout 打包,再通过正常 peer 解析安装到三个全新 consumer。最终根包 SHA-256 为 `552eb9c23846e23e958100974888351a74666c471d614d2f098f2426d5a81cb3`;全部 17 件制品哈希和安装校验见 [type-isolation-verification.json](./type-isolation-verification.json)。 + +三组公开 DSH 均重新完成真实 WebUI Runtime 写入与原生 CLI 状态调用。每个已安装的 `schemastery-live` 包全部八个文件均与未修改的官方 `@deepseek-ai/schemastery@3.18.3` 制品一致。RC.2、alpha.2、alpha.7 的同版本 DSH 包数量/选定官方文件校验仍分别为 234/8、251/8、267/10。 + +alpha.7 中,浏览器先准备核心/UI 草稿,再通过独立的即时插件写入关闭 Light,最后保存剩余草稿,没有虚假冲突。完整重启 Host 后,Builtin、间隔 `350000`、回合记忆条关闭/存入按钮开启,以及 Light 关闭/Auto Capture 和 Scoped 开启均保留。原对话完整加载,Builtin 工作台显示一条 Runtime 记忆。Profile 与压缩 Session 的核对记录与浏览器证据一并保留。 + +该实现提交的 [Node 22.19 源码检查](https://github.com/omdsh-dev/dsh-mnemon/actions/runs/35733033143/job/106763140386)和 [Node 24 独立插件制品检查](https://github.com/omdsh-dev/dsh-mnemon/actions/runs/35733033143/job/106763140029)均通过。 + +![类型隔离后的 alpha.7 制品完成两个工具调用](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/v4-alpha7-session-tools.png) +![类型隔离后的 RC.2 制品完成两个工具调用](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/v4-rc2-session-tools.png) +![类型隔离后的 alpha.2 制品完成两个工具调用](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/v4-alpha2-session-tools.png) +![完整 Host 重启后 Builtin 设置保持](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/v4-alpha7-settings-restart.png) +![审查间隔与组合 Strategy 选择保持](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/v4-alpha7-strategies-restart.png) +![最终 Builtin Runtime 记忆在完整重启后保留](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-267-settings-migration/v4-alpha7-builtin-restart.png) + +## 完整本地检查与范围 + +以下命令通过,插件检查在 `verify` 完成后顺序执行: + +```sh +MNEMON_NATIVE_TEST_CLI=/path/to/mnemon \ +MNEMON_DSH_V4_CONTRACT_ROOT=/path/to/alpha7-consumer/node_modules \ + npx --yes --package=node@22.19.0 --package=pnpm@10.13.1 pnpm run verify +npx --yes --package=node@24.20.0 --package=pnpm@10.13.1 pnpm run verify:plugins --skip-build +``` + +- Root 测试 1,342 项通过、5 项跳过;插件测试 382 项通过、2 项跳过。跳过项依赖外部 Flash 模型、Windows 或真实 OpenViking 服务。真实原生 CLI、公开 V4 codec 用例均已启用。 +- 文档、类型、确定性构建、包体预算、公开入口导入、声明依赖、`publint`、`attw` 全部通过。最终根包包含 49 个文件、解包后 1,370,296 字节;验证了 12 个 Node 兼容入口、28 个公开类型依赖和修复 CLI 帮助。 +- Headless 检查通过,包含 39 个工具和 8 个代表性 Mnemon 工具。16 个独立插件仓库、17 个 tarball 的 consumer 检查通过,包括仅依赖 SDK 的外部 consumer、三个可选 Strategy 同时启用及仅 Starter 激活。 +- 浏览器夹具运行于 macOS、Node `25.1.0`,最终源码与制品检查分别运行于 Node `22.19.0`、`24.20.0`。这是 Mnemon 兼容证据,未覆盖 DSH 全部功能。精简夹具省略 terminal/deliverables/workspace-change 服务;对应等待项、通用 permission-preset 请求失败和 alpha.2 的终端重试按钮不属于 Mnemon 故障。本轮未验证外部模型质量、外部 Provider 或嵌入服务。 +- 多次本地夹具运行后,最终重启复核曾遇到浏览器 HTTP 431。使用合成的 14 KB Cookie 独立复现了短请求成功、较长插件资源请求失败。将同一运行中监听器的浏览地址改用 `localhost` 后完成复核;没有修改 Profile/数据、官方文件、服务端限制或已有浏览器 Cookie。重复执行时可参考夹具说明。 diff --git a/dsh/docs/pr-assets/issue-267-settings-migration/final-verification.json b/dsh/docs/pr-assets/issue-267-settings-migration/final-verification.json new file mode 100644 index 000000000..735706ac8 --- /dev/null +++ b/dsh/docs/pr-assets/issue-267-settings-migration/final-verification.json @@ -0,0 +1,523 @@ +{ + "verifiedAt": "2026-09-22", + "sourceCommit": "dc10b5b6a2b39bd3093823741e286592dc640cd6", + "baselineCommit": "65c0e23ba410993e16c00c8b3adf92d59d853425", + "scope": "Final packed Session V3/V4 WebUI turns and alpha7 Host restart. Earlier settings/migration evidence is retained separately.", + "environment": { + "platform": "macOS", + "node": "25.1.0", + "pnpm": "10.13.1", + "nativeMnemonCli": "0.2.8" + }, + "allRootArchivesIdentical": true, + "allInstalledRootIndicesIdentical": true, + "allHaveExactlyOneFixtureMemory": true, + "allPersistMnemonSource": true, + "allPersistSuccessfulRuntimeAddAndHealthyStatus": true, + "cohorts": [ + { + "frameworkVersion": "0.1.5-rc.2", + "artifactCommit": "dc10b5b6a2b39bd3093823741e286592dc640cd6", + "rootArtifactSha256": "b2204a284db063a128dce845cd115ecac33b5c91e91071d050d2a9d5daf1db16", + "actualRootArchiveSha256": "b2204a284db063a128dce845cd115ecac33b5c91e91071d050d2a9d5daf1db16", + "rootArchiveMatchesProvenance": true, + "installedRootIndexSha256": "1fa52c2d95acd2be01d7335926ef42dbfb2cf5a27cd5fec196a263be05c54ded", + "runtimeMemories": { + "count": 1, + "exactFixtureMemory": true, + "target": "memory", + "importance": "normal", + "contentSha256": "b4b99ca1dd86117d5ecfe875ed2af85d6d38486722167d114294cb52532e5045", + "fileSha256": "334e39646ad4ea4b03d8392a7eda07405b0e585978ac05c4b5890c0ad6064183" + }, + "sessions": [ + { + "formatVersion": 3, + "compressedSha256": "ad857041e9a5df3465b1fe9d5e1ace6618e8cde5ac270e2cc2595a8953412e8d", + "frameCount": 12, + "eventCount": 28, + "allFramesDecoded": true, + "mnemonSourceMessageCount": 2, + "persistedSourceKind": "dsh-mnemon", + "toolCalls": [ + { + "name": "mnemon_runtime_memory", + "action": "add" + }, + { + "name": "mnemon_status" + } + ], + "toolReceipts": [ + { + "tool": "mnemon_runtime_memory", + "success": true, + "target": "memory", + "entryCount": 1, + "sourceTypeId": "runtime" + }, + { + "tool": "mnemon_status", + "healthy": true, + "commandFound": true, + "writeEnabled": true + } + ], + "turnEndCount": 1, + "runtimeAddSucceeded": true, + "statusHealthy": true + } + ], + "dshPackageCount": 234, + "mnemonPackageCount": 17, + "officialInstalledFilesChecked": 8, + "normalNpmPeers": true, + "sourceCheckoutClean": true, + "mnemonErrorLogCandidateCount": 0, + "webuiTurnVerified": true, + "webuiScreenshot": "final-rc2-session-tools.png" + }, + { + "frameworkVersion": "0.1.6-alpha.2", + "artifactCommit": "dc10b5b6a2b39bd3093823741e286592dc640cd6", + "rootArtifactSha256": "b2204a284db063a128dce845cd115ecac33b5c91e91071d050d2a9d5daf1db16", + "actualRootArchiveSha256": "b2204a284db063a128dce845cd115ecac33b5c91e91071d050d2a9d5daf1db16", + "rootArchiveMatchesProvenance": true, + "installedRootIndexSha256": "1fa52c2d95acd2be01d7335926ef42dbfb2cf5a27cd5fec196a263be05c54ded", + "runtimeMemories": { + "count": 1, + "exactFixtureMemory": true, + "target": "memory", + "importance": "normal", + "contentSha256": "b4b99ca1dd86117d5ecfe875ed2af85d6d38486722167d114294cb52532e5045", + "fileSha256": "fcb84238c640a5f9496423d9d089f33d710c5e09f12a07db7f6a38eb0d8666ea" + }, + "sessions": [ + { + "formatVersion": 3, + "compressedSha256": "72e2fd12a5b5b52e951defebaa17045c2cf9e2c82d2eda88068ebc7617ecce79", + "frameCount": 12, + "eventCount": 32, + "allFramesDecoded": true, + "mnemonSourceMessageCount": 2, + "persistedSourceKind": "dsh-mnemon", + "toolCalls": [ + { + "name": "mnemon_runtime_memory", + "action": "add" + }, + { + "name": "mnemon_status" + } + ], + "toolReceipts": [ + { + "tool": "mnemon_runtime_memory", + "success": true, + "target": "memory", + "entryCount": 1, + "sourceTypeId": "runtime" + }, + { + "tool": "mnemon_status", + "healthy": true, + "commandFound": true, + "writeEnabled": true + } + ], + "turnEndCount": 1, + "runtimeAddSucceeded": true, + "statusHealthy": true + } + ], + "dshPackageCount": 251, + "mnemonPackageCount": 17, + "officialInstalledFilesChecked": 8, + "normalNpmPeers": true, + "sourceCheckoutClean": true, + "mnemonErrorLogCandidateCount": 0, + "webuiTurnVerified": true, + "webuiScreenshot": "final-alpha2-session-tools.png" + }, + { + "frameworkVersion": "0.1.7-alpha.1", + "artifactCommit": "dc10b5b6a2b39bd3093823741e286592dc640cd6", + "rootArtifactSha256": "b2204a284db063a128dce845cd115ecac33b5c91e91071d050d2a9d5daf1db16", + "actualRootArchiveSha256": "b2204a284db063a128dce845cd115ecac33b5c91e91071d050d2a9d5daf1db16", + "rootArchiveMatchesProvenance": true, + "installedRootIndexSha256": "1fa52c2d95acd2be01d7335926ef42dbfb2cf5a27cd5fec196a263be05c54ded", + "runtimeMemories": { + "count": 1, + "exactFixtureMemory": true, + "target": "memory", + "importance": "normal", + "contentSha256": "b4b99ca1dd86117d5ecfe875ed2af85d6d38486722167d114294cb52532e5045", + "fileSha256": "dbde2cd48a29428c66e8e3282ecf4dcf971d6ee555dd65b81bde0d8d7b8b7a51" + }, + "sessions": [ + { + "formatVersion": 4, + "compressedSha256": "bf43654d40eae5d72c5e1ee079ae1db62d9af6b29aae035889d69c3e78045ea3", + "frameCount": 12, + "eventCount": 32, + "allFramesDecoded": true, + "mnemonSourceMessageCount": 2, + "persistedSourceKind": "dsh-mnemon", + "toolCalls": [ + { + "name": "mnemon_runtime_memory", + "action": "add" + }, + { + "name": "mnemon_status" + } + ], + "toolReceipts": [ + { + "tool": "mnemon_runtime_memory", + "success": true, + "target": "memory", + "entryCount": 1, + "sourceTypeId": "runtime" + }, + { + "tool": "mnemon_status", + "healthy": true, + "commandFound": true, + "writeEnabled": true + } + ], + "turnEndCount": 1, + "runtimeAddSucceeded": true, + "statusHealthy": true + } + ], + "afterRestartSettings": { + "state": { + "displayMode": "builtin", + "minIntervalMs": 340000, + "lightEnabled": false, + "autoCaptureNativeEnabled": true, + "scopedNativeEnabled": true, + "autoCaptureViewOverridePresent": false, + "scopedViewOverridePresent": false, + "turnBar": false, + "saveAction": true + }, + "allExpectedValuesMatch": true, + "patchSha256": "79e6541ee888503b1e478bde4349cd566601774f150aaeaa8049421bc8fabe89" + }, + "dshPackageCount": 267, + "mnemonPackageCount": 17, + "officialInstalledFilesChecked": 10, + "normalNpmPeers": true, + "sourceCheckoutClean": true, + "mnemonErrorLogCandidateCount": 0, + "webuiTurnVerified": true, + "webuiScreenshot": "final-alpha7-session-tools.png", + "webuiAfterFullHostRestart": { + "settingsVerified": true, + "restoredConversationVerified": true, + "builtinRuntimeMemoryCount": 1, + "screenshots": [ + "final-alpha7-settings-restart.png", + "final-alpha7-builtin-restart.png" + ] + } + } + ], + "originalFileMigration": { + "sourceCommit": "407eac75ee46e677f694e6c318ca9380357fae27", + "result": "pass", + "settingsImplementationUnchangedAtFinalSourceCommit": true, + "fullHostRestartCount": 1, + "backupBytesUnchangedAcrossStages": true, + "webuiAfterRestartVerified": true, + "screenshot": "after-alpha7-native-import-restart.png", + "stages": [ + { + "stage": "first-start-native-import", + "checkedAt": "2026-09-22T12:30:46.211Z", + "result": "pass", + "checks": { + "normalPeers": true, + "exactAlpha7Framework": true, + "seventeenArtifacts": true, + "tenOfficialBytechecks": true, + "settingsSourceRenamed": true, + "retainedBackupUnchanged": true, + "mnemonMarkerAbsent": true, + "mnemonViewAbsent": true, + "nativeDefaultRecallLimitRetained": true, + "nativeCliRetained": true, + "explicitTurnBarRetained": true, + "supplementalSaveActionNotYetImported": true, + "pendingNativeImportWarning": true, + "nativeCli028": true, + "noStartupErrors": true + }, + "observed": { + "defaultRecallLimit": 7, + "cliPathMatchesNative": true, + "turnBar": true, + "legacySettingsImported": null, + "memoryViewPresent": false, + "saveAction": null + }, + "backupSha256": "c372088ea43007431c7841032c7249c7b21b7a4f02f50b9f470b1d2e706c6c4e", + "expectedBackupSha256": "c372088ea43007431c7841032c7249c7b21b7a4f02f50b9f470b1d2e706c6c4e", + "profileSha256": "e68f5f2e8d2052ebc41608f841c12dbff9ef6d844f6741b169ca6cafe3873ec2" + }, + { + "stage": "after-single-SIGUSR2-supplementary-import", + "checkedAt": "2026-09-22T12:32:29.200Z", + "result": "pass", + "checks": { + "sameHarness": true, + "newWebProcess": true, + "harnessAlive": true, + "webAlive": true, + "sourceRemainsRenamed": true, + "supplementaryMarkerWritten": true, + "retainedBackupEqualsFixture": true, + "retainedBackupEqualsFirstStage": true, + "nativeDefaultRecallLimitPreserved": true, + "nativeCliPreserved": true, + "ownViewStrategyImported": true, + "otherProfileViewIgnored": true, + "noPendingImportWarning": true, + "noStartupErrors": true, + "otherServicesUndisturbed": true, + "defaultRecallLimit": true, + "turnBar": true, + "saveAction": true, + "lightEnabled": true, + "lightMaxProjectionCharacters": true, + "autoCaptureEnabled": true, + "documentsEnabled": true, + "scopedEnabled": true + }, + "observed": { + "defaultRecallLimit": 7, + "turnBar": true, + "saveAction": false, + "lightEnabled": true, + "lightMaxProjectionCharacters": 1200, + "autoCaptureEnabled": true, + "documentsEnabled": true, + "scopedEnabled": true + }, + "expected": { + "defaultRecallLimit": 7, + "turnBar": true, + "saveAction": false, + "lightEnabled": true, + "lightMaxProjectionCharacters": 1200, + "autoCaptureEnabled": true, + "documentsEnabled": true, + "scopedEnabled": true + }, + "legacySettingsImported": true, + "backupSha256": "c372088ea43007431c7841032c7249c7b21b7a4f02f50b9f470b1d2e706c6c4e", + "profileSha256": "dc9a81f6289ae345e514d3a744e6c94b1ff03d3d62db4f1207ea1bae81d640e5" + } + ] + }, + "localValidation": { + "verify": { + "result": "pass", + "command": "MNEMON_NATIVE_TEST_CLI=/path/to/mnemon MNEMON_DSH_V4_CONTRACT_ROOT=/path/to/alpha7-consumer/node_modules npx --yes pnpm@10.13.1 run verify", + "rootTests": { + "passed": 1342, + "skipped": 5 + }, + "pluginTests": { + "passed": 382, + "skipped": 2 + }, + "lifecycleTests": { + "passed": 56, + "actualPublishedV4CodecEnabled": true + }, + "nativeCliCasesEnabled": true, + "skippedReasons": [ + "Five external Flash quality/stress cases", + "One Windows-only case", + "One live OpenViking integration case" + ], + "package": { + "files": 49, + "packedBytes": 306952, + "unpackedBytes": 1370207, + "nodeCompatibleEntries": 12, + "publicTypeDependencies": 28, + "executableHelpEntries": 1 + }, + "headless": { + "tools": 39, + "representativeTools": 8 + }, + "logSha256": "bc72b05e768a5a81f233de7a04e9ffda5e01be9ab65f55baf03032eb218e3930" + }, + "verifyPlugins": { + "result": "pass", + "command": "npx --yes pnpm@10.13.1 run verify:plugins --skip-build", + "runAfterVerify": true, + "independentPluginRepositories": 16, + "tarballs": 17, + "externalSdkOnlyConsumer": true, + "threeOptionalStrategiesTogether": true, + "starterOnlyActivation": true, + "logSha256": "f73a1039ed403a44c2887fe90e7256485630e1490d3155cc783bd1879b2f89a0" + } + }, + "browserFixtureHeaderLimit": { + "nodeDefaultMaxHeaderSize": 16384, + "sameCookieShortLongComparison": [ + { + "phase": "bootstrap", + "requestTargetBytes": 69, + "syntheticCookieValueBytes": 14000, + "status": 200 + }, + { + "phase": "application", + "requestTargetBytes": 2823, + "syntheticCookieValueBytes": 14000, + "status": 431 + } + ], + "realBrowserCookiesRead": false, + "realBrowserCookiesChanged": false, + "observedBrowserStatus": 431, + "resolution": "Navigate the private bootstrap URL using localhost on the same running loopback listener. No profile, data, framework, header limit or existing cookie changes.", + "finalWebuiRestartVerified": true + }, + "limits": [ + "Deterministic loopback model; no external model API used.", + "The fixture title and prompt reuse Issue 261; all final artifacts and runs documented here are for Issue 267.", + "Minimal fixture omits terminal, deliverables and workspace-change services; general permission-preset and terminal UI failures are outside Mnemon.", + "External model quality, external Provider integration, embeddings and Windows execution are not claimed by the browser runs." + ], + "artifacts": [ + { + "name": "dsh-mnemon", + "version": "0.5.12", + "filename": "dsh-mnemon-0.5.12.tgz", + "sha256": "b2204a284db063a128dce845cd115ecac33b5c91e91071d050d2a9d5daf1db16", + "integrity": "sha512-ixlnhOupSnCiFjWGKRd0weldp4xJ+Fdiq3sdE/35iMgzkhNPeBsa38mX2YWfXZ4i7YNYWNZbtAViqL0xlixoOQ==" + }, + { + "name": "dsh-mnemon-provider-byterover", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-byterover-0.5.4.tgz", + "sha256": "94677bb5a27261dd2051f67c9ae5bc41c17ea86a4ea95cbd0baee1728203633f", + "integrity": "sha512-0/ru18uVKNILv3Q2Dx7soOpm+9SrARnbxCsewmQKPy8fvj14zwd7TNLlI9/lV0/vN5SgjKq95PnOvsJZVD/krw==" + }, + { + "name": "dsh-mnemon-provider-hindsight", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-hindsight-0.5.4.tgz", + "sha256": "bf527bc83d51bd7b2e510ed3be59665ae6b3ddbd4e1c3beee16c32235fa0afbb", + "integrity": "sha512-nQ3rQ83J2ugNLXDkG1eYPjpibFANoe3n9OkxfyPCxXkAHHKYdmIZ35mzCFG8sMWsDeSt8y1RezulWjima4ZAVw==" + }, + { + "name": "dsh-mnemon-provider-holographic", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-holographic-0.5.4.tgz", + "sha256": "67f9cc593a7bcf33001e0817a4932e23cf9f0927e90b6bc2b2ccd5ef49b91259", + "integrity": "sha512-ULO6MBVq8rmRMlMnDcqJ26XPsk1K5j3HPjtTXHoBlEy6GdYQ7/2vZLtDQZ9FNjMZ9VT/W6Qj+RAc1HfgIj3bZA==" + }, + { + "name": "dsh-mnemon-provider-honcho", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-honcho-0.5.4.tgz", + "sha256": "c102952aa1b9fdd953b25068663635a9ffad606bb39c68b56eafa538c8fab72d", + "integrity": "sha512-xbbDeshwpiFZHlIUFveMbv6o6xTRY1sjNUvkcXKN/oInxCYdV4sC7Q6pBMRoSoSXYoAgUVExLLXxG+p9Z7uDZA==" + }, + { + "name": "dsh-mnemon-provider-mem0", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-mem0-0.5.4.tgz", + "sha256": "42cd225bb376d387d8e7f5503d64b6a00ddae4f5aea26b495cb9d5aa44dab3b0", + "integrity": "sha512-jriF73KpQp6DoH0RbdfJx+6HzUGEGq1/zy1Y8DmyRaRF62OhBmySGSTHZHasVu1kw61NWzSqIBFWFVn6La0wYQ==" + }, + { + "name": "dsh-mnemon-provider-mnemon-native", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-mnemon-native-0.5.5.tgz", + "sha256": "cb1532ec04e1bc2550456340b453717686c2bc399e742e61be529bdae5e6f812", + "integrity": "sha512-M6v7W2MomcPaPEcf5vUVfJa0j1HUdEmHwdCYR+jmK05doyuP/Vfg/+S2ZnpySKWEGu7jDlgRQbd5hb8RANKahg==" + }, + { + "name": "dsh-mnemon-provider-openviking", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-openviking-0.5.5.tgz", + "sha256": "da3505e93462a7860d331e830930a04d4e2406be5b540333f340f9351346920f", + "integrity": "sha512-7XP4we0/Zl9QasILR5SvEGEONk7YSXj1X9PTpPZ7eS6IgCV3uauhTV5aqZ0d2VPgSR7MYaq1nWnc0MuEZfVbXA==" + }, + { + "name": "dsh-mnemon-provider-retaindb", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-retaindb-0.5.4.tgz", + "sha256": "a7b5f1efb4e68df2d7ccc7e6078cd8c3229c82cf14f0162a196c975cbe19727f", + "integrity": "sha512-wztasXBFvV0M4xnJM0G0K819EMWLB0gVE0rvc4CY7XkmnqTAPoZ/26pZT9WY6ENHj84onkC2ck58xy7eqiX70A==" + }, + { + "name": "dsh-mnemon-provider-supermemory", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-supermemory-0.5.4.tgz", + "sha256": "d04b900f7f9e9359ba53b349bdbc4550fc19b350771e61df3aa451e8f67f9afd", + "integrity": "sha512-hB6G2B5hu1ktMyqP5Al7b9uLVfV876Fs5zrgh5URlaE25e4s3VrJBkzebq5ZuhSocTDYFKMsZL4AWQGLFkb0Rg==" + }, + { + "name": "dsh-mnemon-source-documents", + "version": "0.5.6", + "filename": "dsh-mnemon-source-documents-0.5.6.tgz", + "sha256": "411900a7eef2e2d9b90e83bb4a3669269d150a46142fcfe51cc4ba3819ccb9ec", + "integrity": "sha512-GlSzJs5C4nR1fG7dLRWMx7KOEF5ZDyKbU2UQGaBKXWeQrD5xZKfu/fZUbvUr/yj0HVfRKxbhvwkK1Iv9Oq3BLQ==" + }, + { + "name": "dsh-mnemon-source-memory-spaces", + "version": "0.5.8", + "filename": "dsh-mnemon-source-memory-spaces-0.5.8.tgz", + "sha256": "a98a8b03fd384785406d85548254b1182276057c6f9b98784a7a35cee58f90c6", + "integrity": "sha512-OuNWar7KABXPS+6XKan5FVcJPrjIV9vdeNUkXNzvp9ofc2CRiKqwpBNjDADPemza3XqIeWZQYF6SrBCN6r+1kg==" + }, + { + "name": "dsh-mnemon-source-runtime", + "version": "0.5.8", + "filename": "dsh-mnemon-source-runtime-0.5.8.tgz", + "sha256": "f9cdd113522fc62b08b4993260e03ca7cf71d14f0f2bd188cacb400f6e27f607", + "integrity": "sha512-QPZBv2dvwa7so77NvyAZcAC0YkfXRfRzgTogaLWCbkrBf0Xk39zz0noo+4GgE8idMh0M/oLS6Erb2fErWSz2AQ==" + }, + { + "name": "dsh-mnemon-strategy-auto-capture", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-auto-capture-0.5.4.tgz", + "sha256": "3816601abd83f0c5c679f3e4c9ae96c8f697e8e15f6546e975a96fd7f86b8c02", + "integrity": "sha512-SYHrc0H2Pxgs5dqa48AgoYudnAlkB450H8dqAz7ILiFGEo4V2u6RQuBsF5A7QOJXwV9v3fd0YQ9eAcc5tFds9Q==" + }, + { + "name": "dsh-mnemon-strategy-default-three-tier", + "version": "0.5.5", + "filename": "dsh-mnemon-strategy-default-three-tier-0.5.5.tgz", + "sha256": "52af5caf635f2cf2b165feff1ff3d35d241e6d073771b7b92b3ea37b36ff589f", + "integrity": "sha512-6cg2mz8QyYKYjdk0YnW+IYp2TEQQsZatj89CwsDXU50sN2D8dDal5TZwBl5+qRa5MZj11mIN5ZENWCDcBqTFxw==" + }, + { + "name": "dsh-mnemon-strategy-light-context", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-light-context-0.5.4.tgz", + "sha256": "62947c3ff777fbb8d42724acf2a9c651d2b2302b387bb8579c59cd79f82a9e0d", + "integrity": "sha512-7qbo0e9igj0x03ekcBbAunkldWOme07d/R+tI/U+0P6WsyiPGy8EX0UnqYAervd1oZ8G06+srJ0zDX6tcFX0JA==" + }, + { + "name": "dsh-mnemon-strategy-scoped", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-scoped-0.5.4.tgz", + "sha256": "f3f945add93d2d5f8f48f0e70f5de9360814499a552218af7fa93f574dd3457a", + "integrity": "sha512-aWPYgkexWdmg8LWFddnlnX9HbFRA6E43pbQxMTnkC8YOFja/EAArpqa0McBlffYMZedvCg7FhGWuQV7Qy7nezw==" + } + ] +} diff --git a/dsh/docs/pr-assets/issue-267-settings-migration/harness/HARNESS.md b/dsh/docs/pr-assets/issue-267-settings-migration/harness/HARNESS.md new file mode 100644 index 000000000..0512de877 --- /dev/null +++ b/dsh/docs/pr-assets/issue-267-settings-migration/harness/HARNESS.md @@ -0,0 +1,50 @@ +# Issue 267: public Settings migration fixture + +This harness installs the unchanged public DSH packages into a disposable consumer, then installs all 17 Mnemon tarballs from the selected artifact set. It validates every Mnemon tarball's SHA-512 and SHA-256, checks the installed lockfile's tarball integrity, rejects mixed DSH versions, checks package resolution stays inside the consumer, and compares selected installed framework bytes with integrity-verified public tarballs. + +The framework is installed with normal npm peer resolution first. Its physically installed framework and peer packages become exact consumer dependencies. The **baseline only** uses `--legacy-peer-deps`, because dsh-mnemon 0.5.12's old public peer range excludes DSH 0.1.7-alpha.1. The fixed consumer explicitly uses normal peer resolution. There are no framework edits, TypeScript path aliases, workspace package links, or substituted Cordis/Settings services. + +## Prepare and launch + +Run from the repository root. Use disposable paths outside the checkout, and supply the real native Mnemon executable. + +```sh +node docs/pr-assets/issue-267-settings-migration/harness/prepare-framework.mjs \ + --framework-version 0.1.7-alpha.1 \ + --root /tmp/issue267/framework-alpha7 + +node docs/pr-assets/issue-261-dsh-slots/harness/pack-artifacts.mjs \ + --source /path/to/built/source \ + --output /tmp/issue267/artifacts/baseline + +node docs/pr-assets/issue-267-settings-migration/harness/packed-e2e.mjs \ + --mode baseline \ + --framework-version 0.1.7-alpha.1 \ + --framework-root /tmp/issue267/framework-alpha7 \ + --artifacts /tmp/issue267/artifacts/baseline \ + --run-root /tmp/issue267/baseline-alpha7 \ + --native-cli /path/to/mnemon \ + --serve true +``` + +Pack the built fixed checkout into a separate artifact directory, then use the same launch command with `--mode fixed`, its artifact directory, and a fresh run root. The normal install must succeed without widening or altering the official package manifests. `--framework-version` also accepts `0.1.5-rc.2` and `0.1.6-alpha.2` with matching clean framework roots for the existing compatibility cohorts. + +For the alpha.7 profile, settings are fields of the ordinary `mnemon` Config row in `profiles/web/cordis.patch.yml`. The harness uses the published `agent-preset-registry` and `dsh-agent-preset` contracts for a minimal persona preset. Older cohorts use their published settings.yaml and preset-directory contracts. Automatic maintenance is disabled and the only model endpoint is the deterministic loopback fixture reused from Issue 261. The model accepts `compatibility-261` to exercise real Mnemon Runtime add and status tools. + +The WebUI binds only to `127.0.0.1` on an ephemeral port. The bootstrap URL is written to **`/server.json`**, and raw startup output remains in `web.log`; neither is printed by the harness. Keep bootstrap URLs private when collecting screenshots or evidence. Use the browser to open the URL read from that file. The harness does not operate a browser. + +The baseline `web.log` should contain `TypeError: ctx.settings.register is not a function`. A matching error also becomes the non-secret `activationFailure` field in `server.json`. If the official launcher's required-entry audit exits, its exit code is recorded there. The fixed run should have no Settings activation failure and should expose the Mnemon UI and tools. + +Important evidence files are `install-policy.json`, `artifact-provenance.json`, `framework-pins.json`, `runtime-graph.json`, `effective-config.yml`, `cli-version.log`, `mnemon-cli-version.log`, `web.log`, and the model request/event logs. Runtime data stays below the run root. Send `SIGUSR2` to the harness PID in `server.json` to restart the Web process without reinstalling or rewriting the profile; this preserves settings edits and persistence data. Stop the harness with `SIGTERM`. + +`--serve false` prepares and verifies the consumer without starting a Web server. `--reuse-install true` is only for re-running setup after an interrupted fixture preparation; it rewrites the fixture profile and is not a persistence check. + +## Retained settings fixture + +For the fixed alpha.7 run, add `--legacy-settings retained` to seed an unchanged `settings.yaml.imported` backup. The fixture records the exact profile hash and backup SHA-256 in `legacy-settings-fixture.json`. It covers root recovery, independent UI toggles, a current profile override, an existing Strategy config row, legacy Source enablement, and a foreign profile namespace. Verify the recorded expected values after startup and again after a `SIGUSR2` host restart. + +`--legacy-settings pending` instead seeds the original `settings.yaml`. The first host leaves Mnemon-specific recovery to the next startup while DSH renames/imports the old file; the second host recovers the retained backup. This path avoids racing DSH's private asynchronous importer. The harness never evaluates YAML expressions itself. + +## Repeated loopback runs + +Many disposable ports can accumulate cookies on `127.0.0.1`. If the browser's long plugin-resource request returns HTTP 431 while the same public resource succeeds without those headers, retain the current loopback listener and change only the privately read bootstrap URL's hostname to `localhost` before navigating. The official Web authentication accepts that loopback authority and issues its own session. Do not pass `--host localhost`: the public CLI host schema accepts only `127.0.0.1` or `0.0.0.0`. Do not print the bootstrap URL, remove existing browser cookies, increase header limits or alter framework files for this workaround. diff --git a/dsh/docs/pr-assets/issue-267-settings-migration/harness/packed-e2e.mjs b/dsh/docs/pr-assets/issue-267-settings-migration/harness/packed-e2e.mjs new file mode 100644 index 000000000..00a6fdbc2 --- /dev/null +++ b/dsh/docs/pr-assets/issue-267-settings-migration/harness/packed-e2e.mjs @@ -0,0 +1,315 @@ +import assert from 'node:assert/strict'; +import { createServer } from 'node:http'; +import { readFile, writeFile, appendFile, mkdir, realpath } from 'node:fs/promises'; +import { createReadStream } from 'node:fs'; +import { spawn } from 'node:child_process'; +import { createHash } from 'node:crypto'; +import { createRequire } from 'node:module'; +import { resolve, join } from 'node:path'; +import { pathToFileURL } from 'node:url'; +import { compatibilityModel, sendModelReply, responseProtocol } from '../../issue-261-dsh-slots/harness/compatibility-model.mjs'; +import { frameworkPins } from '../../issue-261-dsh-slots/harness/framework-pins.mjs'; + +const args = new Map(); +for (let i = 2; i < process.argv.length; i += 2) args.set(process.argv[i], process.argv[i + 1]); +const mode = args.get('--mode'); +const version = args.get('--framework-version') ?? '0.1.7-alpha.1'; +const cohort = version.includes('alpha') ? 'alpha' : 'rc'; +const configSettings = version === '0.1.7-alpha.1'; +assert(['baseline', 'fixed'].includes(mode), '--mode must be baseline or fixed'); +assert(['0.1.5-rc.2', '0.1.6-alpha.2', '0.1.7-alpha.1'].includes(version), 'unsupported --framework-version'); +assert(args.has('--framework-root'), 'supply --framework-root from a clean normal-peer public framework install'); +const artifactsRoot = resolve(args.get('--artifacts')); +const runRoot = resolve(args.get('--run-root') ?? `./e2e/runs/${mode}-${version}`); +const serve = args.get('--serve') === 'true'; +const legacySettings = args.get('--legacy-settings') ?? 'none'; +assert(['none', 'retained', 'pending'].includes(legacySettings), 'unsupported --legacy-settings'); +assert(legacySettings === 'none' || configSettings, 'legacy migration fixture requires alpha7'); +const dshHome = join(runRoot, 'dsh-home'); +const consumer = join(dshHome, 'profiles/web'); +const workspace = join(runRoot, 'workspace'); +const data = join(runRoot, 'mnemon-data'); +const nativeCliInput = args.get('--native-cli') ?? process.env.MNEMON_CLI_PATH; +assert(nativeCliInput, 'supply --native-cli or MNEMON_CLI_PATH'); +const nativeCli = resolve(nativeCliInput); +await Promise.all([consumer, workspace, data].map(path => mkdir(path, { recursive: true }))); +const artifactSet = JSON.parse(await readFile(join(artifactsRoot, 'artifacts.json'), 'utf8')); +assert.equal(artifactSet.artifacts.length, 17); +const localPackages = new Map(artifactSet.artifacts.map(item => [item.name, item])); +const artifactPath = item => join(item.artifactRoot ?? artifactsRoot, item.filename); +for (const item of localPackages.values()) { + const bytes = await readFile(artifactPath(item)); + assert.equal('sha512-' + createHash('sha512').update(bytes).digest('base64'), item.integrity); + assert.equal(createHash('sha256').update(bytes).digest('hex'), item.sha256); +} +await writeFile(join(runRoot, 'artifact-provenance.json'), JSON.stringify({ artifactsRoot, ...artifactSet.provenance, artifacts: artifactSet.artifacts.map(({ name, version, filename, integrity, sha256, source }) => ({ name, version, filename, integrity, sha256, source })) }, null, 2) + '\n'); +const legacyPeers = mode === 'baseline' && configSettings; +const frameworkPeers = await frameworkPins(resolve(args.get('--framework-root')), version); +await writeFile(join(runRoot, 'framework-pins.json'), JSON.stringify(frameworkPeers, null, 2) + '\n'); +for (const [name, pinned] of Object.entries(frameworkPeers)) if (name.startsWith('@deepseek-ai/dsh')) assert.equal(pinned, version); +const env = { ...process.env, DSH_HOME: dshHome, DSH_TELEMETRY_DISABLED: '1', DSH_TELEMETRY_MODE: 'DISABLED', MNEMON_DATA_DIR: data, MNEMON_CLI_PATH: nativeCli, DEEPSEEK_API_KEY: 'isolated-issue267-fixture-key' }; +for (const key of ['NODE_PATH', 'NODE_OPTIONS', 'npm_config_legacy_peer_deps', 'NPM_CONFIG_LEGACY_PEER_DEPS', 'OPENAI_API_KEY', 'ANTHROPIC_API_KEY']) delete env[key]; +const registry = createServer(async (request, response) => { + try { + const path = new URL(request.url, 'http://127.0.0.1').pathname; + if (path.startsWith('/artifacts/')) { + const file = decodeURIComponent(path.slice('/artifacts/'.length)); + const item = [...localPackages.values()].find(candidate => candidate.filename === file); + if (!item) { response.writeHead(404); response.end(); return; } + response.writeHead(200, { 'content-type': 'application/octet-stream' }); + createReadStream(artifactPath(item)).pipe(response); + await appendFile(join(runRoot, 'registry.log'), JSON.stringify({ at: new Date().toISOString(), kind: 'local-tarball', package: item.name, version: item.version, integrity: item.integrity }) + '\n'); + return; + } + const name = decodeURIComponent(path.slice(1)); + const item = localPackages.get(name); + if (item) { + const base = `http://127.0.0.1:${registry.address().port}`; + const manifest = { ...item.manifest, dist: { tarball: `${base}/artifacts/${encodeURIComponent(item.filename)}`, integrity: item.integrity, shasum: item.shasum } }; + response.writeHead(200, { 'content-type': 'application/json' }); + response.end(JSON.stringify({ _id: name, name, 'dist-tags': { latest: item.version }, versions: { [item.version]: manifest } })); + return; + } + if (name.startsWith('dsh-mnemon')) { response.writeHead(404); response.end(JSON.stringify({ error: 'All Mnemon packages must come from the selected artifacts' })); return; } + const upstream = await fetch('https://registry.npmjs.org' + path, { headers: { accept: request.headers.accept ?? 'application/json' } }); + response.writeHead(upstream.status, { 'content-type': upstream.headers.get('content-type') ?? 'application/json' }); + response.end(Buffer.from(await upstream.arrayBuffer())); + } catch (error) { response.writeHead(500); response.end(String(error)); } +}); +await new Promise(resolveListen => registry.listen(0, '127.0.0.1', resolveListen)); +const registryUrl = `http://127.0.0.1:${registry.address().port}`; +env.npm_config_registry = registryUrl; +env.NPM_CONFIG_REGISTRY = registryUrl; + +function command(executable, argv, log, cwd = consumer, timeoutMs = 240000) { + return new Promise((resolveCommand, reject) => { + const child = spawn(executable, argv, { cwd, env, stdio: ['ignore', 'pipe', 'pipe'] }); + let out = ''; + child.stdout.on('data', chunk => { out += chunk; void appendFile(join(runRoot, log), chunk); }); + child.stderr.on('data', chunk => { out += chunk; void appendFile(join(runRoot, log), chunk); }); + const timer = setTimeout(() => { child.kill('SIGTERM'); reject(new Error(`${argv[0]} timed out`)); }, timeoutMs); + child.once('error', reject); + child.once('exit', code => { clearTimeout(timer); code === 0 ? resolveCommand(out) : reject(new Error(`${executable} ${argv.join(' ')} exited ${code}; see ${join(runRoot, log)}`)); }); + }); +} + +const manifest = { + name: `issue267-${mode}-${version}-web`, version: '0.0.0', private: true, type: 'module', + dependencies: { + ...frameworkPeers, + '@deepseek-ai/dsh': version, '@deepseek-ai/dsh-client-ui-primitives': version, '@deepseek-ai/dsh-typert-protocol': version, + ...Object.fromEntries(['dsh-client-ui-slots', 'dsh-client-ui-chat', 'dsh-client-ui-conversation', 'dsh-client-ui-renderer', 'dsh-client-ui-layout', 'dsh-client-ui-settings', 'dsh-client-store', 'dsh-client-locale'].map(name => [`@deepseek-ai/${name}`, version])), + '@deepseek-ai/cordis': configSettings ? '4.0.3' : '4.0.2', react: '18.3.1', 'react-dom': '18.3.1', + ...Object.fromEntries([...localPackages.values()].map(item => [item.name, item.version])), + }, + dsh: { profile: { bundles: ['@deepseek-ai/dsh-base', '@deepseek-ai/dsh-web-app', 'dsh-mnemon'] } }, +}; +await writeFile(join(consumer, 'package.json'), JSON.stringify(manifest, null, 2) + '\n'); +await writeFile(join(runRoot, 'install-policy.json'), JSON.stringify({ mode, cohort, version, registryUrl, legacyPeers, reusedExistingInstall: args.get('--reuse-install') === 'true', reason: legacyPeers ? `Baseline dsh-mnemon 0.5.12 peer ranges exclude ${version}; only the baseline reproduction uses --legacy-peer-deps. The exact framework was installed normally first. Fixed consumers must normal-install.` : 'Normal npm peer resolution, no legacy-peer-deps.' }, null, 2) + '\n'); +console.log(`Installing ${mode} ${cohort} from ${artifactsRoot} at ${consumer}`); +if (args.get('--reuse-install') !== 'true') await command('npm', ['install', '--registry', registryUrl, '--no-audit', '--no-fund', legacyPeers ? '--legacy-peer-deps' : '--legacy-peer-deps=false'], 'install.log'); + +const lock = JSON.parse(await readFile(join(consumer, 'package-lock.json'), 'utf8')); +const dshPackages = Object.entries(lock.packages).flatMap(([path, info]) => /(?:^|\/)node_modules\/@deepseek-ai\/dsh(?:-[^/]+)?$/.test(path) ? [{ path, version: info.version, resolved: info.resolved, integrity: info.integrity }] : []); +assert(dshPackages.length > 200); +assert(dshPackages.every(item => item.version === version), 'all installed DSH packages must match exact cohort'); +for (const item of localPackages.values()) { + const entry = lock.packages[`node_modules/${item.name}`]; + assert.equal(entry.version, item.version); + assert.equal(entry.integrity, item.integrity); + assert(/^http:\/\/127\.0\.0\.1:\d+\/artifacts\//.test(entry.resolved), 'Mnemon must resolve from the test loopback registry'); + assert.equal(decodeURIComponent(new URL(entry.resolved).pathname), `/artifacts/${item.filename}`); +} +const resolutions = []; +for (const item of localPackages.values()) { + const installed = join(consumer, 'node_modules', item.name, 'package.json'); + const loader = createRequire(installed); + for (const dependency of ['@deepseek-ai/cordis', '@deepseek-ai/schemastery', '@deepseek-ai/dsh-client-ui-primitives', '@deepseek-ai/dsh-typert-protocol']) { + const path = await realpath(loader.resolve(dependency)); + assert(path.startsWith(await realpath(consumer) + '/node_modules/'), `external resolution detected: ${path}`); + resolutions.push({ from: item.name, dependency, path }); + } +} +const bytesChecked = []; +for (const [pkg, file] of [ + ['dsh-settings', 'lib/index.js'], ['dsh-client-ui-slots', 'lib/index.js'], + ['dsh-client-ui-chat', 'lib/client.js'], ['dsh-client-ui-renderer', 'lib/client.js'], + ['dsh-client-ui-conversation', 'lib/client.js'], ['dsh-client-ui-settings', 'lib/client.js'], + ['dsh-base', 'cordis.patch.yml'], ['dsh-web-app', 'cordis.patch.yml'], + ...(configSettings ? [['dsh-config-editor', 'lib/index.js'], ['dsh-agent-preset-registry', 'lib/index.js']] : []), +]) { + const publicRoot = join(runRoot, 'public-contracts', version, pkg); + await mkdir(publicRoot, { recursive: true }); + const metadata = await (await fetch(`https://registry.npmjs.org/@deepseek-ai/${pkg}/${version}`)).json(); + assert.equal(metadata.name, `@deepseek-ai/${pkg}`); + assert.equal(metadata.version, version); + assert.equal(lock.packages[`node_modules/@deepseek-ai/${pkg}`].integrity, metadata.dist.integrity); + const archive = Buffer.from(await (await fetch(metadata.dist.tarball)).arrayBuffer()); + assert.equal('sha512-' + createHash('sha512').update(archive).digest('base64'), metadata.dist.integrity); + const tarball = join(publicRoot, 'package.tgz'); + await writeFile(tarball, archive); + await writeFile(join(publicRoot, 'metadata.json'), JSON.stringify(metadata, null, 2) + '\n'); + await command('tar', ['-xzf', tarball, '-C', publicRoot], 'public-contract-extraction.log', runRoot); + const reference = join(publicRoot, 'package', file); + const installed = join(consumer, 'node_modules/@deepseek-ai', pkg, file); + const original = await readFile(reference); + const actual = await readFile(installed); + assert(actual.equals(original), `public package bytes changed: ${pkg}`); + bytesChecked.push({ package: `@deepseek-ai/${pkg}`, file, tarballIntegrity: metadata.dist.integrity, sha256: createHash('sha256').update(actual).digest('hex') }); +} +await writeFile(join(runRoot, 'runtime-graph.json'), JSON.stringify({ version, dshPackageCount: dshPackages.length, dshPackages, mnemonPackages: [...localPackages.keys()].filter(name => name.startsWith('dsh-mnemon')), testClientPackages: [...localPackages.keys()].filter(name => !name.startsWith('dsh-mnemon')), resolutions, publicBytesChecked: bytesChecked }, null, 2) + '\n'); +const cli = join(consumer, 'node_modules/@deepseek-ai/dsh/lib/bin.js'); +await command(process.execPath, [cli, '--version'], 'cli-version.log'); +await command(nativeCli, ['--version'], 'mnemon-cli-version.log', workspace); + +const disabled = ['subprocess', 'open-in-app', 'bash-sandbox', 'pwsh-sandbox', 'tool-bash', 'tool-pwsh', 'permission', 'tool-fs-search', 'directory-picker']; +const extensionIds = ['mnemon-strategy-scoped', 'mnemon-strategy-light-context', 'mnemon-strategy-auto-capture']; +const commonPatch = disabled.map(id => `- id: ${id}\n disabled: true\n`).join('') + + '- insert:\n - id: e2e-directory-picker\n name: "@deepseek-ai/dsh-host-directory-picker-browse"\n - id: e2e-directory-picker-ui\n name: "@deepseek-ai/dsh-client-ui-directory-picker-browse"\n' + + extensionIds.map(id => `- id: ${id}\n disabled: false\n`).join(''); +const mnemonConfig = ` cliPath: ${JSON.stringify(nativeCli)}\n idleReview:\n enabled: false\n persistenceStrategy:\n mode: manual\n`; +let profilePatch = commonPatch; +if (configSettings) { + // Alpha.7 uses normal profile Config rows and declarative presets; no settings.yaml. + profilePatch += '- id: mnemon\n config:\n' + mnemonConfig + + '- id: agent-preset-registry\n config:\n default: mnemon-e2e\n' + + ['standard', 'minimal', 'cordis', 'ptc'].map(id => `- id: preset-${id}\n disabled: true\n`).join('') + + '- insert:\n - id: preset-mnemon-e2e\n name: "@deepseek-ai/dsh-agent-preset"\n config:\n id: mnemon-e2e\n name: Mnemon E2E\n description: Isolated packed-artifact Issue 267 fixture.\n order: 0\n plugins:\n - id: persona\n name: "@deepseek-ai/dsh-persona"\n config:\n prefix: You are testing packed Mnemon compatibility with an isolated deterministic model.\n'; +} else { + profilePatch += '- id: agent-presets\n config:\n default: mnemon-e2e\n'; + await writeFile(join(dshHome, 'settings.yaml'), 'mnemon:\n' + mnemonConfig.replace(/^ /gm, '')); + const preset = join(dshHome, '.agent-presets/mnemon-e2e'); + await mkdir(preset, { recursive: true }); + await writeFile(join(preset, 'preset.yml'), 'name: Mnemon E2E\ndescription: Isolated packed-artifact Issue 267 fixture.\norder: 0\n'); + await writeFile(join(preset, 'agent.cordis.yml'), '- id: persona\n name: "@deepseek-ai/dsh-persona"\n config:\n prefix: You are testing packed Mnemon compatibility with an isolated deterministic model.\n'); +} +if (legacySettings !== 'none') { + const anchor = pathToFileURL(consumer + '/').href; + const suffix = createHash('sha256').update(JSON.stringify(anchor)).digest('hex').slice(0, 16); + const backup = `mnemon: + defaultRecallLimit: 7 + cliPath: ${legacySettings === 'pending' ? JSON.stringify(nativeCli) : '/fixture/old-cli-must-not-win'} +mnemon-ui: + turnBar: false + saveAction: false +mnemon-view-${suffix}: + strategyTypeId: default-three-tier + entries: + include:mnemon-strategy-light-context: + enabled: false + config: + maxProjectionCharacters: 700 + include:mnemon-strategy-auto-capture: + enabled: true + config: {} +mnemon-plugins-${suffix}: + sources: + include:mnemon-source-documents: + enabled: false +mnemon-view-ffffffffffffffff: + entries: + include:mnemon-strategy-scoped: + enabled: false + config: {} +`; + profilePatch = profilePatch.replace('- id: mnemon-strategy-auto-capture\n disabled: false\n', ''); + profilePatch += '- id: mnemon\n config:\n conversationInteraction:\n turnBar: true\n' + + '- id: mnemon-strategy-light-context\n config:\n maxProjectionCharacters: 1200\n' + + '- id: mnemon-source-documents\n disabled: false\n'; + const filename = legacySettings === 'retained' ? 'settings.yaml.imported' : 'settings.yaml'; + await writeFile(join(dshHome, filename), backup); + await writeFile(join(runRoot, 'legacy-settings-fixture.json'), JSON.stringify({ + mode: legacySettings, backupSha256: createHash('sha256').update(backup).digest('hex'), + viewNamespace: `mnemon-view-${suffix}`, legacyNamespace: `mnemon-plugins-${suffix}`, + expected: { defaultRecallLimit: 7, turnBar: true, saveAction: false, lightEnabled: true, lightMaxProjectionCharacters: 1200, autoCaptureEnabled: true, documentsEnabled: true, scopedEnabled: true }, + }, null, 2) + '\n'); +} +await writeFile(join(consumer, 'cordis.patch.yml'), profilePatch); +await writeFile(join(workspace, 'README.md'), '# Issue 267 disposable workspace\n\nAll memory and model data in this fixture are test-owned.\n'); +await command(process.execPath, [cli, 'web', '--dump-config'], 'effective-config.yml', workspace); +let status = { mode, cohort, version, runRoot, artifactsRoot, consumer, dshHome, workspace, data, cli, nativeCli, registryUrl, legacyPeers, configSettings, pid: process.pid, preparedAt: new Date().toISOString(), serverStarted: false }; +let serverWrites = Promise.resolve(); +function writeStatus() { + const bytes = JSON.stringify(status, null, 2) + '\n'; + serverWrites = serverWrites.then(() => writeFile(join(runRoot, 'server.json'), bytes, { mode: 0o600 })); + return serverWrites; +} +await writeStatus(); +if (!serve) { + await new Promise(done => registry.close(done)); + console.log(`Prepared ${mode} ${version}; server state: ${join(runRoot, 'server.json')}`); + process.exit(0); +} + +const decide = compatibilityModel(event => { void appendFile(join(runRoot, 'model-events.jsonl'), JSON.stringify({ at: new Date().toISOString(), ...event }) + '\n'); }); +let modelRequests = 0; +const model = createServer(async (request, response) => { + try { + let body = ''; + for await (const chunk of request) body += chunk; + const input = JSON.parse(body); + const number = ++modelRequests; + await appendFile(join(runRoot, 'model-requests.jsonl'), JSON.stringify({ at: new Date().toISOString(), number, path: request.url, protocol: responseProtocol(input, request.url), body: input }) + '\n'); + const reply = decide(input); + sendModelReply(response, input, request.url, reply, `compatibility-261-${number}`); + } catch (error) { + await appendFile(join(runRoot, 'model-errors.log'), String(error.stack ?? error) + '\n'); + response.writeHead(500, { 'content-type': 'application/json' }); + response.end(JSON.stringify({ error: { message: String(error) } })); + } +}); +await new Promise(done => model.listen(0, '127.0.0.1', done)); +env.DEEPSEEK_BASE_URL = `http://127.0.0.1:${model.address().port}`; +let web; +let stopping = false; +let restarting = false; +function launch() { + web = spawn(process.execPath, [cli, 'web', '--no-open', '--host', '127.0.0.1', '--port', '0'], { cwd: workspace, env, stdio: ['ignore', 'pipe', 'pipe'] }); + status = { ...status, serverStarted: true, webPid: web.pid, modelUrl: env.DEEPSEEK_BASE_URL, startedAt: new Date().toISOString() }; + void writeStatus(); + let output = ''; + let reported = false; + const record = chunk => { + output += chunk; + // The bootstrap URL is a credential. Preserve it only in local evidence. + void appendFile(join(runRoot, 'web.log'), chunk, { mode: 0o600 }); + if (output.includes('ctx.settings.register is not a function') && !status.activationFailure) { + status = { ...status, activationFailure: 'ctx.settings.register is not a function' }; + void writeStatus(); + console.log('Captured baseline activation failure: ctx.settings.register is not a function'); + } + const urls = [...output.matchAll(/https?:\/\/127\.0\.0\.1:\d+[^\s\x1b]*/g)].map(match => match[0]); + const url = urls.find(candidate => !candidate.startsWith(registryUrl) && !candidate.startsWith(env.DEEPSEEK_BASE_URL)); + if (url && status.url !== url) { + status = { ...status, url }; + if (!reported) { console.log(`Loopback WebUI available; private server state: ${join(runRoot, 'server.json')}`); reported = true; } + void writeStatus(); + } + }; + web.stdout.on('data', record); + web.stderr.on('data', record); + web.once('error', error => { console.error(error); process.exitCode = 1; void stop(); }); + web.once('exit', code => { status = { ...status, webExitCode: code }; if (!stopping && !restarting) { process.exitCode = code ?? 1; void stop(); } }); +} +async function stop() { + if (stopping) return; + stopping = true; + if (web && web.exitCode === null) { web.kill('SIGTERM'); await new Promise(done => web.once('exit', done)); } + model.closeAllConnections(); + registry.closeAllConnections(); + await Promise.all([new Promise(done => model.close(done)), new Promise(done => registry.close(done))]); + status = { ...status, stoppedAt: new Date().toISOString() }; + await writeStatus(); +} +process.once('SIGINT', () => { void stop(); }); +process.once('SIGTERM', () => { void stop(); }); +process.on('SIGUSR2', async () => { + if (stopping || restarting || !web || web.exitCode !== null) return; + restarting = true; + web.kill('SIGTERM'); + await new Promise(done => web.once('exit', done)); + restarting = false; + if (!stopping) launch(); +}); +console.log(`Starting isolated ${mode} ${version}; private server state: ${join(runRoot, 'server.json')}`); +launch(); diff --git a/dsh/docs/pr-assets/issue-267-settings-migration/harness/prepare-framework.mjs b/dsh/docs/pr-assets/issue-267-settings-migration/harness/prepare-framework.mjs new file mode 100644 index 000000000..3989fe374 --- /dev/null +++ b/dsh/docs/pr-assets/issue-267-settings-migration/harness/prepare-framework.mjs @@ -0,0 +1,56 @@ +import assert from 'node:assert/strict'; +import { mkdir, readFile, writeFile } from 'node:fs/promises'; +import { spawn } from 'node:child_process'; +import { createHash } from 'node:crypto'; +import { join, resolve } from 'node:path'; +import { frameworkPins } from '../../issue-261-dsh-slots/harness/framework-pins.mjs'; + +const args = new Map(); +for (let i = 2; i < process.argv.length; i += 2) args.set(process.argv[i], process.argv[i + 1]); +assert(args.has('--root'), 'supply a disposable --root outside the source checkout'); +const root = resolve(args.get('--root')); +const version = args.get('--framework-version') ?? '0.1.7-alpha.1'; +assert(['0.1.5-rc.2', '0.1.6-alpha.2', '0.1.7-alpha.1'].includes(version)); +const current = version === '0.1.7-alpha.1'; +await mkdir(root, { recursive: true }); +const manifest = { + name: 'issue267-clean-public-framework', version: '0.0.0', private: true, type: 'module', + dependencies: { + '@deepseek-ai/dsh': version, + '@deepseek-ai/dsh-web-app': version, + '@deepseek-ai/cordis': current ? '4.0.3' : '4.0.2', + ...(current ? { '@deepseek-ai/schemastery': '3.18.3' } : {}), + react: '18.3.1', 'react-dom': '18.3.1', + }, +}; +await writeFile(join(root, 'package.json'), JSON.stringify(manifest, null, 2) + '\n'); +const env = { ...process.env }; +for (const key of ['NODE_PATH', 'NODE_OPTIONS', 'npm_config_legacy_peer_deps', 'NPM_CONFIG_LEGACY_PEER_DEPS']) delete env[key]; +const registry = 'https://registry.npmjs.org'; +const argv = ['install', '--registry', registry, '--legacy-peer-deps=false', '--no-audit', '--no-fund']; +const log = []; +await new Promise((done, reject) => { + const child = spawn('npm', argv, { cwd: root, env, stdio: ['ignore', 'pipe', 'pipe'] }); + child.stdout.on('data', chunk => log.push(chunk)); + child.stderr.on('data', chunk => log.push(chunk)); + child.once('error', reject); + child.once('exit', async code => { + await writeFile(join(root, 'install.log'), Buffer.concat(log)); + code === 0 ? done() : reject(new Error(`Public framework install exited ${code}; see ${join(root, 'install.log')}`)); + }); +}); +const pins = await frameworkPins(root, version); +const dsh = Object.entries(pins).filter(([name]) => name.startsWith('@deepseek-ai/dsh')); +assert(dsh.length > 200); +const lockBytes = await readFile(join(root, 'package-lock.json')); +const provenance = { + version, root, argv, legacyPeers: false, registry, + dshPackageCount: dsh.length, + cordis: pins['@deepseek-ai/cordis'], schemastery: pins['@deepseek-ai/schemastery'], + lockSha256: createHash('sha256').update(lockBytes).digest('hex'), + node: process.version, platform: process.platform, arch: process.arch, + completedAt: new Date().toISOString(), +}; +await writeFile(join(root, 'framework-pins.json'), JSON.stringify(pins, null, 2) + '\n'); +await writeFile(join(root, 'framework-provenance.json'), JSON.stringify(provenance, null, 2) + '\n'); +console.log(JSON.stringify(provenance)); diff --git a/dsh/docs/pr-assets/issue-267-settings-migration/settings-verification.json b/dsh/docs/pr-assets/issue-267-settings-migration/settings-verification.json new file mode 100644 index 000000000..fcd48aa16 --- /dev/null +++ b/dsh/docs/pr-assets/issue-267-settings-migration/settings-verification.json @@ -0,0 +1,221 @@ +{ + "verifiedAt": "2026-09-22", + "sourceCommit": "407eac75ee46e677f694e6c318ca9380357fae27", + "scope": "Settings, activation, migration, native CLI; the later Session V4 follow-up is recorded separately.", + "cohorts": [ + { + "version": "0.1.7-alpha.1", + "dshPackageCount": 267, + "mnemonPackageCount": 17, + "publicBytesChecked": 10, + "normalNpmPeers": true, + "webui": { + "workbenchOpened": true, + "settingsSaved": true, + "pageReloadPreserved": true, + "idleReviewMinIntervalMs": 310000, + "autoCaptureEnabled": true, + "lightContextEnabled": false, + "scopedEnabled": true, + "turnBar": false, + "saveAction": false, + "hostRestartVerified": true + } + }, + { + "version": "0.1.5-rc.2", + "dshPackageCount": 234, + "mnemonPackageCount": 17, + "publicBytesChecked": 8, + "normalNpmPeers": true, + "webui": { + "workbenchOpened": true, + "settingsSaved": true, + "pageReloadPreserved": true, + "idleReviewMinIntervalMs": 320000, + "autoCaptureEnabled": true, + "lightContextEnabled": false, + "scopedEnabled": true, + "turnBar": false, + "saveAction": true, + "hostRestartVerified": false + } + }, + { + "version": "0.1.6-alpha.2", + "dshPackageCount": 251, + "mnemonPackageCount": 17, + "publicBytesChecked": 8, + "normalNpmPeers": true, + "webui": { + "workbenchOpened": true, + "settingsSaved": true, + "pageReloadPreserved": true, + "idleReviewMinIntervalMs": 330000, + "autoCaptureEnabled": true, + "lightContextEnabled": false, + "scopedEnabled": true, + "turnBar": false, + "saveAction": true, + "hostRestartVerified": false + } + } + ], + "retainedMigration": { + "publicState": { + "marker": true, + "defaultRecallLimit": 7, + "turnBar": true, + "saveAction": false, + "lightEnabled": true, + "lightMaxProjectionCharacters": 1200, + "autoCaptureEnabled": true, + "documentsEnabled": true, + "scopedEnabled": true, + "foreignScopedOverlayAbsent": true + }, + "allFixtureExpectationsMatch": true, + "publicStateUnchanged": true, + "patchSha256": "abb1325a31201ec2134a9981c2c8163d34f92b5fce43b3f7be58c8c3e93f3857", + "patchBytesUnchanged": true, + "backupSha256": "5205ac9f024eab0f78224ee454e8e5c4145eb29d06f9665cec809288469f52a6", + "backupBytesUnchanged": true, + "backupMatchesOriginalFixture": true, + "rootConfigSha256": "1245122d323f86c2f289076ddb19c0f4c918a05cc29199117ec66322e2d8af70", + "rootUnchanged": true, + "nonRootRowsCount": 21, + "nonRootRowsSha256": "11adf9c0e4776ace5a080877b4bd5066fa38004cefe85aca5480d30fa63202e1", + "nonRootRowsUnchanged": true, + "nonRootRowFingerprintsUnchanged": true, + "welcomeNoticeVersion": "2026-08-13.1" + }, + "nativeCli": { + "version": "0.2.8", + "webuiStatusExecuted": true, + "webuiSpaceCreated": "Issue 267 CLI smoke", + "webuiSpaceEnabled": true, + "embeddingEndpoint": "Not configured; the optional default Ollama endpoint is unavailable.", + "spaceStorageDisplay": "88.0 KB" + }, + "artifacts": [ + { + "name": "dsh-mnemon", + "version": "0.5.12", + "filename": "dsh-mnemon-0.5.12.tgz", + "sha256": "e4c174440665bce87f6ea59df9815f69cb26767aea25cd31a8eb3443a208a1d5", + "integrity": "sha512-QRtXd3BYplLp0Ee4+eNLoqDkaMkwvxpsiVmA5K/3GLwjW83LzCgXYeVNQmlw3SxTIVBgQQCjd9Mg6bTSXu3Ojg==" + }, + { + "name": "dsh-mnemon-provider-byterover", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-byterover-0.5.4.tgz", + "sha256": "94677bb5a27261dd2051f67c9ae5bc41c17ea86a4ea95cbd0baee1728203633f", + "integrity": "sha512-0/ru18uVKNILv3Q2Dx7soOpm+9SrARnbxCsewmQKPy8fvj14zwd7TNLlI9/lV0/vN5SgjKq95PnOvsJZVD/krw==" + }, + { + "name": "dsh-mnemon-provider-hindsight", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-hindsight-0.5.4.tgz", + "sha256": "bf527bc83d51bd7b2e510ed3be59665ae6b3ddbd4e1c3beee16c32235fa0afbb", + "integrity": "sha512-nQ3rQ83J2ugNLXDkG1eYPjpibFANoe3n9OkxfyPCxXkAHHKYdmIZ35mzCFG8sMWsDeSt8y1RezulWjima4ZAVw==" + }, + { + "name": "dsh-mnemon-provider-holographic", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-holographic-0.5.4.tgz", + "sha256": "67f9cc593a7bcf33001e0817a4932e23cf9f0927e90b6bc2b2ccd5ef49b91259", + "integrity": "sha512-ULO6MBVq8rmRMlMnDcqJ26XPsk1K5j3HPjtTXHoBlEy6GdYQ7/2vZLtDQZ9FNjMZ9VT/W6Qj+RAc1HfgIj3bZA==" + }, + { + "name": "dsh-mnemon-provider-honcho", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-honcho-0.5.4.tgz", + "sha256": "c102952aa1b9fdd953b25068663635a9ffad606bb39c68b56eafa538c8fab72d", + "integrity": "sha512-xbbDeshwpiFZHlIUFveMbv6o6xTRY1sjNUvkcXKN/oInxCYdV4sC7Q6pBMRoSoSXYoAgUVExLLXxG+p9Z7uDZA==" + }, + { + "name": "dsh-mnemon-provider-mem0", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-mem0-0.5.4.tgz", + "sha256": "42cd225bb376d387d8e7f5503d64b6a00ddae4f5aea26b495cb9d5aa44dab3b0", + "integrity": "sha512-jriF73KpQp6DoH0RbdfJx+6HzUGEGq1/zy1Y8DmyRaRF62OhBmySGSTHZHasVu1kw61NWzSqIBFWFVn6La0wYQ==" + }, + { + "name": "dsh-mnemon-provider-mnemon-native", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-mnemon-native-0.5.5.tgz", + "sha256": "cb1532ec04e1bc2550456340b453717686c2bc399e742e61be529bdae5e6f812", + "integrity": "sha512-M6v7W2MomcPaPEcf5vUVfJa0j1HUdEmHwdCYR+jmK05doyuP/Vfg/+S2ZnpySKWEGu7jDlgRQbd5hb8RANKahg==" + }, + { + "name": "dsh-mnemon-provider-openviking", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-openviking-0.5.5.tgz", + "sha256": "da3505e93462a7860d331e830930a04d4e2406be5b540333f340f9351346920f", + "integrity": "sha512-7XP4we0/Zl9QasILR5SvEGEONk7YSXj1X9PTpPZ7eS6IgCV3uauhTV5aqZ0d2VPgSR7MYaq1nWnc0MuEZfVbXA==" + }, + { + "name": "dsh-mnemon-provider-retaindb", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-retaindb-0.5.4.tgz", + "sha256": "a7b5f1efb4e68df2d7ccc7e6078cd8c3229c82cf14f0162a196c975cbe19727f", + "integrity": "sha512-wztasXBFvV0M4xnJM0G0K819EMWLB0gVE0rvc4CY7XkmnqTAPoZ/26pZT9WY6ENHj84onkC2ck58xy7eqiX70A==" + }, + { + "name": "dsh-mnemon-provider-supermemory", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-supermemory-0.5.4.tgz", + "sha256": "d04b900f7f9e9359ba53b349bdbc4550fc19b350771e61df3aa451e8f67f9afd", + "integrity": "sha512-hB6G2B5hu1ktMyqP5Al7b9uLVfV876Fs5zrgh5URlaE25e4s3VrJBkzebq5ZuhSocTDYFKMsZL4AWQGLFkb0Rg==" + }, + { + "name": "dsh-mnemon-source-documents", + "version": "0.5.6", + "filename": "dsh-mnemon-source-documents-0.5.6.tgz", + "sha256": "411900a7eef2e2d9b90e83bb4a3669269d150a46142fcfe51cc4ba3819ccb9ec", + "integrity": "sha512-GlSzJs5C4nR1fG7dLRWMx7KOEF5ZDyKbU2UQGaBKXWeQrD5xZKfu/fZUbvUr/yj0HVfRKxbhvwkK1Iv9Oq3BLQ==" + }, + { + "name": "dsh-mnemon-source-memory-spaces", + "version": "0.5.8", + "filename": "dsh-mnemon-source-memory-spaces-0.5.8.tgz", + "sha256": "a98a8b03fd384785406d85548254b1182276057c6f9b98784a7a35cee58f90c6", + "integrity": "sha512-OuNWar7KABXPS+6XKan5FVcJPrjIV9vdeNUkXNzvp9ofc2CRiKqwpBNjDADPemza3XqIeWZQYF6SrBCN6r+1kg==" + }, + { + "name": "dsh-mnemon-source-runtime", + "version": "0.5.8", + "filename": "dsh-mnemon-source-runtime-0.5.8.tgz", + "sha256": "f9cdd113522fc62b08b4993260e03ca7cf71d14f0f2bd188cacb400f6e27f607", + "integrity": "sha512-QPZBv2dvwa7so77NvyAZcAC0YkfXRfRzgTogaLWCbkrBf0Xk39zz0noo+4GgE8idMh0M/oLS6Erb2fErWSz2AQ==" + }, + { + "name": "dsh-mnemon-strategy-auto-capture", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-auto-capture-0.5.4.tgz", + "sha256": "3816601abd83f0c5c679f3e4c9ae96c8f697e8e15f6546e975a96fd7f86b8c02", + "integrity": "sha512-SYHrc0H2Pxgs5dqa48AgoYudnAlkB450H8dqAz7ILiFGEo4V2u6RQuBsF5A7QOJXwV9v3fd0YQ9eAcc5tFds9Q==" + }, + { + "name": "dsh-mnemon-strategy-default-three-tier", + "version": "0.5.5", + "filename": "dsh-mnemon-strategy-default-three-tier-0.5.5.tgz", + "sha256": "52af5caf635f2cf2b165feff1ff3d35d241e6d073771b7b92b3ea37b36ff589f", + "integrity": "sha512-6cg2mz8QyYKYjdk0YnW+IYp2TEQQsZatj89CwsDXU50sN2D8dDal5TZwBl5+qRa5MZj11mIN5ZENWCDcBqTFxw==" + }, + { + "name": "dsh-mnemon-strategy-light-context", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-light-context-0.5.4.tgz", + "sha256": "62947c3ff777fbb8d42724acf2a9c651d2b2302b387bb8579c59cd79f82a9e0d", + "integrity": "sha512-7qbo0e9igj0x03ekcBbAunkldWOme07d/R+tI/U+0P6WsyiPGy8EX0UnqYAervd1oZ8G06+srJ0zDX6tcFX0JA==" + }, + { + "name": "dsh-mnemon-strategy-scoped", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-scoped-0.5.4.tgz", + "sha256": "f3f945add93d2d5f8f48f0e70f5de9360814499a552218af7fa93f574dd3457a", + "integrity": "sha512-aWPYgkexWdmg8LWFddnlnX9HbFRA6E43pbQxMTnkC8YOFja/EAArpqa0McBlffYMZedvCg7FhGWuQV7Qy7nezw==" + } + ] +} diff --git a/dsh/docs/pr-assets/issue-267-settings-migration/type-isolation-verification.json b/dsh/docs/pr-assets/issue-267-settings-migration/type-isolation-verification.json new file mode 100644 index 000000000..de6ddbc41 --- /dev/null +++ b/dsh/docs/pr-assets/issue-267-settings-migration/type-isolation-verification.json @@ -0,0 +1,732 @@ +{ + "verifiedAt": "2026-09-22", + "sourceCommit": "d57e14054a4c523097b382ff36ca6bd3a993ff03", + "cleanSourceCheckout": true, + "previousBrowserSourceCommit": "dc10b5b6a2b39bd3093823741e286592dc640cd6", + "reason": "Clean CI exposed conflicting global Schemastery generics from legacy schemas and the live fork; a private static ESM bridge isolates the published runtime from legacy type builders.", + "typeIsolation": { + "runtimeAlias": { + "name": "schemastery-live", + "target": "npm:@deepseek-ai/schemastery@^3.18.3" + }, + "canonicalDevelopmentDependency": "@deepseek-ai/schemastery@3.18.2", + "officialPackagesModified": false, + "typescriptSourceAliases": false, + "cjsBridge": false, + "topLevelAwaitAdded": false, + "emittedDeclarationLeaks": [], + "existingMainLockPackageAndSnapshotValuesPreserved": true + }, + "localValidation": { + "verify": { + "node": "22.19.0", + "pnpm": "10.13.1", + "result": "pass", + "nativeCli": "0.2.8", + "publishedV4CodecEnabled": true, + "rootTests": { + "passed": 1342, + "skipped": 5 + }, + "pluginTests": { + "passed": 382, + "skipped": 2 + }, + "package": { + "files": 49, + "packedBytes": 308200, + "unpackedBytes": 1370296, + "nodeCompatibleEntries": 12, + "publicTypeDependencies": 28, + "executableHelpEntries": 1 + }, + "logSha256": "a8896f8b52ce80ec571876cf55ecd9b9733361f48db6a75ecb1afd5d4fdf087f" + }, + "verifyPlugins": { + "node": "24.20.0", + "pnpm": "10.13.1", + "result": "pass", + "independentPluginRepositories": 16, + "packedArtifacts": 17, + "externalSdkOnlyConsumer": true, + "threeOptionalStrategiesTogether": true, + "starterOnlyActivation": true, + "logSha256": "c05fe93e0d7e886a4f5a574a1eb65e6e7fc627c06730948640e497ed91a2d7f3" + } + }, + "artifacts": [ + { + "name": "dsh-mnemon", + "version": "0.5.12", + "filename": "dsh-mnemon-0.5.12.tgz", + "sha256": "552eb9c23846e23e958100974888351a74666c471d614d2f098f2426d5a81cb3", + "integrity": "sha512-T8dsk+cwEVC4WwofiQ3sNCv3qOcCWTa6Hl/I6m36N/sIxoeFQS/nR64in8oL/MeJG30+hrGgr6PtXLt+nVmi2g==" + }, + { + "name": "dsh-mnemon-provider-byterover", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-byterover-0.5.4.tgz", + "sha256": "94677bb5a27261dd2051f67c9ae5bc41c17ea86a4ea95cbd0baee1728203633f", + "integrity": "sha512-0/ru18uVKNILv3Q2Dx7soOpm+9SrARnbxCsewmQKPy8fvj14zwd7TNLlI9/lV0/vN5SgjKq95PnOvsJZVD/krw==" + }, + { + "name": "dsh-mnemon-provider-hindsight", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-hindsight-0.5.4.tgz", + "sha256": "bf527bc83d51bd7b2e510ed3be59665ae6b3ddbd4e1c3beee16c32235fa0afbb", + "integrity": "sha512-nQ3rQ83J2ugNLXDkG1eYPjpibFANoe3n9OkxfyPCxXkAHHKYdmIZ35mzCFG8sMWsDeSt8y1RezulWjima4ZAVw==" + }, + { + "name": "dsh-mnemon-provider-holographic", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-holographic-0.5.4.tgz", + "sha256": "67f9cc593a7bcf33001e0817a4932e23cf9f0927e90b6bc2b2ccd5ef49b91259", + "integrity": "sha512-ULO6MBVq8rmRMlMnDcqJ26XPsk1K5j3HPjtTXHoBlEy6GdYQ7/2vZLtDQZ9FNjMZ9VT/W6Qj+RAc1HfgIj3bZA==" + }, + { + "name": "dsh-mnemon-provider-honcho", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-honcho-0.5.4.tgz", + "sha256": "c102952aa1b9fdd953b25068663635a9ffad606bb39c68b56eafa538c8fab72d", + "integrity": "sha512-xbbDeshwpiFZHlIUFveMbv6o6xTRY1sjNUvkcXKN/oInxCYdV4sC7Q6pBMRoSoSXYoAgUVExLLXxG+p9Z7uDZA==" + }, + { + "name": "dsh-mnemon-provider-mem0", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-mem0-0.5.4.tgz", + "sha256": "42cd225bb376d387d8e7f5503d64b6a00ddae4f5aea26b495cb9d5aa44dab3b0", + "integrity": "sha512-jriF73KpQp6DoH0RbdfJx+6HzUGEGq1/zy1Y8DmyRaRF62OhBmySGSTHZHasVu1kw61NWzSqIBFWFVn6La0wYQ==" + }, + { + "name": "dsh-mnemon-provider-mnemon-native", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-mnemon-native-0.5.5.tgz", + "sha256": "cb1532ec04e1bc2550456340b453717686c2bc399e742e61be529bdae5e6f812", + "integrity": "sha512-M6v7W2MomcPaPEcf5vUVfJa0j1HUdEmHwdCYR+jmK05doyuP/Vfg/+S2ZnpySKWEGu7jDlgRQbd5hb8RANKahg==" + }, + { + "name": "dsh-mnemon-provider-openviking", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-openviking-0.5.5.tgz", + "sha256": "da3505e93462a7860d331e830930a04d4e2406be5b540333f340f9351346920f", + "integrity": "sha512-7XP4we0/Zl9QasILR5SvEGEONk7YSXj1X9PTpPZ7eS6IgCV3uauhTV5aqZ0d2VPgSR7MYaq1nWnc0MuEZfVbXA==" + }, + { + "name": "dsh-mnemon-provider-retaindb", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-retaindb-0.5.4.tgz", + "sha256": "a7b5f1efb4e68df2d7ccc7e6078cd8c3229c82cf14f0162a196c975cbe19727f", + "integrity": "sha512-wztasXBFvV0M4xnJM0G0K819EMWLB0gVE0rvc4CY7XkmnqTAPoZ/26pZT9WY6ENHj84onkC2ck58xy7eqiX70A==" + }, + { + "name": "dsh-mnemon-provider-supermemory", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-supermemory-0.5.4.tgz", + "sha256": "d04b900f7f9e9359ba53b349bdbc4550fc19b350771e61df3aa451e8f67f9afd", + "integrity": "sha512-hB6G2B5hu1ktMyqP5Al7b9uLVfV876Fs5zrgh5URlaE25e4s3VrJBkzebq5ZuhSocTDYFKMsZL4AWQGLFkb0Rg==" + }, + { + "name": "dsh-mnemon-source-documents", + "version": "0.5.6", + "filename": "dsh-mnemon-source-documents-0.5.6.tgz", + "sha256": "411900a7eef2e2d9b90e83bb4a3669269d150a46142fcfe51cc4ba3819ccb9ec", + "integrity": "sha512-GlSzJs5C4nR1fG7dLRWMx7KOEF5ZDyKbU2UQGaBKXWeQrD5xZKfu/fZUbvUr/yj0HVfRKxbhvwkK1Iv9Oq3BLQ==" + }, + { + "name": "dsh-mnemon-source-memory-spaces", + "version": "0.5.8", + "filename": "dsh-mnemon-source-memory-spaces-0.5.8.tgz", + "sha256": "a98a8b03fd384785406d85548254b1182276057c6f9b98784a7a35cee58f90c6", + "integrity": "sha512-OuNWar7KABXPS+6XKan5FVcJPrjIV9vdeNUkXNzvp9ofc2CRiKqwpBNjDADPemza3XqIeWZQYF6SrBCN6r+1kg==" + }, + { + "name": "dsh-mnemon-source-runtime", + "version": "0.5.8", + "filename": "dsh-mnemon-source-runtime-0.5.8.tgz", + "sha256": "f9cdd113522fc62b08b4993260e03ca7cf71d14f0f2bd188cacb400f6e27f607", + "integrity": "sha512-QPZBv2dvwa7so77NvyAZcAC0YkfXRfRzgTogaLWCbkrBf0Xk39zz0noo+4GgE8idMh0M/oLS6Erb2fErWSz2AQ==" + }, + { + "name": "dsh-mnemon-strategy-auto-capture", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-auto-capture-0.5.4.tgz", + "sha256": "3816601abd83f0c5c679f3e4c9ae96c8f697e8e15f6546e975a96fd7f86b8c02", + "integrity": "sha512-SYHrc0H2Pxgs5dqa48AgoYudnAlkB450H8dqAz7ILiFGEo4V2u6RQuBsF5A7QOJXwV9v3fd0YQ9eAcc5tFds9Q==" + }, + { + "name": "dsh-mnemon-strategy-default-three-tier", + "version": "0.5.5", + "filename": "dsh-mnemon-strategy-default-three-tier-0.5.5.tgz", + "sha256": "52af5caf635f2cf2b165feff1ff3d35d241e6d073771b7b92b3ea37b36ff589f", + "integrity": "sha512-6cg2mz8QyYKYjdk0YnW+IYp2TEQQsZatj89CwsDXU50sN2D8dDal5TZwBl5+qRa5MZj11mIN5ZENWCDcBqTFxw==" + }, + { + "name": "dsh-mnemon-strategy-light-context", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-light-context-0.5.4.tgz", + "sha256": "62947c3ff777fbb8d42724acf2a9c651d2b2302b387bb8579c59cd79f82a9e0d", + "integrity": "sha512-7qbo0e9igj0x03ekcBbAunkldWOme07d/R+tI/U+0P6WsyiPGy8EX0UnqYAervd1oZ8G06+srJ0zDX6tcFX0JA==" + }, + { + "name": "dsh-mnemon-strategy-scoped", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-scoped-0.5.4.tgz", + "sha256": "f3f945add93d2d5f8f48f0e70f5de9360814499a552218af7fa93f574dd3457a", + "integrity": "sha512-aWPYgkexWdmg8LWFddnlnX9HbFRA6E43pbQxMTnkC8YOFja/EAArpqa0McBlffYMZedvCg7FhGWuQV7Qy7nezw==" + } + ], + "cohorts": [ + { + "result": "pass", + "normalPeers": true, + "freshInstall": true, + "frameworkVersion": "0.1.5-rc.2", + "dshPackageCount": 234, + "mnemonPackageCount": 17, + "artifactCount": 17, + "verifiedArtifactHashes": true, + "verifiedLockIntegrities": true, + "publicBytesChecked": 8, + "verifiedPublicBytesAgain": true, + "commit": "d57e14054a4c523097b382ff36ca6bd3a993ff03", + "cleanCommit": true, + "rootSha256": "552eb9c23846e23e958100974888351a74666c471d614d2f098f2426d5a81cb3", + "schemaAlias": { + "alias": "schemastery-live", + "declaration": "npm:@deepseek-ai/schemastery@^3.18.3", + "actualName": "@deepseek-ai/schemastery", + "actualVersion": "3.18.3", + "devCanonicalVersion": "3.18.2", + "officialIntegrity": "sha512-plZIZADuGLT/3PoYmUaEDZd0vG1rVahU89MevnxXsyQhFEi2D6DUuTJel7FUb3cUM9aCzqGMN99sHJe1RIushA==", + "officialArchiveSha256": "3e8c63ab919f13c75773a2cbcd3ae72c7bd9a5fb0a6f753cf1f427a996727644", + "officialFilesCompared": 8, + "allOfficialFilesByteIdentical": true, + "staticEsmImportInPackedRoot": true, + "esmExport": "./lib/index.mjs", + "esmSha256": "cfcbe46c7416bdb35ce891c252152bbf519e08199bede5930dd7cb880c65f1ef", + "cjsSha256": "aca1c1a47c7b6034feee01501aefca3e6dd97a0bac4fe334fd30647ae5a9e480" + }, + "nativeCli": "mnemon version 0.2.8", + "dshCli": "0.1.5-rc.2", + "node": "v25.1.0", + "checkedAt": "2026-09-22T13:28:05.493Z" + }, + { + "result": "pass", + "normalPeers": true, + "freshInstall": true, + "frameworkVersion": "0.1.6-alpha.2", + "dshPackageCount": 251, + "mnemonPackageCount": 17, + "artifactCount": 17, + "verifiedArtifactHashes": true, + "verifiedLockIntegrities": true, + "publicBytesChecked": 8, + "verifiedPublicBytesAgain": true, + "commit": "d57e14054a4c523097b382ff36ca6bd3a993ff03", + "cleanCommit": true, + "rootSha256": "552eb9c23846e23e958100974888351a74666c471d614d2f098f2426d5a81cb3", + "schemaAlias": { + "alias": "schemastery-live", + "declaration": "npm:@deepseek-ai/schemastery@^3.18.3", + "actualName": "@deepseek-ai/schemastery", + "actualVersion": "3.18.3", + "devCanonicalVersion": "3.18.2", + "officialIntegrity": "sha512-plZIZADuGLT/3PoYmUaEDZd0vG1rVahU89MevnxXsyQhFEi2D6DUuTJel7FUb3cUM9aCzqGMN99sHJe1RIushA==", + "officialArchiveSha256": "3e8c63ab919f13c75773a2cbcd3ae72c7bd9a5fb0a6f753cf1f427a996727644", + "officialFilesCompared": 8, + "allOfficialFilesByteIdentical": true, + "staticEsmImportInPackedRoot": true, + "esmExport": "./lib/index.mjs", + "esmSha256": "cfcbe46c7416bdb35ce891c252152bbf519e08199bede5930dd7cb880c65f1ef", + "cjsSha256": "aca1c1a47c7b6034feee01501aefca3e6dd97a0bac4fe334fd30647ae5a9e480" + }, + "nativeCli": "mnemon version 0.2.8", + "dshCli": "0.1.6-alpha.2", + "node": "v25.1.0", + "checkedAt": "2026-09-22T13:28:05.510Z" + }, + { + "result": "pass", + "normalPeers": true, + "freshInstall": true, + "frameworkVersion": "0.1.7-alpha.1", + "dshPackageCount": 267, + "mnemonPackageCount": 17, + "artifactCount": 17, + "verifiedArtifactHashes": true, + "verifiedLockIntegrities": true, + "publicBytesChecked": 10, + "verifiedPublicBytesAgain": true, + "commit": "d57e14054a4c523097b382ff36ca6bd3a993ff03", + "cleanCommit": true, + "rootSha256": "552eb9c23846e23e958100974888351a74666c471d614d2f098f2426d5a81cb3", + "schemaAlias": { + "alias": "schemastery-live", + "declaration": "npm:@deepseek-ai/schemastery@^3.18.3", + "actualName": "@deepseek-ai/schemastery", + "actualVersion": "3.18.3", + "devCanonicalVersion": "3.18.2", + "officialIntegrity": "sha512-plZIZADuGLT/3PoYmUaEDZd0vG1rVahU89MevnxXsyQhFEi2D6DUuTJel7FUb3cUM9aCzqGMN99sHJe1RIushA==", + "officialArchiveSha256": "3e8c63ab919f13c75773a2cbcd3ae72c7bd9a5fb0a6f753cf1f427a996727644", + "officialFilesCompared": 8, + "allOfficialFilesByteIdentical": true, + "staticEsmImportInPackedRoot": true, + "esmExport": "./lib/index.mjs", + "esmSha256": "cfcbe46c7416bdb35ce891c252152bbf519e08199bede5930dd7cb880c65f1ef", + "cjsSha256": "aca1c1a47c7b6034feee01501aefca3e6dd97a0bac4fe334fd30647ae5a9e480" + }, + "nativeCli": "mnemon version 0.2.8", + "dshCli": "0.1.7-alpha.1", + "node": "v25.1.0", + "checkedAt": "2026-09-22T13:28:05.523Z" + } + ], + "browserVerification": { + "driver": "Codex CUA Chrome browser operations", + "model": "deterministic loopback fixture; no external model provider", + "frameworkVersions": [ + "0.1.5-rc.2", + "0.1.6-alpha.2", + "0.1.7-alpha.1" + ], + "completedToolsOnEach": [ + "mnemon_runtime_memory action:add", + "mnemon_status" + ], + "fixturePrompt": "compatibility-261", + "fixtureTitle": "Issue 261 compatibility", + "fixtureReuseNote": "The prompt and title are inherited from the deterministic fixture; all consumers and artifacts in this verification belong to Issue 267.", + "alpha7DraftAcrossPluginWrite": { + "prepared": [ + "displayMode=builtin", + "intervalMs=350000", + "turnBar=false" + ], + "immediateWrite": "Light Context disabled", + "saveResult": "saved and live without a false revision conflict" + }, + "alpha7AfterCompleteHostRestart": { + "profileOrDataRecreated": false, + "displayMode": "builtin", + "intervalMs": 350000, + "conversationInteraction": { + "turnBar": false, + "saveAction": true + }, + "strategies": { + "lightContext": false, + "autoCapture": true, + "scoped": true + }, + "threeSourcesEnabled": true, + "conversationHistoryRestored": true, + "runtimeMemoryCount": 1, + "builtinWorkbenchConnected": true, + "nativeProviderReady": true + } + }, + "screenshots": [ + { + "file": "v4-alpha7-session-tools.png", + "sha256": "60c21ac88080b6aff998764d3274445a6665f48834815a158714bb696d12acab" + }, + { + "file": "v4-rc2-session-tools.png", + "sha256": "9a64f290a90eecc0a3bc5104de2973a7962fd84d12bffd25488190a60d1f7004" + }, + { + "file": "v4-alpha2-session-tools.png", + "sha256": "db154c9d38a32b3727357b54aadfa84ad034c1878209d7122283d7b52bc88c18" + }, + { + "file": "v4-alpha7-settings-restart.png", + "sha256": "2e0d64c820b764220609e4175fd903fa120795d55ef1efa35ce4857b1ef619ec" + }, + { + "file": "v4-alpha7-strategies-restart.png", + "sha256": "bbeaea7bc8d90f97a4234b1ebe11036d23c181f8a6dacee2a29c94b4575cb0db" + }, + { + "file": "v4-alpha7-builtin-restart.png", + "sha256": "5302afe30ceee4c2ad72dd389123dd37d28f7ba9fddb5206918de84cc0e4684d" + } + ], + "ci": { + "sourceCommit": "d57e14054a4c523097b382ff36ca6bd3a993ff03", + "conclusion": "success", + "run": "https://github.com/omdsh-dev/dsh-mnemon/actions/runs/35733033143", + "sourceNode22": "https://github.com/omdsh-dev/dsh-mnemon/actions/runs/35733033143/job/106763140386", + "packedNode24": "https://github.com/omdsh-dev/dsh-mnemon/actions/runs/35733033143/job/106763140029" + }, + "limits": [ + "The browser consumers ran on macOS with Node 25.1.0; final full local source and packed checks ran on Node 22.19.0 and 24.20.0.", + "The minimal DSH fixture omits terminal, deliverables and workspace-change services; general permission-preset requests and the alpha.2 terminal retry button are outside Mnemon verification.", + "No external model quality, cloud Provider account or embedding endpoint was exercised.", + "Legacy migration and native memory-space creation are recorded in settings-verification.json and final-verification.json with their earlier implementation commits.", + "Bootstrap URLs, raw server/model logs and private data are excluded." + ], + "persistenceAudit": { + "audit": "Issue 267 final-v4 read-only persistence verification", + "checkedAt": "2026-09-22T13:42:55.980Z", + "expectedRuntimeCommit": "d57e14054a4c523097b382ff36ca6bd3a993ff03", + "fixtureModel": "Deterministic compatibility-261 model exercises real Mnemon Runtime add and status tools.", + "artifacts": { + "expectedCount": 17, + "actualTarballCount": 17, + "rootArchiveSha256": "552eb9c23846e23e958100974888351a74666c471d614d2f098f2426d5a81cb3", + "packedRootIndexSha256": "efcc45beeed1efe215f4e4d1eb89529c5b0043e2c0877742359bd3fe67f943f2" + }, + "cohorts": [ + { + "cohort": "alpha7", + "frameworkVersion": "0.1.7-alpha.1", + "artifactCommit": "d57e14054a4c523097b382ff36ca6bd3a993ff03", + "artifactCount": 17, + "allArtifactFilesMatchRecordedHashes": true, + "artifactSetMatchesFirstCohort": true, + "rootArchiveSha256": "552eb9c23846e23e958100974888351a74666c471d614d2f098f2426d5a81cb3", + "installedRootIndexSha256": "efcc45beeed1efe215f4e4d1eb89529c5b0043e2c0877742359bd3fe67f943f2", + "installedRootMatchesPackedRootIndex": true, + "cli": { + "recordedDshVersion": "0.1.7-alpha.1", + "recordedNativeVersion": "0.2.8", + "dshVersionMatchesCohort": true, + "nativeExecutableSha256": "f8f21151ce9777983b8d2dfb9cbea1a00b223de6063659af232d04aa57cedad0", + "probeSource": "Fixture preparation version probes; actual runtime/status receipts are checked below." + }, + "runtimeMemory": { + "count": 1, + "exactlyOneFixtureMemory": true, + "target": "memory", + "importance": "normal", + "contentSha256": "b4b99ca1dd86117d5ecfe875ed2af85d6d38486722167d114294cb52532e5045", + "fileSha256": "4d758f7e9555949bec15eb8e5b547f001467473a2e195f3a931c126b73fd0067" + }, + "sessions": [ + { + "ordinal": 1, + "formatVersion": 4, + "compressedSha256": "9e21444892d730c9a9e3ab0859c10faf932d4f8017c9e3de44b82c9995e4f33e", + "frameCount": 13, + "allFramesDecoded": true, + "eventCount": 33, + "sourceKinds": [ + "dsh-mnemon", + "fallback", + "model", + "provider", + "runtime-context", + "system-prompt", + "tool", + "user" + ], + "mnemonSourceMessageCount": 2, + "toolCalls": [ + { + "tool": "mnemon_runtime_memory", + "action": "add", + "target": "memory", + "contentMatchesFixture": true + }, + { + "tool": "mnemon_status" + } + ], + "toolReceipts": [ + { + "tool": "mnemon_runtime_memory", + "success": true, + "target": "memory", + "entryCount": 1, + "sourceTypeId": "runtime" + }, + { + "tool": "mnemon_status", + "healthy": true, + "commandFound": true, + "writeEnabled": true + } + ], + "completedTurnCount": 1, + "runtimeAddSucceeded": true, + "statusHealthy": true + }, + { + "ordinal": 2, + "formatVersion": 4, + "compressedSha256": "651d182e36fef439bb8981c57f09293630290630094ab726e58003ce6367684f", + "frameCount": 1, + "allFramesDecoded": true, + "eventCount": 1, + "sourceKinds": [], + "mnemonSourceMessageCount": 0, + "toolCalls": [], + "toolReceipts": [], + "completedTurnCount": 0, + "runtimeAddSucceeded": false, + "statusHealthy": false + } + ], + "sessionTotals": { + "count": 2, + "mnemonSourceMessageCount": 2, + "runtimeAddCallCount": 1, + "statusCallCount": 1, + "successfulFixtureSessionCount": 1 + }, + "mnemonLogScan": { + "scope": "Lines naming Mnemon and error/failed markers; not a whole-host error audit.", + "candidateCount": 0 + }, + "afterRestartProfile": { + "settings": { + "displayMode": "builtin", + "minIntervalMs": 350000, + "turnBar": false, + "saveAction": true, + "light": { + "id": "mnemon-strategy-light-context", + "nativeDisabled": false, + "viewOverridePresent": true, + "viewEnabled": false, + "resolvedStoredEnabled": false + }, + "autoCapture": { + "id": "mnemon-strategy-auto-capture", + "nativeDisabled": false, + "viewOverridePresent": false, + "resolvedStoredEnabled": true + }, + "scoped": { + "id": "mnemon-strategy-scoped", + "nativeDisabled": false, + "viewOverridePresent": false, + "resolvedStoredEnabled": true + }, + "memoryViewEntryCount": 1 + }, + "patchSha256": "17248e61632a8f54187a41f9a23345cbee0e2b81c5d3d44d871838e94db9c47f", + "allExpectedValuesMatch": true + }, + "restartEvidence": { + "reportedMechanism": "Parent sent SIGUSR2 to the existing harness; this audit made no process-control calls.", + "harnessPid": 72589, + "initialHarnessPid": 72589, + "initialWebPid": 73834, + "currentWebPid": 74967, + "initialActivationCheckedAt": "2026-09-22T13:28:05.523Z", + "currentWebStartedAt": "2026-09-22T13:35:56.638Z", + "currentProcess": { + "pid": 74967, + "parentPid": 72589, + "startedAtLocal": "Tue Sep 22 21:35:56 2026" + }, + "sameHarness": true, + "webChildReplaced": true, + "currentChildOwnedByHarness": true, + "sessionWithToolsPersistedBeforeCurrentWebStart": true, + "runtimeMemoryFilePredatesCurrentWebStart": false, + "runtimeMemoryTimestampNote": "The runtime JSON modification time is after current child startup. No pre-restart memory-file byte snapshot was captured, so this audit makes no memory-file byte-idempotence claim." + } + }, + { + "cohort": "alpha2", + "frameworkVersion": "0.1.6-alpha.2", + "artifactCommit": "d57e14054a4c523097b382ff36ca6bd3a993ff03", + "artifactCount": 17, + "allArtifactFilesMatchRecordedHashes": true, + "artifactSetMatchesFirstCohort": true, + "rootArchiveSha256": "552eb9c23846e23e958100974888351a74666c471d614d2f098f2426d5a81cb3", + "installedRootIndexSha256": "efcc45beeed1efe215f4e4d1eb89529c5b0043e2c0877742359bd3fe67f943f2", + "installedRootMatchesPackedRootIndex": true, + "cli": { + "recordedDshVersion": "0.1.6-alpha.2", + "recordedNativeVersion": "0.2.8", + "dshVersionMatchesCohort": true, + "nativeExecutableSha256": "f8f21151ce9777983b8d2dfb9cbea1a00b223de6063659af232d04aa57cedad0", + "probeSource": "Fixture preparation version probes; actual runtime/status receipts are checked below." + }, + "runtimeMemory": { + "count": 1, + "exactlyOneFixtureMemory": true, + "target": "memory", + "importance": "normal", + "contentSha256": "b4b99ca1dd86117d5ecfe875ed2af85d6d38486722167d114294cb52532e5045", + "fileSha256": "fac1fa14df37829efd534b64744f02654c1875d0dd08b61150c8d693d937e733" + }, + "sessions": [ + { + "ordinal": 1, + "formatVersion": 3, + "compressedSha256": "3abc6bfa016e575241f7f6a11e8663f3ad3e0761b8cf9ded84c455037dbb4368", + "frameCount": 12, + "allFramesDecoded": true, + "eventCount": 32, + "sourceKinds": [ + "dsh-mnemon", + "fallback", + "model", + "plugin", + "provider", + "tool", + "user" + ], + "mnemonSourceMessageCount": 2, + "toolCalls": [ + { + "tool": "mnemon_runtime_memory", + "action": "add", + "target": "memory", + "contentMatchesFixture": true + }, + { + "tool": "mnemon_status" + } + ], + "toolReceipts": [ + { + "tool": "mnemon_runtime_memory", + "success": true, + "target": "memory", + "entryCount": 1, + "sourceTypeId": "runtime" + }, + { + "tool": "mnemon_status", + "healthy": true, + "commandFound": true, + "writeEnabled": true + } + ], + "completedTurnCount": 1, + "runtimeAddSucceeded": true, + "statusHealthy": true + } + ], + "sessionTotals": { + "count": 1, + "mnemonSourceMessageCount": 2, + "runtimeAddCallCount": 1, + "statusCallCount": 1, + "successfulFixtureSessionCount": 1 + }, + "mnemonLogScan": { + "scope": "Lines naming Mnemon and error/failed markers; not a whole-host error audit.", + "candidateCount": 0 + } + }, + { + "cohort": "rc2", + "frameworkVersion": "0.1.5-rc.2", + "artifactCommit": "d57e14054a4c523097b382ff36ca6bd3a993ff03", + "artifactCount": 17, + "allArtifactFilesMatchRecordedHashes": true, + "artifactSetMatchesFirstCohort": true, + "rootArchiveSha256": "552eb9c23846e23e958100974888351a74666c471d614d2f098f2426d5a81cb3", + "installedRootIndexSha256": "efcc45beeed1efe215f4e4d1eb89529c5b0043e2c0877742359bd3fe67f943f2", + "installedRootMatchesPackedRootIndex": true, + "cli": { + "recordedDshVersion": "0.1.5-rc.2", + "recordedNativeVersion": "0.2.8", + "dshVersionMatchesCohort": true, + "nativeExecutableSha256": "f8f21151ce9777983b8d2dfb9cbea1a00b223de6063659af232d04aa57cedad0", + "probeSource": "Fixture preparation version probes; actual runtime/status receipts are checked below." + }, + "runtimeMemory": { + "count": 1, + "exactlyOneFixtureMemory": true, + "target": "memory", + "importance": "normal", + "contentSha256": "b4b99ca1dd86117d5ecfe875ed2af85d6d38486722167d114294cb52532e5045", + "fileSha256": "44dcea2ab462c8f12c87757a1dfcf594f455fdcdb3079de4d03ed029ce2e917b" + }, + "sessions": [ + { + "ordinal": 1, + "formatVersion": 3, + "compressedSha256": "25eb7acffbf65ca817ef25232a195123369d237d2df0a8bc6634f31617992b3c", + "frameCount": 12, + "allFramesDecoded": true, + "eventCount": 28, + "sourceKinds": [ + "dsh-mnemon", + "fallback", + "model", + "plugin", + "provider", + "tool", + "user" + ], + "mnemonSourceMessageCount": 2, + "toolCalls": [ + { + "tool": "mnemon_runtime_memory", + "action": "add", + "target": "memory", + "contentMatchesFixture": true + }, + { + "tool": "mnemon_status" + } + ], + "toolReceipts": [ + { + "tool": "mnemon_runtime_memory", + "success": true, + "target": "memory", + "entryCount": 1, + "sourceTypeId": "runtime" + }, + { + "tool": "mnemon_status", + "healthy": true, + "commandFound": true, + "writeEnabled": true + } + ], + "completedTurnCount": 1, + "runtimeAddSucceeded": true, + "statusHealthy": true + } + ], + "sessionTotals": { + "count": 1, + "mnemonSourceMessageCount": 2, + "runtimeAddCallCount": 1, + "statusCallCount": 1, + "successfulFixtureSessionCount": 1 + }, + "mnemonLogScan": { + "scope": "Lines naming Mnemon and error/failed markers; not a whole-host error audit.", + "candidateCount": 0 + } + } + ], + "checks": { + "allCohortsUseExpectedCommit": true, + "exactly17Artifacts": true, + "allArtifactHashesMatch": true, + "allInstalledRootBytesMatchArchive": true, + "allHaveExactlyOneFixtureMemory": true, + "allHaveOnePersistedRuntimeAddAndStatus": true, + "allPersistMnemonSourceKind": true, + "alpha7PostRestartSettingsMatch": true, + "alpha7WebChildReplacedUnderSameHarness": true, + "zeroMnemonErrorCandidates": true + }, + "verifiedScope": [ + "Exact local package archive hashes and installed root module bytes for three published DSH cohorts.", + "Actual persisted session frames, Mnemon source kinds, runtime add success, healthy CLI/status receipt, and the resulting single fixture memory.", + "Alpha7 persisted Core/UI/View settings and absence of unwanted AutoCapture or Scoped overrides after host child replacement." + ], + "limitations": [ + "This is an isolated deterministic-model fixture, not a real external model/provider or production-account test.", + "Version strings come from fixture preparation probes; native command availability and health are additionally confirmed in actual persisted tool receipts.", + "The read-only audit does not operate a browser; the parent separately performed the reported WebUI actions and post-restart visual checks.", + "No pre-restart v4 byte snapshot was captured by this audit. It verifies settings values, exact fixture-memory content, and the pre-restart persisted tool session after restart, not before/after byte identity.", + "Restart evidence compares the activation snapshot with current metadata and OS PID/PPID; the specific SIGUSR2 dispatch is parent-reported.", + "The error scan is limited to Mnemon-named error/failed lines and does not assert all host logs are error-free." + ] + } +} diff --git a/dsh/docs/pr-assets/issue-271-user-key/README.md b/dsh/docs/pr-assets/issue-271-user-key/README.md new file mode 100644 index 000000000..b9d740bde --- /dev/null +++ b/dsh/docs/pr-assets/issue-271-user-key/README.md @@ -0,0 +1,71 @@ +# OpenViking user-key discovery — issue 271 + +[简体中文](./README.zh-CN.md) + +The baseline is `84d469ffa838a36fa579295d94029fcac8ac058e`. A user key can use OpenViking's data API while every `/api/v1/admin/*` route is denied. Baseline discovery still requests admin users when an account is supplied, so configuring an account alone does not allow saving the service. + +The regression first failed on the baseline driver with `API is currently unavailable due to access restrictions`. The fixed driver opts into one-owner data-plane discovery only when `discoveryUser` is nonempty. It requires an account and API key, validates the selected root with read-only `fs/ls`, and leaves configuration unchanged on failure. Subsequent reads, exact writes and deletion omit the trusted account/user headers and reject a mismatched memory owner. The existing admin path remains the default. + +## Public contract + +The reviewed upstream revision is [OpenViking `4edc30b0`](https://github.com/volcengine/OpenViking/tree/4edc30b068934893bc94a4e1b8e87bab2100bce5). Its [authentication guide](https://github.com/volcengine/OpenViking/blob/4edc30b068934893bc94a4e1b8e87bab2100bce5/docs/en/guides/04-authentication.md) says `api_key` mode derives the tenant identity from the key and rejects account/user identity headers. Its [filesystem router](https://github.com/volcengine/OpenViking/blob/4edc30b068934893bc94a4e1b8e87bab2100bce5/openviking/server/routers/filesystem.py) exposes scoped `fs/ls` with the request context. The configured account names the local projection; it does not prove or override the key's account. + +## Automated checks + +- The standalone Provider regression covers denied admin routes, invalid/incomplete configuration, malformed discovery responses, denied data access, cancellation, timeout, scoped subsequent operations, owner mismatch and unchanged default enumeration. +- The composed Host regression mounts two instances of the real OpenViking plugin alongside Native, Holographic, Runtime and Documents. It verifies secret redaction, independent accounts with the same user name, exact write/search/delete and atomic rejection of a replacement user. Separate real-CLI checks cover Native Source create/write/recall/forget and exact archival to two Native namespaces in one View; see the [CLI composition record](./cli-composition.json). +- The actual generic settings form submits the field to its owning instance and renders success or rejection. The bilingual presentation fingerprint records the single added field label in each locale. +- Full `verify` passed with Node 24.20.0, pnpm 10.13.1 and official Mnemon CLI 0.2.9 in disposable stores: 1,352 root tests and 402 plugin tests passed, with 8 documented opt-in/platform skips. OpenViking includes 69 passing tests, of which 20 cover the new path. The subsequent `verify:plugins --skip-build` passed for all 16 independent plugins, 17 artifacts, the external SDK consumer and real DSH composition with package concurrency set to one. See the [aggregate verification record](./verification.json). + +Initial concurrent verification attempts exceeded existing timing fences/timeouts. The complete unchanged suite passed in an exclusive window with the documented Vitest worker limits set to one; no test timeout or performance budget changed. The [baseline](./artifacts-before.json) and [fixed](./artifacts-after.json) manifests identify all 17 tarballs by SHA-256, with hashes of the changed production sources. + +```sh +export VITEST_MAX_FORKS=1 VITEST_MIN_FORKS=1 +export VITEST_MAX_THREADS=1 VITEST_MIN_THREADS=1 +export MNEMON_PLUGIN_VERIFY_CONCURRENCY=1 +export MNEMON_NATIVE_TEST_CLI=/absolute/path/to/mnemon +pnpm run verify +pnpm run verify:plugins --skip-build +``` + +## Reproduce the browser comparison + +The [harness](./harness/serve.mjs) composes a deterministic loopback OpenViking fixture with the existing [packed DSH harness](../issue-267-settings-migration/harness/packed-e2e.mjs). Pack the baseline and fixed revisions separately with [pack-artifacts.mjs](../issue-261-dsh-slots/harness/pack-artifacts.mjs). For each artifact set, use a separate disposable run directory: + +```sh +node docs/pr-assets/issue-271-user-key/harness/serve.mjs \ + --artifacts /tmp/issue271/artifacts \ + --run-root /tmp/issue271/browser \ + --framework-root /tmp/issue271/public-dsh \ + --native-cli /tmp/issue271/mnemon +``` + +The framework must be an unchanged, normal-peer install of published DSH `0.1.7-alpha.1`. The harness normal-installs all 17 Mnemon tarballs into another isolated profile and uses the supplied real CLI. Both fresh profiles passed checks for the 267-package DSH cohort and 10 selected official package files, recorded in the [runtime proof](./browser-runtime.json). It writes the bootstrap URL and generated fixture credential only to local mode-`0600` JSON files; do not commit or screenshot those values. `provider-evidence.json` records synthetic data and redacted request metadata. + +1. In baseline **Settings → Memory System → OpenViking**, save with the fixture endpoint/key and an empty account, then with its account. Both attempts fail on admin routes. +2. In the fixed profile, fill the account and **User key owner (skip admin)** with the fixture's supplied user. Saving succeeds and creates one active mapping. Browse/search the synthetic canary in Memory Spaces. +3. Change the user to `bob`. Saving is rejected; the original `alice` mapping remains. Browse and search its canary, then create an independent Runtime memory. + +The helper's `fixed` mode selects normal peer installation for both artifact revisions; it does not alter either artifact set. The accepted fixed run uses the production sources in implementation commit `4ed532540a6353e87be08f744934d9c51f829c5b`. + +The real in-app browser reproduced both baseline failures using official DSH `0.1.7-alpha.1`, the 17 baseline tarballs and Mnemon CLI `0.2.9`. The [browser record](./browser-before.json) and [redacted provider trace](./provider-before.json) distinguish `GET /api/v1/admin/accounts` with an empty account from `GET /api/v1/admin/accounts/fixture/users` with the account filled. Both return the controlled access-restriction error. The `account`/`user` values in those request records describe the fixture identity bound to the generated key; `accountField` records the actual form input. Screenshots keep the key masked and omit the bootstrap URL. + +![Baseline save rejected with account empty](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-271-user-key/before-account-empty.png) + +![Baseline save still rejected with account supplied](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-271-user-key/before-account-filled.png) + +The fixed GUI saved `alice` successfully. A subsequent save for `bob` was rejected, after which browsing and direct search still returned the exact `alice` canary. Runtime independently stored one composition canary. The [browser acceptance record](./browser-after.json) verifies the persisted original key, endpoint, account, enabled service and single active `alice` projection using booleans only; `bob` was not persisted and the registry retained mode `0600`. The [provider trace](./provider-after.json) contains 10 requests for `alice` and one rejected `bob` probe, with zero Admin calls and zero account/user identity headers. The synthetic remote content was unchanged. All disposable WebUI/fixture processes were stopped after acceptance. + +![Fixed user-key service saved](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-271-user-key/after-user-key-saved.png) + +![Unauthorized replacement user rejected](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-271-user-key/after-invalid-user-rejected.png) + +![Original alice content remains readable after rejection](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-271-user-key/after-user-memory-content.png) + +![Direct search returns the exact original canary](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-271-user-key/after-user-memory-search.png) + +![Runtime remains independently writable](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-271-user-key/after-runtime-coexistence.png) + +## Limits and data effects + +This is a deterministic provider protocol fixture, not validation of an actual Volcengine account, external model, embedding service or live cloud availability. A readable existing root does not establish write permission. Missing roots and unavailable data APIs are rejected; no remote namespace is created during discovery. Existing persistence formats and RPC permissions are unchanged. Disable/remove the new field before downgrading to a compatible admin setup; no remote content is deleted by discovery or downgrade. The general getting-started guides were reviewed and retain their existing link to the updated provider setup guide. diff --git a/dsh/docs/pr-assets/issue-271-user-key/README.zh-CN.md b/dsh/docs/pr-assets/issue-271-user-key/README.zh-CN.md new file mode 100644 index 000000000..03833c142 --- /dev/null +++ b/dsh/docs/pr-assets/issue-271-user-key/README.zh-CN.md @@ -0,0 +1,71 @@ +# OpenViking user-key 发现 — issue 271 + +[English](./README.md) + +基线为 `84d469ffa838a36fa579295d94029fcac8ac058e`。user key 可访问 OpenViking 数据 API,但全部 `/api/v1/admin/*` 路由被拒绝。基线即使配置账号仍会枚举 admin users,因此单独填写 Account 无法保存服务。 + +回归先在基线驱动上以 `API is currently unavailable due to access restrictions` 失败。修复仅在 `discoveryUser` 非空时显式采用单用户数据面发现,要求账号与 API key,通过只读 `fs/ls` 校验所选根,失败时保留原配置。后续读取、精确写入和删除不发送 trusted account/user 身份请求头,并拒绝不匹配的空间用户。默认仍为原有 admin 发现。 + +## 公开契约 + +核对的上游版本为 [OpenViking `4edc30b0`](https://github.com/volcengine/OpenViking/tree/4edc30b068934893bc94a4e1b8e87bab2100bce5)。其[身份验证指南](https://github.com/volcengine/OpenViking/blob/4edc30b068934893bc94a4e1b8e87bab2100bce5/docs/en/guides/04-authentication.md)明确说明 `api_key` 模式从 key 解析租户身份,并拒绝 account/user 身份请求头;[文件系统路由](https://github.com/volcengine/OpenViking/blob/4edc30b068934893bc94a4e1b8e87bab2100bce5/openviking/server/routers/filesystem.py)提供带请求身份的 `fs/ls`。配置的 account 标识本地映射,不证明或覆盖 key 所属账号。 + +## 自动验证 + +- 独立 Provider 回归覆盖 admin 拒绝、不完整或危险配置、无效发现响应、数据访问拒绝、取消、超时、后续操作作用域、用户不匹配和原有默认枚举。 +- 真实 Host 组合回归将两个 OpenViking 插件实例与 Native、Holographic、Runtime、Documents 同时挂载,核验凭据脱敏、不同账号同名用户隔离、精确写入/检索/删除,以及更换用户被拒时的原子保留。独立的真实 CLI 检查覆盖 Native Source 创建、写入、召回、删除,以及同一 View 内两个 Native 命名空间的精确归档,详见 [CLI 组合记录](./cli-composition.json)。 +- 实际通用设置表单向所属实例提交新字段,分别显示成功和拒绝反馈;双语文案指纹记录每种语言各新增的一个字段标签。 +- 完整 `verify` 已通过,使用 Node 24.20.0、pnpm 10.13.1 与官方 Mnemon CLI 0.2.9 操作临时空间:根测试 1,352 项、插件测试 402 项通过,8 项按已有平台/显式启用条件跳过。OpenViking 69 项通过,其中 20 项覆盖新路径,随后以单插件并行度运行的 `verify:plugins --skip-build` 已通过全部 16 个独立插件、17 个制品、外部 SDK 消费者及真实 DSH 组合,详见[汇总验证记录](./verification.json)。 + +最初并发验证时触发了既有时间预算或超时;在独占窗口中把公开 Vitest worker 上限设为一后,完整原有套件通过,未改任何测试超时或性能预算。[基线](./artifacts-before.json)与[修复](./artifacts-after.json)清单包含全部 17 个 tarball 的 SHA-256,以及改动的生产源码哈希。 + +```sh +export VITEST_MAX_FORKS=1 VITEST_MIN_FORKS=1 +export VITEST_MAX_THREADS=1 VITEST_MIN_THREADS=1 +export MNEMON_PLUGIN_VERIFY_CONCURRENCY=1 +export MNEMON_NATIVE_TEST_CLI=/absolute/path/to/mnemon +pnpm run verify +pnpm run verify:plugins --skip-build +``` + +## 复现浏览器对比 + +[本次脚本](./harness/serve.mjs)将确定性 loopback OpenViking 夹具与已有[打包 DSH 脚本](../issue-267-settings-migration/harness/packed-e2e.mjs)组合。先用 [pack-artifacts.mjs](../issue-261-dsh-slots/harness/pack-artifacts.mjs)分别打包基线与修复代码,每组制品使用独立临时运行目录: + +```sh +node docs/pr-assets/issue-271-user-key/harness/serve.mjs \ + --artifacts /tmp/issue271/artifacts \ + --run-root /tmp/issue271/browser \ + --framework-root /tmp/issue271/public-dsh \ + --native-cli /tmp/issue271/mnemon +``` + +framework 必须是未修改、正常 peer 安装的正式 DSH `0.1.7-alpha.1`。脚本在另一隔离 profile 正常安装全部 17 个 Mnemon tarball,并使用所提供的真实 CLI。前后两个全新 profile 均通过 267 个 DSH 同版本依赖与 10 个选定官方文件的字节核验,详见[运行环境证明](./browser-runtime.json)。bootstrap URL 与随机生成的夹具凭据只写入本地 `0600` JSON 文件,不应提交或截图;`provider-evidence.json` 仅记录合成数据与脱敏请求元信息。 + +1. 在基线的**设置 → 记忆系统 → OpenViking**,使用夹具 endpoint/key,先留空 account 保存,再填写账号保存;两次均在 admin 路由失败。 +2. 在修复 profile 填写账号与夹具提供的 **User Key 所属用户(跳过 Admin)**,保存成功并生成一个已启用映射;在记忆空间浏览和检索合成 canary。 +3. 将用户改为 `bob`,保存被拒,原 `alice` 映射保留;继续浏览和检索 canary,再独立创建一条 Runtime 记忆。 + +复用脚本的 `fixed` 模式为两组制品都选择正常 peer 安装,不改动任何制品。修复后的验收使用实现提交 `4ed532540a6353e87be08f744934d9c51f829c5b` 对应的生产源码。 + +真实应用内浏览器已使用官方 DSH `0.1.7-alpha.1`、17 个基线 tarball 与 Mnemon CLI `0.2.9` 复现两次失败。[浏览器记录](./browser-before.json)与[脱敏 Provider 请求记录](./provider-before.json)区分了 account 留空时的 `GET /api/v1/admin/accounts`,以及填写账号后的 `GET /api/v1/admin/accounts/fixture/users`,均收到受控 access restrictions 错误。请求记录的 `account`/`user` 描述随机 key 对应的夹具身份,`accountField` 才是实际表单输入。截图保留密码掩码,不包含 bootstrap URL。 + +![基线 account 留空时保存被拒](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-271-user-key/before-account-empty.png) + +![基线填写 account 后仍被拒](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-271-user-key/before-account-filled.png) + +修复后 GUI 成功保存 `alice`;随后保存 `bob` 被拒,但浏览与直接检索仍返回原 `alice` 的精确 canary,Runtime 也独立写入一条组合 canary。[浏览器验收记录](./browser-after.json)仅用布尔值核验持久化的原 key、endpoint、account、启用状态与唯一活动 `alice` 映射均保留,`bob` 未持久化,注册表权限仍为 `0600`。[Provider 请求记录](./provider-after.json)包含 10 次 `alice` 请求与 1 次被拒的 `bob` 探测,Admin 调用与 account/user 身份请求头均为零,远端合成正文保持不变。验收后已停止全部临时 WebUI/夹具进程。 + +![修复后 user-key 服务保存成功](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-271-user-key/after-user-key-saved.png) + +![无权访问的替换用户被拒](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-271-user-key/after-invalid-user-rejected.png) + +![拒绝后原 alice 正文仍可读取](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-271-user-key/after-user-memory-content.png) + +![直接检索返回原精确 canary](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-271-user-key/after-user-memory-search.png) + +![Runtime 仍可独立写入](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-271-user-key/after-runtime-coexistence.png) + +## 限制与数据影响 + +这是确定性 Provider 协议夹具,不代表真实火山云账号、外部模型、embedding 或在线云服务可用性验证。现有根可读不证明写权限;根不存在或数据 API 不可用仍拒绝,不在发现时创建远端命名空间。持久化格式和 RPC 权限不变。降级前禁用/移除新字段并恢复兼容的 admin 配置;发现或降级不会删除远端内容。已审阅通用 getting-started 文档,继续通过原有链接引用更新后的 Provider 配置指南。 diff --git a/dsh/docs/pr-assets/issue-271-user-key/artifacts-after.json b/dsh/docs/pr-assets/issue-271-user-key/artifacts-after.json new file mode 100644 index 000000000..c1a30cd0c --- /dev/null +++ b/dsh/docs/pr-assets/issue-271-user-key/artifacts-after.json @@ -0,0 +1,115 @@ +{ + "baselineCommit": "84d469ffa838a36fa579295d94029fcac8ac058e", + "sourceState": "production sources match the implementation commit; evidence-only files are recorded separately", + "node": "v24.20.0", + "artifacts": [ + { + "name": "dsh-mnemon", + "version": "0.5.13", + "filename": "dsh-mnemon-0.5.13.tgz", + "sha256": "f500a564a6dce45aba72e0908a53b5b7a726a3f1f261ee9a9da4dd91c203fec0" + }, + { + "name": "dsh-mnemon-provider-byterover", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-byterover-0.5.4.tgz", + "sha256": "17dcd57cb35c0c81af2b93c92be66c2e6b7e8180d5d1cf634c589281003dddc1" + }, + { + "name": "dsh-mnemon-provider-hindsight", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-hindsight-0.5.4.tgz", + "sha256": "739dec0d42689fdbdb8183ba75f7b6afc4be899b585c03c2a85842e0e998a170" + }, + { + "name": "dsh-mnemon-provider-holographic", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-holographic-0.5.4.tgz", + "sha256": "700fa0f94387ee04f5202dbbe0d410afb7114ba2486b25c32d64d7b059245f8c" + }, + { + "name": "dsh-mnemon-provider-honcho", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-honcho-0.5.4.tgz", + "sha256": "db8ffcbbc1df42555295a1ff818e2059b5c09776a0dc4cc9c311779355b06fd7" + }, + { + "name": "dsh-mnemon-provider-mem0", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-mem0-0.5.4.tgz", + "sha256": "ccd61716ead4d9462e15a662edb59c9850d812e8660bc33c4cc574601b816255" + }, + { + "name": "dsh-mnemon-provider-mnemon-native", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-mnemon-native-0.5.5.tgz", + "sha256": "8b839950e17b086578490b9eb3ef438331afa96a85ccd588ddaf7af9649f1f32" + }, + { + "name": "dsh-mnemon-provider-openviking", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-openviking-0.5.5.tgz", + "sha256": "b1555a86cb9b9d2383d5285909eb98c581122d06a3988e20a68cd4e317033d4d" + }, + { + "name": "dsh-mnemon-provider-retaindb", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-retaindb-0.5.4.tgz", + "sha256": "8962cfc5b8e777a86c8816dfd47f32943aa6a1f1973bc8882727b4b430579e3a" + }, + { + "name": "dsh-mnemon-provider-supermemory", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-supermemory-0.5.4.tgz", + "sha256": "c117f467d0eb66954ab5b27a8ae08722770699e0f9113fa4bdc9f0724485b508" + }, + { + "name": "dsh-mnemon-source-documents", + "version": "0.5.6", + "filename": "dsh-mnemon-source-documents-0.5.6.tgz", + "sha256": "9a48cc271970d7c29596811c5b0f1b3e424c1c2557d2332efbb468fe1d1b7653" + }, + { + "name": "dsh-mnemon-source-memory-spaces", + "version": "0.5.8", + "filename": "dsh-mnemon-source-memory-spaces-0.5.8.tgz", + "sha256": "6353633150c167137422aa45d824165681e8495509a1e20a7a717b431df5e4d8" + }, + { + "name": "dsh-mnemon-source-runtime", + "version": "0.5.8", + "filename": "dsh-mnemon-source-runtime-0.5.8.tgz", + "sha256": "4d97b9149087df298295b8c5ace0bc46503f21fc1a26d1f96caeedbc13304bcd" + }, + { + "name": "dsh-mnemon-strategy-auto-capture", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-auto-capture-0.5.4.tgz", + "sha256": "a653c1ebec85da0fe2c878e42a0eea84e20da877682fa1c9c97f5d8f8f70c074" + }, + { + "name": "dsh-mnemon-strategy-default-three-tier", + "version": "0.5.5", + "filename": "dsh-mnemon-strategy-default-three-tier-0.5.5.tgz", + "sha256": "fe4e722303ffeff5a598011d9ee6b8f5de73fe21aa2bf25ae215c750817b60df" + }, + { + "name": "dsh-mnemon-strategy-light-context", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-light-context-0.5.4.tgz", + "sha256": "eac6b61e9652ed54d03dd013bec285afe8714547a27c33ace286439e5958250b" + }, + { + "name": "dsh-mnemon-strategy-scoped", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-scoped-0.5.4.tgz", + "sha256": "c751c30b61f5ae60eca489cda936ed17a5b86dadaae538d9657dd76fe8467843" + } + ], + "sourceHashes": { + "plugins/dsh-mnemon-provider-openviking/src/driver.ts": "cb58fbac6612514be611fd6fea13a08607b31b179b8359fbb28297899222d708", + "plugins/dsh-mnemon-provider-openviking/src/descriptor.ts": "1268df8e40c6d8428cc00932a4f1479e96e97cc04e04246074a107ec6d372b94", + "plugins/dsh-mnemon-source-memory-spaces/presentation/locales.json": "fdfc6e8515b8cc2129ed9cebf091698305e853d90eeed553257a24a5fe5c8f04" + }, + "implementationCommit": "4ed532540a6353e87be08f744934d9c51f829c5b" +} diff --git a/dsh/docs/pr-assets/issue-271-user-key/artifacts-before.json b/dsh/docs/pr-assets/issue-271-user-key/artifacts-before.json new file mode 100644 index 000000000..e53cd0745 --- /dev/null +++ b/dsh/docs/pr-assets/issue-271-user-key/artifacts-before.json @@ -0,0 +1,109 @@ +{ + "baselineCommit": "84d469ffa838a36fa579295d94029fcac8ac058e", + "sourceState": "clean baseline", + "node": "v24.20.0", + "artifacts": [ + { + "name": "dsh-mnemon", + "version": "0.5.13", + "filename": "dsh-mnemon-0.5.13.tgz", + "sha256": "2342294fbcb0dbb98cad015036fdcb04cb0c6b0e99c139c81ae8783aa600d267" + }, + { + "name": "dsh-mnemon-provider-byterover", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-byterover-0.5.4.tgz", + "sha256": "17dcd57cb35c0c81af2b93c92be66c2e6b7e8180d5d1cf634c589281003dddc1" + }, + { + "name": "dsh-mnemon-provider-hindsight", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-hindsight-0.5.4.tgz", + "sha256": "739dec0d42689fdbdb8183ba75f7b6afc4be899b585c03c2a85842e0e998a170" + }, + { + "name": "dsh-mnemon-provider-holographic", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-holographic-0.5.4.tgz", + "sha256": "700fa0f94387ee04f5202dbbe0d410afb7114ba2486b25c32d64d7b059245f8c" + }, + { + "name": "dsh-mnemon-provider-honcho", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-honcho-0.5.4.tgz", + "sha256": "db8ffcbbc1df42555295a1ff818e2059b5c09776a0dc4cc9c311779355b06fd7" + }, + { + "name": "dsh-mnemon-provider-mem0", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-mem0-0.5.4.tgz", + "sha256": "ccd61716ead4d9462e15a662edb59c9850d812e8660bc33c4cc574601b816255" + }, + { + "name": "dsh-mnemon-provider-mnemon-native", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-mnemon-native-0.5.5.tgz", + "sha256": "8b839950e17b086578490b9eb3ef438331afa96a85ccd588ddaf7af9649f1f32" + }, + { + "name": "dsh-mnemon-provider-openviking", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-openviking-0.5.5.tgz", + "sha256": "64d1ee70d1411ea33db680fefb1b143ca19adace2c1632334101a6680c827e41" + }, + { + "name": "dsh-mnemon-provider-retaindb", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-retaindb-0.5.4.tgz", + "sha256": "8962cfc5b8e777a86c8816dfd47f32943aa6a1f1973bc8882727b4b430579e3a" + }, + { + "name": "dsh-mnemon-provider-supermemory", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-supermemory-0.5.4.tgz", + "sha256": "c117f467d0eb66954ab5b27a8ae08722770699e0f9113fa4bdc9f0724485b508" + }, + { + "name": "dsh-mnemon-source-documents", + "version": "0.5.6", + "filename": "dsh-mnemon-source-documents-0.5.6.tgz", + "sha256": "9a48cc271970d7c29596811c5b0f1b3e424c1c2557d2332efbb468fe1d1b7653" + }, + { + "name": "dsh-mnemon-source-memory-spaces", + "version": "0.5.8", + "filename": "dsh-mnemon-source-memory-spaces-0.5.8.tgz", + "sha256": "3646e4ed24017f1987d8f594a02f9f6ae1c85f2af4a00a6d1983bcc30b5677cc" + }, + { + "name": "dsh-mnemon-source-runtime", + "version": "0.5.8", + "filename": "dsh-mnemon-source-runtime-0.5.8.tgz", + "sha256": "4d97b9149087df298295b8c5ace0bc46503f21fc1a26d1f96caeedbc13304bcd" + }, + { + "name": "dsh-mnemon-strategy-auto-capture", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-auto-capture-0.5.4.tgz", + "sha256": "a653c1ebec85da0fe2c878e42a0eea84e20da877682fa1c9c97f5d8f8f70c074" + }, + { + "name": "dsh-mnemon-strategy-default-three-tier", + "version": "0.5.5", + "filename": "dsh-mnemon-strategy-default-three-tier-0.5.5.tgz", + "sha256": "fe4e722303ffeff5a598011d9ee6b8f5de73fe21aa2bf25ae215c750817b60df" + }, + { + "name": "dsh-mnemon-strategy-light-context", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-light-context-0.5.4.tgz", + "sha256": "eac6b61e9652ed54d03dd013bec285afe8714547a27c33ace286439e5958250b" + }, + { + "name": "dsh-mnemon-strategy-scoped", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-scoped-0.5.4.tgz", + "sha256": "c751c30b61f5ae60eca489cda936ed17a5b86dadaae538d9657dd76fe8467843" + } + ] +} diff --git a/dsh/docs/pr-assets/issue-271-user-key/browser-after.json b/dsh/docs/pr-assets/issue-271-user-key/browser-after.json new file mode 100644 index 000000000..cd30c5cd6 --- /dev/null +++ b/dsh/docs/pr-assets/issue-271-user-key/browser-after.json @@ -0,0 +1,61 @@ +{ + "implementationCommit": "4ed532540a6353e87be08f744934d9c51f829c5b", + "artifactManifest": "artifacts-after.json", + "runtimeProof": "browser-runtime.json", + "fixture": "deterministic-openviking-user-key", + "cloudAccountVerified": false, + "manualResults": { + "aliceSave": "succeeded", + "bobReplacement": "rejected by data API", + "contentAfterRejection": "one exact synthetic alice memory", + "searchAfterRejection": "one exact synthetic alice result", + "runtimeComposition": "one independently stored runtime canary" + }, + "persistenceChecks": { + "ownerAlicePreserved": true, + "originalKeyPreserved": true, + "accountPreserved": true, + "endpointPreserved": true, + "serviceEnabled": true, + "oneAliceProjectionPreserved": true, + "projectionActive": true, + "rejectedOwnerNotPersisted": true, + "registryMode600": true, + "runtimeCanaryPersisted": true + }, + "screenshots": [ + { + "file": "after-user-key-saved.png", + "sha256": "75270e744eec7f0dd59372c6f2a6de0323b5fbe5571d4ef8544f8a2817e0c1ba" + }, + { + "file": "after-invalid-user-rejected.png", + "sha256": "9e7b69e318e975c5438f9c5dc42ab1c49c0a169484ae2731d589d39d54c98056" + }, + { + "file": "after-user-memory-content.png", + "sha256": "b014825746e385ed4a093903e76c261132194acdbf051d99afce164801a5df13" + }, + { + "file": "after-user-memory-search.png", + "sha256": "e8ff0fd92d3fb8b961b5297c3b57be1d2fbcb8b9a6dd3f24e8d27306b55dabb8" + }, + { + "file": "after-runtime-coexistence.png", + "sha256": "f2a093e6cb4a45f0b0fd85d36939a8beabbd268d6387d40507e9999a9c285fe0" + } + ], + "requestSummary": { + "total": 11, + "adminRequests": 0, + "identityHeaderRequests": 0, + "paths": [ + "GET /api/v1/fs/ls", + "GET /api/v1/content/read", + "POST /api/v1/search/find" + ], + "aliceDataRequests": 10, + "bobDataRequests": 1 + }, + "fixtureFilesUnchanged": true +} diff --git a/dsh/docs/pr-assets/issue-271-user-key/browser-before.json b/dsh/docs/pr-assets/issue-271-user-key/browser-before.json new file mode 100644 index 000000000..25a880aef --- /dev/null +++ b/dsh/docs/pr-assets/issue-271-user-key/browser-before.json @@ -0,0 +1,37 @@ +{ + "baselineRevision": "84d469ffa838a36fa579295d94029fcac8ac058e", + "dshVersion": "0.1.7-alpha.1", + "mnemonCliVersion": "0.2.9", + "fixture": "deterministic-openviking-user-key", + "actualCloudAccountVerified": false, + "attempts": [ + { + "accountField": "", + "result": "rejected: API is currently unavailable due to access restrictions", + "request": { + "method": "GET", + "path": "/api/v1/admin/accounts", + "account": "fixture", + "user": "alice", + "identityHeaders": false, + "uri": null + }, + "screenshot": "before-account-empty.png", + "sha256": "8d17f453ce2608bed81c13ac43399dc2a1a77cd7133f1f0007a0d9bb9312460d" + }, + { + "accountField": "fixture", + "result": "rejected: API is currently unavailable due to access restrictions", + "request": { + "method": "GET", + "path": "/api/v1/admin/accounts/fixture/users", + "account": "fixture", + "user": "alice", + "identityHeaders": true, + "uri": null + }, + "screenshot": "before-account-filled.png", + "sha256": "d7a69054481319327878b7cdd1de511750d2881622826f6c596cb150bfd88f10" + } + ] +} diff --git a/dsh/docs/pr-assets/issue-271-user-key/browser-runtime.json b/dsh/docs/pr-assets/issue-271-user-key/browser-runtime.json new file mode 100644 index 000000000..2d9a7654f --- /dev/null +++ b/dsh/docs/pr-assets/issue-271-user-key/browser-runtime.json @@ -0,0 +1,122 @@ +{ + "dshVersion": "0.1.7-alpha.1", + "mnemonCliVersion": "0.2.9", + "unchangedOfficialDsh": true, + "frameworkSourceCheckoutUsed": false, + "runs": [ + { + "phase": "before", + "dshPackageCount": 267, + "mnemonPackages": [ + "dsh-mnemon", + "dsh-mnemon-provider-byterover", + "dsh-mnemon-provider-hindsight", + "dsh-mnemon-provider-holographic", + "dsh-mnemon-provider-honcho", + "dsh-mnemon-provider-mem0", + "dsh-mnemon-provider-mnemon-native", + "dsh-mnemon-provider-openviking", + "dsh-mnemon-provider-retaindb", + "dsh-mnemon-provider-supermemory", + "dsh-mnemon-source-documents", + "dsh-mnemon-source-memory-spaces", + "dsh-mnemon-source-runtime", + "dsh-mnemon-strategy-auto-capture", + "dsh-mnemon-strategy-default-three-tier", + "dsh-mnemon-strategy-light-context", + "dsh-mnemon-strategy-scoped" + ], + "legacyPeerResolution": false, + "reusedExistingInstall": false, + "artifactManifest": "artifacts-before.json" + }, + { + "phase": "after", + "dshPackageCount": 267, + "mnemonPackages": [ + "dsh-mnemon", + "dsh-mnemon-provider-byterover", + "dsh-mnemon-provider-hindsight", + "dsh-mnemon-provider-holographic", + "dsh-mnemon-provider-honcho", + "dsh-mnemon-provider-mem0", + "dsh-mnemon-provider-mnemon-native", + "dsh-mnemon-provider-openviking", + "dsh-mnemon-provider-retaindb", + "dsh-mnemon-provider-supermemory", + "dsh-mnemon-source-documents", + "dsh-mnemon-source-memory-spaces", + "dsh-mnemon-source-runtime", + "dsh-mnemon-strategy-auto-capture", + "dsh-mnemon-strategy-default-three-tier", + "dsh-mnemon-strategy-light-context", + "dsh-mnemon-strategy-scoped" + ], + "legacyPeerResolution": false, + "reusedExistingInstall": false, + "artifactManifest": "artifacts-after.json" + } + ], + "publicBytesChecked": [ + { + "package": "@deepseek-ai/dsh-settings", + "file": "lib/index.js", + "tarballIntegrity": "sha512-I7xEso742PC9lmpu9F+011mVRsvvemVxrFrf3+MgQpSOl2PM1iNjD6I4/+tMYQsV+i/N6yDVC5RB8U/anM/MFA==", + "sha256": "9432b872597b7a31473072eb38a55979ab23375c76bd1d86d751388a692a0136" + }, + { + "package": "@deepseek-ai/dsh-client-ui-slots", + "file": "lib/index.js", + "tarballIntegrity": "sha512-lN4WVTzDb/t01KGmwjtZxjoqCIMKvi5kyY9/XPVkFjI+6q5RmorbCIyZiq84YJ2I36Cj2nYUQLB8KSRXjGMQbw==", + "sha256": "57e1314e31a2015f30231e1e7596da658f1bb5cb64c103c4cf13381647412c99" + }, + { + "package": "@deepseek-ai/dsh-client-ui-chat", + "file": "lib/client.js", + "tarballIntegrity": "sha512-bBiPngl5Mvrio8sdWZJmjeZeRZFaDEKIWa5aNi9kkukPGrZaaxABdwH+52URsJWadj8V0HEyahffSAACvit0AQ==", + "sha256": "f89f5ae18fe09bb7a38b2ab0bc2de514c84794138e28c77fdd77498ccd9c6569" + }, + { + "package": "@deepseek-ai/dsh-client-ui-renderer", + "file": "lib/client.js", + "tarballIntegrity": "sha512-hxcW9WeEW9mnT7pH5jN4i3S+vj7beh6I//zBhuRk9tvBeoWEgDzo4Z6Q1A1FD7Q6+fAkZ/l5QV2p4FIPhYDwwg==", + "sha256": "d92aaa305d56631110f46d7b9cd3b21547eaf2cb0c5ed34055391e0a46d9e2ce" + }, + { + "package": "@deepseek-ai/dsh-client-ui-conversation", + "file": "lib/client.js", + "tarballIntegrity": "sha512-PeZKCFJIi65sMWk7tCxkRRXmh2+LqNyct8LjcydWY54ZDJuDNysK6YxED01qck+WVTCh/NsE9wOgmzfHtfGQzA==", + "sha256": "e886ad7023f5f6f5bd52f4481c5f53709764f20a3791899fb3b67f19d094c837" + }, + { + "package": "@deepseek-ai/dsh-client-ui-settings", + "file": "lib/client.js", + "tarballIntegrity": "sha512-bFUKeO5KsxNteVwMhB8LOayPKXXkATUqPZMH2c9LJKnxCw9MliC3hmiMfHw3QjL7cFaoNA7/T9f49zmtIwvlFQ==", + "sha256": "2ac7f186165c5a8ec2cd90045cf74693e5d3518ec90cd429951360d79754aa94" + }, + { + "package": "@deepseek-ai/dsh-base", + "file": "cordis.patch.yml", + "tarballIntegrity": "sha512-FNJ4eyOV7PdZ36DyOT9hTvSQpMxBgfOdkTKAOxn0ney0kjklXQiGEDRmnLT8PkQU3q/CYKrGkXDOw0ILbOq//w==", + "sha256": "d10c6baa177d94d69f259d487552199c807761a029d95a64102e8f0523a23b74" + }, + { + "package": "@deepseek-ai/dsh-web-app", + "file": "cordis.patch.yml", + "tarballIntegrity": "sha512-e3CppXixd+lnqUbAT2k87U2pXkeUm8f+iVLwhy+lm8Mp1mrhaHcJ9RUEHqQY6bP+WYLdNZHjHw34IW25a0/Q5A==", + "sha256": "e6acb583f603d60ea971b54cc3f30777e03f059542b81a81e129e8b34eb8869a" + }, + { + "package": "@deepseek-ai/dsh-config-editor", + "file": "lib/index.js", + "tarballIntegrity": "sha512-smttBp9Mc+DIQbZmYNb6ypvWbLB1dtJ0HC+4IO+NshtqPD4YwuA7tbRm14nEFlvZe+fxa8bcZuSs9h3ObuzjRA==", + "sha256": "bb85243d9f5729a7186cace56c4ce31d4918bd026482797ef5b7c01bf612fab5" + }, + { + "package": "@deepseek-ai/dsh-agent-preset-registry", + "file": "lib/index.js", + "tarballIntegrity": "sha512-omTfcTtfuWPVOsTh5aHskuKDBB8KuhxeoSF28Nox3QMzaYRXo1P7vZc38yN9GwnVva3gP5EVZxXpKMEzI5aybA==", + "sha256": "01e3b0d0509f5f8ffad1f6ec8b3acbfe1b6007918f9ea61be1116addd076b3fa" + } + ] +} diff --git a/dsh/docs/pr-assets/issue-271-user-key/cli-composition.json b/dsh/docs/pr-assets/issue-271-user-key/cli-composition.json new file mode 100644 index 000000000..f18f4c1de --- /dev/null +++ b/dsh/docs/pr-assets/issue-271-user-key/cli-composition.json @@ -0,0 +1,27 @@ +{ + "mnemonCliVersion": "0.2.9", + "officialArchiveSha256": "6fbb1d572d8b4c080c36080e23ae0a183fce6841748f6c426e712136ac756b24", + "realExecutable": true, + "globalUserInstallModified": false, + "automaticCompositionChecks": [ + { + "file": "plugins/dsh-mnemon-source-memory-spaces/tests/native-integration.spec.ts", + "scenario": "Real Native Provider through Source composition: create space, View write, recall and forget", + "passed": 1, + "failed": 0, + "durationMs": 680 + }, + { + "file": "tests/runtime-capacity-workflow.spec.ts", + "scenario": "Exact checkpoints archived to two same-View Native namespaces through real Host tools", + "passed": true, + "scenarioDurationMs": 464 + } + ], + "webUiComposition": { + "runtimeCanaryWritten": true, + "openVikingCanaryRemainedReadable": true, + "record": "browser-after.json" + }, + "scope": "CLI checks used disposable Native stores. Browser OpenViking calls used the separate deterministic loopback fixture; the GUI Runtime write is not represented as a Native CLI write." +} diff --git a/dsh/docs/pr-assets/issue-271-user-key/harness/serve.mjs b/dsh/docs/pr-assets/issue-271-user-key/harness/serve.mjs new file mode 100644 index 000000000..15ac20bf4 --- /dev/null +++ b/dsh/docs/pr-assets/issue-271-user-key/harness/serve.mjs @@ -0,0 +1,47 @@ +import assert from 'node:assert/strict' +import { randomUUID } from 'node:crypto' +import { mkdir, writeFile } from 'node:fs/promises' +import { spawn } from 'node:child_process' +import { dirname, join, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' +import { openVikingUserKeyFixture } from '../../../../scripts/fixtures/openviking-user-key.mjs' + +const args = new Map() +for (let i = 2; i < process.argv.length; i += 2) args.set(process.argv[i], process.argv[i + 1]) +for (const name of ['--artifacts', '--run-root', '--framework-root', '--native-cli']) assert(args.has(name), `Missing ${name}`) +const runRoot = resolve(args.get('--run-root')) +const nativeCli = resolve(args.get('--native-cli')) +await mkdir(runRoot, { recursive: true, mode: 0o700 }) +const apiKey = randomUUID() +const backend = openVikingUserKeyFixture(new Map([[apiKey, { account: 'fixture', user: 'alice' }]])) +const uri = 'viking://user/alice/memories/entities/issue-271-canary.md' +backend.files.set(`fixture:${uri}`, { account: 'fixture', uri, content: 'Issue 271 synthetic memory: user-key discovery can browse this exact text.\n这是隔离夹具数据,不是真实火山云账号。' }) +await new Promise(done => backend.server.listen(0, '127.0.0.1', done)) +const settings = { endpoint: `http://127.0.0.1:${backend.server.address().port}/openviking`, account: 'fixture', discoveryUser: 'alice', apiKey } +await writeFile(join(runRoot, 'provider-private.json'), JSON.stringify(settings, null, 2) + '\n', { mode: 0o600 }) +const saveEvidence = () => writeFile(join(runRoot, 'provider-evidence.json'), JSON.stringify({ + fixture: 'deterministic-openviking-user-key', cloudAccountVerified: false, + requests: backend.requests, files: [...backend.files.values()], +}, null, 2) + '\n') +await saveEvidence() +const evidenceTimer = setInterval(() => { void saveEvidence() }, 1000) +const helper = resolve(dirname(fileURLToPath(import.meta.url)), '../../issue-267-settings-migration/harness/packed-e2e.mjs') +// Both source revisions support this public DSH cohort. "fixed" selects the +// helper's normal-peer install path; the supplied artifacts select the code. +const child = spawn(process.execPath, [helper, '--mode', 'fixed', '--framework-version', '0.1.7-alpha.1', + '--framework-root', resolve(args.get('--framework-root')), '--artifacts', resolve(args.get('--artifacts')), + '--run-root', join(runRoot, 'web'), '--native-cli', nativeCli, '--serve', 'true'], { stdio: 'inherit' }) +console.log(`Issue 271 fixture settings: ${join(runRoot, 'provider-private.json')} (private)`) +console.log(`WebUI bootstrap state will be written to ${join(runRoot, 'web/server.json')} (private)`) +let stopping = false +async function stop() { + if (stopping) return + stopping = true + clearInterval(evidenceTimer) + if (child.exitCode === null && child.signalCode === null) { child.kill('SIGTERM'); await new Promise(done => child.once('exit', done)) } + backend.server.closeAllConnections() + await new Promise(done => backend.server.close(done)) + await saveEvidence() +} +child.once('exit', code => { process.exitCode = code ?? 0; void stop() }) +for (const signal of ['SIGINT', 'SIGTERM']) process.once(signal, () => { void stop() }) diff --git a/dsh/docs/pr-assets/issue-271-user-key/provider-after.json b/dsh/docs/pr-assets/issue-271-user-key/provider-after.json new file mode 100644 index 000000000..90d216a4b --- /dev/null +++ b/dsh/docs/pr-assets/issue-271-user-key/provider-after.json @@ -0,0 +1,101 @@ +{ + "fixture": "deterministic-openviking-user-key", + "cloudAccountVerified": false, + "requests": [ + { + "method": "GET", + "path": "/api/v1/fs/ls", + "account": "fixture", + "user": "alice", + "identityHeaders": false, + "uri": "viking://user/alice/memories" + }, + { + "method": "GET", + "path": "/api/v1/fs/ls", + "account": "fixture", + "user": "alice", + "identityHeaders": false, + "uri": "viking://user/bob/memories" + }, + { + "method": "GET", + "path": "/api/v1/fs/ls", + "account": "fixture", + "user": "alice", + "identityHeaders": false, + "uri": "viking://user/alice/memories" + }, + { + "method": "GET", + "path": "/api/v1/fs/ls", + "account": "fixture", + "user": "alice", + "identityHeaders": false, + "uri": "viking://user/alice/memories" + }, + { + "method": "GET", + "path": "/api/v1/fs/ls", + "account": "fixture", + "user": "alice", + "identityHeaders": false, + "uri": "viking://user/alice/memories" + }, + { + "method": "GET", + "path": "/api/v1/content/read", + "account": "fixture", + "user": "alice", + "identityHeaders": false, + "uri": "viking://user/alice/memories/entities/issue-271-canary.md" + }, + { + "method": "GET", + "path": "/api/v1/fs/ls", + "account": "fixture", + "user": "alice", + "identityHeaders": false, + "uri": "viking://user/alice/memories" + }, + { + "method": "GET", + "path": "/api/v1/content/read", + "account": "fixture", + "user": "alice", + "identityHeaders": false, + "uri": "viking://user/alice/memories/entities/issue-271-canary.md" + }, + { + "method": "POST", + "path": "/api/v1/search/find", + "account": "fixture", + "user": "alice", + "identityHeaders": false, + "uri": "viking://user/alice/memories" + }, + { + "method": "GET", + "path": "/api/v1/content/read", + "account": "fixture", + "user": "alice", + "identityHeaders": false, + "uri": "viking://user/alice/memories/entities/issue-271-canary.md" + }, + { + "method": "GET", + "path": "/api/v1/fs/ls", + "account": "fixture", + "user": "alice", + "identityHeaders": false, + "uri": "viking://user/alice/memories" + } + ], + "files": [ + { + "account": "fixture", + "uri": "viking://user/alice/memories/entities/issue-271-canary.md", + "content": "Issue 271 synthetic memory: user-key discovery can browse this exact text.\n这是隔离夹具数据,不是真实火山云账号。" + } + ] +} diff --git a/dsh/docs/pr-assets/issue-271-user-key/provider-before.json b/dsh/docs/pr-assets/issue-271-user-key/provider-before.json new file mode 100644 index 000000000..d89280523 --- /dev/null +++ b/dsh/docs/pr-assets/issue-271-user-key/provider-before.json @@ -0,0 +1,29 @@ +{ + "fixture": "deterministic-openviking-user-key", + "cloudAccountVerified": false, + "requests": [ + { + "method": "GET", + "path": "/api/v1/admin/accounts", + "account": "fixture", + "user": "alice", + "identityHeaders": false, + "uri": null + }, + { + "method": "GET", + "path": "/api/v1/admin/accounts/fixture/users", + "account": "fixture", + "user": "alice", + "identityHeaders": true, + "uri": null + } + ], + "files": [ + { + "account": "fixture", + "uri": "viking://user/alice/memories/entities/issue-271-canary.md", + "content": "Issue 271 synthetic memory: user-key discovery can browse this exact text.\n这是隔离夹具数据,不是真实火山云账号。" + } + ] +} diff --git a/dsh/docs/pr-assets/issue-271-user-key/verification.json b/dsh/docs/pr-assets/issue-271-user-key/verification.json new file mode 100644 index 000000000..2bc6211c8 --- /dev/null +++ b/dsh/docs/pr-assets/issue-271-user-key/verification.json @@ -0,0 +1,108 @@ +{ + "issue": 271, + "baselineCommit": "84d469ffa838a36fa579295d94029fcac8ac058e", + "environment": { + "node": "24.20.0", + "pnpm": "10.13.1", + "mnemonCli": "0.2.9", + "mnemonArchiveSha256": "6fbb1d572d8b4c080c36080e23ae0a183fce6841748f6c426e712136ac756b24", + "browserFramework": "0.1.7-alpha.1", + "officialFrameworkSourceModified": false, + "workspaceFramework": "0.1.5-rc.1" + }, + "before": { + "command": "pnpm --filter dsh-mnemon-provider-openviking exec vitest run tests/user-discovery.spec.ts -t 'discovers the explicit owner'", + "failed": 1, + "reason": "API is currently unavailable due to access restrictions" + }, + "verify": { + "command": "pnpm run verify", + "passed": true, + "workerEnvironment": { + "VITEST_MAX_FORKS": "1", + "VITEST_MIN_FORKS": "1", + "VITEST_MAX_THREADS": "1", + "VITEST_MIN_THREADS": "1" + }, + "rootTests": { + "passed": 1352, + "skipped": 6 + }, + "pluginTests": { + "passed": 402, + "skipped": 2 + }, + "openvikingTests": { + "passed": 69, + "skipped": 1, + "newRegressionTests": 20 + }, + "hostRegressionTests": 1, + "genericUiSaveCases": 2, + "nativeCliIntegration": "passed in disposable stores", + "headless": { + "tools": 39, + "representativeMnemonTools": 8 + }, + "reproducibleBuild": "passed", + "packageValidation": "passed" + }, + "skipped": [ + "5 opt-in real DeepSeek Flash tests: no external model run requested", + "1 optional published V4 codec fixture: contract path not configured", + "1 Windows smoke test: macOS host", + "1 live OpenViking integration: no live server credentials configured" + ], + "earlierAttempts": { + "fullVerify": "Existing timing fences/timeouts failed during concurrent full runs: 6 failures, 1346 passed, 6 skipped.", + "targetedRerun": "201 passed, 3 existing timing failures while other verification work remained active.", + "resolution": "The full unchanged suite passed in an exclusive window using the documented Vitest worker environment. No performance budget or test timeout was changed.", + "pluginArtifacts": "Default four-package concurrency: existing Memory Spaces client search and Runtime client edit exceeded 5-second timeouts; the following Runtime test attempted to update an unmounted root. The complete artifact verification passed when rerun with MNEMON_PLUGIN_VERIFY_CONCURRENCY=1; no tests, timeouts or budgets were changed." + }, + "limitations": [ + "Deterministic OpenViking HTTP fixture based on the pinned public contract, not a real Volcengine account.", + "Read-only discovery does not certify cloud write permission or create missing roots.", + "The configured account identifies the local projection, not an independently verified server identity." + ], + "rawLogSha256": { + "discovery-before.log": "c906b05355dc23a4be5defa55b6956bb0f715f8b24737f5778ae2abf56bd6f08", + "verify.log": "4a57ba67e3ee87a8ce9ed0274dfaa9d9ce8ce2298c59a229f669cf693fa014f3", + "verify-timeout-rerun.log": "e2dd2f612a3e63b4c8f764b22cd7efda514dd98c03f7876d22e2a2d60a1f250d", + "verify-exclusive.log": "eb9b392ac499179d709a631479e94a59865385f16ae9eb8b59a43a16444e41cb", + "verify-plugins.log": "e6108bdf770403dae043ebf1d241104330770931835674563003d1fa1a3d6576", + "verify-plugins-serial.log": "b4512bc767485fc786ae05324d78662896ad6d3d339fac5910fee57bd293e731", + "docs-final.log": "0e29b4f4c5609d208b532b26b1fcb710da3de6fd96efe1175bb2bba9ecd94e95" + }, + "pluginArtifacts": { + "command": "pnpm run verify:plugins --skip-build", + "passed": true, + "environment": { + "MNEMON_PLUGIN_VERIFY_CONCURRENCY": "1" + }, + "independentPlugins": 16, + "packedArtifacts": 17, + "publicSdkConsumer": "passed", + "realDshStarterAndOptionalStrategies": "passed", + "noWorkspaceLinks": true + }, + "implementationCommit": "4ed532540a6353e87be08f744934d9c51f829c5b", + "browserAcceptance": { + "passed": true, + "record": "browser-after.json", + "screenshots": 7, + "providerRequests": 11, + "adminRequests": 0, + "identityHeaderRequests": 0, + "originalKeyAndOwnerPreserved": true, + "independentRuntimeWrite": true + }, + "disposableServersStopped": true, + "releaseIntent": "Passed for dsh-mnemon-provider-openviking and dsh-mnemon-source-memory-spaces; bilingual changeset also includes the root package.", + "documentation": { + "passed": true, + "localLinks": 1734, + "markdownAnchors": 66, + "bilingualPaths": true, + "officialPluginCoverage": true + } +} diff --git a/dsh/docs/pr-assets/issue-273-skin-hooks/README.md b/dsh/docs/pr-assets/issue-273-skin-hooks/README.md new file mode 100644 index 000000000..3fdbcfdfb --- /dev/null +++ b/dsh/docs/pr-assets/issue-273-skin-hooks/README.md @@ -0,0 +1,56 @@ +# Stable Mnemon skin hooks — issue 273 + +[简体中文](./README.zh-CN.md) + +Baseline: `6dc4e4201585223f7f1da371256da11deee34d6c` (v0.5.14); production change: `fb584052`. Tested on macOS arm64 with Node 24.20.0, pnpm 10.13.1, published DSH 0.1.7-rc.2 and official Mnemon CLI 0.2.9. Each profile installs all seventeen Mnemon tarballs and enables Scoped, Light Context and Auto Capture. + +The workbench now exposes `[data-dsh-plugin="dsh-mnemon"][data-dsh-part="mnemon-view"]` in Sidebar and Builtin modes. Skins can override `--mn-bg`, `--mn-backdrop` and `--mn-surface` through this stable selector. Production CSS and the default opaque surface are unchanged. Body-portaled dialogs are outside this hook. + +## Browser evidence + +The fixture packs and installs a small Client skin through the public DSH plugin command. It uses only the paired selector and three declared tokens, without hashed selectors, `!important` or browser-injected CSS. + +- [Baseline](./before-browser.json): the skin is installed before Mnemon's stylesheet, but no root matches the hook; the official white surface remains. +- [Fixed Sidebar](./after-before-order-browser.json): one root matches, and the real computed background uses the mint skin even though its stylesheet comes first. +- [Fixed Builtin](./after-builtin-browser.json): the same skin applies, the composer remains visible, all three enhancements are enabled, settings save succeeds and the conversation replies. +- [Runtime edit dialog](./dialog-browser.json): the portal has no workbench hook and retains its default surface. A synthetic Runtime fact was added, edited to use SQLite WAL and retained after browser reload; Builtin mode also persisted. + +Fresh Chrome runs checked the remaining cascade controls. With the [skin loaded after Mnemon](./after-order-browser.json), root, header and canvas all used the mint surface ([screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-273-skin-hooks/after-order-sidebar.png)); with [no skin installed](./default-browser.json), all three retained the original opaque white surface and two gradients ([screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-273-skin-hooks/default-sidebar.png)). Runtime, Documents and Memory Spaces navigation worked in both. These two runs inspected Sidebar surfaces without starting sessions or writing memory. + +| Baseline Sidebar | Fixed Sidebar | +| --- | --- | +| ![Baseline surface](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-273-skin-hooks/before-skin-sidebar.png) | ![Stable skin override](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-273-skin-hooks/after-skin-sidebar.png) | + +![Fixed Builtin surface](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-273-skin-hooks/after-skin-builtin.png) + +## Verification + +The two hook regressions fail on the baseline; the focused Client checks pass **67 tests** after the fix. `pnpm verify` passed: **1,364 Root tests / 7 skips**, **402 plugin tests / 2 skips**, with type, deterministic-build, Headless and package checks. Root unpacked size is **1,374,875 bytes**, below the unchanged 1,376,000-byte limit. `verify:plugins --skip-build` passed all **16 independent plugin repositories and 17 artifacts**. + +Optional Native integration tests ran with the verified CLI 0.2.9. An independent isolated-store smoke also wrote, recalled and soft-deleted a synthetic fact. Baseline artifact checksums exactly match the saved `6dc4e4` artifacts; fixed artifacts were packed afresh. Generated Client comments contain build locations, so unrelated tarball hash differences are not treated as production-code changes. + +Machine-readable summaries: [verification and artifact hashes](./verification.json) and [isolated CLI smoke](./cli-smoke.json). + +## Reproduce + +Copy [rc2-profile.json](./rc2-profile.json) to `package.json` in an empty temporary profile and run `npm install --ignore-scripts --registry https://registry.npmjs.org` there with Node 24. In each baseline/fixed checkout, build Root and the plugins, then pack all seventeen packages into a separate empty directory: + +```sh +pnpm build +pnpm --workspace-concurrency=1 -r build +node --input-type=module - /absolute/empty-artifacts <<'JS' +import { readReleasePackages, createReleasePlan, packRelease } from './scripts/release.mjs' +await packRelease(createReleasePlan(await readReleasePackages(), { baseVersions: new Map() }), process.argv[2]) +JS +MNEMON_CLI_PATH=/absolute/verified/mnemon node scripts/fixtures/serve-skin-hook.mjs \ + --profile /absolute/rc2-profile --artifacts /absolute/packed-artifacts \ + --state /absolute/empty-state --skin-order before +``` + +Run the final fixture command from this PR checkout for both comparisons; only change `--artifacts` to the baseline or fixed tarball directory and choose a fresh `--state`. The baseline checkout does not contain these new fixture scripts. + +The [scenario](../../../scripts/fixtures/serve-skin-hook.mjs) uses the shared [packed-profile helper](../../../scripts/fixtures/packed-web-fixture.mjs). Open the private URL recorded in the state's `private.json`, select its `workspace` directory and the official Coding preset, then open Mnemon. Use fresh states with `--skin-order after` or `none` to inspect the later stylesheet order or the unskinned default. The installed skin can also be disabled through DSH's plugin settings. + +In this browser environment, the printed `127.0.0.1` URL returned `ERR_BLOCKED_BY_CLIENT`; changing only its hostname to `localhost` reached the same loopback host through the normal token-to-cookie redirect. Authentication and browser protections were unchanged. Keep authentication URLs and raw logs private. `SIGTERM` stops the fixture; optional `--port` selects a stable port. + +Only temporary synthetic data was used. The published adapter, tools, transport, storage and browser were real; the loopback model only supplied deterministic replies. This is not a live-model quality or Windows result. Optional Flash, Teams, lifecycle, OpenViking-service and Windows checks retain their reported skips. diff --git a/dsh/docs/pr-assets/issue-273-skin-hooks/README.zh-CN.md b/dsh/docs/pr-assets/issue-273-skin-hooks/README.zh-CN.md new file mode 100644 index 000000000..3845a50f9 --- /dev/null +++ b/dsh/docs/pr-assets/issue-273-skin-hooks/README.zh-CN.md @@ -0,0 +1,56 @@ +# 稳定的 Mnemon 皮肤钩子 — Issue 273 + +[English](./README.md) + +基线为 `6dc4e4201585223f7f1da371256da11deee34d6c`(v0.5.14),生产修改为 `fb584052`。环境为 macOS arm64、Node 24.20.0、pnpm 10.13.1、正式发布的 DSH 0.1.7-rc.2 和官方 Mnemon CLI 0.2.9。每个 Profile 均安装完整的十七个 Mnemon tarball,并同时启用 Scoped、Light Context 和 Auto Capture。 + +工作台在 Sidebar 和 Builtin 模式下均提供 `[data-dsh-plugin="dsh-mnemon"][data-dsh-part="mnemon-view"]`。皮肤可通过此稳定选择器覆盖 `--mn-bg`、`--mn-backdrop` 和 `--mn-surface`。生产 CSS 和默认不透明表面保持不变;挂载到 body 的弹窗不属于此钩子的范围。 + +## 浏览器证据 + +夹具通过公开的 DSH 插件命令打包、安装一个小型 Client 皮肤。它只使用上述属性组合与三个已声明 token,不依赖哈希类名、`!important` 或浏览器注入 CSS。 + +- [基线](./before-browser.json):皮肤样式位于 Mnemon 样式之前,但没有根节点匹配钩子,仍显示官方白色表面。 +- [修复后 Sidebar](./after-before-order-browser.json):恰好一个根节点匹配;即使皮肤样式排在前面,实际计算背景仍采用浅绿色皮肤。 +- [修复后 Builtin](./after-builtin-browser.json):同一皮肤生效,输入框保持可见,三个增强全部开启,设置保存成功,对话正常回复。 +- [Runtime 编辑弹窗](./dialog-browser.json):portal 没有工作台钩子,保留默认表面。界面新增合成 Runtime 记录、编辑为 SQLite WAL 后,浏览器刷新仍保留该记录和 Builtin 模式。 + +新的 Chrome 会话完成了其余层叠顺序对照:[皮肤样式后置](./after-order-browser.json)时,根节点、页头和画布均采用浅绿色表面([截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-273-skin-hooks/after-order-sidebar.png));[不安装皮肤](./default-browser.json)时,三者均保留原有不透明白色表面和两层渐变([截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-273-skin-hooks/default-sidebar.png))。两组中的 Runtime、Documents 和 Memory Spaces 导航均正常。这两组只检查 Sidebar 表面,没有启动任务会话或写入记忆。 + +| 基线 Sidebar | 修复后 Sidebar | +| --- | --- | +| ![基线表面](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-273-skin-hooks/before-skin-sidebar.png) | ![稳定皮肤覆盖](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-273-skin-hooks/after-skin-sidebar.png) | + +![修复后 Builtin 表面](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-273-skin-hooks/after-skin-builtin.png) + +## 验证 + +两个新增钩子回归在基线失败,修复后的定向 Client 检查 **67 项通过**。`pnpm verify` 通过:Root **1,364 项通过 / 7 项跳过**,插件 **402 项通过 / 2 项跳过**,以及类型、确定性构建、Headless 和包检查。Root 解包体积为 **1,374,875 字节**,低于未调整的 1,376,000 字节上限。`verify:plugins --skip-build` 的 **16 个独立插件仓库和 17 个制品全部通过**。 + +可选 Native 集成测试使用已校验的 CLI 0.2.9 实际运行;另一个隔离存储冒烟完成了合成事实的写入、召回和软删除。复用的基线制品校验和与已保存的 `6dc4e4` 制品完全一致;修复版重新打包。生成的 Client 注释包含构建位置,因此不将无关 tarball 的哈希差异等同于生产代码修改。 + +结构化汇总:[验证结果和制品哈希](./verification.json)、[隔离 CLI 冒烟检查](./cli-smoke.json)。 + +## 复现 + +将 [rc2-profile.json](./rc2-profile.json) 复制为新临时 Profile 中的 `package.json`,用 Node 24 在该目录执行 `npm install --ignore-scripts --registry https://registry.npmjs.org`。在基线与修复版 checkout 中分别构建 Root 和插件,再将十七个包打入各自的空目录: + +```sh +pnpm build +pnpm --workspace-concurrency=1 -r build +node --input-type=module - /absolute/empty-artifacts <<'JS' +import { readReleasePackages, createReleasePlan, packRelease } from './scripts/release.mjs' +await packRelease(createReleasePlan(await readReleasePackages(), { baseVersions: new Map() }), process.argv[2]) +JS +MNEMON_CLI_PATH=/absolute/verified/mnemon node scripts/fixtures/serve-skin-hook.mjs \ + --profile /absolute/rc2-profile --artifacts /absolute/packed-artifacts \ + --state /absolute/empty-state --skin-order before +``` + +两次对比的最后一条 fixture 命令都从本 PR checkout 执行,仅将 `--artifacts` 切换为基线或修复版 tarball 目录,并使用新的 `--state`。基线 checkout 本身不包含这些新增复现脚本。 + +[场景脚本](../../../scripts/fixtures/serve-skin-hook.mjs) 使用共享的[打包 Profile 辅助脚本](../../../scripts/fixtures/packed-web-fixture.mjs)。打开 state 中 `private.json` 记录的私有 URL,选择其 `workspace` 目录和官方 Coding 预设,再打开 Mnemon。使用新的 state,并传入 `--skin-order after` 或 `none`,可分别检查皮肤样式后置或未安装皮肤的默认表现;也可从 DSH 插件设置停用已安装皮肤。 + +本次浏览器环境对打印出的 `127.0.0.1` URL 返回 `ERR_BLOCKED_BY_CLIENT`;只将 hostname 换成 `localhost` 后,即可通过正常的 token 换 cookie 重定向访问同一回环 Host。认证机制和浏览器保护没有改变。认证 URL 和原始日志保持私有。`SIGTERM` 停止夹具;可选 `--port` 可指定稳定端口。 + +全程只使用临时合成数据。正式适配器、工具、传输、存储和浏览器均为真实实现,回环模型仅提供确定性回复。本记录不代表真实模型质量或 Windows 执行结果。Flash、Teams、lifecycle、OpenViking 服务及 Windows 可选检查保留其明确跳过记录。 diff --git a/dsh/docs/pr-assets/issue-273-skin-hooks/after-before-order-browser.json b/dsh/docs/pr-assets/issue-273-skin-hooks/after-before-order-browser.json new file mode 100644 index 000000000..3252da378 --- /dev/null +++ b/dsh/docs/pr-assets/issue-273-skin-hooks/after-before-order-browser.json @@ -0,0 +1,10 @@ +{ + "background": "rgb(224, 247, 237) linear-gradient(115deg, rgba(35, 166, 136, 0.18), rgba(238, 195, 88, 0.16)) repeat scroll 0% 0% / auto padding-box border-box", + "bg": "#e0f7ed", + "mnemonStyleIndex": 61, + "sidebarSurfaceToken": "", + "skinStyleIndex": 0, + "stableRootCount": 1, + "surface": "sidebar", + "surfaceToken": "linear-gradient(115deg, rgba(35, 166, 136, .18), rgba(238, 195, 88, .16)) #e0f7ed" +} diff --git a/dsh/docs/pr-assets/issue-273-skin-hooks/after-builtin-browser.json b/dsh/docs/pr-assets/issue-273-skin-hooks/after-builtin-browser.json new file mode 100644 index 000000000..f23d4d1d8 --- /dev/null +++ b/dsh/docs/pr-assets/issue-273-skin-hooks/after-builtin-browser.json @@ -0,0 +1,25 @@ +{ + "composerVisible": true, + "roots": [ + { + "background": "rgb(224, 247, 237) linear-gradient(115deg, rgba(35, 166, 136, 0.18), rgba(238, 195, 88, 0.16)) repeat scroll 0% 0% / auto padding-box border-box", + "surface": "builtin" + } + ], + "activeStrategies": [ + { + "checked": true, + "id": "mnemon-enhancement-include-mnemon-strategy-auto-capture" + }, + { + "checked": true, + "id": "mnemon-enhancement-include-mnemon-strategy-light-context" + }, + { + "checked": true, + "id": "mnemon-enhancement-include-mnemon-strategy-scoped" + } + ], + "settingsSaved": true, + "conversationReplyVisible": true +} diff --git a/dsh/docs/pr-assets/issue-273-skin-hooks/after-order-browser.json b/dsh/docs/pr-assets/issue-273-skin-hooks/after-order-browser.json new file mode 100644 index 000000000..70f679296 --- /dev/null +++ b/dsh/docs/pr-assets/issue-273-skin-hooks/after-order-browser.json @@ -0,0 +1,205 @@ +{ + "pairedRootCount": 1, + "rootTag": "MAIN", + "selectedPage": "状态", + "sourceTabs": [ + "状态", + "运行时", + "档案", + "记忆空间" + ], + "styles": [ + { + "fixtureStyleOrder": null, + "index": 60, + "isMnemonDefaults": true, + "plugin": "dsh-mnemon", + "pluginCss": "dsh-mnemon/src/client/MnemonView.module.css" + }, + { + "fixtureStyleOrder": "after", + "index": 80, + "isMnemonDefaults": false, + "plugin": "issue273-mnemon-skin-fixture", + "pluginCss": "issue273-mnemon-skin-fixture/skin.css" + } + ], + "surface": "sidebar", + "surfaces": { + "canvas": { + "backgroundColor": "rgb(224, 247, 237)", + "backgroundImage": "linear-gradient(115deg, rgba(35, 166, 136, 0.18), rgba(238, 195, 88, 0.16))", + "mnBackdrop": "#c4eadc", + "mnBg": "#e0f7ed", + "mnSurface": "linear-gradient(115deg, rgba(35, 166, 136, .18), rgba(238, 195, 88, .16)) #e0f7ed" + }, + "header": { + "backgroundColor": "rgb(224, 247, 237)", + "backgroundImage": "linear-gradient(115deg, rgba(35, 166, 136, 0.18), rgba(238, 195, 88, 0.16))", + "mnBackdrop": "#c4eadc", + "mnBg": "#e0f7ed", + "mnSurface": "linear-gradient(115deg, rgba(35, 166, 136, .18), rgba(238, 195, 88, .16)) #e0f7ed" + }, + "root": { + "backgroundColor": "rgb(224, 247, 237)", + "backgroundImage": "linear-gradient(115deg, rgba(35, 166, 136, 0.18), rgba(238, 195, 88, 0.16))", + "mnBackdrop": "#c4eadc", + "mnBg": "#e0f7ed", + "mnSurface": "linear-gradient(115deg, rgba(35, 166, 136, .18), rgba(238, 195, 88, .16)) #e0f7ed" + } + }, + "title": "DeepSeek Harness", + "navigation": [ + { + "page": "Runtime", + "selected": true, + "heading": "运行时记忆", + "result": "0 entries; USER.md and MEMORY.md summaries, filters and Add memory action rendered" + }, + { + "page": "Documents", + "selected": true, + "heading": "项目档案", + "result": "Empty directory, reader, search and New document action rendered" + }, + { + "page": "Memory Spaces", + "selected": true, + "heading": "记忆空间", + "result": "Overview, empty directory, secondary tabs and Create memory space action rendered" + } + ], + "browser": "Chrome via CUA, fresh task-owned tab", + "appearance": "Light selected through the fixture Settings UI", + "sessionStarted": false, + "fixture": { + "issue": 273, + "dsh": "0.1.7-rc.2", + "node": "v24.20.0", + "idleReview": false, + "preset": "standard", + "toolPresentation": "native", + "optionalStrategies": [ + "scoped", + "light-context", + "auto-capture" + ], + "skin": { + "package": "issue273-mnemon-skin-fixture", + "order": "after", + "selector": "[data-dsh-plugin=\"dsh-mnemon\"][data-dsh-part=\"mnemon-view\"]", + "installed": true + }, + "artifacts": [ + { + "name": "dsh-mnemon", + "version": "0.5.14", + "filename": "dsh-mnemon-0.5.14.tgz", + "sha256": "cc579518bc03090f098cae69b29182cec4b45ceb8a075216523bf4dc848cb396" + }, + { + "name": "dsh-mnemon-provider-byterover", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-byterover-0.5.4.tgz", + "sha256": "94677bb5a27261dd2051f67c9ae5bc41c17ea86a4ea95cbd0baee1728203633f" + }, + { + "name": "dsh-mnemon-provider-hindsight", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-hindsight-0.5.4.tgz", + "sha256": "bf527bc83d51bd7b2e510ed3be59665ae6b3ddbd4e1c3beee16c32235fa0afbb" + }, + { + "name": "dsh-mnemon-provider-holographic", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-holographic-0.5.4.tgz", + "sha256": "67f9cc593a7bcf33001e0817a4932e23cf9f0927e90b6bc2b2ccd5ef49b91259" + }, + { + "name": "dsh-mnemon-provider-honcho", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-honcho-0.5.4.tgz", + "sha256": "c102952aa1b9fdd953b25068663635a9ffad606bb39c68b56eafa538c8fab72d" + }, + { + "name": "dsh-mnemon-provider-mem0", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-mem0-0.5.4.tgz", + "sha256": "42cd225bb376d387d8e7f5503d64b6a00ddae4f5aea26b495cb9d5aa44dab3b0" + }, + { + "name": "dsh-mnemon-provider-mnemon-native", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-mnemon-native-0.5.5.tgz", + "sha256": "cb1532ec04e1bc2550456340b453717686c2bc399e742e61be529bdae5e6f812" + }, + { + "name": "dsh-mnemon-provider-openviking", + "version": "0.5.6", + "filename": "dsh-mnemon-provider-openviking-0.5.6.tgz", + "sha256": "2ba3e8e5248222733b7463c311443832a0872e0904c2b09304590234cf6c27ca" + }, + { + "name": "dsh-mnemon-provider-retaindb", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-retaindb-0.5.4.tgz", + "sha256": "a7b5f1efb4e68df2d7ccc7e6078cd8c3229c82cf14f0162a196c975cbe19727f" + }, + { + "name": "dsh-mnemon-provider-supermemory", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-supermemory-0.5.4.tgz", + "sha256": "d04b900f7f9e9359ba53b349bdbc4550fc19b350771e61df3aa451e8f67f9afd" + }, + { + "name": "dsh-mnemon-source-documents", + "version": "0.5.6", + "filename": "dsh-mnemon-source-documents-0.5.6.tgz", + "sha256": "f506da0096e7fbbbc9b024d123fb9b7423fe29f28c9718525cf472677cc61dfd" + }, + { + "name": "dsh-mnemon-source-memory-spaces", + "version": "0.5.9", + "filename": "dsh-mnemon-source-memory-spaces-0.5.9.tgz", + "sha256": "df7b3e8e7be6e4593dedff9d0e7de50e4462456d83914a568006e83b885c66c2" + }, + { + "name": "dsh-mnemon-source-runtime", + "version": "0.5.8", + "filename": "dsh-mnemon-source-runtime-0.5.8.tgz", + "sha256": "6ed0594a0491b59712027223c228c44718dc27df5949351facf51ed2d424feed" + }, + { + "name": "dsh-mnemon-strategy-auto-capture", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-auto-capture-0.5.4.tgz", + "sha256": "3816601abd83f0c5c679f3e4c9ae96c8f697e8e15f6546e975a96fd7f86b8c02" + }, + { + "name": "dsh-mnemon-strategy-default-three-tier", + "version": "0.5.5", + "filename": "dsh-mnemon-strategy-default-three-tier-0.5.5.tgz", + "sha256": "52af5caf635f2cf2b165feff1ff3d35d241e6d073771b7b92b3ea37b36ff589f" + }, + { + "name": "dsh-mnemon-strategy-light-context", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-light-context-0.5.4.tgz", + "sha256": "62947c3ff777fbb8d42724acf2a9c651d2b2302b387bb8579c59cd79f82a9e0d" + }, + { + "name": "dsh-mnemon-strategy-scoped", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-scoped-0.5.4.tgz", + "sha256": "f3f945add93d2d5f8f48f0e70f5de9360814499a552218af7fa93f574dd3457a" + } + ] + }, + "screenshot": "after-order-sidebar.png", + "screenshotVisuallyReviewed": true, + "limitations": [ + "Sidebar navigation only in this run; no memory writes, session messages, or remote Provider operations.", + "A pre-existing browser translation extension shows a small floating button at the right edge; no extension settings changed." + ], + "screenshotPage": "Runtime" +} diff --git a/dsh/docs/pr-assets/issue-273-skin-hooks/before-browser.json b/dsh/docs/pr-assets/issue-273-skin-hooks/before-browser.json new file mode 100644 index 000000000..1e013b436 --- /dev/null +++ b/dsh/docs/pr-assets/issue-273-skin-hooks/before-browser.json @@ -0,0 +1,10 @@ +{ + "background": "linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255)) repeat scroll 0% 0% / auto padding-box border-box, rgb(255, 255, 255) linear-gradient(rgb(233, 236, 242), rgb(233, 236, 242)) repeat scroll 0% 0% / auto padding-box border-box", + "bg": "#fff", + "installedSkin": true, + "mnemonStyleIndex": 61, + "skinStyleIndex": 0, + "stableRootCount": 0, + "surface": "sidebar", + "surfaceToken": "linear-gradient(#fff, #fff), linear-gradient(#e9ecf2, #e9ecf2) #fff" +} diff --git a/dsh/docs/pr-assets/issue-273-skin-hooks/cli-smoke.json b/dsh/docs/pr-assets/issue-273-skin-hooks/cli-smoke.json new file mode 100644 index 000000000..935179330 --- /dev/null +++ b/dsh/docs/pr-assets/issue-273-skin-hooks/cli-smoke.json @@ -0,0 +1,30 @@ +{ + "cliVersion": "0.2.9", + "binarySha256": "bd1b080b0f98e8703b7cd942bd9b7ea3e15f69081e26c302e129d306ac86aaf4", + "scope": "isolated acceptance store", + "beforeSoftDelete": { + "results": [ + { + "id": "452446fd-15fd-45ee-a6f8-47ed5e14ebd4", + "content": "CLI280273_CHECK: the disposable acceptance project stores durable facts in SQLite.", + "category": "fact", + "importance": 4, + "intent": "GENERAL", + "matched_via": "hybrid", + "confidence": "medium", + "score": 0.45 + } + ], + "hint": "sparse_results" + }, + "softDelete": { + "id": "452446fd-15fd-45ee-a6f8-47ed5e14ebd4", + "message": "Insight soft-deleted successfully", + "status": "deleted" + }, + "afterSoftDelete": { + "results": [], + "hint": "sparse_results" + }, + "passed": true +} diff --git a/dsh/docs/pr-assets/issue-273-skin-hooks/default-browser.json b/dsh/docs/pr-assets/issue-273-skin-hooks/default-browser.json new file mode 100644 index 000000000..7d9eddf70 --- /dev/null +++ b/dsh/docs/pr-assets/issue-273-skin-hooks/default-browser.json @@ -0,0 +1,197 @@ +{ + "pairedRootCount": 1, + "rootTag": "MAIN", + "selectedPage": "状态", + "sourceTabs": [ + "状态", + "运行时", + "档案", + "记忆空间" + ], + "styles": [ + { + "fixtureStyleOrder": null, + "index": 60, + "isMnemonDefaults": true, + "plugin": "dsh-mnemon", + "pluginCss": "dsh-mnemon/src/client/MnemonView.module.css" + } + ], + "surface": "sidebar", + "surfaces": { + "canvas": { + "backgroundColor": "rgb(255, 255, 255)", + "backgroundImage": "linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255)), linear-gradient(rgb(233, 236, 242), rgb(233, 236, 242))", + "mnBackdrop": "#e9ecf2", + "mnBg": "#fff", + "mnSurface": "linear-gradient(#fff, #fff), linear-gradient(#e9ecf2, #e9ecf2) #fff" + }, + "header": { + "backgroundColor": "rgb(255, 255, 255)", + "backgroundImage": "linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255)), linear-gradient(rgb(233, 236, 242), rgb(233, 236, 242))", + "mnBackdrop": "#e9ecf2", + "mnBg": "#fff", + "mnSurface": "linear-gradient(#fff, #fff), linear-gradient(#e9ecf2, #e9ecf2) #fff" + }, + "root": { + "backgroundColor": "rgb(255, 255, 255)", + "backgroundImage": "linear-gradient(rgb(255, 255, 255), rgb(255, 255, 255)), linear-gradient(rgb(233, 236, 242), rgb(233, 236, 242))", + "mnBackdrop": "#e9ecf2", + "mnBg": "#fff", + "mnSurface": "linear-gradient(#fff, #fff), linear-gradient(#e9ecf2, #e9ecf2) #fff" + } + }, + "title": "DeepSeek Harness", + "navigation": [ + { + "page": "Runtime", + "selected": true, + "heading": "运行时记忆", + "result": "0 entries; USER.md and MEMORY.md summaries, filters and Add memory action rendered" + }, + { + "page": "Documents", + "selected": true, + "heading": "项目档案", + "result": "Empty directory, reader, search and New document action rendered" + }, + { + "page": "Memory Spaces", + "selected": true, + "heading": "记忆空间", + "result": "Overview, empty directory, secondary tabs and Create memory space action rendered" + } + ], + "browser": "Chrome via CUA, fresh task-owned tab", + "appearance": "Light selected through Settings and checked state observed", + "sessionStarted": false, + "fixture": { + "issue": 273, + "dsh": "0.1.7-rc.2", + "node": "v24.20.0", + "idleReview": false, + "preset": "standard", + "toolPresentation": "native", + "optionalStrategies": [ + "scoped", + "light-context", + "auto-capture" + ], + "skin": { + "order": "none", + "installed": false + }, + "artifacts": [ + { + "name": "dsh-mnemon", + "version": "0.5.14", + "filename": "dsh-mnemon-0.5.14.tgz", + "sha256": "cc579518bc03090f098cae69b29182cec4b45ceb8a075216523bf4dc848cb396" + }, + { + "name": "dsh-mnemon-provider-byterover", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-byterover-0.5.4.tgz", + "sha256": "94677bb5a27261dd2051f67c9ae5bc41c17ea86a4ea95cbd0baee1728203633f" + }, + { + "name": "dsh-mnemon-provider-hindsight", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-hindsight-0.5.4.tgz", + "sha256": "bf527bc83d51bd7b2e510ed3be59665ae6b3ddbd4e1c3beee16c32235fa0afbb" + }, + { + "name": "dsh-mnemon-provider-holographic", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-holographic-0.5.4.tgz", + "sha256": "67f9cc593a7bcf33001e0817a4932e23cf9f0927e90b6bc2b2ccd5ef49b91259" + }, + { + "name": "dsh-mnemon-provider-honcho", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-honcho-0.5.4.tgz", + "sha256": "c102952aa1b9fdd953b25068663635a9ffad606bb39c68b56eafa538c8fab72d" + }, + { + "name": "dsh-mnemon-provider-mem0", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-mem0-0.5.4.tgz", + "sha256": "42cd225bb376d387d8e7f5503d64b6a00ddae4f5aea26b495cb9d5aa44dab3b0" + }, + { + "name": "dsh-mnemon-provider-mnemon-native", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-mnemon-native-0.5.5.tgz", + "sha256": "cb1532ec04e1bc2550456340b453717686c2bc399e742e61be529bdae5e6f812" + }, + { + "name": "dsh-mnemon-provider-openviking", + "version": "0.5.6", + "filename": "dsh-mnemon-provider-openviking-0.5.6.tgz", + "sha256": "2ba3e8e5248222733b7463c311443832a0872e0904c2b09304590234cf6c27ca" + }, + { + "name": "dsh-mnemon-provider-retaindb", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-retaindb-0.5.4.tgz", + "sha256": "a7b5f1efb4e68df2d7ccc7e6078cd8c3229c82cf14f0162a196c975cbe19727f" + }, + { + "name": "dsh-mnemon-provider-supermemory", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-supermemory-0.5.4.tgz", + "sha256": "d04b900f7f9e9359ba53b349bdbc4550fc19b350771e61df3aa451e8f67f9afd" + }, + { + "name": "dsh-mnemon-source-documents", + "version": "0.5.6", + "filename": "dsh-mnemon-source-documents-0.5.6.tgz", + "sha256": "f506da0096e7fbbbc9b024d123fb9b7423fe29f28c9718525cf472677cc61dfd" + }, + { + "name": "dsh-mnemon-source-memory-spaces", + "version": "0.5.9", + "filename": "dsh-mnemon-source-memory-spaces-0.5.9.tgz", + "sha256": "df7b3e8e7be6e4593dedff9d0e7de50e4462456d83914a568006e83b885c66c2" + }, + { + "name": "dsh-mnemon-source-runtime", + "version": "0.5.8", + "filename": "dsh-mnemon-source-runtime-0.5.8.tgz", + "sha256": "6ed0594a0491b59712027223c228c44718dc27df5949351facf51ed2d424feed" + }, + { + "name": "dsh-mnemon-strategy-auto-capture", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-auto-capture-0.5.4.tgz", + "sha256": "3816601abd83f0c5c679f3e4c9ae96c8f697e8e15f6546e975a96fd7f86b8c02" + }, + { + "name": "dsh-mnemon-strategy-default-three-tier", + "version": "0.5.5", + "filename": "dsh-mnemon-strategy-default-three-tier-0.5.5.tgz", + "sha256": "52af5caf635f2cf2b165feff1ff3d35d241e6d073771b7b92b3ea37b36ff589f" + }, + { + "name": "dsh-mnemon-strategy-light-context", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-light-context-0.5.4.tgz", + "sha256": "62947c3ff777fbb8d42724acf2a9c651d2b2302b387bb8579c59cd79f82a9e0d" + }, + { + "name": "dsh-mnemon-strategy-scoped", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-scoped-0.5.4.tgz", + "sha256": "f3f945add93d2d5f8f48f0e70f5de9360814499a552218af7fa93f574dd3457a" + } + ] + }, + "screenshot": "default-sidebar.png", + "screenshotPage": "Runtime", + "screenshotVisuallyReviewed": true, + "limitations": [ + "Sidebar navigation only in this run; no memory writes, session messages, or remote Provider operations.", + "A pre-existing browser translation extension shows a small floating button at the right edge; no extension settings changed." + ], + "artifactsMatchAfterOrder": true +} diff --git a/dsh/docs/pr-assets/issue-273-skin-hooks/dialog-browser.json b/dsh/docs/pr-assets/issue-273-skin-hooks/dialog-browser.json new file mode 100644 index 000000000..343d8f1d1 --- /dev/null +++ b/dsh/docs/pr-assets/issue-273-skin-hooks/dialog-browser.json @@ -0,0 +1,5 @@ +{ + "dialogHasWorkbenchHook": false, + "dialogSurfaceToken": "linear-gradient(#fff, #fff), linear-gradient(#e9ecf2, #e9ecf2) #fff", + "editorValue": "SKIN273_RUNTIME: The disposable project uses SQLite." +} diff --git a/dsh/docs/pr-assets/issue-273-skin-hooks/rc2-profile.json b/dsh/docs/pr-assets/issue-273-skin-hooks/rc2-profile.json new file mode 100644 index 000000000..8cc92e743 --- /dev/null +++ b/dsh/docs/pr-assets/issue-273-skin-hooks/rc2-profile.json @@ -0,0 +1,6 @@ +{ + "private": true, + "dependencies": { + "@deepseek-ai/dsh": "0.1.7-rc.2" + } +} diff --git a/dsh/docs/pr-assets/issue-273-skin-hooks/verification.json b/dsh/docs/pr-assets/issue-273-skin-hooks/verification.json new file mode 100644 index 000000000..b0971affb --- /dev/null +++ b/dsh/docs/pr-assets/issue-273-skin-hooks/verification.json @@ -0,0 +1,296 @@ +{ + "issue": 273, + "baseline": "6dc4e4201585223f7f1da371256da11deee34d6c", + "testedProductionCommits": [ + "fb584052" + ], + "platform": "macOS arm64", + "node": "24.20.0", + "pnpm": "10.13.1", + "dsh": "0.1.7-rc.2", + "mnemonCli": "0.2.9", + "model": "deterministic loopback fixture using the published DeepSeek adapter", + "verify": { + "passed": true, + "root": { + "passed": 1364, + "skipped": 7 + }, + "plugins": { + "passed": 402, + "skipped": 2 + }, + "rootUnpackedBytes": 1374875, + "rootUnpackedLimit": 1376000, + "nativeCliIntegrationEnabled": true + }, + "independentPlugins": { + "passed": true, + "repositories": 16, + "artifacts": 17 + }, + "commands": [ + "MNEMON_NATIVE_TEST_CLI=/absolute/verified/mnemon pnpm verify", + "pnpm verify:plugins --skip-build", + "pnpm verify:docs", + "pnpm release:intent" + ], + "optionalStrategies": [ + "scoped", + "light-context", + "auto-capture" + ], + "limits": [ + "No live Flash quality evaluation", + "No manual Windows WebUI verification", + "Manual browser reload and automated Headless restart are different coverage" + ], + "profiles": { + "before": { + "issue": 273, + "dsh": "0.1.7-rc.2", + "node": "v24.20.0", + "idleReview": false, + "preset": "standard", + "toolPresentation": "native", + "optionalStrategies": [ + "scoped", + "light-context", + "auto-capture" + ], + "skin": { + "package": "issue273-mnemon-skin-fixture", + "order": "before", + "selector": "[data-dsh-plugin=\"dsh-mnemon\"][data-dsh-part=\"mnemon-view\"]", + "installed": true + }, + "artifacts": [ + { + "name": "dsh-mnemon", + "version": "0.5.14", + "filename": "dsh-mnemon-0.5.14.tgz", + "sha256": "6275ed7a275afac2f23d8f3bc9ba6402a0dd3d8e33e87bbb99c9d08a74924356" + }, + { + "name": "dsh-mnemon-provider-byterover", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-byterover-0.5.4.tgz", + "sha256": "17dcd57cb35c0c81af2b93c92be66c2e6b7e8180d5d1cf634c589281003dddc1" + }, + { + "name": "dsh-mnemon-provider-hindsight", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-hindsight-0.5.4.tgz", + "sha256": "739dec0d42689fdbdb8183ba75f7b6afc4be899b585c03c2a85842e0e998a170" + }, + { + "name": "dsh-mnemon-provider-holographic", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-holographic-0.5.4.tgz", + "sha256": "700fa0f94387ee04f5202dbbe0d410afb7114ba2486b25c32d64d7b059245f8c" + }, + { + "name": "dsh-mnemon-provider-honcho", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-honcho-0.5.4.tgz", + "sha256": "db8ffcbbc1df42555295a1ff818e2059b5c09776a0dc4cc9c311779355b06fd7" + }, + { + "name": "dsh-mnemon-provider-mem0", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-mem0-0.5.4.tgz", + "sha256": "ccd61716ead4d9462e15a662edb59c9850d812e8660bc33c4cc574601b816255" + }, + { + "name": "dsh-mnemon-provider-mnemon-native", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-mnemon-native-0.5.5.tgz", + "sha256": "8b839950e17b086578490b9eb3ef438331afa96a85ccd588ddaf7af9649f1f32" + }, + { + "name": "dsh-mnemon-provider-openviking", + "version": "0.5.6", + "filename": "dsh-mnemon-provider-openviking-0.5.6.tgz", + "sha256": "e5b3f9c80c6c450e5cb0e0103f30f8df39550dd980aac70dc4d44e069e2bc570" + }, + { + "name": "dsh-mnemon-provider-retaindb", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-retaindb-0.5.4.tgz", + "sha256": "8962cfc5b8e777a86c8816dfd47f32943aa6a1f1973bc8882727b4b430579e3a" + }, + { + "name": "dsh-mnemon-provider-supermemory", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-supermemory-0.5.4.tgz", + "sha256": "c117f467d0eb66954ab5b27a8ae08722770699e0f9113fa4bdc9f0724485b508" + }, + { + "name": "dsh-mnemon-source-documents", + "version": "0.5.6", + "filename": "dsh-mnemon-source-documents-0.5.6.tgz", + "sha256": "7a94dee1d0e47b2d76a71d86b27d4165875f5bb3a8066b9ef68c2e5b2cb1825a" + }, + { + "name": "dsh-mnemon-source-memory-spaces", + "version": "0.5.9", + "filename": "dsh-mnemon-source-memory-spaces-0.5.9.tgz", + "sha256": "a2c913d866be46736ed8aca1dd61371319a0b40593a6258f6d6eec581245bc85" + }, + { + "name": "dsh-mnemon-source-runtime", + "version": "0.5.8", + "filename": "dsh-mnemon-source-runtime-0.5.8.tgz", + "sha256": "a0ddb4d9283277614253e4000ad8e6234c6ab1fcfb733aef1894bf080b1193d7" + }, + { + "name": "dsh-mnemon-strategy-auto-capture", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-auto-capture-0.5.4.tgz", + "sha256": "a653c1ebec85da0fe2c878e42a0eea84e20da877682fa1c9c97f5d8f8f70c074" + }, + { + "name": "dsh-mnemon-strategy-default-three-tier", + "version": "0.5.5", + "filename": "dsh-mnemon-strategy-default-three-tier-0.5.5.tgz", + "sha256": "fe4e722303ffeff5a598011d9ee6b8f5de73fe21aa2bf25ae215c750817b60df" + }, + { + "name": "dsh-mnemon-strategy-light-context", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-light-context-0.5.4.tgz", + "sha256": "eac6b61e9652ed54d03dd013bec285afe8714547a27c33ace286439e5958250b" + }, + { + "name": "dsh-mnemon-strategy-scoped", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-scoped-0.5.4.tgz", + "sha256": "c751c30b61f5ae60eca489cda936ed17a5b86dadaae538d9657dd76fe8467843" + } + ] + }, + "after": { + "issue": 273, + "dsh": "0.1.7-rc.2", + "node": "v24.20.0", + "idleReview": false, + "preset": "standard", + "toolPresentation": "native", + "optionalStrategies": [ + "scoped", + "light-context", + "auto-capture" + ], + "skin": { + "package": "issue273-mnemon-skin-fixture", + "order": "before", + "selector": "[data-dsh-plugin=\"dsh-mnemon\"][data-dsh-part=\"mnemon-view\"]", + "installed": true + }, + "artifacts": [ + { + "name": "dsh-mnemon", + "version": "0.5.14", + "filename": "dsh-mnemon-0.5.14.tgz", + "sha256": "cc579518bc03090f098cae69b29182cec4b45ceb8a075216523bf4dc848cb396" + }, + { + "name": "dsh-mnemon-provider-byterover", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-byterover-0.5.4.tgz", + "sha256": "94677bb5a27261dd2051f67c9ae5bc41c17ea86a4ea95cbd0baee1728203633f" + }, + { + "name": "dsh-mnemon-provider-hindsight", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-hindsight-0.5.4.tgz", + "sha256": "bf527bc83d51bd7b2e510ed3be59665ae6b3ddbd4e1c3beee16c32235fa0afbb" + }, + { + "name": "dsh-mnemon-provider-holographic", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-holographic-0.5.4.tgz", + "sha256": "67f9cc593a7bcf33001e0817a4932e23cf9f0927e90b6bc2b2ccd5ef49b91259" + }, + { + "name": "dsh-mnemon-provider-honcho", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-honcho-0.5.4.tgz", + "sha256": "c102952aa1b9fdd953b25068663635a9ffad606bb39c68b56eafa538c8fab72d" + }, + { + "name": "dsh-mnemon-provider-mem0", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-mem0-0.5.4.tgz", + "sha256": "42cd225bb376d387d8e7f5503d64b6a00ddae4f5aea26b495cb9d5aa44dab3b0" + }, + { + "name": "dsh-mnemon-provider-mnemon-native", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-mnemon-native-0.5.5.tgz", + "sha256": "cb1532ec04e1bc2550456340b453717686c2bc399e742e61be529bdae5e6f812" + }, + { + "name": "dsh-mnemon-provider-openviking", + "version": "0.5.6", + "filename": "dsh-mnemon-provider-openviking-0.5.6.tgz", + "sha256": "2ba3e8e5248222733b7463c311443832a0872e0904c2b09304590234cf6c27ca" + }, + { + "name": "dsh-mnemon-provider-retaindb", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-retaindb-0.5.4.tgz", + "sha256": "a7b5f1efb4e68df2d7ccc7e6078cd8c3229c82cf14f0162a196c975cbe19727f" + }, + { + "name": "dsh-mnemon-provider-supermemory", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-supermemory-0.5.4.tgz", + "sha256": "d04b900f7f9e9359ba53b349bdbc4550fc19b350771e61df3aa451e8f67f9afd" + }, + { + "name": "dsh-mnemon-source-documents", + "version": "0.5.6", + "filename": "dsh-mnemon-source-documents-0.5.6.tgz", + "sha256": "f506da0096e7fbbbc9b024d123fb9b7423fe29f28c9718525cf472677cc61dfd" + }, + { + "name": "dsh-mnemon-source-memory-spaces", + "version": "0.5.9", + "filename": "dsh-mnemon-source-memory-spaces-0.5.9.tgz", + "sha256": "df7b3e8e7be6e4593dedff9d0e7de50e4462456d83914a568006e83b885c66c2" + }, + { + "name": "dsh-mnemon-source-runtime", + "version": "0.5.8", + "filename": "dsh-mnemon-source-runtime-0.5.8.tgz", + "sha256": "6ed0594a0491b59712027223c228c44718dc27df5949351facf51ed2d424feed" + }, + { + "name": "dsh-mnemon-strategy-auto-capture", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-auto-capture-0.5.4.tgz", + "sha256": "3816601abd83f0c5c679f3e4c9ae96c8f697e8e15f6546e975a96fd7f86b8c02" + }, + { + "name": "dsh-mnemon-strategy-default-three-tier", + "version": "0.5.5", + "filename": "dsh-mnemon-strategy-default-three-tier-0.5.5.tgz", + "sha256": "52af5caf635f2cf2b165feff1ff3d35d241e6d073771b7b92b3ea37b36ff589f" + }, + { + "name": "dsh-mnemon-strategy-light-context", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-light-context-0.5.4.tgz", + "sha256": "62947c3ff777fbb8d42724acf2a9c651d2b2302b387bb8579c59cd79f82a9e0d" + }, + { + "name": "dsh-mnemon-strategy-scoped", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-scoped-0.5.4.tgz", + "sha256": "f3f945add93d2d5f8f48f0e70f5de9360814499a552218af7fa93f574dd3457a" + } + ] + } + } +} diff --git a/dsh/docs/pr-assets/issue-274-profile-generation/README.md b/dsh/docs/pr-assets/issue-274-profile-generation/README.md new file mode 100644 index 000000000..2ba3dd153 --- /dev/null +++ b/dsh/docs/pr-assets/issue-274-profile-generation/README.md @@ -0,0 +1,89 @@ +# Desktop profile generations — issue #274 + +[中文](./README.zh-CN.md) · [Issue](https://github.com/omdsh-dev/dsh-mnemon/issues/274) · [Compatibility](../../en/reference/compatibility.md) + +Recorded on 2026-09-25, from main `84d469ffa838a36fa579295d94029fcac8ac058e` plus this fix. The failure is a framework capability mismatch, not a group-relative resolution bug. The production change keeps the existing bundle and chooses the ordinary schema before attempting to import a live schema on older hosts. + +## Original runtime and failure + +The fixture uses the unchanged [Desktop v0.9.2 macOS arm64 release](https://github.com/dataelement/dsh-desktop/releases/tag/v0.9.2), containing DSH `0.1.5-rc.2`, Cosmokit `1.8.3`, and the shipped market installer and patched Cordis loader. The ZIP is 212,666,216 bytes; its SHA-256 matches the release asset digest: + +```text +7832934f5c1c6606826791c625db5eaf5db4ab2e20bd72b2a3585d7d486dfa4c +``` + +`installGeneration`, `writeDesired`, and `projectGenerations` install published `dsh-mnemon@0.5.13` with `autoInstallPeers: false`. The result is exactly the reported generation `dsh-mnemon+0.5.13+5b2e12f917e4`. The official installer removes framework singletons from that generation. The aliased `schemastery-live` package consequently imports host Cosmokit `1.8.3`, whose exports do not include `createVolatile` or `isVolatile`. + +The old root imports that schema unconditionally. A direct import fails with `SyntaxError: ... does not provide an export named 'createVolatile'`. The Desktop loader's real fallback first tries a bare import from its application directory, then resolves the root through the profile. It catches the second import's error and rethrows the first `ERR_MODULE_NOT_FOUND`, hiding the schema failure. The original Group shares its parent's EntryTree; removing the Group leaves the same failure. + +| Probe | Before | After | +|---|---|---| +| Complete built root; public Cosmokit 1.8.3 | Missing `createVolatile`; regression fails | Ordinary Config; passes | +| Complete built root; public Cosmokit 1.8.4 | Passes | Live Config; passes | +| Original Desktop fallback, no native addons | `ERR_MODULE_NOT_FOUND` | Complete root imports with PlainConfig | +| Grouped / flattened fallback diagnostics | Both fail importing the root | Bundle remains grouped | +| Normal Desktop Web startup with native addons | Original SyntaxError, exit 1, no URL | Real WebUI starts | +| Shipped Electron Node 24.18.1 direct / fallback | Both fail with their respective errors | Both pass | + +The fallback probes use `--no-addons` to make the original loader select its existing fallback. They do not alter loader fields, fake exports, or replace resolution. Full WebUI and Headless checks retain native addons. The WebUI uses the release's actual `harness-node-entry.mjs` and modules under Node `24.20.0`; the additional import probes use the release's own Electron binary with `ELECTRON_RUN_AS_NODE=1`. No DSH source or resource file was modified. [Release-file hashes](./logs/desktop-file-hashes.json) identify the inspected runtime files. + +Full sanitized diagnostics: [direct import](./logs/before-direct.log), [fallback](./logs/before-fallback.log), [grouped](./logs/before-grouped.log), [flat](./logs/before-flat.log), [normal Web startup](./logs/before-web.log), [fixed fallback](./logs/after-fallback.log), [Electron before](./logs/electron-before-import.log), [Electron after](./logs/electron-after-import.log). Fixture and checkout paths are replaced with placeholders. Before startup never reached an HTTP URL, so there is no before WebUI screenshot. + +## Verification + +The regression copies the complete built package into a real profile/generation symlink layout and uses public Cosmokit packages, not mocks. It failed on the unchanged main build with 1.8.3, while 1.8.4 passed. After rebuilding the fix, both generation tests and all seven existing live-config tests passed: [before](./logs/regression-before.log), [after](./logs/regression-after.log). + +- [Legacy Settings](./logs/legacy-settings.json): real Desktop profile activation, ordinary Config, existing `mnemon` / `mnemon-ui` namespaces, validated save, full restart with unchanged saved bytes, all three Strategy extensions, and the root gate disposing all eight entries. +- [DSH 0.1.7-rc.1](./logs/live-rc1.json): public npm profile runner; retained root/UI/View/Source settings recover under their existing namespaces; live update retains the owning fiber; stale revision is rejected; one Source independently re-enables; the `!!js 15 * 1000` expression and backup bytes survive saving and restart; the root gate still disables all eight entries. +- [Desktop Headless](./logs/after-checks.json): actual generation, complete representative Mnemon tool surface and simultaneous scoped/light-context/auto-capture contributions; disabling the root removes Mnemon tools without pending dependents and the Host still completes its turn. +- Real Mnemon native CLI `0.2.9`, verified against the official archive digest, is selected in every disposable Host. The WebUI's actual `mnemon_status` result reports `healthy: true` and `commandFound: true`. +- `pnpm run verify` passed under Node 24.20.0 / pnpm 10.13.1: 98 Root files, 1,351 passed / 6 skipped Root tests; all 16 plugin suites, 382 passed / 2 skipped tests; deterministic builds, real Headless and package/export checks passed. +- Sequential `pnpm run verify:plugins --skip-build` passed: 16 independent plugin repositories and 17 tarballs; isolated install/type/test/build checks, an external SDK/Client consumer, packed Starter Headless and all three optional Strategies together. All four Vitest worker environment variables and `MNEMON_PLUGIN_VERIFY_CONCURRENCY` were `1`; no test timeout was changed. [Verification summary and full-log hashes](./logs/verification-summary.json). + +The skipped cases are the five opt-in live-model stress/quality tests, one separately supplied V4 codec check, one Windows-only smoke test and one unconfigured live OpenViking test. Native Mnemon integration was enabled and passed. + +The tested fixed root tarball SHA-256 is `13ce7382311342126dc0089e05dfbd841aaf4ac79ae0b0110978967a6b620b02`. Child packages use the Starter's pinned published versions. Third-party Provider backends are installed as private children but not contacted; this check makes no claim about their remote availability. Storage formats, RPC authority, client/host scope, bundle IDs and namespaces are unchanged. + +The disposable profiles disable the unrelated shell/PTY tools; the newer settings probe also disables their PTC/workflow dependents. Native attachment services stay enabled for full WebUI runs. + +## Real WebUI acceptance + +Only model decisions are deterministic. Browser, DSH tools, Runtime storage, CLI discovery, settings writes and restart are real. The reused [Issue 261 model fixture](../issue-261-dsh-slots/harness/compatibility-model.mjs) retains its `compatibility-261` trigger and visible “Issue 261” test text; these screenshots were captured in this Issue 274 Desktop generation environment. + +The conversation successfully calls Runtime add and status: + +![Actual tool receipts and healthy CLI](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-274-profile-generation/images/after-tool-receipts.png) + +The UI saved `idleReview.minIntervalMs = 310000`. A new Host process reopened the same profile; the setting remained visible and the settings file was byte-identical: + +![Saved interval after a cold Host restart](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-274-profile-generation/images/after-cold-restart-settings.png) + +The single synthetic work-memory entry remained visible, and `runtime/memories.json` was byte-identical: + +![Runtime entry after a cold Host restart](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-274-profile-generation/images/after-cold-restart-runtime.png) + +## Reproduction + +Use an extracted official Desktop release, Node 24, pnpm 10.13.1, a verified native Mnemon CLI and disposable directories. No personal `DSH_HOME`, model credentials or memory is used. `DSH_DESKTOP_RESOURCES`, `MNEMON_CLI`, `PNPM_ENTRY`, and `FIXED_PACKAGE` below refer to those explicit local files; `FIXED_PACKAGE` is a root tarball produced after `pnpm build` and `pnpm pack`. + +```bash +node docs/pr-assets/issue-274-profile-generation/harness/desktop-generation.mjs \ + --resources "$DSH_DESKTOP_RESOURCES" --run-root /tmp/issue274-before \ + --package dsh-mnemon@0.5.13 --pnpm-entry "$PNPM_ENTRY" \ + --native-cli "$MNEMON_CLI" --serve true + +node --no-addons docs/pr-assets/issue-274-profile-generation/harness/desktop-import.mjs \ + --state /tmp/issue274-before/server.json --mode grouped +# Repeat with --mode flat, direct, and import; all four fail before the fix. + +node docs/pr-assets/issue-274-profile-generation/harness/desktop-generation.mjs \ + --resources "$DSH_DESKTOP_RESOURCES" --run-root /tmp/issue274-after \ + --package "$FIXED_PACKAGE" --pnpm-entry "$PNPM_ENTRY" \ + --native-cli "$MNEMON_CLI" --serve true +``` + +Open the URL from the mode-0600 `server.json` locally; never attach the file or paste its access key into a report. Use the fixture workspace, select Mnemon E2E and send `compatibility-261`. Save the interval in Memory System settings, send `SIGUSR2` to the harness PID from that JSON, then reopen its updated URL to confirm settings and Runtime content. `SIGTERM` stops the disposable server. A restart does not reinstall packages or rewrite settings. + +For Headless, use a fresh run directory and append `--profile headless`; repeat with `--root-disabled true`. Desktop's projector keeps only its Web-owned builtins, so the harness adds the official Headless bundle to its disposable profile after projection. The resulting real model requests and tool surface are asserted before `headlessVerified` is written. + +`profile-legacy.mjs` takes the same resource/package/runtime arguments and checks old Settings through the original CLI's profile runner. `profile-live.mjs` takes `--desktop-resources`, `--framework-modules` pointing at a separate public npm DSH `0.1.7-rc.1` installation, and the same package/pnpm/run-root/native-cli arguments. It uses the Desktop installer for the generation and the unmodified newer profile runner for live settings. All raw model requests, access URLs and process logs remain local; the repository retains only these sanitized diagnostics, aggregate assertions and three screenshots. diff --git a/dsh/docs/pr-assets/issue-274-profile-generation/README.zh-CN.md b/dsh/docs/pr-assets/issue-274-profile-generation/README.zh-CN.md new file mode 100644 index 000000000..57de6ab33 --- /dev/null +++ b/dsh/docs/pr-assets/issue-274-profile-generation/README.zh-CN.md @@ -0,0 +1,89 @@ +# Desktop profile generation — issue #274 + +[English](./README.md) · [Issue](https://github.com/omdsh-dev/dsh-mnemon/issues/274) · [兼容性](../../zh-CN/reference/compatibility.md) + +记录日期为 2026-09-25,基于 main `84d469ffa838a36fa579295d94029fcac8ac058e` 加本修复。根因是框架能力不匹配,并非 group 改变了解析基准。生产代码保留原 bundle,在尝试导入动态 schema 之前为旧宿主选择普通 schema。 + +## 原版运行时与失败 + +夹具使用未经修改的 [Desktop v0.9.2 macOS arm64 release](https://github.com/dataelement/dsh-desktop/releases/tag/v0.9.2),其中包含 DSH `0.1.5-rc.2`、Cosmokit `1.8.3`、市场安装器和 Desktop 自带的 Cordis loader。ZIP 为 212,666,216 字节,SHA-256 与 release asset digest 一致: + +```text +7832934f5c1c6606826791c625db5eaf5db4ab2e20bd72b2a3585d7d486dfa4c +``` + +调用原版 `installGeneration`、`writeDesired` 和 `projectGenerations`,以 `autoInstallPeers: false` 安装正式 `dsh-mnemon@0.5.13`,得到与报告完全相同的 generation:`dsh-mnemon+0.5.13+5b2e12f917e4`。官方安装器删除 generation 内的框架单例,因此别名包 `schemastery-live` 实际导入宿主的 Cosmokit `1.8.3`,后者不提供 `createVolatile` 和 `isVolatile`。 + +旧 root 无条件导入该 schema,直接导入时出现 `SyntaxError: ... does not provide an export named 'createVolatile'`。Desktop loader 的真实回退路径先从应用目录裸导入,再通过 profile 解析 root;第二次导入的错误被 catch 吞掉,最后重抛第一次的 `ERR_MODULE_NOT_FOUND`。Group 与父级共享 EntryTree,移除 Group 后错误相同。 + +| 检查 | 修复前 | 修复后 | +|---|---|---| +| 完整构建 root;正式 Cosmokit 1.8.3 | 缺少 `createVolatile`,回归失败 | 普通 Config,通过 | +| 完整构建 root;正式 Cosmokit 1.8.4 | 通过 | 动态 Config,通过 | +| 原版 Desktop 回退,不加载 native addon | `ERR_MODULE_NOT_FOUND` | 完整 root 以 PlainConfig 成功导入 | +| grouped / flat 回退诊断 | 两者均在 root 导入失败 | 保留 grouped bundle | +| 启用 native addon 的正常 Web 启动 | 原始 SyntaxError,退出 1,无 URL | 真实 WebUI 启动 | +| 自带 Electron Node 24.18.1 直接 / 回退导入 | 分别出现上述两种错误 | 均通过 | + +回退探针通过 `--no-addons` 让原版 loader 选择已有回退分支,不改 loader 字段、不伪造导出、不替换解析。完整 WebUI 和 Headless 保留 native addon。WebUI 在 Node `24.20.0` 下运行 release 自带的 `harness-node-entry.mjs` 和模块;补充导入检查以 `ELECTRON_RUN_AS_NODE=1` 运行 release 自带的 Electron。未修改 DSH 源码或资源文件。[运行时文件哈希](./logs/desktop-file-hashes.json)记录了受检文件。 + +完整脱敏日志:[直接导入](./logs/before-direct.log)、[回退导入](./logs/before-fallback.log)、[grouped](./logs/before-grouped.log)、[flat](./logs/before-flat.log)、[正常 Web 启动](./logs/before-web.log)、[修复后回退](./logs/after-fallback.log)、[Electron 修复前](./logs/electron-before-import.log)、[Electron 修复后](./logs/electron-after-import.log)。本地夹具与 checkout 路径已替换为占位符。修复前没有产生 HTTP URL,因此没有伪造 before WebUI 截图。 + +## 验证 + +回归测试将完整构建包放入真实 profile/generation 符号链接结构,使用正式 Cosmokit 包,没有 mock。原 main 构建在 1.8.3 下失败、1.8.4 下通过;修复后两个 generation 测试和已有七个 live-config 测试均通过:[修复前](./logs/regression-before.log)、[修复后](./logs/regression-after.log)。 + +- [旧 Settings](./logs/legacy-settings.json):真实 Desktop profile 激活、普通 Config、原 `mnemon` / `mnemon-ui` 命名空间、校验后保存、重启后保存字节不变、三个 Strategy 扩展同时启用、根开关释放全部八个入口。 +- [DSH 0.1.7-rc.1](./logs/live-rc1.json):使用正式 npm profile runner;旧 root/UI/View/Source 设置按原命名空间恢复;动态更新保留同一 fiber;拒绝过期 revision;Source 可独立重新启用;`!!js 15 * 1000` 表达式与备份字节在保存、重启后保留;根开关仍关闭全部八个入口。 +- [Desktop Headless](./logs/after-checks.json):真实 generation 暴露完整代表性 Mnemon 工具,scoped/light-context/auto-capture 同时贡献;关闭 root 后不残留 Mnemon 工具或 pending 依赖,宿主仍正常完成回合。 +- 每个隔离宿主都显式选择真实 Mnemon native CLI `0.2.9`,制品已核对官方 digest。WebUI 的真实 `mnemon_status` 返回 `healthy: true`、`commandFound: true`。 +- `pnpm run verify` 在 Node 24.20.0 / pnpm 10.13.1 下通过:Root 98 个文件通过,1,351 项测试通过 / 6 项跳过;16 个插件合计 382 项通过 / 2 项跳过;确定性构建、真实 Headless 与包/导出检查全部通过。 +- 串行 `pnpm run verify:plugins --skip-build` 通过:16 个独立插件仓库、17 个 tarball,独立安装/类型/测试/构建、外部 SDK/Client 消费者、打包 Starter Headless 及三种可选 Strategy 同时启用均通过。四个 Vitest worker 环境变量和 `MNEMON_PLUGIN_VERIFY_CONCURRENCY` 均为 `1`,没有调整测试超时。[验证汇总与完整日志哈希](./logs/verification-summary.json)。 + +跳过项为五个需显式启用的真实模型压力/质量测试、一个需要单独提供 V4 codec 的检查、一个 Windows 专用 smoke 测试和一个未配置服务的真实 OpenViking 测试;Native Mnemon 集成已启用并通过。 + +受检修复版 root tarball SHA-256 为 `13ce7382311342126dc0089e05dfbd841aaf4ac79ae0b0110978967a6b620b02`。子包使用 Starter 固定的正式发布版本。第三方 Provider 以私有子项安装,但未连接远端服务,本次不声称其远端可用性。存储格式、RPC 权限、client/host scope、bundle ID 与命名空间保持不变。 + +一次性 profile 停用无关的 shell/PTY 工具,新版设置探针同时停用其 PTC/workflow 依赖方。完整 WebUI 运行保留原生附件服务。 + +## 真实 WebUI 验收 + +只有模型决策是确定性夹具;浏览器、DSH 工具、Runtime 存储、CLI 发现、设置写入与重启均为真实路径。复用的 [Issue 261 模型夹具](../issue-261-dsh-slots/harness/compatibility-model.mjs)保留 `compatibility-261` 触发词和可见的 “Issue 261” 测试文本,以下截图均来自本次 Issue 274 Desktop generation 环境。 + +对话成功调用 Runtime add 和 status: + +![真实工具回执与健康 CLI](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-274-profile-generation/images/after-tool-receipts.png) + +GUI 保存 `idleReview.minIntervalMs = 310000`。新的 Host 进程打开同一 profile 后,设置仍可见,设置文件逐字节相同: + +![冷重启后保存的审查间隔](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-274-profile-generation/images/after-cold-restart-settings.png) + +一条合成的工作记忆仍可见,`runtime/memories.json` 逐字节相同: + +![冷重启后保留的 Runtime 记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-274-profile-generation/images/after-cold-restart-runtime.png) + +## 复现 + +使用解压的官方 Desktop release、Node 24、pnpm 10.13.1、已验证的 native Mnemon CLI 和一次性目录,不使用个人 `DSH_HOME`、模型凭据或记忆。以下 `DSH_DESKTOP_RESOURCES`、`MNEMON_CLI`、`PNPM_ENTRY`、`FIXED_PACKAGE` 均指向显式准备的本地文件;`FIXED_PACKAGE` 为 `pnpm build`、`pnpm pack` 之后生成的 root tarball。 + +```bash +node docs/pr-assets/issue-274-profile-generation/harness/desktop-generation.mjs \ + --resources "$DSH_DESKTOP_RESOURCES" --run-root /tmp/issue274-before \ + --package dsh-mnemon@0.5.13 --pnpm-entry "$PNPM_ENTRY" \ + --native-cli "$MNEMON_CLI" --serve true + +node --no-addons docs/pr-assets/issue-274-profile-generation/harness/desktop-import.mjs \ + --state /tmp/issue274-before/server.json --mode grouped +# 再运行 --mode flat、direct 和 import;修复前四者均失败。 + +node docs/pr-assets/issue-274-profile-generation/harness/desktop-generation.mjs \ + --resources "$DSH_DESKTOP_RESOURCES" --run-root /tmp/issue274-after \ + --package "$FIXED_PACKAGE" --pnpm-entry "$PNPM_ENTRY" \ + --native-cli "$MNEMON_CLI" --serve true +``` + +在本地读取权限为 0600 的 `server.json` 并打开其中 URL,勿把该文件或 access key 贴入报告。选择夹具 workspace 与 Mnemon E2E,发送 `compatibility-261`。在记忆系统设置页保存间隔,向 JSON 中的 harness PID 发送 `SIGUSR2`,再打开更新后的 URL 检查设置和 Runtime;用 `SIGTERM` 结束服务。重启不会重新安装包或重写设置。 + +Headless 使用新的 run 目录并追加 `--profile headless`,再以 `--root-disabled true` 重跑。Desktop projector 仅保留其 Web 内置 bundle,因此夹具在投影完成后向一次性 profile 添加官方 Headless bundle;真实模型请求和工具面通过断言后才写入 `headlessVerified`。 + +`profile-legacy.mjs` 接受相同的 resource/package/runtime 参数,通过原版 CLI 的 profile runner 验证旧 Settings。`profile-live.mjs` 接受 `--desktop-resources`、指向独立正式 npm DSH `0.1.7-rc.1` 安装的 `--framework-modules`,以及相同的 package/pnpm/run-root/native-cli 参数;使用 Desktop 安装器构造 generation,通过未修改的新宿主 profile runner 验证动态设置。原始模型请求、访问 URL 和进程日志只保留本地,仓库仅保存脱敏诊断、汇总断言和三张截图。 diff --git a/dsh/docs/pr-assets/issue-274-profile-generation/harness/desktop-generation.mjs b/dsh/docs/pr-assets/issue-274-profile-generation/harness/desktop-generation.mjs new file mode 100644 index 000000000..51305e1f6 --- /dev/null +++ b/dsh/docs/pr-assets/issue-274-profile-generation/harness/desktop-generation.mjs @@ -0,0 +1,164 @@ +import assert from 'node:assert/strict' +import { spawn } from 'node:child_process' +import { createServer } from 'node:http' +import { appendFile, mkdir, readFile, writeFile } from 'node:fs/promises' +import { createHash } from 'node:crypto' +import { join, resolve } from 'node:path' +import { pathToFileURL } from 'node:url' +import { compatibilityModel, sendModelReply } from '../../issue-261-dsh-slots/harness/compatibility-model.mjs' + +const args = new Map() +for (let i = 2; i < process.argv.length; i += 2) args.set(process.argv[i], process.argv[i + 1]) +for (const key of ['--resources', '--run-root', '--package', '--pnpm-entry', '--native-cli']) assert(args.get(key), `supply ${key}`) +const resources = resolve(args.get('--resources')) +const runRoot = resolve(args.get('--run-root')) +const modules = join(resources, 'app/node_modules') +const dshHome = join(runRoot, 'dsh-home') +const profile = args.get('--profile') ?? 'web' +assert(['web', 'headless'].includes(profile)) +const profileDir = join(dshHome, 'profiles', profile) +const workspace = join(runRoot, 'workspace') +const data = join(runRoot, 'mnemon-data') +const cli = join(modules, '@deepseek-ai/dsh/lib/bin.js') +const nativeCli = resolve(args.get('--native-cli')) +await Promise.all([profileDir, workspace, data].map(path => mkdir(path, { recursive: true }))) +const log = (name, value) => appendFile(join(runRoot, name), value, { mode: 0o600 }) +const official = path => import(pathToFileURL(join(modules, path)).href) +const { installGeneration } = await official('dsh-desktop-market-installer/generations/installer.mjs') +const { writeDesired } = await official('dsh-desktop-market-installer/generations/registry.mjs') +const { projectGenerations } = await official('dsh-desktop-market-installer/generations/projection.mjs') +const generation = await installGeneration({ + dshHome, profile, pluginSpec: args.get('--package'), expectedPluginName: 'dsh-mnemon', + registry: 'https://registry.npmjs.org', autoInstallPeers: false, + nodeExecutablePath: process.execPath, pnpmEntryPath: resolve(args.get('--pnpm-entry')), + onTrace: line => { void log('install.log', line + '\n') }, + onOutput: line => { void log('install-output.log', line) }, +}) +assert.equal(generation.ok, true, generation.detail) +await writeFile(join(profileDir, 'package.json'), JSON.stringify({ + name: `issue274-${profile}`, private: true, dependencies: {}, + dsh: { profile: { bundles: ['@deepseek-ai/dsh-base', profile === 'web' ? '@deepseek-ai/dsh-web-app' : '@deepseek-ai/dsh-headless'], patchReload: 'startup' } }, +}, null, 2)) +await writeDesired(dshHome, [generation.generation.id]) +await projectGenerations(dshHome, profile) +if (profile === 'headless') { + // Desktop preserves only its own Web bundles during market projection. + // Add the official Headless bundle to this disposable profile afterwards. + const path = join(profileDir, 'package.json') + const manifest = JSON.parse(await readFile(path, 'utf8')) + manifest.dsh.profile.bundles.push('@deepseek-ai/dsh-headless') + await writeFile(path, JSON.stringify(manifest, null, 2)) +} +const disabled = ['subprocess', 'open-in-app', 'bash-sandbox', 'pwsh-sandbox', 'tool-bash', 'tool-pwsh', 'permission', 'tool-fs-search', 'directory-picker'] +let patch = disabled.map(id => `- id: ${id}\n disabled: true\n`).join('') +patch += ['scoped', 'light-context', 'auto-capture'].map(id => `- id: mnemon-strategy-${id}\n disabled: false\n`).join('') +if (args.get('--root-disabled') === 'true') patch += '- id: mnemon\n disabled: true\n' +if (profile === 'web') { + patch += '- id: agent-presets\n config:\n default: mnemon-e2e\n' + + '- insert:\n - id: e2e-directory-picker\n name: "@deepseek-ai/dsh-host-directory-picker-browse"\n - id: e2e-directory-picker-ui\n name: "@deepseek-ai/dsh-client-ui-directory-picker-browse"\n' + const preset = join(dshHome, '.agent-presets/mnemon-e2e') + await mkdir(preset, { recursive: true }) + await writeFile(join(preset, 'preset.yml'), 'name: Mnemon E2E\ndescription: Disposable Issue 274 generation fixture.\norder: 0\n') + await writeFile(join(preset, 'agent.cordis.yml'), '- id: persona\n name: "@deepseek-ai/dsh-persona"\n config:\n prefix: You are testing Mnemon with an isolated deterministic model.\n') +} +await writeFile(join(profileDir, 'cordis.patch.yml'), patch) +await writeFile(join(dshHome, 'settings.yaml'), `# Synthetic legacy settings; keep on restart.\nmnemon:\n cliPath: ${JSON.stringify(nativeCli)}\n defaultRecallLimit: 7\n idleReview:\n enabled: false\n persistenceStrategy:\n mode: manual\n`) +await writeFile(join(workspace, 'README.md'), '# Issue 274 disposable workspace\n') +const bytes = [] +for (const path of [ + 'host-module-fallback.mjs', 'harness-node-entry.mjs', + 'app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js', + 'app/node_modules/@deepseek-ai/dsh-app-boot/lib/index.js', + 'app/node_modules/@deepseek-ai/cosmokit/lib/index.js', + 'app/node_modules/dsh-desktop-market-installer/generations/installer.mjs', + 'app/node_modules/dsh-desktop-market-installer/generations/projection.mjs', +]) bytes.push({ path, sha256: createHash('sha256').update(await readFile(join(resources, path))).digest('hex') }) +await writeFile(join(runRoot, 'provenance.json'), JSON.stringify({ node: process.version, generation, bytes }, null, 2)) +let state = { pid: process.pid, runRoot, profile, profileDir, dshHome, workspace, data, nativeCli, resources, generation: generation.generation, runtime: process.version, addonsDisabled: args.get('--no-addons') === 'true' } +let writes = Promise.resolve() +function status() { writes = writes.then(() => writeFile(join(runRoot, 'server.json'), JSON.stringify(state, null, 2), { mode: 0o600 })); return writes } +await status() +if (args.get('--serve') !== 'true') { console.log(`Prepared generation: ${join(runRoot, 'server.json')}`); process.exit(0) } +const decide = compatibilityModel(event => { void log('model-events.jsonl', JSON.stringify(event) + '\n') }) +let requests = 0 +const modelRequests = [] +const model = createServer(async (request, response) => { + try { + let body = '' + for await (const chunk of request) body += chunk + const input = JSON.parse(body) + modelRequests.push(input) + await log('model-requests.jsonl', JSON.stringify(input) + '\n') + const reply = profile === 'headless' ? 'ISSUE274_HEADLESS_READY' : decide(input) + sendModelReply(response, input, request.url, reply, `issue274-${++requests}`) + } catch (error) { response.writeHead(500); response.end(String(error)); void log('model-errors.log', String(error) + '\n') } +}) +await new Promise(done => model.listen(0, '127.0.0.1', done)) +const env = { ...process.env, DSH_HOME: dshHome, MNEMON_DATA_DIR: data, MNEMON_CLI_PATH: nativeCli, DSH_TELEMETRY_DISABLED: '1', DSH_TELEMETRY_MODE: 'DISABLED', DEEPSEEK_BASE_URL: `http://127.0.0.1:${model.address().port}`, DEEPSEEK_API_KEY: 'issue274-disposable-fixture' } +for (const key of ['NODE_PATH', 'NODE_OPTIONS', 'OPENAI_API_KEY', 'ANTHROPIC_API_KEY']) delete env[key] +let web +let stopping = false +let restarting = false +function launch() { + // The optional no-addons probe exercises Desktop's genuine fallback; full + // WebUI runs retain the native attachment/sandbox services they require. + const argv = [...(args.get('--no-addons') === 'true' ? ['--no-addons'] : []), join(resources, 'harness-node-entry.mjs'), cli, + ...(profile === 'web' ? ['web', '--no-open', '--host', '127.0.0.1', '--port', '0'] : ['--profile', 'headless', 'Verify Mnemon tools.'])] + web = spawn(process.execPath, argv, { cwd: workspace, env, stdio: ['ignore', 'pipe', 'pipe'] }) + state = { ...state, webPid: web.pid, argv, startedAt: new Date().toISOString(), url: undefined, exitCode: undefined } + void status() + let output = '' + let announced = false + const record = chunk => { + output += chunk + void log('web.log', chunk) + if (output.includes("Cannot find package 'dsh-mnemon'")) { state.activationFailure = "Cannot find package 'dsh-mnemon'"; void status() } + const url = [...output.matchAll(/https?:\/\/127\.0\.0\.1:\d+[^\s\x1b]*/g)].map(item => item[0]).find(url => !url.startsWith(env.DEEPSEEK_BASE_URL)) + if (url && state.url !== url) { state.url = url; void status(); if (!announced) { console.log(`WebUI state: ${join(runRoot, 'server.json')}`); announced = true } } + } + web.stdout.on('data', record) + web.stderr.on('data', record) + web.once('error', error => { state.error = error.message; void stop() }) + web.once('exit', code => { + state.exitCode = code + if (profile === 'headless' && code === 0) { + try { + const names = new Set(modelRequests.flatMap(request => (request.tools ?? []).map(tool => tool.function?.name))) + const required = ['mnemon_status', 'mnemon_recall', 'mnemon_document_search', 'mnemon_document_create', 'mnemon_runtime_memory', 'mnemon_remember', 'mnemon_view_route', 'mnemon_view_action'] + if (args.get('--root-disabled') === 'true') assert(![...names].some(name => name?.startsWith('mnemon_'))) + else { + for (const name of required) assert(names.has(name), `missing ${name}`) + const prompt = JSON.stringify(modelRequests[0]?.messages) + for (const marker of ['Source order expresses preference', 'MNEMON OPTIONAL AUTO CAPTURE']) assert(prompt.includes(marker), `missing Strategy contribution: ${marker}`) + } + assert(output.includes('ISSUE274_HEADLESS_READY')) + assert(!output.includes('waiting for service:')) + state.headlessVerified = true + } catch (error) { state.verificationError = error.message; process.exitCode = 1 } + } + if (code !== 0 && !stopping && !restarting) process.exitCode = code ?? 1 + void status() + if (!stopping && !restarting) void stop() + }) +} +async function stop() { + if (stopping) return + stopping = true + if (web?.exitCode === null) { web.kill('SIGTERM'); await new Promise(done => web.once('exit', done)) } + model.closeAllConnections() + await new Promise(done => model.close(done)) + state.stoppedAt = new Date().toISOString() + await status() +} +process.once('SIGTERM', () => { void stop() }) +process.once('SIGINT', () => { void stop() }) +process.on('SIGUSR2', async () => { + if (stopping || restarting || web?.exitCode !== null) return + restarting = true + web.kill('SIGTERM') + await new Promise(done => web.once('exit', done)) + restarting = false + launch() +}) +console.log(`Starting Desktop generation fixture; private state: ${join(runRoot, 'server.json')}`) +launch() diff --git a/dsh/docs/pr-assets/issue-274-profile-generation/harness/desktop-import.mjs b/dsh/docs/pr-assets/issue-274-profile-generation/harness/desktop-import.mjs new file mode 100644 index 000000000..a921110fc --- /dev/null +++ b/dsh/docs/pr-assets/issue-274-profile-generation/harness/desktop-import.mjs @@ -0,0 +1,56 @@ +import assert from 'node:assert/strict' +import { lstat, readFile, unlink, writeFile } from 'node:fs/promises' +import { createRequire } from 'node:module' +import { join } from 'node:path' +import { pathToFileURL } from 'node:url' + +const args = new Map() +for (let i = 2; i < process.argv.length; i += 2) args.set(process.argv[i], process.argv[i + 1]) +assert(args.get('--state'), 'supply the private desktop-generation server.json') +const state = JSON.parse(await readFile(args.get('--state'), 'utf8')) +const mode = args.get('--mode') ?? 'import' +assert(['direct', 'import', 'grouped', 'flat'].includes(mode)) +const modules = join(state.resources, 'app/node_modules') +Object.assign(process.env, { DSH_HOME: state.dshHome, MNEMON_DATA_DIR: state.data }) +const official = path => import(pathToFileURL(path).href) +const { registerHostModuleFallback } = await official(join(state.resources, 'host-module-fallback.mjs')) +registerHostModuleFallback(join(modules, '@deepseek-ai/dsh/lib/bin.js')) +const { boot, loadProfileDirectory, healProfilesModuleFallback } = await official(join(modules, '@deepseek-ai/dsh-app-boot/lib/index.js')) +const anchor = join(modules, '@deepseek-ai/dsh/package.json') +const loaded = loadProfileDirectory('issue274', state.profileDir, anchor) +const shared = join(state.dshHome, 'profiles/node_modules') +// The public healer replaces the installer's host symlink with its normal +// fallback directory, including real transitive Source/Provider projections. +if ((await lstat(shared)).isSymbolicLink()) await unlink(shared) +await healProfilesModuleFallback({ installAnchor: anchor, profile: loaded, home: state.dshHome }) +if (mode === 'direct') { + const entry = createRequire(join(state.profileDir, 'package.json')).resolve('dsh-mnemon') + const plugin = await official(entry) + assert.equal(plugin.name, 'dsh-mnemon') + console.log('Direct generation import succeeded') +} else if (mode === 'import') { + const { Context } = await official(join(modules, '@deepseek-ai/cordis/lib/index.js')) + const { Loader, EntryTree } = await official(join(modules, '@deepseek-ai/cordis-plugin-loader/lib/index.js')) + const ctx = new Context() + try { + ctx.baseUrl = pathToFileURL(state.profileDir + '/').href + await ctx.plugin(Loader) + // --no-addons makes the real release choose its documented JS fallback. + // Do not replace internal loader state, package resolution or exports. + assert.equal(ctx.loader.internal, undefined, 'invoke this probe with --no-addons') + const plugin = await new EntryTree(ctx).import('dsh-mnemon') + assert.equal(plugin.name, 'dsh-mnemon') + assert.equal(plugin.Config({ defaultRecallLimit: 7 }).defaultRecallLimit, 7) + console.log('Official Desktop fallback imported the complete root with PlainConfig') + } finally { await ctx.fiber.dispose() } +} else { + const config = join(state.profileDir, 'probe.cordis.yml') + await writeFile(config, '[]\n') + const patches = loaded.layers.find(layer => layer.packageName === 'dsh-mnemon').patches + const group = patches.find(patch => patch.insert).insert[0] + // These two diagnostic variants only isolate the failing import. A complete + // successful Host is exercised separately with the untouched grouped bundle. + const selected = mode === 'grouped' ? patches : [{ insert: group.config }] + const ctx = await boot('issue274', config, selected) + await ctx.fiber.dispose() +} diff --git a/dsh/docs/pr-assets/issue-274-profile-generation/harness/profile-legacy.mjs b/dsh/docs/pr-assets/issue-274-profile-generation/harness/profile-legacy.mjs new file mode 100644 index 000000000..a9988c3e6 --- /dev/null +++ b/dsh/docs/pr-assets/issue-274-profile-generation/harness/profile-legacy.mjs @@ -0,0 +1,70 @@ +import assert from 'node:assert/strict' +import { spawnSync } from 'node:child_process' +import { readFile, writeFile } from 'node:fs/promises' +import { join, resolve } from 'node:path' +import { fileURLToPath, pathToFileURL } from 'node:url' + +const args = new Map() +for (let i = 2; i < process.argv.length; i += 2) args.set(process.argv[i], process.argv[i + 1]) +for (const key of ['--resources', '--run-root', '--package', '--pnpm-entry', '--native-cli']) assert(args.get(key), `supply ${key}`) +const preparation = spawnSync(process.execPath, [fileURLToPath(new URL('./desktop-generation.mjs', import.meta.url)), ...process.argv.slice(2), '--profile', 'headless', '--serve', 'false'], { encoding: 'utf8', timeout: 120_000 }) +assert.equal(preparation.status, 0, preparation.stderr) +const runRoot = resolve(args.get('--run-root')) +const state = JSON.parse(await readFile(join(runRoot, 'server.json'), 'utf8')) +const modules = join(state.resources, 'app/node_modules') +const profile = state.profileDir +const manifestPath = join(profile, 'package.json') +const manifest = JSON.parse(await readFile(manifestPath, 'utf8')) +manifest.dsh.profile.bundles = ['@deepseek-ai/dsh-base', 'dsh-mnemon'] +await writeFile(manifestPath, JSON.stringify(manifest, null, 2)) +const settingsPath = join(state.dshHome, 'settings.yaml') +await writeFile(settingsPath, await readFile(settingsPath, 'utf8') + '\nmnemon-ui:\n turnBar: false\n') +Object.assign(process.env, { DSH_HOME: state.dshHome, MNEMON_DATA_DIR: state.data, MNEMON_CLI_PATH: state.nativeCli, DSH_TELEMETRY_DISABLED: '1', DSH_TELEMETRY_MODE: 'DISABLED', DEEPSEEK_API_KEY: 'fixture', DEEPSEEK_BASE_URL: 'http://127.0.0.1:9' }) +process.chdir(state.workspace) +const cli = join(modules, '@deepseek-ai/dsh/lib/bin.js') +const official = path => import(pathToFileURL(path).href) +const { registerHostModuleFallback } = await official(join(state.resources, 'host-module-fallback.mjs')) +registerHostModuleFallback(cli) +// Use the unchanged entry exported by the release's own CLI import. The older +// release bundles this public runtime file with a content hash in its name. +const importPath = (await readFile(cli, 'utf8')).match(/import\("(\.\/profile-boot-[^"]+\.js)"\)/)?.[1] +assert(importPath, 'the official CLI must expose its profile runner') +const { runProfile } = await import(new URL(importPath, pathToFileURL(cli)).href) +const { createLaunchEnvironmentSnapshot } = await official(join(modules, '@deepseek-ai/dsh-launch-environment/lib/index.js')) +const options = { profile: 'headless', patchFiles: [], args: [], environment: createLaunchEnvironmentSnapshot([{ source: 'process', values: Object.fromEntries(Object.entries(process.env).filter(([, value]) => typeof value === 'string')) }]) } +const evidence = { framework: JSON.parse(await readFile(join(modules, '@deepseek-ai/dsh/package.json'), 'utf8')).version, node: process.version, generation: state.generation.id, checks: [] } +const booted = await runProfile(options) +try { + const entries = [...booted.ctx.loader.entries()] + const core = entries.find(entry => entry.options.id === 'mnemon') + assert.equal(typeof core.fiber.config.defaultRecallLimit, 'number') + assert.equal(booted.ctx.settings.get('mnemon').defaultRecallLimit, 7) + assert.equal(booted.ctx.settings.get('mnemon-ui').turnBar, false) + const descriptor = booted.ctx.settings.describe().find(item => item.ns === 'mnemon') + assert(!JSON.stringify(descriptor.schema).includes('"volatile":true')) + for (const id of ['scoped', 'light-context', 'auto-capture']) assert.equal(entries.find(entry => entry.options.id === `mnemon-strategy-${id}`).disabled, false) + await booted.ctx.settings.update('mnemon', { defaultRecallLimit: 11 }, descriptor.revision) + assert.equal(booted.ctx.settings.get('mnemon').defaultRecallLimit, 11) + assert.match(await readFile(settingsPath, 'utf8'), /defaultRecallLimit: 11/) + evidence.checks.push('the old host receives PlainConfig and retains the mnemon and mnemon-ui settings namespaces', 'all three Strategy extensions activate', 'legacy Settings.update validates and persists an ordinary value') +} finally { await booted.shutdown.shutdown(0) } +const saved = await readFile(settingsPath, 'utf8') +const restarted = await runProfile(options) +try { + assert.equal(restarted.ctx.settings.get('mnemon').defaultRecallLimit, 11) + assert.equal(restarted.ctx.settings.get('mnemon-ui').turnBar, false) + assert.equal(await readFile(settingsPath, 'utf8'), saved) + evidence.checks.push('restart retains the saved settings and comment bytes') +} finally { await restarted.shutdown.shutdown(0) } +const patchPath = join(profile, 'cordis.patch.yml') +await writeFile(patchPath, await readFile(patchPath, 'utf8') + '\n- id: mnemon\n disabled: true\n') +const disabled = await runProfile(options) +try { + const entries = [...disabled.ctx.loader.entries()].filter(entry => entry.options.id === 'mnemon' || entry.options.id.startsWith('mnemon-source-') || entry.options.id.startsWith('mnemon-strategy-')) + assert.equal(entries.length, 8) + assert(entries.every(entry => entry.disabled && !entry.fiber)) + assert.equal(disabled.ctx.settings.get('mnemon'), undefined) + evidence.checks.push('the original root gate disposes all eight entries and the owned settings namespace') +} finally { await disabled.shutdown.shutdown(0) } +await writeFile(join(runRoot, 'verification.json'), JSON.stringify(evidence, null, 2)) +console.log(JSON.stringify(evidence, null, 2)) diff --git a/dsh/docs/pr-assets/issue-274-profile-generation/harness/profile-live.mjs b/dsh/docs/pr-assets/issue-274-profile-generation/harness/profile-live.mjs new file mode 100644 index 000000000..6148fe90a --- /dev/null +++ b/dsh/docs/pr-assets/issue-274-profile-generation/harness/profile-live.mjs @@ -0,0 +1,87 @@ +import assert from 'node:assert/strict' +import { createHash } from 'node:crypto' +import { mkdir, readFile, writeFile, appendFile } from 'node:fs/promises' +import { join, resolve } from 'node:path' +import { pathToFileURL } from 'node:url' + +const args = new Map() +for (let i = 2; i < process.argv.length; i += 2) args.set(process.argv[i], process.argv[i + 1]) +for (const key of ['--framework-modules', '--desktop-resources', '--package', '--pnpm-entry', '--run-root', '--native-cli']) assert(args.get(key), `supply ${key}`) +const modules = resolve(args.get('--framework-modules')) +const desktopModules = join(resolve(args.get('--desktop-resources')), 'app/node_modules') +const runRoot = resolve(args.get('--run-root')) +const home = join(runRoot, 'dsh-home') +const profile = join(home, 'profiles/probe') +const workspace = join(runRoot, 'workspace') +const data = join(runRoot, 'data') +await Promise.all([profile, workspace, data].map(path => mkdir(path, { recursive: true }))) +Object.assign(process.env, { DSH_HOME: home, MNEMON_DATA_DIR: data, MNEMON_CLI_PATH: resolve(args.get('--native-cli')), DSH_TELEMETRY_DISABLED: '1', DEEPSEEK_API_KEY: 'fixture', DEEPSEEK_BASE_URL: 'http://127.0.0.1:9' }) +process.chdir(workspace) +const official = (base, file) => import(pathToFileURL(join(base, file)).href) +const { installGeneration } = await official(desktopModules, 'dsh-desktop-market-installer/generations/installer.mjs') +const { writeDesired } = await official(desktopModules, 'dsh-desktop-market-installer/generations/registry.mjs') +const { projectGenerations } = await official(desktopModules, 'dsh-desktop-market-installer/generations/projection.mjs') +const result = await installGeneration({ dshHome: home, profile: 'probe', pluginSpec: args.get('--package'), expectedPluginName: 'dsh-mnemon', registry: 'https://registry.npmjs.org', autoInstallPeers: false, nodeExecutablePath: process.execPath, pnpmEntryPath: resolve(args.get('--pnpm-entry')), onOutput: chunk => { void appendFile(join(runRoot, 'install.log'), chunk) } }) +assert.equal(result.ok, true, result.detail) +await writeFile(join(profile, 'package.json'), JSON.stringify({ name: 'issue274-live-profile', private: true, dependencies: {}, dsh: { profile: { bundles: ['@deepseek-ai/dsh-base'], patchReload: 'startup' } } }, null, 2)) +await writeDesired(home, [result.generation.id]) +await projectGenerations(home, 'probe') +// This settings probe does not execute shell/PTC workflows. Disable their full +// dependency chain rather than leaving unrelated consumers waiting for a PTY. +const disabled = ['subprocess', 'ptc-runtime', 'workflow-ptc', 'tool-workflow', 'bash-sandbox', 'pwsh-sandbox', 'tool-bash', 'tool-pwsh', 'permission', 'tool-fs-search'] +const extensions = ['scoped', 'light-context', 'auto-capture'].map(name => `mnemon-strategy-${name}`) +const patchPath = join(profile, 'cordis.patch.yml') +await writeFile(patchPath, disabled.map(id => `- id: ${id}\n disabled: true\n`).join('') + extensions.map(id => `- id: ${id}\n disabled: false\n`).join('') + `- id: mnemon\n config:\n cliPath: ${JSON.stringify(resolve(args.get('--native-cli')))}\n idleReviewMs: !!js 15 * 1000\n idleReview:\n enabled: false\n`) +const suffix = createHash('sha256').update(JSON.stringify(pathToFileURL(profile + '/').href)).digest('hex').slice(0, 16) +const backup = `# Issue 274 retained settings\nmnemon:\n defaultRecallLimit: 7\nmnemon-ui:\n turnBar: false\nmnemon-view-${suffix}:\n entries:\n include:mnemon-strategy-light-context:\n enabled: true\n config:\n maxProjectionCharacters: 700\nmnemon-plugins-${suffix}:\n sources:\n include:mnemon-source-documents:\n enabled: false\n` +await writeFile(join(home, 'settings.yaml.imported'), backup) +const { runProfile } = await official(modules, '@deepseek-ai/dsh/lib/profile-boot.js') +const { createLaunchEnvironmentSnapshot } = await official(modules, '@deepseek-ai/dsh-launch-environment/lib/index.js') +const options = { profile: 'probe', patchFiles: [], args: [], environment: createLaunchEnvironmentSnapshot([{ source: 'process', values: Object.fromEntries(Object.entries(process.env).filter(([, value]) => typeof value === 'string')) }]) } +const waitFor = async predicate => { for (let i = 0; i < 100; i++) { if (predicate()) return; await new Promise(done => setTimeout(done, 20)) } throw new Error('Profile state did not settle') } +const { ctx, shutdown } = await runProfile(options) +const evidence = { framework: JSON.parse(await readFile(join(modules, '@deepseek-ai/dsh/package.json'))).version, node: process.version, generation: result.generation.id, checks: [] } +try { + const entries = () => [...ctx.loader.entries()] + const core = () => entries().find(entry => entry.options.id === 'mnemon') + const descriptor = () => ctx.settings.describe().find(value => value.ns === 'mnemon') + await waitFor(() => core()?.fiber?.config.defaultRecallLimit.get() === 7) + assert.equal(core().fiber.config.idleReviewMs.get(), 15000) + assert.equal(core().fiber.config.conversationInteraction.get().turnBar, false) + assert.equal(core().fiber.config.memoryView.get().entries['include:mnemon-strategy-light-context'].config.maxProjectionCharacters, 700) + await waitFor(() => entries().find(entry => entry.options.id === 'mnemon-source-documents')?.disabled === true) + for (const id of extensions) assert.equal(entries().find(entry => entry.options.id === id)?.disabled, false) + assert.equal(await readFile(join(home, 'settings.yaml.imported'), 'utf8'), backup) + evidence.checks.push('retained root/UI/View/Source settings recover under the same profile namespaces', 'all three Strategy extensions remain active', 'backup bytes and the dynamic expression are preserved') + const previous = descriptor() + const fiber = core().fiber + await ctx.settings.update('mnemon', { defaultRecallLimit: 11 }, previous.revision) + assert.equal(core().fiber, fiber) + assert.equal(core().fiber.config.defaultRecallLimit.get(), 11) + await assert.rejects(ctx.settings.update('mnemon', { defaultRecallLimit: 12 }, previous.revision), error => error.code === 'SETTINGS_CONFLICT') + assert.equal(core().fiber.config.defaultRecallLimit.get(), 11) + const current = descriptor() + await ctx.settings.update('mnemon', { memoryView: { ...current.value.memoryView, entries: { ...current.value.memoryView.entries, 'include:mnemon-source-documents': { enabled: true, config: {} } } } }, current.revision) + await waitFor(() => entries().find(entry => entry.options.id === 'mnemon-source-documents')?.disabled === false) + assert.match(await readFile(patchPath, 'utf8'), /idleReviewMs: !!js 15 \* 1000/) + evidence.checks.push('live update preserves the owning fiber and rejects stale revisions', 'independent Source re-enable survives profile reconciliation') +} finally { await shutdown.shutdown(0) } +const savedPatch = await readFile(patchPath, 'utf8') +const restarted = await runProfile(options) +try { + const core = [...restarted.ctx.loader.entries()].find(entry => entry.options.id === 'mnemon') + assert.equal(core.fiber.config.defaultRecallLimit.get(), 11) + assert.equal(core.fiber.config.idleReviewMs.get(), 15000) + assert.equal(await readFile(patchPath, 'utf8'), savedPatch) + evidence.checks.push('full host restart retains values, expressions and profile bytes') +} finally { await restarted.shutdown.shutdown(0) } +await writeFile(patchPath, savedPatch + '\n- id: mnemon\n disabled: true\n') +const stopped = await runProfile(options) +try { + const entries = [...stopped.ctx.loader.entries()].filter(entry => entry.options.id === 'mnemon' || entry.options.id.startsWith('mnemon-source-') || entry.options.id.startsWith('mnemon-strategy-')) + assert.equal(entries.length, 8) + assert(entries.every(entry => entry.disabled && !entry.fiber)) + evidence.checks.push('the original root gate disables all eight entries without pending services') +} finally { await stopped.shutdown.shutdown(0) } +await writeFile(join(runRoot, 'verification.json'), JSON.stringify(evidence, null, 2)) +console.log(JSON.stringify(evidence, null, 2)) diff --git a/dsh/docs/pr-assets/issue-274-profile-generation/logs/after-checks.json b/dsh/docs/pr-assets/issue-274-profile-generation/logs/after-checks.json new file mode 100644 index 000000000..20ab16461 --- /dev/null +++ b/dsh/docs/pr-assets/issue-274-profile-generation/logs/after-checks.json @@ -0,0 +1,20 @@ +{ + "fixed-desktop-headless-final": { + "exitCode": 0, + "verified": true, + "framework": "0.1.5-rc.2", + "nativeAddons": true + }, + "fixed-desktop-headless-disabled": { + "exitCode": 0, + "verified": true, + "framework": "0.1.5-rc.2", + "nativeAddons": true + }, + "guiColdRestart": { + "settingsSha256": "52520c59a903d233f06372aa84c662851b168d20c9e3dd58b15b3296691903bc", + "runtimeSha256": "b884da44b90bac3347cd1b9f104a4d97a50ee385eed829101419564d620b20a1", + "runtimeBytes": 314, + "savedMinIntervalMs": 310000 + } +} diff --git a/dsh/docs/pr-assets/issue-274-profile-generation/logs/after-fallback.log b/dsh/docs/pr-assets/issue-274-profile-generation/logs/after-fallback.log new file mode 100644 index 000000000..1e5c22a45 --- /dev/null +++ b/dsh/docs/pr-assets/issue-274-profile-generation/logs/after-fallback.log @@ -0,0 +1 @@ +Official Desktop fallback imported the complete root with PlainConfig diff --git a/dsh/docs/pr-assets/issue-274-profile-generation/logs/before-direct.log b/dsh/docs/pr-assets/issue-274-profile-generation/logs/before-direct.log new file mode 100644 index 000000000..c9f609c03 --- /dev/null +++ b/dsh/docs/pr-assets/issue-274-profile-generation/logs/before-direct.log @@ -0,0 +1,10 @@ +file:///baseline-desktop-web-native/dsh-home/profiles/.generations/live/dsh-mnemon+0.5.13+5b2e12f917e4/node_modules/schemastery-live/lib/index.mjs:1 +import { Binary, clone, createVolatile, deepEqual, filterKeys, isNullable, isPlainObject, isVolatile, pick, valueMap } from "@deepseek-ai/cosmokit"; + ^^^^^^^^^^^^^^ +SyntaxError: The requested module '@deepseek-ai/cosmokit' does not provide an export named 'createVolatile' + at #asyncInstantiate (node:internal/modules/esm/module_job:455:21) + at async ModuleJob.run (node:internal/modules/esm/module_job:553:5) + at async node:internal/modules/esm/loader:647:26 + at async file:///docs/pr-assets/issue-274-profile-generation/harness/desktop-import.mjs:28:18 + +Node.js v24.20.0 diff --git a/dsh/docs/pr-assets/issue-274-profile-generation/logs/before-fallback.log b/dsh/docs/pr-assets/issue-274-profile-generation/logs/before-fallback.log new file mode 100644 index 000000000..7eebd3f1f --- /dev/null +++ b/dsh/docs/pr-assets/issue-274-profile-generation/logs/before-fallback.log @@ -0,0 +1,19 @@ +node:internal/modules/run_main:107 + triggerUncaughtException( + ^ + +Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'dsh-mnemon' imported from /desktop/extracted/DSH Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js + at Object.getPackageJSONURL (node:internal/modules/package_json_reader:301:9) + at packageResolve (node:internal/modules/esm/resolve:784:25) + at moduleResolve (node:internal/modules/esm/resolve:873:18) + at defaultResolve (node:internal/modules/esm/resolve:1006:11) + at #cachedDefaultResolve (node:internal/modules/esm/loader:705:20) + at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:725:38) + at nextStep (node:internal/modules/customization_hooks:189:26) + at resolve (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/host-module-fallback.mjs:32:16) + at nextStep (node:internal/modules/customization_hooks:189:26) + at resolveWithHooks (node:internal/modules/customization_hooks:417:10) { + code: 'ERR_MODULE_NOT_FOUND' +} + +Node.js v24.20.0 diff --git a/dsh/docs/pr-assets/issue-274-profile-generation/logs/before-flat.log b/dsh/docs/pr-assets/issue-274-profile-generation/logs/before-flat.log new file mode 100644 index 000000000..592267c25 --- /dev/null +++ b/dsh/docs/pr-assets/issue-274-profile-generation/logs/before-flat.log @@ -0,0 +1,92 @@ +file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/dsh-app-boot/lib/index.js:1603 + throw new Error(`${binName}: ${stage}: ${detail}${stack}`, { cause }); + ^ + +Error: issue274: plugin tree failed to load: failed to apply loader entry include (cordis:include): failed to import loader entry mnemon (dsh-mnemon): Cannot find package 'dsh-mnemon' imported from /desktop/extracted/DSH Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js +Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'dsh-mnemon' imported from /desktop/extracted/DSH Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js + at Object.getPackageJSONURL (node:internal/modules/package_json_reader:301:9) + at packageResolve (node:internal/modules/esm/resolve:784:25) + at moduleResolve (node:internal/modules/esm/resolve:873:18) + at defaultResolve (node:internal/modules/esm/resolve:1006:11) + at #cachedDefaultResolve (node:internal/modules/esm/loader:705:20) + at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:725:38) + at nextStep (node:internal/modules/customization_hooks:189:26) + at resolve (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/host-module-fallback.mjs:32:16) + at nextStep (node:internal/modules/customization_hooks:189:26) + at resolveWithHooks (node:internal/modules/customization_hooks:417:10) + at boot (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/dsh-app-boot/lib/index.js:1603:9) + at async file:///docs/pr-assets/issue-274-profile-generation/harness/desktop-import.mjs:54:15 { + [cause]: Error: failed to apply loader entry include (cordis:include): failed to import loader entry mnemon (dsh-mnemon): Cannot find package 'dsh-mnemon' imported from /desktop/extracted/DSH Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js + at updateError (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:406:16) + at Entry._init (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:640:10) + at async Entry.init (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:613:4) + at async Entry.update (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:534:37) + at async EntryGroup.create (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:65:4) + at async Proxy.create (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:309:14) + at async mountRootInclude (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/dsh-app-boot/lib/index.js:1394:20) + at async boot (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/dsh-app-boot/lib/index.js:1588:3) + at async file:///docs/pr-assets/issue-274-profile-generation/harness/desktop-import.mjs:54:15 { + dshPluginFailure: { + stage: 'apply', + entryId: 'include', + packageName: 'cordis:include', + owner: undefined, + chain: [ + { + entryId: 'include', + packageName: 'cordis:include', + baseUrl: 'file:///baseline-desktop-web-native/dsh-home/profiles/web/' + } + ], + message: "failed to import loader entry mnemon (dsh-mnemon): Cannot find package 'dsh-mnemon' imported from /desktop/extracted/DSH Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js" + }, + [cause]: Error: failed to import loader entry mnemon (dsh-mnemon): Cannot find package 'dsh-mnemon' imported from /desktop/extracted/DSH Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js + at updateError (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:406:16) + at Entry._init (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:635:10) + at async Entry.init (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:613:4) + at async Entry.update (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:534:37) + at async EntryGroup.create (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:65:4) + at async Promise.allSettled (index 0) + at async EntryGroup.update (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:97:21) + at async Include._apply (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/dsh-app-boot/lib/index.js:240:3) { + dshPluginFailure: { + stage: 'import', + entryId: 'mnemon', + packageName: 'dsh-mnemon', + owner: { + packageName: 'dsh-mnemon', + packageDir: '/baseline-desktop-web-native/dsh-home/profiles/web/node_modules/dsh-mnemon', + version: '0.5.13' + }, + chain: [ + { + entryId: 'mnemon', + packageName: 'dsh-mnemon', + baseUrl: 'file:///baseline-desktop-web-native/dsh-home/profiles/web/' + }, + { + entryId: 'include', + packageName: 'cordis:include', + baseUrl: 'file:///baseline-desktop-web-native/dsh-home/profiles/web/' + } + ], + message: "Cannot find package 'dsh-mnemon' imported from /desktop/extracted/DSH Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js" + }, + [cause]: Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'dsh-mnemon' imported from /desktop/extracted/DSH Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js + at Object.getPackageJSONURL (node:internal/modules/package_json_reader:301:9) + at packageResolve (node:internal/modules/esm/resolve:784:25) + at moduleResolve (node:internal/modules/esm/resolve:873:18) + at defaultResolve (node:internal/modules/esm/resolve:1006:11) + at #cachedDefaultResolve (node:internal/modules/esm/loader:705:20) + at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:725:38) + at nextStep (node:internal/modules/customization_hooks:189:26) + at resolve (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/host-module-fallback.mjs:32:16) + at nextStep (node:internal/modules/customization_hooks:189:26) + at resolveWithHooks (node:internal/modules/customization_hooks:417:10) { + code: 'ERR_MODULE_NOT_FOUND' + } + } + } +} + +Node.js v24.20.0 diff --git a/dsh/docs/pr-assets/issue-274-profile-generation/logs/before-grouped.log b/dsh/docs/pr-assets/issue-274-profile-generation/logs/before-grouped.log new file mode 100644 index 000000000..770e655fb --- /dev/null +++ b/dsh/docs/pr-assets/issue-274-profile-generation/logs/before-grouped.log @@ -0,0 +1,114 @@ +file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/dsh-app-boot/lib/index.js:1603 + throw new Error(`${binName}: ${stage}: ${detail}${stack}`, { cause }); + ^ + +Error: issue274: plugin tree failed to load: failed to apply loader entry include (cordis:include): failed to apply loader entry mnemon-bundle (cordis:group): failed to import loader entry mnemon (dsh-mnemon): Cannot find package 'dsh-mnemon' imported from /desktop/extracted/DSH Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js +Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'dsh-mnemon' imported from /desktop/extracted/DSH Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js + at Object.getPackageJSONURL (node:internal/modules/package_json_reader:301:9) + at packageResolve (node:internal/modules/esm/resolve:784:25) + at moduleResolve (node:internal/modules/esm/resolve:873:18) + at defaultResolve (node:internal/modules/esm/resolve:1006:11) + at #cachedDefaultResolve (node:internal/modules/esm/loader:705:20) + at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:725:38) + at nextStep (node:internal/modules/customization_hooks:189:26) + at resolve (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/host-module-fallback.mjs:32:16) + at nextStep (node:internal/modules/customization_hooks:189:26) + at resolveWithHooks (node:internal/modules/customization_hooks:417:10) + at boot (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/dsh-app-boot/lib/index.js:1603:9) + at async file:///docs/pr-assets/issue-274-profile-generation/harness/desktop-import.mjs:54:15 { + [cause]: Error: failed to apply loader entry include (cordis:include): failed to apply loader entry mnemon-bundle (cordis:group): failed to import loader entry mnemon (dsh-mnemon): Cannot find package 'dsh-mnemon' imported from /desktop/extracted/DSH Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js + at updateError (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:406:16) + ... 3 lines matching cause stack trace ... + at async EntryGroup.create (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:65:4) + at async Proxy.create (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:309:14) + at async mountRootInclude (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/dsh-app-boot/lib/index.js:1394:20) + at async boot (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/dsh-app-boot/lib/index.js:1588:3) + at async file:///docs/pr-assets/issue-274-profile-generation/harness/desktop-import.mjs:54:15 { + dshPluginFailure: { + stage: 'apply', + entryId: 'include', + packageName: 'cordis:include', + owner: undefined, + chain: [ + { + entryId: 'include', + packageName: 'cordis:include', + baseUrl: 'file:///baseline-desktop-web-native/dsh-home/profiles/web/' + } + ], + message: "failed to apply loader entry mnemon-bundle (cordis:group): failed to import loader entry mnemon (dsh-mnemon): Cannot find package 'dsh-mnemon' imported from /desktop/extracted/DSH Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js" + }, + [cause]: Error: failed to apply loader entry mnemon-bundle (cordis:group): failed to import loader entry mnemon (dsh-mnemon): Cannot find package 'dsh-mnemon' imported from /desktop/extracted/DSH Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js + at updateError (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:406:16) + at Entry._init (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:640:10) + at async Entry.init (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:613:4) + at async Entry.update (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:534:37) + at async EntryGroup.create (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:65:4) + at async Promise.allSettled (index 0) + at async EntryGroup.update (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:97:21) + at async Include._apply (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/dsh-app-boot/lib/index.js:240:3) { + dshPluginFailure: { + stage: 'apply', + entryId: 'mnemon-bundle', + packageName: 'cordis:group', + owner: { + packageName: 'dsh-mnemon', + packageDir: '/baseline-desktop-web-native/dsh-home/profiles/web/node_modules/dsh-mnemon', + version: '0.5.13' + }, + chain: [ + { + entryId: 'mnemon-bundle', + packageName: 'cordis:group', + baseUrl: 'file:///baseline-desktop-web-native/dsh-home/profiles/web/' + }, + { + entryId: 'include', + packageName: 'cordis:include', + baseUrl: 'file:///baseline-desktop-web-native/dsh-home/profiles/web/' + } + ], + message: "failed to import loader entry mnemon (dsh-mnemon): Cannot find package 'dsh-mnemon' imported from /desktop/extracted/DSH Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js" + }, + [cause]: Error: failed to import loader entry mnemon (dsh-mnemon): Cannot find package 'dsh-mnemon' imported from /desktop/extracted/DSH Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js + at updateError (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:406:16) + at Entry._init (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:635:10) + at async Entry.init (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:613:4) + at async Entry.update (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:534:37) + at async Group.create (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:65:4) + at async Promise.allSettled (index 0) + at async Group.update (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:97:21) + at async [cordis.init] (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:142:3) + at file:///baseline-desktop-web-native/dsh-home/profiles/web/#mnemon-bundle + at file:///baseline-desktop-web-native/dsh-home/profiles/web/#include { + dshPluginFailure: { + stage: 'import', + entryId: 'mnemon', + packageName: 'dsh-mnemon', + owner: { + packageName: 'dsh-mnemon', + packageDir: '/baseline-desktop-web-native/dsh-home/profiles/web/node_modules/dsh-mnemon', + version: '0.5.13' + }, + chain: [ [Object], [Object], [Object] ], + message: "Cannot find package 'dsh-mnemon' imported from /desktop/extracted/DSH Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js" + }, + [cause]: Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'dsh-mnemon' imported from /desktop/extracted/DSH Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js + at Object.getPackageJSONURL (node:internal/modules/package_json_reader:301:9) + at packageResolve (node:internal/modules/esm/resolve:784:25) + at moduleResolve (node:internal/modules/esm/resolve:873:18) + at defaultResolve (node:internal/modules/esm/resolve:1006:11) + at #cachedDefaultResolve (node:internal/modules/esm/loader:705:20) + at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:725:38) + at nextStep (node:internal/modules/customization_hooks:189:26) + at resolve (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/host-module-fallback.mjs:32:16) + at nextStep (node:internal/modules/customization_hooks:189:26) + at resolveWithHooks (node:internal/modules/customization_hooks:417:10) { + code: 'ERR_MODULE_NOT_FOUND' + } + } + } + } +} + +Node.js v24.20.0 diff --git a/dsh/docs/pr-assets/issue-274-profile-generation/logs/before-web.log b/dsh/docs/pr-assets/issue-274-profile-generation/logs/before-web.log new file mode 100644 index 000000000..36bc82083 --- /dev/null +++ b/dsh/docs/pr-assets/issue-274-profile-generation/logs/before-web.log @@ -0,0 +1,27 @@ +[harness-node] runtime node=v24.20.0 platform=darwin arch=arm64 +[harness-node] execPath=/node_modules/node/bin/node +[harness-node] cwd=/baseline-desktop-web-native/workspace +[harness-node] DSH_HOME=/baseline-desktop-web-native/dsh-home +[harness-node] loading=/desktop/extracted/DSH Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/dsh/lib/bin.js +[harness-node] DSH entry loaded +[harness-node] invoking DSH runCli() +[harness-node] plugin failures: {"version":1,"failures":[{"stage":"import","entryId":"mnemon","packageName":"dsh-mnemon","owner":{"packageName":"dsh-mnemon","packageDir":"/baseline-desktop-web-native/dsh-home/profiles/web/node_modules/dsh-mnemon","version":"0.5.13"},"chain":[{"entryId":"mnemon","packageName":"dsh-mnemon","baseUrl":"file:///baseline-desktop-web-native/dsh-home/profiles/web/"},{"entryId":"mnemon-bundle","packageName":"cordis:group","baseUrl":"file:///baseline-desktop-web-native/dsh-home/profiles/web/"},{"entryId":"include","packageName":"cordis:include","baseUrl":"file:///baseline-desktop-web-native/dsh-home/profiles/web/"}],"message":"The requested module '@deepseek-ai/cosmokit' does not provide an export named 'createVolatile'"}]} +[harness-node] DSH entry failed: Error: dsh: plugin tree failed to load: failed to apply loader entry include (cordis:include): failed to apply loader entry mnemon-bundle (cordis:group): failed to import loader entry mnemon (dsh-mnemon): The requested module '@deepseek-ai/cosmokit' does not provide an export named 'createVolatile' +file:///baseline-desktop-web-native/dsh-home/profiles/.generations/live/dsh-mnemon+0.5.13+5b2e12f917e4/node_modules/schemastery-live/lib/index.mjs:1 +import { Binary, clone, createVolatile, deepEqual, filterKeys, isNullable, isPlainObject, isVolatile, pick, valueMap } from "@deepseek-ai/cosmokit"; + ^^^^^^^^^^^^^^ +SyntaxError: The requested module '@deepseek-ai/cosmokit' does not provide an export named 'createVolatile' + at #asyncInstantiate (node:internal/modules/esm/module_job:455:21) + at async ModuleJob.run (node:internal/modules/esm/module_job:553:5) + at async node:internal/modules/esm/loader:647:26 + at async file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:356:41 + at async Entry._init (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:633:39) + at async Entry.init (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:613:4) + at async Entry.update (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:534:37) + at async Group.create (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:65:4) + at async Promise.allSettled (index 0) + at async Group.update (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js:97:21) + at boot (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/dsh-app-boot/lib/index.js:1603:9) + at async runProfile (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/dsh/lib/profile-boot-Dk-7KqJc.js:320:14) + at async Module.runCli (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/dsh/lib/bin.js:146:4) + at async file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/harness-node-entry.mjs:97:7 diff --git a/dsh/docs/pr-assets/issue-274-profile-generation/logs/desktop-file-hashes.json b/dsh/docs/pr-assets/issue-274-profile-generation/logs/desktop-file-hashes.json new file mode 100644 index 000000000..4ffe0af52 --- /dev/null +++ b/dsh/docs/pr-assets/issue-274-profile-generation/logs/desktop-file-hashes.json @@ -0,0 +1,33 @@ +{ + "runtime": "v24.20.0", + "files": [ + { + "path": "host-module-fallback.mjs", + "sha256": "bf3ef86bbaa49560a9a1bb039ec26b6d0dc186f84e372282acc5a21af55f0bf1" + }, + { + "path": "harness-node-entry.mjs", + "sha256": "615167048ce49b545f54942cdd50ffb14eddd504a0128fd44609157212e72fc3" + }, + { + "path": "app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js", + "sha256": "4d9732122405bcbe0760847fbbc7eeda2f5c402e5d0bdb624be6a621bcc1226f" + }, + { + "path": "app/node_modules/@deepseek-ai/dsh-app-boot/lib/index.js", + "sha256": "c74879df2c5a79d593209d5d8c16d2025184b7148c42469d7844aa4224e657a6" + }, + { + "path": "app/node_modules/@deepseek-ai/cosmokit/lib/index.js", + "sha256": "a024ad32ead85dd6708cedd5b56acacb578e048e8f71e78b20ed56053ac7beb4" + }, + { + "path": "app/node_modules/dsh-desktop-market-installer/generations/installer.mjs", + "sha256": "272cc78ae041e70fa3cd8d2fb8e03e5a6c7ceaf7fe4eab449eeaa4e68bb11f22" + }, + { + "path": "app/node_modules/dsh-desktop-market-installer/generations/projection.mjs", + "sha256": "32b818476f1e3ed834ae3846685947c322e3ab63f992d6f363f3dbacd3429eeb" + } + ] +} diff --git a/dsh/docs/pr-assets/issue-274-profile-generation/logs/electron-after-direct.log b/dsh/docs/pr-assets/issue-274-profile-generation/logs/electron-after-direct.log new file mode 100644 index 000000000..b8d53e125 --- /dev/null +++ b/dsh/docs/pr-assets/issue-274-profile-generation/logs/electron-after-direct.log @@ -0,0 +1 @@ +Direct generation import succeeded diff --git a/dsh/docs/pr-assets/issue-274-profile-generation/logs/electron-after-import.log b/dsh/docs/pr-assets/issue-274-profile-generation/logs/electron-after-import.log new file mode 100644 index 000000000..1e5c22a45 --- /dev/null +++ b/dsh/docs/pr-assets/issue-274-profile-generation/logs/electron-after-import.log @@ -0,0 +1 @@ +Official Desktop fallback imported the complete root with PlainConfig diff --git a/dsh/docs/pr-assets/issue-274-profile-generation/logs/electron-before-direct.log b/dsh/docs/pr-assets/issue-274-profile-generation/logs/electron-before-direct.log new file mode 100644 index 000000000..72f4b46ae --- /dev/null +++ b/dsh/docs/pr-assets/issue-274-profile-generation/logs/electron-before-direct.log @@ -0,0 +1,10 @@ +file:///baseline-desktop-web-native/dsh-home/profiles/.generations/live/dsh-mnemon+0.5.13+5b2e12f917e4/node_modules/schemastery-live/lib/index.mjs:1 +import { Binary, clone, createVolatile, deepEqual, filterKeys, isNullable, isPlainObject, isVolatile, pick, valueMap } from "@deepseek-ai/cosmokit"; + ^^^^^^^^^^^^^^ +SyntaxError: The requested module '@deepseek-ai/cosmokit' does not provide an export named 'createVolatile' + at #asyncInstantiate (node:internal/modules/esm/module_job:327:21) + at async ModuleJob.run (node:internal/modules/esm/module_job:431:5) + at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:643:26) + at async file:///docs/pr-assets/issue-274-profile-generation/harness/desktop-import.mjs:28:18 + +Node.js v24.18.1 diff --git a/dsh/docs/pr-assets/issue-274-profile-generation/logs/electron-before-import.log b/dsh/docs/pr-assets/issue-274-profile-generation/logs/electron-before-import.log new file mode 100644 index 000000000..848d733ed --- /dev/null +++ b/dsh/docs/pr-assets/issue-274-profile-generation/logs/electron-before-import.log @@ -0,0 +1,19 @@ +node:internal/modules/run_main:122 + triggerUncaughtException( + ^ + +Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'dsh-mnemon' imported from /desktop/extracted/DSH Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js + at Object.getPackageJSONURL (node:internal/modules/package_json_reader:301:9) + at packageResolve (node:internal/modules/esm/resolve:780:81) + at moduleResolve (node:internal/modules/esm/resolve:871:18) + at defaultResolve (node:internal/modules/esm/resolve:1004:11) + at #cachedDefaultResolve (node:internal/modules/esm/loader:701:20) + at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:721:38) + at nextStep (node:internal/modules/customization_hooks:189:26) + at resolve (file:///desktop/extracted/DSH%20Desktop.app/Contents/Resources/host-module-fallback.mjs:32:16) + at nextStep (node:internal/modules/customization_hooks:189:26) + at resolveWithHooks (node:internal/modules/customization_hooks:418:10) { + code: 'ERR_MODULE_NOT_FOUND' +} + +Node.js v24.18.1 diff --git a/dsh/docs/pr-assets/issue-274-profile-generation/logs/legacy-settings.json b/dsh/docs/pr-assets/issue-274-profile-generation/logs/legacy-settings.json new file mode 100644 index 000000000..deb7b3f3a --- /dev/null +++ b/dsh/docs/pr-assets/issue-274-profile-generation/logs/legacy-settings.json @@ -0,0 +1,12 @@ +{ + "framework": "0.1.5-rc.2", + "node": "v24.20.0", + "generation": "dsh-mnemon+0.5.13+2775bb3fa512", + "checks": [ + "the old host receives PlainConfig and retains the mnemon and mnemon-ui settings namespaces", + "all three Strategy extensions activate", + "legacy Settings.update validates and persists an ordinary value", + "restart retains the saved settings and comment bytes", + "the original root gate disposes all eight entries and the owned settings namespace" + ] +} \ No newline at end of file diff --git a/dsh/docs/pr-assets/issue-274-profile-generation/logs/live-rc1.json b/dsh/docs/pr-assets/issue-274-profile-generation/logs/live-rc1.json new file mode 100644 index 000000000..58be6ceb0 --- /dev/null +++ b/dsh/docs/pr-assets/issue-274-profile-generation/logs/live-rc1.json @@ -0,0 +1,14 @@ +{ + "framework": "0.1.7-rc.1", + "node": "v24.20.0", + "generation": "dsh-mnemon+0.5.13+2775bb3fa512", + "checks": [ + "retained root/UI/View/Source settings recover under the same profile namespaces", + "all three Strategy extensions remain active", + "backup bytes and the dynamic expression are preserved", + "live update preserves the owning fiber and rejects stale revisions", + "independent Source re-enable survives profile reconciliation", + "full host restart retains values, expressions and profile bytes", + "the original root gate disables all eight entries without pending services" + ] +} \ No newline at end of file diff --git a/dsh/docs/pr-assets/issue-274-profile-generation/logs/regression-after.log b/dsh/docs/pr-assets/issue-274-profile-generation/logs/regression-after.log new file mode 100644 index 000000000..4bf6c7902 --- /dev/null +++ b/dsh/docs/pr-assets/issue-274-profile-generation/logs/regression-after.log @@ -0,0 +1,10 @@ + + RUN v3.2.7 + + ✓ tests/live-config.spec.ts (7 tests) 25ms + ✓ tests/profile-generation-loading.spec.ts (2 tests) 424ms + + Test Files 2 passed (2) + Tests 9 passed (9) + Start at 00:43:22 + Duration 905ms (transform 321ms, setup 0ms, collect 536ms, tests 449ms, environment 0ms, prepare 167ms) diff --git a/dsh/docs/pr-assets/issue-274-profile-generation/logs/regression-before.log b/dsh/docs/pr-assets/issue-274-profile-generation/logs/regression-before.log new file mode 100644 index 000000000..d89b34773 --- /dev/null +++ b/dsh/docs/pr-assets/issue-274-profile-generation/logs/regression-before.log @@ -0,0 +1,54 @@ + + RUN v3.2.7 + + ❯ tests/profile-generation-loading.spec.ts (2 tests | 1 failed) 439ms + × profile generation imports against host-owned framework packages > loads the complete built Starter with public Cosmokit cosmokit-legacy 228ms + → file:///mnemon-generation-import-v6usRT/profiles/.generations/live/mnemon/node_modules/schemastery-live/lib/index.mjs:1 +import { Binary, clone, createVolatile, deepEqual, filterKeys, isNullable, isPlainObject, isVolatile, pick, valueMap } from "@deepseek-ai/cosmokit"; + ^^^^^^^^^^^^^^ +SyntaxError: The requested module '@deepseek-ai/cosmokit' does not provide an export named 'createVolatile' + at #asyncInstantiate (node:internal/modules/esm/module_job:455:21) + at async ModuleJob.run (node:internal/modules/esm/module_job:553:5) + at async node:internal/modules/esm/loader:647:26 + at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:101:5) + +Node.js v24.20.0 +: expected 1 to be +0 // Object.is equality + ✓ profile generation imports against host-owned framework packages > loads the complete built Starter with public Cosmokit @deepseek-ai/cosmokit 210ms + +⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯ + + FAIL tests/profile-generation-loading.spec.ts > profile generation imports against host-owned framework packages > loads the complete built Starter with public Cosmokit cosmokit-legacy +AssertionError: file:///mnemon-generation-import-v6usRT/profiles/.generations/live/mnemon/node_modules/schemastery-live/lib/index.mjs:1 +import { Binary, clone, createVolatile, deepEqual, filterKeys, isNullable, isPlainObject, isVolatile, pick, valueMap } from "@deepseek-ai/cosmokit"; + ^^^^^^^^^^^^^^ +SyntaxError: The requested module '@deepseek-ai/cosmokit' does not provide an export named 'createVolatile' + at #asyncInstantiate (node:internal/modules/esm/module_job:455:21) + at async ModuleJob.run (node:internal/modules/esm/module_job:553:5) + at async node:internal/modules/esm/loader:647:26 + at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:101:5) + +Node.js v24.20.0 +: expected 1 to be +0 // Object.is equality + +- Expected ++ Received + +- 0 ++ 1 + + ❯ tests/profile-generation-loading.spec.ts:71:44 + 69| const result = spawnSync(process.execPath, [entry], { encoding: … + 70| expect(result.error).toBeUndefined() + 71| expect(result.status, result.stderr).toBe(0) + | ^ + 72| expect(JSON.parse(result.stdout)).toEqual({ loaded: true, live }) + 73| } finally { rmSync(fixture.directory, { recursive: true, force: tr… + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯ + + + Test Files 1 failed (1) + Tests 1 failed | 1 passed (2) + Start at 00:42:40 + Duration 1.05s (transform 53ms, setup 0ms, collect 52ms, tests 439ms, environment 0ms, prepare 85ms) diff --git a/dsh/docs/pr-assets/issue-274-profile-generation/logs/verification-summary.json b/dsh/docs/pr-assets/issue-274-profile-generation/logs/verification-summary.json new file mode 100644 index 000000000..ddea80696 --- /dev/null +++ b/dsh/docs/pr-assets/issue-274-profile-generation/logs/verification-summary.json @@ -0,0 +1,47 @@ +{ + "versions": { + "node": "v24.20.0", + "pnpm": "10.13.1" + }, + "realNativeCli": "0.2.9", + "vitestWorkers": 1, + "artifactConcurrency": 1, + "stages": [ + { + "stage": "verify", + "exitCode": 0, + "seconds": 357.75 + }, + { + "stage": "verify:plugins", + "exitCode": 0, + "seconds": 557.52 + } + ], + "commands": [ + "pnpm run verify", + "pnpm run verify:plugins --skip-build" + ], + "root": { + "filesPassed": 98, + "filesSkipped": 2, + "testsPassed": 1351, + "testsSkipped": 6 + }, + "plugins": { + "packages": 16, + "testsPassed": 382, + "testsSkipped": 2 + }, + "artifacts": { + "independentRepositories": 16, + "packedArtifacts": 17, + "externalConsumer": "passed", + "realHeadlessStarter": "passed", + "threeOptionalStrategies": "passed" + }, + "localLogSha256": { + "verify-final.log": "b47965d48dadb9ff04f56aa6931245fbc14302fbb625d4d8749817c3411cfae0", + "verify-plugins-final.log": "e26014d10b584b37931c2695b0cc9c03147ddfc1882fc5e0690db0d18b36a34a" + } +} diff --git a/dsh/docs/pr-assets/issue-275-agent-teams/README.md b/dsh/docs/pr-assets/issue-275-agent-teams/README.md new file mode 100644 index 000000000..4699fee62 --- /dev/null +++ b/dsh/docs/pr-assets/issue-275-agent-teams/README.md @@ -0,0 +1,67 @@ +# Agent Teams idle review — issue #275 + +[简体中文](./README.zh-CN.md) + +Test base: `84d469ffa838a36fa579295d94029fcac8ac058e` (Mnemon 0.5.13), macOS, Node 24.20.0 and pnpm 10.13.1. Only published DSH contracts were used. The root development cohort and lockfile remain unchanged. + +The original Team gate skipped both fork and spawn before child creation on published DSH/Teams 0.1.7-rc.1; [before-published.json](./before-published.json) records the same controls with no Teams and with TeamService alone. The earlier 0.1.5-rc.2 / Teams 0.1.5-alpha.2 policy is dynamic, while 0.1.7-rc.1's policy is static. The fix keeps default `pause` and adds an explicit `scoped` setting without removing another plugin's policy or weakening publication, ownership, depth or execution restrictions. + +Browser acceptance also exposed that bounded checkpoints read a nonexistent user-message wrapper. The public `user/message` payload is flat on both tested DSH generations. The regression test failed before the correction and now checks complete human decisions and English/Chinese no-write instructions, while excluding injected recall, summaries and unknown sources. Current-surface, completed-turn and whole-message budget rules remain in effect. + +## Reproduce + +Copy [rc1-profile.json](./rc1-profile.json) or [legacy-profile.json](./legacy-profile.json) to `package.json` in an empty temporary directory, then install there using Node 24. For the older exact cohort, use `npm install --ignore-scripts --legacy-peer-deps`; its explicit pins prevent newer prereleases from replacing the historical runtime. No official package is edited. + +```sh +MNEMON_TEAM_TEST_PROFILE=/absolute/rc1-profile pnpm exec vitest run tests/agent-team-review-host.spec.ts --maxWorkers=1 --minWorkers=1 +MNEMON_TEAM_TEST_PROFILE=/absolute/legacy-profile MNEMON_TEAM_TEST_LEGACY=1 pnpm exec vitest run tests/agent-team-review-host.spec.ts --maxWorkers=1 --minWorkers=1 +``` + +The matrix combines fork/spawn, native/Code Mode, no Teams/TeamService/full Team tools, and pause/scoped modes. Scoped, Light Context and Auto Capture are mounted together, with the compiled View checked on each model call. The only scripted part is model choice: DSH actually publishes children, executes/denies tools, commits Runtime changes, preserves parent Team tasks and disposes completed children. The model must receive the explicit parent-user evidence. A cancellation case runs before mutation. + +For WebUI, build and pack Root and all sixteen independent plugins into one artifact directory, preserving the baseline artifacts separately. Start each side with a different empty state directory: + +```sh +MNEMON_CLI_PATH=/absolute/verified/mnemon node scripts/fixtures/serve-team-review.mjs \ + --profile /absolute/rc1-profile --artifacts /absolute/packed-artifacts \ + --state /absolute/disposable-state --policy pause +``` + +The fixture installs the packed root through the public DSH plugin command and resolves all plugin tarballs through a loopback registry. Official Coding, Teams and PTC services stay enabled on both sides. Only the native directory dialog is replaced with the official browser picker. Private connection metadata and raw logs stay in the state directory with restricted permissions. Startup tests both text and tool-use SSE through the actual published DeepSeek adapter. + +Select the fixture workspace, then select **Scoped child review** in Mnemon Settings for the fixed side. Send these two separate messages: + +```text +Synthetic issue 275 acceptance: for this disposable project we explicitly choose SQLite as the durable local state database. Preserve this project decision for later implementation and use the existing scoped memory workflow; no production data or credentials are involved. +``` + +```text +Synthetic issue 275 checkpoint: the project decision is final and stable. SQLite remains the durable local state database for all future modules in this disposable workspace. Record this explicit project convention through the bounded idle review after this completed conversation. +``` + +The child model endpoint rejects the run unless its own request includes both whole user messages. It then attempts forbidden Team delegation, commits one Runtime fact, performs a bounded Document search and completes. Send `TEAM_CHECK` afterward to invoke the real parent `team_task_create`; additional completed turns must respect the one-attempt fixture budget. Disabling Idle review must keep Teams and the existing data usable. + +## Results and limits + +The [final verification report](./verification.json) records successful `verify` and `verify:plugins --skip-build` exits. Root tests passed 1,354 with eight optional skips; the sixteen plugin packages passed 381 with three optional skips. The published Team cohorts and the real Native Provider integration with CLI 0.2.9 passed separately. Independent artifact verification passed for all sixteen plugin repositories and seventeen tarballs, including the public SDK consumer and real DSH Starter/Strategy composition. All four Vitest worker variables and artifact concurrency were set to one; timeout and performance limits were unchanged. The report lists each default skip explicitly. + +The final package contains 49 files and 1,373,650 unpacked bytes, an increase of 2,872 bytes over the baseline. Every published file matches the artifact used for final browser acceptance. The package budget is 1,376,000 bytes, leaving 2,350 bytes of headroom. + +The [0.1.7-rc.1 matrix](./after-published.json) passes all sixteen combinations: twelve successful scoped reviews and four pauses with no child creation. Two additional native cancellation attempts make no mutation. The [legacy matrix](./legacy-published.json) also passes its sixteen expected outcomes: eight successful no-Team/service-only controls, four pauses, and four scoped full-Team runs that fail with `TEAM_NOT_MEMBER` after the denied delegation attempt, before a Runtime write. No failed provider is replayed. Both matrices verify all three Strategy contributions and preserve successful parent Team tools. + +The [completed browser report](./web-acceptance.json) records two successful baseline turns with zero review children and zero Runtime facts. The fixed run has four successful parent turns, exactly one review child and one Runtime fact, including after the extra budget-check turn. Both original user messages are present in the persisted child checkpoint and all four model requests. The child receives an execution error for `spawn_teammate`, then committed Runtime, successful Document search and completion receipts. The parent creates an official Team task afterward. Mnemon CLI 0.2.9 independently reads the original canary back through a read-only Store snapshot. + +| Screenshot | Observed result | +|---|---| +| [Before: Team pause](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-275-agent-teams/before-team-paused.png) | Original warning and no Runtime fact after two eligible turns | +| [Scoped setting](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-275-agent-teams/final-scoped-settings.png) | Explicit compatibility choice saved through the actual settings UI | +| [Child user evidence](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-275-agent-teams/final-child-user-evidence.png) | Whole live-user decisions retained in the bounded checkpoint | +| [Child guard and write](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-275-agent-teams/final-child-guard-and-receipt.png) | Team delegation denied; the allowed Runtime tool succeeds | +| [Parent Team task](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-275-agent-teams/final-parent-team-task.png) | Parent Team tools remain functional | +| [Runtime after the budget check](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-275-agent-teams/final-runtime.png) | One project fact, no duplicate entry | + +![Original Team compatibility pause](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-275-agent-teams/before-team-paused.png) + +![Final Runtime fact after the extra budget-check turn](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-275-agent-teams/final-runtime.png) + +The compact reports contain only synthetic case metadata and receipts. The deterministic model validates integration and evidence transport; it does not measure live-model memory quality. Disabling review, no-write instructions and startup errors also have automated coverage; they are not additional GUI claims. No production credentials, memories or personal CLI installation are used. Windows was not exercised in this macOS run. diff --git a/dsh/docs/pr-assets/issue-275-agent-teams/README.zh-CN.md b/dsh/docs/pr-assets/issue-275-agent-teams/README.zh-CN.md new file mode 100644 index 000000000..a7b3c8ca0 --- /dev/null +++ b/dsh/docs/pr-assets/issue-275-agent-teams/README.zh-CN.md @@ -0,0 +1,59 @@ +# Agent Teams 空闲审查 — issue #275 + +[English](./README.md) + +测试基线:`84d469ffa838a36fa579295d94029fcac8ac058e`(Mnemon 0.5.13)、macOS、Node 24.20.0 与 pnpm 10.13.1。仅使用正式发布的 DSH 契约,根项目开发依赖族和 lockfile 未改变。 + +旧 Team gate 在正式 DSH/Teams 0.1.7-rc.1 中仍于创建子代理前跳过 fork 与 spawn;[before-published.json](./before-published.json)同时记录无 Teams、只有 TeamService 的对照。旧 0.1.5-rc.2 / Teams 0.1.5-alpha.2 使用动态策略,0.1.7-rc.1 改为静态策略。本修复保留默认 `pause`,新增显式 `scoped` 设置,不移除其他插件策略,也不放松发布、归属、深度或执行限制。 + +浏览器验收还发现有界检查点错误读取不存在的用户消息包装;两个被测 DSH 世代的公开 `user/message` 都是扁平载荷。回归测试在修复前真实失败,修复后检查完整用户决策与中英禁止写入指令,拒绝注入召回、摘要和未知来源。当前 surface、已完成回合、整条消息预算规则均保留。 + +## 复现 + +将 [rc1-profile.json](./rc1-profile.json) 或 [legacy-profile.json](./legacy-profile.json)复制为新临时目录中的 `package.json`,用 Node 24 在其中安装。旧版精确组合使用 `npm install --ignore-scripts --legacy-peer-deps`;显式版本 pin 防止较新预发布替换历史 runtime。不修改任何官方包。 + +```sh +MNEMON_TEAM_TEST_PROFILE=/absolute/rc1-profile pnpm exec vitest run tests/agent-team-review-host.spec.ts --maxWorkers=1 --minWorkers=1 +MNEMON_TEAM_TEST_PROFILE=/absolute/legacy-profile MNEMON_TEAM_TEST_LEGACY=1 pnpm exec vitest run tests/agent-team-review-host.spec.ts --maxWorkers=1 --minWorkers=1 +``` + +矩阵组合 fork/spawn、原生/Code Mode、无 Teams/只有 TeamService/全部 Team 工具,以及 pause/scoped。Scoped、Light Context、Auto Capture 同时加载,每次模型调用都检查编译 View。只有模型选择由脚本固定;DSH 实际发布子代理、执行/拒绝工具、提交 Runtime、保留父 Team 任务并 dispose 完成的子代理。模型必须收到父会话明确用户证据,另在写入前执行取消用例。 + +WebUI 先构建 Root 与十六个独立插件,将全部 tarball 放入同一目录,另保留基线制品。前后两侧使用不同的空状态目录启动: + +```sh +MNEMON_CLI_PATH=/absolute/verified/mnemon node scripts/fixtures/serve-team-review.mjs \ + --profile /absolute/rc1-profile --artifacts /absolute/packed-artifacts \ + --state /absolute/disposable-state --policy pause +``` + +夹具通过公开 DSH plugin 命令安装根包,经 loopback registry 解析全部插件制品。前后两侧均启用官方 Coding、Teams 与 PTC 服务;仅将原生目录对话框替换为官方浏览器目录选择器。私有连接信息与原始日志保存在受限权限的状态目录,启动时通过真实公开 DeepSeek 适配器检查文本与工具 SSE。 + +选择夹具工作区,修复后在 Mnemon 设置中选择**受限子代理审查**,然后分两轮发送 [English 记录](./README.md#reproduce)中的两条完整合成用户消息。 + +子代理模型端点只有在自身请求中包含两条完整用户消息时才继续。随后尝试被禁止的 Team 委派、提交一条 Runtime 事实、执行有界 Document 搜索并完成。之后发送 `TEAM_CHECK`,调用真实父代理 `team_task_create`;继续完成回合仍应遵守夹具的一次尝试预算。关闭空闲审查后,Teams 和已有数据仍应可用。 + +## 结果与边界 + +[最终验证报告](./verification.json)记录 `verify` 与 `verify:plugins --skip-build` 均成功退出。根测试通过 1,354 项,默认跳过八项可选测试;十六个插件包通过 381 项,默认跳过三项可选测试。公开 Team 组合与使用 CLI 0.2.9 的真实 Native Provider 集成另行执行并通过。独立制品验证覆盖全部十六个插件仓库与十七个 tarball,包括公开 SDK 消费方和真实 DSH Starter/Strategy 组合。四个 Vitest worker 变量及制品并发均设为一,超时和性能限制未改变。报告逐项列出默认跳过项。 + +最终包包含 49 个文件,解包体积 1,373,650 字节,比基线增加 2,872 字节。全部发布文件与最终浏览器验收使用的制品逐一相同。包体积预算为 1,376,000 字节,剩余 2,350 字节。 + +[0.1.7-rc.1 矩阵](./after-published.json)的十六种组合全部通过:十二次受限审查成功、四次暂停且不创建子代理;另有两次原生取消尝试,没有新增写入。[旧版矩阵](./legacy-published.json)的十六个预期结果也全部通过:八个无 Teams/仅服务对照成功、四次暂停、四次完整 Teams 的 scoped 运行在委派被拒后报 `TEAM_NOT_MEMBER`,在 Runtime 写入前停止,失败 provider 不被重放。两个矩阵均验证全部三个 Strategy 贡献和父 Team 工具成功。 + +[已完成的浏览器报告](./web-acceptance.json)记录基线两个成功回合、零审查子代理、零 Runtime 事实。修复后父会话四个成功回合、恰好一个审查子代理和一条 Runtime 事实;额外预算验证回合没有重复写入。两个完整原用户消息出现在持久化子代理检查点和四次模型请求中。子代理的 `spawn_teammate` 被执行限制拒绝,随后得到 Runtime committed、Document 搜索成功和 completion 回执;父会话之后仍成功创建官方 Team 任务。Mnemon CLI 0.2.9 另通过 Store 只读快照读回原有 canary。 + +| 截图 | 观察结果 | +|---|---| +| [修复前 Team 暂停](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-275-agent-teams/before-team-paused.png) | 两个合格回合后仍显示原警告,没有 Runtime 事实 | +| [受限设置](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-275-agent-teams/final-scoped-settings.png) | 真实设置界面保存显式兼容选项 | +| [子代理用户证据](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-275-agent-teams/final-child-user-evidence.png) | 有界检查点保留完整真实用户决策 | +| [子代理执行限制与写入](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-275-agent-teams/final-child-guard-and-receipt.png) | Team 委派被拒绝,允许的 Runtime 工具成功 | +| [父会话 Team 任务](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-275-agent-teams/final-parent-team-task.png) | 父会话 Team 工具继续有效 | +| [预算验证后的 Runtime](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-275-agent-teams/final-runtime.png) | 只有一条项目事实,没有重复记录 | + +![原 Team 兼容暂停](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-275-agent-teams/before-team-paused.png) + +![额外预算验证回合后的 Runtime 事实](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-275-agent-teams/final-runtime.png) + +精简报告只包含合成用例元数据与回执。确定性模型验证集成与证据传输,不评价真实模型的记忆判断质量。关闭审查、禁止写入指令与启动错误另有自动测试,不额外宣称 GUI 验收。不使用生产凭据、个人记忆或替换个人 CLI。此次 macOS 运行未覆盖 Windows。 diff --git a/dsh/docs/pr-assets/issue-275-agent-teams/after-published.json b/dsh/docs/pr-assets/issue-275-agent-teams/after-published.json new file mode 100644 index 000000000..26dcfc0e4 --- /dev/null +++ b/dsh/docs/pr-assets/issue-275-agent-teams/after-published.json @@ -0,0 +1,536 @@ +{ + "versions": { + "dsh": "0.1.7-rc.1", + "dsh-agent": "0.1.7-rc.1", + "dsh-tools": "0.1.7-rc.1", + "dsh-subagent": "0.1.7-rc.1", + "dsh-experimental-agent-team": "0.1.7-rc.1", + "dsh-experimental-tool-agent-team": "0.1.7-rc.1" + }, + "reports": [ + { + "provider": "spawn", + "teams": "off", + "mode": "native", + "policy": "scoped", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 4, + "children": 1, + "cancelled": false, + "receipts": [ + { + "name": "spawn_teammate", + "isError": true + }, + { + "name": "mnemon_runtime_memory", + "isError": false + }, + { + "name": "mnemon_document_search", + "isError": false + }, + { + "name": "mnemon_subagent_result", + "isError": false + } + ] + }, + { + "provider": "spawn", + "teams": "off", + "mode": "ptc", + "policy": "scoped", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 4, + "children": 1, + "cancelled": false, + "receipts": [ + { + "name": "run_code", + "isError": true + }, + { + "name": "mnemon_runtime_memory", + "isError": false + }, + { + "name": "run_code", + "isError": false + }, + { + "name": "mnemon_document_search", + "isError": false + }, + { + "name": "run_code", + "isError": false + }, + { + "name": "mnemon_subagent_result", + "isError": false + }, + { + "name": "run_code", + "isError": false + } + ] + }, + { + "provider": "spawn", + "teams": "service", + "mode": "native", + "policy": "scoped", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 4, + "children": 1, + "cancelled": false, + "receipts": [ + { + "name": "spawn_teammate", + "isError": true + }, + { + "name": "mnemon_runtime_memory", + "isError": false + }, + { + "name": "mnemon_document_search", + "isError": false + }, + { + "name": "mnemon_subagent_result", + "isError": false + } + ] + }, + { + "provider": "spawn", + "teams": "service", + "mode": "ptc", + "policy": "scoped", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 4, + "children": 1, + "cancelled": false, + "receipts": [ + { + "name": "run_code", + "isError": true + }, + { + "name": "mnemon_runtime_memory", + "isError": false + }, + { + "name": "run_code", + "isError": false + }, + { + "name": "mnemon_document_search", + "isError": false + }, + { + "name": "run_code", + "isError": false + }, + { + "name": "mnemon_subagent_result", + "isError": false + }, + { + "name": "run_code", + "isError": false + } + ] + }, + { + "provider": "spawn", + "teams": "tools", + "mode": "native", + "policy": "pause", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 0, + "children": 0, + "cancelled": false, + "receipts": [] + }, + { + "provider": "spawn", + "teams": "tools", + "mode": "native", + "policy": "scoped", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 5, + "children": 2, + "cancelled": true, + "receipts": [ + { + "name": "spawn_teammate", + "isError": true + }, + { + "name": "mnemon_runtime_memory", + "isError": false + }, + { + "name": "mnemon_document_search", + "isError": false + }, + { + "name": "mnemon_subagent_result", + "isError": false + } + ] + }, + { + "provider": "spawn", + "teams": "tools", + "mode": "ptc", + "policy": "pause", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 0, + "children": 0, + "cancelled": false, + "receipts": [] + }, + { + "provider": "spawn", + "teams": "tools", + "mode": "ptc", + "policy": "scoped", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 4, + "children": 1, + "cancelled": false, + "receipts": [ + { + "name": "spawn_teammate", + "isError": true + }, + { + "name": "run_code", + "isError": true + }, + { + "name": "mnemon_runtime_memory", + "isError": false + }, + { + "name": "run_code", + "isError": false + }, + { + "name": "mnemon_document_search", + "isError": false + }, + { + "name": "run_code", + "isError": false + }, + { + "name": "mnemon_subagent_result", + "isError": false + }, + { + "name": "run_code", + "isError": false + } + ] + }, + { + "provider": "fork", + "teams": "off", + "mode": "native", + "policy": "scoped", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 4, + "children": 1, + "cancelled": false, + "receipts": [ + { + "name": "spawn_teammate", + "isError": true + }, + { + "name": "mnemon_runtime_memory", + "isError": false + }, + { + "name": "mnemon_document_search", + "isError": false + }, + { + "name": "mnemon_subagent_result", + "isError": false + } + ] + }, + { + "provider": "fork", + "teams": "off", + "mode": "ptc", + "policy": "scoped", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 4, + "children": 1, + "cancelled": false, + "receipts": [ + { + "name": "run_code", + "isError": true + }, + { + "name": "mnemon_runtime_memory", + "isError": false + }, + { + "name": "run_code", + "isError": false + }, + { + "name": "mnemon_document_search", + "isError": false + }, + { + "name": "run_code", + "isError": false + }, + { + "name": "mnemon_subagent_result", + "isError": false + }, + { + "name": "run_code", + "isError": false + } + ] + }, + { + "provider": "fork", + "teams": "service", + "mode": "native", + "policy": "scoped", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 4, + "children": 1, + "cancelled": false, + "receipts": [ + { + "name": "spawn_teammate", + "isError": true + }, + { + "name": "mnemon_runtime_memory", + "isError": false + }, + { + "name": "mnemon_document_search", + "isError": false + }, + { + "name": "mnemon_subagent_result", + "isError": false + } + ] + }, + { + "provider": "fork", + "teams": "service", + "mode": "ptc", + "policy": "scoped", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 4, + "children": 1, + "cancelled": false, + "receipts": [ + { + "name": "run_code", + "isError": true + }, + { + "name": "mnemon_runtime_memory", + "isError": false + }, + { + "name": "run_code", + "isError": false + }, + { + "name": "mnemon_document_search", + "isError": false + }, + { + "name": "run_code", + "isError": false + }, + { + "name": "mnemon_subagent_result", + "isError": false + }, + { + "name": "run_code", + "isError": false + } + ] + }, + { + "provider": "fork", + "teams": "tools", + "mode": "native", + "policy": "pause", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 0, + "children": 0, + "cancelled": false, + "receipts": [] + }, + { + "provider": "fork", + "teams": "tools", + "mode": "native", + "policy": "scoped", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 5, + "children": 2, + "cancelled": true, + "receipts": [ + { + "name": "spawn_teammate", + "isError": true + }, + { + "name": "mnemon_runtime_memory", + "isError": false + }, + { + "name": "mnemon_document_search", + "isError": false + }, + { + "name": "mnemon_subagent_result", + "isError": false + } + ] + }, + { + "provider": "fork", + "teams": "tools", + "mode": "ptc", + "policy": "pause", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 0, + "children": 0, + "cancelled": false, + "receipts": [] + }, + { + "provider": "fork", + "teams": "tools", + "mode": "ptc", + "policy": "scoped", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 4, + "children": 1, + "cancelled": false, + "receipts": [ + { + "name": "spawn_teammate", + "isError": true + }, + { + "name": "run_code", + "isError": true + }, + { + "name": "mnemon_runtime_memory", + "isError": false + }, + { + "name": "run_code", + "isError": false + }, + { + "name": "mnemon_document_search", + "isError": false + }, + { + "name": "run_code", + "isError": false + }, + { + "name": "mnemon_subagent_result", + "isError": false + }, + { + "name": "run_code", + "isError": false + } + ] + } + ] +} diff --git a/dsh/docs/pr-assets/issue-275-agent-teams/before-published.json b/dsh/docs/pr-assets/issue-275-agent-teams/before-published.json new file mode 100644 index 000000000..aa813e9f6 --- /dev/null +++ b/dsh/docs/pr-assets/issue-275-agent-teams/before-published.json @@ -0,0 +1,86 @@ +{ + "base": "84d469ffa838a36fa579295d94029fcac8ac058e", + "versions": { + "dsh": "0.1.7-rc.1", + "dsh-agent": "0.1.7-rc.1", + "dsh-agent-loop": "0.1.7-rc.1", + "dsh-subagent": "0.1.7-rc.1", + "dsh-subagent-in-process-driver": "0.1.7-rc.1", + "dsh-subagent-fork-in-process": "0.1.7-rc.1", + "dsh-subagent-spawn-in-process": "0.1.7-rc.1", + "dsh-experimental-agent-team": "0.1.7-rc.1", + "dsh-experimental-tool-agent-team": "0.1.7-rc.1" + }, + "cases": [ + { + "provider": "fork", + "teams": "off", + "preflight": { + "hasTeamService": false, + "hasScopedTeamSpawnTool": false, + "hasGlobalTeamSpawnTool": false + }, + "childModelCalls": 2, + "result": "completed" + }, + { + "provider": "spawn", + "teams": "off", + "preflight": { + "hasTeamService": false, + "hasScopedTeamSpawnTool": false, + "hasGlobalTeamSpawnTool": false + }, + "childModelCalls": 2, + "result": "completed" + }, + { + "provider": "fork", + "teams": "service", + "preflight": { + "hasTeamService": true, + "hasScopedTeamSpawnTool": false, + "hasGlobalTeamSpawnTool": false + }, + "childModelCalls": 2, + "result": "completed" + }, + { + "provider": "spawn", + "teams": "service", + "preflight": { + "hasTeamService": true, + "hasScopedTeamSpawnTool": false, + "hasGlobalTeamSpawnTool": false + }, + "childModelCalls": 2, + "result": "completed" + }, + { + "provider": "fork", + "teams": "tools", + "preflight": { + "hasTeamService": true, + "hasScopedTeamSpawnTool": true, + "hasGlobalTeamSpawnTool": false, + "blockReason": "agent-team" + }, + "guardOutcome": "skipped before child creation", + "childModelCalls": 0, + "parentTeamToolSucceeded": true + }, + { + "provider": "spawn", + "teams": "tools", + "preflight": { + "hasTeamService": true, + "hasScopedTeamSpawnTool": true, + "hasGlobalTeamSpawnTool": false, + "blockReason": "agent-team" + }, + "guardOutcome": "skipped before child creation", + "childModelCalls": 0, + "parentTeamToolSucceeded": true + } + ] +} diff --git a/dsh/docs/pr-assets/issue-275-agent-teams/legacy-profile.json b/dsh/docs/pr-assets/issue-275-agent-teams/legacy-profile.json new file mode 100644 index 000000000..20cd11386 --- /dev/null +++ b/dsh/docs/pr-assets/issue-275-agent-teams/legacy-profile.json @@ -0,0 +1,55 @@ +{ + "dependencies": { + "@deepseek-ai/cordis": "4.0.2", + "@deepseek-ai/cordis-plugin-include": "1.0.7", + "@deepseek-ai/cordis-plugin-loader": "1.0.3", + "@deepseek-ai/dsh": "0.1.5-rc.2", + "@deepseek-ai/dsh-agent": "0.1.5-rc.2", + "@deepseek-ai/dsh-agent-loop": "0.1.5-rc.2", + "@deepseek-ai/dsh-agent-presets": "0.1.5-rc.2", + "@deepseek-ai/dsh-atomic-write": "0.1.5-rc.2", + "@deepseek-ai/dsh-attachment": "0.1.5-rc.2", + "@deepseek-ai/dsh-brand": "0.1.5-rc.2", + "@deepseek-ai/dsh-chunked-list": "0.1.5-rc.2", + "@deepseek-ai/dsh-code-runtime": "0.1.5-rc.2", + "@deepseek-ai/dsh-code-runtime-worker-thread": "0.1.5-rc.2", + "@deepseek-ai/dsh-experimental-agent-team": "0.1.5-alpha.2", + "@deepseek-ai/dsh-experimental-tool-agent-team": "0.1.5-alpha.2", + "@deepseek-ai/dsh-home-paths": "0.1.5-rc.2", + "@deepseek-ai/dsh-invariants": "0.1.5-rc.2", + "@deepseek-ai/dsh-jobs": "0.1.5-rc.2", + "@deepseek-ai/dsh-llm": "0.1.5-rc.2", + "@deepseek-ai/dsh-sandbox": "0.1.5-rc.2", + "@deepseek-ai/dsh-sandbox-policy": "0.1.5-rc.2", + "@deepseek-ai/dsh-scope": "0.1.5-rc.2", + "@deepseek-ai/dsh-session": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-format": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-format-catalog": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-format-v0-to-v1": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-format-v1-to-v2": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-format-v2-to-v3": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-persistence": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-persistence-jsonl": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-projection": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-projection-cache": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-query": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-query-sqlite": "0.1.5-rc.2", + "@deepseek-ai/dsh-session-title": "0.1.5-rc.2", + "@deepseek-ai/dsh-settings": "0.1.5-rc.2", + "@deepseek-ai/dsh-storage": "0.1.5-rc.2", + "@deepseek-ai/dsh-storage-domain": "0.1.5-rc.2", + "@deepseek-ai/dsh-subagent": "0.1.5-rc.2", + "@deepseek-ai/dsh-subagent-fork-in-process": "0.1.5-rc.2", + "@deepseek-ai/dsh-subagent-in-process-driver": "0.1.5-rc.2", + "@deepseek-ai/dsh-subagent-spawn-in-process": "0.1.5-rc.2", + "@deepseek-ai/dsh-system-prompt": "0.1.5-rc.2", + "@deepseek-ai/dsh-timeout": "0.1.5-rc.2", + "@deepseek-ai/dsh-tool-todo": "0.1.5-rc.2", + "@deepseek-ai/dsh-tools": "0.1.5-rc.2", + "@deepseek-ai/dsh-typert-protocol": "0.1.5-rc.2", + "@deepseek-ai/dsh-user-approval": "0.1.5-rc.2", + "@deepseek-ai/dsh-util-crypto": "0.1.5-rc.2", + "@deepseek-ai/dsh-util-time": "0.1.5-rc.2", + "@deepseek-ai/dsh-util-values": "0.1.5-rc.2" + } +} diff --git a/dsh/docs/pr-assets/issue-275-agent-teams/legacy-published.json b/dsh/docs/pr-assets/issue-275-agent-teams/legacy-published.json new file mode 100644 index 000000000..a638475d5 --- /dev/null +++ b/dsh/docs/pr-assets/issue-275-agent-teams/legacy-published.json @@ -0,0 +1,464 @@ +{ + "versions": { + "dsh": "0.1.5-rc.2", + "dsh-agent": "0.1.5-rc.2", + "dsh-tools": "0.1.5-rc.2", + "dsh-subagent": "0.1.5-rc.2", + "dsh-experimental-agent-team": "0.1.5-alpha.2", + "dsh-experimental-tool-agent-team": "0.1.5-alpha.2" + }, + "reports": [ + { + "provider": "spawn", + "teams": "off", + "mode": "native", + "policy": "scoped", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 4, + "children": 1, + "cancelled": false, + "receipts": [ + { + "name": "spawn_teammate", + "isError": true + }, + { + "name": "mnemon_runtime_memory", + "isError": false + }, + { + "name": "mnemon_document_search", + "isError": false + }, + { + "name": "mnemon_subagent_result", + "isError": false + } + ] + }, + { + "provider": "spawn", + "teams": "off", + "mode": "ptc", + "policy": "scoped", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 4, + "children": 1, + "cancelled": false, + "receipts": [ + { + "name": "run_code", + "isError": true + }, + { + "name": "mnemon_runtime_memory", + "isError": false + }, + { + "name": "run_code", + "isError": false + }, + { + "name": "mnemon_document_search", + "isError": false + }, + { + "name": "run_code", + "isError": false + }, + { + "name": "mnemon_subagent_result", + "isError": false + }, + { + "name": "run_code", + "isError": false + } + ] + }, + { + "provider": "spawn", + "teams": "service", + "mode": "native", + "policy": "scoped", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 4, + "children": 1, + "cancelled": false, + "receipts": [ + { + "name": "spawn_teammate", + "isError": true + }, + { + "name": "mnemon_runtime_memory", + "isError": false + }, + { + "name": "mnemon_document_search", + "isError": false + }, + { + "name": "mnemon_subagent_result", + "isError": false + } + ] + }, + { + "provider": "spawn", + "teams": "service", + "mode": "ptc", + "policy": "scoped", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 4, + "children": 1, + "cancelled": false, + "receipts": [ + { + "name": "run_code", + "isError": true + }, + { + "name": "mnemon_runtime_memory", + "isError": false + }, + { + "name": "run_code", + "isError": false + }, + { + "name": "mnemon_document_search", + "isError": false + }, + { + "name": "run_code", + "isError": false + }, + { + "name": "mnemon_subagent_result", + "isError": false + }, + { + "name": "run_code", + "isError": false + } + ] + }, + { + "provider": "spawn", + "teams": "tools", + "mode": "native", + "policy": "pause", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 0, + "children": 0, + "cancelled": false, + "receipts": [] + }, + { + "provider": "spawn", + "teams": "tools", + "mode": "native", + "policy": "scoped", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 1, + "children": 1, + "cancelled": false, + "receipts": [ + { + "name": "spawn_teammate", + "isError": true + } + ] + }, + { + "provider": "spawn", + "teams": "tools", + "mode": "ptc", + "policy": "pause", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 0, + "children": 0, + "cancelled": false, + "receipts": [] + }, + { + "provider": "spawn", + "teams": "tools", + "mode": "ptc", + "policy": "scoped", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 1, + "children": 1, + "cancelled": false, + "receipts": [ + { + "name": "spawn_teammate", + "isError": true + }, + { + "name": "run_code", + "isError": true + } + ] + }, + { + "provider": "fork", + "teams": "off", + "mode": "native", + "policy": "scoped", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 4, + "children": 1, + "cancelled": false, + "receipts": [ + { + "name": "spawn_teammate", + "isError": true + }, + { + "name": "mnemon_runtime_memory", + "isError": false + }, + { + "name": "mnemon_document_search", + "isError": false + }, + { + "name": "mnemon_subagent_result", + "isError": false + } + ] + }, + { + "provider": "fork", + "teams": "off", + "mode": "ptc", + "policy": "scoped", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 4, + "children": 1, + "cancelled": false, + "receipts": [ + { + "name": "run_code", + "isError": true + }, + { + "name": "mnemon_runtime_memory", + "isError": false + }, + { + "name": "run_code", + "isError": false + }, + { + "name": "mnemon_document_search", + "isError": false + }, + { + "name": "run_code", + "isError": false + }, + { + "name": "mnemon_subagent_result", + "isError": false + }, + { + "name": "run_code", + "isError": false + } + ] + }, + { + "provider": "fork", + "teams": "service", + "mode": "native", + "policy": "scoped", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 4, + "children": 1, + "cancelled": false, + "receipts": [ + { + "name": "spawn_teammate", + "isError": true + }, + { + "name": "mnemon_runtime_memory", + "isError": false + }, + { + "name": "mnemon_document_search", + "isError": false + }, + { + "name": "mnemon_subagent_result", + "isError": false + } + ] + }, + { + "provider": "fork", + "teams": "service", + "mode": "ptc", + "policy": "scoped", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 4, + "children": 1, + "cancelled": false, + "receipts": [ + { + "name": "run_code", + "isError": true + }, + { + "name": "mnemon_runtime_memory", + "isError": false + }, + { + "name": "run_code", + "isError": false + }, + { + "name": "mnemon_document_search", + "isError": false + }, + { + "name": "run_code", + "isError": false + }, + { + "name": "mnemon_subagent_result", + "isError": false + }, + { + "name": "run_code", + "isError": false + } + ] + }, + { + "provider": "fork", + "teams": "tools", + "mode": "native", + "policy": "pause", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 0, + "children": 0, + "cancelled": false, + "receipts": [] + }, + { + "provider": "fork", + "teams": "tools", + "mode": "native", + "policy": "scoped", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 1, + "children": 1, + "cancelled": false, + "receipts": [ + { + "name": "spawn_teammate", + "isError": true + } + ] + }, + { + "provider": "fork", + "teams": "tools", + "mode": "ptc", + "policy": "pause", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 0, + "children": 0, + "cancelled": false, + "receipts": [] + }, + { + "provider": "fork", + "teams": "tools", + "mode": "ptc", + "policy": "scoped", + "strategyExtensions": [ + "scoped", + "light-context", + "auto-capture" + ], + "childCalls": 1, + "children": 1, + "cancelled": false, + "receipts": [ + { + "name": "spawn_teammate", + "isError": true + }, + { + "name": "run_code", + "isError": true + } + ] + } + ] +} diff --git a/dsh/docs/pr-assets/issue-275-agent-teams/rc1-profile.json b/dsh/docs/pr-assets/issue-275-agent-teams/rc1-profile.json new file mode 100644 index 000000000..976d2a51b --- /dev/null +++ b/dsh/docs/pr-assets/issue-275-agent-teams/rc1-profile.json @@ -0,0 +1,9 @@ +{ + "dependencies": { + "@deepseek-ai/dsh": "0.1.7-rc.1", + "@deepseek-ai/dsh-experimental-agent-team": "0.1.7-rc.1", + "@deepseek-ai/dsh-experimental-agent-team-profile": "0.1.7-rc.1", + "@deepseek-ai/dsh-experimental-client-ui-agent-team": "0.1.7-rc.1", + "@deepseek-ai/dsh-experimental-tool-agent-team": "0.1.7-rc.1" + } +} diff --git a/dsh/docs/pr-assets/issue-275-agent-teams/verification.json b/dsh/docs/pr-assets/issue-275-agent-teams/verification.json new file mode 100644 index 000000000..d515d706b --- /dev/null +++ b/dsh/docs/pr-assets/issue-275-agent-teams/verification.json @@ -0,0 +1,150 @@ +{ + "base": "84d469ffa838a36fa579295d94029fcac8ac058e", + "platform": "macOS", + "versions": { + "node": "24.20.0", + "pnpm": "10.13.1", + "mnemonCli": "0.2.9" + }, + "environment": { + "VITEST_MAX_THREADS": "1", + "VITEST_MIN_THREADS": "1", + "VITEST_MAX_FORKS": "1", + "VITEST_MIN_FORKS": "1" + }, + "verify": { + "command": "npx --yes --package=node@24.20.0 --package=pnpm@10.13.1 pnpm run verify", + "exitCode": 0, + "root": { + "passedFiles": 97, + "skippedFiles": 3, + "passedTests": 1354, + "skippedTests": 8 + }, + "workspacePlugins": { + "packages": 16, + "passedTests": 381, + "skippedTests": 3 + }, + "defaultSkipped": [ + { + "scope": "root", + "suite": "agent-team-review-host", + "tests": 1, + "reason": "MNEMON_TEAM_TEST_PROFILE unset; both published cohorts passed separately" + }, + { + "scope": "root", + "suite": "runtime-capacity-workflow", + "tests": 1, + "reason": "MNEMON_NATIVE_TEST_CLI unset in full verify" + }, + { + "scope": "root", + "suite": "lifecycle published V4 codec", + "tests": 1, + "reason": "MNEMON_DSH_V4_CONTRACT_ROOT unset" + }, + { + "scope": "root", + "suite": "runtime-capacity-flash-stress", + "tests": 3, + "reason": "live Flash stress opt-in unset" + }, + { + "scope": "root", + "suite": "runtime-memory-flash-quality", + "tests": 2, + "reason": "live Flash quality opt-in unset" + }, + { + "scope": "plugins", + "suite": "source-memory-spaces native-integration", + "tests": 1, + "reason": "MNEMON_NATIVE_TEST_CLI unset; passed separately with official CLI 0.2.9" + }, + { + "scope": "plugins", + "suite": "source-memory-spaces windows-smoke", + "tests": 1, + "reason": "macOS host" + }, + { + "scope": "plugins", + "suite": "provider-openviking integration", + "tests": 1, + "reason": "external OpenViking endpoint unset" + } + ] + }, + "publishedCohorts": { + "rc1": { + "exitCode": 0, + "passedTests": 1, + "combinations": 16, + "successfulScoped": 12, + "pausedBeforeChild": 4, + "additionalCancellationsBeforeMutation": 2, + "report": "after-published.json" + }, + "legacy": { + "exitCode": 0, + "passedTests": 1, + "combinations": 16, + "successfulControls": 8, + "pausedBeforeChild": 4, + "failClosedBeforeRuntimeWrite": 4, + "report": "legacy-published.json" + } + }, + "nativeIntegration": { + "command": "MNEMON_NATIVE_TEST_CLI=/absolute/verified/mnemon npx --yes --package=node@24.20.0 --package=pnpm@10.13.1 pnpm --filter dsh-mnemon-source-memory-spaces exec vitest run tests/native-integration.spec.ts --maxWorkers=1 --minWorkers=1", + "exitCode": 0, + "passedTests": 1, + "operations": [ + "create Space", + "write through compiled View", + "recall", + "forget" + ] + }, + "independentArtifacts": { + "command": "MNEMON_PLUGIN_VERIFY_CONCURRENCY=1 npx --yes --package=node@24.20.0 --package=pnpm@10.13.1 pnpm run verify:plugins --skip-build", + "exitCode": 0, + "workers": 1, + "independentRepositories": 16, + "packedArtifacts": 17, + "checks": [ + "standalone install without workspace links", + "standalone types, Client tests and build", + "external SDK consumer", + "real DSH packed Starter activation", + "real DSH three optional Strategy plugins together" + ] + }, + "package": { + "publishedFiles": 49, + "finalVerifyPackedBytes": 309087, + "unpackedBytes": 1373650, + "baselineUnpackedBytes": 1370778, + "unpackedDeltaBytes": 2872, + "unpackedLimitBytes": 1376000, + "remainingHeadroomBytes": 2350, + "finalWebUiArchiveSha256": "71c282c88af13a770e389ad85c7ec3bc8ea596c1679e08b926779b2bcd37b336", + "finalWebUiPublishedFilesMatchingFullVerify": 49 + }, + "browserAcceptance": "web-acceptance.json", + "privateLogsSha256": { + "verify-final.log": "79f358b4a6a192a278831fcd44943522199060a2bea6884e394c15a5ec0f40e8", + "verify-plugins-final.log": "d1bda73329e5666d93bbee4d8851cc25b94a08cf85065de4931dabfc7e812b12", + "native-integration-final.log": "4d14201fe513d310e401c3745823e0799444d6c78c829c2d002df104acab831c", + "rc1-team-matrix-final.log": "dc5b3a6c281fe647bcda25bc8091a3c0bca1b6410c7ba644423d732cc6ef79a1", + "legacy-team-matrix-final.log": "c4396e7ced7eb867198e8cb677995f8ea7e028d9a0d3debaea739993552f75f9" + }, + "limits": [ + "Deterministic model choices; framework child, tools, storage and CLI are real", + "No live-model memory-quality claim", + "No Windows execution", + "Legacy full-Team dynamic policy fails closed; no automatic replay" + ] +} diff --git a/dsh/docs/pr-assets/issue-275-agent-teams/web-acceptance.json b/dsh/docs/pr-assets/issue-275-agent-teams/web-acceptance.json new file mode 100644 index 000000000..bc1663aef --- /dev/null +++ b/dsh/docs/pr-assets/issue-275-agent-teams/web-acceptance.json @@ -0,0 +1,102 @@ +{ + "base": "84d469ffa838a36fa579295d94029fcac8ac058e", + "dsh": "0.1.7-rc.1", + "teams": "0.1.7-rc.1", + "cli": "0.2.9", + "model": "deterministic loopback via published DeepSeek Messages adapter", + "cases": [ + { + "kind": "before", + "artifactSha256": "c50d9be5efaa7bd9200659802f8fbc3b1e17d03d1f589a64f7f3f13750a0f510", + "successfulParentSession": "session-881b215f-71c7-4f06-9bb9-c62a47947bf0", + "parentCompletedTurns": 2, + "reviewChildren": 0, + "reviewModelSteps": [], + "runtimeEntries": 0, + "runtimeFactMatches": false, + "parentTeamTasksSucceeded": 0, + "children": [] + }, + { + "kind": "after", + "artifactSha256": "71c282c88af13a770e389ad85c7ec3bc8ea596c1679e08b926779b2bcd37b336", + "successfulParentSession": "session-b9834c58-1440-41fa-b4f4-6f0e5046e54d", + "parentCompletedTurns": 4, + "reviewChildren": 1, + "reviewModelSteps": [ + { + "event": "review-model-step", + "review": 1, + "step": 0, + "completeUserMessages": 2 + }, + { + "event": "review-model-step", + "review": 1, + "step": 1, + "completeUserMessages": 2 + }, + { + "event": "review-model-step", + "review": 1, + "step": 2, + "completeUserMessages": 2 + }, + { + "event": "review-model-step", + "review": 1, + "step": 3, + "completeUserMessages": 2 + } + ], + "runtimeEntries": 1, + "runtimeFactMatches": true, + "parentTeamTasksSucceeded": 1, + "children": [ + { + "id": "1464b142-eb61-4532-b08c-fc9c19a6502d", + "parent": "session-b9834c58-1440-41fa-b4f4-6f0e5046e54d", + "completeUserEvidence": [ + true, + true + ], + "receipts": [ + { + "seq": 16, + "name": "spawn_teammate", + "isError": true, + "committed": false, + "error": "Error: Mnemon review cannot execute \"spawn_teammate\"; reuse the inherited checkpoint and bounded Document search." + }, + { + "seq": 22, + "name": "mnemon_runtime_memory", + "isError": false, + "committed": true, + "completion": "committed", + "revision": "9998e8bc15ed7c04e3ac2d0d7a6e54d07f6f47c8046ca80af3ec609a5ede0793" + }, + { + "seq": 28, + "name": "mnemon_document_search", + "isError": false, + "committed": false + }, + { + "seq": 34, + "name": "mnemon_subagent_result", + "isError": false, + "committed": false + } + ] + } + ], + "oneAttemptBudgetPreserved": true + } + ], + "nativeCli": { + "version": "mnemon version 0.2.9", + "operation": "readonly recall from disposable team-review store", + "canaryMatched": true + } +} diff --git a/dsh/docs/pr-assets/issue-280-runtime-metadata/README.md b/dsh/docs/pr-assets/issue-280-runtime-metadata/README.md new file mode 100644 index 000000000..84370f983 --- /dev/null +++ b/dsh/docs/pr-assets/issue-280-runtime-metadata/README.md @@ -0,0 +1,55 @@ +# Runtime projection metadata — issue 280 + +[简体中文](./README.zh-CN.md) + +Baseline: `6dc4e4201585223f7f1da371256da11deee34d6c` (v0.5.14); production changes: `5b8cd640` and `ff66e6ba`. Tested on macOS arm64 with Node 24.20.0, pnpm 10.13.1, published DSH 0.1.7-rc.2 and official Mnemon CLI 0.2.9. Profiles install all seventeen Mnemon tarballs with Scoped, Light Context and Auto Capture enabled. + +Runtime model projections now place recorded importance and elapsed whole-day ages above each entry, for example `[importance=critical; created=14d; updated=2d]`. Future timestamps render as `future`, invalid timestamps as `unknown`; one clock value serves the whole projection. Exact content, stored JSON/Markdown, capacity accounting and mutation matching are unchanged. Replacement/removal still uses content alone, and current instructions retain priority. + +Annotations count toward the existing projection budget. The default Strategy now describes Runtime as budget limited, avoiding a completeness claim when a dense store is truncated. No migration, new setting or on-demand expansion route was introduced. + +## Evidence and verification + +The real WebUI baseline received all three synthetic stored entries but **zero metadata annotations** in the actual model wire. The fixed request contains all three: critical USER at **14d/2d**, normal MEMORY at **7d/3d**, and low MEMORY at **4d/1d** (created/updated). `METADATA280_SHOW` extracts that request's Runtime snapshot; its visible response does not assume an expected answer. + +| Baseline | Fixed | +| --- | --- | +| ![Projection without metadata](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-280-runtime-metadata/before-model-projection.png) | ![Actual projected importance and ages](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-280-runtime-metadata/after-model-projection.png) | + +Real WebUI turns then added, replaced and removed a USER entry, each with a committed tool receipt; the final Runtime store contains the original three seeds. Documents creation and a `DOC280_CHECK` body search returned the one matching document. A Native space was created and activated through the UI, official CLI 0.2.9 wrote `NATIVE280_CHECK: The disposable project uses SQLite WAL for durable storage.`, and the UI keyword search returned that exact text and record ID as its only result. + +![Native CLI write read back through WebUI](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-280-runtime-metadata/native-cli-webui-recall.png) + +- Six new metadata regressions failed before the Source fix; an additional dense-store regression failed before the Strategy guidance correction. Fixed targeted checks passed **50 tests**, with **63 Runtime Source** and **17 default Strategy** tests passing. +- `pnpm verify` passed **1,363 Root tests / 7 skips**, **406 plugin tests / 2 skips**, and the type, deterministic-build, Headless and package checks. Root unpacked size: **1,374,802 bytes**, below the unchanged 1,376,000-byte limit. +- `verify:plugins --skip-build` passed **16 independent plugin repositories and 17 artifacts**. Optional Native integration tests used verified CLI 0.2.9; an independent isolated-store smoke also wrote, recalled and soft-deleted a synthetic fact. + +Baseline tarball checksums exactly match the saved `6dc4e4` artifacts; fixed artifacts were packed afresh. Generated Client comments contain build locations, so unrelated tarball hash differences are not treated as production-code changes. + +Machine-readable evidence: [verification and artifact hashes](./verification.json), [actual Runtime projections and mutation receipts](./wire-evidence.json), [CLI/WebUI workflows](./cli-webui-evidence.json), and [isolated CLI smoke](./cli-smoke.json). + +## Reproduce + +Copy [rc2-profile.json](./rc2-profile.json) to `package.json` in an empty temporary profile and run `npm install --ignore-scripts --registry https://registry.npmjs.org` there with Node 24. In each baseline/fixed checkout, build Root and plugins, then pack all seventeen packages into a separate empty directory: + +```sh +pnpm build +pnpm --workspace-concurrency=1 -r build +node --input-type=module - /absolute/empty-artifacts <<'JS' +import { readReleasePackages, createReleasePlan, packRelease } from './scripts/release.mjs' +await packRelease(createReleasePlan(await readReleasePackages(), { baseVersions: new Map() }), process.argv[2]) +JS +MNEMON_CLI_PATH=/absolute/verified/mnemon node scripts/fixtures/serve-runtime-metadata.mjs \ + --profile /absolute/rc2-profile --artifacts /absolute/packed-artifacts \ + --state /absolute/empty-state +``` + +Run the final fixture command from this PR checkout for both comparisons; only change `--artifacts` to the baseline or fixed tarball directory and choose a fresh `--state`. The baseline checkout does not contain these new fixture scripts. + +The [scenario](../../../scripts/fixtures/serve-runtime-metadata.mjs) uses the shared [packed-profile helper](../../../scripts/fixtures/packed-web-fixture.mjs). Before startup it seeds three short USER/MEMORY entries with critical/normal/low importance and distinct created/updated ages, each six hours beyond a whole-day boundary. Real Runtime initialization preserves those values. + +Open the private URL in the state's `private.json`, select its `workspace` directory and the official Coding preset. Send `METADATA280_SHOW`, then `METADATA280_ADD`, `METADATA280_REPLACE`, `METADATA280_SHOW`, `METADATA280_REMOVE`, and `METADATA280_SHOW` as separate turns. These mutations use the real `mnemon_runtime_memory` tool, omit USER branches and summarize actual receipts. `observations.json` records the synthetic Runtime wire, advertised tool schema and receipts; it does not dump full system prompts. + +If this browser environment returns `ERR_BLOCKED_BY_CLIENT` for the printed `127.0.0.1` URL, changing only its hostname to `localhost` reaches the same loopback host through the normal token-to-cookie redirect. This was the working route here; authentication and browser protections were unchanged. Keep authentication URLs and raw logs private. `SIGTERM` stops the fixture; optional `--port` selects a stable port. + +Only temporary synthetic data was used. The published adapter, tools, providers, transport, persistence and browser were real; the deterministic model observed actual wire content. This does not evaluate live Flash quality or Windows behavior. Host restart coverage is from the automated Headless checks; no manual cold-Host browser restart is claimed. Optional Flash, Teams, lifecycle, OpenViking-service and Windows checks retain their reported skips. diff --git a/dsh/docs/pr-assets/issue-280-runtime-metadata/README.zh-CN.md b/dsh/docs/pr-assets/issue-280-runtime-metadata/README.zh-CN.md new file mode 100644 index 000000000..d290a93f4 --- /dev/null +++ b/dsh/docs/pr-assets/issue-280-runtime-metadata/README.zh-CN.md @@ -0,0 +1,55 @@ +# Runtime 投影元数据 — Issue 280 + +[English](./README.md) + +基线为 `6dc4e4201585223f7f1da371256da11deee34d6c`(v0.5.14),生产修改为 `5b8cd640` 和 `ff66e6ba`。环境为 macOS arm64、Node 24.20.0、pnpm 10.13.1、正式发布的 DSH 0.1.7-rc.2 和官方 Mnemon CLI 0.2.9。Profile 安装完整的十七个 Mnemon tarball,同时开启 Scoped、Light Context 和 Auto Capture。 + +Runtime 模型投影现在会在每条正文上方显示已记录的重要性及经过的完整天数,例如 `[importance=critical; created=14d; updated=2d]`。未来时间显示为 `future`,无效时间显示为 `unknown`;整份投影只采样一次时钟。正文、持久化 JSON/Markdown、容量计量和修改匹配规则不变。替换与删除仍只使用正文,当前指令继续优先。 + +元数据标注计入现有投影预算。默认 Strategy 现在明确 Runtime 受预算限制,避免密集存储被截断时仍声称内容完整。没有引入迁移、新设置或按需展开路由。 + +## 证据与验证 + +真实 WebUI 基线的模型请求收到三个合成存储条目,但实际出站内容包含 **零条元数据标注**。修复后请求包含三条标注:critical USER 为 **14d/2d**,normal MEMORY 为 **7d/3d**,low MEMORY 为 **4d/1d**(依次为创建/更新年龄)。`METADATA280_SHOW` 从该次请求中提取 Runtime 快照,页面回复不预设期望答案。 + +| 基线 | 修复后 | +| --- | --- | +| ![投影缺少元数据](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-280-runtime-metadata/before-model-projection.png) | ![实际投影中的重要性和年龄](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-280-runtime-metadata/after-model-projection.png) | + +随后通过真实 WebUI 对话新增、替换、删除一条 USER 记录,每步均收到已提交的工具回执;最终 Runtime 恢复为原来的三个种子条目。界面创建档案后,按正文中的 `DOC280_CHECK` 搜索返回唯一匹配文档。界面创建并激活 Native 空间,再由官方 CLI 0.2.9 写入 `NATIVE280_CHECK: The disposable project uses SQLite WAL for durable storage.`,WebUI 关键词检索返回唯一结果,正文与记录 ID 均完全一致。 + +![CLI 写入后由 WebUI 读回](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-280-runtime-metadata/native-cli-webui-recall.png) + +- 六个新元数据回归在 Source 修复前失败;另一个密集存储回归在 Strategy 提示修正前失败。修复后定向检查 **50 项通过**,Runtime Source **63 项**、默认 Strategy **17 项**测试通过。 +- `pnpm verify`:Root **1,363 项通过 / 7 项跳过**,插件 **406 项通过 / 2 项跳过**,以及类型、确定性构建、Headless 和包检查全部通过。Root 解包体积为 **1,374,802 字节**,低于未调整的 1,376,000 字节上限。 +- `verify:plugins --skip-build` 的 **16 个独立插件仓库和 17 个制品全部通过**。可选 Native 集成测试使用已校验的 CLI 0.2.9;另一个隔离存储冒烟也完成了合成事实的写入、召回和软删除。 + +复用的基线 tarball 校验和与已保存的 `6dc4e4` 制品完全一致,修复版重新打包。生成的 Client 注释包含构建位置,因此不将无关 tarball 的哈希差异等同于生产代码修改。 + +结构化证据:[验证汇总和制品哈希](./verification.json)、[实际 Runtime 投影与修改回执](./wire-evidence.json)、[CLI/WebUI 流程](./cli-webui-evidence.json)、[隔离 CLI 冒烟检查](./cli-smoke.json)。 + +## 复现 + +将 [rc2-profile.json](./rc2-profile.json) 复制为新临时 Profile 中的 `package.json`,用 Node 24 在该目录执行 `npm install --ignore-scripts --registry https://registry.npmjs.org`。在基线与修复版 checkout 中分别构建 Root 和插件,再将十七个包打入各自的空目录: + +```sh +pnpm build +pnpm --workspace-concurrency=1 -r build +node --input-type=module - /absolute/empty-artifacts <<'JS' +import { readReleasePackages, createReleasePlan, packRelease } from './scripts/release.mjs' +await packRelease(createReleasePlan(await readReleasePackages(), { baseVersions: new Map() }), process.argv[2]) +JS +MNEMON_CLI_PATH=/absolute/verified/mnemon node scripts/fixtures/serve-runtime-metadata.mjs \ + --profile /absolute/rc2-profile --artifacts /absolute/packed-artifacts \ + --state /absolute/empty-state +``` + +两次对比的最后一条 fixture 命令都从本 PR checkout 执行,仅将 `--artifacts` 切换为基线或修复版 tarball 目录,并使用新的 `--state`。基线 checkout 本身不包含这些新增复现脚本。 + +[场景脚本](../../../scripts/fixtures/serve-runtime-metadata.mjs) 使用共享的[打包 Profile 辅助脚本](../../../scripts/fixtures/packed-web-fixture.mjs)。启动前写入三个简短 USER/MEMORY 合成条目,分别使用 critical/normal/low 重要性及不同创建、更新年龄,每个年龄均在完整天数后额外留出六小时。真实 Runtime 初始化会保留这些值。 + +打开 state 中 `private.json` 记录的私有 URL,选择其 `workspace` 目录和官方 Coding 预设。每轮分别发送 `METADATA280_SHOW`、`METADATA280_ADD`、`METADATA280_REPLACE`、`METADATA280_SHOW`、`METADATA280_REMOVE`、`METADATA280_SHOW`。这些修改使用真实的 `mnemon_runtime_memory` 工具,省略 USER 的 branches,并汇总实际回执。`observations.json` 记录合成 Runtime 出站内容、实际工具 schema 和回执,不导出完整系统提示。 + +若本浏览器环境对打印出的 `127.0.0.1` URL 返回 `ERR_BLOCKED_BY_CLIENT`,只将 hostname 换成 `localhost` 即可通过正常的 token 换 cookie 重定向访问同一回环 Host;本轮即采用此方式,认证机制和浏览器保护没有改变。认证 URL 和原始日志保持私有。`SIGTERM` 停止夹具;可选 `--port` 可指定稳定端口。 + +全程只使用临时合成数据。正式适配器、工具、Provider、传输、持久化和浏览器均为真实实现;确定性模型读取实际请求内容。本记录不评价真实 Flash 质量或 Windows 行为。Host 重启覆盖来自自动化 Headless 检查,不声称手动完成了冷启动后的浏览器验收。Flash、Teams、lifecycle、OpenViking 服务及 Windows 可选检查保留其明确跳过记录。 diff --git a/dsh/docs/pr-assets/issue-280-runtime-metadata/cli-smoke.json b/dsh/docs/pr-assets/issue-280-runtime-metadata/cli-smoke.json new file mode 100644 index 000000000..935179330 --- /dev/null +++ b/dsh/docs/pr-assets/issue-280-runtime-metadata/cli-smoke.json @@ -0,0 +1,30 @@ +{ + "cliVersion": "0.2.9", + "binarySha256": "bd1b080b0f98e8703b7cd942bd9b7ea3e15f69081e26c302e129d306ac86aaf4", + "scope": "isolated acceptance store", + "beforeSoftDelete": { + "results": [ + { + "id": "452446fd-15fd-45ee-a6f8-47ed5e14ebd4", + "content": "CLI280273_CHECK: the disposable acceptance project stores durable facts in SQLite.", + "category": "fact", + "importance": 4, + "intent": "GENERAL", + "matched_via": "hybrid", + "confidence": "medium", + "score": 0.45 + } + ], + "hint": "sparse_results" + }, + "softDelete": { + "id": "452446fd-15fd-45ee-a6f8-47ed5e14ebd4", + "message": "Insight soft-deleted successfully", + "status": "deleted" + }, + "afterSoftDelete": { + "results": [], + "hint": "sparse_results" + }, + "passed": true +} diff --git a/dsh/docs/pr-assets/issue-280-runtime-metadata/cli-webui-evidence.json b/dsh/docs/pr-assets/issue-280-runtime-metadata/cli-webui-evidence.json new file mode 100644 index 000000000..d2374dd21 --- /dev/null +++ b/dsh/docs/pr-assets/issue-280-runtime-metadata/cli-webui-evidence.json @@ -0,0 +1,47 @@ +{ + "cli": { + "version": "0.2.9", + "store": "default", + "query": "NATIVE280_CHECK", + "recall": { + "results": [ + { + "id": "715864d5-641a-4724-a8e1-bf8ba30039f9", + "content": "NATIVE280_CHECK: The disposable project uses SQLite WAL for durable storage.", + "category": "fact", + "importance": 4, + "intent": "GENERAL", + "matched_via": "hybrid", + "confidence": "medium", + "score": 0.45 + } + ], + "hint": "sparse_results" + } + }, + "browser": { + "keywordModeVisible": true, + "providerVisible": true, + "recordIdPrefixVisible": true, + "exactContentVisible": true, + "oneResultVisible": true, + "searchMode": "keyword", + "screenshot": "native-cli-webui-recall.png" + }, + "documents": { + "createdInWebUI": true, + "id": "7a9ce5f8-cce4-4a3d-8128-2cac38ded72d", + "title": "Metadata acceptance notes", + "relativePath": "documents/active/metadata-acceptance-notes-7a9ce5f8.md", + "revision": 1, + "searchQuery": "DOC280_CHECK", + "visibleMatches": 1 + }, + "runtime": { + "added": true, + "replaced": true, + "removed": true, + "originalSeedCount": 3, + "finalEntryCount": 3 + } +} diff --git a/dsh/docs/pr-assets/issue-280-runtime-metadata/rc2-profile.json b/dsh/docs/pr-assets/issue-280-runtime-metadata/rc2-profile.json new file mode 100644 index 000000000..8cc92e743 --- /dev/null +++ b/dsh/docs/pr-assets/issue-280-runtime-metadata/rc2-profile.json @@ -0,0 +1,6 @@ +{ + "private": true, + "dependencies": { + "@deepseek-ai/dsh": "0.1.7-rc.2" + } +} diff --git a/dsh/docs/pr-assets/issue-280-runtime-metadata/verification.json b/dsh/docs/pr-assets/issue-280-runtime-metadata/verification.json new file mode 100644 index 000000000..5dab2a1a7 --- /dev/null +++ b/dsh/docs/pr-assets/issue-280-runtime-metadata/verification.json @@ -0,0 +1,299 @@ +{ + "issue": 280, + "baseline": "6dc4e4201585223f7f1da371256da11deee34d6c", + "testedProductionCommits": [ + "5b8cd640", + "ff66e6ba" + ], + "platform": "macOS arm64", + "node": "24.20.0", + "pnpm": "10.13.1", + "dsh": "0.1.7-rc.2", + "mnemonCli": "0.2.9", + "model": "deterministic loopback fixture using the published DeepSeek adapter", + "verify": { + "passed": true, + "root": { + "passed": 1363, + "skipped": 7 + }, + "plugins": { + "passed": 406, + "skipped": 2 + }, + "rootUnpackedBytes": 1374802, + "rootUnpackedLimit": 1376000, + "nativeCliIntegrationEnabled": true + }, + "independentPlugins": { + "passed": true, + "repositories": 16, + "artifacts": 17 + }, + "commands": [ + "MNEMON_NATIVE_TEST_CLI=/absolute/verified/mnemon pnpm verify", + "pnpm verify:plugins --skip-build", + "pnpm verify:docs", + "pnpm release:intent" + ], + "optionalStrategies": [ + "scoped", + "light-context", + "auto-capture" + ], + "limits": [ + "No live Flash quality evaluation", + "No manual Windows WebUI verification", + "Manual browser reload and automated Headless restart are different coverage" + ], + "profiles": { + "before": { + "issue": 280, + "dsh": "0.1.7-rc.2", + "node": "v24.20.0", + "idleReview": false, + "preset": "standard", + "toolPresentation": "native", + "optionalStrategies": [ + "scoped", + "light-context", + "auto-capture" + ], + "commands": [ + "METADATA280_SHOW", + "METADATA280_ADD", + "METADATA280_REPLACE", + "METADATA280_REMOVE" + ], + "seededEntries": 3, + "artifacts": [ + { + "name": "dsh-mnemon", + "version": "0.5.14", + "filename": "dsh-mnemon-0.5.14.tgz", + "sha256": "6275ed7a275afac2f23d8f3bc9ba6402a0dd3d8e33e87bbb99c9d08a74924356" + }, + { + "name": "dsh-mnemon-provider-byterover", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-byterover-0.5.4.tgz", + "sha256": "17dcd57cb35c0c81af2b93c92be66c2e6b7e8180d5d1cf634c589281003dddc1" + }, + { + "name": "dsh-mnemon-provider-hindsight", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-hindsight-0.5.4.tgz", + "sha256": "739dec0d42689fdbdb8183ba75f7b6afc4be899b585c03c2a85842e0e998a170" + }, + { + "name": "dsh-mnemon-provider-holographic", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-holographic-0.5.4.tgz", + "sha256": "700fa0f94387ee04f5202dbbe0d410afb7114ba2486b25c32d64d7b059245f8c" + }, + { + "name": "dsh-mnemon-provider-honcho", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-honcho-0.5.4.tgz", + "sha256": "db8ffcbbc1df42555295a1ff818e2059b5c09776a0dc4cc9c311779355b06fd7" + }, + { + "name": "dsh-mnemon-provider-mem0", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-mem0-0.5.4.tgz", + "sha256": "ccd61716ead4d9462e15a662edb59c9850d812e8660bc33c4cc574601b816255" + }, + { + "name": "dsh-mnemon-provider-mnemon-native", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-mnemon-native-0.5.5.tgz", + "sha256": "8b839950e17b086578490b9eb3ef438331afa96a85ccd588ddaf7af9649f1f32" + }, + { + "name": "dsh-mnemon-provider-openviking", + "version": "0.5.6", + "filename": "dsh-mnemon-provider-openviking-0.5.6.tgz", + "sha256": "e5b3f9c80c6c450e5cb0e0103f30f8df39550dd980aac70dc4d44e069e2bc570" + }, + { + "name": "dsh-mnemon-provider-retaindb", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-retaindb-0.5.4.tgz", + "sha256": "8962cfc5b8e777a86c8816dfd47f32943aa6a1f1973bc8882727b4b430579e3a" + }, + { + "name": "dsh-mnemon-provider-supermemory", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-supermemory-0.5.4.tgz", + "sha256": "c117f467d0eb66954ab5b27a8ae08722770699e0f9113fa4bdc9f0724485b508" + }, + { + "name": "dsh-mnemon-source-documents", + "version": "0.5.6", + "filename": "dsh-mnemon-source-documents-0.5.6.tgz", + "sha256": "7a94dee1d0e47b2d76a71d86b27d4165875f5bb3a8066b9ef68c2e5b2cb1825a" + }, + { + "name": "dsh-mnemon-source-memory-spaces", + "version": "0.5.9", + "filename": "dsh-mnemon-source-memory-spaces-0.5.9.tgz", + "sha256": "a2c913d866be46736ed8aca1dd61371319a0b40593a6258f6d6eec581245bc85" + }, + { + "name": "dsh-mnemon-source-runtime", + "version": "0.5.8", + "filename": "dsh-mnemon-source-runtime-0.5.8.tgz", + "sha256": "a0ddb4d9283277614253e4000ad8e6234c6ab1fcfb733aef1894bf080b1193d7" + }, + { + "name": "dsh-mnemon-strategy-auto-capture", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-auto-capture-0.5.4.tgz", + "sha256": "a653c1ebec85da0fe2c878e42a0eea84e20da877682fa1c9c97f5d8f8f70c074" + }, + { + "name": "dsh-mnemon-strategy-default-three-tier", + "version": "0.5.5", + "filename": "dsh-mnemon-strategy-default-three-tier-0.5.5.tgz", + "sha256": "fe4e722303ffeff5a598011d9ee6b8f5de73fe21aa2bf25ae215c750817b60df" + }, + { + "name": "dsh-mnemon-strategy-light-context", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-light-context-0.5.4.tgz", + "sha256": "eac6b61e9652ed54d03dd013bec285afe8714547a27c33ace286439e5958250b" + }, + { + "name": "dsh-mnemon-strategy-scoped", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-scoped-0.5.4.tgz", + "sha256": "c751c30b61f5ae60eca489cda936ed17a5b86dadaae538d9657dd76fe8467843" + } + ] + }, + "after": { + "issue": 280, + "dsh": "0.1.7-rc.2", + "node": "v24.20.0", + "idleReview": false, + "preset": "standard", + "toolPresentation": "native", + "optionalStrategies": [ + "scoped", + "light-context", + "auto-capture" + ], + "commands": [ + "METADATA280_SHOW", + "METADATA280_ADD", + "METADATA280_REPLACE", + "METADATA280_REMOVE" + ], + "seededEntries": 3, + "artifacts": [ + { + "name": "dsh-mnemon", + "version": "0.5.14", + "filename": "dsh-mnemon-0.5.14.tgz", + "sha256": "dacaf9f91a7296575de60f6d7d1ae660250ee376b925321fd4a23aab11098a80" + }, + { + "name": "dsh-mnemon-provider-byterover", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-byterover-0.5.4.tgz", + "sha256": "94677bb5a27261dd2051f67c9ae5bc41c17ea86a4ea95cbd0baee1728203633f" + }, + { + "name": "dsh-mnemon-provider-hindsight", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-hindsight-0.5.4.tgz", + "sha256": "bf527bc83d51bd7b2e510ed3be59665ae6b3ddbd4e1c3beee16c32235fa0afbb" + }, + { + "name": "dsh-mnemon-provider-holographic", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-holographic-0.5.4.tgz", + "sha256": "67f9cc593a7bcf33001e0817a4932e23cf9f0927e90b6bc2b2ccd5ef49b91259" + }, + { + "name": "dsh-mnemon-provider-honcho", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-honcho-0.5.4.tgz", + "sha256": "c102952aa1b9fdd953b25068663635a9ffad606bb39c68b56eafa538c8fab72d" + }, + { + "name": "dsh-mnemon-provider-mem0", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-mem0-0.5.4.tgz", + "sha256": "42cd225bb376d387d8e7f5503d64b6a00ddae4f5aea26b495cb9d5aa44dab3b0" + }, + { + "name": "dsh-mnemon-provider-mnemon-native", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-mnemon-native-0.5.5.tgz", + "sha256": "cb1532ec04e1bc2550456340b453717686c2bc399e742e61be529bdae5e6f812" + }, + { + "name": "dsh-mnemon-provider-openviking", + "version": "0.5.6", + "filename": "dsh-mnemon-provider-openviking-0.5.6.tgz", + "sha256": "2ba3e8e5248222733b7463c311443832a0872e0904c2b09304590234cf6c27ca" + }, + { + "name": "dsh-mnemon-provider-retaindb", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-retaindb-0.5.4.tgz", + "sha256": "a7b5f1efb4e68df2d7ccc7e6078cd8c3229c82cf14f0162a196c975cbe19727f" + }, + { + "name": "dsh-mnemon-provider-supermemory", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-supermemory-0.5.4.tgz", + "sha256": "d04b900f7f9e9359ba53b349bdbc4550fc19b350771e61df3aa451e8f67f9afd" + }, + { + "name": "dsh-mnemon-source-documents", + "version": "0.5.6", + "filename": "dsh-mnemon-source-documents-0.5.6.tgz", + "sha256": "5633eb876955ef99fcdfca7001df59c7d8b34edf4ef37eaeea730d467b3be386" + }, + { + "name": "dsh-mnemon-source-memory-spaces", + "version": "0.5.9", + "filename": "dsh-mnemon-source-memory-spaces-0.5.9.tgz", + "sha256": "6af6c1819eeab26bf4ce2997f7205f2c6d9a6d2885f6be8a8b67c407ddc3d144" + }, + { + "name": "dsh-mnemon-source-runtime", + "version": "0.5.8", + "filename": "dsh-mnemon-source-runtime-0.5.8.tgz", + "sha256": "5688195fcbfcaf196879031f31baa91c1015d1661cd0419570aee061ad567a64" + }, + { + "name": "dsh-mnemon-strategy-auto-capture", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-auto-capture-0.5.4.tgz", + "sha256": "3816601abd83f0c5c679f3e4c9ae96c8f697e8e15f6546e975a96fd7f86b8c02" + }, + { + "name": "dsh-mnemon-strategy-default-three-tier", + "version": "0.5.5", + "filename": "dsh-mnemon-strategy-default-three-tier-0.5.5.tgz", + "sha256": "ee54bd3cf96f197a0d8b584a162afee9cfad2c2f205f4b8f17710c27ad70eefa" + }, + { + "name": "dsh-mnemon-strategy-light-context", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-light-context-0.5.4.tgz", + "sha256": "62947c3ff777fbb8d42724acf2a9c651d2b2302b387bb8579c59cd79f82a9e0d" + }, + { + "name": "dsh-mnemon-strategy-scoped", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-scoped-0.5.4.tgz", + "sha256": "f3f945add93d2d5f8f48f0e70f5de9360814499a552218af7fa93f574dd3457a" + } + ] + } + } +} diff --git a/dsh/docs/pr-assets/issue-280-runtime-metadata/wire-evidence.json b/dsh/docs/pr-assets/issue-280-runtime-metadata/wire-evidence.json new file mode 100644 index 000000000..f1f676bf7 --- /dev/null +++ b/dsh/docs/pr-assets/issue-280-runtime-metadata/wire-evidence.json @@ -0,0 +1,167 @@ +{ + "before": { + "snapshotFound": true, + "annotations": [], + "projection": "MNEMON RUNTIME MEMORY SNAPSHOT\nRevision: 62a0c7fb2db0134568d5480cdcde5e04551fe84e712734e627150689cc6e5460\nGit branch: main\n\nContents of USER.md (user profile; entries: 1; UTF-8 bytes: 39/4096)\n\nMETA280_USER: I prefer concise replies.\n\n\nContents of MEMORY.md (working reference; entries: 2; UTF-8 bytes: 84/10240)\n\nMETA280_PROJECT: The project uses SQLite.\n\u00a7\nMETA280_LOW: The sample accent is blue.\n", + "reply": "The actual Runtime projection contains no importance/age annotations.\n\nUSER.md:\nMETA280_USER: I prefer concise replies.\n\nMEMORY.md:\nMETA280_PROJECT: The project uses SQLite.\n\u00a7\nMETA280_LOW: The sample accent is blue." + }, + "after": { + "snapshotFound": true, + "annotations": [ + { + "importance": "critical", + "created": "14d", + "updated": "2d" + }, + { + "importance": "normal", + "created": "7d", + "updated": "3d" + }, + { + "importance": "low", + "created": "4d", + "updated": "1d" + } + ], + "projection": "MNEMON RUNTIME MEMORY SNAPSHOT\nRevision: aeaafba6adc2ca6202c296261a628c430cabefc08a94cc9290f9b556a35cc624\nGit branch: main\nMetadata lines are annotations; created/updated are ages at projection in whole days (future/unknown for future/invalid timestamps). Current instructions win. For old_text/oldText, use entry content only.\n\nContents of USER.md (user profile; entries: 1; UTF-8 bytes: 39/4096)\n\n[importance=critical; created=14d; updated=2d]\nMETA280_USER: I prefer concise replies.\n\n\nContents of MEMORY.md (working reference; entries: 2; UTF-8 bytes: 84/10240)\n\n[importance=normal; created=7d; updated=3d]\nMETA280_PROJECT: The project uses SQLite.\n\u00a7\n[importance=low; created=4d; updated=1d]\nMETA280_LOW: The sample accent is blue.\n", + "reply": "The actual Runtime projection contains 3 importance/age annotations.\n\nUSER.md:\n[importance=critical; created=14d; updated=2d]\nMETA280_USER: I prefer concise replies.\n\nMEMORY.md:\n[importance=normal; created=7d; updated=3d]\nMETA280_PROJECT: The project uses SQLite.\n\u00a7\n[importance=low; created=4d; updated=1d]\nMETA280_LOW: The sample accent is blue." + }, + "mutations": [ + { + "command": "METADATA280_ADD", + "call": { + "id": "issue280-5", + "name": "mnemon_runtime_memory", + "args": { + "action": "add", + "target": "user", + "content": "META280_LIVE: I prefer metric units.", + "importance": "low" + } + }, + "receipt": { + "callId": "issue280-5", + "isError": false, + "result": { + "success": true, + "message": "Entry added.", + "target": "user", + "entryCount": 2, + "usage": { + "used": 79, + "limit": 4096 + }, + "added": "META280_LIVE: I prefer metric units.", + "revision": "ffdec3e9bbe8f8468bb89a473ef61b1fbe5f3c9041e66b7bf95538baaa7ef16c", + "memoryReceipt": { + "status": "succeeded", + "completion": "committed", + "committedAt": "2026-09-26T02:50:08.125Z" + } + } + } + }, + { + "command": "METADATA280_REPLACE", + "call": { + "id": "issue280-7", + "name": "mnemon_runtime_memory", + "args": { + "action": "replace", + "target": "user", + "old_text": "META280_LIVE: I prefer metric units.", + "content": "META280_UPDATED: I prefer metric units and Celsius.", + "importance": "critical" + } + }, + "receipt": { + "callId": "issue280-7", + "isError": false, + "result": { + "success": true, + "message": "Entry replaced.", + "target": "user", + "entryCount": 2, + "usage": { + "used": 94, + "limit": 4096 + }, + "replaced": { + "from": "META280_LIVE: I prefer metric units.", + "to": "META280_UPDATED: I prefer metric units and Celsius." + }, + "revision": "3a8baa58a7acf8fdb224a22e105adb0e474ab2d2ef7001f365bd05a20de58cf4", + "memoryReceipt": { + "status": "succeeded", + "completion": "committed", + "committedAt": "2026-09-26T02:50:08.334Z" + } + } + } + }, + { + "command": "METADATA280_REMOVE", + "call": { + "id": "issue280-9", + "name": "mnemon_runtime_memory", + "args": { + "action": "remove", + "target": "user", + "old_text": "META280_UPDATED: I prefer metric units and Celsius." + } + }, + "receipt": { + "callId": "issue280-9", + "isError": false, + "result": { + "success": true, + "message": "Entry removed.", + "target": "user", + "entryCount": 1, + "usage": { + "used": 39, + "limit": 4096 + }, + "removed": "META280_UPDATED: I prefer metric units and Celsius.", + "revision": "aeaafba6adc2ca6202c296261a628c430cabefc08a94cc9290f9b556a35cc624", + "memoryReceipt": { + "status": "succeeded", + "completion": "committed", + "committedAt": "2026-09-26T02:50:08.446Z" + } + } + } + } + ], + "storage": { + "originalEntriesRestored": true, + "entries": [ + { + "content": "META280_USER: I prefer concise replies.", + "created_at": "2026-09-11T20:45:51.825Z", + "updated_at": "2026-09-23T20:45:51.825Z", + "target": "user", + "importance": "critical" + }, + { + "content": "META280_PROJECT: The project uses SQLite.", + "created_at": "2026-09-18T20:45:51.825Z", + "updated_at": "2026-09-22T20:45:51.825Z", + "target": "memory", + "importance": "normal" + }, + { + "content": "META280_LOW: The sample accent is blue.", + "created_at": "2026-09-21T20:45:51.825Z", + "updated_at": "2026-09-24T20:45:51.825Z", + "target": "memory", + "importance": "low" + } + ], + "plainMarkdown": { + "USER.md": "META280_USER: I prefer concise replies.\n", + "MEMORY.md": "META280_PROJECT: The project uses SQLite.\n\u00a7\nMETA280_LOW: The sample accent is blue.\n" + } + } +} diff --git a/dsh/docs/pr-assets/issue-281-user-branches/README.md b/dsh/docs/pr-assets/issue-281-user-branches/README.md new file mode 100644 index 000000000..4c2ed392d --- /dev/null +++ b/dsh/docs/pr-assets/issue-281-user-branches/README.md @@ -0,0 +1,51 @@ +# Runtime USER empty branches — issue 281 + +[简体中文](./README.zh-CN.md) + +Baseline: `6dc4e4201585223f7f1da371256da11deee34d6c` (v0.5.14). Tested on macOS arm64 with Node 24.20.0, pnpm 10.13.1, official Mnemon CLI 0.2.9 and published DSH 0.1.7-rc.2. Both WebUI profiles install all seventeen packed artifacts without workspace links; all three optional Strategy extensions are enabled. Only the Root artifact differs between baseline and fix. + +## Reproduction and correction + +Through the real WebUI, `USER281_PREPARE` first calls `mnemon_runtime_memory` to add a synthetic USER entry with `branches` omitted. `USER281_REPLACE` then replaces that entry with `branches: []`. The baseline returns `Error: branches applies to target=memory only` and retains the original entry. The fixed artifact commits the replacement, with exactly one USER entry and no duplicate. + +The named tool now drops only a literal empty array for `target=user`. Non-empty or malformed USER branch values still fail. Runtime Source, generic View actions and RPC retain their strict contracts; MEMORY replacement still distinguishes omitted scope from an explicit empty scope. No storage migration or Source package release is needed. + +The actual advertised schema requires only `action` and `target`. Published rc.2 schema serialization also preserves the parameters. This evidence establishes the failure for caller-supplied `[]`; it does not establish that DSH forces callers to include the field. + +| Baseline | Fixed | +| --- | --- | +| ![Baseline USER replacement error](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-281-user-branches/before-user-replace.png) | ![Fixed USER replacement receipt](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-281-user-branches/after-user-replace.png) | + +## Verification + +- The new regression file fails on the baseline: **4 failed, 9 passed**. After the fix all **13 pass**, covering root/child agents, default composition and three enhancements with renamed Source entries, USER add/replace/remove and unchanged MEMORY contents, malformed inputs, MEMORY scope preservation/change/clearing, and strict Source/generic action boundaries. +- Focused Runtime, RPC and Strategy checks: **45 passed**. +- `pnpm verify`: **1,373 Root tests passed / 7 skipped**, **402 plugin tests passed / 2 skipped**; types, deterministic builds, real Headless activation/restart, settings persistence, exports and package checks passed. Root unpacked size: **1,374,961 bytes** against the existing 1,376,000-byte limit. +- `MNEMON_PLUGIN_VERIFY_CONCURRENCY=1 pnpm verify:plugins --skip-build`: **16 independent plugin repositories / 17 artifacts passed**, including external SDK/Client consumers and simultaneous activation of all three optional strategies. +- Real CLI tests ran with `MNEMON_NATIVE_TEST_CLI`: Native Source/View write, recall and soft deletion, plus same-View creation and archival to two Native destinations. +- WebUI: USER empty-array add/replace/remove succeeded; non-empty USER branches failed; MEMORY branch assignment/clearing succeeded. Manual USER editing, Documents creation and content search passed. A Native space was created and activated in WebUI; the verified CLI wrote a synthetic record and WebUI keyword search read its exact text. +- Settings saved with a visible success receipt. Runtime and Documents files retained identical SHA-256 hashes through a Host cold restart; the Native CLI still recalled the same ID and content. + +![Native CLI write read back through WebUI](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-281-user-branches/native-cli-webui-recall.png) + +[verification.json](./verification.json) contains the minimal actual calls/receipts and content hashes. [Baseline artifacts](./before-artifacts.json) and [fixed artifacts](./after-artifacts.json) record the seventeen tarball hashes and synthetic inputs. Raw Host logs, local authentication URLs, npm installs and full session logs stay outside the repository. + +## Repeat the WebUI check + +Copy [rc2-profile.json](./rc2-profile.json) to `package.json` in an empty temporary directory and run `npm install --ignore-scripts` using Node 24. Build Root before the independent plugins, then pack all seventeen artifacts with `scripts/release.mjs`'s `readReleasePackages`, `createReleasePlan` and `packRelease` helpers into an empty directory. These local acceptance packs are not published. + +```sh +MNEMON_CLI_PATH=/absolute/verified/mnemon node scripts/fixtures/serve-runtime-user-branches.mjs \ + --profile /absolute/rc2-profile --artifacts /absolute/packed-artifacts \ + --state /absolute/empty-disposable-state +``` + +Open the private URL in `private.json`, select the fixture's `workspace` directory and the official standard preset, then send one command per turn: `USER281_PREPARE`, `USER281_REPLACE`, `USER281_ADD`, `USER281_REMOVE`, `USER281_REJECT`, `MEMORY281_SCOPE`, `MEMORY281_CLEAR`. The loopback model scripts choices and summarizes actual tool receipts; it never invents successful storage. The published adapter, tool execution, persistence, transport and browser are real. `SIGUSR2` restarts the Host while retaining state; inspect the new URL before reconnecting. Optional `--port` keeps a chosen port across restarts (default `0` chooses a free port). `SIGTERM` stops the fixture and its Host. + +## Limits and remaining triage + +This is not a Windows execution or a real-model quality evaluation. No external API credentials were needed. Optional live Flash, Teams, OpenViking service and standalone lifecycle opt-in tests were not enabled in the default full suite; their skips remain explicit. The optional Source-link WebUI harness was not used: these checks use installed tarballs and official DSH packages. + +Fresh WebUI reconnection after the cold restart was blocked by the browser environment (`ERR_BLOCKED_BY_CLIENT`), including a repeat on the original port. The official bootstrap URL still returned the expected authenticated redirect and cookie-backed HTML through HTTP, without changing authentication. Existing UI state was disconnected and is not counted as cold-reopen evidence. Cold-restart claims are limited to file hashes, saved configuration and CLI readback; the screenshots and interactive checks above are from the connected pre-restart Host. + +Open bug #251 has no new comments or failing original session. Its existing fix #253 is unchanged in current main. Six fresh preview-only audits preserve original bytes and match the previous migration evidence. No new #251 PR or closure is justified without a sanitized failing full event chain, exact source-build revisions and the current repair/migration failure. Historical UI/migration evidence was reviewed, not presented as a fresh execution. diff --git a/dsh/docs/pr-assets/issue-281-user-branches/README.zh-CN.md b/dsh/docs/pr-assets/issue-281-user-branches/README.zh-CN.md new file mode 100644 index 000000000..8ba62bc07 --- /dev/null +++ b/dsh/docs/pr-assets/issue-281-user-branches/README.zh-CN.md @@ -0,0 +1,51 @@ +# Runtime USER 空分支 — Issue 281 + +[English](./README.md) + +基线为 `6dc4e4201585223f7f1da371256da11deee34d6c`(v0.5.14)。环境为 macOS arm64、Node 24.20.0、pnpm 10.13.1、官方 Mnemon CLI 0.2.9,以及正式发布的 DSH 0.1.7-rc.2。两套 WebUI 均安装完整的十七个打包制品,没有 workspace 链接;三个可选 Strategy 增强同时开启。前后只有 Root 制品发生变化。 + +## 复现与修复 + +在真实 WebUI 中,先发送 `USER281_PREPARE`,通过 `mnemon_runtime_memory` 省略 `branches` 新增合成 USER 条目;再发送 `USER281_REPLACE`,携带 `branches: []` 替换同一条目。基线返回 `Error: branches applies to target=memory only`,原条目保持不变。修复版提交替换后仍恰好只有一条 USER 记录,没有重复。 + +具名工具只对 `target=user` 的字面空数组做省略处理。USER 的非空和畸形分支值仍被拒绝;Runtime Source、通用 View action 和 RPC 保持严格契约;MEMORY 替换仍区分省略范围与显式空范围。不需要迁移存储或发布 Source 包。 + +实际出站 schema 仅要求 `action` 和 `target`,正式 rc.2 的序列化也保留原始参数契约。证据证明“调用者传入 `[]` 时失败”,并未证明 DSH 强制要求该字段。 + +| 修复前 | 修复后 | +| --- | --- | +| ![基线 USER 替换错误](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-281-user-branches/before-user-replace.png) | ![修复后 USER 替换回执](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-281-user-branches/after-user-replace.png) | + +## 验证 + +- 新回归文件在基线为 **4 项失败、9 项通过**,修复后 **13 项全部通过**。覆盖父/子 Agent、默认组合、三个增强加重命名 Source entry、USER 增改删及 MEMORY 不变、畸形参数、MEMORY 分支保留/更换/清空,以及 Source/通用 action 的严格边界。 +- Runtime、RPC 和 Strategy 定向检查:**45 项通过**。 +- `pnpm verify`:Root **1,373 项通过 / 7 项跳过**,插件 **402 项通过 / 2 项跳过**;类型、确定性构建、真实 Headless 激活与重启、设置持久化、出口和包检查均通过。Root 解包体积 **1,374,961 字节**,低于现有 1,376,000 字节上限。 +- `MNEMON_PLUGIN_VERIFY_CONCURRENCY=1 pnpm verify:plugins --skip-build`:**16 个独立插件仓库、17 个制品全部通过**,包括外部 SDK/Client 消费者及三个可选 Strategy 同时启用。 +- 通过 `MNEMON_NATIVE_TEST_CLI` 实际运行 Native Source/View 写入、召回与软删除,以及同一 View 新建两个 Native 目标并归档的用例。 +- WebUI:USER 空数组增改删成功,非空分支拒绝,MEMORY 分支设置/清空成功。手动编辑 USER、创建档案及按正文搜索成功。界面创建并激活 Native 空间后,由已校验 CLI 写入合成记录,再由 WebUI 关键词检索读回完整原文。 +- 设置保存出现成功回执。Host 冷启动后 Runtime 和 Documents 文件 SHA-256 完全一致,Native CLI 仍能召回同一个 ID 和原文。 + +![CLI 写入后由 WebUI 读回](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-281-user-branches/native-cli-webui-recall.png) + +[verification.json](./verification.json) 保留最小实际调用、回执和内容哈希。[基线制品](./before-artifacts.json)与[修复制品](./after-artifacts.json)记录十七个 tarball 的哈希及合成输入。原始 Host 日志、本地认证 URL、npm 安装和完整会话日志保留在仓库之外。 + +## 重跑 WebUI + +将 [rc2-profile.json](./rc2-profile.json) 复制为新临时目录中的 `package.json`,使用 Node 24 执行 `npm install --ignore-scripts`。先构建 Root,再构建独立插件;通过 `scripts/release.mjs` 的 `readReleasePackages`、`createReleasePlan`、`packRelease` 将十七个制品打包到空目录。本地验收包不发布到 registry。 + +```sh +MNEMON_CLI_PATH=/absolute/verified/mnemon node scripts/fixtures/serve-runtime-user-branches.mjs \ + --profile /absolute/rc2-profile --artifacts /absolute/packed-artifacts \ + --state /absolute/empty-disposable-state +``` + +打开 `private.json` 中的私有 URL,选择夹具的 `workspace` 目录和官方标准预设,每轮分别发送:`USER281_PREPARE`、`USER281_REPLACE`、`USER281_ADD`、`USER281_REMOVE`、`USER281_REJECT`、`MEMORY281_SCOPE`、`MEMORY281_CLEAR`。回环模型固定决策并汇总实际工具回执,不预设存储成功;正式适配器、工具执行、持久化、传输和浏览器都是真实实现。`SIGUSR2` 保留数据并重启 Host,重新连接前读取新 URL;可选 `--port` 在重启时保持指定端口(默认 `0` 自动选择空闲端口);`SIGTERM` 停止夹具及 Host。 + +## 边界与剩余分类 + +本次没有在 Windows 执行,也不评价真实模型质量;无需外部 API 凭据。默认完整套件没有启用可选的 Flash、Teams、OpenViking 服务及独立 lifecycle 测试,保留其明确跳过记录。本次使用正式 DSH 与安装制品,没有使用可选的 Source-link WebUI 夹具。 + +冷启动后的新 WebUI 连接受到浏览器环境阻止(`ERR_BLOCKED_BY_CLIENT`),换回原端口复验仍相同。官方认证 URL 经 HTTP 仍正确返回认证重定向,并可凭其 cookie 取得页面;认证机制没有改变。旧页面已断线,不计为冷启动读回证据。冷启动结论仅限文件哈希、已保存配置和 CLI 读回;上述截图和交互检查均来自重启前正常连接的 Host。 + +仍开放的 #251 没有新增评论或失败原始会话;当前 main 中已有修复 #253 未变。六次新的只读预览审计保留原文件字节,并与既有迁移证据一致。在取得脱敏的完整失败事件链、确切源码构建 revision 及当前修复/迁移错误前,不据此新建重复 PR 或关闭 Issue。既有 UI/迁移证据仅复核,没有当作本轮新执行。 diff --git a/dsh/docs/pr-assets/issue-281-user-branches/after-artifacts.json b/dsh/docs/pr-assets/issue-281-user-branches/after-artifacts.json new file mode 100644 index 000000000..c176946cb --- /dev/null +++ b/dsh/docs/pr-assets/issue-281-user-branches/after-artifacts.json @@ -0,0 +1,171 @@ +{ + "issue": 281, + "dsh": "0.1.7-rc.2", + "node": "v25.1.0", + "commands": { + "USER281_PREPARE": { + "action": "add", + "target": "user", + "content": "USER281_SEED: I prefer concise replies.", + "importance": "normal" + }, + "USER281_REPLACE": { + "action": "replace", + "target": "user", + "old_text": "USER281_SEED: I prefer concise replies.", + "content": "USER281_REPLACED: I prefer concise replies with examples.", + "importance": "normal", + "branches": [] + }, + "USER281_ADD": { + "action": "add", + "target": "user", + "content": "USER281_EXTRA: I prefer metric units.", + "importance": "normal", + "branches": [] + }, + "USER281_REMOVE": { + "action": "remove", + "target": "user", + "old_text": "USER281_EXTRA: I prefer metric units.", + "branches": [] + }, + "USER281_REJECT": { + "action": "add", + "target": "user", + "content": "USER281_REJECTED: I prefer afternoon reminders.", + "branches": [ + "experiment" + ] + }, + "MEMORY281_SCOPE": { + "action": "add", + "target": "memory", + "content": "MEMORY281_SCOPED: The experiment uses SQLite.", + "importance": "normal", + "branches": [ + "experiment" + ] + }, + "MEMORY281_CLEAR": { + "action": "replace", + "target": "memory", + "old_text": "MEMORY281_SCOPED: The experiment uses SQLite.", + "content": "MEMORY281_GLOBAL: The project uses SQLite.", + "importance": "normal", + "branches": [] + } + }, + "idleReview": false, + "toolPresentation": "native", + "preset": "standard", + "optionalStrategies": [ + "scoped", + "light-context", + "auto-capture" + ], + "artifacts": [ + { + "name": "dsh-mnemon", + "version": "0.5.14", + "filename": "dsh-mnemon-0.5.14.tgz", + "sha256": "2ee1c94766f9b723b934a12634ba7bbd5f3812cc14cda0199c16488aba11fe24" + }, + { + "name": "dsh-mnemon-provider-byterover", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-byterover-0.5.4.tgz", + "sha256": "17dcd57cb35c0c81af2b93c92be66c2e6b7e8180d5d1cf634c589281003dddc1" + }, + { + "name": "dsh-mnemon-provider-hindsight", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-hindsight-0.5.4.tgz", + "sha256": "739dec0d42689fdbdb8183ba75f7b6afc4be899b585c03c2a85842e0e998a170" + }, + { + "name": "dsh-mnemon-provider-holographic", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-holographic-0.5.4.tgz", + "sha256": "700fa0f94387ee04f5202dbbe0d410afb7114ba2486b25c32d64d7b059245f8c" + }, + { + "name": "dsh-mnemon-provider-honcho", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-honcho-0.5.4.tgz", + "sha256": "db8ffcbbc1df42555295a1ff818e2059b5c09776a0dc4cc9c311779355b06fd7" + }, + { + "name": "dsh-mnemon-provider-mem0", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-mem0-0.5.4.tgz", + "sha256": "ccd61716ead4d9462e15a662edb59c9850d812e8660bc33c4cc574601b816255" + }, + { + "name": "dsh-mnemon-provider-mnemon-native", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-mnemon-native-0.5.5.tgz", + "sha256": "8b839950e17b086578490b9eb3ef438331afa96a85ccd588ddaf7af9649f1f32" + }, + { + "name": "dsh-mnemon-provider-openviking", + "version": "0.5.6", + "filename": "dsh-mnemon-provider-openviking-0.5.6.tgz", + "sha256": "e5b3f9c80c6c450e5cb0e0103f30f8df39550dd980aac70dc4d44e069e2bc570" + }, + { + "name": "dsh-mnemon-provider-retaindb", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-retaindb-0.5.4.tgz", + "sha256": "8962cfc5b8e777a86c8816dfd47f32943aa6a1f1973bc8882727b4b430579e3a" + }, + { + "name": "dsh-mnemon-provider-supermemory", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-supermemory-0.5.4.tgz", + "sha256": "c117f467d0eb66954ab5b27a8ae08722770699e0f9113fa4bdc9f0724485b508" + }, + { + "name": "dsh-mnemon-source-documents", + "version": "0.5.6", + "filename": "dsh-mnemon-source-documents-0.5.6.tgz", + "sha256": "7a94dee1d0e47b2d76a71d86b27d4165875f5bb3a8066b9ef68c2e5b2cb1825a" + }, + { + "name": "dsh-mnemon-source-memory-spaces", + "version": "0.5.9", + "filename": "dsh-mnemon-source-memory-spaces-0.5.9.tgz", + "sha256": "a2c913d866be46736ed8aca1dd61371319a0b40593a6258f6d6eec581245bc85" + }, + { + "name": "dsh-mnemon-source-runtime", + "version": "0.5.8", + "filename": "dsh-mnemon-source-runtime-0.5.8.tgz", + "sha256": "a0ddb4d9283277614253e4000ad8e6234c6ab1fcfb733aef1894bf080b1193d7" + }, + { + "name": "dsh-mnemon-strategy-auto-capture", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-auto-capture-0.5.4.tgz", + "sha256": "a653c1ebec85da0fe2c878e42a0eea84e20da877682fa1c9c97f5d8f8f70c074" + }, + { + "name": "dsh-mnemon-strategy-default-three-tier", + "version": "0.5.5", + "filename": "dsh-mnemon-strategy-default-three-tier-0.5.5.tgz", + "sha256": "fe4e722303ffeff5a598011d9ee6b8f5de73fe21aa2bf25ae215c750817b60df" + }, + { + "name": "dsh-mnemon-strategy-light-context", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-light-context-0.5.4.tgz", + "sha256": "eac6b61e9652ed54d03dd013bec285afe8714547a27c33ace286439e5958250b" + }, + { + "name": "dsh-mnemon-strategy-scoped", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-scoped-0.5.4.tgz", + "sha256": "c751c30b61f5ae60eca489cda936ed17a5b86dadaae538d9657dd76fe8467843" + } + ] +} diff --git a/dsh/docs/pr-assets/issue-281-user-branches/before-artifacts.json b/dsh/docs/pr-assets/issue-281-user-branches/before-artifacts.json new file mode 100644 index 000000000..9a2caf1ee --- /dev/null +++ b/dsh/docs/pr-assets/issue-281-user-branches/before-artifacts.json @@ -0,0 +1,171 @@ +{ + "issue": 281, + "dsh": "0.1.7-rc.2", + "node": "v25.1.0", + "commands": { + "USER281_PREPARE": { + "action": "add", + "target": "user", + "content": "USER281_SEED: I prefer concise replies.", + "importance": "normal" + }, + "USER281_REPLACE": { + "action": "replace", + "target": "user", + "old_text": "USER281_SEED: I prefer concise replies.", + "content": "USER281_REPLACED: I prefer concise replies with examples.", + "importance": "normal", + "branches": [] + }, + "USER281_ADD": { + "action": "add", + "target": "user", + "content": "USER281_EXTRA: I prefer metric units.", + "importance": "normal", + "branches": [] + }, + "USER281_REMOVE": { + "action": "remove", + "target": "user", + "old_text": "USER281_EXTRA: I prefer metric units.", + "branches": [] + }, + "USER281_REJECT": { + "action": "add", + "target": "user", + "content": "USER281_REJECTED: I prefer afternoon reminders.", + "branches": [ + "experiment" + ] + }, + "MEMORY281_SCOPE": { + "action": "add", + "target": "memory", + "content": "MEMORY281_SCOPED: The experiment uses SQLite.", + "importance": "normal", + "branches": [ + "experiment" + ] + }, + "MEMORY281_CLEAR": { + "action": "replace", + "target": "memory", + "old_text": "MEMORY281_SCOPED: The experiment uses SQLite.", + "content": "MEMORY281_GLOBAL: The project uses SQLite.", + "importance": "normal", + "branches": [] + } + }, + "idleReview": false, + "toolPresentation": "native", + "preset": "standard", + "optionalStrategies": [ + "scoped", + "light-context", + "auto-capture" + ], + "artifacts": [ + { + "name": "dsh-mnemon", + "version": "0.5.14", + "filename": "dsh-mnemon-0.5.14.tgz", + "sha256": "6275ed7a275afac2f23d8f3bc9ba6402a0dd3d8e33e87bbb99c9d08a74924356" + }, + { + "name": "dsh-mnemon-provider-byterover", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-byterover-0.5.4.tgz", + "sha256": "17dcd57cb35c0c81af2b93c92be66c2e6b7e8180d5d1cf634c589281003dddc1" + }, + { + "name": "dsh-mnemon-provider-hindsight", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-hindsight-0.5.4.tgz", + "sha256": "739dec0d42689fdbdb8183ba75f7b6afc4be899b585c03c2a85842e0e998a170" + }, + { + "name": "dsh-mnemon-provider-holographic", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-holographic-0.5.4.tgz", + "sha256": "700fa0f94387ee04f5202dbbe0d410afb7114ba2486b25c32d64d7b059245f8c" + }, + { + "name": "dsh-mnemon-provider-honcho", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-honcho-0.5.4.tgz", + "sha256": "db8ffcbbc1df42555295a1ff818e2059b5c09776a0dc4cc9c311779355b06fd7" + }, + { + "name": "dsh-mnemon-provider-mem0", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-mem0-0.5.4.tgz", + "sha256": "ccd61716ead4d9462e15a662edb59c9850d812e8660bc33c4cc574601b816255" + }, + { + "name": "dsh-mnemon-provider-mnemon-native", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-mnemon-native-0.5.5.tgz", + "sha256": "8b839950e17b086578490b9eb3ef438331afa96a85ccd588ddaf7af9649f1f32" + }, + { + "name": "dsh-mnemon-provider-openviking", + "version": "0.5.6", + "filename": "dsh-mnemon-provider-openviking-0.5.6.tgz", + "sha256": "e5b3f9c80c6c450e5cb0e0103f30f8df39550dd980aac70dc4d44e069e2bc570" + }, + { + "name": "dsh-mnemon-provider-retaindb", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-retaindb-0.5.4.tgz", + "sha256": "8962cfc5b8e777a86c8816dfd47f32943aa6a1f1973bc8882727b4b430579e3a" + }, + { + "name": "dsh-mnemon-provider-supermemory", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-supermemory-0.5.4.tgz", + "sha256": "c117f467d0eb66954ab5b27a8ae08722770699e0f9113fa4bdc9f0724485b508" + }, + { + "name": "dsh-mnemon-source-documents", + "version": "0.5.6", + "filename": "dsh-mnemon-source-documents-0.5.6.tgz", + "sha256": "7a94dee1d0e47b2d76a71d86b27d4165875f5bb3a8066b9ef68c2e5b2cb1825a" + }, + { + "name": "dsh-mnemon-source-memory-spaces", + "version": "0.5.9", + "filename": "dsh-mnemon-source-memory-spaces-0.5.9.tgz", + "sha256": "a2c913d866be46736ed8aca1dd61371319a0b40593a6258f6d6eec581245bc85" + }, + { + "name": "dsh-mnemon-source-runtime", + "version": "0.5.8", + "filename": "dsh-mnemon-source-runtime-0.5.8.tgz", + "sha256": "a0ddb4d9283277614253e4000ad8e6234c6ab1fcfb733aef1894bf080b1193d7" + }, + { + "name": "dsh-mnemon-strategy-auto-capture", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-auto-capture-0.5.4.tgz", + "sha256": "a653c1ebec85da0fe2c878e42a0eea84e20da877682fa1c9c97f5d8f8f70c074" + }, + { + "name": "dsh-mnemon-strategy-default-three-tier", + "version": "0.5.5", + "filename": "dsh-mnemon-strategy-default-three-tier-0.5.5.tgz", + "sha256": "fe4e722303ffeff5a598011d9ee6b8f5de73fe21aa2bf25ae215c750817b60df" + }, + { + "name": "dsh-mnemon-strategy-light-context", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-light-context-0.5.4.tgz", + "sha256": "eac6b61e9652ed54d03dd013bec285afe8714547a27c33ace286439e5958250b" + }, + { + "name": "dsh-mnemon-strategy-scoped", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-scoped-0.5.4.tgz", + "sha256": "c751c30b61f5ae60eca489cda936ed17a5b86dadaae538d9657dd76fe8467843" + } + ] +} diff --git a/dsh/docs/pr-assets/issue-281-user-branches/rc2-profile.json b/dsh/docs/pr-assets/issue-281-user-branches/rc2-profile.json new file mode 100644 index 000000000..8cc92e743 --- /dev/null +++ b/dsh/docs/pr-assets/issue-281-user-branches/rc2-profile.json @@ -0,0 +1,6 @@ +{ + "private": true, + "dependencies": { + "@deepseek-ai/dsh": "0.1.7-rc.2" + } +} diff --git a/dsh/docs/pr-assets/issue-281-user-branches/verification.json b/dsh/docs/pr-assets/issue-281-user-branches/verification.json new file mode 100644 index 000000000..4872ecb3a --- /dev/null +++ b/dsh/docs/pr-assets/issue-281-user-branches/verification.json @@ -0,0 +1,383 @@ +{ + "issue": 281, + "baselineRevision": "6dc4e4201585223f7f1da371256da11deee34d6c", + "baselineRegression": { + "passed": 9, + "failed": 4, + "error": "branches applies to target=memory only", + "affectedCompositions": [ + "default root", + "default child", + "three enhancements with renamed entries/root", + "three enhancements with renamed entries/child" + ] + }, + "fixedFocusedRegression": { + "passed": 45, + "failed": 0, + "files": [ + "tests/runtime-user-branches.spec.ts", + "tests/rpc.spec.ts", + "tests/strategy-extensions-host.spec.ts" + ] + }, + "cli": { + "version": "0.2.9", + "platform": "darwin_arm64", + "archiveSha256": "6fbb1d572d8b4c080c36080e23ae0a183fce6841748f6c426e712136ac756b24", + "binarySha256": "bd1b080b0f98e8703b7cd942bd9b7ea3e15f69081e26c302e129d306ac86aaf4" + }, + "fullVerification": { + "passed": true, + "rootTests": 1373, + "rootSkipped": 7, + "rootFiles": 100, + "independentPluginRepositories": 16, + "packedArtifacts": 17, + "unpackedBytes": 1374961, + "sourceArtifactChanges": false, + "pluginTests": 402, + "pluginSkipped": 2 + }, + "webui": { + "before": { + "dsh": "0.1.7-rc.2", + "model": "loopback deterministic fixture", + "cases": { + "USER281_PREPARE": { + "call": { + "name": "mnemon_runtime_memory", + "args": { + "action": "add", + "target": "user", + "content": "USER281_SEED: I prefer concise replies.", + "importance": "normal" + } + }, + "receipt": { + "callId": "issue281-4", + "isError": false, + "result": { + "success": true, + "message": "Entry added.", + "target": "user", + "entryCount": 1, + "usage": { + "used": 39, + "limit": 4096 + }, + "added": "USER281_SEED: I prefer concise replies.", + "revision": "a2d1605f49a3c7c7cb1ef8b9f85cd464a782660d6476fee966f7128d83d4a6aa", + "memoryReceipt": { + "status": "succeeded", + "completion": "committed", + "committedAt": "2026-09-25T16:04:42.181Z" + } + } + } + }, + "USER281_REPLACE": { + "call": { + "name": "mnemon_runtime_memory", + "args": { + "action": "replace", + "target": "user", + "old_text": "USER281_SEED: I prefer concise replies.", + "content": "USER281_REPLACED: I prefer concise replies with examples.", + "importance": "normal", + "branches": [] + } + }, + "receipt": { + "callId": "issue281-6", + "isError": true, + "result": { + "text": "Error: branches applies to target=memory only" + } + } + } + } + }, + "after": { + "dsh": "0.1.7-rc.2", + "model": "loopback deterministic fixture", + "cases": { + "USER281_PREPARE": { + "call": { + "id": "issue281-3", + "name": "mnemon_runtime_memory", + "args": { + "action": "add", + "target": "user", + "content": "USER281_SEED: I prefer concise replies.", + "importance": "normal" + } + }, + "receipt": { + "callId": "issue281-3", + "isError": false, + "result": { + "success": true, + "message": "Entry added.", + "target": "user", + "entryCount": 1, + "usage": { + "used": 39, + "limit": 4096 + }, + "added": "USER281_SEED: I prefer concise replies.", + "revision": "752e1ee8ff95441b0b4405daaacbbc1e0c2492fcfce8a6f43f8066a78c8a9dbc", + "memoryReceipt": { + "status": "succeeded", + "completion": "committed", + "committedAt": "2026-09-25T16:08:14.194Z" + } + } + } + }, + "USER281_REPLACE": { + "call": { + "id": "issue281-6", + "name": "mnemon_runtime_memory", + "args": { + "action": "replace", + "target": "user", + "old_text": "USER281_SEED: I prefer concise replies.", + "content": "USER281_REPLACED: I prefer concise replies with examples.", + "importance": "normal", + "branches": [] + } + }, + "receipt": { + "callId": "issue281-6", + "isError": false, + "result": { + "success": true, + "message": "Entry replaced.", + "target": "user", + "entryCount": 1, + "usage": { + "used": 57, + "limit": 4096 + }, + "replaced": { + "from": "USER281_SEED: I prefer concise replies.", + "to": "USER281_REPLACED: I prefer concise replies with examples." + }, + "revision": "6601afdf483d86518e7e447965831bd03598b195f3777e77db262f479be86aa7", + "memoryReceipt": { + "status": "succeeded", + "completion": "committed", + "committedAt": "2026-09-25T16:08:32.610Z" + } + } + } + }, + "USER281_ADD": { + "call": { + "id": "issue281-8", + "name": "mnemon_runtime_memory", + "args": { + "action": "add", + "target": "user", + "content": "USER281_EXTRA: I prefer metric units.", + "importance": "normal", + "branches": [] + } + }, + "receipt": { + "callId": "issue281-8", + "isError": false, + "result": { + "success": true, + "message": "Entry added.", + "target": "user", + "entryCount": 2, + "usage": { + "used": 98, + "limit": 4096 + }, + "added": "USER281_EXTRA: I prefer metric units.", + "revision": "1820de19b3c253861fae19029c2904a1f30d43a5b1f1265f66e605bcf75c84b2", + "memoryReceipt": { + "status": "succeeded", + "completion": "committed", + "committedAt": "2026-09-25T16:09:16.050Z" + } + } + } + }, + "USER281_REMOVE": { + "call": { + "id": "issue281-10", + "name": "mnemon_runtime_memory", + "args": { + "action": "remove", + "target": "user", + "old_text": "USER281_EXTRA: I prefer metric units.", + "branches": [] + } + }, + "receipt": { + "callId": "issue281-10", + "isError": false, + "result": { + "success": true, + "message": "Entry removed.", + "target": "user", + "entryCount": 1, + "usage": { + "used": 57, + "limit": 4096 + }, + "removed": "USER281_EXTRA: I prefer metric units.", + "revision": "6601afdf483d86518e7e447965831bd03598b195f3777e77db262f479be86aa7", + "memoryReceipt": { + "status": "succeeded", + "completion": "committed", + "committedAt": "2026-09-25T16:09:16.240Z" + } + } + } + }, + "USER281_REJECT": { + "call": { + "id": "issue281-12", + "name": "mnemon_runtime_memory", + "args": { + "action": "add", + "target": "user", + "content": "USER281_REJECTED: I prefer afternoon reminders.", + "branches": [ + "experiment" + ] + } + }, + "receipt": { + "callId": "issue281-12", + "isError": true, + "result": { + "text": "Error: branches applies to target=memory only" + } + } + }, + "MEMORY281_SCOPE": { + "call": { + "id": "issue281-14", + "name": "mnemon_runtime_memory", + "args": { + "action": "add", + "target": "memory", + "content": "MEMORY281_SCOPED: The experiment uses SQLite.", + "importance": "normal", + "branches": [ + "experiment" + ] + } + }, + "receipt": { + "callId": "issue281-14", + "isError": false, + "result": { + "success": true, + "message": "Entry added.", + "target": "memory", + "entryCount": 1, + "usage": { + "used": 45, + "limit": 10240 + }, + "added": "MEMORY281_SCOPED: The experiment uses SQLite.", + "revision": "8a7c550c896ec3d0da3a928086cf708994e6c1768987997fa2b08714003f0200", + "memoryReceipt": { + "status": "succeeded", + "completion": "committed", + "committedAt": "2026-09-25T16:09:24.852Z" + } + } + } + }, + "MEMORY281_CLEAR": { + "call": { + "id": "issue281-16", + "name": "mnemon_runtime_memory", + "args": { + "action": "replace", + "target": "memory", + "old_text": "MEMORY281_SCOPED: The experiment uses SQLite.", + "content": "MEMORY281_GLOBAL: The project uses SQLite.", + "importance": "normal", + "branches": [] + } + }, + "receipt": { + "callId": "issue281-16", + "isError": false, + "result": { + "success": true, + "message": "Entry replaced.", + "target": "memory", + "entryCount": 1, + "usage": { + "used": 42, + "limit": 10240 + }, + "replaced": { + "from": "MEMORY281_SCOPED: The experiment uses SQLite.", + "to": "MEMORY281_GLOBAL: The project uses SQLite." + }, + "revision": "0bb07c5f0b3529a09197c55a8887d4af6d7ec4620f525ccceff7f50014c0dcdb", + "memoryReceipt": { + "status": "succeeded", + "completion": "committed", + "committedAt": "2026-09-25T16:09:25.416Z" + } + } + } + } + } + }, + "foundation": { + "runtimeManualEdit": true, + "documentsCreateAndContentSearch": true, + "nativeSpaceCreateAndActivate": true, + "nativeCliWriteAndWebuiKeywordReadback": true, + "settingsSaved": true, + "threeEnhancementsEnabled": true + } + }, + "scope": { + "fix": "named mnemon_runtime_memory adapter only", + "sourcesAndGenericActions": "strict USER branch contract retained", + "realWindowsTest": false, + "externalModelUsed": false + }, + "coldRestart": { + "fileHashes": { + "runtime/memories.json": "7474fda50ce2d424aacdcae5c297681ad43bf8b203afb96672171178d9c9d81d", + "runtime/USER.md": "e171c4ab1ba69b4e03abe8ed1f3eb6e03c4f2616b9b05c2a3f6851c802edd3cf", + "runtime/MEMORY.md": "a45fedb212828eab54e34917b4215335ba25d8fef2f17ee5b4a0c4a391a73771", + "documents/index.json": "445493609fd0c0c8be59bcde8ab5a13d0848351871de6aec79d24f6c80655990", + "documents/active/issue-281-foundation-check-74da7ec3.md": "09799884268f93ed64f90a76700c481d7cf016e89e9e4e0c1db81ba580afb3e7" + }, + "exactRuntimeDocumentsBytes": true, + "nativeRecallId": "f08545a9-87dc-4035-8d7f-1fe741223467", + "webuiReconnection": { + "passed": false, + "reason": "Browser ERR_BLOCKED_BY_CLIENT for the new authenticated URL, including the original port; disconnected cached UI excluded from evidence." + }, + "stablePortFileHashes": { + "dsh-home/profiles/web/cordis.patch.yml": "3d8789e7e214124dee7b3fa4e66b6e33ac0a0d438838c1a4005e9b2e195db656", + "data/data/.dsh-memory-bodies.json": "b228fd2bae3d885a6bef3eb9872b6fce29417e732e4896e58f6bbaa2f88af955", + "data/data/default/mnemon.db": "a1d07a5d312dd6cad09bf83ed1693b488fab43e0b2799ce94604ee370f2cbea0", + "data/documents/active/issue-281-foundation-check-74da7ec3.md": "09799884268f93ed64f90a76700c481d7cf016e89e9e4e0c1db81ba580afb3e7", + "data/documents/index.json": "445493609fd0c0c8be59bcde8ab5a13d0848351871de6aec79d24f6c80655990", + "data/runtime/MEMORY.md": "a45fedb212828eab54e34917b4215335ba25d8fef2f17ee5b4a0c4a391a73771", + "data/runtime/USER.md": "e171c4ab1ba69b4e03abe8ed1f3eb6e03c4f2616b9b05c2a3f6851c802edd3cf", + "data/runtime/memories.json": "7474fda50ce2d424aacdcae5c297681ad43bf8b203afb96672171178d9c9d81d" + }, + "savedMaxPerSession": 19, + "profileAndAllThreeSourcesUnchanged": true + }, + "fixedToolSourceBlob": "d32c0d3c7c5e9d92dcb125bcac08b6b553e1041a" +} diff --git a/dsh/docs/pr-assets/issue-305-desktop-console/README.md b/dsh/docs/pr-assets/issue-305-desktop-console/README.md new file mode 100644 index 000000000..b70cf4a1f --- /dev/null +++ b/dsh/docs/pr-assets/issue-305-desktop-console/README.md @@ -0,0 +1,38 @@ +# Issue #305: desktop CLI console windows + +## 中文 + +基线为 `42640337`(v0.5.17),修复为 `809ad5b6`。通过官方 npm 包 `@mnemon-dev/mnemon@0.2.9` 复现调用链:插件启动 Node 时设置了 `windowsHide: true`,但 npm 启动器随后启动原生 Mnemon 时没有此设置。`--version` 和 `status` 都存在这层间接启动。Node 的 [`windowsHide` 选项](https://nodejs.org/api/child_process.html#child_processspawncommand-args-options)默认关闭,不会成为子进程以后创建进程时的默认策略。 + +修复在 Memory Spaces Source 的公开 `native-cli` 入口中统一解析调用目标,由 Source runner 和 Host 版本检查共用。核对官方包名、当前平台的 optional dependency 和精确版本后,直接执行已安装的原生程序;未知布局、缺失依赖保留原启动器及其诊断。npm 更新继续走原启动器和归属检查,未修改 DSH 或 Mnemon 的发布包。 + +验证结果: + +- 修复前新增回归测试 3 失败、5 通过;修复后 Host/Source 定向回归 70 通过。 +- `pnpm run verify` 通过,包含独立插件测试、Headless 激活、构建与包校验;`pnpm run verify:plugins` 验证 17 个独立仓库和 18 个制品。 +- Windows CI 在 Node 22.19 和 24 上通过真实原生子进程测试、路径/参数/环境/取消策略及 npm 更新归属测试。环境变量大小写断言已适配 Windows。 +- 官方 DSH `0.1.7-rc.2` 在 Electron `44.0.0` 主进程中运行,Host 未设置 `ELECTRON_RUN_AS_NODE`。通过 WebUI 创建并激活原生记忆空间,再用真实 `deepseek-flash` 完成查重、写入与任务结果提交;WebUI 直接检索返回 `SILENT-BRAIN-305` 对应记忆。 +- 状态页显示 Mnemon `0.2.9`、空间已连接及 1 条记忆;版本面板仍识别 npm 安装。隔离的本地 npm 前缀不属于系统全局 npm,面板正确拒绝自动更新它。 +- Electron 验收的所有已记录原生调用均为直接子进程并带 `windowsHide: true`。脱敏计数、模型请求、写入回执和制品哈希见 [acceptance.json](./acceptance.json)。 + +截图在 macOS 拍摄,用于证明功能链路,**不是 Windows 弹窗消失的现场录像**。Windows 覆盖来自 CI 的真实进程测试。npm `update` 的下游安装器行为仍保持原样,不在本次状态/记忆调用修复范围内。 + +| 状态页:修复前 | 状态页:修复后,真实写入完成 | +| --- | --- | +| ![修复前状态](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-305-desktop-console/before-status.png) | ![修复后状态](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-305-desktop-console/after-status.png) | + +| 版本检查:修复前 | 版本检查:修复后 | +| --- | --- | +| ![修复前版本](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-305-desktop-console/before-versions.png) | ![修复后版本](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-305-desktop-console/after-versions.png) | + +![Flash 写入后通过真实 CLI 召回](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-305-desktop-console/after-recall.png) + +## English + +The baseline is `42640337` (v0.5.17), with the fix in `809ad5b6`. The official Mnemon npm 0.2.9 launcher starts another native process without `windowsHide`, even when the plugin's own Node child is hidden. Both version and status calls reproduce that launch chain. + +The Memory Spaces public `native-cli` entry now resolves the native executable pinned by the recognized official npm installation. Source execution and Host version checks share this policy. Package identity, platform dependency and exact version must match; unknown layouts and missing packages retain the launcher fallback. npm updates retain their original launcher and ownership checks. No DSH or upstream Mnemon package is modified. + +The regression initially failed three tests. The completed focused suite passes 70 tests, including real child processes on Windows Node 22.19 and 24. Full repository verification and all 18 packed artifacts pass. A real Electron 44.0.0 main process runs published DSH 0.1.7-rc.2 and the packed fix. WebUI space creation/activation, live Flash deduplication/write/completion, CLI-backed recall, health and version checks succeed. The local npm prefix correctly remains ineligible for a system-global npm update. + +Screenshots document macOS functional acceptance, not interactive Windows console visibility. The process trace and Windows CI provide the launch-policy evidence. Candidate packages retain their pre-release version numbers; [artifact hashes and sanitized results](./acceptance.json) distinguish them from the baseline. This change does not alter downstream npm installer behavior during an explicit update. diff --git a/dsh/docs/pr-assets/issue-305-desktop-console/acceptance.json b/dsh/docs/pr-assets/issue-305-desktop-console/acceptance.json new file mode 100644 index 000000000..619ddb1e7 --- /dev/null +++ b/dsh/docs/pr-assets/issue-305-desktop-console/acceptance.json @@ -0,0 +1,108 @@ +{ + "issue": 305, + "baseline": "42640337f06f4a9ba8fa6bcf18c6ed2cef871182", + "fixCommit": "809ad5b6", + "platform": "darwin-arm64", + "dsh": "0.1.7-rc.2", + "mnemonNpm": "0.2.9", + "electron": { + "version": "44.0.0", + "node": "24.18.1", + "processType": "browser", + "runAsNode": null + }, + "beforeCli": { + "nativeProcesses": 2, + "windowsHideTrue": 0, + "operations": { + "--version": 1, + "status": 1 + } + }, + "afterCli": { + "nativeProcesses": 2, + "windowsHideTrue": 2, + "operations": { + "--version": 1, + "status": 1 + } + }, + "electronSpaceCreation": { + "nativeProcesses": 9, + "windowsHideTrue": 9, + "operations": { + "store": 1, + "status": 4, + "--version": 2, + "viz": 1, + "recall": 1 + } + }, + "electronLiveAcceptance": { + "nativeProcesses": 15, + "windowsHideTrue": 15, + "operations": { + "status": 4, + "--version": 3, + "viz": 2, + "recall": 4, + "other": 1, + "remember": 1 + } + }, + "liveModelRequests": [ + { + "model": "deepseek-flash", + "httpStatus": 200, + "tools": [ + "mnemon_memory_bodies" + ] + }, + { + "model": "deepseek-flash", + "httpStatus": 200, + "tools": [ + "mnemon_recall" + ] + }, + { + "model": "deepseek-flash", + "httpStatus": 200, + "tools": [ + "mnemon_remember" + ] + }, + { + "model": "deepseek-flash", + "httpStatus": 200, + "tools": [ + "mnemon_subagent_result" + ] + } + ], + "writeReceipt": { + "action": "added", + "id": "81fb0c4d-cdf8-4189-98ff-ea8e613260b3", + "content": "Issue 305 desktop regression project decision: the synthetic launch verification code is SILENT-BRAIN-305, to be used in subsequent acceptance checks.", + "category": "decision" + }, + "webuiRecall": { + "query": "SILENT-BRAIN-305", + "resultCount": 1 + }, + "artifactSha256": { + "before": { + "dsh-mnemon-0.5.17.tgz": "d082377285bdf28e14959a96cbfc8789c61395b5b2ba1b3adb0ed16cfa03d772", + "dsh-mnemon-source-memory-spaces-0.5.11.tgz": "84b5c1d702d7308280e71b7c38d154d311353a2efb223f83bbc97fe0c0b8d72c" + }, + "after": { + "dsh-mnemon-0.5.17.tgz": "bb0e5910a5df43849e6db15f37a8b2db36d5169610c77c19d20e4c25476e2b64", + "dsh-mnemon-source-memory-spaces-0.5.11.tgz": "906cde9f7261a68b323f9441765da15587481ba7fe15586fc64dc4a9913d6163" + } + }, + "limits": [ + "No interactive Windows desktop recording; Windows process tests run on Node 22.19 and 24 in CI.", + "Screenshots show functional acceptance on macOS, not Windows console visibility.", + "The npm update launcher and downstream installer behavior remain unchanged." + ] +} diff --git a/dsh/docs/pr-assets/issue-310-desktop-window/README.md b/dsh/docs/pr-assets/issue-310-desktop-window/README.md new file mode 100644 index 000000000..fb3d191fd --- /dev/null +++ b/dsh/docs/pr-assets/issue-310-desktop-window/README.md @@ -0,0 +1,38 @@ +# Desktop windows manage memory — issue #310 + +[中文](README.zh-CN.md) + +Verified on 2026-09-29 (Asia/Shanghai) with isolated profiles and synthetic data. DSH was not modified. + +## Problem and fix + +DSH Desktop loads its window from the app's own `dsh-app://app/` address, and the DSH 0.1.7 app declares no transport for the page. dsh-mnemon 0.5.18 read that page as remote, sent every Mnemon call through the API Gateway, and the default `remoteAccess: read-only` turned Runtime memory, Memory Spaces and the plugin settings read only, while agent tools could still write ([#310](https://github.com/omdsh-dev/dsh-mnemon/issues/310)). + +From `b5f7914b`, a page the application serves itself uses Mnemon's local channels; it counts as remote only when DSH declares a transport for it that does not own the Host. A page opened from another machine keeps the Gateway and its read-only default, and now says which grant would let it manage memory. + +| Published 0.5.18 in a DSH 0.1.7 Desktop window | The fix in the same window | +| --- | --- | +| ![Runtime memory says the deployment is read only and has no Add memory](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-310-desktop-window/before-runtime.png) | ![Add memory is back and a new entry was written](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-310-desktop-window/after-runtime.png) | +| ![The dsh-mnemon page under Plugins says its settings are read only](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-310-desktop-window/before-plugin.png) | ![The dsh-mnemon page under Plugins is editable](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-310-desktop-window/after-plugin.png) | + +## Environment + +- Shell: [`scripts/fixtures/electron-desktop-window.mjs`](../../../scripts/fixtures/electron-desktop-window.mjs) in Electron 44.3.0 registers `dsh-app` as a standard, secure, CORS, fetch and stream scheme, as the official shell does, and proxies the window's requests to `dsh web` listening only on 127.0.0.1. `DSH_TRANSPORT=owns-host` also declares the DSH 0.2 Desktop transport, `{ ownsHost: true, streamBaseUrl }`. +- Hosts: published DSH 0.1.7-rc.2 and 0.2.0-rc.1, each installed with npm into an isolated prefix, with a fresh DSH home and profile for every run. +- Packages: the baseline is dsh-mnemon 0.5.18 as published; the candidate is the 0.5.19 release that `pnpm release:version` builds from `b5f7914b`, installed from its packed tarballs. +- Driver: the DevTools protocol opens Memory System, adds one runtime memory, reads Memory Spaces and the plugin page, and records the URL of every Mnemon request. [validation.json](validation.json) has each run's results. + +## Results + +| Window | Package | Runtime memory | Memory Spaces | Plugin settings | Mnemon requests | +| --- | --- | --- | --- | --- | --- | +| DSH 0.1.7-rc.2 Desktop | 0.5.18 | Read only, no Add memory | Save and Create absent | Read only | 13 through the API Gateway | +| DSH 0.1.7-rc.2 Desktop | 0.5.19 | Add memory writes | Save and Create enabled | Editable | Local channels; the write is `POST dsh-app://app/dsh-mnemon-write/…` | +| DSH 0.2.0-rc.1 Desktop, `ownsHost: true` | 0.5.19 | Add memory writes ([screen](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-310-desktop-window/after-dsh-020-runtime.png)) | Save and Create enabled | Editable | Local channels | +| Remote page through `--trusted-host`, DSH 0.2.0-rc.1 | 0.5.19 | Read only, with the reason ([screen](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-310-desktop-window/remote-runtime.png)) | Save disabled | Read only, with the reason ([screen](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-310-desktop-window/remote-plugin.png)) | 14 through the API Gateway | + +No run logged a console error. DSH shows a 0.1.7 window as reconnecting because its WebSocket stream cannot open through the scheme in this shell; Mnemon's requests are plain HTTP and unaffected, and the screens are cropped to the Memory System. + +## Limits + +This is a shell that behaves like DSH Desktop for the page origin, the scheme and the request path; it is not the Desktop installer. The Host admits the local channels and `/api` with the same Host/Origin checks and browser session, so the desktop result does not depend on a Desktop-only grant. diff --git a/dsh/docs/pr-assets/issue-310-desktop-window/README.zh-CN.md b/dsh/docs/pr-assets/issue-310-desktop-window/README.zh-CN.md new file mode 100644 index 000000000..9f43d534a --- /dev/null +++ b/dsh/docs/pr-assets/issue-310-desktop-window/README.zh-CN.md @@ -0,0 +1,38 @@ +# 桌面版窗口可以管理记忆 — Issue #310 + +[English](README.md) + +于 2026-09-29(Asia/Shanghai)使用隔离的 profile 与合成数据验证,未修改 DSH。 + +## 问题与修复 + +DSH 桌面版从应用自己的 `dsh-app://app/` 地址加载窗口,DSH 0.1.7 桌面版也不为页面声明传输方式。dsh-mnemon 0.5.18 把这样的页面当作远程页面,所有 Mnemon 调用都经 API Gateway,默认的 `remoteAccess: read-only` 让运行时记忆、记忆空间与插件设置变为只读,而 Agent 工具仍可写入([#310](https://github.com/omdsh-dev/dsh-mnemon/issues/310))。 + +自 `b5f7914b` 起,应用自己提供的页面使用 Mnemon 的本地通道;只有 DSH 为页面声明了不持有 Host 的传输方式时,才仍按远程页面处理。从其他设备打开的页面继续使用 API Gateway 与默认只读,并会写明需要哪项授权才能管理记忆。 + +| 已发布的 0.5.18,DSH 0.1.7 桌面窗口 | 同一窗口中的修复 | +| --- | --- | +| ![运行时记忆提示当前部署为只读,没有“添加记忆”](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-310-desktop-window/before-runtime.png) | ![“添加记忆”恢复,新条目已写入](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-310-desktop-window/after-runtime.png) | +| ![插件页中的 dsh-mnemon 提示设置只读](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-310-desktop-window/before-plugin.png) | ![插件页中的 dsh-mnemon 可以编辑](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-310-desktop-window/after-plugin.png) | + +## 环境 + +- 窗口:[`scripts/fixtures/electron-desktop-window.mjs`](../../../scripts/fixtures/electron-desktop-window.mjs) 在 Electron 44.3.0 中像官方桌面壳一样把 `dsh-app` 注册为标准、安全、支持 CORS、fetch 与流式传输的协议,并把窗口的请求代理到只监听 127.0.0.1 的 `dsh web`。`DSH_TRANSPORT=owns-host` 还会声明 DSH 0.2 桌面版的传输方式 `{ ownsHost: true, streamBaseUrl }`。 +- 宿主:正式发布的 DSH 0.1.7-rc.2 与 0.2.0-rc.1,分别用 npm 安装到隔离目录,每次运行使用全新的 DSH home 与 profile。 +- 插件:基线为 npm 上已发布的 dsh-mnemon 0.5.18;候选为 `pnpm release:version` 从 `b5f7914b` 构建的 0.5.19,从打包文件安装。 +- 驱动:通过 DevTools 协议打开记忆系统,添加一条运行时记忆,读取记忆空间与插件页,并按路由记录每个 Mnemon 请求的地址。每次运行的结果见 [validation.json](validation.json)。 + +## 结果 + +| 窗口 | 插件版本 | 运行时记忆 | 记忆空间 | 插件设置 | Mnemon 请求 | +| --- | --- | --- | --- | --- | --- | +| DSH 0.1.7-rc.2 桌面版 | 0.5.18 | 只读,没有“添加记忆” | 没有“存入记忆”与“创建记忆空间” | 只读 | 13 次全部经 API Gateway | +| DSH 0.1.7-rc.2 桌面版 | 0.5.19 | “添加记忆”写入成功 | “存入记忆”与“创建记忆空间”可用 | 可编辑 | 本地通道;写入为 `POST dsh-app://app/dsh-mnemon-write/…` | +| DSH 0.2.0-rc.1 桌面版,`ownsHost: true` | 0.5.19 | “添加记忆”写入成功([截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-310-desktop-window/after-dsh-020-runtime.png)) | 两者可用 | 可编辑 | 本地通道 | +| 通过 `--trusted-host` 打开的远程页面,DSH 0.2.0-rc.1 | 0.5.19 | 只读,并写明原因([截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-310-desktop-window/remote-runtime.png)) | “存入记忆”不可用 | 只读,并写明原因([截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-310-desktop-window/remote-plugin.png)) | 14 次全部经 API Gateway | + +所有运行均无控制台错误。由于 WebSocket 事件流无法通过该协议在此窗口中建立,DSH 会把 0.1.7 窗口显示为“重新连接中”;Mnemon 的请求是普通 HTTP,不受影响,截图已裁到记忆系统区域。 + +## 限制 + +这是在页面地址、协议与请求路径上与 DSH 桌面版一致的窗口,并非桌面版安装包本身。Host 对本地通道与 `/api` 使用相同的 Host/Origin 校验与浏览器会话,因此桌面版的结果不依赖任何桌面版专属授权。 diff --git a/dsh/docs/pr-assets/issue-310-desktop-window/validation.json b/dsh/docs/pr-assets/issue-310-desktop-window/validation.json new file mode 100644 index 000000000..a48b9be01 --- /dev/null +++ b/dsh/docs/pr-assets/issue-310-desktop-window/validation.json @@ -0,0 +1,61 @@ +{ + "record": "issue-310-desktop-window", + "date": "2026-09-29", + "timezone": "Asia/Shanghai", + "candidate": { + "revision": "b5f7914bb7fa6bfbd0bb60b5ccf65b757e1f1514", + "build": "pnpm release:version from the revision: dsh-mnemon 0.5.19 with its pinned plugins, installed from packed tarballs" + }, + "baseline": "dsh-mnemon 0.5.18 with its pinned plugins, as published on npm", + "shell": { + "fixture": "scripts/fixtures/electron-desktop-window.mjs", + "electron": "44.3.0", + "scheme": "dsh-app: standard, secure, CORS, fetch, stream and code cache, as the official shell registers it", + "host": "dsh web on 127.0.0.1 only; a fresh DSH home and profile for every run" + }, + "checks": "Open Memory System; Runtime memory: read-only notice, remote reason, Add memory, one real write; Memory Spaces: Save to memory and Create memory space; the dsh-mnemon page under Plugins: read-only notice and remote reason; every Mnemon request URL by route", + "runs": [ + { + "name": "Baseline in a DSH 0.1.7 Desktop window", + "dsh": "0.1.7-rc.2", "package": "0.5.18", "page": "dsh-app://app/", "transport": null, + "runtime": { "readOnlyNotice": true, "remoteReason": false, "addMemory": false }, + "memorySpaces": { "saveToMemory": "absent", "createMemorySpace": "absent" }, + "pluginPage": { "readOnlyNotice": true, "remoteReason": false }, + "mnemonRequests": { "API Gateway /api/dshMnemon": 13 }, + "consoleErrors": 0 + }, + { + "name": "Fix in a DSH 0.1.7 Desktop window", + "dsh": "0.1.7-rc.2", "package": "0.5.19", "page": "dsh-app://app/", "transport": null, + "runtime": { "readOnlyNotice": false, "remoteReason": false, "addMemory": true, "written": true }, + "memorySpaces": { "saveToMemory": "enabled", "createMemorySpace": "enabled" }, + "pluginPage": { "readOnlyNotice": false, "remoteReason": false }, + "mnemonRequests": { "/dsh-mnemon-read": 14, "/dsh-mnemon-settings": 2, "/dsh-mnemon-view": 3, "/dsh-mnemon-write": 1, "/dsh-mnemon-pack": 1 }, + "write": "POST dsh-app://app/dsh-mnemon-write/source-assistance", + "consoleErrors": 0 + }, + { + "name": "Fix in a DSH 0.2 Desktop window", + "dsh": "0.2.0-rc.1", "package": "0.5.19", "page": "dsh-app://app/", "transport": { "ownsHost": true, "streamBaseUrl": "the Host's loopback origin" }, + "runtime": { "readOnlyNotice": false, "remoteReason": false, "addMemory": true, "written": true }, + "memorySpaces": { "saveToMemory": "enabled", "createMemorySpace": "enabled" }, + "pluginPage": { "readOnlyNotice": false, "remoteReason": false }, + "mnemonRequests": { "/dsh-mnemon-read": 14, "/dsh-mnemon-settings": 4, "/dsh-mnemon-view": 3, "/dsh-mnemon-write": 1, "/dsh-mnemon-pack": 1 }, + "write": "POST dsh-app://app/dsh-mnemon-write/source-assistance", + "consoleErrors": 0 + }, + { + "name": "Fix on a remote page", + "dsh": "0.2.0-rc.1", "package": "0.5.19", "page": "http://memory.test/ (Chrome maps it to the Host, which trusts it with --trusted-host)", "transport": null, + "runtime": { "readOnlyNotice": true, "remoteReason": true, "addMemory": false }, + "memorySpaces": { "saveToMemory": "disabled", "createMemorySpace": "absent" }, + "pluginPage": { "remoteReason": true }, + "mnemonRequests": { "API Gateway /api/dshMnemon": 14 }, + "consoleErrors": 0 + } + ], + "notes": [ + "DSH shows a 0.1.7 window as reconnecting because its WebSocket stream cannot be opened through the scheme; Mnemon's requests are plain HTTP and unaffected. The screenshots are cropped to the Memory System.", + "The remote page shows the Runtime memory Source's own read-only line ('当前为只读模式…') under the new reason; the probe looked for the 0.5.18 wording, so its readOnlyNotice flag for that run was read from the screenshot." + ] +} diff --git a/dsh/docs/pr-assets/issue-318-sidebar-entry/README.md b/dsh/docs/pr-assets/issue-318-sidebar-entry/README.md new file mode 100644 index 000000000..203269ae6 --- /dev/null +++ b/dsh/docs/pr-assets/issue-318-sidebar-entry/README.md @@ -0,0 +1,56 @@ +# Fallback sidebar entry as a native row — issue #318 + +[简体中文](./README.zh-CN.md) | [Issue #318](https://github.com/omdsh-dev/dsh-mnemon/issues/318) | [Measurements](./measurements.json) + +In layouts without DSH's native panel seat, Mnemon inserts its own Memory System entry under New Session. On main that entry kept the older plugin-entry style, so it looked smaller, greyer and indented next to DSH's rows. With the fix, it measures the same as DSH's own rows in the expanded sidebar, when open and on the collapsed rail. + +Baseline: `dfb3196cbcea58fb9b36b7283ccac4662d366858` (main, as published in 0.5.20). Fix: `1d18173aa88933528860ac6b40b9dcdf001b5726`. The runs use macOS 15.6 arm64, Node 24.19.0 and the repository's DSH 0.1.7-rc.2. DSH 0.2.0-rc.2 ships the same `.panelRow` rules and panel glyph sizes. + +## Method + +`pnpm e2e:serve` shows the native seat, so Memory System normally appears as DSH's own row. To compare the fallback with that row on the same page, the check builds the element the fallback builds: +- the classes of the Mnemon stylesheet the page loaded (`NS3bAW_entry`, `_entryIcon`, `_entryLabel`); +- the shared brain icon at the size the fallback passes (18 on main, 16 with the fix); +- the placement `placeEntry` uses, directly under New Session. + +The values are the browser's computed styles and boxes, measured with every row present, so the fallback can be compared with DSH's own Memory System row. The two never appear together in real use: the fallback is removed as soon as the native seat is available. The screenshots therefore hide DSH's Memory System row, so each shows what a fallback layout shows: the fallback **记忆系统** entry under New Session and DSH's **插件** row. + +## Before and after + +| | Main | Fix | +|---|---|---| +| Expanded | ![The fallback row is smaller, grey and indented](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-318-sidebar-entry/before-expanded.jpg) | ![The fallback row matches the native rows](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-318-sidebar-entry/after-expanded.jpg) | +| Open | ![The open fallback row is bold on a darker background](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-318-sidebar-entry/before-open.jpg) | ![The open fallback row uses DSH's selected-row background](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-318-sidebar-entry/after-open.jpg) | +| Collapsed | ![A circle with a larger icon](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-318-sidebar-entry/before-collapsed.jpg) | ![The same rounded square as DSH's rail rows](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-318-sidebar-entry/after-collapsed.jpg) | + +| Expanded row | DSH Plugins row | Fallback on main | Fallback with the fix | +|---|---|---|---| +| Font size | 14px | 13px | 14px | +| Text color | label primary, `rgb(15, 17, 21)` | label secondary, `rgb(97, 102, 107)` | `rgb(15, 17, 21)` | +| Padding / margin | `7px 8px` / `0 2px` | `0 10px` / `0` | `7px 8px` / `0 2px` | +| Size / radius | 252 × 36 / 12px | 256 × 36 / 8px | 252 × 36 / 12px | +| Icon, left edge | 16px at 22px | 18px at 25px | 16px at 22px | +| Label left edge | 46px | 54px | 46px | + +When open, DSH marks its row with the hover background `rgba(38, 49, 72, 0.06)` and `aria-current="page"`. On main the fallback turned bold (600) on `rgba(38, 49, 72, 0.1)`; with the fix it matches the native row, including `aria-current`. On the collapsed rail, DSH rows are 36 × 36 rounded squares with 18px glyphs. On main the fallback was a circle with a 20px icon; with the fix it matches. + +## Cause and fix + +The fallback row copied the plugin-entry style of August 2026 (SSH and task-board entries): 18px icons in a 24px box, `0 10px` padding and a circular rail item. Those entries have since moved to DSH's panel row, while Mnemon's native path already let DSH draw the row. The fallback now copies `dsh-client-ui-sidebar`'s `.panelRow`: +- inherited 14px type, the primary label color and a 22px line height; +- `7px 8px` padding, `0 2px` margins and `--dsw-radius-md` corners; +- 16px glyphs, 18px on the collapsed rail; +- one hover background for hover and the open workspace; +- the native focus ring. + +The open entry also sets `aria-current="page"`. Layouts with the native seat are unchanged. + +## Automated checks + +- `pnpm run verify` passes. It covers docs links, typecheck, the deterministic build, build, typecheck and tests for all 17 plugins, and root tests (113 files, 1,549 passed, 6 skipped). It also covers Headless activation, package contents, public entries, publint and attw. +- `tests/client-sidebar-css.spec.ts` reads the pinned DSH sidebar CSS. It fails if the fallback entry differs from `.panelRow`, its hover or focus rules, or DSH's 16/18px glyph sizes. Changing the fallback padding back to `0 10px` makes it fail. +- `tests/client-sidebar.spec.tsx` checks that the open entry is `aria-current="page"` and loses it when the workspace closes. + +## Limits + +The fallback element is built on the page from the fallback's own markup and the loaded stylesheet, rather than mounted by a layout without the native seat. Its styles come entirely from the stylesheet, so the comparison holds for any layout whose sidebar column matches DSH's; skins that restyle native rows can target `[data-dsh-plugin="dsh-mnemon"][data-dsh-part="sidebar-entry"]`. Screenshots show DSH's default light theme only. diff --git a/dsh/docs/pr-assets/issue-318-sidebar-entry/README.zh-CN.md b/dsh/docs/pr-assets/issue-318-sidebar-entry/README.zh-CN.md new file mode 100644 index 000000000..e22fe6a8d --- /dev/null +++ b/dsh/docs/pr-assets/issue-318-sidebar-entry/README.zh-CN.md @@ -0,0 +1,56 @@ +# 兜底侧栏入口与原生行一致 — issue #318 + +[English](./README.md) | [Issue #318](https://github.com/omdsh-dev/dsh-mnemon/issues/318) | [测量数据](./measurements.json) + +在没有 DSH 原生面板席位的布局中,Mnemon 会在“新建会话”下方插入自己的“记忆系统”入口。main 上该入口仍沿用旧的插件入口样式,与 DSH 的行并排时字更小、颜色偏灰且向右缩进。修复后,它在展开侧栏、打开状态和折叠侧栏中的测量值都与 DSH 自身的行一致。 + +基线:`dfb3196cbcea58fb9b36b7283ccac4662d366858`(main,即 0.5.20 发布的版本)。修复:`1d18173aa88933528860ac6b40b9dcdf001b5726`。运行环境为 macOS 15.6 arm64、Node 24.19.0 与仓库锁定的 DSH 0.1.7-rc.2;DSH 0.2.0-rc.2 的 `.panelRow` 规则与面板图标尺寸与之相同。 + +## 方法 + +`pnpm e2e:serve` 提供原生席位,因此“记忆系统”平时显示为 DSH 自身的行。为了在同一页面上比较兜底入口与原生行,检查按兜底逻辑构建同样的元素: +- 使用页面已加载的 Mnemon 样式表中的类名(`NS3bAW_entry`、`_entryIcon`、`_entryLabel`); +- 使用共用的大脑图标,尺寸取兜底逻辑传入的值(main 为 18,修复后为 16); +- 按 `placeEntry` 的位置插入到“新建会话”正下方。 + +数值取自浏览器的计算样式与元素边框,测量时所有行都在页面上,以便与 DSH 自身的记忆系统行比较。实际使用中二者不会同时出现:一旦原生席位可用,兜底入口就会被移除。因此截图隐藏了 DSH 的记忆系统行,每张都呈现兜底布局中的样子:“新建会话”下方的兜底**记忆系统**入口,以及 DSH 的**插件**行。 + +## 修复前后 + +| | main | 修复后 | +|---|---|---| +| 展开 | ![兜底行字更小、偏灰且缩进](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-318-sidebar-entry/before-expanded.jpg) | ![兜底行与原生行一致](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-318-sidebar-entry/after-expanded.jpg) | +| 打开 | ![打开时加粗且背景更深](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-318-sidebar-entry/before-open.jpg) | ![打开时使用 DSH 选中行的背景](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-318-sidebar-entry/after-open.jpg) | +| 折叠 | ![圆形且图标更大](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-318-sidebar-entry/before-collapsed.jpg) | ![与 DSH 折叠行相同的圆角方形](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-318-sidebar-entry/after-collapsed.jpg) | + +| 展开状态 | DSH“插件”行 | main 的兜底入口 | 修复后的兜底入口 | +|---|---|---|---| +| 字号 | 14px | 13px | 14px | +| 文字颜色 | 主要文字色 `rgb(15, 17, 21)` | 次要文字色 `rgb(97, 102, 107)` | `rgb(15, 17, 21)` | +| 内边距 / 外边距 | `7px 8px` / `0 2px` | `0 10px` / `0` | `7px 8px` / `0 2px` | +| 尺寸 / 圆角 | 252 × 36 / 12px | 256 × 36 / 8px | 252 × 36 / 12px | +| 图标与左边缘 | 16px,位于 22px | 18px,位于 25px | 16px,位于 22px | +| 文字左边缘 | 46px | 54px | 46px | + +打开时,DSH 的行使用悬停背景 `rgba(38, 49, 72, 0.06)`,并标记 `aria-current="page"`。main 上兜底入口会加粗(600),背景为 `rgba(38, 49, 72, 0.1)`;修复后与原生行一致,也带有 `aria-current`。折叠侧栏中,DSH 的行是 36 × 36 的圆角方形,图标 18px;main 上兜底入口是圆形、图标 20px,修复后与原生一致。 + +## 原因与修复 + +兜底入口沿用的是 2026 年 8 月插件入口(SSH、任务看板)的样式:24px 容器中的 18px 图标、`0 10px` 内边距与圆形的折叠项。这些入口此后已改用 DSH 的面板行,而 Mnemon 的原生路径本就由 DSH 绘制行。兜底入口现在沿用 `dsh-client-ui-sidebar` 的 `.panelRow`: +- 继承的 14px 字号、主要文字色与 22px 行高; +- `7px 8px` 内边距、`0 2px` 外边距与 `--dsw-radius-md` 圆角; +- 16px 图标,折叠侧栏为 18px; +- 悬停与打开状态使用同一悬停背景; +- 原生的焦点环。 + +打开时入口还会设置 `aria-current="page"`。有原生席位的布局不受影响。 + +## 自动化检查 + +- `pnpm run verify` 通过。它覆盖文档链接、类型检查、确定性构建、全部 17 个插件的构建、类型检查与测试,以及根测试(113 个文件,1,549 项通过,6 项跳过);还覆盖 Headless 激活、包内容、公共入口、publint 与 attw。 +- `tests/client-sidebar-css.spec.ts` 读取锁定版本的 DSH 侧栏 CSS。兜底入口与 `.panelRow`、其悬停或焦点规则,或 DSH 的 16/18px 图标尺寸不一致时,测试会失败;把内边距改回 `0 10px` 即可让它失败。 +- `tests/client-sidebar.spec.tsx` 检查打开时入口为 `aria-current="page"`,关闭工作区后该标记被移除。 + +## 限制 + +兜底元素是在页面上按兜底入口自身的结构与已加载的样式表构建的,而不是由缺少原生席位的布局挂载。它的样式完全来自样式表,因此对侧栏列与 DSH 一致的任何布局都成立;重绘原生行的皮肤可以使用 `[data-dsh-plugin="dsh-mnemon"][data-dsh-part="sidebar-entry"]`。截图仅包含 DSH 默认浅色主题。 diff --git a/dsh/docs/pr-assets/issue-318-sidebar-entry/measurements.json b/dsh/docs/pr-assets/issue-318-sidebar-entry/measurements.json new file mode 100644 index 000000000..4c0bb1f99 --- /dev/null +++ b/dsh/docs/pr-assets/issue-318-sidebar-entry/measurements.json @@ -0,0 +1,404 @@ +{ + "issue": "https://github.com/omdsh-dev/dsh-mnemon/issues/318", + "date": "2026-10-01", + "revisions": { + "baseline": "dfb3196cbcea58fb9b36b7283ccac4662d366858", + "fix": "1d18173aa88933528860ac6b40b9dcdf001b5726" + }, + "environment": { + "os": "macOS 15.6 arm64", + "node": "24.19.0", + "dsh": "@deepseek-ai/dsh 0.1.7-rc.2 (repository pin); dsh-client-ui-sidebar .panelRow rules are identical in 0.2.0-rc.2", + "viewport": "1440x900, device scale 2, light, zh-CN" + }, + "method": "pnpm e2e:serve shows the native seat, so the page already has DSH's own Memory System row. The fallback entry mounts only in layouts without that seat, so the check builds the element the fallback builds on the live page: the classes of the loaded Mnemon stylesheet (NS3bAW_entry, _entryIcon, _entryLabel), the shared brain icon at the size the fallback passes (18 on main, 16 with the fix), inserted under New Session as placeEntry does. Values are getComputedStyle and getBoundingClientRect with every row present; iconLeft and labelLeft are relative to the sidebar column. \"open\" selects the native Memory System row and marks the fallback as the fallback marks itself. The screenshots hide the native Memory System row, so each shows one Memory System entry beside Plugins, as a fallback layout does.", + "rows": { + "plugins": "DSH native Plugins row", + "nativeMemory": "Memory System through the native seat (DSH renders the row)", + "fallback": "Memory System fallback entry" + }, + "baseline": { + "expanded": { + "plugins": { + "fontSize": "14px", + "fontWeight": "400", + "lineHeight": "22px", + "color": "rgb(15, 17, 21)", + "background": "rgba(0, 0, 0, 0)", + "padding": "7px 8px", + "margin": "0px 2px", + "borderRadius": "12px", + "width": 252, + "height": 36, + "icon": [ + 16, + 16 + ], + "strokeWidth": "1", + "iconLeft": 22, + "labelLeft": 46, + "ariaCurrent": null + }, + "nativeMemory": { + "fontSize": "14px", + "fontWeight": "400", + "lineHeight": "22px", + "color": "rgb(15, 17, 21)", + "background": "rgba(0, 0, 0, 0)", + "padding": "7px 8px", + "margin": "0px 2px", + "borderRadius": "12px", + "width": 252, + "height": 36, + "icon": [ + 16, + 16 + ], + "strokeWidth": "1", + "iconLeft": 22, + "labelLeft": 46, + "ariaCurrent": null + }, + "fallback": { + "fontSize": "13px", + "fontWeight": "400", + "lineHeight": "normal", + "color": "rgb(97, 102, 107)", + "background": "rgba(0, 0, 0, 0)", + "padding": "0px 10px", + "margin": "0px", + "borderRadius": "8px", + "width": 256, + "height": 36, + "icon": [ + 18, + 18 + ], + "strokeWidth": "1", + "iconLeft": 25, + "labelLeft": 54, + "ariaCurrent": null + } + }, + "open": { + "plugins": { + "fontSize": "14px", + "fontWeight": "400", + "lineHeight": "22px", + "color": "rgb(15, 17, 21)", + "background": "rgba(0, 0, 0, 0)", + "padding": "7px 8px", + "margin": "0px 2px", + "borderRadius": "12px", + "width": 252, + "height": 36, + "icon": [ + 16, + 16 + ], + "strokeWidth": "1", + "iconLeft": 22, + "labelLeft": 46, + "ariaCurrent": null + }, + "nativeMemory": { + "fontSize": "14px", + "fontWeight": "400", + "lineHeight": "22px", + "color": "rgb(15, 17, 21)", + "background": "rgba(38, 49, 72, 0.06)", + "padding": "7px 8px", + "margin": "0px 2px", + "borderRadius": "12px", + "width": 252, + "height": 36, + "icon": [ + 16, + 16 + ], + "strokeWidth": "1", + "iconLeft": 22, + "labelLeft": 46, + "ariaCurrent": "page" + }, + "fallback": { + "fontSize": "13px", + "fontWeight": "600", + "lineHeight": "normal", + "color": "rgb(15, 17, 21)", + "background": "rgba(38, 49, 72, 0.1)", + "padding": "0px 10px", + "margin": "0px", + "borderRadius": "8px", + "width": 256, + "height": 36, + "icon": [ + 18, + 18 + ], + "strokeWidth": "1", + "iconLeft": 25, + "labelLeft": 54, + "ariaCurrent": null + } + }, + "collapsed": { + "plugins": { + "fontSize": "14px", + "fontWeight": "400", + "lineHeight": "22px", + "color": "rgb(15, 17, 21)", + "background": "rgba(0, 0, 0, 0)", + "padding": "0px", + "margin": "0px", + "borderRadius": "12px", + "width": 36, + "height": 36, + "icon": [ + 18, + 18 + ], + "strokeWidth": "1", + "iconLeft": 19, + "labelLeft": null, + "ariaCurrent": null + }, + "nativeMemory": { + "fontSize": "14px", + "fontWeight": "400", + "lineHeight": "22px", + "color": "rgb(15, 17, 21)", + "background": "rgba(38, 49, 72, 0.06)", + "padding": "0px", + "margin": "0px", + "borderRadius": "12px", + "width": 36, + "height": 36, + "icon": [ + 18, + 18 + ], + "strokeWidth": "1", + "iconLeft": 19, + "labelLeft": null, + "ariaCurrent": "page" + }, + "fallback": { + "fontSize": "13px", + "fontWeight": "600", + "lineHeight": "normal", + "color": "rgb(15, 17, 21)", + "background": "rgba(38, 49, 72, 0.1)", + "padding": "0px", + "margin": "0px 0px 12px", + "borderRadius": "50%", + "width": 36, + "height": 36, + "icon": [ + 20, + 20 + ], + "strokeWidth": "1", + "iconLeft": 18, + "labelLeft": 0, + "ariaCurrent": null + } + } + }, + "fix": { + "expanded": { + "plugins": { + "fontSize": "14px", + "fontWeight": "400", + "lineHeight": "22px", + "color": "rgb(15, 17, 21)", + "background": "rgba(0, 0, 0, 0)", + "padding": "7px 8px", + "margin": "0px 2px", + "borderRadius": "12px", + "width": 252, + "height": 36, + "icon": [ + 16, + 16 + ], + "strokeWidth": "1", + "iconLeft": 22, + "labelLeft": 46, + "ariaCurrent": null + }, + "nativeMemory": { + "fontSize": "14px", + "fontWeight": "400", + "lineHeight": "22px", + "color": "rgb(15, 17, 21)", + "background": "rgba(0, 0, 0, 0)", + "padding": "7px 8px", + "margin": "0px 2px", + "borderRadius": "12px", + "width": 252, + "height": 36, + "icon": [ + 16, + 16 + ], + "strokeWidth": "1", + "iconLeft": 22, + "labelLeft": 46, + "ariaCurrent": null + }, + "fallback": { + "fontSize": "14px", + "fontWeight": "400", + "lineHeight": "22px", + "color": "rgb(15, 17, 21)", + "background": "rgba(0, 0, 0, 0)", + "padding": "7px 8px", + "margin": "0px 2px", + "borderRadius": "12px", + "width": 252, + "height": 36, + "icon": [ + 16, + 16 + ], + "strokeWidth": "1", + "iconLeft": 22, + "labelLeft": 46, + "ariaCurrent": null + } + }, + "open": { + "plugins": { + "fontSize": "14px", + "fontWeight": "400", + "lineHeight": "22px", + "color": "rgb(15, 17, 21)", + "background": "rgba(0, 0, 0, 0)", + "padding": "7px 8px", + "margin": "0px 2px", + "borderRadius": "12px", + "width": 252, + "height": 36, + "icon": [ + 16, + 16 + ], + "strokeWidth": "1", + "iconLeft": 22, + "labelLeft": 46, + "ariaCurrent": null + }, + "nativeMemory": { + "fontSize": "14px", + "fontWeight": "400", + "lineHeight": "22px", + "color": "rgb(15, 17, 21)", + "background": "rgba(38, 49, 72, 0.06)", + "padding": "7px 8px", + "margin": "0px 2px", + "borderRadius": "12px", + "width": 252, + "height": 36, + "icon": [ + 16, + 16 + ], + "strokeWidth": "1", + "iconLeft": 22, + "labelLeft": 46, + "ariaCurrent": "page" + }, + "fallback": { + "fontSize": "14px", + "fontWeight": "400", + "lineHeight": "22px", + "color": "rgb(15, 17, 21)", + "background": "rgba(38, 49, 72, 0.06)", + "padding": "7px 8px", + "margin": "0px 2px", + "borderRadius": "12px", + "width": 252, + "height": 36, + "icon": [ + 16, + 16 + ], + "strokeWidth": "1", + "iconLeft": 22, + "labelLeft": 46, + "ariaCurrent": "page" + } + }, + "collapsed": { + "plugins": { + "fontSize": "14px", + "fontWeight": "400", + "lineHeight": "22px", + "color": "rgb(15, 17, 21)", + "background": "rgba(0, 0, 0, 0)", + "padding": "0px", + "margin": "0px", + "borderRadius": "12px", + "width": 36, + "height": 36, + "icon": [ + 18, + 18 + ], + "strokeWidth": "1", + "iconLeft": 19, + "labelLeft": null, + "ariaCurrent": null + }, + "nativeMemory": { + "fontSize": "14px", + "fontWeight": "400", + "lineHeight": "22px", + "color": "rgb(15, 17, 21)", + "background": "rgba(38, 49, 72, 0.06)", + "padding": "0px", + "margin": "0px", + "borderRadius": "12px", + "width": 36, + "height": 36, + "icon": [ + 18, + 18 + ], + "strokeWidth": "1", + "iconLeft": 19, + "labelLeft": null, + "ariaCurrent": "page" + }, + "fallback": { + "fontSize": "14px", + "fontWeight": "400", + "lineHeight": "22px", + "color": "rgb(15, 17, 21)", + "background": "rgba(38, 49, 72, 0.06)", + "padding": "0px", + "margin": "0px 0px 12px", + "borderRadius": "12px", + "width": 36, + "height": 36, + "icon": [ + 18, + 18 + ], + "strokeWidth": "1", + "iconLeft": 19, + "labelLeft": 0, + "ariaCurrent": "page" + } + } + }, + "screenshots": { + "after-collapsed.jpg": "04331aa7a4c37f17a34453d74841fbf306a5278ddf00e15d90c1b3a7edbd419a", + "after-expanded.jpg": "3cc795239e48b93a7d622864c27d0130e60161810788a4db7cb30b1b5583886e", + "after-open.jpg": "2506439a12e1addabdb1672db92c62e9c0737596ae17b9540ff888f6566a4738", + "before-collapsed.jpg": "4a39ba6c3426a07e40e6db6cf258db6094e4771d3a189c918a3c3e1f5c64a5ba", + "before-expanded.jpg": "baf04842c54ffe494b677458bd4e67b0660d35664efab4b192f29227d971c729", + "before-open.jpg": "5dd74392f7d9c1b83298d068782fa4528b9372be394eb1e15c1ed2ed515a5b82" + } +} diff --git a/dsh/docs/pr-assets/issue-319-review-layers/README.md b/dsh/docs/pr-assets/issue-319-review-layers/README.md new file mode 100644 index 000000000..008d4f390 --- /dev/null +++ b/dsh/docs/pr-assets/issue-319-review-layers/README.md @@ -0,0 +1,83 @@ +# Idle review writes one layer — issue #319 + +[简体中文](./README.zh-CN.md) | [Issue #319](https://github.com/omdsh-dev/dsh-mnemon/issues/319) | [Verification data](./verification.json) + +On main, one idle review pass records a project fact as a Document and again as a working-memory entry. With the fix, the same scripted pass creates the Document, and the host refuses the working-memory copy, so MEMORY.md stays empty. With **Write runtime memory** off, review is not offered the runtime memory tool at all. + +Baseline: `dfb3196cbcea58fb9b36b7283ccac4662d366858` (main, the root package as published in 0.5.20). Fix: `3eb6eb7dd30fd47bfcfebb9e361479c1e58653ad`. The fixture and the `--review-layers` flag come from the fix branch and are identical in all three runs. + +The 2026-10-01 runs use macOS 15.6 arm64, Node 24.19.0, pnpm 11.19.0 and the repository's DSH 0.1.7-rc.2. Each run owns a disposable `DSH_HOME`, data directory, workspace and loopback model stub. No model is called and no personal memory is read. + +## Setup + +```sh +pnpm build +pnpm --workspace-concurrency=4 -r build +pnpm e2e:serve --review-layers +``` + +In a new conversation, send the two turns recorded in the [verification data](./verification.json). Together they pass review admission. The flag shortens the idle window to 5 s and allows one review per conversation. The [fixture](../../../scripts/fixtures/review-layers-model.mjs) scripts only the review's choices: +1. search Documents; +2. create one Document; +3. when the runtime memory tool is offered, add the same fact to working memory (`target=memory`); +4. finish. + +Tool dispatch, the review guard, the Sources and the WebUI are real. + +![The two turns before idle review](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-319-review-layers/conversation.jpg) + +## Baseline + +| Working memory | Documents | +|---|---| +| ![MEMORY.md holds the same rule](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-319-review-layers/before-runtime.jpg) | ![The Document Boot account resolution](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-319-review-layers/before-documents.jpg) | + +The pass creates **Boot account resolution**, then adds the same rule to MEMORY.md (`Entry added.`). The project fact now sits in both layers, and MEMORY.md, which every turn loads, holds a repository detail. + +## Cause and fix + +The review persona sent stable project facts, decisions and conventions to `target=memory`, while the routing guidance keeps hot memory for new user-supplied facts. The Document branch searched first; the hot-memory branch had no such check, so nothing stopped the same knowledge from landing in both layers. + +- **Persona.** Review considers a Document first. MEMORY.md takes only a compact rule the user stated, such as a convention, a correction, an environment fact or a tool quirk, that no Document or entry already covers. Project records never qualify, such as a design, implementation details, paths, ports, accounts, scope agreements or handoffs. +- **Guard.** The review guard holds each pass to one layer. The first Document creation or working-memory change it admits claims the pass, and the other layer is refused even if that first call fails. USER.md changes stay independent. +- **Switch.** `idleReview.runtimeMemory` (**Write runtime memory** under Idle review, on by default) withholds `mnemon_runtime_memory` from review. +- **Memory Spaces.** Review never writes them directly. Working memory reaches them through capacity archiving and Documents through cold archiving; the docs now say so. + +## Fixed build + +| Working memory | Documents | +|---|---| +| ![MEMORY.md stays empty](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-319-review-layers/after-runtime.jpg) | ![The same Document](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-319-review-layers/after-documents.jpg) | + +The same pass creates the Document. The working-memory add then returns: + +```text +This idle review already created a Document, so it cannot also change working memory (target=memory). Finish with the result tool. +``` + +MEMORY.md stays empty, and the review finishes with the Document's id. + +## Write runtime memory off + +![Write runtime memory switched off on the Layered strategy's page](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-319-review-layers/switch-off.jpg) + +The switch sits under **Idle review** on the Layered strategy's page (**Plugins → dsh-mnemon**). With it off, the review's tool list has no `mnemon_runtime_memory`. The fixture creates the Document and finishes, and MEMORY.md stays empty. None of the three runs logged a console error. + +## Automated checks + +- `pnpm run verify` passes. It covers docs links, typecheck, the deterministic build, build, typecheck and tests for all 17 plugins, and root tests (113 files, 1,559 passed, 6 skipped). It also covers Headless activation, package contents, public entries, publint and attw. +- The package measures 1,498,810 bytes, and its budget moves to 1,501,000. +- New tests cover: + - the layer policy: both orders, all three actions, independent USER.md changes, retries within the claimed layer, calls without `target=memory`, PTC sub-dispatch, and the allowlist checked first; + - the coordinator attaching the policy to the review child's guard; + - the persona's rules and order; + - the switch withholding the tool; + - the config default and validation; + - the settings switch writing `idleReview.runtimeMemory` at once and hiding with review. + +## Limits + +- Model choices are scripted, so these runs show what the host allows, not how often a real model repeats itself. The persona steers real models; the guard holds either way. +- A pass that produces both a Document and an unrelated rule the user stated keeps only the first write. A later review can record the other. +- The runs use the repository's DSH 0.1.7-rc.2. The guard relies on the same `agent.ctx.tools.guard` support that review already requires. +- The screenshots contain synthetic content only. diff --git a/dsh/docs/pr-assets/issue-319-review-layers/README.zh-CN.md b/dsh/docs/pr-assets/issue-319-review-layers/README.zh-CN.md new file mode 100644 index 000000000..27bc27eb5 --- /dev/null +++ b/dsh/docs/pr-assets/issue-319-review-layers/README.zh-CN.md @@ -0,0 +1,83 @@ +# 空闲审查只写一层 — issue #319 + +[English](./README.md) | [Issue #319](https://github.com/omdsh-dev/dsh-mnemon/issues/319) | [验证数据](./verification.json) + +在 main 上,同一轮空闲审查会把一条项目事实先写成项目档案,再写一遍工作记忆。修复后,同样的脚本化审查建好项目档案,宿主拒绝写入工作记忆的副本,MEMORY.md 保持为空。关闭**写入运行时记忆**后,审查根本拿不到运行时记忆工具。 + +基线:`dfb3196cbcea58fb9b36b7283ccac4662d366858`(main,根包即 0.5.20 发布的版本)。修复:`3eb6eb7dd30fd47bfcfebb9e361479c1e58653ad`。fixture 与 `--review-layers` 参数来自修复分支,三次运行完全相同。 + +2026-10-01 的运行环境为 macOS 15.6 arm64、Node 24.19.0、pnpm 11.19.0 与仓库锁定的 DSH 0.1.7-rc.2。每次运行使用一次性的 `DSH_HOME`、数据目录、工作区和本机模型桩,不调用模型,也不读取个人记忆。 + +## 准备 + +```sh +pnpm build +pnpm --workspace-concurrency=4 -r build +pnpm e2e:serve --review-layers +``` + +在新对话中发送[验证数据](./verification.json)里记录的两轮消息,两轮合起来满足审查的准入条件。该参数把空闲等待缩短到 5 秒,每个对话只审查一次。[fixture](../../../scripts/fixtures/review-layers-model.mjs) 只脚本化审查的选择: +1. 检索项目档案; +2. 新建一份项目档案; +3. 有运行时记忆工具时,把同一事实写入工作记忆(`target=memory`); +4. 结束。 + +工具调度、审查 guard、各 Source 与 WebUI 都是真实运行的。 + +![空闲审查前的两轮对话](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-319-review-layers/conversation.jpg) + +## 基线 + +| 工作记忆 | 项目档案 | +|---|---| +| ![MEMORY.md 中出现同一条规则](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-319-review-layers/before-runtime.jpg) | ![项目档案 Boot account resolution](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-319-review-layers/before-documents.jpg) | + +这一轮审查建好 **Boot account resolution** 后,又把同一条规则写入 MEMORY.md(`Entry added.`)。同一项目事实因此同时出现在两层,而每轮都会加载的 MEMORY.md 里多了一条仓库细节。 + +## 原因与修复 + +审查 persona 把稳定的项目事实、决定和约定写到 `target=memory`,而路由提示只把用户新提供的事实放进热记忆。项目档案分支会先检索,热记忆分支没有对应检查,因此没有任何机制阻止同一知识落到两层。 + +- **Persona。**审查先考虑项目档案。MEMORY.md 只收用户明确说过的简短规则,例如约定、纠正、环境事实或工具特性,且项目档案和已有条目都未涵盖。项目记录从不进入,例如设计、实现细节、路径、端口、账号、范围约定或交接说明。 +- **Guard。**审查 guard 让每轮只写一层。它放行的第一次新建档案或工作记忆修改决定本轮所写的层,另一层随后被拒绝,即使第一次调用失败也是如此。USER.md 的修改不受影响。 +- **开关。**`idleReview.runtimeMemory`(空闲审查中的**写入运行时记忆**,默认开启)会从审查中收回 `mnemon_runtime_memory`。 +- **记忆空间。**审查从不直接写入记忆空间。工作记忆在容量整理时归档进去,项目档案在冷归档时建立索引;文档现已写明这一点。 + +## 修复后 + +| 工作记忆 | 项目档案 | +|---|---| +| ![MEMORY.md 保持为空](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-319-review-layers/after-runtime.jpg) | ![同一份项目档案](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-319-review-layers/after-documents.jpg) | + +同一轮审查建好项目档案后,写入工作记忆的请求返回: + +```text +This idle review already created a Document, so it cannot also change working memory (target=memory). Finish with the result tool. +``` + +MEMORY.md 保持为空,审查以这份项目档案的 id 结束。 + +## 关闭写入运行时记忆 + +![分层策略页面中关闭写入运行时记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-319-review-layers/switch-off.jpg) + +该开关位于分层策略页面(**插件 → 可组合记忆**)的**空闲审查**之下。关闭后,审查的工具列表中没有 `mnemon_runtime_memory`。fixture 建好项目档案后直接结束,MEMORY.md 保持为空。三次运行都没有控制台错误。 + +## 自动化检查 + +- `pnpm run verify` 通过。它覆盖文档链接、类型检查、确定性构建、全部 17 个插件的构建、类型检查与测试,以及根测试(113 个文件,1,559 项通过,6 项跳过);还覆盖 Headless 激活、包内容、公共入口、publint 与 attw。 +- 包大小为 1,498,810 字节,上限调整为 1,501,000。 +- 新增测试覆盖: + - 分层策略:两种顺序、三种操作、独立的 USER.md 修改、同一层内的重试、不带 `target=memory` 的调用、PTC 子调用,以及先检查白名单; + - 协调器把该策略挂到审查子代理的 guard 上; + - persona 的规则与顺序; + - 开关收回工具; + - 配置默认值与校验; + - 设置中的开关立即写入 `idleReview.runtimeMemory`,并随审查关闭而隐藏。 + +## 限制 + +- 模型选择是脚本化的,这些运行证明的是宿主允许什么,而不是真实模型多常重复写入。persona 约束真实模型,guard 在任何情况下都生效。 +- 如果一轮审查同时得到项目档案和一条无关的用户规则,只保留先写的那一层;另一条可由之后的审查记录。 +- 运行使用仓库锁定的 DSH 0.1.7-rc.2。guard 依赖审查原本就要求的 `agent.ctx.tools.guard` 能力。 +- 截图只包含合成内容。 diff --git a/dsh/docs/pr-assets/issue-319-review-layers/verification.json b/dsh/docs/pr-assets/issue-319-review-layers/verification.json new file mode 100644 index 000000000..e0f76c925 --- /dev/null +++ b/dsh/docs/pr-assets/issue-319-review-layers/verification.json @@ -0,0 +1,80 @@ +{ + "issue": "https://github.com/omdsh-dev/dsh-mnemon/issues/319", + "date": "2026-10-01", + "revisions": { + "baseline": "dfb3196cbcea58fb9b36b7283ccac4662d366858", + "fix": "3eb6eb7dd30fd47bfcfebb9e361479c1e58653ad", + "note": "The baseline builds the root package from main (0.5.20 code); the fixture script and serve-e2e flag come from the fix branch and are identical in all runs." + }, + "environment": { + "os": "macOS 15.6 arm64", + "node": "24.19.0", + "pnpm": "11.19.0", + "dsh": "@deepseek-ai/dsh 0.1.7-rc.2 (repository pin)", + "viewport": "1440x900, device scale 2, light, zh-CN" + }, + "fixture": { + "command": "pnpm e2e:serve --review-layers", + "config": { + "idleReviewMs": 5000, + "idleReview.minIntervalMs": 5000, + "idleReview.maxPerSession": 1 + }, + "model": "scripts/fixtures/review-layers-model.mjs scripts model choices only: search Documents, create one Document, add the same fact to working memory (target=memory) when the runtime tool is offered, then finish", + "turns": [ + "We settled the boot autostart design for the disposable project today: a boot entry must never run the panel as root. The boot script resolves the service account from the configured user first, then from the owner of the data directory, and it warns when neither is set.", + "The installer and the boot script share that account resolution, so both must change together. The warning should name both settings, so an operator can fix the configuration without reading the source." + ] + }, + "runs": [ + { + "label": "main", + "runtimeMemorySwitch": "on (default)", + "documentCreated": true, + "documentsListed": [ + "Boot account resolution" + ], + "secondWrite": "working-memory-written", + "secondWriteResult": "Entry added. (target=memory, entryCount 1)", + "workingMemoryEntries": [ + { + "target": "memory", + "content": "Boot entries never run the panel as root; the boot script resolves the service account from the configured user, then from the data directory owner." + } + ], + "consoleErrors": 0 + }, + { + "label": "fix", + "runtimeMemorySwitch": "on (default)", + "documentCreated": true, + "documentsListed": [ + "Boot account resolution" + ], + "secondWrite": "working-memory-refused", + "secondWriteResult": "Error: This idle review already created a Document, so it cannot also change working memory (target=memory). Finish with the result tool.", + "workingMemoryEntries": [], + "consoleErrors": 0 + }, + { + "label": "fix, Write runtime memory off", + "runtimeMemorySwitch": "off", + "documentCreated": true, + "documentsListed": [ + "Boot account resolution" + ], + "secondWrite": "runtime-memory-unavailable", + "secondWriteResult": "The review was not offered mnemon_runtime_memory.", + "workingMemoryEntries": [], + "consoleErrors": 0 + } + ], + "screenshots": { + "after-documents.jpg": "6f9058633171c09cb06ab42dd24c10600134973e50ac094b2811a8b9b89be692", + "after-runtime.jpg": "c600aa273a4c02ebdf4bd795e0af988e93edf63d5bffe0a88db12841bff2a5cc", + "before-documents.jpg": "10fc85ed0d510f628e03aaf264bf31e4927f7eff44b0d481f6b95c71964e16bf", + "before-runtime.jpg": "22d0c8e436613f6bb7abe2e1b11bc29d45657a28eb53a65278515cc757cbf72d", + "conversation.jpg": "7a74665984777a06538227f9cebc824636d4ec23ef4df416d5a09cad866f3c6b", + "switch-off.jpg": "a75aba4b7e4b5ab7e519badcce59515e052873915cbbc6e31243ca0ef321d2c3" + } +} diff --git a/dsh/docs/pr-assets/issue-320-native-store-dumps/README.md b/dsh/docs/pr-assets/issue-320-native-store-dumps/README.md new file mode 100644 index 000000000..82109df13 --- /dev/null +++ b/dsh/docs/pr-assets/issue-320-native-store-dumps/README.md @@ -0,0 +1,95 @@ +# Large Native Memory Spaces — issue #320 + +[简体中文](./README.zh-CN.md) | [Issue #320](https://github.com/omdsh-dev/dsh-mnemon/issues/320) | [Verification data](./verification.json) + +With a Mnemon Native store of 900 insights, the baseline cannot list the space, draw its graph or archive runtime memory at capacity. Each of these reads the whole store, and the output passes the 2 MiB cap on every Mnemon command; the error then suggests installing Mnemon. The fixed build lists 900 memories, draws the graph and archives the working-memory entries on the first try, and both archived originals read back byte for byte. + +Baseline: `dfb3196cbcea58fb9b36b7283ccac4662d366858` (main, with Memory Spaces 0.5.13 and Mnemon Native 0.5.7 as published in 0.5.20). Fix: `dd7289d57dddec408b60c11ccb3aed1b2409f0f1`. Only these two plugins differ between the runs. + +The 2026-10-01 runs use macOS 15.6 arm64, Node 24.19.0, pnpm 11.19.0, the repository's DSH 0.1.7-rc.2 and Mnemon CLI 0.2.9. Each run owns a disposable `DSH_HOME`, data directory, workspace and loopback model stub. No model is called and no personal memory is read. + +## Setup + +```sh +pnpm build +pnpm --workspace-concurrency=4 -r build +node docs/pr-assets/issue-320-native-store-dumps/large-store-draft.mjs /tmp/i320-draft.json +mnemon --data-dir /tmp/i320-store --store default import /tmp/i320-draft.json --no-diff +MNEMON_CLI_PATH=/absolute/path/to/mnemon pnpm e2e:serve --runtime-write-scope +``` + +The import takes several minutes. Before opening the WebUI, copy `/tmp/i320-store/data/default/mnemon.db` to `/data/data/default/mnemon.db`, where `` is the path the server prints at startup. Memory Spaces discovers it as the `default` space. `--runtime-write-scope` limits working memory to 512 bytes and archives to Mnemon Native. + +The [draft generator](./large-store-draft.mjs) writes 900 synthetic insights of about 3.3 KB each; the imported store has 14,956 edges. Whole-store commands on it exceed the 2,097,152-byte cap: + +| Command | Output bytes | Used by | +|---|---:|---| +| `recall "" --basic --limit 100000` | 3,248,516 | Contents list; exact-content check before archiving | +| `viz --format html --output -` | 5,687,735 | Graph | +| `search --limit 10` | 34,545 | Search | +| `status` | 1,652 | Space card and Status | + +Both runs take the same steps: the Memory Spaces overview and contents, then **Runtime Memory → Add memory** three times. Entries A (200 B) and B (210 B) bring working memory to 414 of 512 B, so entry C (205 B) needs capacity maintenance. With one eligible space, the Host routes the archive without a model. + +## Baseline + +| Graph | Contents | +|---|---| +| ![The space's graph reads connection unavailable](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-320-native-store-dumps/before-graph.jpg) | ![Contents reads 0 memories](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-320-native-store-dumps/before-contents.jpg) | + +The space card reads 900 memories because `status` is small. The graph section reads "connection unavailable" and says the space supports only on-demand queries. Contents reads 0 memories and suggests saving the first one. + +Adding entry C fails and the dialog stays open: + +```text +mnemon output exceeded 2097152 bytes. Install Mnemon and ensure "mnemon" is on PATH, or set MNEMON_CLI_PATH or mnemon.cliPath. +``` + +![Adding C fails with the install hint](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-320-native-store-dumps/before-capacity-error.jpg) + +Nothing is written: the store keeps 900 insights and working memory keeps A and B. Every later add that needs room fails the same way, so working memory stays full. + +## Cause and fix + +Mnemon Native reads the whole store in three places. Its contents list and the exact-content check before runtime memory archives both run `recall "" --basic --limit 100000`, and its graph runs `viz --format html --output -`. The Memory Spaces process runner stops a Mnemon command after 2 MiB of output and did not forward a per-call cap. Its error handler added the install hint to every failure. + +- Memory Spaces forwards `maxOutputBytes` to the process, and the Provider SDK's `MemorySpaceNativeRunOptions` carries it. Source hosts before this change ignore it. +- Mnemon Native allows 128 MiB for the two whole-store reads (`STORE_DUMP_MAX_OUTPUT_BYTES`). Every other command keeps 2 MiB. +- Process failures carry a reason. Only a failed launch adds the install hint; an oversized output reads, for example, `mnemon recall stopped: output exceeded 2097152 bytes`. + +The 2 MiB constant in the root package's `lib/index.js`, which the issue cites, belongs to plugin installation and version checks. It never runs Mnemon, so it is unchanged. + +## Fixed build + +| Graph | Contents | +|---|---| +| ![The graph of 900 memories](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-320-native-store-dumps/after-graph.jpg) | ![Contents lists 900 memories](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-320-native-store-dumps/after-contents.jpg) | + +The graph section reads 900 observable memories and 14,956 relations and draws the live snapshot. Contents lists 900 memories. + +Adding entry C succeeds on the first try: "容量整理完成:已先归档到记忆空间 default,再更新工作记忆" (capacity maintenance finished: archived to the `default` space, then updated working memory). Working memory holds only C at 205 of 512 B, and the space reads 902 memories and 14,958 relations. + +| Capacity maintenance | The space after archiving | +|---|---| +| ![Capacity maintenance archives to default](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-320-native-store-dumps/after-capacity-archived.jpg) | ![The space reads 902 memories](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-320-native-store-dumps/after-archived-space.jpg) | + +Read back with the Mnemon CLI in `--readonly` mode, the store holds 902 insights, and A and B equal the added entries as exact UTF-8 strings. `runtime/memories.json` holds only C, also exact. Neither run logged a console error. The [verification data](./verification.json) records the counts, page text, entry hashes and screenshot hashes. + +A direct Provider check with the real CLI covers the graph limit separately. On a store of 1,000 short insights, `recall ""` returns 782,779 bytes and the graph 5,148,040 bytes. The fixed Provider lists 1,000 insights, draws 1,000 nodes and 26,486 edges, and archives through `rememberMany`. + +## Automated checks + +- `pnpm run verify` passes. It covers docs links, typecheck, the deterministic build, build, typecheck and tests for all 17 plugins, and root tests (113 files, 1,549 passed, 6 skipped). It also covers Headless activation, package contents, public entries, publint and attw. +- Memory Spaces: 17 files pass and 2 are skipped (191 tests). New cases cover: + - each failure reason (launch, timeout, cancellation, output limit); + - a per-call cap above the default; + - cap forwarding in `runJson`, `runText` and `runTextBatch`; + - messages that add the install hint only after a failed launch. +- Mnemon Native: 9 tests pass. The 128 MiB cap applies to `list`, `graph` and the `rememberMany` snapshot, and never to import, search or status. + +## Limits + +- Windows was not run. The reporter measured on Windows 11 with the same Mnemon 0.2.9, and the cap does not depend on the platform. +- Both runs use the repository's DSH 0.1.7-rc.2. The change stays inside the two plugins and no DSH contract changes. +- Output beyond 128 MiB still stops, now with a message that names the cause. +- The screenshots contain synthetic content only. diff --git a/dsh/docs/pr-assets/issue-320-native-store-dumps/README.zh-CN.md b/dsh/docs/pr-assets/issue-320-native-store-dumps/README.zh-CN.md new file mode 100644 index 000000000..e3d405a65 --- /dev/null +++ b/dsh/docs/pr-assets/issue-320-native-store-dumps/README.zh-CN.md @@ -0,0 +1,95 @@ +# 大型 Native 记忆空间 — issue #320 + +[English](./README.md) | [Issue #320](https://github.com/omdsh-dev/dsh-mnemon/issues/320) | [验证数据](./verification.json) + +Mnemon Native 库有 900 条记忆时,基线版本无法列出空间内容、无法绘制图谱,运行时记忆写满后也无法归档。这三处都会读取整个库,输出超过了每条 Mnemon 命令 2 MiB 的上限,报错却提示去安装 Mnemon。修复后,空间列出 900 条记忆、图谱正常绘制,工作记忆首次尝试即完成归档,两条归档原文逐字节核对一致。 + +基线:`dfb3196cbcea58fb9b36b7283ccac4662d366858`(main,其中记忆空间 0.5.13 与 Mnemon Native 0.5.7 即 0.5.20 发布的版本)。修复:`dd7289d57dddec408b60c11ccb3aed1b2409f0f1`。两次运行只有这两个插件不同。 + +2026-10-01 的运行环境为 macOS 15.6 arm64、Node 24.19.0、pnpm 11.19.0、仓库锁定的 DSH 0.1.7-rc.2 与 Mnemon CLI 0.2.9。每次运行使用一次性的 `DSH_HOME`、数据目录、工作区和本机模型桩,不调用模型,也不读取个人记忆。 + +## 准备 + +```sh +pnpm build +pnpm --workspace-concurrency=4 -r build +node docs/pr-assets/issue-320-native-store-dumps/large-store-draft.mjs /tmp/i320-draft.json +mnemon --data-dir /tmp/i320-store --store default import /tmp/i320-draft.json --no-diff +MNEMON_CLI_PATH=/absolute/path/to/mnemon pnpm e2e:serve --runtime-write-scope +``` + +导入需要几分钟。打开 WebUI 之前,把 `/tmp/i320-store/data/default/mnemon.db` 复制到 `/data/data/default/mnemon.db`,`` 是服务启动时打印的路径。记忆空间会把它识别为 `default` 空间。`--runtime-write-scope` 把工作记忆上限设为 512 字节,并归档到 Mnemon Native。 + +[草稿生成脚本](./large-store-draft.mjs)写出 900 条合成记忆,每条约 3.3 KB;导入后的库有 14,956 条连接。读取整个库的命令都超过 2,097,152 字节的上限: + +| 命令 | 输出字节 | 用途 | +|---|---:|---| +| `recall "" --basic --limit 100000` | 3,248,516 | 内容列表;归档前的逐字比对 | +| `viz --format html --output -` | 5,687,735 | 图谱 | +| `search --limit 10` | 34,545 | 检索 | +| `status` | 1,652 | 空间卡片与状态页 | + +两次运行步骤相同:先看记忆空间的概览和内容,再在 **运行时记忆 → 添加记忆** 中添加三条。A(200 B)和 B(210 B)使工作记忆达到 414 / 512 B,因此 C(205 B)需要容量整理。只有一个可用空间时,Host 直接分配归档目标,不需要模型。 + +## 基线 + +| 图谱 | 内容 | +|---|---| +| ![空间图谱显示连接不可用](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-320-native-store-dumps/before-graph.jpg) | ![内容显示 0 条记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-320-native-store-dumps/before-contents.jpg) | + +空间卡片显示 900 条记忆,因为 `status` 的输出很小。图谱区域显示“连接不可用”,并称该空间只支持按需查询。内容页显示 0 条记忆,并建议先存入第一条记忆。 + +添加 C 失败,对话框保持打开: + +```text +mnemon output exceeded 2097152 bytes. Install Mnemon and ensure "mnemon" is on PATH, or set MNEMON_CLI_PATH or mnemon.cliPath. +``` + +![添加 C 失败并提示安装](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-320-native-store-dumps/before-capacity-error.jpg) + +没有任何写入:库仍是 900 条,工作记忆仍是 A 和 B。之后每次需要腾出空间的添加都以同样方式失败,工作记忆因此一直是满的。 + +## 原因与修复 + +Mnemon Native 在三处读取整个库:内容列表和运行时记忆归档前的逐字比对都执行 `recall "" --basic --limit 100000`,图谱执行 `viz --format html --output -`。记忆空间的进程 runner 在 Mnemon 命令输出超过 2 MiB 时终止它,而且没有转发单次调用的上限;它的错误处理还会给每种失败都加上安装提示。 + +- 记忆空间把 `maxOutputBytes` 传给进程,Provider SDK 的 `MemorySpaceNativeRunOptions` 提供该字段。此前版本的 Source 宿主会忽略它。 +- Mnemon Native 为两次整库读取放宽到 128 MiB(`STORE_DUMP_MAX_OUTPUT_BYTES`),其余命令仍为 2 MiB。 +- 进程失败会带上原因。只有启动失败才附加安装提示;输出超限时显示的例如 `mnemon recall stopped: output exceeded 2097152 bytes`。 + +Issue 中提到的根包 `lib/index.js` 里的 2 MiB 常量属于插件安装和版本检查,从不运行 Mnemon,因此保持不变。 + +## 修复后 + +| 图谱 | 内容 | +|---|---| +| ![900 条记忆的图谱](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-320-native-store-dumps/after-graph.jpg) | ![内容列出 900 条记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-320-native-store-dumps/after-contents.jpg) | + +图谱区域显示 900 条可观察记忆与 14,956 条关系,并绘制实时快照。内容页列出 900 条记忆。 + +添加 C 首次即成功:“容量整理完成:已先归档到记忆空间 default,再更新工作记忆”。工作记忆只剩 C,为 205 / 512 B;空间显示 902 条记忆与 14,958 条关系。 + +| 容量整理 | 归档后的空间 | +|---|---| +| ![容量整理归档到 default](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-320-native-store-dumps/after-capacity-archived.jpg) | ![空间显示 902 条记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-320-native-store-dumps/after-archived-space.jpg) | + +用 Mnemon CLI 以 `--readonly` 模式读回,库中有 902 条记忆,A 和 B 与添加的内容按 UTF-8 字符串完全一致。`runtime/memories.json` 只保留 C,同样完全一致。两次运行都没有控制台错误。[验证数据](./verification.json)记录了数量、页面文字、条目哈希与截图哈希。 + +另有一次直接调用 Provider 的真实 CLI 检查,单独覆盖图谱上限:1,000 条短记忆的库,`recall ""` 返回 782,779 字节,图谱 5,148,040 字节。修复后的 Provider 列出 1,000 条记忆,绘制 1,000 个节点和 26,486 条连接,并通过 `rememberMany` 完成归档。 + +## 自动化检查 + +- `pnpm run verify` 通过。它覆盖文档链接、类型检查、确定性构建、全部 17 个插件的构建、类型检查与测试,以及根测试(113 个文件,1,549 项通过,6 项跳过);还覆盖 Headless 激活、包内容、公共入口、publint 与 attw。 +- 记忆空间:17 个文件通过,2 个跳过(191 项测试)。新增用例覆盖: + - 每种失败原因(启动、超时、取消、输出超限); + - 高于默认值的单次调用上限; + - `runJson`、`runText` 与 `runTextBatch` 对上限的转发; + - 只有启动失败时才附加安装提示。 +- Mnemon Native:9 项测试通过。128 MiB 上限只用于 `list`、`graph` 与 `rememberMany` 的快照,不用于导入、检索和状态。 + +## 限制 + +- 未在 Windows 上运行。报告者在 Windows 11 上使用同样的 Mnemon 0.2.9 测得数据,而该上限与平台无关。 +- 两次运行都使用仓库锁定的 DSH 0.1.7-rc.2。改动只在两个插件内部,不改变任何 DSH 契约。 +- 超过 128 MiB 的输出仍会中止,但报错会写明原因。 +- 截图只包含合成内容。 diff --git a/dsh/docs/pr-assets/issue-320-native-store-dumps/large-store-draft.mjs b/dsh/docs/pr-assets/issue-320-native-store-dumps/large-store-draft.mjs new file mode 100644 index 000000000..8659c414a --- /dev/null +++ b/dsh/docs/pr-assets/issue-320-native-store-dumps/large-store-draft.mjs @@ -0,0 +1,16 @@ +// Write the synthetic Mnemon draft used by the issue #320 record: 900 insights of +// about 3.3 KB each, four categories, one tag and one entity per insight. +// Usage: node large-store-draft.mjs +// Import: mnemon --data-dir --store default import --no-diff +import { writeFileSync } from 'node:fs' + +const [target] = process.argv.slice(2) +if (target === undefined) throw new Error('Usage: node large-store-draft.mjs ') +const words = 'checkout latency cache queue consumer kafka clickhouse deploy rollback schema index migration payment session token budget review release docs runtime archive space provider recall graph entity edge import export window desktop plugin install'.split(' ') +const pick = (i, n) => Array.from({ length: n }, (_, k) => words[(i * 7 + k * 13) % words.length]).join(' ') +const insights = Array.from({ length: 900 }, (_, i) => ({ + content: `Handoff ${i}: ` + Array.from({ length: 6 }, (_, p) => `Paragraph ${p} for service-${i % 41}: ${pick(i + p, 70)}.`).join(' '), + category: ['fact', 'decision', 'insight', 'context'][i % 4], importance: 1 + (i % 5), source: 'user', + tags: [`area-${i % 11}`], entities: [`Service${i % 41}`], +})) +writeFileSync(target, JSON.stringify({ schema_version: '1', source: 'i320-big', insights })) diff --git a/dsh/docs/pr-assets/issue-320-native-store-dumps/verification.json b/dsh/docs/pr-assets/issue-320-native-store-dumps/verification.json new file mode 100644 index 000000000..c492025a3 --- /dev/null +++ b/dsh/docs/pr-assets/issue-320-native-store-dumps/verification.json @@ -0,0 +1,166 @@ +{ + "issue": "https://github.com/omdsh-dev/dsh-mnemon/issues/320", + "date": "2026-10-01", + "revisions": { + "baseline": "dfb3196cbcea58fb9b36b7283ccac4662d366858", + "fix": "dd7289d57dddec408b60c11ccb3aed1b2409f0f1", + "note": "The baseline builds dsh-mnemon-source-memory-spaces 0.5.13 and dsh-mnemon-provider-mnemon-native 0.5.7 from main, the code published in 0.5.20; only these two plugins differ between the runs." + }, + "environment": { + "os": "macOS 15.6 arm64", + "node": "24.19.0", + "pnpm": "11.19.0", + "dsh": "@deepseek-ai/dsh 0.1.7-rc.2 (repository pin)", + "mnemon": "0.2.9", + "viewport": "1440x900, device scale 2, light, zh-CN" + }, + "fixture": { + "command": "MNEMON_CLI_PATH= pnpm e2e:serve --runtime-write-scope", + "runtimeMemoryLimitBytes": 512, + "persistence": "manual, providerId mnemon-native", + "store": { + "draft": "large-store-draft.mjs", + "draftSha256": "f0e8fec0f9e6727dbc6c7ad6706de539d46f30d6c1c213d24422d409750bbfa7", + "import": "mnemon --data-dir --store default import --no-diff", + "insights": 900, + "edges": 14956, + "placedAs": "/data/data/default/mnemon.db before the first page load" + } + }, + "wholeStoreOutputBytes": { + "cap": 2097152, + "recallAllBasic": 3248516, + "vizHtml": 5687735, + "searchLimit10": 34545, + "status": 1652 + }, + "steps": [ + "Memory System > Memory Spaces > Overview", + "Memory Spaces > Contents", + "Runtime Memory > Add memory: entries A (200 B) and B (210 B) bring working memory to 414 of 512 B; entry C (205 B) then needs capacity maintenance", + "Memory Spaces > Overview again" + ], + "entries": { + "A": { + "bytes": 200, + "sha256": "a650c18d00d234adccf3c27f7b1169d8e0d703396553249672918f3fab67d1a1" + }, + "B": { + "bytes": 210, + "sha256": "ee0f2b17fbe732e4e81c4c4b10050d5ce59eec4dbc626e002862c4fdc4968cbd" + }, + "C": { + "bytes": 205, + "sha256": "c56aeeec142cb17a483daac416879aeb40dc11784f133aebda790151ee7b14c8" + } + }, + "baseline": { + "label": "baseline", + "store": { + "before": { + "insights": 900, + "edges": 14956, + "recallAllBytes": 3248516 + }, + "after": { + "insights": 900, + "edges": 14956 + } + }, + "contents": { + "count": "0 条记忆", + "providerRow": "连接不可用" + }, + "graph": { + "scopeRow": "连接不可用", + "liveSnapshot": false + }, + "runtimeAdds": [ + { + "entry": "A", + "dialogStillOpen": false, + "notice": "已添加到工作记忆 · 当前 1 条" + }, + { + "entry": "B", + "dialogStillOpen": false, + "notice": "已添加到工作记忆 · 当前 2 条" + }, + { + "entry": "C", + "dialogStillOpen": true, + "notice": "mnemon output exceeded 2097152 bytes. Install Mnemon and ensure \"mnemon\" is on PATH, or set MNEMON_CLI_PATH or mnemon.cliPath." + } + ], + "archivedExactInNative": { + "A": false, + "B": false, + "C": false + }, + "runtimeEntriesAfter": [ + "A", + "B" + ], + "spaceAfterCapacity": "900 条记忆", + "consoleErrors": 0 + }, + "fix": { + "label": "fix", + "store": { + "before": { + "insights": 900, + "edges": 14956, + "recallAllBytes": 3248516 + }, + "after": { + "insights": 902, + "edges": 14958 + } + }, + "contents": { + "count": "900 条记忆", + "providerRow": "900 条可观察" + }, + "graph": { + "scopeRow": "900 条可观察 · 14956 条真实关系", + "liveSnapshot": true + }, + "runtimeAdds": [ + { + "entry": "A", + "dialogStillOpen": false, + "notice": "已添加到工作记忆 · 当前 1 条" + }, + { + "entry": "B", + "dialogStillOpen": false, + "notice": "已添加到工作记忆 · 当前 2 条" + }, + { + "entry": "C", + "dialogStillOpen": false, + "notice": "容量整理完成:已先归档到记忆空间 default,再更新工作记忆 · 当前 1 条" + } + ], + "archivedExactInNative": { + "A": true, + "B": true, + "C": false + }, + "runtimeEntriesAfter": [ + "C" + ], + "spaceAfterCapacity": "902 条记忆", + "consoleErrors": 0 + }, + "readback": "mnemon --readonly status and recall \"Issue 320 checkpoint\" --basic on the fixture store; runtime/memories.json read from disk. Content equality is exact on UTF-8 strings.", + "screenshots": { + "after-archived-space.jpg": "cf835d9212a40e6b876855acbfa8bbb027021865e86f8917415b6c078a31a431", + "after-capacity-archived.jpg": "ff64ad0981b4fee8ac4ba7f40476d572ef1e6111d632050c7b8aef2db9ee8de4", + "after-contents.jpg": "9d68181bafc716278e7927760a07373d91482ceb17bdc8926855c7bde64ed882", + "after-graph.jpg": "ec780fe7d5005cae1adc4f06cd2c0437809f1ed5dfd0d1239e5861f69ccd7442", + "before-capacity-error.jpg": "2d5580b2008941802ff1c0dac91879c4f4ad4daabf0bec12689e76ced123d211", + "before-contents.jpg": "9c149fd327b32ec09857255beb19873e063ba8942605f953f4c34b7631743bdd", + "before-graph.jpg": "71234ecf108bdd4b4bc7ede98b3b43d95c1bd3bfd25ab5e989b6813fcaf6e6a9" + } +} diff --git a/dsh/docs/pr-assets/issue-327-subagent-user-turn/README.md b/dsh/docs/pr-assets/issue-327-subagent-user-turn/README.md new file mode 100644 index 000000000..b5ae3b33a --- /dev/null +++ b/dsh/docs/pr-assets/issue-327-subagent-user-turn/README.md @@ -0,0 +1,94 @@ +# Memory subagents and chat templates that require a user query — issue #327 + +[简体中文](./README.zh-CN.md) | [Verification data](./verification.json) + +Issue [#327](https://github.com/omdsh-dev/dsh-mnemon/issues/327) reports a local model behind Ollama 0.33 whose chat template requires a user query, as Qwen3.x does. Idle review failed at its fourth request with `500 no user query found in messages`, after it had already created a Document. + +Baseline: `main` `06182f4d`, the published dsh-mnemon 0.5.21. Fix: `771d43e2bdc3ffcc06d2fa057ae0e9d913ce884f`. The runs took place on 2026-10-03 (Asia/Shanghai): +- macOS 15.6 arm64 and Node 24.19.0; +- headless Chrome 154 at 1280×800, zh-CN, light; +- DSH 0.1.7-rc.2, the version in the report, and 0.2.0-rc.2. + +This machine has no Ollama or Qwen model, so a loopback model stub plays the server. DSH, Mnemon and the browser are real, no model is called, and all memories are synthetic. + +## Cause + +- A Mnemon memory subagent, such as idle review or remember, has one user turn: its delegated prompt. Each later step adds an assistant tool call and its results. +- Ollama 0.33 renders the system prompt plus the longest suffix of the other messages that fits `num_ctx`, always keeping the last message. Once tool results fill the window, that suffix starts after the prompt. +- A template that requires a user query then fails, and Ollama answers 500 `no user query found in messages` ([ollama/ollama#18303](https://github.com/ollama/ollama/issues/18303)). The truncation fix, [ollama/ollama#18697](https://github.com/ollama/ollama/pull/18697), is not released. +- DSH's own context compaction is not involved: it replaces the range it compacts with a user-role summary. + +## The emulation + +The stub behaves as Ollama 0.33 does: +- it truncates as above, estimating tokens as the JSON length divided by 4; +- it answers 500 when no user text turn survives. + +Its window is sized when the review sends its third request, to hold that request from the delegated prompt on. One more tool round then overflows it at the fourth request, as in the report. Only the model's choices are scripted: search Documents, create a Document, search again, finish. + +## Before (main) + +![Status: 后台审查失败, memory subagent stopped with error: SERVER: no user query found in messages, with the committed Document receipt](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-327-subagent-user-turn/before-status.jpg) + +On both hosts: +- requests 1 to 3 kept the prompt; +- request 4 kept only tool messages, and the server answered 500. DSH retried it five times and got the same answer each time; +- Status showed 后台审查失败 with `memory subagent stopped with error: SERVER: no user query found in messages`, and the committed `mnemon_document_create · created` receipt. This is the "written, then failed" case in the report. + +## Fix + +Every delegated child gets two DSH hooks while DSH publishes it. Nothing changes until a server refuses one of its requests with `no user query found in messages`: +- **The refused step.** The `agent/request-error` hook adds a short Mnemon user turn, `Continue from the tool results above.`, to the child's session and retries the step at once, as DSH's own context-overflow recovery does. The refused request ran no inference, and the hook runs ahead of DSH's retry backoff. +- **Later steps of that child.** The `agent/pre-step` hook ends each later tool continuation with the same turn, since the context stays full. The server always keeps the last message, so each request keeps a user query. + +A route that never refuses sends exactly what main sends. This matters beyond Ollama: some chat templates, Qwen3's among them, show a model's reasoning only after the last user query, so a user turn after every tool round would drop the earlier steps' reasoning on a server that never truncates, such as vLLM. The main conversation is unchanged. + +| After: Status | After: the Document the review created | +|---|---| +| ![Status: 系统正常, no review failure, one active Document](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-327-subagent-user-turn/after-status.jpg) | ![Project Documents: Review checkpoint storage](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-327-subagent-user-turn/after-documents.jpg) | + +On both hosts the fourth request lost the prompt and was refused once. It ran again at once with the continuation turn and succeeded. Status read 系统正常 with no review failure, and Project Documents showed the Document. No run logged a console error. + +## Request by request + +The in-process composition, for fork and spawn reviews alike: + +| Request | Main: last message | Main: user query | Fix: last message | Fix: user query | Prompt kept | +|---|---|---|---|---|---| +| 1 | DSH runtime context (user) | yes | DSH runtime context (user) | yes | yes | +| 2 | tool result | yes | tool result | yes | yes | +| 3 | tool result | yes | tool result | yes | yes | +| 4 | tool result | **no: 500** | tool result | **no: 500** | no | +| 4, again | — | — | continuation turn (user) | yes | no | + +With a window that never overflows, the fix sends the same four requests as main. The WebUI runs on both hosts match these rows. + +## Design review + +The first version of this fix, `b0dc3c7e`, ended every tool continuation of every delegated child with the user turn, on every route. A review found the costs: +- On the DeepSeek route it bought nothing, cost a few tokens per step, and added a "Context injection" row per step to the child's transcript. +- On reasoning models served through OpenAI-compatible routes, it dropped the earlier steps' reasoning from the template, even where nothing was truncated. + +The fix now acts only after a refusal. The review also asked the pre-step hook to keep the incoming decision's other fields, and to leave a step alone when another handler emptied it, as DSH's own hooks do. Both are done. + +## Automated checks + +- `tests/review-user-turn-host.spec.ts` runs a real DSH 0.1.7-rc.2 composition in process with the emulation: agent loop, fork and spawn providers, and Mnemon's lifecycle, coordinator and tools. + - On main, both reviews fail with the reported error and a partial receipt. + - With the fix, a truncating window gets one refusal and one retry, and both reviews create the Document. + - A window that never overflows gets requests identical to main's. The parent conversation gets no continuation turn. +- `tests/continuation-turn.spec.ts` covers the hooks: + - nothing changes before a refusal; + - other failures, first steps and aborted steps go to the next handler; + - a refused step retries once; + - later continuations keep the incoming decision and leave user text and emptied steps alone; + - the hooks attach only to the child that their own start creates, even when starts overlap, and are released when the start fails or the run ends. +- `tests/subagent.spec.ts`: a delegated write child gets the same recovery. +- `pnpm run verify` passes: docs (2,979 local links), typecheck and the deterministic build, build, typecheck and tests for all 17 plugins, root tests (115 files, 1,571 passed, 6 skipped), Headless activation, package contents (1,502,943 unpacked bytes, with the budget moved to 1,504,000), public entries, publint and attw. + +## Limits + +- The server is an emulation of Ollama 0.33, built from its published behavior (ollama/ollama#18303 and #18697), not Ollama with a Qwen model. The token estimate is coarse, and the window is sized during the run so that the overflow lands on the fourth request, as reported. +- The recovery keys on the error text `no user query found in messages`, which Ollama reports and Qwen's own chat template raises. A server that words the same refusal differently is not recovered. +- Truncation still drops older context, including the prompt. A larger Ollama context length (`OLLAMA_CONTEXT_LENGTH` or the model's `num_ctx`) keeps the whole review in view. +- The main conversation belongs to DSH and is unchanged; the report found no failures there. diff --git a/dsh/docs/pr-assets/issue-327-subagent-user-turn/README.zh-CN.md b/dsh/docs/pr-assets/issue-327-subagent-user-turn/README.zh-CN.md new file mode 100644 index 000000000..ed01ff792 --- /dev/null +++ b/dsh/docs/pr-assets/issue-327-subagent-user-turn/README.zh-CN.md @@ -0,0 +1,94 @@ +# 记忆子代理与要求用户查询的聊天模板:issue #327 + +[English](./README.md) | [验证数据](./verification.json) + +Issue [#327](https://github.com/omdsh-dev/dsh-mnemon/issues/327) 反馈:通过 Ollama 0.33 运行本地模型、且聊天模板要求用户查询(例如 Qwen3.x)时,后台审查在第 4 次请求报 `500 no user query found in messages`,而此前它已经创建了项目档案。 + +基线:`main` `06182f4d`,即已发布的 dsh-mnemon 0.5.21。修复:`771d43e2bdc3ffcc06d2fa057ae0e9d913ce884f`。验证于 2026-10-03(北京时间): +- macOS 15.6 arm64 与 Node 24.19.0; +- 无头 Chrome 154,1280×800,zh-CN,浅色; +- DSH 0.1.7-rc.2(反馈中的版本)与 0.2.0-rc.2。 + +本机没有 Ollama 和 Qwen 模型,由本机回环的模型桩扮演服务端。DSH、Mnemon 和浏览器均为真实运行,未调用真实模型,记忆均为合成数据。 + +## 原因 + +- Mnemon 的记忆子代理(后台审查、记住等)只有一条用户消息,即委派给它的任务提示。之后每一步都会追加一次助手的工具调用及其结果。 +- Ollama 0.33 渲染时保留系统提示,以及其余消息中能放进 `num_ctx` 的最长后缀,并且总会保留最后一条消息。工具结果填满窗口后,这段后缀就从任务提示之后开始。 +- 要求用户查询的模板随即失败,Ollama 返回 500 `no user query found in messages`([ollama/ollama#18303](https://github.com/ollama/ollama/issues/18303))。截断时保留用户消息的修复 [ollama/ollama#18697](https://github.com/ollama/ollama/pull/18697) 尚未发布。 +- 与 DSH 自己的上下文压缩无关:它会把压缩的区段替换为一条用户角色的摘要。 + +## 模拟方式 + +模型桩的行为与 Ollama 0.33 一致: +- 按上述规则截断,token 数按 JSON 长度除以 4 估算; +- 截断后没有用户文本消息时返回 500。 + +审查发出第 3 次请求时,窗口定为恰好容纳这次请求从任务提示开始的部分;再多一轮工具调用,第 4 次请求就会溢出,与反馈一致。只有模型的选择是脚本化的:检索项目档案、新建档案、再次检索、完成。 + +## 修复前(main) + +![状态页:后台审查失败,memory subagent stopped with error: SERVER: no user query found in messages,并列出已提交的档案回执](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-327-subagent-user-turn/before-status.jpg) + +两个宿主上: +- 第 1 至 3 次请求都保留了任务提示; +- 第 4 次请求只剩工具消息,服务端返回 500。DSH 重试了 5 次,结果相同; +- 状态页显示“后台审查失败”与 `memory subagent stopped with error: SERVER: no user query found in messages`,并列出已提交的 `mnemon_document_create · created` 回执,即反馈中“先写后死”的情形。 + +## 修复 + +每个委派子代理在 DSH 发布时都会挂上两个 DSH 钩子。在服务端以 `no user query found in messages` 拒绝它的某次请求之前,什么都不改变: +- **被拒的这一步。** `agent/request-error` 钩子在子代理的会话中追加一条简短的 Mnemon 用户消息 `Continue from the tool results above.`,并立即重试这一步,与 DSH 自己的上下文溢出恢复做法相同。被拒的请求没有进行推理,这个钩子也排在 DSH 的退避重试之前。 +- **该子代理之后的步骤。** 上下文仍是满的,`agent/pre-step` 钩子让之后每次工具续轮都以同样的消息结尾。服务端总会保留最后一条消息,因此每次请求都带有用户查询。 + +从不拒绝的路由,发出的请求与 main 完全相同。这一点不只关乎 Ollama:包括 Qwen3 在内的一些聊天模板只显示最后一条用户查询之后的推理内容,如果每轮工具调用后都追加用户消息,在 vLLM 这类从不截断的服务端上,前面各步的推理就会被丢掉。主会话不变。 + +| 修复后:状态页 | 修复后:审查创建的档案 | +|---|---| +| ![状态页:系统正常,没有审查失败,1 份活跃档案](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-327-subagent-user-turn/after-status.jpg) | ![项目档案:Review checkpoint storage](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-327-subagent-user-turn/after-documents.jpg) | + +两个宿主上,第 4 次请求丢掉了任务提示,被拒一次,随即带着续行消息重试并成功。状态页显示“系统正常”,没有审查失败,项目档案中出现了这份档案。所有运行都没有控制台错误。 + +## 逐次请求 + +进程内组合的结果,fork 与 spawn 两种审查相同: + +| 请求 | main:最后一条消息 | main:用户查询 | 修复:最后一条消息 | 修复:用户查询 | 保留任务提示 | +|---|---|---|---|---|---| +| 1 | DSH 运行时上下文(user) | 有 | DSH 运行时上下文(user) | 有 | 是 | +| 2 | 工具结果 | 有 | 工具结果 | 有 | 是 | +| 3 | 工具结果 | 有 | 工具结果 | 有 | 是 | +| 4 | 工具结果 | **无:500** | 工具结果 | **无:500** | 否 | +| 4(重试) | — | — | 续行消息(user) | 有 | 否 | + +窗口从不溢出时,修复版本发出的 4 次请求与 main 相同。两个宿主上的 WebUI 运行与上表一致。 + +## 设计复核 + +本修复的第一版 `b0dc3c7e` 在所有路由上,让每个委派子代理的每次工具续轮都以这条用户消息结尾。复核发现了代价: +- 在 DeepSeek 路由上没有收益,每步多几个 token,子代理记录里每步多一行“上下文注入”。 +- 在 OpenAI 兼容路由上的推理模型,即使没有发生截断,模板也会丢掉前面各步的推理。 + +现在只在被拒之后才生效。复核还建议 pre-step 钩子保留传入 decision 的其他字段,并在其他处理器清空某一步时不再改动,与 DSH 自己的钩子一致。两处都已照做。 + +## 自动检查 + +- `tests/review-user-turn-host.spec.ts` 在进程内运行真实的 DSH 0.1.7-rc.2 组合并接入上述模拟:agent 循环、fork 与 spawn provider,以及 Mnemon 的生命周期、协调器与工具。 + - main 上两种审查都以反馈中的错误失败,并留下部分写入回执。 + - 修复后,会截断的窗口只被拒一次、重试一次,两种审查都创建了档案。 + - 从不溢出的窗口,请求与 main 完全相同。父会话没有任何续行消息。 +- `tests/continuation-turn.spec.ts` 覆盖两个钩子: + - 被拒之前什么都不改变; + - 其他失败、第 1 步和已中止的步骤交给下一个处理器; + - 被拒的步骤只重试一次; + - 之后的续轮保留传入的 decision,已带用户文本和被清空的步骤保持不变; + - 钩子只挂到自己这次启动所创建的子代理上,启动相互重叠时也能区分;启动失败或运行结束时释放。 +- `tests/subagent.spec.ts`:委派的写入子代理同样能恢复。 +- `pnpm run verify` 通过:文档(2,979 个本地链接)、类型检查与确定性构建,全部 17 个插件的构建、类型检查与测试,根测试(115 个文件,1,571 通过,6 跳过),Headless 激活,包内容(解压后 1,502,943 字节,预算调整为 1,504,000),公开入口、publint 与 attw。 + +## 限制 + +- 服务端是依据 Ollama 0.33 已公开行为(ollama/ollama#18303 与 #18697)构建的模拟,并非真实的 Ollama 加 Qwen 模型。token 估算较粗,窗口在运行中确定,使溢出与反馈一样落在第 4 次请求。 +- 恢复依据错误文本 `no user query found in messages` 触发,Ollama 返回的、以及 Qwen 自己的聊天模板抛出的都是这段文本。措辞不同的同类拒绝不会被恢复。 +- 截断仍会丢掉较早的上下文,包括任务提示。调大 Ollama 的上下文长度(`OLLAMA_CONTEXT_LENGTH` 或模型的 `num_ctx`)能让整个审查都留在窗口内。 +- 主会话由 DSH 负责,本次不变;反馈中主会话没有失败。 diff --git a/dsh/docs/pr-assets/issue-327-subagent-user-turn/verification.json b/dsh/docs/pr-assets/issue-327-subagent-user-turn/verification.json new file mode 100644 index 000000000..8655962c9 --- /dev/null +++ b/dsh/docs/pr-assets/issue-327-subagent-user-turn/verification.json @@ -0,0 +1,508 @@ +{ + "date": "2026-10-03", + "issue": "https://github.com/omdsh-dev/dsh-mnemon/issues/327", + "revisions": { + "baseline": "main 06182f4d828efb1569d2f68a2df5e0e1d1c69f89 (dsh-mnemon 0.5.21)", + "fix": "771d43e2bdc3ffcc06d2fa057ae0e9d913ce884f", + "firstVersion": "b0dc3c7ea684fcd17f2556482b9a3239e57af250 (a user turn on every continuation; narrowed after review)" + }, + "environment": { + "os": "macOS 15.6 arm64", + "node": "24.19.0", + "browser": "headless Chrome 154, 1280x800, device scale 1, zh-CN, light", + "dsh": [ + "@deepseek-ai/dsh 0.1.7-rc.2 (the repository devDependency)", + "@deepseek-ai/dsh 0.2.0-rc.2 (npm latest and next, isolated prefix)" + ] + }, + "emulation": { + "server": "Ollama 0.33 serving a model whose chat template requires a user query (Qwen3.x), as ollama/ollama#18303 and #18697 describe", + "truncation": "Keep the system prompt and the longest suffix that fits num_ctx, always including the last message; tokens estimated as JSON length / 4", + "refusal": "HTTP 500 \"no user query found in messages\" when no user text turn survives", + "window": "Sized when the review sends its third request, to hold that request from the delegated prompt on; the fourth request then overflows it, as reported", + "model": "Scripted choices only: Document search, Document create, search, completion. DSH, the review, its tools and the browser are real" + }, + "hostComposition": { + "test": "tests/review-user-turn-host.spec.ts: a real DSH 0.1.7-rc.2 composition in process (agent loop, fork and spawn providers, Mnemon lifecycle, coordinator and tools); pi-ai folds a non-leading system message into a user message", + "baseline": { + "fork": { + "outcome": { + "error": "memory subagent stopped with error: SERVER_ERROR: no user query found in messages", + "status": "partial", + "receipts": [ + "mnemon_document_create · created" + ] + }, + "childRequests": [ + { + "request": 1, + "last": "runtime-context", + "continuations": 0, + "userQuery": true, + "promptKept": true + }, + { + "request": 2, + "last": "tool", + "continuations": 0, + "userQuery": true, + "promptKept": true + }, + { + "request": 3, + "last": "tool", + "continuations": 0, + "userQuery": true, + "promptKept": true + }, + { + "request": 4, + "last": "tool", + "continuations": 0, + "userQuery": false, + "promptKept": false + } + ] + }, + "spawn": { + "outcome": { + "error": "memory subagent stopped with error: SERVER_ERROR: no user query found in messages", + "status": "partial", + "receipts": [ + "mnemon_document_create · created" + ] + }, + "childRequests": [ + { + "request": 1, + "last": "runtime-context", + "continuations": 0, + "userQuery": true, + "promptKept": true + }, + { + "request": 2, + "last": "tool", + "continuations": 0, + "userQuery": true, + "promptKept": true + }, + { + "request": 3, + "last": "tool", + "continuations": 0, + "userQuery": true, + "promptKept": true + }, + { + "request": 4, + "last": "tool", + "continuations": 0, + "userQuery": false, + "promptKept": false + } + ] + } + }, + "fix": { + "truncating": { + "fork": { + "outcome": { + "delegated": true, + "action": "created", + "documentIds": 1 + }, + "childRequests": [ + { + "request": 1, + "last": "runtime-context", + "continuations": 0, + "userQuery": true, + "promptKept": true + }, + { + "request": 2, + "last": "tool", + "continuations": 0, + "userQuery": true, + "promptKept": true + }, + { + "request": 3, + "last": "tool", + "continuations": 0, + "userQuery": true, + "promptKept": true + }, + { + "request": 4, + "last": "tool", + "continuations": 0, + "userQuery": false, + "promptKept": false + }, + { + "request": 5, + "last": "dsh-mnemon", + "continuations": 1, + "userQuery": true, + "promptKept": false + } + ] + }, + "spawn": { + "outcome": { + "delegated": true, + "action": "created", + "documentIds": 1 + }, + "childRequests": [ + { + "request": 1, + "last": "runtime-context", + "continuations": 0, + "userQuery": true, + "promptKept": true + }, + { + "request": 2, + "last": "tool", + "continuations": 0, + "userQuery": true, + "promptKept": true + }, + { + "request": 3, + "last": "tool", + "continuations": 0, + "userQuery": true, + "promptKept": true + }, + { + "request": 4, + "last": "tool", + "continuations": 0, + "userQuery": false, + "promptKept": false + }, + { + "request": 5, + "last": "dsh-mnemon", + "continuations": 1, + "userQuery": true, + "promptKept": false + } + ] + } + }, + "neverRefuses": { + "fork": { + "outcome": { + "delegated": true, + "action": "created", + "documentIds": 1 + }, + "childRequests": [ + { + "request": 1, + "last": "runtime-context", + "continuations": 0, + "userQuery": true, + "promptKept": true + }, + { + "request": 2, + "last": "tool", + "continuations": 0, + "userQuery": true, + "promptKept": true + }, + { + "request": 3, + "last": "tool", + "continuations": 0, + "userQuery": true, + "promptKept": true + }, + { + "request": 4, + "last": "tool", + "continuations": 0, + "userQuery": true, + "promptKept": true + } + ] + }, + "spawn": { + "outcome": { + "delegated": true, + "action": "created", + "documentIds": 1 + }, + "childRequests": [ + { + "request": 1, + "last": "runtime-context", + "continuations": 0, + "userQuery": true, + "promptKept": true + }, + { + "request": 2, + "last": "tool", + "continuations": 0, + "userQuery": true, + "promptKept": true + }, + { + "request": 3, + "last": "tool", + "continuations": 0, + "userQuery": true, + "promptKept": true + }, + { + "request": 4, + "last": "tool", + "continuations": 0, + "userQuery": true, + "promptKept": true + } + ] + } + } + } + }, + "webui": { + "command": "pnpm e2e:serve --strict-template (DSH 0.2.0-rc.2: the same script with its DSH binary swapped); two substantive turns, then the idle review after 5 s", + "note": "DSH's DeepSeek adapter speaks the Messages protocol, so a continuation turn arrives as text in the user message that carries the tool results; the fixture reads it as a user text turn, as Ollama's /v1/messages does", + "baseline": { + "dsh-0.1.7-rc.2": { + "reviewRequests": [ + { + "request": 1, + "last": "user", + "userQuery": true, + "promptKept": true + }, + { + "request": 2, + "last": "tool", + "userQuery": true, + "promptKept": true + }, + { + "request": 3, + "last": "tool", + "userQuery": true, + "promptKept": true + }, + { + "request": 4, + "last": "tool", + "userQuery": false, + "promptKept": false + }, + { + "request": 4, + "last": "tool", + "userQuery": false, + "promptKept": false + }, + { + "request": 4, + "last": "tool", + "userQuery": false, + "promptKept": false + }, + { + "request": 4, + "last": "tool", + "userQuery": false, + "promptKept": false + }, + { + "request": 4, + "last": "tool", + "userQuery": false, + "promptKept": false + }, + { + "request": 4, + "last": "tool", + "userQuery": false, + "promptKept": false + } + ], + "statusAlert": [ + "后台审查失败", + "memory subagent stopped with error: SERVER: no user query found in messages", + "本轮记忆检查未完成,不会自动重放。后续审查受冷却时间和会话次数限制。", + "已有写入提交;请按下列回执核对,未回滚。", + "mnemon_document_create · created · " + ], + "documentInProjectDocuments": true, + "consoleErrors": [] + }, + "dsh-0.2.0-rc.2": { + "reviewRequests": [ + { + "request": 1, + "last": "user", + "userQuery": true, + "promptKept": true + }, + { + "request": 2, + "last": "tool", + "userQuery": true, + "promptKept": true + }, + { + "request": 3, + "last": "tool", + "userQuery": true, + "promptKept": true + }, + { + "request": 4, + "last": "tool", + "userQuery": false, + "promptKept": false + }, + { + "request": 4, + "last": "tool", + "userQuery": false, + "promptKept": false + }, + { + "request": 4, + "last": "tool", + "userQuery": false, + "promptKept": false + }, + { + "request": 4, + "last": "tool", + "userQuery": false, + "promptKept": false + }, + { + "request": 4, + "last": "tool", + "userQuery": false, + "promptKept": false + }, + { + "request": 4, + "last": "tool", + "userQuery": false, + "promptKept": false + } + ], + "statusAlert": [ + "后台审查失败", + "memory subagent stopped with error: SERVER: no user query found in messages", + "本轮记忆检查未完成,不会自动重放。后续审查受冷却时间和会话次数限制。", + "已有写入提交;请按下列回执核对,未回滚。", + "mnemon_document_create · created · " + ], + "documentInProjectDocuments": true, + "consoleErrors": [] + } + }, + "fix": { + "dsh-0.1.7-rc.2": { + "reviewRequests": [ + { + "request": 1, + "last": "user", + "userQuery": true, + "promptKept": true + }, + { + "request": 2, + "last": "tool", + "userQuery": true, + "promptKept": true + }, + { + "request": 3, + "last": "tool", + "userQuery": true, + "promptKept": true + }, + { + "request": 4, + "last": "tool", + "userQuery": false, + "promptKept": false + }, + { + "request": 4, + "last": "user", + "userQuery": true, + "promptKept": false + } + ], + "statusAlert": null, + "documentInProjectDocuments": true, + "consoleErrors": [] + }, + "dsh-0.2.0-rc.2": { + "reviewRequests": [ + { + "request": 1, + "last": "user", + "userQuery": true, + "promptKept": true + }, + { + "request": 2, + "last": "tool", + "userQuery": true, + "promptKept": true + }, + { + "request": 3, + "last": "tool", + "userQuery": true, + "promptKept": true + }, + { + "request": 4, + "last": "tool", + "userQuery": false, + "promptKept": false + }, + { + "request": 4, + "last": "user", + "userQuery": true, + "promptKept": false + } + ], + "statusAlert": null, + "documentInProjectDocuments": true, + "consoleErrors": [] + } + } + }, + "automated": { + "command": "pnpm run verify", + "rootTests": { + "files": 115, + "passed": 1571, + "skipped": 6 + }, + "docsLocalLinks": 2979, + "packageUnpackedBytes": 1502943, + "packageBudget": 1504000, + "newTests": [ + "tests/review-user-turn-host.spec.ts (fork and spawn, truncating and never refusing)", + "tests/continuation-turn.spec.ts (7)", + "tests/subagent.spec.ts: a delegated write child recovers" + ] + } +} diff --git a/dsh/docs/pr-assets/issues-225-230-20260911/README.md b/dsh/docs/pr-assets/issues-225-230-20260911/README.md new file mode 100644 index 000000000..236e299f8 --- /dev/null +++ b/dsh/docs/pr-assets/issues-225-230-20260911/README.md @@ -0,0 +1,54 @@ +# Issues #225 and #230: verification of the existing v0.5.7 fixes + +[简体中文](./README.zh-CN.md) | [Issue #225](https://github.com/omdsh-dev/dsh-mnemon/issues/225) | [Issue #230](https://github.com/omdsh-dev/dsh-mnemon/issues/230) + +Both reports are already addressed by [PR #226](https://github.com/omdsh-dev/dsh-mnemon/pull/226), released in v0.5.7 through [PR #228](https://github.com/omdsh-dev/dsh-mnemon/pull/228). This record adds fresh verification from main `1e19caf`; it contains no new implementation or package release. Each issue received a separate worktree from that revision. + +The test environment was macOS arm64, Node 25.1.0, pnpm 11.19.0, the published DSH 0.1.5-rc.1 packages, and the installed Mnemon CLI 0.2.7. Each real WebUI harness had its own disposable `DSH_HOME`, `MNEMON_DATA_DIR`, workspace and loopback model endpoint. [Results and screenshot hashes](./verification.json). + +## #225: strict migration and explicit copy recovery + +The checked-in [v0 fixture](../../../tests/fixtures/issue-223-legacy-v0.jsonl) was emitted by the published DSH 0.1.2 Agent loop. For this browser run only, its session id, workspace, preset and model route were assigned to the disposable harness; its two historical invalid summaries were retained. Each line was encoded as a checksummed Zstandard frame. + +Opening that session in the real WebUI failed with `user/message 5 source summary requires notice form; source v0 artifact remains unchanged`. The current plugin therefore does not silently rewrite existing logs. + +The existing `dsh-mnemon-repair-session` implementation created a new compressed copy. Comparing decoded bytes confirmed that only the two known `summary` members were removed and all 16 lines were retained. The original backup SHA-256 remained `74ba9572caf1b816b18e39b994a036f70d9a262960b6df423b39cee7dffc9f01`. + +After explicitly installing the repaired copy in the disposable session directory and restarting DSH, the original user request and assistant reply became readable. A second turn completed through the real WebUI. Another Host restart and cold reopen retained both turns. DSH published the v3 successor: 29 events, 6 user messages and 2 assistant messages. All 3 Mnemon context records omit `summary`. + +| Before repair | After resume and cold reopen | +|---|---| +| ![Strict migration refusal](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issues-225-230-20260911/225-before-repair.png) | ![Both turns retained](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issues-225-230-20260911/225-cold-reopen.png) | + +The existing [repair and lifecycle tests](../../../tests/legacy-session-repair.spec.ts) also pass: plain/compressed strict migration, exclusive copy publication, byte preservation, rejection of malformed input and cold reopen. The lifecycle suite checks newly generated instructions/recall against the released v0 payload validator and preserves other plugins' context messages. Follow the [recovery procedure](../../en/guides/operations.md#dsh-015-compatibility-and-legacy-session-recovery) for an affected installation; plugin upgrade alone cannot repair an existing artifact. + +## #230: connected WebUI with the real CLI + +The separate issue-230 worktree ran the complete Starter with all three optional strategy contributions enabled using `--strategy-extensions`. Status and Settings loaded successfully. An authenticated POST to `/dsh-mnemon-read/status-summary` returned HTTP 200 with `healthy: true`, `commandFound: true` and `cliPath: /opt/homebrew/bin/mnemon`; the same unauthenticated request returned 401. + +A Native memory space was created and activated through the UI. The real CLI stored the synthetic fact “Issue230 canary: the disposable compatibility workspace uses cedar notebooks.” Keyword recall returned that exact fact. After a Host restart, Status reported Mnemon 0.2.7 and the Content page retained the fact. Confirming the UI's soft-delete dialog then removed it from Content. + +| Current status after restart | Settings | +|---|---| +| ![Connected Native 0.2.7](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issues-225-230-20260911/230-restarted-status.png) | ![Connected settings](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issues-225-230-20260911/230-current-settings.png) | + +| Keyword recall | Content retained after restart | +|---|---| +| ![Real CLI canary recalled](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issues-225-230-20260911/230-native-recall.png) | ![Persistent Native content](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issues-225-230-20260911/230-content-after-restart.png) | + +The original 0.5.5 installation and missing-CLI environment in #230 were not recreated. The earlier HTTP 405 before/after evidence remains attached to its tested revision in [PR #226's record](../issue-223-dsh-015/README.md), including the [original failure screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-223-dsh-015/00-before-http-405.png). This run verifies the fixed current state, including real CLI discovery, without claiming another implementation fix. + +## Commands and limits + +```sh +pnpm install --frozen-lockfile +pnpm exec vitest run tests/legacy-session-repair.spec.ts tests/lifecycle.spec.ts tests/dsh-connection-compat.spec.ts +pnpm run verify +MNEMON_NATIVE_TEST_CLI=/opt/homebrew/bin/mnemon pnpm --filter dsh-mnemon-source-memory-spaces test -- tests/native-integration.spec.ts +MNEMON_CLI_PATH=/opt/homebrew/bin/mnemon pnpm e2e:serve +MNEMON_CLI_PATH=/opt/homebrew/bin/mnemon pnpm e2e:serve --strategy-extensions +``` + +Targeted tests: 58 passed. Full verification: 1,249 passed, 7 opt-in skips; types, deterministic builds, docs, package validation and real Headless activation passed. The separately enabled Native Source run passed 169 tests, including real CLI create/write/recall/forget, with its Windows smoke skipped. Headless exposed 38 tools, including all 8 representative Mnemon tools, and passed restart and whole-Starter disable checks. + +The browser used fixed loopback model responses; this is not a model-quality test. No Windows, Electron, physical mobile device or live third-party Provider was retested. Independent packed-artifact checks were not repeated for this documentation-only addition; their historical result belongs to PR #226. No user sessions, private memories or credentials are included. diff --git a/dsh/docs/pr-assets/issues-225-230-20260911/README.zh-CN.md b/dsh/docs/pr-assets/issues-225-230-20260911/README.zh-CN.md new file mode 100644 index 000000000..61aaeedc0 --- /dev/null +++ b/dsh/docs/pr-assets/issues-225-230-20260911/README.zh-CN.md @@ -0,0 +1,54 @@ +# Issue #225 与 #230:复验已有 v0.5.7 修复 + +[English](./README.md) | [Issue #225](https://github.com/omdsh-dev/dsh-mnemon/issues/225) | [Issue #230](https://github.com/omdsh-dev/dsh-mnemon/issues/230) + +两个问题已由 [PR #226](https://github.com/omdsh-dev/dsh-mnemon/pull/226) 修复,并通过 [PR #228](https://github.com/omdsh-dev/dsh-mnemon/pull/228) 随 v0.5.7 发布。本记录补充 main `1e19caf` 的独立复验,不新增实现或发布版本。每个 Issue 均从该 revision 创建单独 worktree。 + +环境为 macOS arm64、Node 25.1.0、pnpm 11.19.0、已发布 DSH 0.1.5-rc.1 包及本机已安装的 Mnemon CLI 0.2.7。两套真实 WebUI 各自使用可丢弃的 `DSH_HOME`、`MNEMON_DATA_DIR`、工作区和本地模型端点。[结果与截图哈希](./verification.json)。 + +## #225:严格迁移与显式副本恢复 + +仓库中的 [v0 夹具](../../../tests/fixtures/issue-223-legacy-v0.jsonl) 由已发布 DSH 0.1.2 Agent loop 生成。本次浏览器验证只将会话 id、工作区、preset 和模型路由指向隔离环境,保留两处历史非法 summary,并将各行编码为带校验和的 Zstandard 帧。 + +真实 WebUI 打开时复现 `user/message 5 source summary requires notice form; source v0 artifact remains unchanged`。这也确认当前插件不会静默改写已有日志。 + +已有 `dsh-mnemon-repair-session` 实现生成了新的压缩副本。解码后的逐字节比较确认只删除两处已知 `summary`,16 行全部保留。原始备份 SHA-256 始终为 `74ba9572caf1b816b18e39b994a036f70d9a262960b6df423b39cee7dffc9f01`。 + +在可丢弃的会话目录中显式安装修复副本并重启 DSH 后,原始用户请求和助手回复均可读取。通过真实 WebUI 继续第二回合,再次重启 Host 并冷打开,两回合均保留。DSH 生成的 v3 后继文件含 29 个事件、6 条用户消息、2 条助手消息;3 条 Mnemon 上下文消息均不带 `summary`。 + +| 修复前 | 继续对话并冷打开后 | +|---|---| +| ![严格迁移拒绝](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issues-225-230-20260911/225-before-repair.png) | ![两回合均保留](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issues-225-230-20260911/225-cold-reopen.png) | + +已有[修复与生命周期测试](../../../tests/legacy-session-repair.spec.ts)通过,覆盖普通和压缩日志的严格迁移、副本独占发布、字节保留、损坏输入拒绝与冷打开。生命周期测试使用已发布 v0 payload validator 检查新 instructions/recall,并验证其他插件上下文消息得到保留。已有受影响日志请按[恢复步骤](../../zh-CN/guides/operations.md#dsh-015-兼容与旧会话恢复)处理;只升级插件不会修复已有制品。 + +## #230:真实 CLI 与已连接的 WebUI + +issue-230 独立 worktree 通过 `--strategy-extensions` 启用完整 Starter 和全部三个可选策略。状态页及设置页正常加载。已认证的 `/dsh-mnemon-read/status-summary` POST 返回 HTTP 200,且 `healthy: true`、`commandFound: true`、`cliPath: /opt/homebrew/bin/mnemon`;相同未认证请求返回 401。 + +通过 UI 创建并激活 Native 记忆空间,真实 CLI 写入合成事实 “Issue230 canary: the disposable compatibility workspace uses cedar notebooks.”,关键词检索返回原文。Host 重启后,状态页显示 Mnemon 0.2.7,内容页仍可读取该事实。最后确认 UI 的软删除对话框,内容页不再显示该事实。 + +| 重启后的状态 | 设置 | +|---|---| +| ![Native 0.2.7 已连接](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issues-225-230-20260911/230-restarted-status.png) | ![设置已连接](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issues-225-230-20260911/230-current-settings.png) | + +| 关键词检索 | 重启后保留的内容 | +|---|---| +| ![召回真实 CLI 写入的事实](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issues-225-230-20260911/230-native-recall.png) | ![持久化 Native 内容](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issues-225-230-20260911/230-content-after-restart.png) | + +未重新搭建 #230 原报告中的 0.5.5 安装和缺失 CLI 环境。早期 HTTP 405 的前后对照仍由其实际 revision 对应的 [PR #226 验证记录](../issue-223-dsh-015/README.zh-CN.md)和[原始失败截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/issue-223-dsh-015/00-before-http-405.png)负责。本次验证已修复的当前状态及真实 CLI 发现,不声称新增实现修复。 + +## 命令与限制 + +```sh +pnpm install --frozen-lockfile +pnpm exec vitest run tests/legacy-session-repair.spec.ts tests/lifecycle.spec.ts tests/dsh-connection-compat.spec.ts +pnpm run verify +MNEMON_NATIVE_TEST_CLI=/opt/homebrew/bin/mnemon pnpm --filter dsh-mnemon-source-memory-spaces test -- tests/native-integration.spec.ts +MNEMON_CLI_PATH=/opt/homebrew/bin/mnemon pnpm e2e:serve +MNEMON_CLI_PATH=/opt/homebrew/bin/mnemon pnpm e2e:serve --strategy-extensions +``` + +定向测试 58 项通过。完整验证 1,249 项通过、7 项 opt-in 跳过,类型、确定性构建、文档、制品验证及真实 Headless 激活均通过。单独启用真实 Native 的 Source 测试 169 项通过,含真实 CLI 创建、写入、召回和忘记;Windows 冒烟跳过。Headless 暴露 38 个工具,包含全部 8 个代表性 Mnemon 工具,并通过重启及整体停用 Starter 检查。 + +浏览器使用固定的本地模型回复,不评估模型质量。未复测 Windows、Electron、实体移动设备或真实三方 Provider。仅文档新增不重复独立打包制品检查,其历史结果仍属于 PR #226。未包含用户会话、私有记忆或凭据。 diff --git a/dsh/docs/pr-assets/issues-225-230-20260911/verification.json b/dsh/docs/pr-assets/issues-225-230-20260911/verification.json new file mode 100644 index 000000000..681c4d1ce --- /dev/null +++ b/dsh/docs/pr-assets/issues-225-230-20260911/verification.json @@ -0,0 +1,148 @@ +{ + "date": "2026-09-11", + "sourceRevision": "1e19caf40f0bf37edf678c3d829f2398ab28792a", + "existingFixPR": 226, + "releasePR": 228, + "starter": "0.5.7", + "dsh": "0.1.5-rc.1", + "node": "25.1.0", + "pnpm": "11.19.0", + "platform": "macOS arm64", + "nativeCli": "0.2.7", + "worktrees": { + "issue225": "codex/fix-225-legacy-session-summary", + "issue230": "codex/verify-230-dsh-compatibility" + }, + "tests": { + "targetedPassed": 58, + "repairPassed": 17, + "lifecyclePassed": 39, + "connectionPassed": 2, + "fullVerifyPassed": 1249, + "fullVerifySkipped": 7, + "nativeSourceSuitePassed": 169, + "nativeSourceSuiteSkipped": 1, + "nativeOptInPassed": 1, + "headlessTotalTools": 38, + "headlessRepresentativeMnemonTools": 8 + }, + "legacyRepair": { + "mode": "copy", + "format": 0, + "encoding": "zstd", + "repairedMessages": 2, + "inputSha256": "74ba9572caf1b816b18e39b994a036f70d9a262960b6df423b39cee7dffc9f01", + "outputSha256": "3ff415c237c85d7dddd4d64a574d1352f3363fe7ddd7e041ce3212f0bcc567d2", + "originalPreserved": true + }, + "legacyDelta": { + "onlyTwoKnownSummariesRemoved": true, + "originalPreserved": true, + "originalSha256": "74ba9572caf1b816b18e39b994a036f70d9a262960b6df423b39cee7dffc9f01", + "originalLines": 16, + "repairedLines": 16 + }, + "legacyFinal": { + "version": 3, + "eventCount": 29, + "userMessages": 6, + "assistantMessages": 2, + "mnemonMessages": [ + { + "seq": 6, + "source": { + "kind": "plugin", + "plugin": "dsh-mnemon", + "form": "instructions" + } + }, + { + "seq": 7, + "source": { + "kind": "plugin", + "plugin": "dsh-mnemon", + "form": "recall" + } + }, + { + "seq": 22, + "source": { + "kind": "plugin", + "plugin": "dsh-mnemon", + "form": "recall" + } + } + ], + "originalBackupSha256": "74ba9572caf1b816b18e39b994a036f70d9a262960b6df423b39cee7dffc9f01", + "coldReopenedInWebUI": true + }, + "status": { + "authenticatedStatus": 200, + "unauthenticatedStatus": 401, + "rpcSucceeded": true, + "healthy": true, + "commandFound": true, + "cliPath": "/opt/homebrew/bin/mnemon", + "activeStores": [ + "default", + "f3020dc8-3197-4be7-bc58-6380081ee82b" + ] + }, + "browser": { + "legacyFailureReproduced": true, + "legacyReadAfterRepair": true, + "legacyResumeAndColdReopen": true, + "currentStatusConnected": true, + "settingsConnected": true, + "optionalStrategiesEnabled": [ + "scoped", + "light-context", + "auto-capture" + ], + "nativeSpaceCreatedAndActivatedInUI": true, + "nativeFactSeededBy": "real CLI without embeddings or LLM", + "nativeKeywordRecall": true, + "nativeContentRetainedAfterRestart": true, + "nativeSoftDeleteInUI": true + }, + "screenshots": [ + { + "path": "225-before-repair.png", + "bytes": 56267, + "sha256": "0c7d4f226e3928d861a7825e0551fe1e0008f134e877961394c479164d4adc72" + }, + { + "path": "225-cold-reopen.png", + "bytes": 54765, + "sha256": "8bf8c6f2af437067158248845e5e5e32b425403fc0a571164e24a633ced49cdd" + }, + { + "path": "230-current-settings.png", + "bytes": 86249, + "sha256": "b36db44220630cf361f8d3e96c430432e6feaf02d7035bf7c12d5b10811b573a" + }, + { + "path": "230-native-recall.png", + "bytes": 70390, + "sha256": "c4c06e81255f850ce24994423f74410291f4598505eff50dd0ea7f5c0f969dc5" + }, + { + "path": "230-restarted-status.png", + "bytes": 78384, + "sha256": "7ee2cb27bdadc8bebc02fb74b147bd41fea5fbcce2983f0cb8183a5b9c6ecb1e" + }, + { + "path": "230-content-after-restart.png", + "bytes": 73069, + "sha256": "828fd66f322b91ae47f26bc1b910b5f4bdf51b4bd2e46ebee4b4356cd715edf1" + } + ], + "limits": [ + "No source changes; this rechecks the already-merged 0.5.7 fix.", + "The original 0.5.5 installation and absent-CLI environment from issue 230 were not recreated; the historical HTTP 405 capture belongs to PR 226.", + "Loopback fixed model responses; no live model-quality measurement.", + "No Windows, Electron, physical mobile, or live third-party Provider retest.", + "No user sessions or personal memory data were accessed.", + "Standalone tarball verification remains the recorded PR 226 evidence; not rerun for this documentation-only addition." + ] +} diff --git a/dsh/docs/pr-assets/main-rebase-20260831/README.md b/dsh/docs/pr-assets/main-rebase-20260831/README.md new file mode 100644 index 000000000..73ff30b45 --- /dev/null +++ b/dsh/docs/pr-assets/main-rebase-20260831/README.md @@ -0,0 +1,78 @@ +# Latest-main rebase verification — 2026-08-31 + +This is an integration record, not a new LLM benchmark or a release announcement. + +## Revisions and ownership + +| Item | Revision | +|---|---| +| Development branch | `codex/view-concretization` | +| Pre-rebase development baseline | `7861d85c4b2636e84ae2c63ec6ffebed1935d34c` | +| Recoverable local backup | `codex/archive-view-before-main-rebase-20260831` | +| Latest fetched `origin/main` | `aa446cf598ee5928c787d340eb314d3f1803eb4c` — v0.4.3 | +| Verified implementation | `f10c2212c431919eb9de5eec4632cfba1a733448` | + +The development history was rebased onto `origin/main`, not merged into or pushed over main. Other worktrees and experiment branches were not updated. The official distribution remains `0.5.0-beta.1`, with thirteen plugins and the Root Starter. Nothing was published or pushed. + +Replaying historical merges alone was insufficient: their upstream parents were already ancestors of the new base, but their architecture-aware conflict resolutions still mattered in the relocated modules. Those ports were reapplied to the Host, shared Client, Source pages, CLI runner and tests. Obsolete monolithic modules were not reinstated. The final implementation, rather than a conflict-free intermediate replay, is the verified artifact. + +The following comparison is empty, including the earlier default-Strategy and Source I/O optimizations: + +```sh +git diff --exit-code 7861d85c4b2636e84ae2c63ec6ffebed1935d34c f10c2212c431919eb9de5eec4632cfba1a733448 -- \ + src/core src/sdk plugins .github cordis.patch.yml package.json pnpm-lock.yaml +``` + +## Main behavior mapped into the composable architecture + +| Main change | Final owner and behavior | Regression coverage | +|---|---|---| +| Restore Builtin (`d0b114e`) | Client mounts either DSH `shell.overlay` or `conversation.view`. Both declare the same root-scoped Source child Slot and render the same Source-owned pages. Builtin uses its owning session; Sidebar retains its independent workspace picker. | `client-apply`, `client-interaction`, `client`, `client-sidebar`, `live-runtime`; real WebUI | +| Normalize legacy `buildin` (`d1bf61e`) | Host configuration and the revision-fenced DSH settings writer normalize only the preference. Existing user overrides, unrelated fields, comments and read-only behavior are preserved. No Core taxonomy or data migration is added. | 17 migration cases; Host startup/external-event/disposal test; real Headless restart and WebUI | +| Align collapsed icon (`66ce4be`) | Shared Client CSS recognizes native `data-sidebar-collapsed`, without requiring a layout plugin's frame marker. Expanded styles remain unchanged. | Sidebar CSS tests and a real native-frame measurement: 36×36 button, 20×20 SVG | +| v0.4.2/v0.4.3 release and UI documentation | Stable release records/assets remain historical evidence; current beta instructions describe the shared placements and canonical spelling. | Documentation link checks and release consistency check | + +Earlier main fixes remain in their new owners: receiver-sensitive settings bindings, Sidebar subtree retention and peer-panel coordination, asynchronous memory tasks, command-name Native CLI resolution, bounded Source operations and deterministic ZIP export. They are covered by the full suite and preserved against the development baseline; this task did not introduce alternative controllers or duplicate Source pages. + +## Automated verification + +Environment: macOS, Node `v25.1.0`, pnpm `11.19.0`, pinned DSH `0.1.1-rc.2`. + +| Command / gate | Result | +|---|---| +| `pnpm verify` | Exit 0; Root and plugin typechecks/builds/tests, Headless and package gates passed | +| Root full Vitest run | **102 files / 888 tests passed**; 2 files / 2 tests conditionally skipped | +| Deterministic Root build | All 36 generated-file hashes matched across two builds | +| Headless fixture | 37 total tools / 7 representative Mnemon tools; legacy spelling persisted canonically, unrelated comments/settings preserved, restart idempotent | +| Package contents / exports | 43 packed files; 9 Node-compatible entry imports; 21 public type dependencies | +| `publint --strict` and `attw` | Passed | +| `pnpm verify:plugins` | Exit 0; **13 standalone plugin repositories / 14 packed artifacts**, installed through declared semver dependencies outside the workspace; each plugin's typecheck/test/build and the external SDK/Client consumer passed | +| Packed Starter activation | A real isolated DSH installation of only the Root tarball resolved and activated its default plugins through the loopback registry | +| `pnpm release:check` | Exit 0; fourteen coordinated `0.5.0-beta.1` artifacts with the `beta` tag | +| Git integrity | Latest fetched main is an ancestor; development-baseline Core/SDK/plugins are unchanged; full diff whitespace checks passed | + +The 888-test total already includes the plugin suites; their independent re-execution is not added again to inflate the count. The two skipped tests are the opt-in real Native binary integration and the Windows-only smoke test. + +The first full verification stopped on an obsolete upgrade-test expectation that discarded `displayMode`. It was changed to require canonical `builtin`, while retaining the copied-data inventory, grant-based writes, unchanged storage versions and reopen assertions. The complete verification was then rerun successfully. No assertion or performance threshold was removed or loosened to make the suite pass. + +## Real WebUI checks + +The Browser skill was used to click the actual DSH WebUI served by `pnpm e2e:serve`. This used built Root/Source Client artifacts, real DSH Slots, Host RPC and temporary on-disk storage. `DSH_HOME`, memory roots, two workspaces and the model endpoint were isolated. The two test conversations received fixed replies from the loopback-only model stub; these were not paid model evaluations. + +1. Before any conversation existed, Sidebar exposed all four primary-page entries, opened Status/Runtime, and created a Runtime record through the Source management contract. +2. The native collapsed Sidebar rendered the Memory button at 36×36 and its SVG at 20×20; its dark-theme foreground matched the intended primary label color. Returning to the conversation restored chat interaction. +3. Saving Builtin removed the Sidebar entry immediately. With no conversation, no unbound Builtin workbench was shown. Once a test conversation existed, its Memory tab rendered the same four Source pages without an independent workspace picker or Back button. +4. Builtin read the Runtime record created in Sidebar, edited it successfully, and created/read a managed Document with revision 1 and a content hash. +5. Memory Spaces opened Overview, Recall, Content and Entities, with the expected distinct headings and controls. Its strategy dialog opened and canceled without saving. +6. Changing from global to workspace scope removed the global records from the visible Source pages. Workspace A and B each accepted their own Runtime record; neither appeared in the other workspace. Returning to A reopened A's record, with B absent. Status showed the actual corresponding `/.mnemon` root. +7. Switching back to Sidebar and global scope produced one Sidebar entry and zero Builtin tabs. The original global Runtime edit and the Document created in Builtin were still readable; neither workspace-specific record appeared in the global view. +8. Directly changing the temporary settings file to `buildin` caused the Host to save `builtin`, preserving `storageScope` and the unrelated onboarding setting. Reloading the Client restored the Builtin tab and all Source pages. +9. No browser console warnings/errors were captured. The missing Native binary was honestly shown as a Provider status, without breaking Runtime/Documents. No live cloud Provider was enabled. + +The browser tab and test processes were closed. The pnpm Ctrl-C wrapper exited before fixture cleanup completed, so the exact task-owned temporary directory was removed after confirming its Web port and processes had stopped. No personal memory or workspace was changed, and no test server was left running. + +## Retained limitations + +- Settings-page saves update the Client immediately. Direct external YAML edits normalize on the Host, but an already-open Mnemon Client must be refreshed: its settings snapshot does not subscribe to external-file pushes. Apart from its relocated import, that Client implementation is identical to main's. This existing limitation is documented, not presented as a newly implemented live-push feature. +- Browser verification used the native dark frame. Existing CSS/interaction tests cover the supported layout selectors and peer-panel contracts; this run did not reinstall every third-party skin. +- Real Native/Windows execution, live cloud Provider accounts, source-only DSH alpha, LLM accuracy, paid benchmark cost and production network latency were not revalidated here. Controlled Provider suites and the existing local performance fences passed; no claim of a new benchmark improvement is made. diff --git a/dsh/docs/pr-assets/main-rebase-20260903/README.md b/dsh/docs/pr-assets/main-rebase-20260903/README.md new file mode 100644 index 000000000..2d373722e --- /dev/null +++ b/dsh/docs/pr-assets/main-rebase-20260903/README.md @@ -0,0 +1,90 @@ +# Latest-main rebase verification — 2026-09-03 + +This is an integration record for the Composable View development branch. It +does not announce a release or a new memory-quality benchmark. + +## Revisions + +| Item | Revision | +|---|---| +| Development branch | `codex/view-concretization` | +| Pre-rebase development baseline | `5e4aac5c3f53be99d84c5741e6068ce4e68d3d09` | +| Recoverable local backup | `codex/archive-view-before-main-rebase-20260903` | +| Latest fetched `origin/main` and local `main` | `b2b589ee95ab6987af08a4630f4e95c0c3f6f537` — v0.4.6 | +| Verified integration code | `239ca355bc705f189dbefceffa8c5dae0261fcd5` | + +The development history was rebased onto the fetched main with merge topology +preserved. Main was not merged into the feature branch, and the feature branch +was not pushed or published. The local `main` reference was fast-forwarded to +the same fetched revision. Experiment branches and their worktrees were not +changed. + +## Main behavior mapped into the composable architecture + +| Main addition | Final owner and boundary | Verification | +|---|---|---| +| Both DSH session-projection contracts (#147) | The Host adapter accepts both projection shapes. Core, Source and Strategy contracts remain DSH-independent. | Projection fixtures, Host lifecycle and full suite | +| Stable `events[]` and alpha `snapshotEvents()` / `eventAt()` (#156) | `src/host/session-events.ts` performs capability detection and exposes one Host-local snapshot/read path. Callers no longer assume that a DSH session is directly iterable. | Four compatibility cases, alpha.5 source build, full suite | +| Optional Better Sidebar placement (#153) | Better Sidebar contributes only a DOM seat and `{ sessionId, cwd, visible }` scope. DSH's `shell.overlay` renderer still owns every Source child Slot; the shared workspace is portalled into the optional seat from that owning React tree. No Source receives a Better Sidebar dependency. | Unit lifecycle/context tests and real WebUI clicks | +| v0.4.4–v0.4.6 release/docs changes | Historical stable release material is retained. The development distribution remains the coordinated `0.5.0-beta.1` set. | Release consistency and package gates | +| DSH alpha.5 source verification | The temporary linker overlays the full Root DSH graph and only the Cordis identity inside installed plugin workspaces. Plugin Client dependencies stay workspace-local, preserving their independent Vite/test boundary. Every original pnpm link is recorded and restored. | 37-path link/restore cycle and complete alpha.5 verification | + +The first Better Sidebar bridge attempted to pass a Source renderer through the +optional service. Real WebUI navigation rejected that design because the Slot +was rendered outside its installed DSH renderer tree. The DOM-seat/portal model +replaced it, preserving the actual ownership boundary instead of suppressing +the error. Likewise, linking every plugin Client dependency to the external DSH +checkout crossed each plugin's Vite filesystem root; limiting plugin overlays +to the shared Cordis identity fixed the boundary without weakening tests. + +## Automated verification + +Environment: macOS, Node `v25.1.0`, pnpm `11.19.0`. Normal dependencies use +DSH `0.1.1-rc.2`; the source-only compatibility run used a built DSH +`0.1.2-alpha.5` checkout at `db6bdc3576c2d4e7c965e8e3ed0c2a731eed87f5`. + +| Gate | Result | +|---|---| +| Complete `pnpm verify` against DSH alpha.5 source | Exit 0; Root and 16 plugin builds/typechecks/tests, Headless and package gates passed | +| Root Vitest portion | **113 files passed / 1005 tests passed**; 2 files / 2 tests conditionally skipped | +| Deterministic build | 38 generated files matched across both builds | +| Headless fixture | 38 total tools / 7 representative Mnemon tools | +| Package/export checks | 45 packed files; 11 Node-compatible entries; 25 public type dependencies; `publint` and `attw` passed | +| Source link restoration | All 37 Root/workspace dependency paths restored to their original registry links; normal Root typecheck passed afterward | +| `pnpm verify:plugins` on restored registry graph | Exit 0; **16 independent plugin repositories / 17 packed artifacts**; standalone install/typecheck/test/build and external consumer passed | +| Packed Starter and Strategies | Real DSH activated the packed Starter alone and three optional packed Strategies together | +| `pnpm release:check` | Exit 0; all 17 coordinated `0.5.0-beta.1` artifacts remain on the `beta` tag | +| Git integrity | Freshly fetched main is an ancestor; local main matches origin; whitespace checks passed | + +The two skipped tests are the opt-in real Native binary integration and the +Windows-only smoke test. Registry UI primitives emit an existing missing source +map warning during Client tests; tests and builds complete successfully. + +## Real WebUI verification + +The Browser skill drove the actual DSH rc.2 WebUI using built Mnemon artifacts, +an isolated profile/workspace/storage root and a loopback-only model stub. +`dsh-better-sidebar` `0.19.0-alpha.0` at +`dde0923980dc01798c09696bcd5987c280bff927` was loaded as the optional service. + +- The Better Sidebar menu contained exactly one **Memory System** item. +- Opening it rendered the shared Mnemon workspace and successfully navigated + Status, Runtime, Documents and Memory Spaces. +- Collapsing Better Sidebar and opening the native Mnemon Sidebar still rendered + Runtime, demonstrating that optional placement did not replace DSH placement. +- A fresh browser tab restored the persistent Memory System tab and all three + Source pages tested there. +- The fresh tab reported zero browser console errors and zero warnings. + +The first browser run intentionally retained the real Slot ownership failure as +diagnostic evidence before the portal correction. The successful rerun used a +fresh disposable fixture. Both browser tabs, the server and the fixture were +closed or removed; no personal profile or remote model was used. + +## Retained limits + +- This run does not claim improved LLM accuracy, token cost or Provider network + latency; it verifies integration and behavioral preservation. +- Native execution, Windows execution and live cloud Provider accounts were not + rerun here. +- No package, branch or tag was pushed or published. diff --git a/dsh/docs/pr-assets/main-rebase-20260904/README.md b/dsh/docs/pr-assets/main-rebase-20260904/README.md new file mode 100644 index 000000000..c983b1e0f --- /dev/null +++ b/dsh/docs/pr-assets/main-rebase-20260904/README.md @@ -0,0 +1,113 @@ +# Latest-main rebase verification — 2026-09-04 + +This is the integration record for rebasing the Composable View development +branch onto the v0.4.7 main line. It verifies compatibility and behavior; it is +not a release announcement or a memory-quality benchmark. + +## Revisions + +| Item | Revision | +|---|---| +| Development branch | `codex/view-concretization` | +| Pre-rebase development baseline | `73c23c837c0af3d9eea1c6cbc4e3040bc5bf768d` | +| Recoverable local backup | `codex/archive-view-before-main-rebase-20260904` | +| Latest fetched `origin/main` and local `main` | `47993c55c78ddb2c0ff1eb8e18859b4f5c35a14f` — v0.4.7 | +| Rebased feature graph before semantic integration | `f65bdc9` | +| Verified semantic integration | `60112e8ca5353395ed9bd4365e0e767f699dfd1d` | + +The development history was rebased with merge topology preserved. Main was +not merged into the feature branch, and neither branch nor packages were +pushed or published. The local `main` reference was fast-forwarded to the same +freshly fetched revision after confirming it was not checked out elsewhere. + +## Main behavior mapped into the composable architecture + +| Main addition | Final owner and boundary | Verification | +|---|---|---| +| Stable DSH `0.1.2-rc.1` baseline from v0.4.7 | Root build/test dependencies and Source Client development fixtures use one exact rc.1 cohort. The public Cordis peer remains `^4.0.1`, which accepts rc.1's Cordis 4.0.2 without unnecessarily rejecting compatible Hosts. | Frozen install, dependency assertions, registry and source-overlay full suites | +| Store and Invariants packages | `SnapshotSelectorHook` is imported from its public owner, `@deepseek-ai/dsh-client-store`; `StoredEntry` remains owned by Slots. `@deepseek-ai/dsh-invariants` closes the published peer graph. Core, Source and Strategy contracts gain no DSH dependency. | Typecheck, Client projection tests, real WebUI | +| Architecture Client layout dependency | Root explicitly owns `@deepseek-ai/dsh-client-ui-layout`; independent Source plugins keep only their declared Client primitives and their own workspace-local build boundary. | Root/plugin builds and 16 standalone repository checks | +| Exact prerelease quarantine | Every direct rc.1 package represented in the lockfile is named explicitly in `minimumReleaseAgeExclude`; wildcard exemptions are rejected. The retained legacy projection alias is audited separately. | Plugin dependency/lock assertions and frozen install | +| DSH alpha.5 source compatibility | The linker overlays 23 Root DSH packages plus Root Cordis and only the shared Cordis identity in each of 16 plugin workspaces. It also includes Store, Invariants and UI Layout. All 40 original registry links are recorded and restored. | Full alpha.5 suite, restore check and post-restore typecheck | +| Cloud WebUI documentation from main | The rc.1 launch-token/cookie transport, HTTPS/access-control requirements, `trustedHosts` boundary and explicit rc.2 rollback procedure remain in the English and Simplified Chinese guides. | Documentation and release-link audit | +| v0.4.7 release metadata | Historical stable installation guidance remains v0.4.7. The development distribution remains the coordinated 17-package `0.5.0-beta.1` set. | `release:check` | + +The rebase initially retained the feature branch's old rc.2 package/import +choices while accepting main's newer workspace and linker structure. That +state compiled only accidentally through transitive packages. The semantic +integration commit removes that split state: package ownership, direct +dependencies, source overlays, tests and documentation now describe the same +rc.1 graph without moving DSH concerns into the Composable Core or plugin SDK. + +## Automated verification + +Environment: macOS, Node `v25.1.0`, pnpm `11.19.0`. Registry verification used +DSH `0.1.2-rc.1`; source verification used the built DSH `0.1.2-alpha.5` +checkout at `db6bdc3576c2d4e7c965e8e3ed0c2a731eed87f5`. + +| Gate | Result | +|---|---| +| Frozen dependency installation | Exit 0; the lockfile resolves the direct rc.1/Cordis 4.0.2 cohort | +| Complete `pnpm verify` against registry rc.1 | Exit 0; Root and all 16 plugin builds, typechecks and tests passed | +| Complete `pnpm verify` against alpha.5 source overlay | Exit 0; the same build/test/package chain passed | +| Root Vitest portion in each full run | **114 files passed / 1000 tests passed**; 2 files / 2 tests conditionally skipped | +| Deterministic build | 38 generated files matched across both builds | +| Headless fixture | 38 total tools / 7 representative Mnemon tools | +| Package/export checks | 45 packed files; 11 Node-compatible entries; 25 public type dependencies; `publint` and `attw` passed | +| Source-link restoration | All 40 temporary links restored; restore record absent and Root typecheck passed | +| `pnpm verify:plugins` | Exit 0; **16 independent plugin repositories / 17 packed artifacts** installed, typechecked, tested, built and consumed without workspace links | +| Packed DSH activation | The packed Starter activated alone; three optional packed Strategies activated together | +| `pnpm release:check` | Exit 0; all 17 coordinated artifacts remain `0.5.0-beta.1` on the `beta` tag | + +The full suites emit the already known missing source-map warning from the +published rc.1 UI primitives. It does not affect compilation or execution. The +two skipped tests are the opt-in Native CLI integration and Windows-only smoke +test. + +## Real WebUI verification + +The Browser skill drove the actual DSH rc.1 WebUI with built repository +artifacts, an isolated profile/workspace/storage root and a loopback-only model +stub. Better Sidebar `0.19.0-alpha.0` at +`dde0923980dc01798c09696bcd5987c280bff927` and all three optional Strategy +bundles were installed. + +- Status reported `dsh-mnemon 0.5.0-beta.1`, all three Sources running and the + expected disabled state for eight unconfigured third-party Providers. +- Plugin management discovered three Sources and four Strategies. Enabling the + Light Context Strategy changed the composition from 1/4 to 2/4, saved + successfully and remained enabled after a complete WebUI restart. +- Runtime created a branch-scoped critical memory and then edited it through + the real public management path. +- After binding the disposable workspace, Documents created and read a managed + Markdown document with its revision, hash, source path and rendered body. +- Memory Spaces navigated Overview, Search, Content and Entities. Creating a + native space without a CLI produced the expected actionable `ENOENT` alert + and kept the dialog and application responsive. +- DSH Settings loaded the complete Mnemon scope, Source, Provider, background + Agent and conversation controls under the rc.1 Client Store boundary. +- Better Sidebar offered one Memory System tab; opening it rendered the same + shared workspace and reflected the Runtime and Documents writes. +- One actual conversation completed through the loopback model while the + composed default plus Light Context Strategies were active. + +The final browser page reported zero console errors. Five warnings came only +from the deliberately terminated first WebUI port during the persistence +restart; the replacement page completed all checks without a script error. Two +model requests stayed on localhost. The browser tab and server were stopped, +and the explicit disposable fixture was moved to Trash after verifying the +server no longer listened. No personal profile, credential, memory or remote +model was used. + +## Retained limits + +- This run verifies integration and behavioral preservation; it does not claim + improved LLM accuracy, token cost or Provider network latency. +- Native success was not rerun because no Native CLI was installed; its + user-facing failure path, isolated provider tests and package boundary were + verified instead. +- Windows execution and live third-party Provider accounts were not available. +- The rc.2 backward WebUI regression was not repeated in this run; v0.4.7's + retained compatibility evidence and branch-level compatibility fixtures + remain present. +- No package, branch or tag was pushed or published. diff --git a/dsh/docs/pr-assets/main-strategy-choice-20260927/README.md b/dsh/docs/pr-assets/main-strategy-choice-20260927/README.md new file mode 100644 index 000000000..1f6d54355 --- /dev/null +++ b/dsh/docs/pr-assets/main-strategy-choice-20260927/README.md @@ -0,0 +1,30 @@ +# Main Strategy choice and the general Strategy + +[简体中文](./README.zh-CN.md) + +Tested implementation: `4a42eb33567461ed7f4b890cd014373956e2f0d6`, stacked on the DSH 0.1.7 cleanup (`6200e087a37f8be6c252c951aeabea6c7efa0806`). macOS 15.6, Node 25.1.0, pnpm 11.19.0 and published DSH 0.1.7-rc.2. The WebUI ran from `pnpm e2e:serve` with an isolated profile and a scripted loopback model; no personal memory, credentials or external Provider was used. + +## Visible result + +The `dsh-mnemon` page under Plugins shows the main Strategy choice and the memory enhancements above DSH's own component list, with a pointer to Settings for detailed options. Settings → Memory System shows the same controls. A switch applies through one View transaction and changes the DSH component switches to match. + +| State | Plugins page | Settings | +|---|---|---| +| Default (three-tier, enhancements off) | [Chinese](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/main-strategy-choice-20260927/plugins-zh-default.png) | | +| General with light context | [Chinese](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/main-strategy-choice-20260927/plugins-zh-general.png), [English](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/main-strategy-choice-20260927/plugins-en-general.png) | [Chinese](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/main-strategy-choice-20260927/settings-zh-general.png), [English](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/main-strategy-choice-20260927/settings-en-general.png) | + +The captures were taken with Settings opened over the Plugins page. That also exposed duplicate element ids between the two mounted copies of the controls; each copy now has its own ids, a change in one reloads the other, and a rejected change shows the Host's current state. + +## General Strategy conversation + +`pnpm e2e:serve --general-strategy` selects the general Strategy and scripts only the model's decisions. Turn 1 (`general-strategy-check remember`) checked that the general memory protocol was in the system prompt, that the Runtime, Documents and Memory Spaces Sources were admitted, and that the named Runtime tool and the Route envelope were offered; it then saved one fact, which returned a committed receipt. Turn 2 (`general-strategy-check recall`) found that fact in the resident projection. See [the conversation](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/main-strategy-choice-20260927/general-conversation.png) and [evidence.json](./evidence.json). + +## Validation + +- `pnpm verify`: documentation (2,104 local links, 81 anchors), deterministic build (42 files), build, typecheck and tests for 17 workspace packages (general Strategy 6, three-tier 17, Runtime 61, Documents 39, Memory Spaces 168 with 2 conditional skips), root suite 1,417 passed with 6 conditional skips, real Headless activation (37 tools, 8 representative Mnemon tools), legacy settings import and restart, the root disable gate, 52 packed files, publint and attw. +- `pnpm verify:plugins`: 17 independent plugin repositories and 18 packed artifacts, including the external consumer applying a packed enhancement to the packed general Strategy, three optional Strategies in real DSH, and a real DSH upgrade from published 0.5.15. +- `pnpm release:intent` and `pnpm release:check`: passed; nine packages carry patch intents, including the introduced general Strategy. + +## Limits + +The scripted model shows that the protocol, Sources, tools and projection reach a real turn; it says nothing about how a live model chooses among Sources. The Headless upgrade in `verify:plugins` starts from published 0.5.15, whose pinned plugin versions mostly equal the packed ones, so pnpm may keep the published copy of an unchanged plugin version there; the independent-install, external-consumer and Strategy-composition checks cover the packed plugin code. The Plugins page still shows DSH's `cordis:group` row as closed ([dsh-external/issues#649](https://github.com/dsh-external/issues/issues/649)). diff --git a/dsh/docs/pr-assets/main-strategy-choice-20260927/README.zh-CN.md b/dsh/docs/pr-assets/main-strategy-choice-20260927/README.zh-CN.md new file mode 100644 index 000000000..a6e1e9a02 --- /dev/null +++ b/dsh/docs/pr-assets/main-strategy-choice-20260927/README.zh-CN.md @@ -0,0 +1,30 @@ +# 主策略选择与通用主策略 + +[English](./README.md) + +实测实现:`4a42eb33567461ed7f4b890cd014373956e2f0d6`,叠加在 DSH 0.1.7 清理(`6200e087a37f8be6c252c951aeabea6c7efa0806`)之上。环境为 macOS 15.6、Node 25.1.0、pnpm 11.19.0 与正式 DSH 0.1.7-rc.2。WebUI 通过 `pnpm e2e:serve` 运行,使用隔离 profile 与脚本化回环模型;未使用个人记忆、凭据或外部 Provider。 + +## 展示结果 + +“插件”中的 `dsh-mnemon` 页面在 DSH 原生组件列表上方显示主策略选择与记忆增强,并提示详细参数在设置中调整。**设置 → 记忆系统**显示同一组控件。切换经一次 View 事务生效,DSH 组件开关随之一致。 + +| 状态 | 插件页 | 设置 | +|---|---|---| +| 默认(默认三层,增强关闭) | [中文](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/main-strategy-choice-20260927/plugins-zh-default.png) | | +| 通用 + 轻量上下文 | [中文](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/main-strategy-choice-20260927/plugins-zh-general.png)、[English](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/main-strategy-choice-20260927/plugins-en-general.png) | [中文](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/main-strategy-choice-20260927/settings-zh-general.png)、[English](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/main-strategy-choice-20260927/settings-en-general.png) | + +截图时设置对话框叠在插件页之上,由此发现两份同时挂载的控件使用了重复的元素 id;现在每份控件有各自的 id,一处的修改会让另一处重新加载,被拒绝的修改会显示 Host 当前状态。 + +## 通用主策略对话 + +`pnpm e2e:serve --general-strategy` 选中通用主策略,只把模型决策脚本化。第 1 回合(`general-strategy-check remember`)确认系统提示包含通用记忆协议,Runtime、项目档案与记忆空间三个 Source 均已接入,并提供了具名 Runtime 工具与 Route 信封;随后保存一条事实,得到已提交回执。第 2 回合(`general-strategy-check recall`)在常驻投影中读到了这条事实。见[对话截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/main-strategy-choice-20260927/general-conversation.png)与 [evidence.json](./evidence.json)。 + +## 验证 + +- `pnpm verify`:文档(2,104 个本地链接、81 个锚点)、确定性构建(42 个文件)、17 个 workspace 包的构建、类型检查与测试(通用主策略 6、三层 17、Runtime 61、项目档案 39、记忆空间 168 且 2 个条件跳过)、根测试 1,417 通过且 6 个条件跳过、真实 Headless 激活(37 个工具,其中 8 个代表性 Mnemon 工具)、旧设置导入与重启、根停用门、打包 52 个文件、publint 与 attw。 +- `pnpm verify:plugins`:17 个独立插件仓库与 18 个打包制品通过,包括外部消费者把打包的增强作用于打包的通用主策略、真实 DSH 同时启用三个可选策略,以及从正式 0.5.15 升级的真实 DSH 检查。 +- `pnpm release:intent` 与 `pnpm release:check`:通过;九个包带 patch 发布意图,包括新增的通用主策略。 + +## 限制 + +脚本化模型只证明协议、Source、工具与投影进入了真实回合,不代表真实模型如何在 Source 之间取舍。`verify:plugins` 的 Headless 升级从正式 0.5.15 开始,其固定的插件版本大多与打包版本相同,pnpm 在该步骤可能沿用未变版本插件的已发布副本;打包插件代码由独立安装、外部消费者与策略组合检查覆盖。插件页仍把 DSH 的 `cordis:group` 行显示为已关闭([dsh-external/issues#649](https://github.com/dsh-external/issues/issues/649))。 diff --git a/dsh/docs/pr-assets/main-strategy-choice-20260927/evidence.json b/dsh/docs/pr-assets/main-strategy-choice-20260927/evidence.json new file mode 100644 index 000000000..84167e179 --- /dev/null +++ b/dsh/docs/pr-assets/main-strategy-choice-20260927/evidence.json @@ -0,0 +1,185 @@ +{ + "testedImplementation": "4a42eb33567461ed7f4b890cd014373956e2f0d6", + "base": "6200e087a37f8be6c252c951aeabea6c7efa0806", + "environment": { + "os": "macOS 15.6", + "node": "v25.1.0", + "pnpm": "11.19.0", + "dsh": "0.1.7-rc.2" + }, + "generalStrategyConversation": { + "command": "pnpm e2e:serve --general-strategy", + "turns": [ + "general-strategy-check remember", + "general-strategy-check recall" + ], + "events": [ + { + "event": "composition", + "protocol": true, + "admitted": [ + "source:include:mnemon-source-runtime", + "source:include:mnemon-source-documents", + "source:include:mnemon-source-memory-spaces" + ], + "namedRuntimeTool": true, + "routes": true + }, + { + "event": "write", + "success": true, + "receipt": "succeeded" + }, + { + "event": "recall", + "resident": true + } + ] + }, + "screens": [ + { + "file": "plugins-zh-default.png", + "state": { + "mainStrategy": "default-three-tier", + "enhancements": [] + }, + "text": [ + "主策略", + "同一时间只由一个主策略组合记忆视图;切换后从下一轮对话生效。", + "默认三层", + "运行时常驻,档案与长期记忆按需读取", + "通用", + "全部来源可用,由模型决定如何使用", + "记忆增强", + "为当前主策略开启可选行为;切换后立即生效。", + "主动记录", + "在当前对话中识别并保存值得长期保留的事实", + "轻量上下文", + "减少常驻内容,同时保留按需读取能力", + "范围组合", + "按稳定顺序组合当前可用的记忆来源" + ] + }, + { + "file": "plugins-zh-general.png", + "state": { + "mainStrategy": "general", + "enhancements": [ + "light-context" + ] + }, + "text": [ + "主策略", + "同一时间只由一个主策略组合记忆视图;切换后从下一轮对话生效。", + "默认三层", + "运行时常驻,档案与长期记忆按需读取", + "通用", + "全部来源可用,由模型决定如何使用", + "记忆增强", + "为当前主策略开启可选行为;切换后立即生效。", + "主动记录", + "在当前对话中识别并保存值得长期保留的事实", + "轻量上下文", + "减少常驻内容,同时保留按需读取能力", + "范围组合", + "按稳定顺序组合当前可用的记忆来源" + ] + }, + { + "file": "settings-zh-general.png", + "state": { + "mainStrategy": "general", + "enhancements": [ + "light-context" + ] + }, + "text": [ + "主策略", + "同一时间只由一个主策略组合记忆视图;切换后从下一轮对话生效。", + "默认三层", + "运行时常驻,档案与长期记忆按需读取", + "通用", + "全部来源可用,由模型决定如何使用", + "记忆增强", + "为当前主策略开启可选行为;切换后立即生效。", + "主动记录", + "在当前对话中识别并保存值得长期保留的事实", + "轻量上下文", + "减少常驻内容,同时保留按需读取能力", + "范围组合", + "按稳定顺序组合当前可用的记忆来源" + ] + }, + { + "file": "plugins-en-general.png", + "state": { + "mainStrategy": "general", + "enhancements": [ + "light-context" + ] + }, + "text": [ + "Main strategy", + "One main strategy composes the memory view at a time; a switch applies from the next turn.", + "Default three-tier", + "Resident runtime memory, on-demand documents and durable memory", + "General", + "Every source available; the model decides how to use each", + "Memory enhancements", + "Enable optional behavior for the selected main strategy. Changes apply immediately.", + "Active capture", + "Identify and retain durable facts from the current conversation", + "Light context", + "Reduce resident content while keeping on-demand reads available", + "Scoped composition", + "Compose currently available memory sources in a stable order" + ] + }, + { + "file": "settings-en-general.png", + "state": { + "mainStrategy": "general", + "enhancements": [ + "light-context" + ] + }, + "text": [ + "Main strategy", + "One main strategy composes the memory view at a time; a switch applies from the next turn.", + "Default three-tier", + "Resident runtime memory, on-demand documents and durable memory", + "General", + "Every source available; the model decides how to use each", + "Memory enhancements", + "Enable optional behavior for the selected main strategy. Changes apply immediately.", + "Active capture", + "Identify and retain durable facts from the current conversation", + "Light context", + "Reduce resident content while keeping on-demand reads available", + "Scoped composition", + "Compose currently available memory sources in a stable order" + ] + } + ], + "consoleErrors": [], + "verification": { + "verify": { + "documentationLinks": 2104, + "anchors": 81, + "deterministicFiles": 42, + "workspacePackages": 17, + "rootTests": { + "passed": 1417, + "skipped": 6 + }, + "headlessTools": 37, + "packedFiles": 52 + }, + "verifyPlugins": { + "independentRepositories": 17, + "packedArtifacts": 18, + "upgradeFrom": "0.5.15" + }, + "releaseIntentPackages": 9 + } +} diff --git a/dsh/docs/pr-assets/npm-sidebar-cli/README.md b/dsh/docs/pr-assets/npm-sidebar-cli/README.md new file mode 100644 index 000000000..af07a6f81 --- /dev/null +++ b/dsh/docs/pr-assets/npm-sidebar-cli/README.md @@ -0,0 +1,177 @@ +# npm Sidebar and CLI regression — 2026-08-30 + +Historical evidence / 历史证据。These instructions and results belong to the named 2026-08-30 builds, not the current checkout. 操作步骤与成绩仅属于当时的构建,不代表当前版本。 + +[Recorded source](https://github.com/omdsh-dev/dsh-mnemon/tree/5907523) · [Current WebUI fixture](../../en/development/README.md#real-webui) · [当前 WebUI 夹具](../../zh-CN/development/README.md#真实-webui) + +## English + +The latest [issue #139 Builtin verification record](../builtin-139/README.md) covers the unmodified npm 0.4.1 control, canonical `builtin` migration, all three storage scopes, and real Web screenshots. The baseline description and 2026-08-30 record below remain historical evidence for the earlier npm 0.3.5 regressions. + +This regression uses the `v0.3.5` source corresponding to npm `dsh-mnemon@0.3.5` as its baseline, not the view-based development branch (then labeled 0.4.0, now planned for v0.5). The Host is pinned to the latest non-alpha npm release at the time of testing, `@deepseek-ai/dsh@0.1.1-rc.2`. The Web UI is `@linxin666/dsh-web-all@0.3.6`, with additional coverage of its original package name, `@linxin666/dsh-web-ui-all@0.3.6`. + +The fixes were first completed against that npm baseline, then synchronized with `main`, still at 0.3.5, and verified again for the PR. Keep separate records of the original and patched packages; current `main` is not an unmodified npm control. + +### Isolated startup + +Install dependencies and build in a separate worktree: + +```sh +pnpm install --frozen-lockfile +pnpm build +pnpm --workspace-concurrency=1 -r build +node scripts/serve-web-regression.mjs --cli /absolute/path/to/test-owned/mnemon +``` + +Each run creates an independent DSH_HOME, memory directory, workspace, random loopback port, and local model response service. It does not change global dsh, user profiles, personal memories, or existing workspaces. The session model returns fixed test text without calling a paid model. Shell tools and optional SSH/PTY/tunnel installation scripts are outside this regression. `fixture.json` records versions, directories, test switches, and owned processes; `dsh.log` stores this instance's service log. Ctrl-C stops only this instance and retains its directory for audit. + +`--package dsh-mnemon@0.3.5` starts the unmodified npm control; `--package /absolute/path/to/local-pack.tgz` installs a locally built patch. By default, the profile uses `cliPath: mnemon` and a child-process-only PATH to test command-name resolution instead of bypassing the issue with an absolute cliPath. Use `--cli-name` to select a different configured value. + +### Panel navigation + +#### Entry placement and storage scope + +The harness also accepts `--display-mode sidebar|builtin` (default `sidebar`, with legacy `buildin` input accepted) and `--storage-scope global|workspace|custom` (default `custom`). Global mode uses the fixture-owned `MNEMON_DATA_DIR`, workspace mode uses the test session's `.mnemon`, and custom mode uses the fixture's explicit directory. No personal root is used. + +```sh +node scripts/serve-web-regression.mjs --cli /absolute/path/to/test-owned/mnemon --display-mode buildin --storage-scope workspace +node scripts/serve-web-regression.mjs --cli /absolute/path/to/test-owned/mnemon --package dsh-mnemon@0.4.1 --display-mode buildin --storage-scope workspace +``` + +Both commands intentionally start with the old spelling; the second is the unmodified issue #139 control. The patched Host must save `mnemon.displayMode: builtin` automatically. Verify that the conversation tab shares Sidebar pages and dialogs, has no scope selector, and follows the owning session for reads and writes. Switch entry placement live and reload to check persistence; switch conversations across two workspaces to check isolation. Record the linked commit or packed artifact separately from the displayed package version. + +`pnpm verify:headless` also starts with a legacy YAML user setting, checks its canonical writeback and preservation of unrelated fields/comments, then restarts Headless to verify idempotence. No Web connection or real model is needed for migration. + +#### Sidebar panel round trips + +1. Click Memory System → Task Board → Memory System, checking the genuinely visible main page after each step. +2. Repeat multiple rounds, including session and task data, SSH, returning to the conversation, collapsing and reopening the sidebar, and reloading. +3. Confirm that session content, task data, and the selected memory page remain intact instead of checking only sidebar highlights. +4. Use `--mnemon-first` to cover installing Mnemon before the Web UI. + +The reported stuck navigation did not reproduce directly with the ordinary npm combination in this run. Do not describe it as an inevitable failure of the default original environment. Code-level tests demonstrate desynchronization when an activation announcement is missing: Mnemon's private state still says open, so clicking its entry incorrectly closes it. + +For a repeatable browser before/after comparison, add `--panel-event-loss`. This installs a **test-only plugin excluded from the published package** that drops the active Task Board's `dsh-panel-activate` announcement. It does not replace Task Board, modify npm plugin code, or rewrite the displayed result. This explicitly labeled, controlled compatibility fault does not establish that the user's environment lost that announcement. + +The control still displays Task Board after the third click; the patched package restores Memory System. Unit tests also cover SSH, programmatic DOM activation, navigation before synchronization, and subsequent refreshes under the legacy Web UI protocol so a hidden panel cannot reclaim the foreground. + +### CLI checks + +1. Confirm that the test CLI is executable and on the harness child processes' PATH. +2. With `cliPath: mnemon`, the original status page incorrectly reports that Mnemon CLI was not found, while the version dialog displays its version. +3. The patched package agrees in both places. With no active Memory Space, it reports service readiness rather than pretending to have a healthy active space. +4. Temporarily move the CLI within its test-owned directory and click Recheck: it should report missing. Restore the file and recheck again: availability should recover without restarting DSH. +5. Create and activate a test native Memory Space to verify actual CLI status execution and health. Never use personal memory directories. + +### Automated verification + +```sh +pnpm typecheck +pnpm exec vitest run --reporter=json --outputFile=/absolute/path/to/test-results.json +pnpm verify:build +pnpm verify:headless +pnpm verify:package +``` + +Save screenshots, browser-visible state records, and before/after JSON test results alongside the run's `fixture.json`. Label screenshots as ordinary-environment or controlled-fault evidence. + +### Public verification record: 2026-08-30 + +The original control is the unmodified npm 0.3.5 release. PR code commit `5907523` incorporates `main` at `05c128b`; the patched screenshots below use a local tarball built from that commit, not a newly published version. The CLI is official Mnemon 0.2.5 for macOS arm64. + +| Scenario | Screenshot | Visible result | +|---|---|---| +| Original, ordinary environment | [Incorrect CLI status](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/npm-sidebar-cli/before-cli.jpg) | The installed CLI is reported missing | +| PR code, ordinary environment | [After ten round trips](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/npm-sidebar-cli/after-normal.jpg) | Memory System is visible; the CLI reports readiness with no active Memory Space | +| Original, controlled announcement loss | [Failed return](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/npm-sidebar-cli/before-controlled.jpg) | Task Board remains visible after the third click on Memory System | +| PR code, controlled announcement loss | [Successful return](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/npm-sidebar-cli/after-controlled.jpg) | Memory System is visible after the third click | + +After synchronization, `pnpm run verify` passed 541 tests and skipped one Windows-only test on macOS. Determinism for 106 build files, 35 Headless tools, 10 Node-compatible public entries, publint, and attw all passed. The upstream rc.2 missing-source-map warning remains and does not fail verification. The ordinary environment completed another ten real panel round trips, the CLI version check showed 0.2.5, and the browser console had no errors. The controlled run confirmed that the test plugin actually dropped an announcement and still returned successfully. + +Patch tarball SHA-256: `0d3757f30a7f1dd79a31bec5d988bbdcdf20a9802f5b75d0e8aa9072b74b68f9`. Raw logs, version dialogs showing machine paths, and fixtures remain local; the public screenshots contain no credentials or personal memory. + +## 简体中文 + +最新的 [issue #139 Builtin 验证记录](../builtin-139/README.md)覆盖未修改的 npm 0.4.1 对照版、规范 `builtin` 迁移、三种存储范围及真实 Web 截图。下述基线说明和 2026-08-30 记录仍保留为较早 npm 0.3.5 回归的历史证据。 + +本轮以 npm `dsh-mnemon@0.3.5` 对应的 `v0.3.5` 源码为基线,不使用 view-based 开发分支(当时标为 0.4.0,现计划于 v0.5 发布)。Host 固定为 npm 的非 alpha 最新版 `@deepseek-ai/dsh@0.1.1-rc.2`;Web UI 使用 `@linxin666/dsh-web-all@0.3.6`,并覆盖原包名 `@linxin666/dsh-web-ui-all@0.3.6`。 + +修复先在该 npm 基线上完成,再同步仍为 0.3.5 的 `main` 并重新验证 PR 代码。保留原版与补丁版的独立记录,不能将最新 `main` 当作未经修改的 npm 对照版。 + +### 隔离启动 + +在独立 worktree 中安装依赖并构建: + +```sh +pnpm install --frozen-lockfile +pnpm build +pnpm --workspace-concurrency=1 -r build +node scripts/serve-web-regression.mjs --cli /absolute/path/to/test-owned/mnemon +``` + +脚本为每次运行创建独立 DSH_HOME、记忆目录、工作区、随机 loopback 端口及本地模型响应服务。不会修改全局 dsh、用户 profile、真实记忆或已有工作区。会话模型只返回固定测试文本,不调用付费模型;shell 工具与可选的 SSH/PTY/tunnel 安装脚本未参与此回归。`fixture.json` 记录版本、目录、测试开关和所属进程;`dsh.log` 保存本次服务日志。Ctrl-C 只停止本次测试实例,保留目录供审计。 + +`--package dsh-mnemon@0.3.5` 启动未修改的 npm 对照版;`--package /absolute/path/to/local-pack.tgz` 安装本地构建的补丁包。默认以 `cliPath: mnemon` 和仅对子进程生效的 PATH 验证命令名解析,而不是用绝对 cliPath 绕过问题。可通过 `--cli-name` 指定另一个配置值。 + +### 页面切换 + +#### 入口位置与存储范围 + +脚本还接受 `--display-mode sidebar|builtin`(默认 `sidebar`,也接受旧输入 `buildin`)与 `--storage-scope global|workspace|custom`(默认 `custom`)。全局模式使用夹具独占的 `MNEMON_DATA_DIR`,工作区模式使用测试会话的 `.mnemon`,自定义模式使用夹具的显式目录,不使用个人数据根。 + +```sh +node scripts/serve-web-regression.mjs --cli /absolute/path/to/test-owned/mnemon --display-mode buildin --storage-scope workspace +node scripts/serve-web-regression.mjs --cli /absolute/path/to/test-owned/mnemon --package dsh-mnemon@0.4.1 --display-mode buildin --storage-scope workspace +``` + +两条命令都刻意使用旧拼写启动,第二条是 issue #139 的未修改对照组;修复后的 Host 必须自动保存 `mnemon.displayMode: builtin`。会话标签页应共用 Sidebar 页面与弹窗、不显示范围选择,并按所属会话读写。实时切换入口并刷新检查持久化;在两个工作区之间切换会话检查隔离。单独记录 link commit 或打包产物,不把页面显示的包版本当作修复身份。 + +`pnpm verify:headless` 也会从含旧拼写的 YAML 用户设置启动,检查规范写回、其他字段及注释保留,再重启 Headless 验证幂等性。迁移不依赖 Web 连接或真实模型。 + +#### Sidebar 面板往返 + +1. 点击“记忆系统 → 任务看板 → 记忆系统”,每一步检查真正可见的主页面。 +2. 重复多轮,覆盖有会话、任务数据、SSH、返回会话、侧栏收起再展开和刷新页面。 +3. 检查会话内容、任务数据与记忆页选择仍保留,而不是只检查侧栏高亮。 +4. 使用 `--mnemon-first` 覆盖先安装 Mnemon、再安装 Web UI 的顺序。 + +普通 npm 组合下本轮未直接复现用户报告的卡住现象;不要将其描述为“原版默认环境必现”。代码级测试证实了激活通知缺失时的状态失同步:Mnemon 私有状态仍为打开,再点入口会错误地执行关闭。 + +为得到可重复的浏览器前后对照,可加 `--panel-event-loss`。该开关安装一个 **仅供测试、不进入发布包** 的插件,丢弃处于打开状态的任务看板的 `dsh-panel-activate` 通知。它不替换任务看板、不修改 npm 插件代码,也不改写页面显示结果。这是明确标记的受控兼容性故障,不能用来声称用户环境确实发生了通知丢失。 + +对照版第三次点击仍显示任务看板;补丁版可恢复记忆系统。单元测试还覆盖 SSH、程序化 DOM 激活、同步前再次导航,以及旧 Web UI 协议中的后续刷新,防止被隐藏的面板重新抢回前台。 + +### CLI 检查 + +1. 确认测试 CLI 可执行且在脚本子进程的 PATH 中。 +2. 原版 `cliPath: mnemon` 的状态页误报“未找到 Mnemon CLI”,但版本弹窗能显示 CLI 版本。 +3. 补丁版两处一致;没有激活的记忆空间时显示“服务就绪”,并不冒充已有健康记忆空间。 +4. 在测试专属目录内临时移走 CLI,点击“重新检查”应显示缺失;恢复文件后再次检查应恢复,无需重启 DSH。 +5. 创建并激活一个测试原生记忆空间,验证实际 CLI 状态调用与健康显示。不要操作真实记忆目录。 + +### 自动化验证 + +```sh +pnpm typecheck +pnpm exec vitest run --reporter=json --outputFile=/absolute/path/to/test-results.json +pnpm verify:build +pnpm verify:headless +pnpm verify:package +``` + +截图、浏览器可见状态记录及前后 JSON 测试结果应与运行的 `fixture.json` 一起保存。截图必须标明普通环境还是受控故障环境。 + +### 2026-08-30 公开验证记录 + +原版对照为未经修改的 npm 0.3.5。PR 代码提交 `5907523` 已同步 `main` 的 `05c128b`;下表的修复后截图来自该提交打出的本地 tarball,不是已发布的新版本。CLI 为官方 Mnemon 0.2.5(macOS arm64)。 + +| 场景 | 截图 | 可见结果 | +|---|---|---| +| 原版,普通环境 | [CLI 状态误报](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/npm-sidebar-cli/before-cli.jpg) | 已安装 CLI,却显示“未找到 Mnemon CLI” | +| PR 代码,普通环境 | [十轮切换后](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/npm-sidebar-cli/after-normal.jpg) | 返回记忆系统;CLI 显示服务就绪,无已激活记忆空间 | +| 原版,受控通知丢失 | [返回失败](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/npm-sidebar-cli/before-controlled.jpg) | 第三次点击记忆系统后仍显示任务看板 | +| PR 代码,受控通知丢失 | [返回成功](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/npm-sidebar-cli/after-controlled.jpg) | 第三次点击后显示记忆系统 | + +同步后的 `pnpm run verify`:541 项通过、1 项 Windows 专用测试在 macOS 跳过;106 个构建文件确定性检查、35 个 Headless 工具、10 个 Node 兼容公共入口、publint 与 attw 均通过。上游 rc.2 缺失 source map 的告警保留,不影响测试结果。普通环境再次完成十轮真实页面切换,CLI 版本检查显示 0.2.5,浏览器控制台无 error;受控故障环境确认测试插件确实丢弃通知后仍可返回。 + +补丁 tarball SHA-256:`0d3757f30a7f1dd79a31bec5d988bbdcdf20a9802f5b75d0e8aa9072b74b68f9`。原始日志、带本机路径的版本弹窗与 fixture 仅保存在本地;公开截图没有凭据或个人记忆。 diff --git a/dsh/docs/pr-assets/optional-mnemon-cli-20260927/README.md b/dsh/docs/pr-assets/optional-mnemon-cli-20260927/README.md new file mode 100644 index 000000000..aa39a6bbb --- /dev/null +++ b/dsh/docs/pr-assets/optional-mnemon-cli-20260927/README.md @@ -0,0 +1,39 @@ +# Mnemon CLI as an optional Provider backend + +[简体中文](./README.zh-CN.md) + +Tested implementation: `c45680308ad74be9963f9e20e41897da5af2f9a8`, stacked on the main Strategy choice (`533b67fe78470fb08488a379346313f26e80ec21`). macOS 15.6, Node 25.1.0, pnpm 11.19.0, published DSH 0.1.7-rc.2 and Mnemon CLI 0.2.7. Third-party Providers ran from the repository's [Provider Lab](../../../provider-lab/README.md) in Docker, bound to `127.0.0.1`, with Ollama 0.34.4 in a container serving `qwen2.5:3b` and `nomic-embed-text` on the CPU. No personal memory or credentials were used. + +## Without the Mnemon CLI + +`pnpm e2e:serve --without-mnemon-cli` hides the installed CLI. Mem0 was then connected under Settings → Memory System as a self-hosted service. + +| Surface | Result | Capture | +|---|---|---| +| Status | No Mnemon Native card; the Provider list shows Mem0 ready | [Status](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/optional-mnemon-cli-20260927/nocli-status.png) | +| Versions | dsh-mnemon first; Mnemon CLI "Not installed (optional)" with its install command | [Versions](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/optional-mnemon-cli-20260927/nocli-versions.png) | +| Settings | Native tagged "CLI not installed (optional)"; the embedding test, which runs the CLI, is disabled | [Settings](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/optional-mnemon-cli-20260927/nocli-settings.png) | +| Create Memory Space | Starts on Mem0; Mnemon Native disabled with "Needs the Mnemon CLI on this machine" | [Create](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/optional-mnemon-cli-20260927/nocli-create.png) | + +Creating the space from that dialog produced a Mem0 space with healthy storage. A Headless run against the same Mem0 container then followed the default persistence strategy: before any Provider was ready, persisting failed with "No memory provider is ready: install the Mnemon CLI to use Mnemon Native, or connect another provider in Settings". After Mem0 was connected, the strategy reported Mem0, the persisted space was created on Mem0, a fact was stored and a natural-language query recalled it. See [evidence.json](./evidence.json). + +## With the Mnemon CLI and the Provider Lab + +A WebUI fixture with the CLI installed was seeded through the public plugin composition (`scripts/seed-provider-lab.mjs`): Mnemon Native, OpenViking, Honcho, Mem0, Hindsight, Holographic, RetainDB and Supermemory each received a space and five facts. See the [Status](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/optional-mnemon-cli-20260927/lab-status.png) capture. + +- Status shows the Mnemon Native card as healthy, with Mnemon 0.2.7 and its space, beside the enabled third-party Providers. +- The create dialog still starts on Mnemon Native while seven other Providers are ready, including four that sort before it. The first version of this change picked the alphabetically first ready Provider; the WebUI check found it, and Native now leads whenever it is ready. +- Recall lists one source per active space and attributes every result to its Provider. With Ollama idle, “记忆系统的三层结构是什么?” (what is the memory system's three-tier structure?) returned the matching fact from OpenViking, Mem0, Honcho and RetainDB, and the fact in the Mem0 space created without the CLI. +- Content lists each enumerable Provider's facts. It showed 43 facts: five each from Mnemon Native, OpenViking, Honcho, Holographic, RetainDB and Supermemory, eleven across three Mem0 spaces, and Hindsight's first two units while its extraction was still running ([Content](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/optional-mnemon-cli-20260927/lab-content.png)). +- Entities offers only Mnemon Native, Hindsight and Holographic as entity indexes; every other Provider is marked unsupported. It reported 24 active entities from Mnemon Native (9), Hindsight (15) and Holographic (13) ([Entities](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/optional-mnemon-cli-20260927/lab-entities.png)). + +## Validation + +- `pnpm verify`: documentation (2,122 local links, 81 anchors), deterministic build (42 files), build, typecheck and tests for 17 workspace packages (Memory Spaces 174 passed with 2 conditional skips, Runtime 61, Documents 39, general Strategy 6), root suite 1,417 passed with 6 conditional skips and one timing-sensitive Session repair test that timed out while Docker extraction loaded the machine and passed alone (132/132), real Headless activation (37 tools), legacy settings import and restart, the root disable gate, 52 packed files, publint and attw. +- `pnpm verify:plugins --skip-build`: 17 independent plugin repositories and 18 packed artifacts, including the standalone Memory Spaces suite and a real DSH upgrade from published 0.5.15. +- `pnpm release:intent`: covered for dsh-mnemon, dsh-mnemon-provider-mnemon-native and dsh-mnemon-source-memory-spaces. +- New regression tests fail on the previous code: persisting without the CLI, Native staying first in an alphabetical catalog, provider stats after a failed version probe, and the create dialog without Native. + +## Limits + +Ollama ran on the CPU inside Docker. Extraction queues (Hindsight retain, Supermemory ingestion, OpenViking semantic writes) saturated it, and while they ran, Providers that embed each query timed out and were shown as unavailable; they answered once the queue drained. One OpenViking write reported a 10 s timeout although the fact landed. ByteRover was not exercised because its `brv` CLI is not installed. The scripted checks show that the paths work end to end, not how well each Provider extracts or ranks. diff --git a/dsh/docs/pr-assets/optional-mnemon-cli-20260927/README.zh-CN.md b/dsh/docs/pr-assets/optional-mnemon-cli-20260927/README.zh-CN.md new file mode 100644 index 000000000..991700c35 --- /dev/null +++ b/dsh/docs/pr-assets/optional-mnemon-cli-20260927/README.zh-CN.md @@ -0,0 +1,39 @@ +# Mnemon CLI 作为可选的 Provider 后端 + +[English](./README.md) + +实测实现:`c45680308ad74be9963f9e20e41897da5af2f9a8`,叠加在主策略选择(`533b67fe78470fb08488a379346313f26e80ec21`)之上。环境为 macOS 15.6、Node 25.1.0、pnpm 11.19.0、正式 DSH 0.1.7-rc.2 与 Mnemon CLI 0.2.7。第三方 Provider 使用仓库中的 [Provider Lab](../../../provider-lab/README.md) 在 Docker 中运行,只绑定 `127.0.0.1`;Ollama 0.34.4 同样运行在容器中,用 CPU 提供 `qwen2.5:3b` 与 `nomic-embed-text`。未使用个人记忆或凭据。 + +## 没有 Mnemon CLI 时 + +`pnpm e2e:serve --without-mnemon-cli` 会隐藏已安装的 CLI。随后在“设置 → 记忆系统”中以自托管方式连接 Mem0。 + +| 页面 | 结果 | 截图 | +|---|---|---| +| 状态 | 不显示 Mnemon Native 卡片;Provider 列表显示 Mem0 就绪 | [状态](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/optional-mnemon-cli-20260927/nocli-status.png) | +| 版本 | 先列 dsh-mnemon;Mnemon CLI 显示“未安装(可选)”并给出安装命令 | [版本](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/optional-mnemon-cli-20260927/nocli-versions.png) | +| 设置 | Native 标记为“未安装 CLI(可选)”;依赖 CLI 的向量测试被停用 | [设置](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/optional-mnemon-cli-20260927/nocli-settings.png) | +| 创建记忆空间 | 默认选中 Mem0;Mnemon Native 被停用并提示“需要在本机安装 Mnemon CLI” | [创建](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/optional-mnemon-cli-20260927/nocli-create.png) | + +从该对话框创建的空间落在 Mem0 上,存储正常。之后对同一个 Mem0 容器执行 Headless 流程,走默认沉淀策略:没有就绪的 Provider 时,沉淀失败并提示“No memory provider is ready: install the Mnemon CLI to use Mnemon Native, or connect another provider in Settings”;连接 Mem0 后,策略显示为 Mem0,沉淀出的空间创建在 Mem0 上,写入一条事实后,用自然语言问题召回到了它。见 [evidence.json](./evidence.json)。 + +## 安装 Mnemon CLI 并接入 Provider Lab 时 + +安装了 CLI 的 WebUI 夹具通过公开的插件组合(`scripts/seed-provider-lab.mjs`)写入测试数据:Mnemon Native、OpenViking、Honcho、Mem0、Hindsight、Holographic、RetainDB 与 Supermemory 各自创建了一个空间并写入五条事实。见[状态](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/optional-mnemon-cli-20260927/lab-status.png)截图。 + +- 状态页把 Mnemon Native 卡片显示为连接正常,含 Mnemon 0.2.7 与它的空间,并列显示已启用的第三方 Provider。 +- 七个其他 Provider 同时就绪(其中四个按字母排在它前面)时,创建对话框仍默认选中 Mnemon Native。这一改动的第一版会选中按字母排序的第一个就绪 Provider;WebUI 检查发现了这个问题,现在只要 Native 就绪就排在首位。 +- 检索按每个激活空间列出一个来源,并为每条结果标注 Provider。Ollama 空闲时,“记忆系统的三层结构是什么?”从 OpenViking、Mem0、Honcho 与 RetainDB 召回了对应事实,也召回了无 CLI 流程中创建的 Mem0 空间里的事实。 +- 内容页列出每个可枚举 Provider 的事实。共显示 43 条:Mnemon Native、OpenViking、Honcho、Holographic、RetainDB 与 Supermemory 各 5 条,三个 Mem0 空间合计 11 条,Hindsight 在抽取仍在进行时显示了前 2 个单元([内容](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/optional-mnemon-cli-20260927/lab-content.png))。 +- 实体页只把 Mnemon Native、Hindsight 与 Holographic 作为实体索引,其他 Provider 都标记为不支持。共 24 个活跃实体,来自 Mnemon Native(9)、Hindsight(15)与 Holographic(13)([实体](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/optional-mnemon-cli-20260927/lab-entities.png))。 + +## 验证 + +- `pnpm verify`:文档(2,122 个本地链接、81 个锚点)、确定性构建(42 个文件)、17 个 workspace 包的构建、类型检查与测试(记忆空间 174 通过且 2 个条件跳过、Runtime 61、项目档案 39、通用主策略 6)、根测试 1,417 通过且 6 个条件跳过,另有一个对计时敏感的 Session 修复测试在 Docker 抽取占满机器时超时,单独运行通过(132/132);真实 Headless 激活(37 个工具)、旧设置导入与重启、根停用门、打包 52 个文件、publint 与 attw。 +- `pnpm verify:plugins --skip-build`:17 个独立插件仓库与 18 个打包制品通过,包括独立运行的记忆空间测试与从正式 0.5.15 升级的真实 DSH 检查。 +- `pnpm release:intent`:覆盖 dsh-mnemon、dsh-mnemon-provider-mnemon-native 与 dsh-mnemon-source-memory-spaces。 +- 新增的回归测试在旧代码上失败:无 CLI 时沉淀、按字母排序的目录中 Native 仍居首位、版本探测失败后仍保留 Provider 统计、没有 Native 时的创建对话框。 + +## 限制 + +Ollama 在 Docker 中用 CPU 运行。抽取队列(Hindsight 事实抽取、Supermemory 摄取、OpenViking 语义写入)会占满它;在此期间,每次查询都需要向量化的 Provider 会超时并显示为不可用,队列清空后恢复正常。有一次 OpenViking 写入报告 10 秒超时,但事实实际已写入。ByteRover 未验证,因为本机没有安装它的 `brv` CLI。脚本化检查只说明各条路径可以端到端走通,不代表各 Provider 的抽取或排序质量。 diff --git a/dsh/docs/pr-assets/optional-mnemon-cli-20260927/evidence.json b/dsh/docs/pr-assets/optional-mnemon-cli-20260927/evidence.json new file mode 100644 index 000000000..27b890b18 --- /dev/null +++ b/dsh/docs/pr-assets/optional-mnemon-cli-20260927/evidence.json @@ -0,0 +1,145 @@ +{ + "testedImplementation": "c45680308ad74be9963f9e20e41897da5af2f9a8", + "base": "533b67fe78470fb08488a379346313f26e80ec21", + "environment": { + "os": "macOS 15.6", + "node": "v25.1.0", + "pnpm": "11.19.0", + "dsh": "0.1.7-rc.2", + "mnemonCli": "0.2.7", + "ollama": "0.34.4", + "models": [ + "qwen2.5:3b", + "nomic-embed-text" + ] + }, + "withoutCli": { + "webui": { + "statusFacts": { + "nativeCard": false, + "providers": "1 / 8 已启用" + }, + "versionFacts": [ + "dsh-mnemon | 本地 Link | 已是最新", + "Mnemon CLI | 未安装 | 未安装(可选)" + ], + "settingsFacts": { + "tag": "未安装 CLI(可选)", + "embedding": "测试需要 Mnemon CLI;向量设置只作用于 Mnemon Native。", + "testDisabled": true + }, + "createFacts": [ + { + "provider": "mem0", + "checked": true, + "disabled": false, + "hint": "始终使用全局范围 · 自动事实提取、语义召回、重排与去重" + }, + { + "provider": "mnemon-native", + "checked": false, + "disabled": true, + "hint": "需要在本机安装 Mnemon CLI;其他 Provider 不受影响" + } + ] + }, + "consoleErrors": 0, + "headlessJourney": [ + { + "name": "directory-before", + "native": false, + "persistenceProvider": "mnemon-native" + }, + { + "name": "persist-before-provider", + "error": "No memory provider is ready: install the Mnemon CLI to use Mnemon Native, or connect another provider in Settings" + }, + { + "name": "directory-after-mem0", + "persistenceProvider": "mem0" + }, + { + "name": "persisted-space", + "id": "mem0-c38ecb9d-195a-48fe-a17a-181687dd2f1e", + "provider": "mem0" + }, + { + "name": "remember", + "action": "stored" + }, + { + "name": "recall", + "count": 1, + "top": "The no-CLI journey stores this decision in Mem0: releases ship on Thursdays after the regression suite passes.", + "provider": "mem0-c38ecb9d-195a-48fe-a17a-181687dd2f1e" + }, + { + "name": "status", + "commandFound": false, + "statsTotal": 0, + "error": null + } + ] + }, + "providerLab": { + "seeded": { + "mnemon-native": 5, + "openviking": 5, + "honcho": 5, + "mem0": 5, + "hindsight": 5, + "holographic": 5, + "retaindb": 5, + "supermemory": 5 + }, + "createDialogWithCli": { + "checked": "mnemon-native", + "readyProviders": [ + "hindsight", + "holographic", + "honcho", + "mem0", + "mnemon-native", + "openviking", + "retaindb", + "supermemory" + ] + }, + "recallQuery": "记忆系统的三层结构是什么?", + "recallMatches": [ + "openviking", + "mem0", + "honcho", + "retaindb", + "mem0 (space created without the CLI)" + ], + "content": { + "total": 43, + "perSpace": { + "mnemon-native": 5, + "openviking": 5, + "honcho": 5, + "mem0": [ + 5, + 5, + 1 + ], + "hindsight": 2, + "holographic": 5, + "retaindb": [ + 5, + 0 + ], + "supermemory": 5 + } + }, + "entityIndexes": { + "mnemon-native": 9, + "hindsight": 15, + "holographic": 13 + }, + "notTested": { + "byterover": "brv CLI not installed" + } + } +} diff --git a/dsh/docs/pr-assets/plugin-config-20260927/README.md b/dsh/docs/pr-assets/plugin-config-20260927/README.md new file mode 100644 index 000000000..2b58e913f --- /dev/null +++ b/dsh/docs/pr-assets/plugin-config-20260927/README.md @@ -0,0 +1,67 @@ +# Configuration on the dsh-mnemon page under Plugins + +[简体中文](./README.zh-CN.md) + +Tested implementation: `7e23e37f`, stacked on the DSH-style interface (`74b214eb`). macOS 15.6, Node 25.1.0, pnpm 11.19.0, published DSH 0.1.7-rc.2 and Mnemon CLI 0.2.7, headless Chrome 153 at 1280 × 860 unless noted. The WebUI fixture holds the Provider Lab data from the [DSH-style interface record](../dsh-style-ui-20260927/README.md): Mnemon Native plus Docker services for OpenViking, Honcho, Mem0, Hindsight, RetainDB and Supermemory, and a local Holographic store. No personal memory or credentials were used. + +"Before" is the base revision as recorded for the DSH-style interface. + +## Configuration moves under Plugins + +| Before: Settings → Memory System | After: Plugins → dsh-mnemon | +|---|---| +| ![Settings before](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/settings-after.png) | ![The dsh-mnemon page](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-config-20260927/plugin-page.png) | + +DSH 0.1.7 edits a plugin's own configuration on its page under Plugins and keeps Settings for DSH's sections and the read-only plugin inventory. The Memory System section is gone from Settings: + +![Settings without a Memory System section](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-config-20260927/settings-dialog.png) + +The dsh-mnemon page (titled 可组合记忆 in Chinese) shows the plugin's title and description, then the configuration in six groups (Strategy, Memory layers, Memory providers, Storage, Background tasks, Interface), then the components the Starter includes. Before this change the page carried only the Strategy rows and a pointer back to Settings: + +| Plugins page before | The whole page after | +|---|---| +| ![Plugins page before](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/plugins-after.png) | ![The whole dsh-mnemon page](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-config-20260927/plugin-page-full.png) | + +## Unsaved changes + +Groups with Save stage their edits; a bar floats 16 px above the bottom of the page while the configuration is in view and rests after its last group. Leaving the page drops the staged edits. + +| Floating over the page | At rest before the components | +|---|---| +| ![Floating save bar](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-config-20260927/plugin-page-floating.png) | ![Components after the configuration](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-config-20260927/plugin-page-components.png) | + +## Between the workspace and its configuration + +The page head offers **Open Memory System** (`plugins.detail.actions`), and the Memory System header offers **Configure**, the gear next to refresh, through `ctx.pluginNavigation.openBundle('dsh-mnemon')`. A switched-off memory layer page links to the configuration too. + +![Memory System header with Configure](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-config-20260927/workspace-configure.png) + +## Themes and narrow layout + +| Dark theme | Dark theme, unsaved | 700 × 900 | +|---|---|---| +| ![Dark theme](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-config-20260927/dark-plugin-page.png) | ![Dark theme with the save bar](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-config-20260927/dark-plugin-page-unsaved.png) | ![Narrow layout](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-config-20260927/narrow-plugin-page.png) | + +## Remote pages + +Chrome mapped `memory.test` to 127.0.0.1 and the fixture ran with `pnpm e2e:serve --trusted-host=memory.test:`, so DSH and Mnemon treated the page as remote. The DSH Plugins page, the configuration and both links work there. + +- Default (`remoteAccess: read-only`): the read-only notice leads the page, every control including the Strategy rows is disabled, and a click changes nothing. +- With `--remote-management` (`remoteAccess: trusted-host`): a Save of the Save to memory switch persisted through the API Gateway across a page reload and was then restored. + +![Remote page without the management grant](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-config-20260927/remote-readonly-plugin-page.png) + +## Live refresh + +Two browser tabs on the same loopback WebUI both showed the dsh-mnemon page. Saving the Save to memory switch in the second tab updated the first tab without a reload, through DSH's `configForms` revision of the `mnemon` entry; restoring it synchronized back. + +## Validation + +- `pnpm run verify`: documentation 2,216 local links and 83 anchors; deterministic build of 42 files; build, typecheck and tests for the workspace packages (Memory Spaces 174 passed with 2 conditional skips, Runtime 61, Documents 39); root suite 1,424 passed with 6 conditional skips; real Headless activation (37 tools), legacy settings import with an idempotent restart and the root disable gate; 52 packed files, publint strict and attw. +- `pnpm run verify:plugins --skip-build`: 17 independent plugin repositories and 18 packed artifacts, including real DSH activation of the packed Starter and of the three optional Strategies. +- `pnpm run release:intent`: change intents cover every changed package. +- WebUI: 9 light, 8 dark and 8 narrow states on the loopback fixture and 8 states on each remote fixture, with no console errors in any run. + +## Limits + +Screenshots come from one seeded fixture and show layout and styling, not Provider behavior. The isolated fixture profile leaves DSH's permission presets unavailable, so General in Settings shows DSH's own unavailable notice. Builtin placement opens the workspace from the plugin page only while a conversation is current. Components of the Starter still show their package names on the plugin page; localized row titles and icons from each package's display metadata are a follow-up. diff --git a/dsh/docs/pr-assets/plugin-config-20260927/README.zh-CN.md b/dsh/docs/pr-assets/plugin-config-20260927/README.zh-CN.md new file mode 100644 index 000000000..faae4d80b --- /dev/null +++ b/dsh/docs/pr-assets/plugin-config-20260927/README.zh-CN.md @@ -0,0 +1,67 @@ +# “插件”中的 dsh-mnemon 配置页 + +[English](./README.md) + +实测实现:`7e23e37f`,叠加在与 DSH 统一的界面(`74b214eb`)之上。环境为 macOS 15.6、Node 25.1.0、pnpm 11.19.0、正式 DSH 0.1.7-rc.2 与 Mnemon CLI 0.2.7,无头 Chrome 153,除特别说明外为 1280 × 860。WebUI 夹具使用[与 DSH 统一的界面记录](../dsh-style-ui-20260927/README.zh-CN.md)中的 Provider Lab 数据:Mnemon Native,加上 Docker 中的 OpenViking、Honcho、Mem0、Hindsight、RetainDB、Supermemory,以及本地 Holographic。未使用个人记忆或凭据。 + +“改版前”即与 DSH 统一的界面记录中的 base 版本。 + +## 配置移到“插件”中 + +| 改版前:设置 → 记忆系统 | 改版后:插件 → 可组合记忆 | +|---|---| +| ![改版前的设置页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/settings-after.png) | ![dsh-mnemon 页面](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-config-20260927/plugin-page.png) | + +DSH 0.1.7 在“插件”中每个插件自己的页面编辑其配置,“设置”只保留 DSH 自身的分组与只读的插件清单。“设置”中已不再有“记忆系统”: + +![不再包含记忆系统的设置](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-config-20260927/settings-dialog.png) + +可组合记忆 (dsh-mnemon) 页面依次显示插件的标题与说明、分为六组的配置(策略、记忆层、记忆 Provider、存储、后台任务、界面),以及 Starter 包含的组件。改版前这个页面只有策略行和一句指回“设置”的提示: + +| 改版前的插件页 | 改版后的完整页面 | +|---|---| +| ![改版前的插件页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/dsh-style-ui-20260927/plugins-after.png) | ![完整的 dsh-mnemon 页面](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-config-20260927/plugin-page-full.png) | + +## 未保存的修改 + +需要保存的分组会暂存修改;配置在视口内时,保存栏浮在页面底部上方 16 px,滚过最后一组后停在配置末尾。离开页面即放弃暂存的修改。 + +| 浮在页面上 | 停在组件列表之前 | +|---|---| +| ![浮动的保存栏](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-config-20260927/plugin-page-floating.png) | ![配置之后的组件列表](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-config-20260927/plugin-page-components.png) | + +## 工作台与配置之间 + +页首提供**打开记忆系统**(`plugins.detail.actions`),记忆系统顶部在刷新按钮旁提供**配置**(齿轮),通过 `ctx.pluginNavigation.openBundle('dsh-mnemon')` 打开该页面。已关闭的记忆层页面也提供前往配置的入口。 + +![带“配置”的记忆系统顶部](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-config-20260927/workspace-configure.png) + +## 主题与窄布局 + +| 深色主题 | 深色主题,有未保存修改 | 700 × 900 | +|---|---|---| +| ![深色主题](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-config-20260927/dark-plugin-page.png) | ![深色主题的保存栏](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-config-20260927/dark-plugin-page-unsaved.png) | ![窄布局](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-config-20260927/narrow-plugin-page.png) | + +## 远程页面 + +Chrome 把 `memory.test` 解析到 127.0.0.1,夹具以 `pnpm e2e:serve --trusted-host=memory.test:` 运行,因此 DSH 与 Mnemon 都把页面视为远程页面。DSH 插件页、配置与两个入口在远程页面上均可用。 + +- 默认(`remoteAccess: read-only`):只读提示位于页面最上方,包括策略行在内的全部控件停用,点击不产生任何修改。 +- 加上 `--remote-management`(`remoteAccess: trusted-host`):保存“存入记忆按钮”开关后,修改经 API Gateway 持久化,整页刷新后仍然保留,随后已恢复原值。 + +![没有管理授权的远程页面](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-config-20260927/remote-readonly-plugin-page.png) + +## 实时刷新 + +同一个回环 WebUI 的两个浏览器标签页都打开 dsh-mnemon 页面。在第二个标签页保存“存入记忆按钮”开关后,第一个标签页无需刷新即更新,依据是 DSH `configForms` 中 `mnemon` 条目的 revision;恢复原值后同样同步回来。 + +## 验证 + +- `pnpm run verify`:文档 2,216 个本地链接与 83 个锚点;确定性构建 42 个文件;各 workspace 包的构建、类型检查与测试(记忆空间 174 通过、2 个条件跳过,运行时 61,档案 39);根测试 1,424 通过、6 个条件跳过;真实 Headless 激活(37 个工具)、旧设置导入与幂等重启、根停用门;打包 52 个文件,publint strict 与 attw 通过。 +- `pnpm run verify:plugins --skip-build`:17 个独立插件仓库与 18 个打包制品,包括真实 DSH 激活打包的 Starter 与三个可选 Strategy。 +- `pnpm run release:intent`:变更说明覆盖所有改动的包。 +- WebUI:回环夹具上浅色 9 个、深色 8 个、窄布局 8 个状态,两个远程夹具各 8 个状态;所有运行均无控制台错误。 + +## 限制 + +截图来自一个预置数据的夹具,只说明布局与样式,不代表 Provider 行为。隔离夹具的 profile 未提供 DSH 的权限预设,因此设置中的“通用设置”显示 DSH 自己的不可用提示。Builtin 模式下,只有存在当前会话时才能从插件页打开工作台。Starter 的组件在插件页仍显示包名;按各包的展示元数据显示本地化行标题与图标留作后续工作。 diff --git a/dsh/docs/pr-assets/plugin-description-20260928/README.md b/dsh/docs/pr-assets/plugin-description-20260928/README.md new file mode 100644 index 000000000..b26c67219 --- /dev/null +++ b/dsh/docs/pr-assets/plugin-description-20260928/README.md @@ -0,0 +1,14 @@ +# Plugin description: layered memory + +[简体中文](./README.zh-CN.md) + +Tested implementation: `b65064ea`. macOS 15.6, Node 25.1.0, published DSH 0.1.7-rc.2, headless Chrome at 1280 × 860, on an isolated WebUI fixture from the Starter's defaults with its test model. No personal memory or credentials were used. + +The plugin page header now describes layered memory, matching the Layered strategy selected below it. + +![Plugin page header](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-description-20260928/plugin-header.png) + +## Verification + +- Full `pnpm run verify` and `pnpm run release:intent` passed. +- `git check-ignore` confirms the new rules: `.env` and `.env.*` files, `.cache/`, `.claude/worktrees/` and `.claude/settings.local.json` are ignored, while `.env.example` files and other `.claude/` files stay visible. diff --git a/dsh/docs/pr-assets/plugin-description-20260928/README.zh-CN.md b/dsh/docs/pr-assets/plugin-description-20260928/README.zh-CN.md new file mode 100644 index 000000000..a9bf5c70f --- /dev/null +++ b/dsh/docs/pr-assets/plugin-description-20260928/README.zh-CN.md @@ -0,0 +1,14 @@ +# 插件简介:分层记忆 + +[English](./README.md) + +实测实现:`b65064ea`。macOS 15.6、Node 25.1.0、已发布的 DSH 0.1.7-rc.2、headless Chrome 1280 × 860,从 Starter 默认配置与其测试模型的隔离 WebUI fixture 开始,未使用个人记忆或凭据。 + +插件页顶部的简介现在写作“分层记忆”,与下方选中的“分层策略”一致。 + +![插件页顶部](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-description-20260928/plugin-header.png) + +## 验证 + +- 完整的 `pnpm run verify` 与 `pnpm run release:intent` 均通过。 +- `git check-ignore` 确认新规则:`.env` 与 `.env.*` 文件、`.cache/`、`.claude/worktrees/` 与 `.claude/settings.local.json` 被忽略,`.env.example` 文件与 `.claude/` 下的其他文件仍会显示。 diff --git a/dsh/docs/pr-assets/plugin-linkage-20260927/README.md b/dsh/docs/pr-assets/plugin-linkage-20260927/README.md new file mode 100644 index 000000000..001fa2917 --- /dev/null +++ b/dsh/docs/pr-assets/plugin-linkage-20260927/README.md @@ -0,0 +1,55 @@ +# Component switches, dependencies and linked UI + +[简体中文](./README.zh-CN.md) + +Tested implementation: `3e9f28eb`, stacked on the configuration under Plugins ([record](../plugin-config-20260927/README.md)). macOS 15.6, Node 25.1.0, published DSH 0.1.7-rc.2, headless Chrome at 1280 × 860. Each run starts from a fresh isolated WebUI fixture with the Starter's defaults and its test model; components are switched on DSH's own component list below the configuration, as a user would. No personal memory or credentials were used. + +## Main Strategy states + +Before this change, turning on a second main Strategy showed nothing, turning off the selected one showed a note only after reopening the page, and with none running the note claimed that another Strategy composed memory. The Strategy group now follows DSH's list without a reload and names each state with the switch that resolves it: + +| A second main Strategy runs unused | The selected one is off; a fallback composes | No main Strategy runs | +|---|---|---| +| ![Idle second main Strategy](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-linkage-20260927/strategy-idle.png) | ![Fallback Strategy](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-linkage-20260927/strategy-fallback.png) | ![No main Strategy](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-linkage-20260927/strategy-none.png) | + +Choosing a main Strategy turns the other main Strategies off. Enhancements wait while the selected main Strategy is not running, because the Host validates every write against it. + +## Conversations continue without memory + +Before this change, with both main Strategy components off, every new turn failed with "no Serving memory generation is available: No Memory Strategy contribution is installed." The same turn now completes; the Host logs once: `[dsh-mnemon] this turn runs without memory: …`. + +![A turn completing with no main Strategy](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-linkage-20260927/conversation-without-memory.png) + +The Memory System says so above its pages, marks each layer **Not in use**, and links to the configuration, where **Turn on “Default three-tier”** restores memory: + +![Memory not in use](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-linkage-20260927/status-memory-off.png) + +## Source components switched off + +With Documents and Memory Spaces switched off on DSH's list, each memory layer row says its component is off and offers **Turn on component**. Providers are not read while their Source is off, so the group shows why instead of the Host error "Source memory-spaces is not installed"; Mnemon Native no longer claims a missing CLI: + +![Layer and Provider notes](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-linkage-20260927/layers-component-off.png) + +The workspace keeps the Memory Spaces tab in place, marked **Not running**; Status cards say how to restore the layer instead of "Waiting for workspace" or "Directory not synchronized": + +| Status | The stopped page | After the component runs again | +|---|---|---| +| ![Status with a stopped layer](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-linkage-20260927/status-component-off.png) | ![Stopped Memory Spaces page](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-linkage-20260927/workspace-stopped.png) | ![Memory Spaces resumed](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-linkage-20260927/workspace-resumed.png) | + +The open stopped page moves to the layer's own page once its Source runs again. + +## Dark theme + +| Fallback | Memory not in use | Component notes | Stopped page | +|---|---|---|---| +| ![Fallback, dark](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-linkage-20260927/dark-strategy-fallback.png) | ![Memory not in use, dark](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-linkage-20260927/dark-status-memory-off.png) | ![Component notes, dark](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-linkage-20260927/dark-layers-component-off.png) | ![Stopped page, dark](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-linkage-20260927/dark-workspace-stopped.png) | + +## Verification + +- `pnpm run verify`: documentation 2,216 local links and 83 anchors; deterministic build of 42 files; workspace package builds, typechecks and tests (Memory Spaces 174 passed and 2 conditionally skipped, Runtime 61, Documents 39); root tests 1,447 passed and 6 conditionally skipped; real Headless activation (37 tools), legacy settings import with an idempotent restart, and the root disable gate; package of 52 files, publint strict and attw. +- `pnpm run release:intent`: changesets cover every changed package. +- WebUI: 9 light and 4 dark states captured by a scripted walk on a fresh fixture, with no console errors. The same walk was driven by hand in the in-app browser, including Strategy exclusivity from the configuration's selector. + +## Limits + +The fixture's test model answers every turn with a fixed reply, so the screenshots show that a turn completes, not what memory would add. The `cordis:group` row in DSH's component list still shows as off with a switch that does not work (DSH #649); the configuration now says so right above the list. Strategy option editors and localized row titles for the Starter's components remain later work. diff --git a/dsh/docs/pr-assets/plugin-linkage-20260927/README.zh-CN.md b/dsh/docs/pr-assets/plugin-linkage-20260927/README.zh-CN.md new file mode 100644 index 000000000..93d30a4ad --- /dev/null +++ b/dsh/docs/pr-assets/plugin-linkage-20260927/README.zh-CN.md @@ -0,0 +1,55 @@ +# 组件停启、依赖与界面联动 + +[English](./README.md) + +实测实现:`3e9f28eb`,叠加在“插件”中的配置页([记录](../plugin-config-20260927/README.zh-CN.md))之上。环境为 macOS 15.6、Node 25.1.0、正式 DSH 0.1.7-rc.2,无头 Chrome,1280 × 860。每次运行都从一个全新的隔离 WebUI 夹具开始,使用 Starter 的默认组合与测试模型;组件均在配置下方 DSH 自带的组件列表中切换,与用户操作一致。未使用个人记忆或凭据。 + +## 主策略的各种状态 + +改版前:打开第二个主策略时没有任何提示;关闭所选主策略后,要重新打开页面才出现提示;没有主策略运行时,提示却说记忆由另一个主策略组合。现在策略分组无需刷新即跟随 DSH 的列表,并为每种状态给出说明和解决它的开关: + +| 第二个主策略空转 | 所选主策略停用,由兜底策略组合 | 没有主策略运行 | +|---|---|---| +| ![第二个主策略空转](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-linkage-20260927/strategy-idle.png) | ![兜底策略](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-linkage-20260927/strategy-fallback.png) | ![没有主策略](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-linkage-20260927/strategy-none.png) | + +选择一个主策略时,其他主策略会被停用。所选主策略没有运行时,增强暂不可改,因为 Host 会按所选主策略校验每次写入。 + +## 对话照常进行,只是不使用记忆 + +改版前:两个主策略组件都关闭后,每个新回合都会失败,报错“no Serving memory generation is available: No Memory Strategy contribution is installed.”。现在同一回合正常完成,Host 日志记录一次 `[dsh-mnemon] this turn runs without memory: …`。 + +![没有主策略时回合正常完成](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-linkage-20260927/conversation-without-memory.png) + +记忆系统在页面上方说明这一点,把各层标记为“未生效”,并提供前往配置;在配置中点击“启用‘默认三层’”即可恢复记忆: + +![记忆未生效](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-linkage-20260927/status-memory-off.png) + +## Source 组件被关闭 + +在 DSH 的列表中关闭档案与记忆空间后,对应的记忆层行会说明组件已停用并提供“启用组件”。“记忆空间”组件停用时不再读取 Provider,分组说明原因,不再显示 Host 报错“Source memory-spaces is not installed”;Mnemon Native 也不再误报 CLI 缺失: + +![记忆层与 Provider 的说明](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-linkage-20260927/layers-component-off.png) + +工作台保留记忆空间标签页的原位置并标记“未运行”;状态卡片说明如何恢复,不再显示“等待工作区”或“目录尚未同步”: + +| 状态页 | 未运行页面 | 组件恢复运行后 | +|---|---|---| +| ![有未运行层的状态页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-linkage-20260927/status-component-off.png) | ![记忆空间未运行](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-linkage-20260927/workspace-stopped.png) | ![记忆空间已恢复](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-linkage-20260927/workspace-resumed.png) | + +停在未运行页面时,Source 恢复运行后会自动切回该层自己的页面。 + +## 深色主题 + +| 兜底策略 | 记忆未生效 | 组件说明 | 未运行页面 | +|---|---|---|---| +| ![兜底策略,深色](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-linkage-20260927/dark-strategy-fallback.png) | ![记忆未生效,深色](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-linkage-20260927/dark-status-memory-off.png) | ![组件说明,深色](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-linkage-20260927/dark-layers-component-off.png) | ![未运行页面,深色](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/plugin-linkage-20260927/dark-workspace-stopped.png) | + +## 验证 + +- `pnpm run verify`:文档 2,216 个本地链接与 83 个锚点;确定性构建 42 个文件;各 workspace 包的构建、类型检查与测试(记忆空间 174 通过、2 个条件跳过,运行时 61,档案 39);根测试 1,447 通过、6 个条件跳过;真实 Headless 激活(37 个工具)、旧设置导入与幂等重启、根停用门;打包 52 个文件,publint strict 与 attw 通过。 +- `pnpm run release:intent`:变更说明覆盖所有改动的包。 +- WebUI:在全新夹具上用脚本走查,截取浅色 9 个、深色 4 个状态,无控制台错误。同一流程也在内置浏览器中手动操作过,包括通过配置中的选择器验证主策略互斥。 + +## 限制 + +夹具的测试模型对每个回合都给出固定回复,因此截图只说明回合正常完成,不代表记忆带来的内容差异。DSH 组件列表中的 `cordis:group` 行仍显示为“已关闭”且开关无法使用(DSH #649),配置页现在在列表上方注明了这一点。主策略选项编辑器,以及 Starter 各组件的本地化行标题,留作后续工作。 diff --git a/dsh/docs/pr-assets/pr-286-plugin-metadata/README.md b/dsh/docs/pr-assets/pr-286-plugin-metadata/README.md new file mode 100644 index 000000000..ad7b0623a --- /dev/null +++ b/dsh/docs/pr-assets/pr-286-plugin-metadata/README.md @@ -0,0 +1,45 @@ +# DSH plugin metadata — PR #286 + +[简体中文](./README.zh-CN.md) | **English** + +Verified on 2026-09-26 at `84dc35aaa832e341a01add4b4e934a90a056c46b`, which appends validation and release maintenance to contributor commit `d16d017aa5bb0c3bbbdb40dc5a95554266b6522a`. The original commit, locale wording, and package changes are preserved. + +## Environment and reproduction + +- Node 24.20.0 and pnpm 10.13.1 on macOS. +- The repository's pinned DSH 0.1.5-rc.1 cohort for the full workspace and packed-artifact suites; a separate published DSH 0.1.7-rc.2 profile for metadata and browser acceptance. +- The browser profile installed all 17 packed Mnemon artifacts and enabled the three optional Strategy extensions. It used isolated synthetic state, official DSH UI, no additional skins, and no external model or Provider API. +- The root tarball is `dsh-mnemon-0.5.15.tgz`, SHA-256 `c36e723d632895108b8e8fcbdf8a8e15a79fd7362143758691b9f2bbebd1d301`. It contains the PR changes without a release version bump. The browser used these exact bytes. + +Build and pack the PR, install its tarballs into a disposable DSH profile, and restart the profile. Open **Plugins**, select **dsh-mnemon**, and inspect the Chinese display text. Change **Settings → General → Language** to English, close Settings, and inspect the same plugin again. Both captures below use a 1280 × 720 viewport. + +## Visible result + +The Chinese plugin details and component row show `三级记忆 (dsh-mnemon)` and the contributor's Chinese description. + +![Chinese DSH plugin metadata](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/pr-286-plugin-metadata/plugins-zh.jpg) + +The same entry in English shows the existing `dsh-mnemon` name and complete package description. + +![English DSH manifest fallback](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/pr-286-plugin-metadata/plugins-en.jpg) + +The visible `cordis:group` Off status alongside running children is a separate pre-existing status issue under investigation. These captures validate metadata display, not group enable/disable behavior. + +## Verification + +```sh +pnpm exec vitest run tests/package-locales.spec.mjs tests/release.spec.mjs +pnpm verify +pnpm verify:plugins +pnpm release:intent +``` + +- All 42 focused tests passed, including 23 locale-resource cases. Rejected inputs cover incorrect wildcard/concrete export targets, missing `en.json`, invalid filenames and directories, malformed JSON, non-object dictionaries/metadata, and blank or non-string display fields. +- Full workspace verification passed: deterministic builds, independent plugin types/tests, 1,403 root tests, real Headless activation, package contents, public entries, and package lint. Eight opt-in root tests were skipped by default. Existing performance and package limits were retained. +- Packed verification passed for 16 independent plugin repositories and 17 artifacts, including external consumer build/types/tests, the real Starter upgrade from 0.4.7, and simultaneous activation of the three optional Strategy plugins. +- Release-intent verification confirmed the root patch changeset. A locale-only edit now requires release intent and a version change when preparing a release. +- The package has 52 files and 1,374,969 unpacked bytes, below the unchanged 1,376,000-byte ceiling. + +The published DSH `readPluginMeta()` from `@deepseek-ai/dsh-app-boot@0.1.7-rc.2` also read the freshly extracted tarball. Its English title and description matched the baseline package's display text exactly; its Chinese fields matched the screenshot. [Sanitized result](./metadata-verification.json). + +This change covers the root plugin's metadata only. It does not translate every component package, change configuration or stored memory, or certify all DSH releases, desktop skins, or remote Providers. The UI, getting-started, configuration, and operations guides were reviewed in both languages; their procedures remain applicable. diff --git a/dsh/docs/pr-assets/pr-286-plugin-metadata/README.zh-CN.md b/dsh/docs/pr-assets/pr-286-plugin-metadata/README.zh-CN.md new file mode 100644 index 000000000..56dab3f32 --- /dev/null +++ b/dsh/docs/pr-assets/pr-286-plugin-metadata/README.zh-CN.md @@ -0,0 +1,45 @@ +# DSH 插件元数据 — PR #286 + +**简体中文** | [English](./README.md) + +于 2026-09-26 验证 `84dc35aaa832e341a01add4b4e934a90a056c46b`。该提交在贡献者原始提交 `d16d017aa5bb0c3bbbdb40dc5a95554266b6522a` 后追加验证与发布维护;原始提交、locale 文案和 package 修改均保留。 + +## 环境与复现 + +- macOS,Node 24.20.0,pnpm 10.13.1。 +- 完整工作区和制品验证使用仓库锁定的 DSH 0.1.5-rc.1 组合;元数据与浏览器验收使用单独安装的正式 DSH 0.1.7-rc.2 profile。 +- 浏览器 profile 安装全部 17 个 Mnemon 打包制品,并启用三个可选 Strategy 增强。环境采用隔离的合成数据和 DSH 官方界面,没有额外皮肤,也未调用外部模型或 Provider API。 +- 根 tarball 为 `dsh-mnemon-0.5.15.tgz`,SHA-256 为 `c36e723d632895108b8e8fcbdf8a8e15a79fd7362143758691b9f2bbebd1d301`,包含本 PR 改动但未提升发布版本。浏览器使用完全相同的制品字节。 + +构建并打包本 PR,将 tarball 安装到一次性 DSH profile 后重启。打开“插件”,选择 `dsh-mnemon`,检查中文文案;再在“设置 → 常规 → 语言”切换为 English,关闭设置并检查同一插件。下方两张截图均使用 1280 × 720 视口。 + +## 可见结果 + +中文插件详情及组件行显示 `三级记忆 (dsh-mnemon)` 和贡献者提交的中文说明。 + +![DSH 中文插件元数据](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/pr-286-plugin-metadata/plugins-zh.jpg) + +英文界面的同一条目保留现有 `dsh-mnemon` 名称及完整 package 说明。 + +![DSH 英文 manifest 回退](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/pr-286-plugin-metadata/plugins-en.jpg) + +截图中 `cordis:group` 显示关闭而子组件仍运行,是另行调查的既有状态问题。本记录仅验证元数据展示,不作为分组启停行为的验收证据。 + +## 验证结果 + +```sh +pnpm exec vitest run tests/package-locales.spec.mjs tests/release.spec.mjs +pnpm verify +pnpm verify:plugins +pnpm release:intent +``` + +- 42 项定向测试全部通过,其中包含 23 项 locale 资源测试。拒绝路径覆盖错误通配/具体导出目标、缺少 `en.json`、非法文件名与目录、损坏 JSON、非对象字典/元数据及空白或非字符串展示字段。 +- 完整工作区验证通过:确定性构建、独立插件类型与测试、1,403 项根测试、真实 Headless 激活、包内容、公开入口及包 lint。8 项选择性根测试按默认跳过,性能和制品上限保持不变。 +- 16 个独立插件仓库与 17 个制品的打包验证全部通过,包含外部消费者构建/类型/测试、从 0.4.7 进行真实 Starter 升级,以及三个可选 Strategy 插件同时激活。 +- 发布意图验证确认根包 patch changeset 已覆盖。本次补充使仅修改 locale 的变更也需要发布意图,并在准备发布时检查版本提升。 +- 包含 52 个文件,解包大小为 1,374,969 bytes,低于保持不变的 1,376,000-byte 上限。 + +正式发布的 `@deepseek-ai/dsh-app-boot@0.1.7-rc.2` 中 `readPluginMeta()` 也读取了本次新打包并解压的制品。英文名称和说明与基线展示文案逐字一致,中文字段与截图一致。[脱敏结果](./metadata-verification.json)。 + +本次仅覆盖根插件元数据,不包含所有组件包的翻译,不改变配置或已存记忆,也不代表已验证所有 DSH 版本、桌面皮肤或远程 Provider。已复核中英文界面指南、快速开始、配置参考与运维指南,其操作流程仍适用。 diff --git a/dsh/docs/pr-assets/pr-286-plugin-metadata/metadata-verification.json b/dsh/docs/pr-assets/pr-286-plugin-metadata/metadata-verification.json new file mode 100644 index 000000000..132fef487 --- /dev/null +++ b/dsh/docs/pr-assets/pr-286-plugin-metadata/metadata-verification.json @@ -0,0 +1,20 @@ +{ + "testedRevision": "84dc35aaa832e341a01add4b4e934a90a056c46b", + "dshAppBoot": "0.1.7-rc.2", + "packageName": "dsh-mnemon", + "packedChineseMetadata": { + "title": { + "en": "dsh-mnemon", + "zh": "三级记忆 (dsh-mnemon)" + }, + "description": { + "en": "Composable three-tier memory control plane for DeepSeek Harness: persistent runtime context, searchable project documents, pluggable long-term memory, guarded strategies, WebUI, and headless tools.", + "zh": "三级记忆系统:召回 / 写入 / 记忆页签(策略配置在 profile patch 层)" + } + }, + "baselineMetadata": { + "title": "dsh-mnemon", + "description": "Composable three-tier memory control plane for DeepSeek Harness: persistent runtime context, searchable project documents, pluggable long-term memory, guarded strategies, WebUI, and headless tools." + }, + "englishFallbackUnchanged": true +} diff --git a/dsh/docs/pr-assets/pr199-document-protection/README.md b/dsh/docs/pr-assets/pr199-document-protection/README.md new file mode 100644 index 000000000..e98ecf9a6 --- /dev/null +++ b/dsh/docs/pr-assets/pr199-document-protection/README.md @@ -0,0 +1,67 @@ +# PR #199 document protection evidence / 档案保护验证 + +Verified locally on 2026-09-08 (Asia/Shanghai), with implementation commit `f8f619a2fb77d27204b08ead027cee48440065a1` on top of original PR head `5919b95`. Only `dsh-mnemon` and `dsh-mnemon-source-documents` change published behavior. The original contributor's two commits are retained. + +2026-09-08(Asia/Shanghai)对原 PR 提交 `5919b95` 上追加的实现提交 `f8f619a2fb77d27204b08ead027cee48440065a1` 完成本地验证。发布行为变更仅涉及 `dsh-mnemon` 和 `dsh-mnemon-source-documents`,原贡献者的两条提交保留。 + +## Validation / 验证 + +| Check / 检查 | Result / 结果 | +| --- | --- | +| Node 22.19.0, pnpm 10.13.1: `pnpm run verify` | Passed: 1,114 tests, including 806 root tests and 38 Documents Source tests. Two existing opt-in Native tests skipped. / 通过:1,114 项测试,含根包 806 项、Documents Source 38 项;两项现有 Native 可选测试跳过。 | +| Deterministic build, Headless, package checks / 确定性构建、Headless、包检查 | Passed; 39 deterministic build files, 39 Headless tools, 8 representative Mnemon tools. / 全部通过。 | +| Node 24.20.0, pnpm 10.13.1: `MNEMON_PLUGIN_VERIFY_CONCURRENCY=4 pnpm run verify:plugins` | Passed: 17 packed artifacts and 16 independently installed plugin repositories, external consumer, real DSH Starter and optional Strategy activation. / 17 个制品、16 个插件独立安装、外部消费者和真实 DSH 加载全部通过。 | +| Before-fix targeted tests / 修复前定向回归 | 14 failed, 64 passed: missing create-only capability, explicit-forget persona conflict and false success reporting. / 14 项失败、64 项通过,复现缺失的仅创建能力、显式删除 persona 冲突和误报成功。 | +| Real WebUI / 真实界面 | Forbidden overwrite rejected, separate supplement created, original bytes and metadata unchanged, explicit edit succeeds; no browser console errors. / 覆盖被拒绝,补充独立创建,原文及元数据不变,显式编辑成功,浏览器无控制台错误。 | + +The skipped tests are `source-memory-spaces/tests/native-integration.spec.ts` and `windows-smoke.spec.ts`; they require their opt-in Native environment. A published upstream UI package emits an existing missing-source-map warning during build; the checks pass. + +跳过项为 `source-memory-spaces/tests/native-integration.spec.ts` 和 `windows-smoke.spec.ts`,需要额外启用的 Native 环境。构建存在已发布上游 UI 包缺少 source map 的现有警告,不影响验证通过。 + +Focused regressions cover autonomous forget exclusion, explicit forget availability and receipts, create payload injection, identical titles, capacity rejection without archiving, read-only/disabled/manual-only Sources, preserved management updates, and writeback navigation. See `tests/document-protection.spec.ts`, `tests/subagent.spec.ts`, `tests/commands.spec.ts`, and `plugins/dsh-mnemon-source-documents/tests/create-action.spec.ts`. + +定向回归覆盖自主删除权限、显式删除入口与回执、创建参数注入、同名档案、满容量拒绝且不归档、只读/关闭/仅手动 Source、正常管理更新和写回导航。对应测试文件见上。 + +## Reproduction / 复现 + +```sh +pnpm install --frozen-lockfile +pnpm build +pnpm --workspace-concurrency=4 -r build +pnpm e2e:serve --document-protection +``` + +1. Open the loopback WebUI URL printed by the fixture, select its printed temporary workspace and the **Mnemon E2E** preset. / 打开夹具打印的本机地址,选择对应临时工作区和 **Mnemon E2E** 预设。 +2. Send a substantial request to save a release implementation, then a second request to remember queue-backlog monitoring as a separate supplement. Use at least 320 total user characters and two turns to exercise the real idle-review admission and activity gates. / 先请求保存发布实现,再请求将队列积压观测作为独立补充记住;两轮用户文本合计至少 320 字,以通过真实的空闲审查准入和活动门槛。 +3. Wait for the fixture's five-second idle window. The scripted model deliberately calls the unavailable `mnemon_document_manage` update tool; the fixture asserts DSH returns `unknown tool`, then creates one independent supplement through `mnemon_document_create`. / 等待夹具的五秒空闲窗口;脚本模型故意调用不可用的管理工具覆盖原文,断言 DSH 拒绝后,通过仅创建工具保存独立补充。 +4. Open **Memory system → Documents**. Inspect the original revision, hash, code and rollback steps, and the supplement's reference to the original ID. Compare the original file bytes and index metadata captured before the second turn with those after review. / 打开**记忆系统 → 档案**,核对原文版本、哈希、代码、回滚步骤以及补充对原 ID 的引用;对比第二轮前后保存的原文件字节和索引元数据。 +5. Edit the supplement through the UI and save a revision. Its revision becomes 2; the original stays byte-identical at revision 1. / 在界面明确编辑补充并保存,第 2 版成功产生,原文仍逐字节一致且为第 1 版。 +6. Stop the fixture with Ctrl-C after saving evidence; it removes its disposable state. / 保存证据后用 Ctrl-C 停止,夹具会清理一次性数据。 + +The fixture scripts only model replies. DSH `0.1.2-rc.1`, tool filtering, review scheduling, Source mutation, persistence and WebUI are real published components running against these changes. This validates transport, authorization and data preservation, not a production model's memory-selection quality. All text and IDs in the evidence belong to synthetic disposable data; no remote Provider, production memory or real credentials were used. + +夹具仅脚本化模型回复;DSH `0.1.2-rc.1`、工具过滤、审查调度、Source 写入、持久化和 WebUI 均为加载本次改动的真实已发布组件。本次验证证明调用链、权限和数据保留行为,不代表生产模型的记忆筛选质量。证据中的正文和 ID 均来自合成的一次性数据,未使用远程 Provider、生产记忆或真实凭据。 + +## Persisted assertions / 持久化断言 + +[assertions.json](./assertions.json) records the actual dispatcher rejection and disk comparisons. The original remains active at revision 1 with content hash `67167245d1896658a49a0544ca75a51cd94480276419a817b1ddbd6b256624fc`. There are two active documents and zero archived documents. The separately created supplement advances from revision 1 to 2 only after the explicit UI edit. + +[assertions.json](./assertions.json) 记录真实调度器拒绝和磁盘对比结果。原文始终活跃、修订为 1、内容哈希如上。两份档案均活跃,归档数为零;补充仅在明确的界面编辑后由第 1 版变为第 2 版。 + +## Screenshots / 截图 + +Original revision and independent document list after review / 审查后原文版本和独立档案列表: + +![Original revision remains 1 / 原文版本保持为 1](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/pr199-document-protection/01-original-preserved.png) + +Original implementation, rollback steps and preservation marker / 保留的原实现、回滚步骤和标记: + +![Original content preserved / 原文内容保留](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/pr199-document-protection/02-original-content.png) + +Independent supplement references the original / 独立补充引用原档案: + +![Separate supplement / 独立补充](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/pr199-document-protection/03-separate-supplement.png) + +Explicit UI edit succeeds with revision 2 / 界面明确编辑成功,修订为 2: + +![Explicit edit succeeds / 显式编辑成功](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/pr199-document-protection/04-explicit-edit.png) diff --git a/dsh/docs/pr-assets/pr199-document-protection/assertions.json b/dsh/docs/pr-assets/pr199-document-protection/assertions.json new file mode 100644 index 000000000..3272e8df5 --- /dev/null +++ b/dsh/docs/pr-assets/pr199-document-protection/assertions.json @@ -0,0 +1,18 @@ +{ + "originalId": "029559b7-4cfa-412a-a787-420a1e290e77", + "supplementId": "1a7780a2-9a88-42d7-b216-28706edaf687", + "originalRevision": 1, + "originalContentHash": "67167245d1896658a49a0544ca75a51cd94480276419a817b1ddbd6b256624fc", + "originalMetadataUnchanged": true, + "originalFileByteIdentical": true, + "activeDocuments": 2, + "archivedDocuments": 0, + "overwriteDenied": true, + "explicitEditRevision": 2, + "originalStillByteIdenticalAfterExplicitEdit": true, + "dispatcherDenial": { + "code": "UNKNOWN_TOOL", + "message": "Error: unknown tool \"mnemon_document_manage\"" + }, + "browserConsoleErrors": 0 +} diff --git a/dsh/docs/pr-assets/release-v0.5.14/README.md b/dsh/docs/pr-assets/release-v0.5.14/README.md new file mode 100644 index 000000000..b3a28d7d2 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.14/README.md @@ -0,0 +1,32 @@ +# v0.5.14 packed release verification + +**English** | [简体中文](./README.zh-CN.md) + +Tested revision: `07dac3d3d2d029ce3e4610226d40a757509d6dc7`, on 2026-09-25. The later evidence-only commit does not change any packed package input. [verification.json](./verification.json) records all seventeen artifact integrities, screenshot hashes and the exact Runtime content hash. + +The release worktree consumes the fixes merged as #277, #276 and #278. It versions only the Starter to 0.5.14, OpenViking Provider to 0.5.6 and Memory Spaces Source to 0.5.9. + +## Checks + +- Node 24.20.0 / pnpm 10.13.1: full `pnpm verify` passed, with 1,360 root tests / 7 skips and 402 workspace-plugin tests / 2 skips. Both opt-in official Native CLI 0.2.9 tests ran, including two same-View archive destinations. Live-model, codec/cohort opt-ins and live-service/platform cases account for the remaining skips. +- Serial `pnpm verify:plugins --skip-build` passed for sixteen independent plugin repositories and seventeen packed artifacts, the external SDK consumer and real DSH Starter activation with all three optional Strategies. Worker counts were bounded; test and performance thresholds were unchanged. +- `release:status`, `release:version`, `release:check` and `release:intent` passed. The lockfile was regenerated by pnpm 10.13.1 without adding, removing or advancing third-party package versions. +- Before versioning, the integrated fix branch passed 99 targeted tests plus both official Teams matrices: sixteen combinations on 0.1.7-rc.1 and sixteen on the legacy cohort. See the [issue #275 record](../issue-275-agent-teams/README.md) for expected legacy refusals and scope limits. + +## Real WebUI + +The existing [packed Team fixture](../../../scripts/fixtures/serve-team-review.mjs) ran all seventeen release-version tarballs on an isolated official DSH/Teams 0.1.7-rc.1 Profile with the official CLI and all three Strategy enhancements enabled. The fixture uses disposable storage and a loopback model endpoint; no external model API was used. + +1. Opened Memory System and observed `dsh-mnemon 0.5.14` with healthy status. +2. Added a synthetic Runtime entry through the UI, then edited it and observed the exact saved content. +3. Changed Agent Teams from the default `pause` to `scoped` through Settings and observed the saved/live status. +4. Stopped and restarted the Host process through the fixture's `SIGUSR2` cold-restart handler, then reopened the authenticated UI. Runtime content remained visible and its file SHA-256 was byte-identical. Settings still selected `scoped`, and all three Strategy enhancement checkboxes remained enabled. + +| Observation | Screenshot | +|---|---| +| Runtime added before editing/restart | [runtime-saved.png](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.14/runtime-saved.png) | +| Version and health after cold restart | [status-after-restart.png](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.14/status-after-restart.png) | +| Edited Runtime content after cold restart | [runtime-after-restart.png](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.14/runtime-after-restart.png) | +| Scoped review setting after cold restart | [settings-after-restart.png](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.14/settings-after-restart.png) | + +The optional source-link fixture on the pinned DSH 0.1.5-rc.1 development profile failed to load its `dsh-client-hmr` bundle. Its cause was not established. The packed 0.1.7-rc.1 installation passed without modifying official DSH code; this record does not claim that the source-link failure was fixed. The release smoke does not repeat live cloud-account or real-model acceptance. The original issue records preserve the broader before/after tests. diff --git a/dsh/docs/pr-assets/release-v0.5.14/README.zh-CN.md b/dsh/docs/pr-assets/release-v0.5.14/README.zh-CN.md new file mode 100644 index 000000000..f58cc95d9 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.14/README.zh-CN.md @@ -0,0 +1,32 @@ +# v0.5.14 打包发布验证 + +[English](./README.md) | **简体中文** + +测试 revision:`07dac3d3d2d029ce3e4610226d40a757509d6dc7`,日期为 2026-09-25。后续只补充证据的提交不改变任何包的发布输入。[verification.json](./verification.json) 记录十七个制品的完整性、截图哈希和 Runtime 正文文件的精确哈希。 + +发布 worktree 消费按 #277、#276、#278 顺序合入的修复,只将 Starter 升级到 0.5.14、OpenViking Provider 升级到 0.5.6、Memory Spaces Source 升级到 0.5.9。 + +## 检查 + +- Node 24.20.0 / pnpm 10.13.1 下完整 `pnpm verify` 通过:根测试 1,360 项通过、7 项跳过;工作区插件 402 项通过、2 项跳过。官方 Native CLI 0.2.9 的两项 opt-in 检查均实际运行,包括同一 View 内的两个归档目的地。其余跳过项为在线模型、codec/外部 cohort opt-in、真实服务或平台限定场景。 +- 串行 `pnpm verify:plugins --skip-build` 通过,覆盖十六个独立插件仓库、十七个打包制品、外部 SDK 消费者和三个可选 Strategy 同时启用的真实 DSH Starter。仅限制 worker 数量,测试与性能阈值保持不变。 +- `release:status`、`release:version`、`release:check`、`release:intent` 通过。pnpm 10.13.1 重新生成 lockfile,没有增删或升级第三方包版本。 +- 版本化前的集成分支通过 99 项针对性测试及两套官方 Teams 矩阵:0.1.7-rc.1 与旧版 cohort 各十六种组合。旧版预期拒绝与验证边界见 [#275 记录](../issue-275-agent-teams/README.zh-CN.md)。 + +## 真实 WebUI + +使用已有的 [Team 制品夹具](../../../scripts/fixtures/serve-team-review.mjs),将十七个发布版本 tarball 安装到隔离的官方 DSH/Teams 0.1.7-rc.1 Profile,配置官方 CLI 并启用三个 Strategy 增强。存储为一次性测试数据,模型 Endpoint 为本地夹具,没有调用外部模型 API。 + +1. 打开记忆系统,确认版本显示 `dsh-mnemon 0.5.14` 且系统正常。 +2. 通过界面新增合成 Runtime 记忆,编辑后确认精确保存正文。 +3. 在设置页将 Agent Teams 从默认 `pause` 切换为 `scoped`,确认已保存并实时生效。 +4. 使用夹具的 `SIGUSR2` 冷启动处理停止并重启 Host,再打开认证后的界面。Runtime 正文仍可见,文件 SHA-256 逐字节一致;设置仍选择 `scoped`,三个 Strategy 增强开关仍全部开启。 + +| 观察 | 截图 | +|---|---| +| 编辑与重启前新增 Runtime | [runtime-saved.png](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.14/runtime-saved.png) | +| 冷启动后的版本与健康状态 | [status-after-restart.png](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.14/status-after-restart.png) | +| 冷启动后保留编辑后的 Runtime 正文 | [runtime-after-restart.png](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.14/runtime-after-restart.png) | +| 冷启动后保留受限审查设置 | [settings-after-restart.png](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.14/settings-after-restart.png) | + +可选的源码链接夹具在固定的 DSH 0.1.5-rc.1 开发 Profile 上未能加载 `dsh-client-hmr` bundle,尚未确定原因。打包安装到 0.1.7-rc.1 后通过,且没有修改 DSH 官方代码;本记录不声称已修复源码链接失败。发布冒烟检查不重复真实云账户或在线模型验收,更广的前后对比仍以原始 Issue 记录为准。 diff --git a/dsh/docs/pr-assets/release-v0.5.14/verification.json b/dsh/docs/pr-assets/release-v0.5.14/verification.json new file mode 100644 index 000000000..362f2615e --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.14/verification.json @@ -0,0 +1,154 @@ +{ + "testedRevision": "07dac3d3d2d029ce3e4610226d40a757509d6dc7", + "date": "2026-09-25", + "node": "24.20.0", + "pnpm": "10.13.1", + "dsh": "0.1.7-rc.1", + "mnemonCli": "0.2.9", + "rootTests": { + "passed": 1360, + "skipped": 7 + }, + "workspacePluginTests": { + "passed": 402, + "skipped": 2 + }, + "independentPluginRepositories": 16, + "packedArtifacts": 17, + "nativeCliIntegration": true, + "nativeSameViewArchive": true, + "integratedTeamMatrix": { + "rc1Combinations": 16, + "legacyCombinations": 16, + "performedBeforeVersionBump": true + }, + "webui": { + "packedOnly": true, + "runtimeAdd": true, + "runtimeEdit": true, + "coldRestart": true, + "runtimeSha256BeforeAndAfter": "c572b748e973e0579be4d85169c3baa66a216a7b91d6a9fd09fdb55334843da3", + "runtimeContent": "Release v0.5.14 smoke: SQLite remains the durable local store after editing and a cold Host restart.", + "agentTeamsBefore": "pause", + "agentTeamsAfterSaveAndRestart": "scoped", + "strategyEnhancementsAfterRestart": { + "auto-capture": true, + "light-context": true, + "scoped": true + } + }, + "limitations": [ + "The optional source-link DSH 0.1.5-rc.1 WebUI fixture failed to load its dsh-client-hmr bundle; the cause was not established. The packed 0.1.7-rc.1 Profile passed without upstream source changes.", + "The release smoke does not repeat live cloud-account or real-model acceptance; the individual fix records own their broader scope." + ], + "artifacts": [ + { + "name": "dsh-mnemon-source-documents", + "version": "0.5.6", + "integrity": "sha512-rwXBAYE3FkelvWtc7Cl7WeSAZ9BWxAt6bCCTudlWbtEjVF6ZUATkUaEIkFw0p+05KP59fsfLOrSkaPyhcvUs0A==", + "size": 40052 + }, + { + "name": "dsh-mnemon-source-memory-spaces", + "version": "0.5.9", + "integrity": "sha512-S8dhyx6w5v3UJA06ADjnUq+7uQTbDjVBPzuSow0gjrUT5wgF48ziw9ZkdrHa0X7inRWVHGemSKcL0PT2gAKkTQ==", + "size": 139712 + }, + { + "name": "dsh-mnemon-source-runtime", + "version": "0.5.8", + "integrity": "sha512-RejWqgJmRCGQ6IYBDLm6BPcAjM7JLkmj+YFmTvTLgqvu1H03wLwCJycHZsYBP6dKYkoDG94Htw+E6XwvbChB/Q==", + "size": 27783 + }, + { + "name": "dsh-mnemon-strategy-default-three-tier", + "version": "0.5.5", + "integrity": "sha512-NF472CgnCqPm94hde6e+EIGrAHq861fxm8pMeJ03BuFksDqzQxVKgPkcA+ibn+/wSSaQPwIMQrjWaTGeEyNnFA==", + "size": 15257 + }, + { + "name": "dsh-mnemon-provider-byterover", + "version": "0.5.4", + "integrity": "sha512-XEu6dwuGLzQzoZv9Aata+xMGmXBrAMDMycy0YayXtXj2rD78GfgQiL8V3dOFDQkqi3nKSLn70LcxEavpmGhfqQ==", + "size": 6460 + }, + { + "name": "dsh-mnemon-provider-hindsight", + "version": "0.5.4", + "integrity": "sha512-T7myWzmHdM8VP0Id6yKhG4o1EWcOkk2jEWLMAfwlCOoos6vjHjcZMyc9RiuYvzrld5Vq5G2WIegvuYF/tqT4dg==", + "size": 14827 + }, + { + "name": "dsh-mnemon-provider-holographic", + "version": "0.5.4", + "integrity": "sha512-y92jU17pDE71Vl440SQsi2IpKoVtCaLAKYgt4NBU5avm9TTeLscVsGRx7U7jQIVEwUkh3G8j3m24VnXQYZk0eQ==", + "size": 7002 + }, + { + "name": "dsh-mnemon-provider-honcho", + "version": "0.5.4", + "integrity": "sha512-rfO7GKZ7USYTBOroiOOnY/NRHvUF9m5QuRdb6Rc4IVuKoU9tNJB7I+PtuogS75OWqYRuskcEYBRce2h/BAj3Sg==", + "size": 7620 + }, + { + "name": "dsh-mnemon-provider-mem0", + "version": "0.5.4", + "integrity": "sha512-alI5h2edY5SufWmvU8rjeEwonn9L9OkWZ44K8bDuWIPfsY1qbfIsIQahu5OqcvS88ibBm4hZGeKR1j75M+s4Gg==", + "size": 8209 + }, + { + "name": "dsh-mnemon-provider-mnemon-native", + "version": "0.5.5", + "integrity": "sha512-d/tLkxWOFy2GHYTOPPDkflR+2quIvvFOYk7YHpo+eS63VMaVjEl+++hzAWkeFyRwAkgCbwUR0izchn+JAyQQYg==", + "size": 7382 + }, + { + "name": "dsh-mnemon-provider-openviking", + "version": "0.5.6", + "integrity": "sha512-r5cYcsg4jjosnQ1FKWR9hz+KDwAgYMladO8hE108crSzCQFcv4FXHkQILfKzHrPhza8Puom6moygpSDgIA3TWw==", + "size": 15668 + }, + { + "name": "dsh-mnemon-provider-retaindb", + "version": "0.5.4", + "integrity": "sha512-zX9NShhMwyfbVVdz9pj6XLH2n732mu+8VkxyiEpK1c4ElqyVm2P7Os1KFoOzNzdUHeNuHEHf98pWfKgmdWDFPA==", + "size": 5452 + }, + { + "name": "dsh-mnemon-provider-supermemory", + "version": "0.5.4", + "integrity": "sha512-z70tDyX+J6LwmOFkQJtZqmnO1EUNjOrvAihtPlTQGnQVukZ/mRJ2FpMCFYb69Mteyi/SUaOrx0uAaDJc9ePCoA==", + "size": 5713 + }, + { + "name": "dsh-mnemon-strategy-auto-capture", + "version": "0.5.4", + "integrity": "sha512-l6Xsfjp3/2EXZbTQB1+AdU6GXlCKgG2qhmQN7WhHmAL488APZ88MTKwvKc+1Np89+JWM4UfN1/h2rlOOK6RCdw==", + "size": 3792 + }, + { + "name": "dsh-mnemon-strategy-light-context", + "version": "0.5.4", + "integrity": "sha512-QmQ9s3ie45kGNcXDNmWZKhpzpD2FsdoQtRC8gRGFjtj94Ru8ppnmZnzZtTDGG3gHURuig4Lhcgtqj1vRUG3RcA==", + "size": 3202 + }, + { + "name": "dsh-mnemon-strategy-scoped", + "version": "0.5.4", + "integrity": "sha512-kGfA+Z4PTNHVL3PunrG+4QsYBq0C8xSiica6l74qfQC/02hVf86v1nJq4dStPnyVQI4NQ02HkYlDrS2drpZIGA==", + "size": 3648 + }, + { + "name": "dsh-mnemon", + "version": "0.5.14", + "integrity": "sha512-d6NsrbPqpo7lZ0e0AXiy9fB3h5MUXiA+J20cf8r0XAmTxmqcy9+BHfFRi2VBV8BVgZLsz1oIWso3FqrJ0NUJ5w==", + "size": 309416 + } + ], + "screenshots": { + "runtime-after-restart.png": "bf34b2ab9714db1e287ea8a21635d6851efca27d07446c93677c9788a778a7c8", + "runtime-saved.png": "9895498f8ea63641de04a1e2ddcf88f757e5d8187a449209877fcbfceef78121", + "settings-after-restart.png": "e962bbd9b28a56906d72ea6fa5f1fe9e9a15b2d0e5e65d02a52d612c03d697ce", + "status-after-restart.png": "33c75acf55ac55bbe4effe523b0f91ac2597d1b9403e2d027fffb3bc04ed6363" + } +} diff --git a/dsh/docs/pr-assets/release-v0.5.15/README.md b/dsh/docs/pr-assets/release-v0.5.15/README.md new file mode 100644 index 000000000..57762cf80 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.15/README.md @@ -0,0 +1,42 @@ +# v0.5.15 release composition verification + +[简体中文](./README.zh-CN.md) + +The tested version commit is `d4dfa3b3`, containing merged PRs #282, #283 and #284. The environment is macOS arm64, Node 24.20.0, pnpm 10.13.1, published DSH 0.1.7-rc.2 and official Mnemon CLI 0.2.9. All seventeen local tarballs were installed into a fresh isolated profile with Scoped, Light Context and Auto Capture enabled together. Versions and SHA-256 hashes are in the [verification manifest](./verification.json). The subsequent evidence commit adds only documentation and a fixture, without changing published package contents. + +## Real WebUI + +The model endpoint is a deterministic loopback fixture. It reads actual inbound projections and selects real DSH tools; tools, adapters and storage are not mocked. All content is synthetic. No external model or personal memory was used. + +1. Send `RELEASE515_REPLACE` in a standard-mode conversation in the temporary workspace. The real `mnemon_runtime_memory` receives `target=user` and `branches: []`, returning `success: true` and a committed receipt. +2. Send `RELEASE515_SHOW`. The next turn's actual projection contains the replacement content and `[importance=critical; created=14d; updated=0d]`. +3. Open Runtime and inspect the critical USER entry. Open and cancel its editor, reload the browser and reopen Runtime; the record remains. The public root selector matches exactly once, and the default background stays opaque. + +[Minimal tool call and receipt](./wire.json) · [Browser observation](./browser.json) + +![Actual USER write](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.15/user-write.png) + +![Actual injected Runtime metadata](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.15/runtime-projection.png) + +![Runtime record after reload](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.15/runtime-after-reload.png) + +## Checks and limits + +- Release composition, consumed changesets, documentation, types, deterministic builds, Headless activation/restart, package exports and contents pass. Root unpacked size is 1,374,746 bytes, below the unchanged 1,376,000-byte limit. +- Local plugin tests: 406 passed, 2 skipped. Native CLI integration and same-View two-destination archival actually ran. Sixteen independent plugin repositories, seventeen artifacts and external SDK/Client composition checks pass. +- Local Root tests: 1,379 passed, 7 skipped, 1 failed. The default composition took 5.24 seconds against the existing 5-second wall-time fence. Both compositions also exceeded the wall-time fence in the isolated rerun; CPU assertions passed. These failures are retained, and no threshold was relaxed. +- [Clean CI for the same version commit](https://github.com/omdsh-dev/dsh-mnemon/actions/runs/36221228218) passes throughout: 1,784 integrated tests passed, 11 skipped. Both performance tests completed in 1.25 seconds combined and passed the original CPU and wall-time assertions. Windows path checks, independent artifacts and PR policy checks pass. + +Optional live-model, external-service and Teams/lifecycle checks retain their skip conditions. This WebUI run is macOS acceptance, not Windows WebUI or live-model quality evidence. The corresponding Issue reports retain each fix's full before/after comparison. + +## Reproduction + +Follow the [skin-hook setup steps](../issue-273-skin-hooks/README.md#reproduce) to prepare the official rc.2 profile and seventeen current-version tarballs, then start this directory's reusable fixture: + +```sh +MNEMON_CLI_PATH=/absolute/verified/mnemon node docs/pr-assets/release-v0.5.15/serve-release.mjs \ + --profile /absolute/official-profile --artifacts /absolute/packed-artifacts \ + --state /absolute/empty-disposable-state +``` + +Open the WebUI using the generated private connection information and follow the steps above. Do not publish `private.json` or raw Host logs. Stop the service with Ctrl-C. diff --git a/dsh/docs/pr-assets/release-v0.5.15/README.zh-CN.md b/dsh/docs/pr-assets/release-v0.5.15/README.zh-CN.md new file mode 100644 index 000000000..8685ef118 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.15/README.zh-CN.md @@ -0,0 +1,42 @@ +# v0.5.15 发布组合验证 + +[English](./README.md) + +测试版本提交为 `d4dfa3b3`,包含已合并的 #282、#283、#284。环境为 macOS arm64、Node 24.20.0、pnpm 10.13.1、正式 DSH 0.1.7-rc.2 与官方 Mnemon CLI 0.2.9。十七个本地 tarball 安装进全新隔离 Profile,并同时启用 Scoped、Light Context 和 Auto Capture。版本与 SHA-256 见[验证清单](./verification.json)。后续证据提交只增加文档与夹具,不改变发布包内容。 + +## 真实 WebUI + +模型端点为本地确定性夹具;它读取实际入站投影并选择真实 DSH 工具,工具、适配器和存储未被模拟。所有内容为合成数据,没有调用外部模型或使用个人记忆。 + +1. 在标准模式的临时工作区会话发送 `RELEASE515_REPLACE`。真实 `mnemon_runtime_memory` 收到 `target=user`、`branches: []`,返回 `success: true` 和已提交回执。 +2. 发送 `RELEASE515_SHOW`。下一回合实际投影包含替换后的正文及 `[importance=critical; created=14d; updated=0d]`。 +3. 打开运行时页面,核对关键 USER 记录;打开编辑弹窗后取消,刷新浏览器,再次打开页面,记录仍在。公开根选择器恰好命中一次,默认背景保持不透明。 + +[最小调用与回执](./wire.json) · [浏览器观察](./browser.json) + +![真实 USER 写入](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.15/user-write.png) + +![实际注入的 Runtime 元数据](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.15/runtime-projection.png) + +![刷新后的 Runtime 记录](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.15/runtime-after-reload.png) + +## 检查结果与限制 + +- 发布组合、已消费 changeset、文档、类型、确定性构建、Headless 启动/重启、包出口及内容检查通过。Root 解包体积为 1,374,746 字节,低于原有 1,376,000 字节上限。 +- 本地插件测试 406 通过、2 跳过;Native CLI 集成及同 View 双目标归档实际执行。十六个独立插件仓库、十七个制品与外部 SDK/Client 组合检查通过。 +- 本地 Root 测试 1,379 通过、7 跳过、1 失败:默认组合的 wall-time 为 5.24 秒,高于原有 5 秒门槛。单独重测的两种组合也超过 wall-time 门槛,CPU 断言均通过。保留失败记录,没有放宽阈值。 +- 同一版本提交的[干净 CI](https://github.com/omdsh-dev/dsh-mnemon/actions/runs/36221228218)全部通过:集成测试 1,784 通过、11 跳过;两项性能测试合计 1.25 秒,并通过原始 CPU 与 wall-time 断言。Windows 路径检查、独立制品检查及 PR 规范检查通过。 + +可选在线模型、外部服务和 Teams/lifecycle 检查保留其跳过条件。本次 WebUI 是 macOS 验证,不代表 Windows WebUI 或在线模型质量;各缺陷的完整前后对照仍以对应 Issue 报告为准。 + +## 复现 + +按[皮肤钩子复现步骤](../issue-273-skin-hooks/README.zh-CN.md#复现)准备正式 rc.2 Profile 与当前版本的十七个 tarball,再启动本目录中的可复用夹具: + +```sh +MNEMON_CLI_PATH=/absolute/verified/mnemon node docs/pr-assets/release-v0.5.15/serve-release.mjs \ + --profile /absolute/official-profile --artifacts /absolute/packed-artifacts \ + --state /absolute/empty-disposable-state +``` + +使用生成的私有连接信息打开 WebUI,按上述步骤操作。不要发布 `private.json` 或原始 Host 日志。使用 Ctrl-C 停止服务。 diff --git a/dsh/docs/pr-assets/release-v0.5.15/browser.json b/dsh/docs/pr-assets/release-v0.5.15/browser.json new file mode 100644 index 000000000..24eda63dc --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.15/browser.json @@ -0,0 +1,10 @@ +{ + "background": "linear-gradient(rgb(21, 21, 23), rgb(21, 21, 23)) repeat scroll 0% 0% / auto padding-box border-box, rgb(21, 21, 23) linear-gradient(rgb(97, 102, 107), rgb(97, 102, 107)) repeat scroll 0% 0% / auto padding-box border-box", + "recordAfterReload": true, + "stableRoots": 1, + "surface": "sidebar", + "viewport": { + "height": 720, + "width": 1280 + } +} diff --git a/dsh/docs/pr-assets/release-v0.5.15/serve-release.mjs b/dsh/docs/pr-assets/release-v0.5.15/serve-release.mjs new file mode 100644 index 000000000..d2924d300 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.15/serve-release.mjs @@ -0,0 +1,33 @@ +import { mkdir, writeFile } from 'node:fs/promises' +import { join } from 'node:path' +import { actualReceipt, servePackedWebFixture } from '../../../scripts/fixtures/packed-web-fixture.mjs' + +const seed = 'RELEASE515_USER: I prefer concise replies.' +const updated = 'RELEASE515_USER: I prefer concise replies with examples.' +await servePackedWebFixture({ + issue: 'release-v0.5.15', + async prepare({ data }) { + const now = Date.now() + await mkdir(join(data, 'runtime'), { recursive: true, mode: 0o700 }) + await writeFile(join(data, 'runtime/memories.json'), JSON.stringify({ version: 1, entries: [{ + target: 'user', content: seed, importance: 'critical', + created_at: new Date(now - 14.25 * 86400000).toISOString(), + updated_at: new Date(now - 2.25 * 86400000).toISOString(), + }] }, null, 2) + '\n', { mode: 0o600 }) + return { metadata: { purpose: 'v0.5.15 integrated release smoke', commands: ['RELEASE515_REPLACE', 'RELEASE515_SHOW'] } } + }, + complete({ user, tools, receipts, runtimeProjections }) { + if (user.includes('RELEASE515_REPLACE')) { + if (receipts.length) return { reply: actualReceipt(receipts.at(-1)), observation: { command: 'RELEASE515_REPLACE' } } + if (!tools.some(tool => tool.name === 'mnemon_runtime_memory')) return { reply: 'Native Runtime tool is unavailable; select the standard Coding preset.' } + return { reply: { name: 'mnemon_runtime_memory', args: { action: 'replace', target: 'user', old_text: seed, content: updated, importance: 'critical', branches: [] } }, observation: { command: 'RELEASE515_REPLACE' } } + } + if (user.includes('RELEASE515_SHOW')) { + const projection = runtimeProjections.at(-1) ?? '' + const annotations = [...projection.matchAll(/\[importance=(critical|normal|low); created=([^;\]\n]+); updated=([^\]\n]+)\]/gu)].map(match => ({ importance: match[1], created: match[2], updated: match[3] })) + const contents = [...projection.matchAll(/([^]*?)<\/runtime-memory-file>/gu)].map(match => `${match[1]}.md:\n${match[2].trim()}`) + return { reply: `Actual injected Runtime metadata: ${annotations.length} annotation(s).\n\n${contents.join('\n\n')}`, observation: { command: 'RELEASE515_SHOW', annotations } } + } + return { reply: 'Release fixture ready. Send RELEASE515_REPLACE, then RELEASE515_SHOW. Only model choices are scripted; DSH tools and storage are real.' } + }, +}) diff --git a/dsh/docs/pr-assets/release-v0.5.15/verification.json b/dsh/docs/pr-assets/release-v0.5.15/verification.json new file mode 100644 index 000000000..5579d1307 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.15/verification.json @@ -0,0 +1,171 @@ +{ + "testedRevision": "d4dfa3b32e4d13f54e51e189315eb804663acd83", + "date": "2026-09-26", + "platform": "macOS arm64", + "node": "24.20.0", + "pnpm": "10.13.1", + "dsh": "0.1.7-rc.2", + "mnemonCli": "0.2.9", + "artifacts": [ + { + "name": "dsh-mnemon", + "version": "0.5.15", + "filename": "dsh-mnemon-0.5.15.tgz", + "sha256": "3a353cea8fe78ccdf625138a2e90ac24d80515f19ed873e51af9fc5ed17988b9" + }, + { + "name": "dsh-mnemon-provider-byterover", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-byterover-0.5.4.tgz", + "sha256": "17dcd57cb35c0c81af2b93c92be66c2e6b7e8180d5d1cf634c589281003dddc1" + }, + { + "name": "dsh-mnemon-provider-hindsight", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-hindsight-0.5.4.tgz", + "sha256": "739dec0d42689fdbdb8183ba75f7b6afc4be899b585c03c2a85842e0e998a170" + }, + { + "name": "dsh-mnemon-provider-holographic", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-holographic-0.5.4.tgz", + "sha256": "700fa0f94387ee04f5202dbbe0d410afb7114ba2486b25c32d64d7b059245f8c" + }, + { + "name": "dsh-mnemon-provider-honcho", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-honcho-0.5.4.tgz", + "sha256": "db8ffcbbc1df42555295a1ff818e2059b5c09776a0dc4cc9c311779355b06fd7" + }, + { + "name": "dsh-mnemon-provider-mem0", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-mem0-0.5.4.tgz", + "sha256": "ccd61716ead4d9462e15a662edb59c9850d812e8660bc33c4cc574601b816255" + }, + { + "name": "dsh-mnemon-provider-mnemon-native", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-mnemon-native-0.5.5.tgz", + "sha256": "8b839950e17b086578490b9eb3ef438331afa96a85ccd588ddaf7af9649f1f32" + }, + { + "name": "dsh-mnemon-provider-openviking", + "version": "0.5.6", + "filename": "dsh-mnemon-provider-openviking-0.5.6.tgz", + "sha256": "e5b3f9c80c6c450e5cb0e0103f30f8df39550dd980aac70dc4d44e069e2bc570" + }, + { + "name": "dsh-mnemon-provider-retaindb", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-retaindb-0.5.4.tgz", + "sha256": "8962cfc5b8e777a86c8816dfd47f32943aa6a1f1973bc8882727b4b430579e3a" + }, + { + "name": "dsh-mnemon-provider-supermemory", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-supermemory-0.5.4.tgz", + "sha256": "c117f467d0eb66954ab5b27a8ae08722770699e0f9113fa4bdc9f0724485b508" + }, + { + "name": "dsh-mnemon-source-documents", + "version": "0.5.6", + "filename": "dsh-mnemon-source-documents-0.5.6.tgz", + "sha256": "6ff72097bfcf0f02520a8599e9d6c5937e3ee317547fb60209b4188272de2728" + }, + { + "name": "dsh-mnemon-source-memory-spaces", + "version": "0.5.9", + "filename": "dsh-mnemon-source-memory-spaces-0.5.9.tgz", + "sha256": "42c308d133563a72c946086fd0c2d713eb32528ab30264de6785ab415a8377c7" + }, + { + "name": "dsh-mnemon-source-runtime", + "version": "0.5.9", + "filename": "dsh-mnemon-source-runtime-0.5.9.tgz", + "sha256": "e1242086b8e39fb09723160dbe896141a0909eeefedfc0a53ef4647aa9ee63c7" + }, + { + "name": "dsh-mnemon-strategy-auto-capture", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-auto-capture-0.5.4.tgz", + "sha256": "a653c1ebec85da0fe2c878e42a0eea84e20da877682fa1c9c97f5d8f8f70c074" + }, + { + "name": "dsh-mnemon-strategy-default-three-tier", + "version": "0.5.6", + "filename": "dsh-mnemon-strategy-default-three-tier-0.5.6.tgz", + "sha256": "4506f6a025832395ea0fbefeb58c4ddaa34ee0af369f258000619ba9a5f9efd4" + }, + { + "name": "dsh-mnemon-strategy-light-context", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-light-context-0.5.4.tgz", + "sha256": "eac6b61e9652ed54d03dd013bec285afe8714547a27c33ace286439e5958250b" + }, + { + "name": "dsh-mnemon-strategy-scoped", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-scoped-0.5.4.tgz", + "sha256": "c751c30b61f5ae60eca489cda936ed17a5b86dadaae538d9657dd76fe8467843" + } + ], + "optionalStrategies": [ + "scoped", + "light-context", + "auto-capture" + ], + "workspaceChecks": { + "docs": true, + "types": true, + "deterministicBuild": true, + "plugins": { + "passed": 406, + "skipped": 2 + }, + "root": { + "passed": 1379, + "skipped": 7, + "failed": 1, + "failure": "default performance wall time 5235.779875ms exceeded 5000ms; CPU assertion passed" + }, + "isolatedPerformance": { + "failed": 2, + "failure": "Both wall-time fences exceeded 5000ms; CPU assertions passed" + }, + "headless": true, + "package": true, + "unpackedRootBytes": 1374746, + "rootByteLimit": 1376000, + "nativeCliIntegration": true + }, + "independentArtifacts": { + "repositories": 16, + "artifacts": 17, + "passed": true + }, + "ci": { + "run": "https://github.com/omdsh-dev/dsh-mnemon/actions/runs/36221228218", + "passed": true, + "performanceThresholdUnchanged": true + }, + "webui": { + "actualUserReplaceWithEmptyBranches": true, + "actualInjectedMetadata": [ + { + "importance": "critical", + "created": "14d", + "updated": "0d" + } + ], + "runtimeSurvivesReload": true, + "editorOpensAndCancels": true, + "stableRootCount": 1, + "defaultBackgroundOpaque": true + }, + "limits": [ + "Loopback scripted model choices, actual DSH tools and storage", + "No live model request or Windows WebUI acceptance", + "Local wall-time failures retained; clean CI passed the unchanged fences" + ] +} diff --git a/dsh/docs/pr-assets/release-v0.5.15/wire.json b/dsh/docs/pr-assets/release-v0.5.15/wire.json new file mode 100644 index 000000000..3c804c22c --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.15/wire.json @@ -0,0 +1,48 @@ +{ + "call": { + "id": "issuerelease-v0.5.15-3", + "name": "mnemon_runtime_memory", + "args": { + "action": "replace", + "target": "user", + "old_text": "RELEASE515_USER: I prefer concise replies.", + "content": "RELEASE515_USER: I prefer concise replies with examples.", + "importance": "critical", + "branches": [] + } + }, + "receipt": { + "callId": "issuerelease-v0.5.15-3", + "isError": false, + "result": { + "success": true, + "message": "Entry replaced.", + "target": "user", + "entryCount": 1, + "usage": { + "used": 56, + "limit": 4096 + }, + "replaced": { + "from": "RELEASE515_USER: I prefer concise replies.", + "to": "RELEASE515_USER: I prefer concise replies with examples." + }, + "revision": "8ce71148fa43c5bcd6566b21fcc00ba503185fda91cca92bfff673f862f6951b", + "memoryReceipt": { + "status": "succeeded", + "completion": "committed", + "committedAt": "2026-09-26T05:38:02.374Z" + } + } + }, + "projection": { + "annotations": [ + { + "importance": "critical", + "created": "14d", + "updated": "0d" + } + ], + "renderedActualProjection": "Actual injected Runtime metadata: 1 annotation(s).\n\nUSER.md:\n[importance=critical; created=14d; updated=0d]\nRELEASE515_USER: I prefer concise replies with examples.\n\nMEMORY.md:\n(empty)" + } +} diff --git a/dsh/docs/pr-assets/release-v0.5.16/README.md b/dsh/docs/pr-assets/release-v0.5.16/README.md new file mode 100644 index 000000000..f75af4597 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.16/README.md @@ -0,0 +1,48 @@ +# v0.5.16 release acceptance + +[简体中文](./README.zh-CN.md) + +This record covers the versioned Starter `0.5.16` and Memory Spaces Source `0.5.10` with fifteen unchanged companion packages. Acceptance uses isolated storage, published DSH `0.1.7-rc.2`, the official Mnemon CLI `0.2.9`, and synthetic data. + +## Completed functional acceptance + +The official DSH CLI installed all seventeen versioned tarballs. All 57 installed runtime JavaScript/CSS files matched the packed bytes. The optional scoped, light-context and auto-capture Strategy plugins were enabled together. The Host and official DSH packages were unmodified. + +A loopback audit proxy forwarded thirteen real requests to DeepSeek's Messages API. Every response was HTTP 200 and identified `deepseek-flash`; tool choices and answers came from the model. Two initial protocol-only smoke requests are excluded from that count. No credential, authenticated URL or full model payload is included here. + +| Path | Observed result | +|---|---| +| Flash conversation → Runtime | Status reported healthy; USER write returned a committed receipt. A repeated write did not duplicate the entry. WebUI and disk showed the same content. | +| Runtime editor | Created and edited a MEMORY entry, then cleared its branch restriction using keyboard input. The persisted entry had no `branches` field. | +| Runtime capacity → Native | Adding 5,750-byte and 5,825-byte synthetic entries crossed the 10 KB budget. Existing content was indexed in Native before the pending entry committed; the UI reported completed archival and 5.8 KB active usage. USER remained local. | +| Documents → Native | Created, searched and read a document. Real Flash produced its archive plan, Native stored the cold index, and the original remained readable at its archived path with the same content hash. | +| Remember → Recall → Related | Real Flash checked the active space and existing evidence, then committed the requested convention. UI keyword search and the official CLI returned it. Related opened with a visible heading and close button. | +| Sidebar and frames | Switching Plugins → Memory System retained the query. Both native Sidebar rows measured 252 × 36 px at x=14. Status, Runtime, Documents and Memory Spaces headings all began at x=296, y=101.148 px in a 1280 × 720 viewport. | +| Cold restart → new conversation | Replaced the Host process and reloaded the browser. All five Runtime/Documents files retained their SHA-256 hashes; Native retained four insights and eight edges. A new Flash conversation recalled the Native canary and quoted both injected hot-memory entries exactly, without rewriting them. | + +The audited requests, artifact identities, restart hashes, geometry and screenshot hashes are in [verification.json](./verification.json). + +## Automated verification + +- The complete pre-version integration `pnpm verify` passed; its tree was identical to the three PRs merged on `main`. +- After versioning, docs, types, deterministic build, workspace builds and plugin tests passed. The complete Root suite passed with two workers: **103 files / 1,421 tests**, with three files / eight tests intentionally skipped. The two performance assertions also passed in isolation without changing their thresholds. +- Headless activation, public entry/package checks and strict package lint passed for the versioned Root artifact. +- Official CLI integration passed: **43 Runtime capacity tests** and **one Native Source composition integration test**, including actual Native write, recall, forget and multi-namespace archival. +- `MNEMON_PLUGIN_VERIFY_CONCURRENCY=1 pnpm verify:plugins --skip-build` passed: **16 independent plugin repositories / 17 packed artifacts**, external SDK consumer, actual DSH upgrade from v0.4.7 and all three optional Strategies together. + +One high-concurrency local run exceeded the two Root performance wall-clock limits. An overlapping independent-plugin run also failed Runtime UI tests, including timeouts. The complete Root suite passed with two workers and the independent-plugin gate passed serially; no assertions, timeouts or test thresholds were relaxed. Release CI runs the original full commands on fresh runners. + +## Screenshots + +![Actual Flash Runtime write](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.16/flash-runtime-write.jpg) +![Document archived through Flash and Native](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.16/documents-archived.jpg) +![Runtime capacity archival completed](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.16/runtime-capacity-archive.jpg) +![Native recall and visible Related controls](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.16/native-related.jpg) +![Version and storage health after cold restart](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.16/status-after-restart.jpg) +![New conversation receives hot memory after restart](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.16/cold-restart-flash-recall.jpg) + +Before/after comparisons for the fixes remain in the [metadata](../pr-286-plugin-metadata/README.md), [Sidebar](../sidebar-native-20260926/README.md) and [page-frame](../source-page-frames-20260926/README.md) records. + +## Scope + +Third-party Provider credentials were not configured; independent package and contract verification does not establish the availability of those remote services. Published DSH `0.1.7-rc.2` still has the `cordis:group` container display/toggle issue. This release includes neither a DSH patch nor a replacement plugin-management adapter. diff --git a/dsh/docs/pr-assets/release-v0.5.16/README.zh-CN.md b/dsh/docs/pr-assets/release-v0.5.16/README.zh-CN.md new file mode 100644 index 000000000..65692c0f8 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.16/README.zh-CN.md @@ -0,0 +1,48 @@ +# v0.5.16 发布验收 + +[English](./README.md) + +本记录覆盖版本化 Starter `0.5.16`、Memory Spaces Source `0.5.10` 和十五个版本不变的配套插件。验收使用隔离存储、正式发布的 DSH `0.1.7-rc.2`、官方 Mnemon CLI `0.2.9` 和合成数据。 + +## 已完成的功能验收 + +通过官方 DSH CLI 安装全部十七个版本化 tarball,已安装的 57 个运行时 JavaScript/CSS 文件均与制品字节一致。scoped、light-context 和 auto-capture 三个可选 Strategy 同时启用。Host 与官方 DSH 包均未修改。 + +本地审计代理向 DeepSeek Messages API 转发了十三次真实请求,全部返回 HTTP 200,响应模型均为 `deepseek-flash`;工具选择和回答均由真实模型生成。计数不含启动时的两次协议冒烟请求。本记录不包含凭据、带认证参数的 URL 或完整模型载荷。 + +| 链路 | 实际结果 | +|---|---| +| Flash 对话 → Runtime | 状态健康;USER 写入返回已提交回执;重复写入未产生重复条目;WebUI 与磁盘内容一致。 | +| Runtime 编辑器 | 创建并编辑 MEMORY 条目,用键盘清空分支限制;持久化条目不再含有 `branches` 字段。 | +| Runtime 容量 → Native | 连续添加 5,750 与 5,825 字节合成条目跨过 10 KB 上限;旧内容先写入 Native 索引,再提交待写条目;页面显示归档完成,活跃使用量为 5.8 KB。USER 保持本地。 | +| Documents → Native | 创建、检索并阅读档案;真实 Flash 拟定归档计划,Native 建立冷索引;归档原文仍可阅读,内容哈希不变。 | +| 沉淀 → 检索 → 关联 | 真实 Flash 检查激活空间与已有证据,随后提交指定约定;页面关键词检索与官方 CLI 均能读到;关联面板标题和关闭按钮可见。 | +| Sidebar 与页面框架 | 切换插件面板再返回记忆系统后,检索条件保留;两行原生 Sidebar 均为 252 × 36 px、x=14;1280 × 720 视口中,四个页面标题起点均为 x=296、y=101.148 px。 | +| 冷重启 → 新会话 | 替换 Host 进程并刷新浏览器;Runtime/Documents 的五个文件 SHA-256 均不变;Native 保留四条记忆与八条关系;新 Flash 会话可召回 Native 测试记忆,并逐字引用两条注入的热记忆,未重新写入。 | + +请求审计、制品标识、重启哈希、布局测量与截图哈希见 [verification.json](./verification.json)。 + +## 自动化验证 + +- 版本更新前的完整组合 `pnpm verify` 通过,验证树与三个 PR 合入 `main` 后完全一致。 +- 更新版本后,文档、类型、确定性构建、workspace 构建与插件测试通过。完整 Root 测试以两个 worker 运行:**103 个文件、1,421 项通过**,按配置跳过三个文件、八项测试。两项性能断言单独运行也通过,阈值未修改。 +- 版本化 Root 的 Headless 激活、公共入口、包内容和严格 package lint 通过。 +- 官方 CLI 集成通过:**43 项 Runtime 容量测试**与**一项 Native Source 组合集成测试**,包括真实 Native 写入、召回、忘记和多命名空间归档。 +- `MNEMON_PLUGIN_VERIFY_CONCURRENCY=1 pnpm verify:plugins --skip-build` 通过:**16 个独立插件仓库、17 个制品**,以及外部 SDK 消费者、真实 DSH 从 v0.4.7 升级和三个可选 Strategy 同时启用。 + +本机一次高并发验证超过两项 Root 性能测试的墙钟时限;同时进行的独立插件验证也有 Runtime UI 测试失败,其中包含超时。随后完整 Root 使用两个 worker 通过,独立插件验证串行通过;没有放宽断言、超时或性能阈值。正式 CI 在全新 runner 上执行原始完整命令。 + +## 截图 + +![真实 Flash 写入 Runtime](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.16/flash-runtime-write.jpg) +![Flash 与 Native 完成档案归档](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.16/documents-archived.jpg) +![Runtime 容量归档完成](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.16/runtime-capacity-archive.jpg) +![Native 检索与可见的关联操作](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.16/native-related.jpg) +![冷重启后的版本与存储状态](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.16/status-after-restart.jpg) +![新会话读取重启前的热记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.16/cold-restart-flash-recall.jpg) + +修复前后对比见[元数据](../pr-286-plugin-metadata/README.zh-CN.md)、[Sidebar](../sidebar-native-20260926/README.zh-CN.md)与[页面框架](../source-page-frames-20260926/README.md)记录。 + +## 验证范围 + +没有配置第三方 Provider 凭据;独立包与契约验证不代表对应远程服务已完成真实调用验收。官方 DSH `0.1.7-rc.2` 的 `cordis:group` 容器展示与开关问题仍存在,本版不包含 DSH 补丁或替代插件管理适配器。 diff --git a/dsh/docs/pr-assets/release-v0.5.16/verification.json b/dsh/docs/pr-assets/release-v0.5.16/verification.json new file mode 100644 index 000000000..9c5b4b1de --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.16/verification.json @@ -0,0 +1,607 @@ +{ + "release": "0.5.16", + "memorySpacesSource": "0.5.10", + "host": "DSH 0.1.7-rc.2 (published, unmodified)", + "mnemonCli": "0.2.9", + "model": "deepseek-flash", + "fixture": "Isolated storage and synthetic data; all three optional Strategy extensions enabled; loopback proxy forwards actual Flash requests, without scripted tool choices.", + "modelRequests": [ + { + "request": 3, + "activity": "agent", + "requestedModel": "deepseek-flash", + "returnedModels": [ + "deepseek-flash" + ], + "httpStatus": 200, + "toolCalls": [ + "mnemon_status" + ] + }, + { + "request": 4, + "activity": "session-title", + "requestedModel": "deepseek-flash", + "returnedModels": [ + "deepseek-flash" + ], + "httpStatus": 200, + "toolCalls": [] + }, + { + "request": 5, + "activity": "agent", + "requestedModel": "deepseek-flash", + "returnedModels": [ + "deepseek-flash" + ], + "httpStatus": 200, + "toolCalls": [ + "mnemon_runtime_memory" + ] + }, + { + "request": 6, + "activity": "agent", + "requestedModel": "deepseek-flash", + "returnedModels": [ + "deepseek-flash" + ], + "httpStatus": 200, + "toolCalls": [ + "mnemon_runtime_memory" + ] + }, + { + "request": 7, + "activity": "agent", + "requestedModel": "deepseek-flash", + "returnedModels": [ + "deepseek-flash" + ], + "httpStatus": 200, + "toolCalls": [] + }, + { + "request": 8, + "activity": "agent", + "requestedModel": "deepseek-flash", + "returnedModels": [ + "deepseek-flash" + ], + "httpStatus": 200, + "toolCalls": [ + "mnemon_subagent_result" + ] + }, + { + "request": 9, + "activity": "agent", + "requestedModel": "deepseek-flash", + "returnedModels": [ + "deepseek-flash" + ], + "httpStatus": 200, + "toolCalls": [ + "mnemon_memory_bodies", + "mnemon_recall" + ] + }, + { + "request": 10, + "activity": "agent", + "requestedModel": "deepseek-flash", + "returnedModels": [ + "deepseek-flash" + ], + "httpStatus": 200, + "toolCalls": [ + "mnemon_remember" + ] + }, + { + "request": 11, + "activity": "agent", + "requestedModel": "deepseek-flash", + "returnedModels": [ + "deepseek-flash" + ], + "httpStatus": 200, + "toolCalls": [ + "mnemon_subagent_result" + ] + }, + { + "request": 12, + "activity": "agent", + "requestedModel": "deepseek-flash", + "returnedModels": [ + "deepseek-flash" + ], + "httpStatus": 200, + "toolCalls": [ + "mnemon_recall" + ] + }, + { + "request": 13, + "activity": "session-title", + "requestedModel": "deepseek-flash", + "returnedModels": [ + "deepseek-flash" + ], + "httpStatus": 200, + "toolCalls": [] + }, + { + "request": 14, + "activity": "agent", + "requestedModel": "deepseek-flash", + "returnedModels": [ + "deepseek-flash" + ], + "httpStatus": 200, + "toolCalls": [] + }, + { + "request": 15, + "activity": "agent", + "requestedModel": "deepseek-flash", + "returnedModels": [ + "deepseek-flash" + ], + "httpStatus": 200, + "toolCalls": [] + } + ], + "committedToolReceipts": [ + { + "toolResultAction": "Entry added.", + "receipt": { + "status": "succeeded", + "completion": "committed", + "committedAt": "2026-09-26T14:26:11.163Z" + } + }, + { + "toolResultAction": "Entry already exists (no duplicate added).", + "receipt": { + "status": "succeeded", + "completion": "committed", + "committedAt": "2026-09-26T14:26:12.190Z" + } + }, + { + "toolResultAction": "added", + "receipt": { + "status": "succeeded", + "completion": "committed", + "committedAt": "2026-09-26T14:29:40.385Z" + } + } + ], + "restart": { + "hostProcessReplaced": true, + "runtimeAndDocumentsFilesUnchanged": true, + "fileCount": 5, + "hashes": { + "runtime/memories.json": "ff63b0466387ee0add097f3f766498139d010569bb1ad8588f4548087d34ee02", + "runtime/MEMORY.md": "1ae87b4c4090813be1eedbf4ce23af8877cb142df9f706b7b054804ecac94ebb", + "runtime/USER.md": "935003a7b196f759757598c32ff86452f981a9337ba7bd7f643729da51f230da", + "documents/index.json": "aac3bcc96e0db5be8e73c18773a78b9a34bed4df759d6f20fe6d5bcb803d38cd", + "documents/archived/release-0-5-16-document-acceptance-b00a00e3.md": "6a96379fa6568209e906391c213f179469c47885526cae48c749d058fac108be" + }, + "nativeInsightCounts": [ + 4, + 4 + ], + "nativeEdgeCounts": [ + 8, + 8 + ] + }, + "geometry": [ + { + "tab": "状态", + "headings": [ + { + "text": "记忆系统", + "x": 400, + "y": 15 + }, + { + "text": "系统状态", + "x": 296, + "y": 101.1484375 + }, + { + "text": "三方 Provider", + "x": 313, + "y": 373.1171875 + }, + { + "text": "存储域", + "x": 313, + "y": 948.9609375 + } + ] + }, + { + "tab": "运行时", + "headings": [ + { + "text": "记忆系统", + "x": 400, + "y": 15 + }, + { + "text": "运行时记忆", + "x": 296, + "y": 101.1484375 + }, + { + "text": "用户画像", + "x": 312, + "y": 214.5390625 + }, + { + "text": "工作记忆", + "x": 798.5, + "y": 214.5390625 + } + ] + }, + { + "tab": "档案", + "headings": [ + { + "text": "记忆系统", + "x": 400, + "y": 15 + }, + { + "text": "项目档案", + "x": 296, + "y": 101.1484375 + }, + { + "text": "选择一份档案", + "x": 811.171875, + "y": 617.2578125 + } + ] + }, + { + "tab": "记忆空间", + "headings": [ + { + "text": "记忆系统", + "x": 400, + "y": 15 + }, + { + "text": "记忆空间", + "x": 296, + "y": 101.1484375 + }, + { + "text": "概览", + "x": 296, + "y": 220.1875 + }, + { + "text": "记忆空间目录", + "x": 311, + "y": 289.9296875 + } + ] + } + ], + "sidebarRows": { + "x": 14, + "width": 252, + "height": 36, + "viewport": [ + 1280, + 720 + ] + }, + "relatedCloseButton": { + "x": 1214, + "y": 242.5703125, + "width": 26, + "height": 26 + }, + "artifacts": [ + { + "name": "dsh-mnemon", + "version": "0.5.16", + "filename": "dsh-mnemon-0.5.16.tgz", + "size": 305603, + "unpackedSize": 1345270, + "integrity": "sha512-4uMYSYxyc6283C1YEgozdBFldPZS/UAexFHB1gayYFptyAx8Ug3X2QQmH2ZTHW4YBrKlu2pVHMZz8qQo+UHouw==", + "sha256": "7bc4ec7fc3a4403ecacbdf7c8f221d1db570891e358d3e8503739e6196d54d07" + }, + { + "name": "dsh-mnemon-provider-byterover", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-byterover-0.5.4.tgz", + "size": 6460, + "unpackedSize": 15882, + "integrity": "sha512-XEu6dwuGLzQzoZv9Aata+xMGmXBrAMDMycy0YayXtXj2rD78GfgQiL8V3dOFDQkqi3nKSLn70LcxEavpmGhfqQ==", + "sha256": "17dcd57cb35c0c81af2b93c92be66c2e6b7e8180d5d1cf634c589281003dddc1" + }, + { + "name": "dsh-mnemon-provider-hindsight", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-hindsight-0.5.4.tgz", + "size": 14827, + "unpackedSize": 28850, + "integrity": "sha512-T7myWzmHdM8VP0Id6yKhG4o1EWcOkk2jEWLMAfwlCOoos6vjHjcZMyc9RiuYvzrld5Vq5G2WIegvuYF/tqT4dg==", + "sha256": "739dec0d42689fdbdb8183ba75f7b6afc4be899b585c03c2a85842e0e998a170" + }, + { + "name": "dsh-mnemon-provider-holographic", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-holographic-0.5.4.tgz", + "size": 7002, + "unpackedSize": 18929, + "integrity": "sha512-y92jU17pDE71Vl440SQsi2IpKoVtCaLAKYgt4NBU5avm9TTeLscVsGRx7U7jQIVEwUkh3G8j3m24VnXQYZk0eQ==", + "sha256": "700fa0f94387ee04f5202dbbe0d410afb7114ba2486b25c32d64d7b059245f8c" + }, + { + "name": "dsh-mnemon-provider-honcho", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-honcho-0.5.4.tgz", + "size": 7620, + "unpackedSize": 18569, + "integrity": "sha512-rfO7GKZ7USYTBOroiOOnY/NRHvUF9m5QuRdb6Rc4IVuKoU9tNJB7I+PtuogS75OWqYRuskcEYBRce2h/BAj3Sg==", + "sha256": "db8ffcbbc1df42555295a1ff818e2059b5c09776a0dc4cc9c311779355b06fd7" + }, + { + "name": "dsh-mnemon-provider-mem0", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-mem0-0.5.4.tgz", + "size": 8209, + "unpackedSize": 22624, + "integrity": "sha512-alI5h2edY5SufWmvU8rjeEwonn9L9OkWZ44K8bDuWIPfsY1qbfIsIQahu5OqcvS88ibBm4hZGeKR1j75M+s4Gg==", + "sha256": "ccd61716ead4d9462e15a662edb59c9850d812e8660bc33c4cc574601b816255" + }, + { + "name": "dsh-mnemon-provider-mnemon-native", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-mnemon-native-0.5.5.tgz", + "size": 7382, + "unpackedSize": 22315, + "integrity": "sha512-d/tLkxWOFy2GHYTOPPDkflR+2quIvvFOYk7YHpo+eS63VMaVjEl+++hzAWkeFyRwAkgCbwUR0izchn+JAyQQYg==", + "sha256": "8b839950e17b086578490b9eb3ef438331afa96a85ccd588ddaf7af9649f1f32" + }, + { + "name": "dsh-mnemon-provider-openviking", + "version": "0.5.6", + "filename": "dsh-mnemon-provider-openviking-0.5.6.tgz", + "size": 15668, + "unpackedSize": 35130, + "integrity": "sha512-r5cYcsg4jjosnQ1FKWR9hz+KDwAgYMladO8hE108crSzCQFcv4FXHkQILfKzHrPhza8Puom6moygpSDgIA3TWw==", + "sha256": "e5b3f9c80c6c450e5cb0e0103f30f8df39550dd980aac70dc4d44e069e2bc570" + }, + { + "name": "dsh-mnemon-provider-retaindb", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-retaindb-0.5.4.tgz", + "size": 5452, + "unpackedSize": 14986, + "integrity": "sha512-zX9NShhMwyfbVVdz9pj6XLH2n732mu+8VkxyiEpK1c4ElqyVm2P7Os1KFoOzNzdUHeNuHEHf98pWfKgmdWDFPA==", + "sha256": "8962cfc5b8e777a86c8816dfd47f32943aa6a1f1973bc8882727b4b430579e3a" + }, + { + "name": "dsh-mnemon-provider-supermemory", + "version": "0.5.4", + "filename": "dsh-mnemon-provider-supermemory-0.5.4.tgz", + "size": 5713, + "unpackedSize": 15541, + "integrity": "sha512-z70tDyX+J6LwmOFkQJtZqmnO1EUNjOrvAihtPlTQGnQVukZ/mRJ2FpMCFYb69Mteyi/SUaOrx0uAaDJc9ePCoA==", + "sha256": "c117f467d0eb66954ab5b27a8ae08722770699e0f9113fa4bdc9f0724485b508" + }, + { + "name": "dsh-mnemon-source-documents", + "version": "0.5.6", + "filename": "dsh-mnemon-source-documents-0.5.6.tgz", + "size": 40041, + "unpackedSize": 160056, + "integrity": "sha512-om966kGR67djbb/7ohqATdxLaGuWWEK4bsOyamAvAlwJsJidoHhF5bl5OYjig74FLCOzh6chDwxTc+JHEfnbNw==", + "sha256": "abf9921a2ab5044fc8eecd88371fa18cbf1fe74ccd54e9de21a6fa193901dd18" + }, + { + "name": "dsh-mnemon-source-memory-spaces", + "version": "0.5.10", + "filename": "dsh-mnemon-source-memory-spaces-0.5.10.tgz", + "size": 139989, + "unpackedSize": 659951, + "integrity": "sha512-B5p+lQXsM7GKm3emXQQNxRd+P2rdJ93PLUv1lU86PPmVnkldn40BrzonJrtO4XLAnk3EelPcW4+GkVpZtgXckg==", + "sha256": "0304a57c3e93378ea4d640d8d51ba54394330379f1c8e43f0cfcb6930f21ab7c" + }, + { + "name": "dsh-mnemon-source-runtime", + "version": "0.5.9", + "filename": "dsh-mnemon-source-runtime-0.5.9.tgz", + "size": 28344, + "unpackedSize": 115354, + "integrity": "sha512-SxW2dlG7mBWlpJzQCDXNaBcaMwF340ulkbIqpLxvgi+1onF+hTq8ByBJSKO+618Zov6TutNEZuAzCJht58mMYQ==", + "sha256": "4b7798c925d42de3b40e9b9aa9f96b337ad68d049d71bb030c2e638cb627bca7" + }, + { + "name": "dsh-mnemon-strategy-auto-capture", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-auto-capture-0.5.4.tgz", + "size": 3792, + "unpackedSize": 8663, + "integrity": "sha512-l6Xsfjp3/2EXZbTQB1+AdU6GXlCKgG2qhmQN7WhHmAL488APZ88MTKwvKc+1Np89+JWM4UfN1/h2rlOOK6RCdw==", + "sha256": "a653c1ebec85da0fe2c878e42a0eea84e20da877682fa1c9c97f5d8f8f70c074" + }, + { + "name": "dsh-mnemon-strategy-default-three-tier", + "version": "0.5.6", + "filename": "dsh-mnemon-strategy-default-three-tier-0.5.6.tgz", + "size": 15287, + "unpackedSize": 50565, + "integrity": "sha512-AwJSMRYvE6E0JUYIpyiUJJQV9swDzNzTQwrLj0L9eY9ogdS+bdpK2jWCFbFblQCyGB33E8PvXZosgELocsUPLg==", + "sha256": "4506f6a025832395ea0fbefeb58c4ddaa34ee0af369f258000619ba9a5f9efd4" + }, + { + "name": "dsh-mnemon-strategy-light-context", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-light-context-0.5.4.tgz", + "size": 3202, + "unpackedSize": 7266, + "integrity": "sha512-QmQ9s3ie45kGNcXDNmWZKhpzpD2FsdoQtRC8gRGFjtj94Ru8ppnmZnzZtTDGG3gHURuig4Lhcgtqj1vRUG3RcA==", + "sha256": "eac6b61e9652ed54d03dd013bec285afe8714547a27c33ace286439e5958250b" + }, + { + "name": "dsh-mnemon-strategy-scoped", + "version": "0.5.4", + "filename": "dsh-mnemon-strategy-scoped-0.5.4.tgz", + "size": 3648, + "unpackedSize": 8543, + "integrity": "sha512-kGfA+Z4PTNHVL3PunrG+4QsYBq0C8xSiica6l74qfQC/02hVf86v1nJq4dStPnyVQI4NQ02HkYlDrS2drpZIGA==", + "sha256": "c751c30b61f5ae60eca489cda936ed17a5b86dadaae538d9657dd76fe8467843" + } + ], + "installedArtifactMatch": [ + { + "name": "dsh-mnemon", + "version": "0.5.16", + "runtimeFilesMatched": 10, + "sha256": "7bc4ec7fc3a4403ecacbdf7c8f221d1db570891e358d3e8503739e6196d54d07" + }, + { + "name": "dsh-mnemon-provider-byterover", + "version": "0.5.4", + "runtimeFilesMatched": 3, + "sha256": "17dcd57cb35c0c81af2b93c92be66c2e6b7e8180d5d1cf634c589281003dddc1" + }, + { + "name": "dsh-mnemon-provider-hindsight", + "version": "0.5.4", + "runtimeFilesMatched": 3, + "sha256": "739dec0d42689fdbdb8183ba75f7b6afc4be899b585c03c2a85842e0e998a170" + }, + { + "name": "dsh-mnemon-provider-holographic", + "version": "0.5.4", + "runtimeFilesMatched": 3, + "sha256": "700fa0f94387ee04f5202dbbe0d410afb7114ba2486b25c32d64d7b059245f8c" + }, + { + "name": "dsh-mnemon-provider-honcho", + "version": "0.5.4", + "runtimeFilesMatched": 3, + "sha256": "db8ffcbbc1df42555295a1ff818e2059b5c09776a0dc4cc9c311779355b06fd7" + }, + { + "name": "dsh-mnemon-provider-mem0", + "version": "0.5.4", + "runtimeFilesMatched": 3, + "sha256": "ccd61716ead4d9462e15a662edb59c9850d812e8660bc33c4cc574601b816255" + }, + { + "name": "dsh-mnemon-provider-mnemon-native", + "version": "0.5.5", + "runtimeFilesMatched": 2, + "sha256": "8b839950e17b086578490b9eb3ef438331afa96a85ccd588ddaf7af9649f1f32" + }, + { + "name": "dsh-mnemon-provider-openviking", + "version": "0.5.6", + "runtimeFilesMatched": 3, + "sha256": "e5b3f9c80c6c450e5cb0e0103f30f8df39550dd980aac70dc4d44e069e2bc570" + }, + { + "name": "dsh-mnemon-provider-retaindb", + "version": "0.5.4", + "runtimeFilesMatched": 3, + "sha256": "8962cfc5b8e777a86c8816dfd47f32943aa6a1f1973bc8882727b4b430579e3a" + }, + { + "name": "dsh-mnemon-provider-supermemory", + "version": "0.5.4", + "runtimeFilesMatched": 3, + "sha256": "c117f467d0eb66954ab5b27a8ae08722770699e0f9113fa4bdc9f0724485b508" + }, + { + "name": "dsh-mnemon-source-documents", + "version": "0.5.6", + "runtimeFilesMatched": 3, + "sha256": "abf9921a2ab5044fc8eecd88371fa18cbf1fe74ccd54e9de21a6fa193901dd18" + }, + { + "name": "dsh-mnemon-source-memory-spaces", + "version": "0.5.10", + "runtimeFilesMatched": 9, + "sha256": "0304a57c3e93378ea4d640d8d51ba54394330379f1c8e43f0cfcb6930f21ab7c" + }, + { + "name": "dsh-mnemon-source-runtime", + "version": "0.5.9", + "runtimeFilesMatched": 4, + "sha256": "4b7798c925d42de3b40e9b9aa9f96b337ad68d049d71bb030c2e638cb627bca7" + }, + { + "name": "dsh-mnemon-strategy-auto-capture", + "version": "0.5.4", + "runtimeFilesMatched": 1, + "sha256": "a653c1ebec85da0fe2c878e42a0eea84e20da877682fa1c9c97f5d8f8f70c074" + }, + { + "name": "dsh-mnemon-strategy-default-three-tier", + "version": "0.5.6", + "runtimeFilesMatched": 2, + "sha256": "4506f6a025832395ea0fbefeb58c4ddaa34ee0af369f258000619ba9a5f9efd4" + }, + { + "name": "dsh-mnemon-strategy-light-context", + "version": "0.5.4", + "runtimeFilesMatched": 1, + "sha256": "eac6b61e9652ed54d03dd013bec285afe8714547a27c33ace286439e5958250b" + }, + { + "name": "dsh-mnemon-strategy-scoped", + "version": "0.5.4", + "runtimeFilesMatched": 1, + "sha256": "c751c30b61f5ae60eca489cda936ed17a5b86dadaae538d9657dd76fe8467843" + } + ], + "screenshots": { + "flash-runtime-write.jpg": "4be1113d34d653efe4d9268d81d88131f29452219e551daf690201609fa5b6f2", + "documents-archived.jpg": "daf639e079493d7ac78bead9198bb3b1ba93878ed8c4e31da8c69ed76c0a6b07", + "native-related.jpg": "1666ffbd15a47be8174cd88bcc46dcf7dc3109c57829bbd6f97b0f021fd1ce22", + "runtime-capacity-archive.jpg": "e31df999e2ddf57146354378014506f95ad5cc077e52ed4b18d89e1466408c2e", + "status-after-restart.jpg": "3ff6c6382a7a1c23c5fd26b00531e4da2b3e641c2bd8b902808f58e8ec2cd90a", + "cold-restart-flash-recall.jpg": "02efe44e9824d42afcd0d00edcfaa4eabf59dee6b753a07f97504f25f640e468" + }, + "limitations": [ + "Third-party provider credentials were not configured; their package/contract tests do not establish live service availability.", + "The published DSH cordis:group management issue is upstream and is not patched by these Mnemon packages." + ], + "testedVersionTree": "87cf6a07cbf2dc7bc195f1ba56c51dcd2a2ca9ce", + "mergedFeatureTree": "f5ad2783eb0ecc6c33e1ed2c2071bf8bd4e0da42", + "automated": { + "root": { + "passedTests": 1421, + "skippedTests": 8, + "passedFiles": 103, + "skippedFiles": 3, + "maxWorkers": 2 + }, + "nativeRuntimeCapacityTests": 43, + "nativeSourceCompositionTests": 1, + "independentPluginRepositories": 16, + "packedArtifacts": 17, + "pluginVerifyConcurrency": 1, + "externalConsumer": "passed", + "realDshUpgradeFrom": "0.4.7", + "realDshUpgrade": "passed", + "threeOptionalStrategiesTogether": "passed", + "headlessAndPackageChecks": "passed" + } +} diff --git a/dsh/docs/pr-assets/release-v0.5.17/README.md b/dsh/docs/pr-assets/release-v0.5.17/README.md new file mode 100644 index 000000000..6c05e755e --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.17/README.md @@ -0,0 +1,35 @@ +# v0.5.17 release acceptance + +[简体中文](./README.zh-CN.md) | **English** + +This record checks the composition in [v0.5.17](../../en/releases/v0.5.17.md), based on main `c417f087bdfbc5988e0ff9b19a81ab69cff929d8`, after thirteen merged PRs. It uses the published DSH 0.1.7-rc.2, Node 24.20.0 and real Mnemon CLI 0.2.9. The disposable profile installs only the packed Starter; a loopback registry resolves all seventeen exact companion packages. No DSH source is modified. All memory contents below are synthetic. + +## Functional evidence + +| Check | Observed result | +|---|---| +| Composition | Layered → General through the Plugins selector; dependent component switches reflect the active strategy. Scoped, Light Context and Auto Capture are enabled together. | +| Component settings | Runtime settings open from the composition card in their own dialog. | +| Model write | Live `deepseek-flash` executes `mnemon_runtime_memory`, committing “RELEASE0517 canary: Aurora integration uses pnpm 11.” to MEMORY; USER stays empty. | +| Runtime | The saved entry appears in the Runtime page. | +| Documents | Create an active Markdown document in the WebUI, then search `staging-aurora`; the saved result is returned. | +| Native | Create and activate a Native Memory Space in the WebUI, write a synthetic fact using the real CLI, then retrieve it from the WebUI with keyword recall. | +| Restart | A cold Host restart preserves hashes of six Runtime, Documents and Memory Space metadata files. A new conversation reads pnpm 11 from its injected Runtime projection. | + +[Layered composition](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.17/composition-layered.png) · [General composition](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.17/composition-general.png) · [Component settings](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.17/component-settings.png) · [Flash write](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.17/flash-runtime-write.png) · [Saved Runtime](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.17/runtime-persisted.png) · [Document search](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.17/documents-search.png) · [Native keyword recall](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.17/native-recall.png) + +## Release blocker reproduced and fixed + +The initial General-strategy Flash recall failed at the real DSH boundary: `value is not lossless JSON`. The Source correctly omitted optional Evidence fields; the Host fallback materialized `score` and `revision` as `undefined`. The fix omits absent fields and preserves supplied values, including a zero score. It applies to both named reads, recall and related, without changing Provider data or relaxing DSH validation. + +The regression uses the public General strategy with real composition and Holographic Provider fixtures. Before the fix, three of four metadata combinations fail; afterwards all four pass, alongside six existing Layered Host tests. [Before the fix](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.17/general-recall-before.png). + +The rebuilt eighteen-package composition installs in a second clean Profile with an unchanged copy of the synthetic data. The identical read-only Flash request succeeds: pnpm 11 from Runtime, and the violet gate from one Native keyword recall. All three live model requests return HTTP 200; the actual tool receipt has `isError: false`. The Status page reports v0.5.17, CLI 0.2.9, one Runtime entry, one document and one active Memory Space. [After the fix](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.17/general-recall-after.png) · [Final Status](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.17/status-final.png) · [Redacted verification and artifact hashes](./verification.json). + +## Verification scope + +Final `pnpm verify` passed 1,510 Root tests (6 skipped), workspace builds/type checks/tests, deterministic build, package/public-entry checks and real Headless checks. Seventeen independent plugin consumers and the complete eighteen-package install/upgrade passed. The independent-plugin checks also passed after the final Host fix, including the packed upgrade from v0.5.15. + +Real Native CLI integration and all 43 Runtime-capacity tests passed with `MNEMON_EMBED_ENDPOINT=http://127.0.0.1:1`, explicitly exercising the lexical fallback. The first run using the ambient embedding endpoint timed out; semantic embedding quality and live third-party Provider services are not claimed as covered. The real Flash requests use the published DSH adapter and tool runtime; credentials, auth URLs and full prompts are kept outside the repository. + +The upstream `cordis:group` display/toggle limitation remains documented. Local tarball verification is distinct from npm publication: the publish workflow must verify frozen artifacts, read back registry integrity, install the entire registry composition and upgrade from v0.5.16 before creating the GitHub Release. diff --git a/dsh/docs/pr-assets/release-v0.5.17/README.zh-CN.md b/dsh/docs/pr-assets/release-v0.5.17/README.zh-CN.md new file mode 100644 index 000000000..6943bbadf --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.17/README.zh-CN.md @@ -0,0 +1,35 @@ +# v0.5.17 发布验收 + +**简体中文** | [English](./README.md) + +本记录验收 [v0.5.17](../../zh-CN/releases/v0.5.17.md) 的实际组合,基于已合入十三个 PR 的 main `c417f087bdfbc5988e0ff9b19a81ab69cff929d8`。使用正式发布的 DSH 0.1.7-rc.2、Node 24.20.0 与真实 Mnemon CLI 0.2.9。隔离 Profile 只安装打包后的 Starter,由本地临时 Registry 解析十七个精确版本的伴随包。未修改 DSH 源码,下面的记忆均为合成验收数据。 + +## 功能证据 + +| 验收项 | 实际结果 | +|---|---| +| 组合 | 在插件页由分层策略切换为通用策略,依赖组件开关随之变化。Scoped、Light Context、Auto Capture 三个增强同时启用。 | +| 组件设置 | 由组合卡片打开 Runtime 独立设置弹窗。 | +| 模型写入 | 真实 `deepseek-flash` 调用 `mnemon_runtime_memory`,将 “RELEASE0517 canary: Aurora integration uses pnpm 11.” 写入 MEMORY;USER 仍为空。 | +| Runtime | Runtime 页面显示已保存条目。 | +| Documents | WebUI 创建活动 Markdown 档案,再搜索 `staging-aurora`,返回已保存的文档。 | +| Native | WebUI 创建并激活 Native 记忆空间,真实 CLI 写入合成事实,再从 WebUI 关键词召回。 | +| 重启 | Host 冷重启前后,六个 Runtime、Documents 与记忆空间元数据文件哈希不变;新对话从注入的 Runtime 投影读出 pnpm 11。 | + +[分层组合](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.17/composition-layered.png) · [通用组合](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.17/composition-general.png) · [组件设置](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.17/component-settings.png) · [Flash 写入](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.17/flash-runtime-write.png) · [已保存 Runtime](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.17/runtime-persisted.png) · [文档搜索](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.17/documents-search.png) · [Native 关键词召回](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.17/native-recall.png) + +## 已复现并修复的发布阻塞问题 + +首次通用策略 Flash 召回在真实 DSH 边界失败:`value is not lossless JSON`。Source 正确省略了可选证据字段,但 Host 转换时把 `score` 与 `revision` 写成 `undefined`。修复后省略缺失字段,保留已有值(包括零分数),同时覆盖 recall 与 related,不改变 Provider 数据、不放宽 DSH 校验。 + +回归测试使用公开的通用策略与真实组合、Holographic Provider fixture。修复前四组元数据中三组失败;修复后四组全部通过,同时六项已有分层策略 Host 测试通过。[修复前](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.17/general-recall-before.png)。 + +重新打包后的十八包组合安装至第二个全新 Profile,使用内容未变的合成数据副本。同一条只读 Flash 请求成功:由 Runtime 读出 pnpm 11,由一次 Native 关键词召回读出 violet gate。三次真实模型请求均返回 HTTP 200,实际工具回执为 `isError: false`。Status 显示 v0.5.17、CLI 0.2.9、一条 Runtime、一份档案及一个活动记忆空间。[修复后](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.17/general-recall-after.png) · [最终状态](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.17/status-final.png) · [脱敏验证记录与制品哈希](./verification.json)。 + +## 验证范围 + +最终 `pnpm verify` 通过 1,510 项 Root 测试(跳过 6 项),以及工作区构建、类型检查、测试、确定性构建、包与公共入口检查、真实 Headless 验证。十七个独立插件 consumer 和完整十八包安装、升级通过。最终 Host 修复之后,独立插件检查也全部通过,包括从 v0.5.15 升级到打包制品。 + +真实 Native CLI 集成及全部 43 项 Runtime 容量测试在 `MNEMON_EMBED_ENDPOINT=http://127.0.0.1:1` 下通过,明确验证关键词回退。首次使用环境默认 embedding 端点的测试超时;不声称已验证语义嵌入质量或所有第三方 Provider 的线上服务。真实 Flash 请求经过正式 DSH adapter 与工具运行时,凭据、认证 URL 和完整提示均保留在仓库外。 + +上游 `cordis:group` 显示与开关问题仍按文档说明处理。本地 tarball 验收不等于 npm 发布完成:发布工作流必须验证冻结制品、回读 Registry 完整性、安装完整组合并验证从 v0.5.16 升级,最后才创建 GitHub Release。 diff --git a/dsh/docs/pr-assets/release-v0.5.17/verification.json b/dsh/docs/pr-assets/release-v0.5.17/verification.json new file mode 100644 index 000000000..27be3b4e5 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.17/verification.json @@ -0,0 +1,207 @@ +{ + "release": "v0.5.17", + "baseMain": "c417f087bdfbc5988e0ff9b19a81ab69cff929d8", + "environment": { + "dsh": "0.1.7-rc.2", + "node": "24.20.0", + "mnemonCli": "0.2.9", + "model": "deepseek-flash", + "syntheticDataOnly": true + }, + "regression": { + "before": { + "passed": 1, + "failed": 3 + }, + "after": { + "generalPassed": 4, + "existingLayeredPassed": 6 + }, + "rootSuite": { + "passed": 1510, + "skipped": 6 + } + }, + "fixedArtifactFlash": { + "requests": 3, + "httpStatus": [ + 200, + 200, + 200 + ], + "toolCalls": [ + "mnemon_recall" + ], + "receipts": [ + { + "callId": "call_00_yjkkr02bafDAPvrpezHi7841", + "isError": false, + "result": { + "query": "RELEASE0517native", + "mode": "keyword", + "results": [ + { + "memoryBodyId": "default", + "memoryBodyName": "Release0517 Native", + "memoryProviderId": "mnemon-native", + "memoryProviderLabel": "mnemon", + "memoryCapabilities": { + "search": true, + "browse": true, + "graph": true, + "entities": true, + "related": true, + "remember": true, + "link": true, + "forget": true, + "writeMode": "exact", + "deletionMode": "soft" + }, + "relevanceTier": "high", + "category": "fact", + "importance": 3, + "tags": [], + "id": "2f09f160-b498-4658-8012-ba62cac80181", + "content": "RELEASE0517native: Aurora rollout uses the violet gate.", + "score": 1 + } + ] + } + } + ], + "runtimeCanaryProjected": true + }, + "persistence": { + "coldRestartPreserved": true, + "copiedUnchangedIntoFixedFixture": true, + "files": { + "runtime/memories.json": "0e571992d4bed6af018109915d245d88cdbb7a88deb2dfd5e860d9f7933357d0", + "runtime/MEMORY.md": "f7c3a53c195a546da252bdf87833accfdc070bd4754ac4a774ea46982ef2da92", + "runtime/USER.md": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "documents/index.json": "4a3333765aaec11f0c1f37d7035164d49919aea91f0b367796c958852d6f15e1", + "documents/active/release-0-5-17-acceptance-a57136e2.md": "40125cd50c6709758f2f6b4b3d21ebc9c4b66d9cb135fd917131173608cc422a", + "data/.dsh-memory-bodies.json": "5eda4c0d69157a628512a39d4af47fcd6148ddd1476e14bf30de4f4d8b8235d3" + } + }, + "artifacts": [ + { + "name": "dsh-mnemon", + "version": "0.5.17", + "filename": "dsh-mnemon-0.5.17.tgz", + "sha256": "8f6b88909aab08485546c22e1e6f94d164c1143fe467e4701c95e4cd41f7fe03" + }, + { + "name": "dsh-mnemon-provider-byterover", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-byterover-0.5.5.tgz", + "sha256": "de6bbbde7f16ec2601efab585cd338b6858c5d3d48441ba181cb38fe57d96b35" + }, + { + "name": "dsh-mnemon-provider-hindsight", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-hindsight-0.5.5.tgz", + "sha256": "5ce19336b123c915dc62857f754011a523e18ed0673d216bdb56b268f2120645" + }, + { + "name": "dsh-mnemon-provider-holographic", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-holographic-0.5.5.tgz", + "sha256": "ba927c1d38077d52da7a50e773a57c8cefba2e1f0a3de3b8cefde07c0934c3cd" + }, + { + "name": "dsh-mnemon-provider-honcho", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-honcho-0.5.5.tgz", + "sha256": "4b742ab24408b0b2a391a92e47cc10bba7f6e8f6cdf30fd687c6a1bf227a194b" + }, + { + "name": "dsh-mnemon-provider-mem0", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-mem0-0.5.5.tgz", + "sha256": "96a01ddd47d198ee318c7b9f4b5bfc989e5b123f7cf819621e61ae7fd04b44c7" + }, + { + "name": "dsh-mnemon-provider-mnemon-native", + "version": "0.5.6", + "filename": "dsh-mnemon-provider-mnemon-native-0.5.6.tgz", + "sha256": "ed05278fa5d513aed43ad9e585aba6960236f785d47b1d3093df2950b3caa987" + }, + { + "name": "dsh-mnemon-provider-openviking", + "version": "0.5.7", + "filename": "dsh-mnemon-provider-openviking-0.5.7.tgz", + "sha256": "b309045058d353c1aacc8aba5416dbbffc2c365d85278f3e00e601ffb1df5844" + }, + { + "name": "dsh-mnemon-provider-retaindb", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-retaindb-0.5.5.tgz", + "sha256": "5644167e8a9b77597d4c8c67b626d7e82f36c054b9b3e11b7b0f1f2b8360d158" + }, + { + "name": "dsh-mnemon-provider-supermemory", + "version": "0.5.5", + "filename": "dsh-mnemon-provider-supermemory-0.5.5.tgz", + "sha256": "fe74e5dcfe591124ae8c75e5335f12f4b3e47ff18fcfa29eae07c5ad31589664" + }, + { + "name": "dsh-mnemon-source-documents", + "version": "0.5.7", + "filename": "dsh-mnemon-source-documents-0.5.7.tgz", + "sha256": "40b1de503ace28bb2f7b011b1b3997892a28edca7db6e53882556cbcbfe27583" + }, + { + "name": "dsh-mnemon-source-memory-spaces", + "version": "0.5.11", + "filename": "dsh-mnemon-source-memory-spaces-0.5.11.tgz", + "sha256": "4c214b762210c19a0a2d3d62fc91dda34c2ed8734cb1446c9633bc441f74eeb9" + }, + { + "name": "dsh-mnemon-source-runtime", + "version": "0.5.10", + "filename": "dsh-mnemon-source-runtime-0.5.10.tgz", + "sha256": "6df8003d59ba9d76f74639fe1d59623bdbdc5fa3f95ba48ff3f45078098931d3" + }, + { + "name": "dsh-mnemon-strategy-auto-capture", + "version": "0.5.5", + "filename": "dsh-mnemon-strategy-auto-capture-0.5.5.tgz", + "sha256": "cfe19f1c940e3f8adda8fcd973993a682410ae167987384615736d254207605c" + }, + { + "name": "dsh-mnemon-strategy-default-three-tier", + "version": "0.5.7", + "filename": "dsh-mnemon-strategy-default-three-tier-0.5.7.tgz", + "sha256": "2d4993c07470459dcf3f76e4ece054c841957ea8481439e5b6235b58deb58c5f" + }, + { + "name": "dsh-mnemon-strategy-general", + "version": "0.5.1", + "filename": "dsh-mnemon-strategy-general-0.5.1.tgz", + "sha256": "98ebf4bf9c764e37098017fb3d01489dc4124af89ec222e4b0a59e2ee1767942" + }, + { + "name": "dsh-mnemon-strategy-light-context", + "version": "0.5.5", + "filename": "dsh-mnemon-strategy-light-context-0.5.5.tgz", + "sha256": "5d84387ecb82ce4947ddba6b473f79ad5d46506b5e1bd2baaf9fb574c6bd8aa8" + }, + { + "name": "dsh-mnemon-strategy-scoped", + "version": "0.5.5", + "filename": "dsh-mnemon-strategy-scoped-0.5.5.tgz", + "sha256": "dae822c84e7965f7e3aa6af92406da75705622a868b5931bf70e1257c0990477" + } + ], + "limits": [ + "Keyword recall with the Native lexical fallback; semantic embedding quality not verified.", + "Live third-party Provider services not covered.", + "Local tarball receipt; publication integrity and registry upgrade are separate workflow gates." + ], + "independentPlugins": { + "passed": 17, + "packedArtifacts": 18, + "packedUpgradeFrom": "0.5.15", + "simultaneousEnhancements": 3 + } +} diff --git a/dsh/docs/pr-assets/release-v0.5.18/README.md b/dsh/docs/pr-assets/release-v0.5.18/README.md new file mode 100644 index 000000000..c1348a659 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.18/README.md @@ -0,0 +1,31 @@ +# v0.5.18 release acceptance + +[中文](README.zh-CN.md) + +Verified on 2026-09-28, after merging #308 on top of #306. The versioned candidate is `dsh-mnemon@0.5.18` with `dsh-mnemon-source-memory-spaces@0.5.12`; the other 16 component packages come from npm at the Starter's pinned versions. + +## Real official WebUI + +Using unmodified npm DSH `0.1.7-rc.2`, Node `24.20.0` and a fresh isolated profile: + +1. Started the host without Mnemon installed. +2. Installed the versioned candidate through **Add plugin**. The loopback registry served only the two changed candidate tarballs; unchanged packages came from the official npm registry. +3. Clicked **Enable now** and opened Memory System. The page reports **dsh-mnemon 0.5.18 / System nominal** without a host restart. +4. Created and read the `RELEASE_0518_ACCEPTANCE` Runtime entry through the WebUI. +5. Created and activated a Native space through the WebUI, wrote `RELEASE_0518_NATIVE` with Mnemon CLI `0.2.9`, recalled it with the CLI, then retrieved the same fact using the official WebUI's basic recall. + +[Artifact hashes, installed versions and unchanged host PID](validation.json) identify this candidate. Profiles, paths and memories are synthetic. No remote model or third-party Provider was needed for these installation checks. + +![Versioned Status](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.18/status.png) + +[Runtime write/read](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.18/runtime.png) + +![Native CLI memory retrieved by the official WebUI](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.18/recall.png) + +## Validation and release boundary + +`pnpm run release:check` selects only the Starter and Memory Spaces Source for publication. `pnpm run verify:package` passes package content checks, 13 public Node entry imports, type declarations, `publint` and `attw`. + +The release PR and publication workflow run complete workspace and packed-plugin verification. Publication freezes the merged main revision, publishes the changed Source before the Starter, reads back the npm artifacts, installs the complete 18-package combination and checks a real Registry upgrade before creating the GitHub release. These screenshots show the versioned local candidate; they do not by themselves certify npm publication. + +The [activation regression evidence](../desktop-live-activation/README.md) additionally covers Documents, bundle disable/re-enable with retained data, isolated Desktop generation dependencies and real Electron CLI execution. [Issue #305 evidence](../issue-305-desktop-console/README.md) covers the npm Native launcher and its verification limits. diff --git a/dsh/docs/pr-assets/release-v0.5.18/README.zh-CN.md b/dsh/docs/pr-assets/release-v0.5.18/README.zh-CN.md new file mode 100644 index 000000000..381520454 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.18/README.zh-CN.md @@ -0,0 +1,31 @@ +# v0.5.18 发布验收 + +[English](README.md) + +2026-09-28,在 #306 基础上合入 #308 后验收。版本化候选为 `dsh-mnemon@0.5.18` 与 `dsh-mnemon-source-memory-spaces@0.5.12`,其余 16 个组件从 npm 安装 Starter 固定的版本。 + +## 真实官方 WebUI + +使用未修改的 npm DSH `0.1.7-rc.2`、Node `24.20.0` 和全新隔离 profile: + +1. 在未安装 Mnemon 时启动宿主。 +2. 通过“添加插件”安装版本化候选。本地回环 registry 只提供两个变化的候选 tarball,其余包来自官方 npm registry。 +3. 点击“立即启用”,进入记忆系统。页面显示 **dsh-mnemon 0.5.18/系统正常**,宿主未重启。 +4. 通过 WebUI 新建并读取 `RELEASE_0518_ACCEPTANCE` Runtime 条目。 +5. 通过 WebUI 创建并激活 Native 空间,用 Mnemon CLI `0.2.9` 写入 `RELEASE_0518_NATIVE` 并召回,再在官方 WebUI 中用基础检索读取同一条事实。 + +[制品摘要、安装版本与未变化的宿主 PID](validation.json)标识本次候选制品。profile、目录和记忆均为测试数据;本次安装验收不需要远程模型或第三方 Provider。 + +![版本化状态页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.18/status.png) + +[Runtime 写入与读取](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.18/runtime.png) + +![官方 WebUI 读取 Native CLI 写入的记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.18/recall.png) + +## 验证与发布边界 + +`pnpm run release:check` 仅选中 Starter 和 Memory Spaces Source 发布。`pnpm run verify:package` 的包内容、13 个 Node 公开入口导入、类型声明、`publint` 和 `attw` 均通过。 + +发布 PR 和发布工作流运行完整 workspace 与独立打包插件验证。发布时冻结已合入 main 的 revision,先发布变化的 Source,再发布 Starter;回读 npm 制品,安装完整 18 包组合,验证真实 Registry 升级,最后创建 GitHub Release。这些截图展示版本化本地候选,本身不代表 npm 已发布。 + +[启用回归记录](../desktop-live-activation/README.zh-CN.md)还覆盖 Documents、停用再启用后的数据保留、Desktop 隔离 generation 依赖和真实 Electron CLI 执行。[Issue #305 验收](../issue-305-desktop-console/README.md)记录 npm Native 启动器与验证范围。 diff --git a/dsh/docs/pr-assets/release-v0.5.18/validation.json b/dsh/docs/pr-assets/release-v0.5.18/validation.json new file mode 100644 index 000000000..1922f5249 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.18/validation.json @@ -0,0 +1,50 @@ +{ + "verifiedAt": "2026-09-28T12:56:40.086932+00:00", + "sourceBase": "da1fa6fd160059f07a0f320dc1520997bb7823e0", + "candidate": "release version changes on the merged source base", + "dsh": "0.1.7-rc.2", + "node": "24.20.0", + "mnemonCLI": "0.2.9", + "artifactSha256": { + "dsh-mnemon-0.5.18.tgz": "536f566304d445786fb67836d73b11569e832718259b830492f5528f451f181a", + "dsh-mnemon-source-memory-spaces-0.5.12.tgz": "532b83cfbdc0ad81ed595e16513784d23161d29c754ce7f12c8a9ecc0bf1e62b" + }, + "installedVersions": { + "dsh-mnemon": "0.5.18", + "dsh-mnemon-provider-byterover": "0.5.5", + "dsh-mnemon-provider-hindsight": "0.5.5", + "dsh-mnemon-provider-holographic": "0.5.5", + "dsh-mnemon-provider-honcho": "0.5.5", + "dsh-mnemon-provider-mem0": "0.5.5", + "dsh-mnemon-provider-mnemon-native": "0.5.6", + "dsh-mnemon-provider-openviking": "0.5.7", + "dsh-mnemon-provider-retaindb": "0.5.5", + "dsh-mnemon-provider-supermemory": "0.5.5", + "dsh-mnemon-source-documents": "0.5.7", + "dsh-mnemon-source-memory-spaces": "0.5.12", + "dsh-mnemon-source-runtime": "0.5.10", + "dsh-mnemon-strategy-auto-capture": "0.5.5", + "dsh-mnemon-strategy-default-three-tier": "0.5.7", + "dsh-mnemon-strategy-general": "0.5.1", + "dsh-mnemon-strategy-light-context": "0.5.5", + "dsh-mnemon-strategy-scoped": "0.5.5" + }, + "hostPidAtLaunch": 26012, + "hostPidAfterAcceptance": 26012, + "hostLaunchCount": 1, + "hostAlive": true, + "officialWebUI": { + "installAndEnableNow": true, + "statusVersion": "0.5.18", + "runtimeWriteAndRead": true, + "nativeSpaceCreateAndActivate": true, + "nativeBasicRecallMatchesCLI": true + }, + "nativeCLI": { + "rememberAction": "added", + "memoryId": "d62b1edc-afe3-4e90-b388-89d2426c0bdd", + "content": "RELEASE_0518_NATIVE: The versioned release shares durable Native memory between CLI and the official WebUI.", + "recallMatched": true + }, + "scope": "Versioned local candidates installed through official WebUI; remaining pinned packages from npm; isolated data only." +} diff --git a/dsh/docs/pr-assets/release-v0.5.19/README.md b/dsh/docs/pr-assets/release-v0.5.19/README.md new file mode 100644 index 000000000..eef7f0921 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.19/README.md @@ -0,0 +1,30 @@ +# v0.5.19 release acceptance + +[中文](README.zh-CN.md) + +Verified on 2026-09-29 (Asia/Shanghai), after merging #311 on top of #307. The versioned candidate is `dsh-mnemon@0.5.19` with `dsh-mnemon-source-runtime@0.5.11`, `dsh-mnemon-source-documents@0.5.8`, `dsh-mnemon-source-memory-spaces@0.5.13` and `dsh-mnemon-provider-mnemon-native@0.5.7`; the other 13 component packages come from npm at the Starter's pinned versions. + +## Real official WebUI on both supported hosts + +On unmodified npm DSH `0.2.0-rc.1` and `0.1.7-rc.2`, each with Node `24.19.0`, a fresh home and a fresh profile: + +1. Started the host without Mnemon installed and opened **Plugins → Add plugin**. +2. Installed `dsh-mnemon`. DSH chose the install source itself (its speed check picked the mainland China mirror). A loopback registry answered with npm's real metadata plus the five changed versions, published a week earlier, so the unchanged packages came from npm's own tarballs. +3. Clicked **Enable now** and opened Memory System. [Status](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.19/status.png) reports **dsh-mnemon 0.5.19 / System nominal**, and Mnemon Native names **Mnemon 0.2.7**, without a host restart. +4. Added the `RELEASE_0519_RUNTIME` entry in Runtime memory and read it back after reloading the page: [Runtime](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.19/runtime.png). +5. Created the **发布验收** Mnemon Native space in the WebUI and activated it: [Memory Spaces](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.19/spaces.png). Wrote `RELEASE_0519_NATIVE` with Mnemon CLI `0.2.7`, recalled it with the CLI, then found the same fact with the WebUI's **Direct recall**: [Recall](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.19/recall.png). +6. On a second fresh profile per host, installed from the command line, the live DeepSeek model answered a first message and wrote two runtime entries ([reply](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.19/live-reply.png)); **Save to memory** handed the candidate to the task Agent, which saved it into a new memory space and returned a receipt ([receipt](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.19/live-receipt.png)). + +Both hosts passed every step without a console error, and each host process was started once. [validation.json](validation.json) has the artifact hashes, the installed versions of all 18 packages and each host's results. Profiles and memories are synthetic; the API key reached DSH through the environment only. + +![Status on DSH 0.2.0-rc.1](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.19/status.png) + +![The CLI-written Native memory found by Direct recall](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.19/recall.png) + +## Validation and release boundary + +`pnpm run release:check` selects the Starter, the three Sources and Mnemon Native for publication. The three Sources use Source-page SDK exports that first ship in this Starter, so their `dsh-mnemon` peer floor is `^0.5.19`. The lockfile changes only the new version specifiers, and pnpm 10.13.1 accepts it with `--frozen-lockfile`. + +The release pull request and the publication workflow run the complete workspace and packed-plugin verification. Publication freezes the merged main revision, publishes the changed plugins before the Starter, reads back the npm artifacts, installs the complete 18-package combination and checks a real Registry upgrade before creating the GitHub release. These screenshots show the versioned local candidate; they do not by themselves certify npm publication. + +Related evidence: [desktop windows (#310)](../issue-310-desktop-window/README.md), the [installation gallery](../../assets/install-v0.5.19/README.md) and the [live v0.5.19 gallery](../../assets/webui-v0.5.19/README.md). diff --git a/dsh/docs/pr-assets/release-v0.5.19/README.zh-CN.md b/dsh/docs/pr-assets/release-v0.5.19/README.zh-CN.md new file mode 100644 index 000000000..8ba299e85 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.19/README.zh-CN.md @@ -0,0 +1,30 @@ +# v0.5.19 发布验收 + +[English](README.md) + +于 2026-09-29(Asia/Shanghai)在 #307 之后合入 #311 的基础上验证。版本化候选为 `dsh-mnemon@0.5.19`,以及 `dsh-mnemon-source-runtime@0.5.11`、`dsh-mnemon-source-documents@0.5.8`、`dsh-mnemon-source-memory-spaces@0.5.13` 与 `dsh-mnemon-provider-mnemon-native@0.5.7`;其余 13 个组件按 Starter 固定的版本从 npm 安装。 + +## 两个受支持宿主上的官方 WebUI + +分别使用未修改的 npm DSH `0.2.0-rc.1` 与 `0.1.7-rc.2`,Node `24.19.0`,全新的用户目录与 profile: + +1. 启动未安装 Mnemon 的宿主,打开**插件 → 添加插件**。 +2. 安装 `dsh-mnemon`。安装源由 DSH 自己选择(测速选择了中国大陆镜像源)。本地 registry 返回 npm 的真实元数据,并把五个变化的版本作为一周前发布的新版本加入,因此未变化的包直接从 npm 下载。 +3. 点击**立即启用**并打开记忆系统。[状态页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.19/status.png)显示 **dsh-mnemon 0.5.19 / 系统正常**,Mnemon Native 显示 **Mnemon 0.2.7**,宿主未重启。 +4. 在运行时记忆中添加 `RELEASE_0519_RUNTIME` 条目,刷新页面后读回:[运行时记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.19/runtime.png)。 +5. 在 WebUI 中创建并激活 Mnemon Native 记忆空间“发布验收”:[记忆空间](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.19/spaces.png)。用 Mnemon CLI `0.2.7` 写入 `RELEASE_0519_NATIVE` 并用 CLI 召回,再用 WebUI 的**直接检索**找到同一条事实:[检索](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.19/recall.png)。 +6. 在每个宿主上另建一个用命令行安装的全新 profile,由真实的 DeepSeek 模型回答第一条消息,写入两条运行时记忆([回复](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.19/live-reply.png));**存入记忆**把候选内容交给任务 Agent,任务 Agent 将其存入新建的记忆空间并给出回执([回执](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.19/live-receipt.png))。 + +两个宿主的每一步均通过,无控制台错误,每个宿主进程只启动一次。[validation.json](validation.json) 记录了制品哈希、全部 18 个包的安装版本与各宿主的结果。profile 与记忆均为合成数据;API Key 仅通过环境变量交给 DSH。 + +![DSH 0.2.0-rc.1 上的状态页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.19/status.png) + +![直接检索找到 CLI 写入的 Native 记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.19/recall.png) + +## 验证与发布边界 + +`pnpm run release:check` 选择 Starter、三个 Source 与 Mnemon Native 发布。三个 Source 使用了本版 Starter 首次提供的 Source 页面 SDK 导出,因此它们的 `dsh-mnemon` peer 下限为 `^0.5.19`。锁文件只改变新的版本说明符,pnpm 10.13.1 以 `--frozen-lockfile` 接受它。 + +发布 PR 与发布工作流会运行完整的工作区与打包插件验证。发布时冻结合并后的 main 修订,先发布变化的插件再发布 Starter,读回 npm 制品,安装完整的 18 包组合并检查真实的 Registry 升级,然后创建 GitHub Release。这些截图展示的是版本化本地候选,本身不能证明 npm 发布结果。 + +相关证据:[桌面版窗口(#310)](../issue-310-desktop-window/README.zh-CN.md)、[安装图集](../../assets/install-v0.5.19/README.md)与 [v0.5.19 真实模型图集](../../assets/webui-v0.5.19/README.md)。 diff --git a/dsh/docs/pr-assets/release-v0.5.19/validation.json b/dsh/docs/pr-assets/release-v0.5.19/validation.json new file mode 100644 index 000000000..07ae608cf --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.19/validation.json @@ -0,0 +1,81 @@ +{ + "verifiedAt": "2026-09-29", + "timezone": "Asia/Shanghai", + "sourceBase": "543b42b14815e21e2810fa44c56518b5a9dc5463", + "candidate": "pnpm release:version on the source base, the three Sources' dsh-mnemon peer floors raised to ^0.5.19, built and packed locally", + "artifactSha256": { + "dsh-mnemon-0.5.19.tgz": "a2a8ce77ada8d86588bc207ab5f39b47ae4a067910f0deab5fe0b68ce2319e1e", + "dsh-mnemon-source-runtime-0.5.11.tgz": "b552ae6f2ff8c74674dc3da6f78eaa2cf4a8068a93f39dc417f7c98f30ff583b", + "dsh-mnemon-source-documents-0.5.8.tgz": "98bfb9d51b49ee121b94b8952992f3e5c49c24cbd375e69bf4be1b5b84604943", + "dsh-mnemon-source-memory-spaces-0.5.13.tgz": "c538c1ea11942baaaa6e4bd60b50a041ada2afeab50f6c13b6151b511a52313d", + "dsh-mnemon-provider-mnemon-native-0.5.7.tgz": "c11765a978b50ffe16be2ff1d7e72141a77581ea63e6293d0336ce44324a135c" + }, + "registry": "A loopback registry served npm's real metadata for every dsh-mnemon package and added the five changed versions, published a week earlier; unchanged versions resolved to npm's own tarballs. DSH chose the install source (its speed check picked the mainland China mirror); a pnpm wrapper sent that source's requests to the loopback registry.", + "node": "24.19.0", + "pnpmForDsh": "11.19.0", + "mnemonCLI": "0.2.7", + "installedVersions": { + "dsh-mnemon": "0.5.19", + "dsh-mnemon-provider-byterover": "0.5.5", + "dsh-mnemon-provider-hindsight": "0.5.5", + "dsh-mnemon-provider-holographic": "0.5.5", + "dsh-mnemon-provider-honcho": "0.5.5", + "dsh-mnemon-provider-mem0": "0.5.5", + "dsh-mnemon-provider-mnemon-native": "0.5.7", + "dsh-mnemon-provider-openviking": "0.5.7", + "dsh-mnemon-provider-retaindb": "0.5.5", + "dsh-mnemon-provider-supermemory": "0.5.5", + "dsh-mnemon-source-documents": "0.5.8", + "dsh-mnemon-source-memory-spaces": "0.5.13", + "dsh-mnemon-source-runtime": "0.5.11", + "dsh-mnemon-strategy-auto-capture": "0.5.5", + "dsh-mnemon-strategy-default-three-tier": "0.5.7", + "dsh-mnemon-strategy-general": "0.5.1", + "dsh-mnemon-strategy-light-context": "0.5.5", + "dsh-mnemon-strategy-scoped": "0.5.5" + }, + "hosts": { + "0.2.0-rc.1": { + "addPluginInstall": "0.5.19", + "enableNow": true, + "hostLaunches": 1, + "status": { + "starter": "0.5.19", + "systemNominal": true, + "nativeCli": "0.2.7" + }, + "runtimeWriteAndReadAfterReload": true, + "nativeSpaceCreatedAndActivated": "1 / 1 active", + "cliRememberAndRecall": true, + "webDirectRecallFindsCliMemory": true, + "liveModel": { + "turnMemory": "wrote 2 (runtime memory)", + "saveToMemory": "task Agent saved it into a new memory space and returned a receipt", + "profile": "a second fresh profile on the same host, installed with dsh plugin --profile web add dsh-mnemon" + }, + "consoleErrors": 0 + }, + "0.1.7-rc.2": { + "addPluginInstall": "0.5.19", + "enableNow": true, + "hostLaunches": 1, + "status": { + "starter": "0.5.19", + "systemNominal": true, + "nativeCli": "0.2.7" + }, + "runtimeWriteAndReadAfterReload": true, + "nativeSpaceCreatedAndActivated": "1 / 1 active", + "cliRememberAndRecall": true, + "webDirectRecallFindsCliMemory": true, + "liveModel": { + "turnMemory": "wrote 2 (runtime memory)", + "saveToMemory": "task Agent saved it into a new memory space and returned a receipt", + "profile": "a second fresh profile on the same host, installed with dsh plugin --profile web add dsh-mnemon" + }, + "consoleErrors": 0 + } + }, + "model": "DeepSeek API, deepseek-flash (DeepSeek-V41-Flash in the WebUI); the key reached DSH through the environment only", + "scope": "Versioned local candidates installed through the official WebUI on isolated homes and profiles; synthetic data only." +} diff --git a/dsh/docs/pr-assets/release-v0.5.20/README.md b/dsh/docs/pr-assets/release-v0.5.20/README.md new file mode 100644 index 000000000..e8edbdc97 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.20/README.md @@ -0,0 +1,29 @@ +# v0.5.20 release acceptance + +[中文](README.zh-CN.md) + +Verified on 2026-09-29 (Asia/Shanghai). The package under test is `dsh-mnemon@0.5.20` as `pnpm release:version` builds it; the other 17 component packages come from npm at the Starter's pinned versions, which 0.5.20 does not change. It was built from `94d1e84f` (#313 and #314). The release pull request builds the same package from `4ea87e5b`, because #315 changed documentation only: both artifacts have SHA-256 `c446c553…`. + +## Real official WebUI on both supported hosts + +On unmodified npm DSH `0.2.0-rc.1` and `0.1.7-rc.2`, each with Node `24.19.0` and a fresh home, DSH home and pnpm store: + +1. Started the host without Mnemon installed and opened **Plugins → Add plugin**. +2. Installed `dsh-mnemon`. DSH chose the install source itself (its speed check picked the mainland China mirror). A loopback registry answered with npm's real metadata, read as npm will a day after the release, plus 0.5.20. Installation took 21.8 s on 0.2.0-rc.1 and 13.2 s on 0.1.7-rc.2, starting from an empty pnpm store. +3. Clicked **Enable now**; the Memory System appeared within about a second, without a host restart. [Status](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.20/status.png) reports **dsh-mnemon 0.5.20 / System nominal**, and Mnemon Native names **Mnemon 0.2.7**. +4. Added a Runtime memory entry and read it back after reloading the page: [Runtime](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.20/runtime.png). +5. Created and activated a Mnemon Native space in the WebUI: [Memory Spaces](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.20/spaces.png). Wrote a fact with Mnemon CLI `0.2.7`, recalled it with the CLI, then found the same fact with the WebUI's **Direct recall**: [Recall](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.20/recall.png). + +Both hosts passed every step without a host warning or console error, and each host process was started once. [validation.json](validation.json) has the package digest and each host's results. Profiles and memories are synthetic. + +![Status on DSH 0.2.0-rc.1](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.20/status.png) + +![The CLI-written Native memory found by Direct recall](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.20/recall.png) + +## Upgrades and other ways in + +The [cold start and upgrade record](../starter-group-upgrade/README.md) checks the same package through the command line, Headless and the desktop layout. It also checks updates from 0.5.16 through 0.5.19, including a profile stuck with the Starter row turned off, and pnpm's release-day fallback. The [component group record](../starter-group-readiness/README.md) shows the Plugins page before and after the change. + +## Validation and release boundary + +`pnpm run release:check` selects only the Starter for publication; no component package changes. No plugin uses a new SDK export, so no peer floor moves, and the lockfile is unchanged. The release pull request and the publication workflow run the complete workspace and packed-plugin verification. Publication then freezes the merged main revision, publishes the Starter and reads it back from npm. It installs the complete 18-package combination and checks a real Registry upgrade before creating the GitHub release. These screenshots show the versioned local package; they do not by themselves certify npm publication. diff --git a/dsh/docs/pr-assets/release-v0.5.20/README.zh-CN.md b/dsh/docs/pr-assets/release-v0.5.20/README.zh-CN.md new file mode 100644 index 000000000..2f1c6807c --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.20/README.zh-CN.md @@ -0,0 +1,29 @@ +# v0.5.20 发布验收 + +[English](README.md) + +2026-09-29(Asia/Shanghai)验证。被测包是 `pnpm release:version` 构建出的 `dsh-mnemon@0.5.20`;其余 17 个组件包按 Starter 固定的版本来自 npm,0.5.20 没有改变它们。被测包从 `94d1e84f`(#313 与 #314)构建。release PR 从 `4ea87e5b` 构建出同一个包,因为 #315 只改了文档:两个制品的 SHA-256 都是 `c446c553…`。 + +## 两个受支持宿主上的真实官方 WebUI + +在未修改的 npm DSH `0.2.0-rc.1` 与 `0.1.7-rc.2` 上,各自使用 Node `24.19.0` 以及全新的 home、DSH home 与 pnpm store: + +1. 在未安装 Mnemon 的情况下启动宿主,打开**插件 → 添加插件**。 +2. 安装 `dsh-mnemon`。安装源由 DSH 自己选择(测速选中了中国大陆镜像源)。本地 registry 按发布一天后 npm 的样子返回真实元数据,并加入 0.5.20。从空的 pnpm store 开始,0.2.0-rc.1 安装用时 21.8 秒,0.1.7-rc.2 用时 13.2 秒。 +3. 点击**立即启用**,约一秒内出现记忆系统,宿主无需重启。[状态页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.20/status.png)显示 **dsh-mnemon 0.5.20 / 系统正常**,Mnemon Native 写出 **Mnemon 0.2.7**。 +4. 在运行时记忆中添加一条记录,刷新页面后读回:[运行时记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.20/runtime.png)。 +5. 在 WebUI 中创建并激活 Mnemon Native 记忆空间:[记忆空间](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.20/spaces.png)。用 Mnemon CLI `0.2.7` 写入一条事实并用 CLI 召回,再用 WebUI 的**直接检索**找到同一条事实:[检索](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.20/recall.png)。 + +两个宿主的每一步都通过,没有宿主警告或控制台错误,每个宿主进程只启动一次。[validation.json](validation.json) 记录了包摘要与各宿主的结果。profile 与记忆均为合成数据。 + +![DSH 0.2.0-rc.1 上的状态页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.20/status.png) + +![直接检索找到 CLI 写入的 Native 记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.20/recall.png) + +## 升级与其他接入方式 + +[冷启动与升级验收](../starter-group-upgrade/README.zh-CN.md)用同一个包检查了命令行、Headless 与桌面版布局。它还检查了从 0.5.16 到 0.5.19 的升级(包括 Starter 行被关闭而卡住的 profile),以及 pnpm 发布当天的回退。[组件组验收记录](../starter-group-readiness/README.zh-CN.md)展示了改动前后的插件页。 + +## 校验与发布边界 + +`pnpm run release:check` 只选中 Starter 发布,没有组件包变化。没有插件使用新的 SDK 导出,因此 peer 下限不变,lockfile 也不变。release PR 与发布工作流会执行完整的工作区与打包插件校验。随后,发布流程冻结合并后的 main 修订,发布 Starter 并从 npm 读回,再安装完整的 18 个包组合并检查一次真实的 Registry 升级,最后才创建 GitHub Release。这些截图展示的是版本化的本地包,本身不代表 npm 发布已经完成。 diff --git a/dsh/docs/pr-assets/release-v0.5.20/validation.json b/dsh/docs/pr-assets/release-v0.5.20/validation.json new file mode 100644 index 000000000..48f328f17 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.20/validation.json @@ -0,0 +1,49 @@ +{ + "verifiedAt": "2026-09-28T20:31:00Z", + "revision": { + "tested": "94d1e84fa7d82b1528e3c0ebce749a59f41f7392 with pnpm release:version", + "release": "4ea87e5b57571e71fb3cb90515bed24aa0e7b9e4 with pnpm release:version (the release pull request); #315 changed documentation only", + "packageIdentical": true + }, + "package": { + "name": "dsh-mnemon", + "version": "0.5.20", + "sha256": "c446c553c00043d34c3f178e418499689121ec109090f20ac39ecd00276fb342", + "components": "the 17 pinned component packages from npm, unchanged since 0.5.19" + }, + "registry": "a loopback registry with npm's real metadata, every dsh-mnemon publish time moved two days back, and 0.5.20 published 26 hours earlier; a pnpm wrapper sends the npm and npmmirror registries DSH asks to it", + "hosts": { + "0.2.0-rc.1": { + "home": "fresh HOME, DSH home and pnpm store", + "flow": "Plugins -> Add plugin -> dsh-mnemon -> Install -> Enable now", + "installSource": "chosen by DSH (mainland China mirror)", + "installSeconds": 21.8, + "enableSeconds": 0.8, + "status": { "starter": "0.5.20", "nominal": true, "nativeCli": "0.2.7" }, + "runtimeWriteAndReadAfterReload": true, + "nativeSpaceActive": "1 / 1", + "cliRememberAndRecall": true, + "webDirectRecallFindsCliFact": true, + "hostLaunches": 1, + "hostWarnings": [], + "consoleErrors": [] + }, + "0.1.7-rc.2": { + "home": "fresh HOME, DSH home and pnpm store", + "flow": "Plugins -> Add plugin -> dsh-mnemon -> Install -> Enable now", + "installSource": "chosen by DSH (mainland China mirror)", + "installSeconds": 13.2, + "enableSeconds": 1.1, + "status": { "starter": "0.5.20", "nominal": true, "nativeCli": "0.2.7" }, + "runtimeWriteAndReadAfterReload": true, + "nativeSpaceActive": "1 / 1", + "cliRememberAndRecall": true, + "webDirectRecallFindsCliFact": true, + "hostLaunches": 1, + "hostWarnings": [], + "consoleErrors": [] + } + }, + "moreEvidence": ["../starter-group-readiness/validation.json", "../starter-group-upgrade/validation.json"], + "environment": { "os": "macOS", "node": "24.19.0", "pnpm": "11.19.0", "mnemonCli": "0.2.7", "browser": "headless Google Chrome over the DevTools protocol", "dshModified": false } +} diff --git a/dsh/docs/pr-assets/release-v0.5.21/README.md b/dsh/docs/pr-assets/release-v0.5.21/README.md new file mode 100644 index 000000000..ce06a7357 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.21/README.md @@ -0,0 +1,40 @@ +# v0.5.21 release acceptance + +[中文](README.zh-CN.md) + +Verified on 2026-10-01 (Asia/Shanghai). The packages under test are `dsh-mnemon@0.5.21`, `dsh-mnemon-source-memory-spaces@0.5.14` and `dsh-mnemon-provider-mnemon-native@0.5.8`, as `pnpm release:version` builds them from `main` `73beea9a`, which includes #321, #322 and #323. The other 15 component packages come from npm at the Starter's pinned versions, which 0.5.21 does not change. + +## Real official WebUI on both supported hosts + +On unmodified npm DSH `0.2.0-rc.2` (npm `latest` and `next`) and `0.1.7-rc.2`, each with Node `24.19.0` and a fresh home, DSH home and pnpm store: + +1. Started the host without Mnemon installed and opened **Plugins → Add plugin**. +2. Installed `dsh-mnemon`. DSH chose the install source itself; its speed check picked the mainland China mirror. A loopback registry answered with npm's real metadata plus the three new versions. The profile then held dsh-mnemon 0.5.21, the Memory Spaces Source 0.5.14 and Mnemon Native 0.5.8, with the unchanged components at their pinned npm versions. +3. Clicked **Enable now**; the Memory System appeared without a host restart. [Status](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.21/status.png) reports **dsh-mnemon 0.5.21 / System nominal**, and Mnemon Native names **Mnemon 0.2.9**. +4. Added a Runtime memory entry and read it back after reloading the page: [Runtime](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.21/runtime.png). +5. Created and activated a Mnemon Native space in the WebUI: [Memory Spaces](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.21/spaces.png). Wrote a fact with Mnemon CLI `0.2.9`, recalled it with the CLI, then found the same fact with the WebUI's **Direct recall**: [Recall](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.21/recall.png). + +Both hosts passed every step without a host warning or console error, and each host process was started once. [validation.json](validation.json) has the package digests and each host's results. Profiles and memories are synthetic. + +![Status on DSH 0.2.0-rc.2](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.21/status.png) + +![The CLI-written Native memory found by Direct recall](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.21/recall.png) + +## The fixes in this release + +Each fix has its own before-and-after record on the WebUI: +- [Large Native Memory Spaces](../issue-320-native-store-dumps/README.md): a 900-insight space lists, draws and archives. +- [Idle review writes one layer](../issue-319-review-layers/README.md): a review pass no longer repeats a Document in working memory, and **Write runtime memory** can be switched off. +- [The fallback sidebar entry](../issue-318-sidebar-entry/README.md): it matches DSH's own panel rows. + +## Validation and release boundary + +`pnpm run release:check` confirms the Starter 0.5.21 on the `latest` tag, with a composition that pins the Memory Spaces Source 0.5.14 and Mnemon Native 0.5.8; publication computes the changed packages from the previous release. No plugin uses a new SDK export from the Starter, so no peer floor moves. The version bump changes only specifier lines in the lockfile, which pnpm 10.13.1, the CI version, accepts with `--frozen-lockfile`. + +The release pull request and the publication workflow run the complete workspace and packed-plugin verification. Publication then: +- freezes the merged main revision; +- publishes the changed packages and reads them back from npm; +- installs the complete 18-package combination and checks a real Registry upgrade; +- creates the GitHub release. + +These screenshots show the versioned local packages; they do not by themselves certify npm publication. diff --git a/dsh/docs/pr-assets/release-v0.5.21/README.zh-CN.md b/dsh/docs/pr-assets/release-v0.5.21/README.zh-CN.md new file mode 100644 index 000000000..91a50ba2b --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.21/README.zh-CN.md @@ -0,0 +1,40 @@ +# v0.5.21 发布验收 + +[English](README.md) + +验证日期为 2026-10-01(北京时间)。受测包为 `dsh-mnemon@0.5.21`、`dsh-mnemon-source-memory-spaces@0.5.14` 与 `dsh-mnemon-provider-mnemon-native@0.5.8`,由 `pnpm release:version` 基于包含 #321、#322 与 #323 的 `main` `73beea9a` 构建。其余 15 个组件包按 Starter 锁定的版本来自 npm,0.5.21 未改动它们。 + +## 两个受支持宿主上的真实官方 WebUI + +在未经修改的 npm DSH `0.2.0-rc.2`(npm `latest` 与 `next`)和 `0.1.7-rc.2` 上,分别使用 Node `24.19.0` 以及全新的 home、DSH home 与 pnpm store: + +1. 在未安装 Mnemon 的情况下启动宿主,打开**插件 → 添加插件**。 +2. 安装 `dsh-mnemon`。安装源由 DSH 自行选择,其测速选中了中国大陆镜像。本机 registry 返回 npm 的真实元数据,并附上三个新版本。安装后 profile 中为 dsh-mnemon 0.5.21、记忆空间 Source 0.5.14 与 Mnemon Native 0.5.8,未变更的组件为 npm 上锁定的版本。 +3. 点击**立即启用**,记忆系统无需重启宿主即出现。[状态页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.21/status.png)显示 **dsh-mnemon 0.5.21 / 系统正常**,Mnemon Native 显示 **Mnemon 0.2.9**。 +4. 添加一条运行时记忆,刷新页面后读回:[运行时记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.21/runtime.png)。 +5. 在 WebUI 中创建并激活一个 Mnemon Native 空间:[记忆空间](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.21/spaces.png)。用 Mnemon CLI `0.2.9` 写入并召回一条事实,再用 WebUI 的**直接检索**找到同一条事实:[检索](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.21/recall.png)。 + +两个宿主的每一步均通过,没有宿主警告或控制台错误,每个宿主进程只启动一次。[validation.json](validation.json) 记录了包摘要与各宿主的结果。profile 与记忆均为合成数据。 + +![DSH 0.2.0-rc.2 上的状态页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.21/status.png) + +![直接检索找到 CLI 写入的 Native 记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.21/recall.png) + +## 本版本的修复 + +每项修复另有 WebUI 上的修复前后记录: +- [大型 Native 记忆空间](../issue-320-native-store-dumps/README.zh-CN.md):900 条记忆的空间可以列出内容、绘制图谱并完成归档。 +- [空闲审查只写一层](../issue-319-review-layers/README.zh-CN.md):审查不再把项目档案重复写入工作记忆,且可以关闭**写入运行时记忆**。 +- [兜底侧栏入口](../issue-318-sidebar-entry/README.zh-CN.md):与 DSH 自身的面板行一致。 + +## 验证与发布边界 + +`pnpm run release:check` 确认 Starter 0.5.21 使用 `latest` 标签,其组合锁定记忆空间 Source 0.5.14 与 Mnemon Native 0.5.8;发布流程会根据上一版本计算需要发布的包。没有插件使用 Starter 新增的 SDK 导出,因此 peer 下限不变。版本变更只改动 lockfile 中的 specifier 行,CI 使用的 pnpm 10.13.1 以 `--frozen-lockfile` 接受它。 + +发布 PR 与发布工作流会运行完整的工作区与打包插件验证。之后发布流程会: +- 冻结合并后的 main 修订; +- 发布变更的包并从 npm 读回; +- 安装完整的 18 包组合并检查真实 Registry 升级; +- 创建 GitHub Release。 + +这些截图展示的是版本化的本地包,本身不证明 npm 发布结果。 diff --git a/dsh/docs/pr-assets/release-v0.5.21/validation.json b/dsh/docs/pr-assets/release-v0.5.21/validation.json new file mode 100644 index 000000000..ed55d1448 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.21/validation.json @@ -0,0 +1,90 @@ +{ + "verifiedAt": "2026-09-30T21:40:00Z", + "revision": { + "base": "73beea9abf5d18b959a5dc748138a78d7737846d", + "tested": "73beea9a (main with #321, #322 and #323) with pnpm release:version, the release pull request working tree" + }, + "packages": { + "dsh-mnemon": { + "version": "0.5.21", + "sha256": "a5992e7c55ede9f77be2a45e58d804c97310c225e716ce457488e371a09ebb7b" + }, + "dsh-mnemon-source-memory-spaces": { + "version": "0.5.14", + "sha256": "90dce5b634421a0eae321d6c46cabb09e6e8b9bca55bd463ab6cad0cbdd5383f" + }, + "dsh-mnemon-provider-mnemon-native": { + "version": "0.5.8", + "sha256": "616fb6afed893d354365991adc82ae8f980def9579d1ac0e6d65f3e5e6563823" + }, + "components": "the other 15 pinned component packages from npm, unchanged since 0.5.20" + }, + "registry": "a loopback registry with npm's real metadata plus the three new versions, published 168 hours earlier; a pnpm wrapper sends the npm and npmmirror registries DSH asks to it", + "hosts": { + "0.2.0-rc.2": { + "home": "fresh HOME, DSH home and pnpm store", + "flow": "Plugins -> Add plugin -> dsh-mnemon -> Install -> Enable now", + "installSource": "chosen by DSH (mainland China mirror)", + "installedVersions": { + "dsh-mnemon": "0.5.21", + "dsh-mnemon-source-memory-spaces": "0.5.14", + "dsh-mnemon-provider-mnemon-native": "0.5.8", + "dsh-mnemon-source-runtime": "0.5.11" + }, + "status": { + "starter": "0.5.21", + "nominal": true, + "nativeCli": "0.2.9" + }, + "runtimeWriteAndReadAfterReload": true, + "nativeSpaceActive": "1 / 1", + "cliRememberAndRecall": true, + "webDirectRecallFindsCliFact": true, + "hostLaunches": 1, + "hostWarnings": [], + "consoleErrors": [] + }, + "0.1.7-rc.2": { + "home": "fresh HOME, DSH home and pnpm store", + "flow": "Plugins -> Add plugin -> dsh-mnemon -> Install -> Enable now", + "installSource": "chosen by DSH (mainland China mirror)", + "installedVersions": { + "dsh-mnemon": "0.5.21", + "dsh-mnemon-source-memory-spaces": "0.5.14", + "dsh-mnemon-provider-mnemon-native": "0.5.8", + "dsh-mnemon-source-runtime": "0.5.11" + }, + "status": { + "starter": "0.5.21", + "nominal": true, + "nativeCli": "0.2.9" + }, + "runtimeWriteAndReadAfterReload": true, + "nativeSpaceActive": "1 / 1", + "cliRememberAndRecall": true, + "webDirectRecallFindsCliFact": true, + "hostLaunches": 1, + "hostWarnings": [], + "consoleErrors": [] + } + }, + "moreEvidence": [ + "../issue-320-native-store-dumps/verification.json", + "../issue-319-review-layers/verification.json", + "../issue-318-sidebar-entry/measurements.json" + ], + "environment": { + "os": "macOS", + "node": "24.19.0", + "pnpm": "11.19.0", + "mnemonCli": "0.2.9", + "browser": "headless Google Chrome over the DevTools protocol", + "dshModified": false + }, + "screenshots": { + "status.png": "46b76f240d5f803549fe728d98a59e3f047724afe5daa156562225aa837eff15", + "runtime.png": "92aef6a14827587d85a5eba99497fd8b1b6e9138b500568af8a50812ba5bf9eb", + "spaces.png": "d0e077e5455d4043c5bbf1910b0aaafe87642393a2fb2b9723974a24c5c12436", + "recall.png": "0de5832071e760ed3c8e68538c1b47553c964cabdf6dbdf4628f27bd38706449" + } +} diff --git a/dsh/docs/pr-assets/release-v0.5.22/README.md b/dsh/docs/pr-assets/release-v0.5.22/README.md new file mode 100644 index 000000000..bcc3222be --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.22/README.md @@ -0,0 +1,41 @@ +# v0.5.22 release acceptance + +[中文](README.zh-CN.md) + +Verified on 2026-10-03 (Asia/Shanghai). The package under test is `dsh-mnemon@0.5.22`, as `pnpm release:version` builds it from `main` `a69d1a51`, which includes #325, #326 and #328. The 17 component packages come from npm at the Starter's pinned versions, which 0.5.22 does not change. + +## Real official WebUI on both supported hosts + +On unmodified npm DSH `0.2.0-rc.2` (npm `latest` and `next`) and `0.1.7-rc.2`, each with Node `24.19.0` and a fresh home, DSH home and pnpm store: + +1. Started the host without Mnemon installed and opened **Plugins → Add plugin**. +2. Installed `dsh-mnemon`. DSH chose the install source itself; its speed check picked the mainland China mirror. A loopback registry answered with npm's real metadata plus the new version. The profile then held dsh-mnemon 0.5.22 with every component at its pinned npm version. +3. Clicked **Enable now**; the Memory System appeared without a host restart. [Status](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.22/status.png) reports **dsh-mnemon 0.5.22 / System nominal**, and Mnemon Native names **Mnemon 0.2.9**. +4. Added a Runtime memory entry and read it back after reloading the page: [Runtime](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.22/runtime.png). +5. Created and activated a Mnemon Native space in the WebUI: [Memory Spaces](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.22/spaces.png). Wrote a fact with Mnemon CLI `0.2.9`, recalled it with the CLI, then found the same fact with the WebUI's **Direct recall**: [Recall](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.22/recall.png). +6. Opened **Check versions**. dsh-mnemon lists 0.5.22 as installed and names DSH's own plugin installer as its update route. Before publication npm's latest is still 0.5.21, so the dialog marks 0.5.22 as a local version and offers no update. No restart notice appeared after **Enable now**, since the version that runs is the one installed. + +Both hosts passed every step without a host warning or console error, and each host process was started once. [validation.json](validation.json) has the package digest and each host's results. Profiles and memories are synthetic. + +![Status on DSH 0.2.0-rc.2](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.22/status.png) + +![The CLI-written Native memory found by Direct recall](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.22/recall.png) + +## The fixes in this release + +Each fix has its own before-and-after record: +- [Updates through DSH's installer](../version-update-through-dsh/README.md): Check versions updates dsh-mnemon in the desktop app's Profile and reopens with the result after DSH swaps in the new page, and the restart notice names the installed and running versions. +- [A conversation tab without a loaded Agent](../conversation-tab-workspace/README.md): Status, Project Documents, new Documents and Runtime entries work in Conversation tab and Sidebar mode on both hosts. +- [Subagents and chat templates that require a user query](../issue-327-subagent-user-turn/README.md): idle review behind an Ollama 0.33 emulation recovers from the refusal, and healthy routes send the same requests as before. + +## Validation and release boundary + +`pnpm run release:check` confirms the Starter 0.5.22 on the `latest` tag with an unchanged composition; publication computes the changed packages from the previous release. No plugin uses a new SDK export from the Starter, so no peer floor moves. The version bump leaves the lockfile unchanged, and pnpm 10.13.1, the CI version, accepts it with `--frozen-lockfile`. + +The release pull request and the publication workflow run the complete workspace and packed-plugin verification. Publication then: +- freezes the merged main revision; +- publishes the changed package and reads it back from npm; +- installs the complete 18-package combination and checks a real Registry upgrade; +- creates the GitHub release. + +These screenshots show the versioned local package; they do not by themselves certify npm publication. diff --git a/dsh/docs/pr-assets/release-v0.5.22/README.zh-CN.md b/dsh/docs/pr-assets/release-v0.5.22/README.zh-CN.md new file mode 100644 index 000000000..5c466933a --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.22/README.zh-CN.md @@ -0,0 +1,41 @@ +# v0.5.22 发布验收 + +[English](README.md) + +验证日期为 2026-10-03(北京时间)。受测包为 `dsh-mnemon@0.5.22`,由 `pnpm release:version` 基于包含 #325、#326 与 #328 的 `main` `a69d1a51` 构建。17 个组件包按 Starter 锁定的版本来自 npm,0.5.22 未改动它们。 + +## 两个受支持宿主上的真实官方 WebUI + +在未经修改的 npm DSH `0.2.0-rc.2`(npm `latest` 与 `next`)和 `0.1.7-rc.2` 上,分别使用 Node `24.19.0` 以及全新的 home、DSH home 与 pnpm store: + +1. 在未安装 Mnemon 的情况下启动宿主,打开**插件 → 添加插件**。 +2. 安装 `dsh-mnemon`。安装源由 DSH 自行选择,其测速选中了中国大陆镜像。本机 registry 返回 npm 的真实元数据,并附上新版本。安装后 profile 中为 dsh-mnemon 0.5.22,各组件为 npm 上锁定的版本。 +3. 点击**立即启用**,记忆系统无需重启宿主即出现。[状态页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.22/status.png)显示 **dsh-mnemon 0.5.22 / 系统正常**,Mnemon Native 显示 **Mnemon 0.2.9**。 +4. 添加一条运行时记忆,刷新页面后读回:[运行时记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.22/runtime.png)。 +5. 在 WebUI 中创建并激活一个 Mnemon Native 空间:[记忆空间](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.22/spaces.png)。用 Mnemon CLI `0.2.9` 写入并召回一条事实,再用 WebUI 的**直接检索**找到同一条事实:[检索](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.22/recall.png)。 +6. 打开**检查版本**:dsh-mnemon 显示已安装 0.5.22,更新方式为 DSH 自己的插件安装器。发布前 npm 的最新版本仍是 0.5.21,因此面板将 0.5.22 标为本地版本,不提供更新。**立即启用**之后没有出现重启提示,因为正在运行的就是已安装的版本。 + +两个宿主的每一步均通过,没有宿主警告或控制台错误,每个宿主进程只启动一次。[validation.json](validation.json) 记录了包摘要与各宿主的结果。profile 与记忆均为合成数据。 + +![DSH 0.2.0-rc.2 上的状态页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.22/status.png) + +![直接检索找到 CLI 写入的 Native 记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.22/recall.png) + +## 本版本的修复 + +每项修复另有修复前后记录: +- [通过 DSH 安装器更新](../version-update-through-dsh/README.zh-CN.md):检查版本在桌面版的 Profile 中更新 dsh-mnemon,DSH 换上新页面后重新打开并显示结果,重启提示写明已安装与正在运行的版本。 +- [Agent 未加载的会话标签页](../conversation-tab-workspace/README.zh-CN.md):在两个宿主上,会话标签页与侧边栏模式下的状态页、项目档案、新建档案与运行时记忆均可使用。 +- [要求用户查询的聊天模板与子代理](../issue-327-subagent-user-turn/README.zh-CN.md):Ollama 0.33 模拟下的后台审查能从拒绝中恢复,正常路由发出的请求与之前相同。 + +## 验证与发布边界 + +`pnpm run release:check` 确认 Starter 0.5.22 使用 `latest` 标签,组合不变;发布流程会根据上一版本计算需要发布的包。没有插件使用 Starter 新增的 SDK 导出,因此 peer 下限不变。版本变更没有改动 lockfile,CI 使用的 pnpm 10.13.1 以 `--frozen-lockfile` 接受它。 + +发布 PR 与发布工作流会运行完整的工作区与打包插件验证。之后发布流程会: +- 冻结合并后的 main 修订; +- 发布变更的包并从 npm 读回; +- 安装完整的 18 包组合并检查真实 Registry 升级; +- 创建 GitHub Release。 + +这些截图展示的是版本化的本地包,本身不证明 npm 发布结果。 diff --git a/dsh/docs/pr-assets/release-v0.5.22/validation.json b/dsh/docs/pr-assets/release-v0.5.22/validation.json new file mode 100644 index 000000000..8ecb710b2 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.22/validation.json @@ -0,0 +1,100 @@ +{ + "verifiedAt": "2026-10-02T20:40:35Z", + "revision": { + "base": "a69d1a51a5e588615cff2f6415faa865bf217623", + "tested": "a69d1a51 (main with #325, #326 and #328) with pnpm release:version, the release pull request working tree" + }, + "packages": { + "dsh-mnemon": { + "version": "0.5.22", + "sha256": "31fefff143bfade64af79a5925ef92c68a891dfcab568945f126c3a6f84e2311" + }, + "components": "the 17 pinned component packages from npm, unchanged since 0.5.21" + }, + "registry": "a loopback registry with npm's real metadata plus the new version, published 168 hours earlier; a pnpm wrapper sends the npm and npmmirror registries DSH asks to it", + "hosts": { + "0.2.0-rc.2": { + "home": "fresh HOME, DSH home and pnpm store", + "flow": "Plugins -> Add plugin -> dsh-mnemon -> Install -> Enable now", + "installSource": "chosen by DSH (mainland China mirror)", + "installedVersions": { + "dsh-mnemon": "0.5.22", + "dsh-mnemon-source-runtime": "0.5.11", + "dsh-mnemon-source-documents": "0.5.8", + "dsh-mnemon-source-memory-spaces": "0.5.14", + "dsh-mnemon-provider-mnemon-native": "0.5.8" + }, + "status": { + "starter": "0.5.22", + "nominal": true, + "nativeCli": "0.2.9" + }, + "runtimeWriteAndReadAfterReload": true, + "nativeSpaceActive": "1 / 1", + "cliRememberAndRecall": true, + "webDirectRecallFindsCliFact": true, + "checkVersions": { + "installed": "0.5.22", + "npmLatestBeforePublication": "0.5.21", + "state": "local version", + "updateRoute": "DSH plugin installer", + "updateOffered": false + }, + "restartNoticeAfterEnableNow": false, + "hostLaunches": 1, + "hostWarnings": [], + "consoleErrors": [] + }, + "0.1.7-rc.2": { + "home": "fresh HOME, DSH home and pnpm store", + "flow": "Plugins -> Add plugin -> dsh-mnemon -> Install -> Enable now", + "installSource": "chosen by DSH (mainland China mirror)", + "installedVersions": { + "dsh-mnemon": "0.5.22", + "dsh-mnemon-source-runtime": "0.5.11", + "dsh-mnemon-source-documents": "0.5.8", + "dsh-mnemon-source-memory-spaces": "0.5.14", + "dsh-mnemon-provider-mnemon-native": "0.5.8" + }, + "status": { + "starter": "0.5.22", + "nominal": true, + "nativeCli": "0.2.9" + }, + "runtimeWriteAndReadAfterReload": true, + "nativeSpaceActive": "1 / 1", + "cliRememberAndRecall": true, + "webDirectRecallFindsCliFact": true, + "checkVersions": { + "installed": "0.5.22", + "npmLatestBeforePublication": "0.5.21", + "state": "local version", + "updateRoute": "DSH plugin installer", + "updateOffered": false + }, + "restartNoticeAfterEnableNow": false, + "hostLaunches": 1, + "hostWarnings": [], + "consoleErrors": [] + } + }, + "moreEvidence": [ + "../version-update-through-dsh/verification.json", + "../conversation-tab-workspace/verification.json", + "../issue-327-subagent-user-turn/verification.json" + ], + "environment": { + "os": "macOS", + "node": "24.19.0", + "pnpm": "11.19.0", + "mnemonCli": "0.2.9", + "browser": "headless Google Chrome over the DevTools protocol", + "dshModified": false + }, + "screenshots": { + "status.png": "b616386bcfa5dd3f3069cce16b9bdab922d624f3fa4068dc896ebbd06e459208", + "runtime.png": "9acc6108dcce23e61b13a2f66229f5193fdb02eb4bda66027d74996eb93f109d", + "spaces.png": "d0e077e5455d4043c5bbf1910b0aaafe87642393a2fb2b9723974a24c5c12436", + "recall.png": "82f6736820b8a0efe60a717af9fe66285ad278050283e66a8646c7ad4e5838b8" + } +} diff --git a/dsh/docs/pr-assets/release-v0.5.24/README.md b/dsh/docs/pr-assets/release-v0.5.24/README.md new file mode 100644 index 000000000..ee962cf5e --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.24/README.md @@ -0,0 +1,39 @@ +# v0.5.24 release acceptance + +[中文](README.zh-CN.md) + +Verified on 2026-10-04 (Asia/Shanghai). The packages under test are `dsh-mnemon@0.5.24` and `dsh-mnemon-source-memory-spaces@0.5.16`, as `pnpm release:version` builds them from `main` `b2481b04`, which includes #332. The other 16 component packages come from npm at the Starter's pinned versions, which 0.5.24 does not change. + +## Real official WebUI on both supported hosts + +On unmodified npm DSH `0.2.0-rc.2` (npm `latest` and `next`) and `0.1.7-rc.2`, each with Node `24.19.0` and a fresh home, DSH home and pnpm store: + +1. Started the host without Mnemon installed and opened **Plugins → Add plugin**. +2. Installed `dsh-mnemon`. DSH chose the install source itself; its speed check picked the mainland China mirror. A loopback registry answered with npm's real metadata plus the two new versions. The profile then held dsh-mnemon 0.5.24 and Memory Spaces 0.5.16, with every other component at its pinned npm version. +3. Clicked **Enable now**; the Memory System appeared without a host restart. [Status](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.24/status.png) reports **dsh-mnemon 0.5.24 / System nominal**, and Mnemon Native names **Mnemon 0.2.9**. +4. Added a Runtime memory entry and read it back after reloading the page: [Runtime](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.24/runtime.png). +5. Created and activated a Mnemon Native space in the WebUI: [Memory Spaces](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.24/spaces.png). Wrote a fact with Mnemon CLI `0.2.9`, recalled it with the CLI, then found the same fact with the WebUI's **Direct recall**: [Recall](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.24/recall.png). +6. Wrote three more memories with the CLI that carry the entity `Atlas`, and one that only mentions Atlas. On **Entities**, `Atlas` counts 3, and selecting it lists exactly those three ("Showing 3 / 3"). Related memories stay collapsed behind **Find related memories**, and no recall runs: [Entities](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.24/entities.png). Pressing it found the memory that only mentions Atlas, and the button became **Hide**: [Related memories](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.24/entities-related.png). +7. Opened **Check versions**. dsh-mnemon lists 0.5.24 as installed and names DSH's own plugin installer as its update route. Before publication npm's latest is still 0.5.23, so the dialog marks 0.5.24 as a local version and offers no update for it. The Mnemon CLI row offers its own npm update, since npm now has Mnemon 0.2.10. No restart notice appeared after **Enable now**, since the version that runs is the one installed. + +Both hosts passed every step without a host warning or console error, and each host process was started once. [validation.json](validation.json) has the package digests and each host's results. Profiles and memories are synthetic. + +![Entities on DSH 0.2.0-rc.2: Atlas counted 3 and listed 3, related memories collapsed](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.24/entities.png) + +![Related memories found on demand](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.24/entities-related.png) + +## The fix in this release + +[Entities page counts and lists from every memory](../entities-complete-index/README.md) has the before-and-after runs on synthetic spaces on both hosts, the timings on 4 × 500 memories, and the counts from a copy of the export behind the report. + +## Validation and release boundary + +`pnpm run release:check` confirms the Starter 0.5.24 on the `latest` tag with Memory Spaces 0.5.16 pinned; publication computes the changed packages from the previous release. No plugin uses a new SDK export from the Starter, so no peer floor moves. The version bump changes two specifier lines in the lockfile, and pnpm 10.13.1, the CI version, accepts it with `--frozen-lockfile`. + +The release pull request and the publication workflow run the complete workspace and packed-plugin verification. Publication then: +- freezes the merged main revision; +- publishes the changed packages and reads them back from npm; +- installs the complete 18-package combination and checks a real Registry upgrade; +- creates the GitHub release. + +These screenshots show the versioned local packages; they do not by themselves certify npm publication. diff --git a/dsh/docs/pr-assets/release-v0.5.24/README.zh-CN.md b/dsh/docs/pr-assets/release-v0.5.24/README.zh-CN.md new file mode 100644 index 000000000..83e53fe97 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.24/README.zh-CN.md @@ -0,0 +1,39 @@ +# v0.5.24 发布验收 + +[English](README.md) + +验证日期为 2026-10-04(北京时间)。被测包为 `dsh-mnemon@0.5.24` 与 `dsh-mnemon-source-memory-spaces@0.5.16`,由 `pnpm release:version` 从包含 #332 的 `main` `b2481b04` 构建。其余 16 个组件包按 Starter 锁定的版本取自 npm,0.5.24 没有改动它们。 + +## 两个支持的宿主上的真实官方 WebUI + +在未修改的 npm DSH `0.2.0-rc.2`(npm `latest` 与 `next`)与 `0.1.7-rc.2` 上,各自使用 Node `24.19.0`,以及全新的 home、DSH home 与 pnpm store: + +1. 在未安装 Mnemon 的情况下启动宿主,打开**插件 → 添加插件**。 +2. 安装 `dsh-mnemon`。安装源由 DSH 自行选择,其测速选中了中国大陆镜像源。本地回环 registry 返回 npm 的真实元数据,并加上两个新版本。安装后 profile 中为 dsh-mnemon 0.5.24 与 Memory Spaces 0.5.16,其余组件均为锁定的 npm 版本。 +3. 点击**立即启用**,无需重启宿主即出现记忆系统。[状态页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.24/status.png)显示 **dsh-mnemon 0.5.24 / 系统正常**,Mnemon Native 显示 **Mnemon 0.2.9**。 +4. 添加一条运行时记忆,刷新页面后读回:[运行时记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.24/runtime.png)。 +5. 在 WebUI 中创建并激活一个 Mnemon Native 空间:[记忆空间](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.24/spaces.png)。用 Mnemon CLI `0.2.9` 写入一条事实并用 CLI 召回,再用 WebUI 的**直接检索**找到同一条事实:[检索](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.24/recall.png)。 +6. 再用 CLI 写入三条带有实体 `Atlas` 的记忆,以及一条只在正文中提到 Atlas 的记忆。在**实体**页,`Atlas` 计数为 3,选中后正好列出这三条(“当前显示 3 / 3”)。相关记忆折叠在**查找相关记忆**之后,没有运行召回:[实体](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.24/entities.png)。点击后找到了只提到 Atlas 的那条记忆,按钮变为**收起**:[相关记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.24/entities-related.png)。 +7. 打开**检查版本**。dsh-mnemon 显示已安装 0.5.24,更新方式为 DSH 自己的插件安装器。发布前 npm 的 latest 仍是 0.5.23,因此对话框把 0.5.24 标为本地版本,不为它提供更新。Mnemon CLI 一行提供自己的 npm 更新,因为 npm 上已有 Mnemon 0.2.10。点击**立即启用**后没有出现重启提示,因为正在运行的就是已安装的版本。 + +两个宿主都通过了每一步,没有宿主警告或控制台错误,每个宿主进程只启动一次。包摘要与各宿主的结果见 [validation.json](validation.json)。Profile 与记忆均为合成数据。 + +![DSH 0.2.0-rc.2 上的实体页:Atlas 计数 3、列出 3 条,相关记忆折叠](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.24/entities.png) + +![按需查找到的相关记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.24/entities-related.png) + +## 本版本的修复 + +[实体页的计数与列表来自全部记忆](../entities-complete-index/README.zh-CN.md)记录了两个宿主上合成空间的修复前后、4 × 500 条记忆上的计时,以及报告所用导出数据副本上的计数。 + +## 验证与发布边界 + +`pnpm run release:check` 确认 `latest` 标签上的 Starter 0.5.24 锁定 Memory Spaces 0.5.16;发布流程按上一个版本计算变更的包。没有插件用到 Starter 新增的 SDK 导出,因此 peer 下限不变。版本号变更只改动 lockfile 中的两行版本说明,CI 使用的 pnpm 10.13.1 以 `--frozen-lockfile` 接受它。 + +版本 PR 与发布工作流都运行完整的工作区与打包插件验证。发布流程随后: +- 冻结合并后的 main revision; +- 发布变更的包,并从 npm 读回; +- 安装完整的 18 个包组合,并检查真实 Registry 升级; +- 创建 GitHub Release。 + +这些截图展示的是带版本号的本地包,本身并不证明 npm 发布。 diff --git a/dsh/docs/pr-assets/release-v0.5.24/validation.json b/dsh/docs/pr-assets/release-v0.5.24/validation.json new file mode 100644 index 000000000..69337c927 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.24/validation.json @@ -0,0 +1,145 @@ +{ + "verifiedAt": "2026-10-03T19:59:27Z", + "revision": { + "base": "b2481b0427bd1070de6538502451fb6150a2faf6", + "tested": "549503ae59ef79ba304eff684abeaa78ce1a9d84", + "note": "the release pull request working tree: main with #332, after pnpm release:version" + }, + "packages": { + "dsh-mnemon": { + "version": "0.5.24", + "sha256": "069a1a1d87ba75824def50a4e1f0aa286500a33dc8232ceb17c87c3643fb1ca5" + }, + "dsh-mnemon-source-memory-spaces": { + "version": "0.5.16", + "sha256": "40c479929969b0c90b361039b2709c01f3ffa723b254bf3aaacbf6dbaa28a8bc" + }, + "components": "the other 16 pinned component packages from npm, unchanged since 0.5.23" + }, + "registry": "a loopback registry with npm's real metadata plus the two new versions, published 168 hours earlier; a pnpm wrapper sends the npm and npmmirror registries DSH asks to it", + "hosts": { + "0.2.0-rc.2": { + "home": "fresh HOME, DSH home and pnpm store", + "flow": "Plugins -> Add plugin -> dsh-mnemon -> Install -> Enable now", + "installSource": "chosen by DSH (mainland China mirror)", + "installedVersions": { + "dsh-mnemon": "0.5.24", + "dsh-mnemon-source-runtime": "0.5.12", + "dsh-mnemon-source-documents": "0.5.9", + "dsh-mnemon-source-memory-spaces": "0.5.16", + "dsh-mnemon-provider-mnemon-native": "0.5.8" + }, + "status": { + "starter": "0.5.24", + "nominal": true, + "nativeCli": "0.2.9" + }, + "runtimeWriteAndReadAfterReload": true, + "nativeSpaceActive": "1 / 1", + "cliRememberAndRecall": true, + "webDirectRecallFindsCliFact": true, + "entities": { + "written": "3 CLI memories carrying Atlas, 1 that only mentions it", + "railCount": 3, + "listed": 3, + "listedCarryTheEntity": true, + "footer": [ + "当前显示 3 / 3" + ], + "relatedBeforeAsking": { + "button": "查找相关记忆", + "expanded": false, + "recallShown": null + }, + "findRelated": { + "related": 1, + "mentionFound": true, + "button": "收起" + } + }, + "checkVersions": { + "installed": "0.5.24", + "npmLatestBeforePublication": "0.5.23", + "state": "local version", + "updateRoute": "DSH plugin installer", + "dshMnemonUpdateOffered": false, + "otherRows": "Mnemon CLI 0.2.9 offers its own npm update to 0.2.10" + }, + "restartNoticeAfterEnableNow": false, + "hostLaunches": 1, + "hostWarnings": [], + "consoleErrors": [] + }, + "0.1.7-rc.2": { + "home": "fresh HOME, DSH home and pnpm store", + "flow": "Plugins -> Add plugin -> dsh-mnemon -> Install -> Enable now", + "installSource": "chosen by DSH (mainland China mirror)", + "installedVersions": { + "dsh-mnemon": "0.5.24", + "dsh-mnemon-source-runtime": "0.5.12", + "dsh-mnemon-source-documents": "0.5.9", + "dsh-mnemon-source-memory-spaces": "0.5.16", + "dsh-mnemon-provider-mnemon-native": "0.5.8" + }, + "status": { + "starter": "0.5.24", + "nominal": true, + "nativeCli": "0.2.9" + }, + "runtimeWriteAndReadAfterReload": true, + "nativeSpaceActive": "1 / 1", + "cliRememberAndRecall": true, + "webDirectRecallFindsCliFact": true, + "entities": { + "written": "3 CLI memories carrying Atlas, 1 that only mentions it", + "railCount": 3, + "listed": 3, + "listedCarryTheEntity": true, + "footer": [ + "当前显示 3 / 3" + ], + "relatedBeforeAsking": { + "button": "查找相关记忆", + "expanded": false, + "recallShown": null + }, + "findRelated": { + "related": 1, + "mentionFound": true, + "button": "收起" + } + }, + "checkVersions": { + "installed": "0.5.24", + "npmLatestBeforePublication": "0.5.23", + "state": "local version", + "updateRoute": "DSH plugin installer", + "dshMnemonUpdateOffered": false, + "otherRows": "Mnemon CLI 0.2.9 offers its own npm update to 0.2.10" + }, + "restartNoticeAfterEnableNow": false, + "hostLaunches": 1, + "hostWarnings": [], + "consoleErrors": [] + } + }, + "moreEvidence": [ + "../entities-complete-index/verification.json" + ], + "environment": { + "os": "macOS", + "node": "24.19.0", + "pnpm": "11.25.0", + "mnemonCli": "0.2.9", + "browser": "headless Google Chrome over the DevTools protocol", + "dshModified": false + }, + "screenshots": { + "status.png": "6d46331a9bcfc55f318484094154148c7599e79ef583c5f0efc4a9f644ad2ca5", + "runtime.png": "c16c0580f013c90e6a030d9530100a522de61bd21e9905eddcad79e85745184a", + "spaces.png": "d0e077e5455d4043c5bbf1910b0aaafe87642393a2fb2b9723974a24c5c12436", + "recall.png": "a7f91697d01367070d8af13b18c578c4f1c74c5948409a830702ded79860fd82", + "entities.png": "9b97f580516b9f38e9beb908b08c25dbbef7483a277970bbca60c1eeff4ad144", + "entities-related.png": "a1d277ce158120148649675791cda0958e9e571cae17db24c0440c38e723908c" + } +} diff --git a/dsh/docs/pr-assets/release-v0.5.6/README.md b/dsh/docs/pr-assets/release-v0.5.6/README.md new file mode 100644 index 000000000..da2efaf3c --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.6/README.md @@ -0,0 +1,36 @@ +# v0.5.6 release verification + +[简体中文](./README.zh-CN.md) · [Release notes](../../en/releases/v0.5.6.md) + +Run on 2026-09-10 (Asia/Shanghai), from the dedicated release worktree based on main `0a499d11e9853851ed11aa92633e8122d11f9545`. These screenshots use the versioned release composition: Starter 0.5.6, five changed plugins 0.5.5 and eleven unchanged plugins 0.5.4. This release commit changes versions, generated Provider version metadata and documentation; it adds no runtime behavior beyond that main revision. + +Environment: macOS arm64, Node 22.19.0, pnpm 10.13.1, published DSH 0.1.2-rc.1 and Native CLI 0.2.7. + +| Check | Result | +|---|---| +| Complete `pnpm run verify` | Passed: 852 root tests and 324 plugin tests | +| Native integration | Passed: disposable View write, recall and forget | +| Deterministic build, types, public entries, package lint | Passed; root has 46 files and 1,256,247 unpacked bytes | +| Real Headless profile | Passed: 39 tools, 8 representative Mnemon tools, restart and legacy disable | +| `node scripts/verify-plugin-artifacts.mjs --skip-build` | Passed: 16 independent repositories, 17 packed artifacts, external consumer and real Starter composition | +| Release selection against v0.5.5 | Six packages selected, with dependency-safe layers and stable `latest` tags | +| WebUI | Settings save, version display, Runtime write and reload passed; zero console errors | + +Five opt-in Flash tests and one Windows-only binary test were skipped. Historical model results are not rerun or rescored by this release check. + +## Browser smoke + +Start `pnpm e2e:serve` after the complete build. Use its authenticated launch URL and disposable Profile; all requests remain on loopback with a scripted model endpoint. + +1. In Settings → Memory system, select centralized workspace isolation and global USER.md. Set the central root inside the temporary fixture. Save reports that the configuration is live. +2. Register the fixture workspace. The status page displays `dsh-mnemon 0.5.6` and centralized workspace storage. +3. Add the synthetic work memory shown below. Reload the page and reopen Runtime: one entry remains with its original creation time. +4. Check the filesystem: the entry is in `central/workspaces//runtime/memories.json`; no `.mnemon` directory was created in the project. + +The three screenshots were visually reviewed. The temporary Profile, memory and browser tabs were removed afterward. The broader nine-screenshot [Core workflow](../issue-189-core-workspaces/README.md) retains its original revision labels. + +| Screenshot | Evidence | +|---|---| +| [01 Settings saved](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.6/01-settings-saved.png) | Centralized isolation and its persisted root setting | +| [02 Version and storage](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.6/02-version-and-storage.png) | Release version, workspace selector, active scope and one project entry | +| [03 Runtime after reload](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.6/03-runtime-after-reload.png) | Synthetic memory persists after a real page reload | diff --git a/dsh/docs/pr-assets/release-v0.5.6/README.zh-CN.md b/dsh/docs/pr-assets/release-v0.5.6/README.zh-CN.md new file mode 100644 index 000000000..4b6102217 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.6/README.zh-CN.md @@ -0,0 +1,36 @@ +# v0.5.6 发布验证 + +[English](./README.md) · [发布说明](../../zh-CN/releases/v0.5.6.md) + +于 2026-09-10(Asia/Shanghai)在独立发布 worktree 中执行,基于 main `0a499d11e9853851ed11aa92633e8122d11f9545`。截图使用已升级版本的发布组合:Starter 0.5.6、五个变化插件 0.5.5、十一个未变化插件 0.5.4。本次发布提交调整版本、生成的 Provider 版本声明与文档,没有在该 main revision 之外添加运行时行为。 + +环境:macOS arm64、Node 22.19.0、pnpm 10.13.1、已发布的 DSH 0.1.2-rc.1、Native CLI 0.2.7。 + +| 检查 | 结果 | +|---|---| +| 完整 `pnpm run verify` | 通过:852 个根包测试、324 个插件测试 | +| Native 集成 | 临时 View 写入、召回与 forget 通过 | +| 确定性构建、类型、公开入口、包检查 | 通过;根包 46 个文件,解包后 1,256,247 字节 | +| 真实 Headless Profile | 39 个工具、8 个代表性 Mnemon 工具、重启与旧入口禁用通过 | +| `node scripts/verify-plugin-artifacts.mjs --skip-build` | 16 个独立仓库、17 个制品、外部 consumer 与真实 Starter 组合通过 | +| 相对 v0.5.5 的发布选择 | 选中六个包,依赖分层与正式 `latest` 标签正确 | +| WebUI | 设置保存、版本显示、Runtime 写入与刷新通过;控制台零错误 | + +跳过五个需显式开启的 Flash 测试和一个 Windows 专用二进制测试。本次发布检查未重跑或重评历史模型实验。 + +## 浏览器冒烟 + +完整构建后运行 `pnpm e2e:serve`,使用其带认证的启动 URL 和临时 Profile;请求均使用回环地址和脚本模型端点。 + +1. 在设置 → 记忆系统中选择集中式工作区隔离与全局 USER.md,将集中根目录设在临时 fixture 内。保存后界面显示已实时生效。 +2. 注册 fixture 工作区。状态页显示 `dsh-mnemon 0.5.6` 和集中式工作区存储。 +3. 添加截图中的合成工作记忆,刷新页面后重新打开 Runtime,仍显示一条记忆及原始创建时间。 +4. 检查磁盘:条目位于 `central/workspaces//runtime/memories.json`;项目目录未产生 `.mnemon`。 + +三张截图均已目视检查。测试后删除临时 Profile、记忆与浏览器标签页。更全面的九张 [Core 工作流截图](../issue-189-core-workspaces/README.zh-CN.md) 保留原始 revision 标注。 + +| 截图 | 证据 | +|---|---| +| [01 已保存设置](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.6/01-settings-saved.png) | 集中隔离模式和持久化根目录设置 | +| [02 版本与存储](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.6/02-version-and-storage.png) | 发布版本、工作区选择器、当前范围和一条项目记忆 | +| [03 刷新后 Runtime](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.6/03-runtime-after-reload.png) | 真实刷新页面后合成记忆仍然存在 | diff --git a/dsh/docs/pr-assets/release-v0.5.7/README.md b/dsh/docs/pr-assets/release-v0.5.7/README.md new file mode 100644 index 000000000..d70e8d892 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.7/README.md @@ -0,0 +1,17 @@ +# v0.5.7 release verification + +**English** | [简体中文](./README.zh-CN.md) + +Tested `f237e9a1b195c879472713fac160d965c7b0d522`, the versioned release tree based on merged main `accabbd3ed4cd2e7fd0e2c6d399b4fe7d04a7122`, on 2026-09-10. Environment and screenshot hashes are in [verification.json](./verification.json). + +`pnpm verify` passed 1,218 tests (895 root, 323 plugin), deterministic builds, type checks, real Headless activation/restart and package validation. Seven opt-in tests were skipped: five live-model checks, Native integration and the Windows binary smoke test. `node scripts/verify-plugin-artifacts.mjs --skip-build` verified 16 independent repositories and 17 tarballs, public SDK/Client consumers and real DSH Starter/optional-plugin activation. `release:intent` and `release:check` selected exactly four changed packages: Starter 0.5.7 and the three Sources at 0.5.6. + +Run `pnpm e2e:serve --document-archive` after the builds. In the real browser, check no-session Status, bind the disposable workspace with the Mnemon E2E preset, create and activate a Native Memory Space, and create a synthetic document. Archive it through the confirmation dialog, reload the browser, reopen the archived original, then inspect the Native index in Memory Spaces → Content. The original content and its SHA-256 were preserved; the persisted document is revision 2 with status `archived`. The archive step recorded no browser page errors or failing Mnemon HTTP responses. + +| Screenshot | Observation | +|---|---| +| [Version and Status](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.7/01-version-status.png) | Actual 0.5.7, connected RPC, no live Session required | +| [Archived original](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.7/02-document-archived.png) | Original and Host receipt remain readable after reload | +| [Index after reload](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.7/03-index-after-reload.png) | One Native index with the cold reference and matching content hash | + +The model only scripts archive decisions; the browser, DSH transport, Host, storage and Native CLI remain real. All test content is synthetic and the disposable Profile/browser are removed afterward. This release smoke does not repeat the full historical browser matrix, Windows/Electron checks or live model-quality experiments. The [DSH compatibility report](../issue-223-dsh-015/README.md) and [archive regression report](../issue-222-document-archive/README.md) preserve their original revisions and broader coverage. diff --git a/dsh/docs/pr-assets/release-v0.5.7/README.zh-CN.md b/dsh/docs/pr-assets/release-v0.5.7/README.zh-CN.md new file mode 100644 index 000000000..7e92761c8 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.7/README.zh-CN.md @@ -0,0 +1,17 @@ +# v0.5.7 发布验证 + +**简体中文** | [English](./README.md) + +2026-09-10 验证的版本化提交为 `f237e9a1b195c879472713fac160d965c7b0d522`,基于已合并的 main `accabbd3ed4cd2e7fd0e2c6d399b4fe7d04a7122`。环境与截图 hash 见 [verification.json](./verification.json)。 + +`pnpm verify` 通过 1,218 项测试(根包 895、插件 323),以及确定性构建、类型检查、真实 Headless 激活/重启和包验证。跳过七项 opt-in 测试:五项真实模型检查、Native 集成和 Windows 二进制冒烟。`node scripts/verify-plugin-artifacts.mjs --skip-build` 验证十六个独立仓库、十七个 tarball、公开 SDK/Client 消费者与真实 DSH Starter/可选插件激活。`release:intent` 和 `release:check` 精确选择四个变化包:Starter 0.5.7 与三个 Source 0.5.6。 + +构建后运行 `pnpm e2e:serve --document-archive`。在真实浏览器中检查无会话状态页,再使用 Mnemon E2E preset 绑定临时工作区,创建并激活 Native 记忆空间,新建合成档案。在确认框中归档,刷新浏览器后重新打开归档原文,再从记忆空间 → 内容读取 Native 索引。原文与 SHA-256 均保留,持久化档案为 revision 2、状态 `archived`。归档步骤未记录到浏览器页面错误或失败的 Mnemon HTTP 响应。 + +| 截图 | 验证结果 | +|---|---| +| [版本与状态](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.7/01-version-status.png) | 实际运行 0.5.7,RPC 已连接,无需活动会话 | +| [归档原文](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.7/02-document-archived.png) | 刷新后仍可读取原文与 Host 回执 | +| [刷新后的索引](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.7/03-index-after-reload.png) | 一个 Native 索引,含冷存储引用与匹配的内容 hash | + +模型仅提供预设的归档决策;浏览器、DSH 传输、Host、存储和 Native CLI 均为真实实现。全部内容为合成数据,结束后清理临时 Profile 和浏览器。本次发布冒烟不重复历史完整浏览器矩阵、Windows/Electron 检查或真实模型质量实验。[DSH 兼容报告](../issue-223-dsh-015/README.zh-CN.md) 与[归档回归报告](../issue-222-document-archive/README.md) 保留原始 revision 和更广的覆盖范围。 diff --git a/dsh/docs/pr-assets/release-v0.5.7/verification.json b/dsh/docs/pr-assets/release-v0.5.7/verification.json new file mode 100644 index 000000000..61e5a1567 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.7/verification.json @@ -0,0 +1,64 @@ +{ + "date": "2026-09-10", + "sourceRevision": "f237e9a1b195c879472713fac160d965c7b0d522", + "baseRevision": "accabbd3ed4cd2e7fd0e2c6d399b4fe7d04a7122", + "previousReleaseRevision": "1790251919ec731f9dd49b43da78f78b37890a00", + "version": "0.5.7", + "environment": { + "node": "25.1.0", + "pnpm": "11.19.0", + "dsh": "0.1.5-rc.1", + "playwright": "1.63.0", + "chromium": "153", + "platform": "macOS arm64", + "viewport": "1440x1050" + }, + "tests": { + "passed": 1218, + "skipped": 7, + "rootPassed": 895, + "pluginPassed": 323, + "independentPluginRepositories": 16, + "packedArtifacts": 17, + "selectedReleaseArtifacts": 4 + }, + "browser": { + "versionAndNoSessionStatus": true, + "spaceCreatedAndActivated": true, + "documentArchive": true, + "originalRereadAfterReload": true, + "nativeIndexReadAfterReload": true, + "nativeIndexCount": 1, + "archiveObservation": { + "archived": true, + "pageErrors": [], + "failedRpc": [] + }, + "syntheticDocument": { + "id": "e875e2a2-92cd-4dc5-a29f-837b4ddc994a", + "title": "Release 0.5.7 Archive Check", + "status": "archived", + "revision": 2, + "contentHash": "e7dba9cb5c44f8b640f609d4439d330a2f4a7a9ba4916b12fbfacf152326cf0e" + }, + "exactSyntheticContentPreserved": true, + "model": "loopback scripted archive decisions; no live model API" + }, + "screenshots": [ + { + "path": "01-version-status.png", + "sha256": "c28547d0f56f45694b0746a2c3fa054a6491d806b74a89398b3a28702c191d0c", + "bytes": 135688 + }, + { + "path": "02-document-archived.png", + "sha256": "e90795ec8c99a41d1ee7f487add33e3280503e068f86c882b859672423fa322f", + "bytes": 145089 + }, + { + "path": "03-index-after-reload.png", + "sha256": "3656d43afb0b799ed412a2e9320221c8ae3e1cf29f47bb1598d6e912c0768146", + "bytes": 157928 + } + ] +} diff --git a/dsh/docs/pr-assets/release-v0.5.8/README.md b/dsh/docs/pr-assets/release-v0.5.8/README.md new file mode 100644 index 000000000..e8528abdc --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.8/README.md @@ -0,0 +1,24 @@ +# v0.5.8 release verification + +[简体中文](./README.zh-CN.md) | **English** + +Versioned application revision: `099d39f8fabf8d118b87455b4399f80eb2f98f7b`, based on merged main `ae710d3a552df76739b8ada36240e9176f2e4726`. This record and its screenshots are added afterward without changing application code. + +The release advances only Starter to 0.5.8 and consumes six patch changesets. All sixteen official plugin versions are unchanged. The frozen release plan selects exactly `dsh-mnemon@0.5.8`. + +Validation on macOS arm64, Node 25.1.0 and pnpm 11.19.0: + +- `pnpm run verify`: 1,301 passed (978 root and 323 plugin tests); seven opt-in checks skipped. Types, deterministic builds, real Headless activation/restart and package validation passed. +- `node scripts/verify-plugin-artifacts.mjs --skip-build`: sixteen independent repositories, seventeen packed artifacts, public SDK/Client consumers, packed Starter and all three optional Strategy plugins passed. +- Real Mnemon CLI 0.2.7 integration: create, write, recall and forget passed in an isolated store. +- Release-intent consumption and versioned release selection passed against the preceding v0.5.7 release. +- Package: 47 files, 284,282 packed bytes, 1,278,996 unpacked bytes, within the 1,280,000-byte limit. + +Real browser verification used DSH 0.1.5-rc.1 with the Starter and three Strategy enhancements enabled. The authenticated, session-free status displayed 0.5.8. A synthetic Runtime record remained visible after a browser reload. A Native Memory Space was created and activated; Status showed Mnemon CLI 0.2.7 connected with 1/1 space active. Both screenshots were visually reviewed. The disposable Profile, process and browser tab were removed after verification. + +| Runtime after reload | Version and Native status | +| --- | --- | +| [Screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.8/01-runtime-after-reload.jpg) | [Screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.8/02-version-native-status.jpg) | + +No external model API was called. These checks validate application and plugin behavior; they do not measure model quality, cache hit rates or historical token consumption. Earlier issue reports retain their original reproduction revisions. + diff --git a/dsh/docs/pr-assets/release-v0.5.8/README.zh-CN.md b/dsh/docs/pr-assets/release-v0.5.8/README.zh-CN.md new file mode 100644 index 000000000..068ad17b0 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.8/README.zh-CN.md @@ -0,0 +1,24 @@ +# v0.5.8 发布验证 + +**简体中文** | [English](./README.md) + +实际运行的版本化应用提交为 `099d39f8fabf8d118b87455b4399f80eb2f98f7b`,基于已合并 main `ae710d3a552df76739b8ada36240e9176f2e4726`。本记录和截图随后提交,不再改动应用代码。 + +本次仅将 Starter 升级到 0.5.8,消费六个 patch changeset;十六个官方插件版本不变。冻结发布计划仅选择 `dsh-mnemon@0.5.8`。 + +验证环境:macOS arm64、Node 25.1.0、pnpm 11.19.0。 + +- `pnpm run verify`:1,301 项通过,其中主包 978 项、插件 323 项;七项 opt-in 检查跳过。类型、确定性构建、真实 Headless 激活与重启、包验证通过。 +- `node scripts/verify-plugin-artifacts.mjs --skip-build`:十六个独立仓库、十七个制品、公开 SDK/Client 消费方、打包 Starter 及三个可选策略插件通过。 +- 真实 Mnemon CLI 0.2.7 集成验证:在隔离 Store 中创建、写入、召回与删除通过。 +- 基于前一版本 v0.5.7 的发布意图消费与版本选择校验通过。 +- 包含 47 个文件,压缩后 284,282 B,解包后 1,278,996 B,低于 1,280,000 B 上限。 + +真实浏览器使用 DSH 0.1.5-rc.1,启用 Starter 和三个策略增强插件。鉴权后的无会话状态页显示 0.5.8;新增的合成 Runtime 记录在刷新浏览器后仍可读取。创建并激活 Native 记忆空间后,状态页显示 Mnemon CLI 0.2.7 连接正常、1/1 空间激活。两张截图均经过目视检查。验证结束后已清理临时 Profile、进程和浏览器标签页。 + +| 刷新后的 Runtime 记录 | 版本与 Native 状态 | +| --- | --- | +| [截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.8/01-runtime-after-reload.jpg) | [截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.8/02-version-native-status.jpg) | + +未调用外部模型 API。这些检查验证应用与插件行为,不评价模型质量、缓存命中或历史 token 消耗。此前各 issue 记录保留各自的实际复现提交。 + diff --git a/dsh/docs/pr-assets/release-v0.5.8/verification.json b/dsh/docs/pr-assets/release-v0.5.8/verification.json new file mode 100644 index 000000000..79f72bd91 --- /dev/null +++ b/dsh/docs/pr-assets/release-v0.5.8/verification.json @@ -0,0 +1,36 @@ +{ + "sourceRevision": "099d39f8fabf8d118b87455b4399f80eb2f98f7b", + "baseMain": "ae710d3a552df76739b8ada36240e9176f2e4726", + "baseRelease": "2fa306b916e0995eb739953318ec7a3d4b89709b", + "version": "0.5.8", + "publish": [ + "dsh-mnemon@0.5.8" + ], + "rootTestsPassed": 978, + "pluginTestsPassed": 323, + "optionalTestsSkipped": 7, + "nativeCliTestsPassed": 1, + "independentPlugins": 16, + "packedArtifacts": 17, + "package": { + "files": 47, + "packedBytes": 284282, + "unpackedBytes": 1278996 + }, + "webui": { + "version": "0.5.8", + "dsh": "0.1.5-rc.1", + "mnemonCli": "0.2.7", + "runtimePersistedAfterReload": true, + "runtimeEntries": 1, + "runtimeMarkdownBytes": 95, + "activeNativeSpaces": 1, + "strategyEnhancements": 3 + }, + "screenshots": { + "01-runtime-after-reload.jpg": "311b1af64a6c014adab36be1178e25e0ac432ad0ce9c6acc71c8dc9a82b3ae8a", + "02-version-native-status.jpg": "d024e8d67f017feed055e03001b718a2ed40f041b5eac7de2eb227b966f2fd6e" + }, + "externalModelCalls": 0, + "disposableFixtureRemoved": true +} diff --git a/dsh/docs/pr-assets/runtime-capacity-flash-20260909/README.md b/dsh/docs/pr-assets/runtime-capacity-flash-20260909/README.md new file mode 100644 index 000000000..3c7a73b5e --- /dev/null +++ b/dsh/docs/pr-assets/runtime-capacity-flash-20260909/README.md @@ -0,0 +1,40 @@ +# Runtime capacity pressure with DeepSeek V4 Flash + +[简体中文](README.zh-CN.md) · [Aggregate results](results.json) · [Before-fix examples](before-fixes.json) · [Full evidence ZIP](https://github.com/user-attachments/files/32006240/dsh-mnemon-pr212-evidence-20260909-2994e3d.zip) · [Archive SHA-256 and contents](../runtime-flash-evidence-20260909.json) + +**Passed:** on 2026-09-09, three scenarios completed 240 writes and 38 automatic archives. A quoted-input diagnostic completed another 96 writes and 17 archives. All committed text was preserved exactly, the default 10,240-byte limit held, and no maintenance tasks leaked. Every request and observed response used `deepseek-v4-flash`, with thinking disabled. + +The workload models [issue #203](https://github.com/omdsh-dev/dsh-mnemon/issues/203): backend, frontend, Android and operations workstreams share project Runtime. Facts and stores are synthetic and disposable. Environment: macOS arm64, Node v25.1.0, DSH 0.1.2-rc.1, Native CLI 0.2.7. The recorded baseline is `583842c2847bfb1f0bf4423cc41c261fd4b941d9` plus the fixes committed in `fce3873e9669c9a9eefb902b76ca8d0ddbf68700`; tested source hashes remain in the aggregate results. + +| Scenario | Writes / archives | Peak hot bytes | Final hot / cold rows | Duration | +| --- | ---: | ---: | ---: | ---: | +| Four root sessions, one space | 96 / 16 | 10,235 | 11 / 94 | 67.5 s | +| Four child writers, four spaces | 96 / 16 | 10,229 | 13 / 95 | 244.0 s | +| No open user sessions, Web RPC | 48 / 6 | 10,237 | 12 / 45 | 76.5 s | +| Quoted-input diagnostic, one space | 96 / 17 | 9,646 | 11 / 94 | 65.5 s | + +Hot and cold rows can overlap and must not be summed as unique facts. Both concurrent cases reached four simultaneous model calls and write dispatches. Children used `maxDepth: 1`; all 80 child-scenario and 30 session-free maintenance tasks were released. The session-free case disposed all parent handles before writing. + +The main run used 275 Flash calls, 441,228 input tokens and 77,231 output tokens. Tool-write p95 was 834 ms for one space and 14,157 ms for child writers across four spaces; RPC latency was not individually instrumented. These measurements include queueing and model routing. + +## Findings and limits + +- **Fixed exact archival:** ordinary Native semantic deduplication imported only one of three similar originals. The child workload blocked after 14 writes because the Host rejected an inexact receipt. The Provider now reuses exact snapshot content and imports remaining originals with `--no-diff`, validating exact receipts. Ordinary `remember` semantics remain unchanged. +- **Fixed UTF-8 corruption:** chunk-wise decoding corrupted a split Chinese comma after 84 writes / 13 archives. Each owning process module now streams stdout/stderr decoding independently. The diagnostic rerun preserved all 96 actual submissions; model-added surrounding quotes are counted separately. +- **Routing remains imperfect:** 85/95 child-scenario cold rows matched their intended topic; ten went to another authorized space and three duplicate cold copies remained. Session-free routing matched 45/45. Authorization and exact retention passed; semantic routing is a separate quality finding. +- Explicit memory writes drove capacity pressure; idle recall/distillation was disabled. Natural-conversation quality is covered by the separate [automatic-memory acceptance](../runtime-memory-quality-flash-20260909/README.md). Windows, independent DSH processes, long uptime and other live Providers were not tested. + +## Reproduce and inspect + +Provide `DEEPSEEK_API_KEY` and a verified `MNEMON_NATIVE_TEST_CLI`, then run the retained [opt-in harness](../../../tests/runtime-capacity-flash-stress.spec.ts): + +```sh +MNEMON_RUN_FLASH_STRESS=1 MNEMON_FLASH_STRESS_ROUNDS=8 MNEMON_FLASH_STRESS_REPORT=/tmp/mnemon-flash-stress.json pnpm exec vitest run tests/runtime-capacity-flash-stress.spec.ts +MNEMON_RUN_FLASH_STRESS=1 MNEMON_FLASH_STRESS_ROUNDS=8 MNEMON_FLASH_STRESS_JSON_PROMPT=1 MNEMON_FLASH_STRESS_REPORT=/tmp/mnemon-flash-json.json pnpm exec vitest run tests/runtime-capacity-flash-stress.spec.ts -t 'four concurrent root' +``` + +The harness rejects non-Flash requests before dispatch, checks returned model ids, caps each scenario at 240 calls and cleans up its sessions/stores. Ordinary CI skips the three live tests. Eight configured rounds produce four waves in the session-free case. + +The ZIP preserves the complete original reports, per-wave results and harness snapshot from `2994e3de8cc27a53cd3b54d9c5dc7d6628d1646c`. Verify its SHA-256 using the linked manifest; `MANIFEST.json` inside lists each file's hash. This evidence reorganization did not rerun the model or alter measurements. + +After the fixes, full verification passed 834 root tests and the independent-artifact gate passed 16 plugin repositories / 17 artifacts. After the main merge, verification passed 836 root tests (five live opt-in tests skipped), types, documentation, deterministic builds, Headless and package checks; see the revision-specific [validation record](../runtime-memory-quality-flash-20260909/validation.json). diff --git a/dsh/docs/pr-assets/runtime-capacity-flash-20260909/README.zh-CN.md b/dsh/docs/pr-assets/runtime-capacity-flash-20260909/README.zh-CN.md new file mode 100644 index 000000000..f47b2ae17 --- /dev/null +++ b/dsh/docs/pr-assets/runtime-capacity-flash-20260909/README.zh-CN.md @@ -0,0 +1,40 @@ +# DeepSeek V4 Flash Runtime 容量压力测试 + +[English](README.md) · [汇总指标](results.json) · [修复前最小样例](before-fixes.json) · [完整证据 ZIP](https://github.com/user-attachments/files/32006240/dsh-mnemon-pr212-evidence-20260909-2994e3d.zip) · [归档 SHA-256 与内容清单](../runtime-flash-evidence-20260909.json) + +**通过:**2026-09-09,三个场景完成 240 次写入、38 次自动归档;带引号输入诊断另完成 96 次写入、17 次归档。已提交原文完整保留,默认 10,240 字节上限未突破,维护任务无残留。所有请求及实际响应均为 `deepseek-v4-flash`,关闭思考。 + +工作负载参考 [issue #203](https://github.com/omdsh-dev/dsh-mnemon/issues/203):后端、前端、Android、运维四条工作流共享项目 Runtime,使用合成事实与临时存储。环境为 macOS arm64、Node v25.1.0、DSH 0.1.2-rc.1、Native CLI 0.2.7。记录基线是 `583842c2847bfb1f0bf4423cc41c261fd4b941d9` 加最终提交于 `fce3873e9669c9a9eefb902b76ca8d0ddbf68700` 的修复;实测源文件哈希保留在汇总指标中。 + +| 场景 | 写入 / 归档 | 热记忆峰值字节 | 最终热 / 冷行数 | 耗时 | +| --- | ---: | ---: | ---: | ---: | +| 四个主会话、单空间 | 96 / 16 | 10,235 | 11 / 94 | 67.5 秒 | +| 四个子代理、四空间 | 96 / 16 | 10,229 | 13 / 95 | 244.0 秒 | +| 无打开的用户会话、Web RPC | 48 / 6 | 10,237 | 12 / 45 | 76.5 秒 | +| 带引号输入诊断、单空间 | 96 / 17 | 9,646 | 11 / 94 | 65.5 秒 | + +热记忆与冷记忆可以重叠,不能相加计算唯一事实数。两个并发场景均达到四路模型请求及写入调度重叠。子代理设置 `maxDepth: 1`;子代理场景的 80 个、无会话场景的 30 个维护任务均已释放。无会话场景在写入前释放全部父会话句柄。 + +主测试使用 275 次 Flash 调用、441,228 输入 Token、77,231 输出 Token。工具写入 p95:单空间 834 毫秒、四空间子代理 14,157 毫秒;未单独测量 RPC 延迟。耗时包含排队与模型路由。 + +## 发现与限制 + +- **已修复精确归档:**Native 常规语义去重对三个相似原文仅导入一个。子代理工作负载在 14 次写入后因 Host 拒绝不精确回执而阻塞。Provider 现复用快照中的完全相同内容,其余原文通过 `--no-diff` 导入并校验精确回执;普通 `remember` 语义不变。 +- **已修复 UTF-8 损坏:**逐块解码在 84 次写入、13 次归档后损坏一个跨块中文逗号。各自所属的进程模块现独立维护 stdout/stderr 流解码。诊断重跑完整保留 96 条实际提交内容,模型添加的外围引号单独计量。 +- **路由仍有偏差:**子代理场景 85/95 条冷记忆主题匹配,十条进入其他已授权空间,保留三个重复冷副本;无会话场景为 45/45。授权与原文保留通过,语义分类是独立质量指标。 +- 本轮显式写入记忆以制造容量压力,关闭空闲召回与整理。自然对话质量见独立的[自动记忆验收](../runtime-memory-quality-flash-20260909/README.zh-CN.md)。未测试 Windows、独立 DSH 进程、长时间运行或其他真实 Provider。 + +## 复现与核对 + +通过环境提供 `DEEPSEEK_API_KEY` 与已验证的 `MNEMON_NATIVE_TEST_CLI`,运行保留的[显式启用测试](../../../tests/runtime-capacity-flash-stress.spec.ts): + +```sh +MNEMON_RUN_FLASH_STRESS=1 MNEMON_FLASH_STRESS_ROUNDS=8 MNEMON_FLASH_STRESS_REPORT=/tmp/mnemon-flash-stress.json pnpm exec vitest run tests/runtime-capacity-flash-stress.spec.ts +MNEMON_RUN_FLASH_STRESS=1 MNEMON_FLASH_STRESS_ROUNDS=8 MNEMON_FLASH_STRESS_JSON_PROMPT=1 MNEMON_FLASH_STRESS_REPORT=/tmp/mnemon-flash-json.json pnpm exec vitest run tests/runtime-capacity-flash-stress.spec.ts -t 'four concurrent root' +``` + +测试在发出请求前拒绝非 Flash 模型,核对响应模型,每场景最多 240 次调用,并清理会话与存储。常规 CI 跳过三个真实测试。配置八轮时,无会话场景执行四个波次。 + +ZIP 原样保留 `2994e3de8cc27a53cd3b54d9c5dc7d6628d1646c` 的完整报告、逐轮结果和测试脚本快照。请按链接清单校验压缩包 SHA-256;包内 `MANIFEST.json` 列出各文件哈希。本次仅调整证据存放形式,没有重跑模型或修改指标。 + +修复后的完整验证通过 834 项根测试,独立制品门禁通过 16 个插件仓库、17 个制品。合并 main 后完整验证通过 836 项根测试(跳过五项真实 API 测试),以及类型、文档、确定性构建、Headless、包检查;各次 revision 见[验证记录](../runtime-memory-quality-flash-20260909/validation.json)。 diff --git a/dsh/docs/pr-assets/runtime-capacity-flash-20260909/before-fixes.json b/dsh/docs/pr-assets/runtime-capacity-flash-20260909/before-fixes.json new file mode 100644 index 000000000..ce12d2753 --- /dev/null +++ b/dsh/docs/pr-assets/runtime-capacity-flash-20260909/before-fixes.json @@ -0,0 +1,32 @@ +{ + "nativeImport": { + "scenario": "four-children-four-spaces", + "successfulWritesBeforeBlock": 14, + "archiveCycles": 0, + "symptom": "runtime archive skipped an entry without exact durable recall evidence", + "standaloneCliProbe": { + "distinctSimilarInputs": 3, + "normalImported": 1, + "normalSkipped": 2 + }, + "effect": "The exact-receipt guard preserved committed hot memory and rejected pending writes." + }, + "utf8Output": { + "successfulWritesBeforeDetection": 84, + "archiveCycles": 13, + "missingFacts": [], + "changedFacts": [ + "flashstress-backend-004" + ], + "changedEvidence": [ + { + "id": "flashstress-backend-004", + "submitted": "项目 Orion,模块 backend,记录 flashstress-backend-004。后端订单服务采用事务发件箱处理支付回调,幂等键由租户、订单和请求序列组成。所有状态更新先校验版本号,数据库提交后才发布消息。失败重试必须保留审计记录,禁止绕过租户隔离。接口以服务端生成的追踪编号关联日志,测试环境只使用合成订单。 本记录对应独立验收项 4,配置键为 backend.rule.4,验证端口为 12004,重试上限为 6 次。验收必须保留该记录标识、模块归属、配置键与数字约束,以便跨会话精确召回;本段全部为压力测试合成数据。", + "observed": [ + "项目 Orion,模块 backend,记录 flashstress-backend-004。后端订单服务采用事务发件箱处理支付回调,幂等键由租户、订单和请求序列组成。所有状态更新先校验版本号,数据库提交后才发布消息。失败重试必须保留审计记录,禁止绕过租户隔离。接口以服务端生成的追踪编号关联日志,测试环境只使用合成订单。 本记录对应独立验收项 4,配置键为 backend.rule.4���验证端口为 12004,重试上限为 6 次。验收必须保留该记录标识、模块归属、配置键与数字约束,以便跨会话精确召回;本段全部为压力测试合成数据。" + ] + } + ], + "cause": "Per-chunk Buffer.toString decoded a Chinese comma split across stdout buffers as replacement characters." + } +} diff --git a/dsh/docs/pr-assets/runtime-capacity-flash-20260909/results.json b/dsh/docs/pr-assets/runtime-capacity-flash-20260909/results.json new file mode 100644 index 000000000..1690d2077 --- /dev/null +++ b/dsh/docs/pr-assets/runtime-capacity-flash-20260909/results.json @@ -0,0 +1,181 @@ +{ + "date": "2026-09-09", + "baseCommit": "583842c2847bfb1f0bf4423cc41c261fd4b941d9", + "environment": { + "platform": "macOS arm64", + "node": "v25.1.0", + "dsh": "0.1.2-rc.1", + "nativeCli": "0.2.7", + "model": "deepseek-v4-flash", + "thinking": "disabled", + "endpoint": "https://api.deepseek.com/chat/completions" + }, + "sourceSha256": { + "tests/runtime-capacity-flash-stress.spec.ts": "8873e780e590deab30ade1a1c176e2c6cf92bda8eb953246e7e6fed05365d5f3", + "tests/host-process.spec.ts": "34b972dc74f6eeffd9850a55d534bffea1e44ae982964022c416d95fcbf0fc7f", + "src/host/process.ts": "536f1fbed2057b753d251d59f2a4690bc66822af830be1d456aa90bb2b993edd", + "src/host/subagent.ts": "9724e7b91f912111db513a42100b87a4fc3c4ea426c53a97439a1a250be966f8", + "src/host/lifecycle.ts": "861eef8b93af71417edf7a7a7b203397fd9069fdcc2bd2fa1474a4bd783c9af6", + "plugins/dsh-mnemon-source-memory-spaces/src/providers/process.ts": "536f1fbed2057b753d251d59f2a4690bc66822af830be1d456aa90bb2b993edd", + "plugins/dsh-mnemon-source-memory-spaces/tests/process.spec.ts": "5fed51f8c737817226c6f7cf586bf32702836a264f189240a02667f239020edd", + "plugins/dsh-mnemon-provider-mnemon-native/src/driver.ts": "904e407aefe3720a61130cf875b52fc9b5265086c99745d501092de79947282d", + "plugins/dsh-mnemon-provider-mnemon-native/tests/provider.spec.ts": "eff4b661b6890d2fb87f5aa4571f9a693c3aadc2ade2e8c77cfdd51adcaf98da", + "plugins/dsh-mnemon-source-memory-spaces/tests/service.spec.ts": "0a71c47d2200aa1b75b0ddd3515b03506ffe37331ca68658cd03eb741d9c6925" + }, + "format": "aggregate-summary", + "archiveManifest": "../runtime-flash-evidence-20260909.json", + "mainRun": [ + { + "scenario": "four-roots-single-space", + "promptFormat": "record-elements", + "writers": 4, + "memoryLimitBytes": 10240, + "successfulWrites": 96, + "archiveCycles": 16, + "maxHotBytes": 10235, + "finalHotFacts": 11, + "finalColdFacts": 94, + "duplicateColdFacts": 0, + "maxConcurrentModels": 4, + "maxConcurrentWrites": 4, + "taskAgentsCreated": 0, + "taskAgentsRemaining": 0, + "elapsedMs": 67467, + "status": "passed", + "configuredRounds": 8, + "completedWaves": 8, + "modelUsage": { + "calls": 80, + "inputTokens": 113805, + "outputTokens": 24358, + "purposes": { + "conversation": 80 + } + }, + "toolWriteLatencyMs": { + "samples": 96, + "p50": 4, + "p95": 834, + "max": 871 + }, + "modelInputDriftCount": 0, + "misroutedFactCount": 0 + }, + { + "scenario": "four-children-four-spaces", + "promptFormat": "record-elements", + "writers": 4, + "memoryLimitBytes": 10240, + "successfulWrites": 96, + "archiveCycles": 16, + "maxHotBytes": 10229, + "finalHotFacts": 13, + "finalColdFacts": 95, + "duplicateColdFacts": 3, + "maxConcurrentModels": 4, + "maxConcurrentWrites": 4, + "taskAgentsCreated": 80, + "taskAgentsRemaining": 0, + "elapsedMs": 244035, + "status": "passed", + "configuredRounds": 8, + "completedWaves": 8, + "modelUsage": { + "calls": 165, + "inputTokens": 254715, + "outputTokens": 46376, + "purposes": { + "conversation": 165 + } + }, + "toolWriteLatencyMs": { + "samples": 96, + "p50": 9323, + "p95": 14157, + "max": 14715 + }, + "modelInputDriftCount": 0, + "misroutedFactCount": 10 + }, + { + "scenario": "no-session-four-spaces", + "promptFormat": "record-elements", + "writers": 4, + "memoryLimitBytes": 10240, + "successfulWrites": 48, + "archiveCycles": 6, + "maxHotBytes": 10237, + "finalHotFacts": 12, + "finalColdFacts": 45, + "duplicateColdFacts": 0, + "maxConcurrentModels": 1, + "maxConcurrentWrites": 0, + "taskAgentsCreated": 30, + "taskAgentsRemaining": 0, + "elapsedMs": 76488, + "status": "passed", + "configuredRounds": 8, + "completedWaves": 4, + "modelUsage": { + "calls": 30, + "inputTokens": 72708, + "outputTokens": 6497, + "purposes": { + "conversation": 30 + } + }, + "toolWriteLatencyMs": null, + "modelInputDriftCount": 0, + "misroutedFactCount": 0 + } + ], + "quotedJsonDiagnostic": [ + { + "scenario": "four-roots-single-space", + "promptFormat": "json-strings", + "writers": 4, + "memoryLimitBytes": 10240, + "successfulWrites": 96, + "archiveCycles": 17, + "maxHotBytes": 9646, + "finalHotFacts": 11, + "finalColdFacts": 94, + "duplicateColdFacts": 0, + "maxConcurrentModels": 4, + "maxConcurrentWrites": 4, + "taskAgentsCreated": 0, + "taskAgentsRemaining": 0, + "elapsedMs": 65523, + "status": "passed", + "configuredRounds": 8, + "completedWaves": 8, + "modelUsage": { + "calls": 96, + "inputTokens": 114271, + "outputTokens": 24946, + "purposes": { + "conversation": 96 + } + }, + "toolWriteLatencyMs": { + "samples": 96, + "p50": 4, + "p95": 824, + "max": 864 + }, + "modelInputDriftCount": 96, + "misroutedFactCount": 0 + } + ], + "allRunsChecks": { + "missingFacts": 0, + "changedFacts": 0, + "toolErrors": 0, + "unexpectedActions": 0, + "routerFallbacks": 0, + "observedModels": [ + "deepseek-v4-flash" + ], + "exactContentReference": "Actual text committed by each tool call, including model-added quotes in the diagnostic." + } +} diff --git a/dsh/docs/pr-assets/runtime-flash-evidence-20260909.json b/dsh/docs/pr-assets/runtime-flash-evidence-20260909.json new file mode 100644 index 000000000..e8c3a3f7a --- /dev/null +++ b/dsh/docs/pr-assets/runtime-flash-evidence-20260909.json @@ -0,0 +1,20 @@ +{ + "schemaVersion": 1, + "url": "https://github.com/user-attachments/files/32006240/dsh-mnemon-pr212-evidence-20260909-2994e3d.zip", + "sha256": "0629f6d480da9fe0bffc0d411c088122b97be4f050fa2d68efdddf2a2dfa07d3", + "bytes": 90913, + "snapshotCommit": "2994e3de8cc27a53cd3b54d9c5dc7d6628d1646c", + "snapshotFileCount": 16, + "uncompressedSnapshotBytes": 493526, + "manifest": "MANIFEST.json", + "contents": [ + "docs/pr-assets/runtime-capacity-flash-20260909/ (complete original report and JSON)", + "docs/pr-assets/runtime-memory-quality-flash-20260909/ (complete original report and JSON)", + "tests/runtime-capacity-flash-stress.spec.ts", + "tests/runtime-memory-flash-quality.spec.ts", + "tests/fixtures/flash-quality/live.ts", + "tests/fixtures/flash-quality/workload.ts" + ], + "scope": "Exact committed, sanitized synthetic evidence and harness snapshot before report compaction. No new live run; original measurements, strict verdicts and reviewed acceptance are preserved.", + "verification": "Downloaded without authentication; ZIP integrity and all 16 per-file SHA-256 values matched the committed snapshot." +} diff --git a/dsh/docs/pr-assets/runtime-memory-quality-flash-20260909/README.md b/dsh/docs/pr-assets/runtime-memory-quality-flash-20260909/README.md new file mode 100644 index 000000000..90536f711 --- /dev/null +++ b/dsh/docs/pr-assets/runtime-memory-quality-flash-20260909/README.md @@ -0,0 +1,51 @@ +# Issue #203 automatic memory acceptance + +[简体中文](README.zh-CN.md) · [Aggregate results](results.json) · [Minimal finding examples](examples.json) · [Full evidence ZIP](https://github.com/user-attachments/files/32006240/dsh-mnemon-pr212-evidence-20260909-2994e3d.zip) · [Archive SHA-256 and contents](../runtime-flash-evidence-20260909.json) + +**Acceptance: passed, with non-blocking improvements retained.** Following user review, this bounded simulation was accepted: 144/152 fresh answers were correct (94.7%), eight were unknown, and all 16 tested formal corrections were answered correctly. No stale, cross-module or unsupported answers occurred; all 192 developer turns finished with passing configuration checks. + +Measured production revision: `fce3873e9669c9a9eefb902b76ca8d0ddbf68700`, 2026-09-09. All requests and observed responses used `deepseek-v4-flash`, thinking disabled. The original strict automated verdicts remain `fail`; `acceptanceDecision` records the later user-reviewed pass. The findings below remain unfixed. + +| Metric | One space, 12 waves | Four spaces, 12 waves | Four spaces, 24 waves | +| --- | ---: | ---: | ---: | +| Developer turns / fresh sessions | 48 / 12 | 48 / 12 | 96 / 28 | +| Current facts covered | 35/36 | 36/36 | 80/80 | +| Correct fresh answers | 34/36 | 36/36 | 74/80 | +| Correct formal corrections | 4/4 | 4/4 | 8/8 | +| Retained transient markers | 0 | 1 | 3 | +| Obsolete base documents | 4 | 4 | 4 | +| Final hot entries / documents | 22 / 6 | 29 / 5 | 13 / 23 | +| Peak / final hot bytes | 7,095 / 7,095 | 6,684 / 6,684 | 10,079 / 5,615 | +| Idle reviews / model calls | 40 / 321 | 41 / 299 | 85 / 654 | +| Rejected memory calls | 1 | 3 | 4 | +| Duration | 4m 34s | 4m 9s | 12m 5s | +| Reviewed acceptance | Pass | Pass | Pass | + +## Findings against the complaint + +- **Messy memory partly reproduced:** long-run waves 21 and 23 supplied partial text to a whole-entry replacement. One frontend row lost its module label and seven fields; another lost `timeout_ms=1800`. Exact before/after text and successful calls remain in [examples.json](examples.json). The [Runtime Source](../../../plugins/dsh-mnemon-source-runtime/src/source.ts) applied the submitted whole-entry content; capacity archival was not involved. +- **Retention did not guarantee recall:** one short-run fact was never captured; another existed but was answered unknown. All six long-run unknowns were frontend values still present in Documents. Readers sometimes searched empty cold memory without proceeding to document search. +- **Old and unwanted material persisted:** all 12 base documents retained obsolete values despite Runtime corrections; create-only maintenance accumulated supplements. A wave-13 document retained literal do-not-persist markers from wave 10. No mutations occurred during the no-write turn itself; later history review reintroduced the examples. +- **Operations recovered:** eight rejected calls, duplicate hot facts and missing attribution remain follow-ups. All final development checks passed. The short-four scorer's `development-workload-incomplete` flag counted two recovered intermediate failures; its original flag is preserved alongside this adjudication. +- **No manual cleanup or capacity blockage occurred:** natural consolidation kept hot memory below 10,240 bytes. All three runs had zero archive cycles and empty cold stores. Post-archive quality was not exercised; repeated archival is covered by the separate [capacity test](../runtime-capacity-flash-20260909/README.md). + +## Method and limits + +Four backend/frontend/Android/operations sessions ran concurrently in one real DSH process. Developer tools actually read, update and validate temporary JSON configurations; the model chose memory actions and the default lifecycle performed idle review. Fresh readers had neither development transcripts nor file access. This is bounded configuration work, not full application builds. + +Environment: macOS arm64, Node v25.1.0, DSH 0.1.2-rc.1, Native CLI 0.2.7. Guided recall/writeback and the default capacity stayed unchanged; idle debounce alone changed from 30 to 5 seconds. [protocol.json](protocol.json) retains preregistered parameters, hashes and the pre-run extension rationale. There was one scored run per condition; the two-wave pilot is excluded. Windows, independent processes and a half-day human workflow remain untested. + +Frozen scoring required at least 90% coverage and accuracy, all corrections correct, and zero noise, stale/cross-module/unsupported answers, no-write mutations, cold duplicates, capacity overruns or execution errors. Original strict failures and later reviewed acceptance are separate. All 98 final rows and 152 answer cells were reviewed. The experiments used 1,274 Flash calls, 4,557,451 input tokens and 292,774 output tokens. + +## Reproduce and inspect + +Provide `DEEPSEEK_API_KEY` and a verified `MNEMON_NATIVE_TEST_CLI`. The [test](../../../tests/runtime-memory-flash-quality.spec.ts), [live fixture](../../../tests/fixtures/flash-quality/live.ts) and [workload generator](../../../tests/fixtures/flash-quality/workload.ts) remain in Git: + +```sh +MNEMON_RUN_FLASH_QUALITY=1 MNEMON_FLASH_QUALITY_REPORT=/tmp/mnemon-flash-quality.json pnpm exec vitest run tests/runtime-memory-flash-quality.spec.ts +MNEMON_RUN_FLASH_QUALITY=1 MNEMON_FLASH_QUALITY_WAVES=24 MNEMON_FLASH_QUALITY_REPORT=/tmp/mnemon-flash-quality-long.json pnpm exec vitest run tests/runtime-memory-flash-quality.spec.ts -t 'four preconfigured' +``` + +The ZIP preserves the full original reports, expanded inputs, wave metrics, 152 answer cells, all 98 final rows with review notes, and harness snapshot from `2994e3de8cc27a53cd3b54d9c5dc7d6628d1646c`. Its `MANIFEST.json` lists individual hashes; the linked archive manifest records the ZIP SHA-256. This storage change did not rerun or rescore any experiment. Harness report-only changes after execution are listed in the protocol. + +The separate [validation record](validation.json) covers the main merge: full verification passed 836 root tests (five live tests skipped) and independent validation passed 16 plugin repositories / 17 artifacts. Historical model results still apply to their recorded production revision. diff --git a/dsh/docs/pr-assets/runtime-memory-quality-flash-20260909/README.zh-CN.md b/dsh/docs/pr-assets/runtime-memory-quality-flash-20260909/README.zh-CN.md new file mode 100644 index 000000000..39a608f41 --- /dev/null +++ b/dsh/docs/pr-assets/runtime-memory-quality-flash-20260909/README.zh-CN.md @@ -0,0 +1,51 @@ +# Issue #203 自动记忆验收 + +[English](README.md) · [汇总指标](results.json) · [最小问题样例](examples.json) · [完整证据 ZIP](https://github.com/user-attachments/files/32006240/dsh-mnemon-pr212-evidence-20260909-2994e3d.zip) · [归档 SHA-256 与内容清单](../runtime-flash-evidence-20260909.json) + +**验收通过,保留非阻塞改进项。**用户复核后接受本轮有界模拟结果:新会话答对 144/152 项(94.7%),八项为 unknown,16 项正式纠正全部回答正确。未出现旧值、跨模块或无依据回答;192 个开发回合的最终配置检查全部通过。 + +实际测试的生产 revision:`fce3873e9669c9a9eefb902b76ca8d0ddbf68700`,日期 2026-09-09。所有请求及实际响应均为 `deepseek-v4-flash`,关闭思考。原始严格自动评分仍为 `fail`,后续用户复核通过的决定记录在 `acceptanceDecision` 中。以下发现尚未修复。 + +| 指标 | 单空间、12 波次 | 四空间、12 波次 | 四空间、24 波次 | +| --- | ---: | ---: | ---: | +| 开发回合 / 新会话 | 48 / 12 | 48 / 12 | 96 / 28 | +| 当前事实覆盖 | 35/36 | 36/36 | 80/80 | +| 新会话答对 | 34/36 | 36/36 | 74/80 | +| 正式纠正答对 | 4/4 | 4/4 | 8/8 | +| 残留临时标记 | 0 | 1 | 3 | +| 陈旧基础文档 | 4 | 4 | 4 | +| 最终热记忆 / 文档条数 | 22 / 6 | 29 / 5 | 13 / 23 | +| 热记忆峰值 / 最终字节 | 7,095 / 7,095 | 6,684 / 6,684 | 10,079 / 5,615 | +| 空闲审阅 / 模型调用 | 40 / 321 | 41 / 299 | 85 / 654 | +| 被拒绝的记忆调用 | 1 | 3 | 4 | +| 耗时 | 4 分 34 秒 | 4 分 9 秒 | 12 分 5 秒 | +| 复核验收 | 通过 | 通过 | 通过 | + +## 对照用户吐槽的发现 + +- **部分复现“乱七八糟”:**长程第 21、23 波次将局部文本交给整条替换。一条前端记忆丢失模块名称和七个字段,另一条丢失 `timeout_ms=1800`。[examples.json](examples.json) 保留原样的前后文本和成功调用。[Runtime Source](../../../plugins/dsh-mnemon-source-runtime/src/source.ts) 按收到的内容替换整条记忆,未涉及容量归档。 +- **保存不等于召回:**短程一项事实未被保存,另一项已存在但回答 unknown。长程六项 unknown 都是仍在 Documents 中的前端配置;部分读者只搜索空的冷记忆,没有继续搜索文档。 +- **旧值和无须保存的材料残留:**Runtime 已纠正,但共 12 份基础文档仍有旧值;维护权限只能创建文档,补充文档持续累积。第 13 波次文档保存了第 10 波次明确要求不落盘的标记原文。禁止写入回合本身没有记忆修改,后续历史审阅重新引入了样例。 +- **操作能够恢复:**八次调用被拒绝,重复热记忆和缺少归属继续保留为改进项。所有开发回合最终检查通过;short-four 的 `development-workload-incomplete` 原始评分误计了两次已恢复的中间失败,原始标记与人工裁定同时保留。 +- **未发生人工清理或容量阻塞:**模型自然整理让热记忆保持在 10,240 字节以下。三组归档次数均为零、冷存储为空,本轮没有验收归档后质量;重复归档另见[容量压力测试](../runtime-capacity-flash-20260909/README.zh-CN.md)。 + +## 方法与限制 + +后端、前端、Android、运维四个会话在一个真实 DSH 进程内并发运行。开发工具实际读取、更新、校验临时 JSON 配置,模型自行选择记忆操作,默认生命周期执行空闲审阅。新读者不能访问开发对话或文件。本轮是有界配置任务,没有构建完整应用。 + +环境为 macOS arm64、Node v25.1.0、DSH 0.1.2-rc.1、Native CLI 0.2.7。guided 召回/写回和默认容量保持不变,只将空闲防抖由 30 秒缩短到 5 秒。[protocol.json](protocol.json) 保留预登记参数、哈希及长程运行前确定的扩展理由。每种条件只有一次正式运行,两波次校准不计分。未覆盖 Windows、独立进程或真人半天工作流。 + +冻结评分要求覆盖率和准确率至少 90%、纠正全部正确,且噪声、旧值/跨模块/无依据回答、禁写回合修改、冷副本重复、超容和执行错误均为零。原始严格评分失败与后续复核通过分别记录。共审阅全部 98 条最终记忆、152 项回答;使用 1,274 次 Flash 调用、4,557,451 输入 Token、292,774 输出 Token。 + +## 复现与核对 + +通过环境提供 `DEEPSEEK_API_KEY` 与已验证的 `MNEMON_NATIVE_TEST_CLI`。[测试入口](../../../tests/runtime-memory-flash-quality.spec.ts)、[真实运行夹具](../../../tests/fixtures/flash-quality/live.ts)及[工作负载生成器](../../../tests/fixtures/flash-quality/workload.ts) 继续保留在 Git 中: + +```sh +MNEMON_RUN_FLASH_QUALITY=1 MNEMON_FLASH_QUALITY_REPORT=/tmp/mnemon-flash-quality.json pnpm exec vitest run tests/runtime-memory-flash-quality.spec.ts +MNEMON_RUN_FLASH_QUALITY=1 MNEMON_FLASH_QUALITY_WAVES=24 MNEMON_FLASH_QUALITY_REPORT=/tmp/mnemon-flash-quality-long.json pnpm exec vitest run tests/runtime-memory-flash-quality.spec.ts -t 'four preconfigured' +``` + +ZIP 原样保留 `2994e3de8cc27a53cd3b54d9c5dc7d6628d1646c` 的完整报告、展开输入、逐轮指标、152 项回答、全部 98 条最终记忆及审阅标记和测试脚本快照。包内 `MANIFEST.json` 列出逐文件哈希,链接清单记录 ZIP 的 SHA-256。本次调整存放形式,没有重跑或重新评分;执行后的报告器改动见 protocol。 + +独立的[验证记录](validation.json) 对应合并 main 后的代码:完整验证通过 836 项根测试(跳过五项真实测试),独立验证通过 16 个插件仓库、17 个制品。历史模型结果仍只对应注明的生产 revision。 diff --git a/dsh/docs/pr-assets/runtime-memory-quality-flash-20260909/examples.json b/dsh/docs/pr-assets/runtime-memory-quality-flash-20260909/examples.json new file mode 100644 index 000000000..847c2b726 --- /dev/null +++ b/dsh/docs/pr-assets/runtime-memory-quality-flash-20260909/examples.json @@ -0,0 +1,54 @@ +{ + "archiveManifest": "../runtime-flash-evidence-20260909.json", + "source": "docs/pr-assets/runtime-memory-quality-flash-20260909/observations.json in the archive", + "run": "long-four", + "lossyReplacements": [ + { + "wave": 21, + "before": "Orion frontend formal config (module-specific, acceptance-script-dependent, same-named sibling values independent—keep frontend ownership, never treat one-off temp log as new rule): handoff_channel=fe-interaction; conflict_policy=reject-stale-form; retention_days=43; partition_key=filter_id; max_batch_size=131; backoff_ms=353 (initial retry backoff); audit_event=fe.form.submitted (commit audit event name); dedup_window_seconds=223 (duplicate request detection window); checkpoint_prefix=fe-view-cp; locale_fallback=en-GB (language fallback). retry_limit=13 (supercedes deprecated former value 5). Values module-specific.", + "toolCall": { + "wave": 21, + "actor": "maintenance-76", + "name": "mnemon_runtime_memory", + "arguments": { + "action": "replace", + "target": "memory", + "old_text": "dedup_window_seconds=223 (duplicate request detection window); checkpoint_prefix=fe-view-cp; locale_fallback=en-GB (language fallback). retry_limit=13", + "content": "dedup_window_seconds=223 (duplicate request detection window); checkpoint_prefix=fe-view-cp; locale_fallback=en-GB (language fallback); recovery_marker=fe-rehydrate-ok (recovery-success marker). retry_limit=13", + "importance": "low" + }, + "error": false, + "elapsedMs": 3 + }, + "after": "dedup_window_seconds=223 (duplicate request detection window); checkpoint_prefix=fe-view-cp; locale_fallback=en-GB (language fallback); recovery_marker=fe-rehydrate-ok (recovery-success marker). retry_limit=13" + }, + { + "wave": 23, + "before": "Orion frontend module established config: service_port=8421 (fixed, module-specific port), retry_limit=13 (module-only cap, manual review after reaching; former value 5 is deprecated/superseded and should not be restored from old historical logs), timeout_ms=1800", + "toolCall": { + "wave": 23, + "actor": "maintenance-83", + "name": "mnemon_runtime_memory", + "arguments": { + "action": "replace", + "target": "memory", + "old_text": "frontend module established config: service_port=8421 (fixed, module-specific port), retry_limit=13", + "content": "Orion frontend established config: service_port=8851 (fixed module-specific port; former 8421 deprecated/superseded, do not revert from old logs), retry_limit=13", + "importance": "normal" + }, + "error": false, + "elapsedMs": 5 + }, + "after": "Orion frontend established config: service_port=8851 (fixed module-specific port; former 8421 deprecated/superseded, do not revert from old logs), retry_limit=13" + } + ], + "laterNoWriteMarkerRetention": { + "wave": 13, + "tool": "mnemon_document_create", + "successful": true, + "title": "Orion Frontend module recent-config additions", + "contentExcerpt": "Each dev_config read/update acts on a single key=value per module. Other fields and other modules stay unchanged except for the one key being set. Transient one-off debug output (unadopted hypotheses such as service_port 9999, temporary jitter timings and TEMP_ markers such as TEMP_USER_frontend, fake test credentials like FAKE_CREDENTIAL_frontend, single-session timings, load-test outputs) is not a config decision and must never override established values. The temporary-turn sandbox marker NO_MEMORY_frontend and related TEMP_/FAKE_ strings were explicit do-not-persist do-not-treat-as-rule debugging artifacts.", + "context": "The NO_MEMORY_frontend and FAKE_CREDENTIAL_frontend literals came from explicit no-write wave 10. No successful memory mutation occurred during that turn; automatic review retained the literals later." + }, + "scope": "Selected non-blocking finding evidence. Replacement before/after text and tool calls are copied verbatim. The document excerpt omits unrelated configuration sections; the complete document and all answers remain in the archive." +} diff --git a/dsh/docs/pr-assets/runtime-memory-quality-flash-20260909/protocol.json b/dsh/docs/pr-assets/runtime-memory-quality-flash-20260909/protocol.json new file mode 100644 index 000000000..bee96a72c --- /dev/null +++ b/dsh/docs/pr-assets/runtime-memory-quality-flash-20260909/protocol.json @@ -0,0 +1,53 @@ +{ + "short": { + "codeCommit": "fce3873e9669c9a9eefb902b76ca8d0ddbf68700", + "harnessSha256": { + "tests/runtime-memory-flash-quality.spec.ts": "f48d5c4d7e2ea27983eacf1dd852997fe3f837b6a966a056cf0d708dad2187f4", + "tests/fixtures/flash-quality/workload.ts": "215805cad70fa0aaa05d37aff682a4e719f1ef72ec81a631e53c28c78792d009", + "tests/fixtures/flash-quality/live.ts": "605ad4719d65a551b6fbb1d19dd55a13cbf11c14cec9aa6167dbbf51bbb6a37e" + }, + "scenarios": [ + "one shared Native space", + "four preconfigured Native spaces" + ], + "developerSessionsPerScenario": 4, + "developerTurnsPerScenario": 48, + "freshRecallSessionsPerScenario": 12, + "currentFactsPerScenario": 36, + "correctedFactsPerScenario": 4, + "model": "deepseek-v4-flash", + "thinking": "disabled", + "idleReviewMs": 5000, + "productionIdleReviewMs": 30000, + "recallMode": "guided", + "writebackMode": "guided", + "manualReview": "Review every final retained row and every wrong or unknown answer; exact configuration-key matching is only an automated lower bound on semantic coverage." + }, + "long": { + "codeCommit": "fce3873e9669c9a9eefb902b76ca8d0ddbf68700", + "harnessSha256": { + "tests/runtime-memory-flash-quality.spec.ts": "e3b2b4072a8017a4b3a3d8cb0a5645299eab665fcc6c859094c761f9dae132eb", + "tests/fixtures/flash-quality/workload.ts": "536ba45bdd691bacb819744a5c8666f121cf4ad83a4103011a1c0300d1f74de0", + "tests/fixtures/flash-quality/live.ts": "60b44354fd5e27c02b33cfad7bf7afea37612543083551317e610638c7a7f508" + }, + "scenario": "four preconfigured spaces, extended natural developer workload", + "waves": 24, + "developerSessions": 4, + "developerTurns": 96, + "currentFacts": 80, + "correctedFacts": 8, + "freshRecallQuestions": 28, + "modelCallBudget": 900, + "model": "deepseek-v4-flash", + "thinking": "disabled", + "changesFromShortRun": "Extended the workload after 48 developer turns naturally remained below 10 KiB; no direct seeding of hot memory, no production code or threshold changes." + }, + "reportOnlyChangesAfterExecution": [ + "The committed reporter distinguishes recovered test-before-edit failures from incomplete development turns; original recorded violations are retained in results.json.", + "The committed reporter records the current git revision and harness hashes, redacts fixture roots and credentials throughout the report, and restores its request observer if fixture setup fails." + ], + "rawResultSha256": { + "dsh-mnemon-flash-quality-full.json": "f56241df30e09668979c2218aefbecc54c43829a0047d4eb1b69379adacd0edb", + "dsh-mnemon-flash-quality-long.json": "72aa45df5f3a637aff5587ad8d2f5214d17b7103fa91c59f69bc418d6a318cf7" + } +} diff --git a/dsh/docs/pr-assets/runtime-memory-quality-flash-20260909/results.json b/dsh/docs/pr-assets/runtime-memory-quality-flash-20260909/results.json new file mode 100644 index 000000000..c28ec675b --- /dev/null +++ b/dsh/docs/pr-assets/runtime-memory-quality-flash-20260909/results.json @@ -0,0 +1,181 @@ +{ + "format": "aggregate-summary", + "archiveManifest": "../runtime-flash-evidence-20260909.json", + "testedProductionCommit": "fce3873e9669c9a9eefb902b76ca8d0ddbf68700", + "model": "deepseek-v4-flash", + "acceptanceDecision": { + "status": "pass", + "reviewDate": "2026-09-09", + "authority": "Explicit user acceptance after review of measured accuracy and observed findings.", + "appliesToRuns": [ + "short-single", + "short-four", + "long-four" + ], + "basis": { + "correctAnswers": 144, + "answerCells": 152, + "unknownAnswers": 8, + "unsupportedAnswers": 0, + "staleAnswers": 0, + "crossModuleAnswers": 0, + "allTestedCorrectionsCorrect": true, + "allDeveloperTurnsCompleted": true + }, + "nonBlockingFindings": [ + "lossy whole-entry replacements", + "missing module attribution", + "obsolete active documents", + "transient examples retained", + "missed recall", + "recovered invalid tool calls" + ], + "originalAutomatedVerdictsPreserved": true, + "scope": "Reviewed acceptance of this bounded simulation. Original frozen thresholds, observations and scores remain unchanged; follow-up defects are not represented as fixed." + }, + "config": { + "memoryLimitBytes": 10240, + "idleReviewMs": 5000, + "productionIdleReviewMs": 30000, + "recallMode": "guided", + "writebackMode": "guided", + "acceptance": { + "currentFactCoverage": 0.9, + "freshRecallAccuracy": 0.9, + "forbiddenNoiseMatches": 0, + "staleCorrectionAnswers": 0, + "crossModuleAnswers": 0, + "unsupportedCurrentAnswers": 0, + "correctedFactRecall": 1, + "noWriteTurnMutations": 0, + "exactColdDuplicates": 0, + "memoryLimitBytes": 10240 + } + }, + "runs": [ + { + "id": "short-single", + "developerTurns": 48, + "freshReaderSessions": 12, + "reviews": 40, + "modelCalls": 321, + "elapsedMs": 273767, + "inputTokens": 622608, + "outputTokens": 61074, + "maximumHotBytes": 7095, + "finalHotBytes": 7095, + "archiveCycles": 0, + "finalRowsByLocation": { + "runtime": 22, + "document": 6 + }, + "missingFacts": [ + "operations/handoff_channel" + ], + "retainedNoiseMarkers": [], + "recordedAutomatedVerdict": "fail", + "recordedViolations": [ + "execution-errors" + ], + "currentFactCoverage": 0.9722222222222222, + "freshRecallAccuracy": 0.9444444444444444, + "correctRecall": 34, + "recallCells": 36, + "correctedAnswers": 4, + "correctedQuestions": 4, + "toolErrorCount": 1, + "intermediateDevelopmentCheckFailures": 0, + "incompleteDevelopmentTurns": [], + "waves": 12 + }, + { + "id": "short-four", + "developerTurns": 48, + "freshReaderSessions": 12, + "reviews": 41, + "modelCalls": 299, + "elapsedMs": 248769, + "inputTokens": 620512, + "outputTokens": 62202, + "maximumHotBytes": 6684, + "finalHotBytes": 6684, + "archiveCycles": 0, + "finalRowsByLocation": { + "runtime": 29, + "document": 5 + }, + "missingFacts": [], + "retainedNoiseMarkers": [ + "TEMP_USER_android" + ], + "recordedAutomatedVerdict": "fail", + "recordedViolations": [ + "forbidden-noise-retained", + "execution-errors", + "development-workload-incomplete" + ], + "currentFactCoverage": 1, + "freshRecallAccuracy": 1, + "correctRecall": 36, + "recallCells": 36, + "correctedAnswers": 4, + "correctedQuestions": 4, + "toolErrorCount": 3, + "intermediateDevelopmentCheckFailures": 2, + "incompleteDevelopmentTurns": [], + "waves": 12 + }, + { + "id": "long-four", + "developerTurns": 96, + "freshReaderSessions": 28, + "reviews": 85, + "modelCalls": 654, + "elapsedMs": 724741, + "inputTokens": 3314331, + "outputTokens": 169498, + "maximumHotBytes": 10079, + "finalHotBytes": 5615, + "archiveCycles": 0, + "finalRowsByLocation": { + "runtime": 13, + "document": 23 + }, + "missingFacts": [], + "retainedNoiseMarkers": [ + "TEMP_USER_frontend", + "NO_MEMORY_frontend", + "FAKE_CREDENTIAL_frontend" + ], + "recordedAutomatedVerdict": "fail", + "recordedViolations": [ + "forbidden-noise-retained", + "execution-errors" + ], + "currentFactCoverage": 1, + "freshRecallAccuracy": 0.925, + "correctRecall": 74, + "recallCells": 80, + "correctedAnswers": 8, + "correctedQuestions": 8, + "toolErrorCount": 4, + "intermediateDevelopmentCheckFailures": 0, + "incompleteDevelopmentTurns": [], + "waves": 24, + "modelCallBudget": 900 + } + ], + "allRunsChecks": { + "executionStatus": "completed", + "observedModels": [ + "deepseek-v4-flash" + ], + "unsupportedAnswers": 0, + "staleAnswers": 0, + "crossModuleAnswers": 0, + "exactColdDuplicates": 0, + "noWriteTurnMutations": [], + "remainingRootAgents": 0 + }, + "manualAdjudication": "All developer turns finished with passing configuration checks. The short-four development-workload-incomplete flag counted two recovered intermediate failures; the original recorded flag remains unchanged." +} diff --git a/dsh/docs/pr-assets/runtime-memory-quality-flash-20260909/validation.json b/dsh/docs/pr-assets/runtime-memory-quality-flash-20260909/validation.json new file mode 100644 index 000000000..f83f73a16 --- /dev/null +++ b/dsh/docs/pr-assets/runtime-memory-quality-flash-20260909/validation.json @@ -0,0 +1,50 @@ +{ + "verifiedAtUtc": "2026-09-09T09:44:36.633703+00:00", + "liveExperimentProductionCommit": "fce3873e9669c9a9eefb902b76ca8d0ddbf68700", + "integrationVerifiedCommit": "0017102989747e5ecc55ee7adf4eacf811826c2f", + "mergedMainCommit": "a1c500a27fbdb334c7205936f4ef823fe1edb406", + "committedHarnessSha256": { + "tests/runtime-memory-flash-quality.spec.ts": "81c0423831a504581843658b05fc5698816bcdccb78b0aa6f64aad63cc80f4e5", + "tests/fixtures/flash-quality/workload.ts": "536ba45bdd691bacb819744a5c8666f121cf4ad83a4103011a1c0300d1f74de0", + "tests/fixtures/flash-quality/live.ts": "a5733d1cafedc0ad2eb56b743f4be4db17f012e9a5beb83b2ac006d2918cd3cf" + }, + "commands": [ + { + "command": "pnpm_config_verify_deps_before_run=false pnpm run verify", + "exitCode": 0, + "rootTests": { + "passed": 836, + "skippedOptInLive": 5 + }, + "includes": [ + "bilingual documentation checks", + "TypeScript", + "deterministic double build", + "workspace plugin builds, types and tests", + "real isolated Headless activation", + "package contents and public imports", + "publint and attw" + ] + }, + { + "command": "pnpm_config_verify_deps_before_run=false pnpm run verify:plugins --skip-build", + "exitCode": 0, + "independentPluginRepositories": 16, + "packedArtifacts": 17, + "includes": [ + "public SDK external consumer", + "standalone plugin installs, types, tests and builds", + "packed Starter activation", + "three optional packed Strategies enabled together" + ] + }, + { + "command": "git diff --check", + "exitCode": 0 + } + ], + "nonfatalWarning": "Published DSH UI package omits its sourcemap; verification exits successfully.", + "liveExperimentRerunAfterMainMerge": false, + "scope": "The live quality evidence applies to its recorded production revision. Main added UI ordering and Electron npm-launcher support; the merged integration was verified separately. No live score is reinterpreted as a measurement of the merged revision.", + "redactionAudit": "All new evidence and harness files checked for the actual credential, private local paths, emoji, and JSON syntax. Synthetic FAKE_CREDENTIAL_* canaries are intentionally retained as evidence." +} diff --git a/dsh/docs/pr-assets/session-events-156/README.md b/dsh/docs/pr-assets/session-events-156/README.md new file mode 100644 index 000000000..2a637793c --- /dev/null +++ b/dsh/docs/pr-assets/session-events-156/README.md @@ -0,0 +1,35 @@ +# Issue #156 WebUI compatibility evidence + +Captured on 2026-09-03 from an isolated workspace based on `main` commit +`5153096ac5ad41fe50950114131243b9eb2d18cb`. + +Each run used a fresh DSH home, data directory, and workspace. The real DSH WebUI +was driven through a deterministic local SSE model; no existing user profile or +remote model was used. The Mnemon CLI was version 0.2.6 (SHA-256 +`b1bf3e8e315474bb71e83f94893df6247db7d2fbcad685bee2b914e6d1110609`). + +| DSH | Mnemon plugin | Result | +| --- | --- | --- | +| 0.1.2-alpha.4 | published 0.4.4 | Reproduced `agent.session.events is not iterable` | +| 0.1.2-alpha.4 | this branch | Completed: `Compatibility verified on DSH 0.1.2-alpha.4.` | +| 0.1.2-alpha.5 | this branch | Completed: `Compatibility verified on DSH 0.1.2-alpha.5.` | +| 0.1.1-rc.2 | this branch | Completed: `Compatibility verified on DSH 0.1.1-rc.2.` | + +The three successful runs produced no browser-console errors or warnings. Host +logs also contained no `error`, `warn`, or `not iterable` matches. + +## Before: alpha.4 with published Mnemon 0.4.4 + +![The DSH WebUI reproducing agent.session.events is not iterable](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/session-events-156/before-alpha4-v0.4.4.jpg) + +## After: alpha.4 + +![The DSH WebUI completing a conversation on alpha.4](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/session-events-156/after-alpha4.jpg) + +## After: latest alpha.5 + +![The DSH WebUI completing a conversation on alpha.5](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/session-events-156/after-alpha5.jpg) + +## After: stable rc.2 + +![The DSH WebUI completing a conversation on rc.2](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/session-events-156/after-rc2.jpg) diff --git a/dsh/docs/pr-assets/sidebar-brain-icon-20260927/README.md b/dsh/docs/pr-assets/sidebar-brain-icon-20260927/README.md new file mode 100644 index 000000000..ccb29ad22 --- /dev/null +++ b/dsh/docs/pr-assets/sidebar-brain-icon-20260927/README.md @@ -0,0 +1,17 @@ +# Memory System brain icon + +[简体中文](./README.zh-CN.md) + +Tested implementation: `d02f1502d05c49784452b502c2e3946748d990b0`. The branch includes merged PRs #290 and #291, retaining `可组合记忆 (dsh-mnemon)` and both composable-memory descriptions. + +Memory System now uses an original classic brain outline. Native, fallback and Better Sidebar entries share the same SVG paths, a 16-unit canvas, 1-unit strokes and `currentColor`. Native DSH continues to own button sizing, labels and selected state. + +| Before | After | +|---|---| +| [Database icon with the current name](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-icon-weight-20260927/after-combined.png) | [Brain icon with the current name](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-brain-icon-20260927/expanded.png) | + +The [collapsed entry](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-brain-icon-20260927/collapsed.png) opens a healthy Memory System. Expanded/collapsed navigation and the return to Plugins were exercised in the real WebUI. The plugin list still displays `可组合记忆 (dsh-mnemon)`. + +Environment: macOS, Node 24.20.0, pnpm 11.19.0, published DSH `0.1.7-rc.2`, Mnemon CLI `0.2.9`, this branch's Root tarball and sixteen unchanged companion artifacts. Root SHA-256: `3f0d0008471f565abd9d9dcb415df3ba8fb46b3f28d030ffa6ab8f6f7a0094cc`. The profile, workspace and memory are disposable; no live model API was used. + +Validation: `pnpm build`, `pnpm typecheck`, all 40 tests across the four sidebar suites, `pnpm exec vitest run --dir tests --maxWorkers=2` (1,420 passed, 9 skipped), `pnpm verify:package`, `pnpm release:intent`, `pnpm verify:docs`, and `git diff --check` passed. Browser evidence covers native DSH; existing automated tests cover fallback and Better Sidebar registration and behavior. No storage, configuration, permission or wire contracts change. diff --git a/dsh/docs/pr-assets/sidebar-brain-icon-20260927/README.zh-CN.md b/dsh/docs/pr-assets/sidebar-brain-icon-20260927/README.zh-CN.md new file mode 100644 index 000000000..8bff3a570 --- /dev/null +++ b/dsh/docs/pr-assets/sidebar-brain-icon-20260927/README.zh-CN.md @@ -0,0 +1,17 @@ +# 记忆系统大脑图标 + +[English](./README.md) + +验证的实现提交:`d02f1502d05c49784452b502c2e3946748d990b0`。分支包含已合并的 #290、#291,完整保留“可组合记忆 (dsh-mnemon)”及中英文可组合记忆描述。 + +记忆系统改用原创的经典大脑轮廓。原生侧栏、备用入口和 Better Sidebar 共用同一份 SVG 路径,采用 16 单位画布、1 单位描边及 `currentColor`。原生 DSH 继续负责按钮尺寸、标签和选中状态。 + +| 修改前 | 修改后 | +|---|---| +| [当前名称与数据库图标](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-icon-weight-20260927/after-combined.png) | [当前名称与大脑图标](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-brain-icon-20260927/expanded.png) | + +[折叠入口](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-brain-icon-20260927/collapsed.png)可打开状态正常的记忆系统。已在真实 WebUI 验证展开、折叠导航以及返回“插件”页;插件列表仍显示“可组合记忆 (dsh-mnemon)”。 + +环境:macOS、Node 24.20.0、pnpm 11.19.0、正式 DSH `0.1.7-rc.2`、Mnemon CLI `0.2.9`、本分支 Root 制品及十六个未修改的伴随插件制品。Root SHA-256:`3f0d0008471f565abd9d9dcb415df3ba8fb46b3f28d030ffa6ab8f6f7a0094cc`。profile、工作区和记忆均为隔离测试数据,未调用真实模型 API。 + +验证通过:`pnpm build`、`pnpm typecheck`、四个侧栏测试文件中的全部 40 项测试、`pnpm exec vitest run --dir tests --maxWorkers=2`(1,420 项通过、9 项跳过)、`pnpm verify:package`、`pnpm release:intent`、`pnpm verify:docs` 和 `git diff --check`。浏览器证据覆盖原生 DSH;已有自动化测试覆盖备用入口和 Better Sidebar 的注册与行为。不涉及存储、配置、权限或通信契约变更。 diff --git a/dsh/docs/pr-assets/sidebar-icon-weight-20260927/README.md b/dsh/docs/pr-assets/sidebar-icon-weight-20260927/README.md new file mode 100644 index 000000000..2b8615807 --- /dev/null +++ b/dsh/docs/pr-assets/sidebar-icon-weight-20260927/README.md @@ -0,0 +1,34 @@ +# Sidebar icon weight + +[简体中文](./README.zh-CN.md) + +This record covers the initial stroke correction. The final brain-icon follow-up is recorded [separately](../sidebar-brain-icon-20260927/README.md). + +Implementation: `de6cb7fd3032ba4dd800b0eb4e590b33bf4b8c1a`, based on `main` at `a16ab47f8a61bed19537a07d53ad1030f4d3934b`. The before screenshots use the existing naming preview at `364fe650644fa80d99c02cfed3c3360c4cc47a34`; its sidebar icon is identical to this main revision. The naming change is separately tracked in [PR #290](https://github.com/omdsh-dev/dsh-mnemon/pull/290), so the plugin description differs between these two previews. + +## Result + +The official Plugins icon uses a 1-unit stroke on a 16-unit SVG canvas. Memory System used 1.5 units, making it visibly heavier at the same size. Native, fallback and Better Sidebar icons now use 1 unit. The production change is one stroke-width value in each of the three renderers. + +After merging the naming and compatibility-documentation changes into this branch, revision `37d781b1bf89781bb420528c0b1b934de88bbda3` retains `可组合记忆 (dsh-mnemon)` and both composable-memory descriptions. The [combined preview](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-icon-weight-20260927/after-combined.png) shows the current name and the corrected icon together. Its Root tarball SHA-256 is `d69c10cb453beb1e5d4c8c38ef7728e5e370f05ada74a61977f5c1c10083ec9f`. The earlier screenshots below remain a record of the original icon-only preview; their old plugin title does not represent the merge result. + +The real WebUI uses published DSH `0.1.7-rc.2`, this branch's packed Root, sixteen unchanged published companion artifacts, and Mnemon CLI `0.2.9`. The Root tarball SHA-256 is `0404e788c5101b5d1c5d6d7c56904d6e78519f570eafd955f069dbbcfe5f7668`. The fixture isolates its profile, workspace and memory; no live model API is used. + +[DOM measurements](./measurements.json) confirm matching stroke weights in the expanded 16px and collapsed 18px native icons. Clicking the collapsed entry opens a healthy Memory System. Runtime opens normally and remains selected after a round trip through Plugins. + +| Native sidebar | Before | After | +|---|---|---| +| Expanded | [Screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-icon-weight-20260927/before-expanded.png) | [Screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-icon-weight-20260927/after-expanded.png) | +| Collapsed | [Screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-icon-weight-20260927/before-collapsed.png) | [Screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-icon-weight-20260927/after-collapsed.png) | + +[Runtime after the navigation round trip](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-icon-weight-20260927/after-runtime.png). Browser verification covers the native sidebar; the existing automated sidebar tests also cover fallback and Better Sidebar behavior. + +## Validation + +- `pnpm verify`: documentation, types, deterministic builds and all plugin suites passed. The Root suite passed 1,419 tests and skipped 9; its default-concurrency performance check took 5,311ms against a 5,000ms limit. +- `pnpm exec vitest run --dir tests --maxWorkers=2`: 1,420 passed, 9 skipped, including the unchanged performance threshold and all sidebar suites. +- `pnpm verify:headless`: 39 tools, eight representative Mnemon tools, legacy core disabling and restart passed. +- `pnpm verify:package`: packed files, public entries, types, strict package lint and export checks passed. +- `pnpm release:intent`, `pnpm verify:docs` and `git diff --check`: passed. + +The bilingual UI, getting-started, configuration and operations guides were reviewed; this visual adjustment requires no workflow or configuration changes. diff --git a/dsh/docs/pr-assets/sidebar-icon-weight-20260927/README.zh-CN.md b/dsh/docs/pr-assets/sidebar-icon-weight-20260927/README.zh-CN.md new file mode 100644 index 000000000..9ddfc9848 --- /dev/null +++ b/dsh/docs/pr-assets/sidebar-icon-weight-20260927/README.zh-CN.md @@ -0,0 +1,34 @@ +# 侧栏图标线宽 + +[English](./README.md) + +本记录对应最初的线宽调整;后续最终采用的大脑图标见[独立验收记录](../sidebar-brain-icon-20260927/README.zh-CN.md)。 + +实现提交:`de6cb7fd3032ba4dd800b0eb4e590b33bf4b8c1a`,基于最新 `main` 的 `a16ab47f8a61bed19537a07d53ad1030f4d3934b`。修复前截图来自已有命名预览 `364fe650644fa80d99c02cfed3c3360c4cc47a34`,其侧栏图标与该 main 完全相同。命名调整由独立的 [PR #290](https://github.com/omdsh-dev/dsh-mnemon/pull/290) 跟踪,因此两组预览中的插件描述不同。 + +## 结果 + +官方“插件”图标在 16 单位 SVG 画布上使用 1 单位描边;记忆系统使用 1.5 单位,同尺寸下明显更粗。原生侧栏、备用入口和 Better Sidebar 现统一为 1 单位。生产代码仅调整三个渲染位置各自的线宽值。 + +本分支合入命名调整和兼容性文档后的提交 `37d781b1bf89781bb420528c0b1b934de88bbda3` 完整保留“可组合记忆 (dsh-mnemon)”及中英文可组合记忆描述。[当前组合预览](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-icon-weight-20260927/after-combined.png)同时展示新名称和修正后的图标,Root 制品 SHA-256 为 `d69c10cb453beb1e5d4c8c38ef7728e5e370f05ada74a61977f5c1c10083ec9f`。下方旧截图保留图标单独修复时的原始记录;其中的旧插件名称不代表合并结果。 + +真实 WebUI 使用正式 DSH `0.1.7-rc.2`、本分支打包的 Root、十六个未修改的正式伴随插件制品,以及 Mnemon CLI `0.2.9`。Root 制品 SHA-256:`0404e788c5101b5d1c5d6d7c56904d6e78519f570eafd955f069dbbcfe5f7668`。夹具隔离 profile、工作区和记忆目录,不调用真实模型 API。 + +[DOM 实测](./measurements.json)确认展开时 16px、折叠时 18px 的原生图标均与官方入口线宽一致。点击折叠图标可打开状态正常的记忆系统;运行时页面正常打开,往返“插件”页后仍保留当前页面。 + +| 原生侧栏 | 修复前 | 修复后 | +|---|---|---| +| 展开 | [截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-icon-weight-20260927/before-expanded.png) | [截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-icon-weight-20260927/after-expanded.png) | +| 折叠 | [截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-icon-weight-20260927/before-collapsed.png) | [截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-icon-weight-20260927/after-collapsed.png) | + +[往返导航后的运行时页面](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-icon-weight-20260927/after-runtime.png)。浏览器验收覆盖原生侧栏;已有侧栏自动化测试同时覆盖备用入口和 Better Sidebar 行为。 + +## 验证 + +- `pnpm verify`:文档、类型、确定性构建与全部插件测试通过。Root 测试通过 1,419 项、跳过 9 项;默认并发下的一项性能检查用时 5,311ms,超过 5,000ms 阈值。 +- `pnpm exec vitest run --dir tests --maxWorkers=2`:1,420 项通过、9 项跳过,包含未调整阈值的性能检查和全部侧栏测试。 +- `pnpm verify:headless`:39 个工具、8 个代表性 Mnemon 工具、旧版核心停用及重启验证通过。 +- `pnpm verify:package`:制品文件、公开入口、类型、严格包校验及导出检查通过。 +- `pnpm release:intent`、`pnpm verify:docs` 和 `git diff --check`:通过。 + +已复核中英文 UI、快速开始、配置和运维指南;此视觉调整无需修改使用流程或配置说明。 diff --git a/dsh/docs/pr-assets/sidebar-icon-weight-20260927/measurements.json b/dsh/docs/pr-assets/sidebar-icon-weight-20260927/measurements.json new file mode 100644 index 000000000..b35b8d9d3 --- /dev/null +++ b/dsh/docs/pr-assets/sidebar-icon-weight-20260927/measurements.json @@ -0,0 +1,68 @@ +{ + "dsh": "0.1.7-rc.2", + "baseline": "364fe650644fa80d99c02cfed3c3360c4cc47a34", + "implementation": "de6cb7fd", + "viewport": { + "width": 797, + "height": 983, + "deviceScaleFactor": 2 + }, + "beforeExpanded": [ + { + "color": "rgb(249, 250, 251)", + "height": 16, + "label": "插件", + "strokeWidth": "1", + "viewBox": "0 0 16 16", + "width": 16 + }, + { + "color": "rgb(249, 250, 251)", + "height": 16, + "label": "记忆系统", + "strokeWidth": "1.5", + "viewBox": "0 0 16 16", + "width": 16 + } + ], + "afterExpanded": [ + { + "color": "rgb(249, 250, 251)", + "height": 16, + "label": "插件", + "strokeWidth": "1", + "viewBox": "0 0 16 16", + "width": 16 + }, + { + "color": "rgb(249, 250, 251)", + "height": 16, + "label": "记忆系统", + "strokeWidth": "1", + "viewBox": "0 0 16 16", + "width": 16 + } + ], + "afterCollapsed": [ + { + "height": 18, + "label": "插件", + "strokeWidth": "1", + "viewBox": "0 0 16 16", + "width": 18 + }, + { + "height": 18, + "label": "记忆系统", + "strokeWidth": "1", + "viewBox": "0 0 16 16", + "width": 18 + } + ], + "interactions": { + "collapsedEntryOpensMemory": true, + "systemStatusHealthy": true, + "runtimeTabOpens": true, + "pluginsRoundTripRetainsRuntime": true + } +} diff --git a/dsh/docs/pr-assets/sidebar-icon/README.md b/dsh/docs/pr-assets/sidebar-icon/README.md new file mode 100644 index 000000000..5f23f08a9 --- /dev/null +++ b/dsh/docs/pr-assets/sidebar-icon/README.md @@ -0,0 +1,64 @@ +# 侧栏图标回归证据 / Sidebar icon regression evidence + +本目录记录 issue [#141](https://github.com/omdsh-dev/dsh-mnemon/issues/141) 的实机验证,不替换发布截图。修复前基线是 `d6fa550`;修复代码为 `66ce4be`,已同步最新 `main` 的 `0b4a9af`。截图使用 1280 × 720 的默认浏览器视口。 + +These are live regression artifacts for issue #141, not replacement release screenshots. The original baseline is `d6fa550`; the fix is code commit `66ce4be`, rebased onto `main` at `0b4a9af`. Screenshots use the browser's default 1280 × 720 viewport. + +## 修复范围 / Scope + +原生 DSH frame 只有 `data-sidebar-collapsed`,不会添加第三方 Web UI 的 `data-dsh-frame`;旧选择器因此未应用居中和隐藏文字的规则。折叠入口现在直接响应原生标记,使用与原生图标一致的 primary label 色,并仅在折叠时把 SVG 调整到 20px,以补偿数据库图案内部留白。 + +The native DSH frame exposes `data-sidebar-collapsed` without the Web UI plugin's `data-dsh-frame`, so the previous selector missed centering and label hiding. The collapsed entry now follows the native marker, uses the native primary label color, and increases only the collapsed SVG to 20px to compensate for its internal padding. + +生产代码仅修改一个 CSS 文件,新增 9 行、删除 2 行;没有新增 JavaScript、监听器或状态。展开时保留原有 18px SVG、24px 图标容器、间距、文字和 secondary label 色。已复核中英文 UI、快速开始、配置和运维指南;现有操作与配置无需改写。 + +Production changes are limited to one CSS file: 9 additions and 2 deletions, with no JavaScript, observers, or state changes. Expanded styling retains the original 18px SVG, 24px icon container, spacing, label, and secondary label color. The English and Chinese UI, getting-started, configuration, and operations guides were reviewed; workflows and configuration need no revision. + +## 实测 / Measurements + +[measurements.json](./measurements.json) 保存稳定布局的 DOM 测量、源码与构建 SHA-256。测量在宿主折叠动画结束后进行;图案绘制尺寸包含描边估算,不把 SVG 画布大小等同于可见图案大小。 + +[measurements.json](./measurements.json) records settled DOM geometry and source/build SHA-256 hashes. Measurements wait for the host's collapse animation; painted bounds account for stroke width instead of treating SVG viewport size as artwork size. + +| 场景 / Scenario | 修复前 / Before | 修复后 / After | +| --- | --- | --- | +| 原生图标水平中心偏差 / Native horizontal center offset | +4px | 0px | +| 原生浅色图标 / Native light icon | `rgb(97, 102, 107)` | `rgb(15, 17, 21)`, matches native | +| 原生深色图标 / Native dark icon | `rgb(207, 211, 214)` | `rgb(249, 250, 251)`, matches native | +| Web UI 0.3.9 图标 / Web UI 0.3.9 icon | `rgb(169, 183, 208)` | `rgb(219, 226, 242)`, matches native | +| 折叠 / 展开 SVG / Collapsed / expanded SVG | 18px / 18px | 20px / 18px | + +修复后数据库图案绘制高度约 16.88px,邻近“新建会话”图案约 17.27px。数据库保留自身较窄的比例。原生展开入口的矩形、图标矩形、颜色和文字宽度与修复前完全一致;Web UI 展开状态也已逐项核对。 + +The fixed database artwork is approximately 16.88px high, compared with 17.27px for the neighboring New Session artwork. Its narrower database proportions are preserved. Native expanded entry/icon rectangles, color, and label width match the baseline exactly; Web UI expanded styling was also checked. + +## 截图 / Screenshots + +| 场景 / Scenario | 修复前 / Before | 修复后 / After | +| --- | --- | --- | +| 原生浅色 / Native light | ![Before native light](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-icon/before-classic-light.jpg) | ![After native light](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-icon/after-classic-light.jpg) | +| 原生深色 / Native dark | ![Before native dark](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-icon/before-classic-dark.jpg) | ![After native dark](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-icon/after-classic-dark.jpg) | +| Web UI 0.3.9 | ![Before Web UI](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-icon/before-web-0.3.9.jpg) | ![After Web UI](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-icon/after-web-0.3.9.jpg) | + +展开状态 / Expanded states: [原生浅色 / native light](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-icon/after-classic-light-expanded.jpg), [Web UI 0.3.9](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-icon/after-web-0.3.9-expanded.jpg). + +## 环境与验证 / Environment and validation + +- macOS arm64, Node.js 25.1.0, pnpm 11.19.0; npm DSH 0.1.1-rc.2, optional `@linxin666/dsh-web-all@0.3.9`, checksum-verified Mnemon CLI 0.2.6. +- `pnpm run verify`: 630 passed, 1 Windows-only smoke test skipped; 108 deterministic build files; 35 Headless tools including 5 representative Mnemon tools; 115 package files; 10 public Node entries; publint strict and attw passed. +- Browser checks cover native light/dark, decorated Web UI, collapse/expand, hover/active styling, accessible labels, and opening/returning from Memory System. Task Board round trips retain the selected memory page. No browser console errors were observed. +- The existing rc.2 dependency source-map warnings and Web UI 0.3.9 Host `session/list` startup warning are unrelated to this CSS change. No DSH or third-party package code was modified. + +所有实例使用独立的 DSH_HOME、记忆目录、工作区和随机 loopback 端口;模型仅返回本地固定文本,不调用付费服务,不访问个人记忆。截图不包含凭据或私人数据。 + +Each instance uses an isolated DSH_HOME, memory directory, workspace, and random loopback port. The model returns local fixed text, with no paid service or personal memory access. Screenshots contain no credentials or private data. + +复现 / Reproduce after building: + +```sh +pnpm run verify +node scripts/serve-web-regression.mjs --cli /path/to/test-owned/mnemon --web-ui none +node scripts/serve-web-regression.mjs --cli /path/to/test-owned/mnemon --web-ui @linxin666/dsh-web-all@0.3.9 +``` + +只创建 open PR 供审核;不合并、不发版。 / Open PR for review only; no merge or release. diff --git a/dsh/docs/pr-assets/sidebar-icon/measurements.json b/dsh/docs/pr-assets/sidebar-icon/measurements.json new file mode 100644 index 000000000..900c91cf4 --- /dev/null +++ b/dsh/docs/pr-assets/sidebar-icon/measurements.json @@ -0,0 +1,743 @@ +{ + "date": "2026-08-31", + "baselineCommit": "d6fa55027527e1483233aca3ede4ac56987cb2bd", + "baseAfterRebase": "0b4a9af", + "codeCommit": "66ce4bea1fa68784811729bbd87bc630b18568e4", + "sourceFile": "src/client/MnemonWorkspace.module.css", + "sourceSha256Before": "6eeda37aba59995c533377459efcee780d9a24fef22fcfc3928897ae30ffe0e4", + "sourceSha256After": "a343bc612dd452d0a834e1ca6850dccf2c7bf2f6f7274845495c2bc4f5ccbf49", + "clientBundleSha256": "26c0ebbf4059a8c7fda5b9799249af980c1b8530e6a8c4fe3fcd3d482b747959", + "environment": { + "dsh": "0.1.1-rc.2", + "webUi": "@linxin666/dsh-web-all@0.3.9", + "mnemonCli": "0.2.6", + "node": "25.1.0", + "pnpm": "11.19.0", + "isolated": true + }, + "checks": { + "expandedNativeMatchesBaseline": true, + "expandedWebSvgRetainedAt18px": true, + "collapsedPaintedHeightDifferenceBelowHalfPixel": true, + "taskBoardRoundTripRetainsMemoryPage": true, + "returnToConversation": true, + "browserErrors": { + "classic": [], + "web": [] + } + }, + "measurements": [ + { + "scenario": "before-classic-light-collapsed", + "accessibleName": "记忆系统", + "active": false, + "centerOffsetX": 4, + "collapsed": true, + "decoratedFrame": false, + "entryColor": "rgb(97, 102, 107)", + "entryRect": { + "bottom": 150, + "height": 36, + "left": 10, + "right": 45, + "top": 114, + "width": 35, + "x": 10, + "y": 114 + }, + "iconColor": "rgb(97, 102, 107)", + "iconRect": { + "bottom": 141, + "height": 18, + "left": 23, + "right": 41, + "top": 123, + "width": 18, + "x": 23, + "y": 123 + }, + "labelDisplay": "block", + "labelWidth": 0, + "nativeButtonRect": { + "bottom": 102, + "height": 36, + "left": 10, + "right": 46, + "top": 66, + "width": 36, + "x": 10, + "y": 66 + }, + "nativeIconColor": "rgb(15, 17, 21)", + "nativeIconRect": { + "bottom": 93, + "height": 18, + "left": 19, + "right": 37, + "top": 75, + "width": 18, + "x": 19, + "y": 75 + }, + "title": "记忆系统", + "viewport": { + "height": 720, + "width": 1280 + } + }, + { + "scenario": "before-web-0.3.9-collapsed", + "accessibleName": "记忆系统", + "active": false, + "centerOffsetX": 0, + "collapsed": true, + "decoratedFrame": true, + "entryColor": "rgb(169, 183, 208)", + "entryRect": { + "bottom": 246, + "height": 36, + "left": 10, + "right": 46, + "top": 210, + "width": 36, + "x": 10, + "y": 210 + }, + "iconColor": "rgb(169, 183, 208)", + "iconRect": { + "bottom": 237, + "height": 18, + "left": 19, + "right": 37, + "top": 219, + "width": 18, + "x": 19, + "y": 219 + }, + "labelDisplay": "none", + "labelWidth": 0, + "nativeButtonRect": { + "bottom": 102, + "height": 36, + "left": 10, + "right": 46, + "top": 66, + "width": 36, + "x": 10, + "y": 66 + }, + "nativeIconColor": "rgb(219, 226, 242)", + "nativeIconRect": { + "bottom": 93, + "height": 18, + "left": 19, + "right": 37, + "top": 75, + "width": 18, + "x": 19, + "y": 75 + }, + "title": "记忆系统", + "viewport": { + "height": 720, + "width": 1280 + } + }, + { + "scenario": "before-classic-light-expanded", + "accessibleName": "记忆系统", + "active": false, + "centerOffsetX": -82, + "collapsed": false, + "decoratedFrame": false, + "entryColor": "rgb(97, 102, 107)", + "entryRect": { + "bottom": 156, + "height": 36, + "left": 12, + "right": 268, + "top": 120, + "width": 256, + "x": 12, + "y": 120 + }, + "iconColor": "rgb(97, 102, 107)", + "iconRect": { + "bottom": 147, + "height": 18, + "left": 25, + "right": 43, + "top": 129, + "width": 18, + "x": 25, + "y": 129 + }, + "labelDisplay": "block", + "labelWidth": 52, + "nativeButtonRect": { + "bottom": 112, + "height": 38, + "left": 14, + "right": 266, + "top": 74, + "width": 252, + "x": 14, + "y": 74 + }, + "nativeIconColor": "rgb(15, 17, 21)", + "nativeIconRect": { + "bottom": 100, + "height": 14, + "left": 109, + "right": 123, + "top": 86, + "width": 14, + "x": 109, + "y": 86 + }, + "title": "记忆系统", + "viewport": { + "height": 720, + "width": 1280 + } + }, + { + "scenario": "before-classic-dark-collapsed", + "accessibleName": "记忆系统", + "active": false, + "centerOffsetX": 4, + "collapsed": true, + "decoratedFrame": false, + "entryColor": "rgb(207, 211, 214)", + "entryRect": { + "bottom": 150, + "height": 36, + "left": 10, + "right": 45, + "top": 114, + "width": 35, + "x": 10, + "y": 114 + }, + "iconColor": "rgb(207, 211, 214)", + "iconRect": { + "bottom": 141, + "height": 18, + "left": 23, + "right": 41, + "top": 123, + "width": 18, + "x": 23, + "y": 123 + }, + "labelDisplay": "block", + "labelWidth": 0, + "nativeButtonRect": { + "bottom": 102, + "height": 36, + "left": 10, + "right": 46, + "top": 66, + "width": 36, + "x": 10, + "y": 66 + }, + "nativeIconColor": "rgb(249, 250, 251)", + "nativeIconRect": { + "bottom": 93, + "height": 18, + "left": 19, + "right": 37, + "top": 75, + "width": 18, + "x": 19, + "y": 75 + }, + "title": "记忆系统", + "viewport": { + "height": 720, + "width": 1280 + } + }, + { + "scenario": "after-web-0.3.9-expanded", + "accessibleName": "记忆系统", + "active": false, + "centerOffsetX": -82, + "collapsed": false, + "decoratedFrame": true, + "entryColor": "rgb(169, 183, 208)", + "entryRect": { + "bottom": 228, + "height": 36, + "left": 12, + "right": 268, + "top": 192, + "width": 256, + "x": 12, + "y": 192 + }, + "iconColor": "rgb(169, 183, 208)", + "iconRect": { + "bottom": 219, + "height": 18, + "left": 25, + "right": 43, + "top": 201, + "width": 18, + "x": 25, + "y": 201 + }, + "labelDisplay": "block", + "labelWidth": 52, + "nativeButtonRect": { + "bottom": 112, + "height": 38, + "left": 14, + "right": 266, + "top": 74, + "width": 252, + "x": 14, + "y": 74 + }, + "nativeIconColor": "rgb(219, 226, 242)", + "nativeIconRect": { + "bottom": 100, + "height": 14, + "left": 109, + "right": 123, + "top": 86, + "width": 14, + "x": 109, + "y": 86 + }, + "title": "记忆系统", + "viewport": { + "height": 720, + "width": 1280 + } + }, + { + "scenario": "after-web-0.3.9-collapsed", + "accessibleName": "记忆系统", + "active": false, + "centerOffsetX": 0, + "collapsed": true, + "decoratedFrame": true, + "entryColor": "rgb(219, 226, 242)", + "entryRect": { + "bottom": 246, + "height": 36, + "left": 10, + "right": 46, + "top": 210, + "width": 36, + "x": 10, + "y": 210 + }, + "iconColor": "rgb(219, 226, 242)", + "iconRect": { + "bottom": 238, + "height": 20, + "left": 18, + "right": 38, + "top": 218, + "width": 20, + "x": 18, + "y": 218 + }, + "labelDisplay": "none", + "labelWidth": 0, + "nativeButtonRect": { + "bottom": 102, + "height": 36, + "left": 10, + "right": 46, + "top": 66, + "width": 36, + "x": 10, + "y": 66 + }, + "nativeIconColor": "rgb(219, 226, 242)", + "nativeIconRect": { + "bottom": 93, + "height": 18, + "left": 19, + "right": 37, + "top": 75, + "width": 18, + "x": 19, + "y": 75 + }, + "title": "记忆系统", + "viewport": { + "height": 720, + "width": 1280 + } + }, + { + "scenario": "after-classic-dark-collapsed", + "accessibleName": "记忆系统", + "active": false, + "centerOffsetX": 0, + "collapsed": true, + "decoratedFrame": false, + "entryColor": "rgb(249, 250, 251)", + "entryRect": { + "bottom": 150, + "height": 36, + "left": 10, + "right": 46, + "top": 114, + "width": 36, + "x": 10, + "y": 114 + }, + "iconColor": "rgb(249, 250, 251)", + "iconRect": { + "bottom": 142, + "height": 20, + "left": 18, + "right": 38, + "top": 122, + "width": 20, + "x": 18, + "y": 122 + }, + "labelDisplay": "none", + "labelWidth": 0, + "nativeButtonRect": { + "bottom": 102, + "height": 36, + "left": 10, + "right": 46, + "top": 66, + "width": 36, + "x": 10, + "y": 66 + }, + "nativeIconColor": "rgb(249, 250, 251)", + "nativeIconRect": { + "bottom": 93, + "height": 18, + "left": 19, + "right": 37, + "top": 75, + "width": 18, + "x": 19, + "y": 75 + }, + "title": "记忆系统", + "viewport": { + "height": 720, + "width": 1280 + } + }, + { + "scenario": "after-classic-dark-hover", + "accessibleName": "记忆系统", + "active": false, + "centerOffsetX": 0, + "collapsed": true, + "decoratedFrame": false, + "entryColor": "rgb(249, 250, 251)", + "entryRect": { + "bottom": 150, + "height": 36, + "left": 10, + "right": 46, + "top": 114, + "width": 36, + "x": 10, + "y": 114 + }, + "iconColor": "rgb(249, 250, 251)", + "iconRect": { + "bottom": 142, + "height": 20, + "left": 18, + "right": 38, + "top": 122, + "width": 20, + "x": 18, + "y": 122 + }, + "labelDisplay": "none", + "labelWidth": 0, + "nativeButtonRect": { + "bottom": 102, + "height": 36, + "left": 10, + "right": 46, + "top": 66, + "width": 36, + "x": 10, + "y": 66 + }, + "nativeIconColor": "rgb(249, 250, 251)", + "nativeIconRect": { + "bottom": 93, + "height": 18, + "left": 19, + "right": 37, + "top": 75, + "width": 18, + "x": 19, + "y": 75 + }, + "title": "记忆系统", + "viewport": { + "height": 720, + "width": 1280 + } + }, + { + "scenario": "after-classic-dark-active", + "accessibleName": "记忆系统", + "active": true, + "centerOffsetX": 0, + "collapsed": true, + "decoratedFrame": false, + "entryColor": "rgb(249, 250, 251)", + "entryRect": { + "bottom": 150, + "height": 36, + "left": 10, + "right": 46, + "top": 114, + "width": 36, + "x": 10, + "y": 114 + }, + "iconColor": "rgb(249, 250, 251)", + "iconRect": { + "bottom": 142, + "height": 20, + "left": 18, + "right": 38, + "top": 122, + "width": 20, + "x": 18, + "y": 122 + }, + "labelDisplay": "none", + "labelWidth": 0, + "nativeButtonRect": { + "bottom": 102, + "height": 36, + "left": 10, + "right": 46, + "top": 66, + "width": 36, + "x": 10, + "y": 66 + }, + "nativeIconColor": "rgb(249, 250, 251)", + "nativeIconRect": { + "bottom": 93, + "height": 18, + "left": 19, + "right": 37, + "top": 75, + "width": 18, + "x": 19, + "y": 75 + }, + "title": "记忆系统", + "viewport": { + "height": 720, + "width": 1280 + } + }, + { + "scenario": "after-classic-light-expanded", + "accessibleName": "记忆系统", + "active": false, + "centerOffsetX": -82, + "collapsed": false, + "decoratedFrame": false, + "entryColor": "rgb(97, 102, 107)", + "entryRect": { + "bottom": 156, + "height": 36, + "left": 12, + "right": 268, + "top": 120, + "width": 256, + "x": 12, + "y": 120 + }, + "iconColor": "rgb(97, 102, 107)", + "iconRect": { + "bottom": 147, + "height": 18, + "left": 25, + "right": 43, + "top": 129, + "width": 18, + "x": 25, + "y": 129 + }, + "labelDisplay": "block", + "labelWidth": 52, + "nativeButtonRect": { + "bottom": 112, + "height": 38, + "left": 14, + "right": 266, + "top": 74, + "width": 252, + "x": 14, + "y": 74 + }, + "nativeIconColor": "rgb(15, 17, 21)", + "nativeIconRect": { + "bottom": 100, + "height": 14, + "left": 109, + "right": 123, + "top": 86, + "width": 14, + "x": 109, + "y": 86 + }, + "title": "记忆系统", + "viewport": { + "height": 720, + "width": 1280 + } + }, + { + "scenario": "after-classic-light-collapsed", + "accessibleName": "记忆系统", + "active": false, + "centerOffsetX": 0, + "collapsed": true, + "decoratedFrame": false, + "entryColor": "rgb(15, 17, 21)", + "entryRect": { + "bottom": 150, + "height": 36, + "left": 10, + "right": 46, + "top": 114, + "width": 36, + "x": 10, + "y": 114 + }, + "iconColor": "rgb(15, 17, 21)", + "iconRect": { + "bottom": 142, + "height": 20, + "left": 18, + "right": 38, + "top": 122, + "width": 20, + "x": 18, + "y": 122 + }, + "labelDisplay": "none", + "labelWidth": 0, + "nativeButtonRect": { + "bottom": 102, + "height": 36, + "left": 10, + "right": 46, + "top": 66, + "width": 36, + "x": 10, + "y": 66 + }, + "nativeIconColor": "rgb(15, 17, 21)", + "nativeIconRect": { + "bottom": 93, + "height": 18, + "left": 19, + "right": 37, + "top": 75, + "width": 18, + "x": 19, + "y": 75 + }, + "title": "记忆系统", + "viewport": { + "height": 720, + "width": 1280 + }, + "glyphSizes": [ + { + "name": "记忆系统", + "svgWidth": 20, + "svgHeight": 20, + "paintedWidth": 14.375, + "paintedHeight": 16.875 + }, + { + "name": "新建会话", + "svgWidth": 18, + "svgHeight": 18, + "paintedWidth": 17.27164077758789, + "paintedHeight": 17.27166748046875 + } + ] + }, + { + "scenario": "after-web-0.3.9-active-collapsed", + "accessibleName": "记忆系统", + "active": true, + "centerOffsetX": 0, + "collapsed": true, + "decoratedFrame": true, + "entryColor": "rgb(219, 226, 242)", + "entryRect": { + "bottom": 246, + "height": 36, + "left": 10, + "right": 46, + "top": 210, + "width": 36, + "x": 10, + "y": 210 + }, + "iconColor": "rgb(219, 226, 242)", + "iconRect": { + "bottom": 238, + "height": 20, + "left": 18, + "right": 38, + "top": 218, + "width": 20, + "x": 18, + "y": 218 + }, + "labelDisplay": "none", + "labelWidth": 0, + "nativeButtonRect": { + "bottom": 102, + "height": 36, + "left": 10, + "right": 46, + "top": 66, + "width": 36, + "x": 10, + "y": 66 + }, + "nativeIconColor": "rgb(219, 226, 242)", + "nativeIconRect": { + "bottom": 93, + "height": 18, + "left": 19, + "right": 37, + "top": 75, + "width": 18, + "x": 19, + "y": 75 + }, + "title": "记忆系统", + "viewport": { + "height": 720, + "width": 1280 + } + } + ] +} diff --git a/dsh/docs/pr-assets/sidebar-native-20260926/README.md b/dsh/docs/pr-assets/sidebar-native-20260926/README.md new file mode 100644 index 000000000..5af3233aa --- /dev/null +++ b/dsh/docs/pr-assets/sidebar-native-20260926/README.md @@ -0,0 +1,49 @@ +# Native Sidebar and bundle verification + +[中文](./README.zh-CN.md) + +Base: `6d79c663262f71c4309cd6db5bd70b204abb143d` (v0.5.15). Tested implementation: `8d3a16ec`. The fixture uses Node 24.20.0, published DSH 0.1.7-rc.2, all seventeen packed Mnemon packages, and the official Mnemon CLI 0.2.9. [Verification record](./verification.json) records the artifact hashes and checks. + +## Native navigation + +On main, Memory System inserted its own row outside DSH's native panel list. Its typography and spacing differed from Plugins, and clicking Plugins could leave the memory overlay covering the selected page. + +The fix contributes an icon and label to `sidebar.panellist` and a matching `main` seat, using `layout.selectPanel`. DSH owns the button, typography, tooltip, selection, and collapsed layout. The shell-owned Source tree is retained through a portal into that seat, preserving editor state and Source render authority. Better Sidebar and replacement-layout fallback remain covered. + +| Before | After | +|---|---| +| [Default: memory overlay remains over Plugins](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-native-20260926/before-default-panel-switch.jpg) | [Default: Plugins becomes the visible page](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-native-20260926/after-default-plugin-switch.jpg) | +| [Maid Atelier: separate navigation styles and competing selection](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-native-20260926/before-maid-panel-switch.jpg) | [Maid Atelier: native navigation and accessible page header](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-native-20260926/after-maid-memory.jpg) | + +The [measured navigation geometry](./native-alignment.json) confirms equal native row font/line-height, padding, label origin, and icon sizing. Expanded rows use 14 px type, 22 px line-height, 7 px / 8 px padding, and the same label origin. Collapsed rows are 36 × 36 px with 18 px icons. The native main seat reserves `--dsh-frame-top-clearance` once; desktop window chrome is not emulated in the Web acceptance. + +Real WebUI checks passed for Plugins / Memory switching, return to chat, New Session, collapsed navigation, language switching, and state retention. Runtime add/edit persisted, Documents create/search/read succeeded, and a real CLI write was recalled through the WebUI ([screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-native-20260926/after-native-cli-recall.jpg)). A real native `mnemon_status` tool also completed through the conversation. Only model choices are scripted locally; this does not evaluate model quality or external Provider services. + +Maid Atelier 0.3.2 was loaded through the official marketplace's skin-center / market plugins 0.4.2 in disposable storage. Its active-skin configuration was selected locally because the skin center's own Apply persistence failed in this environment. No personal DSH profile or memory was used. + +## Bundle diagnostic and isolated upstream candidate + +Published DSH 0.1.7-rc.2 lists the native `cordis:group` container as an off component even though its children run. Its inventory deliberately excludes groups, so trying the displayed switch returns `unknown-plugin` ([before](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-native-20260926/before-bundle-toggle-error.jpg)). Mnemon's stable group is necessary to preserve the legacy core disable gate and safe reloads; deleting its ID produced duplicate instances and was rejected. + +The committed lifecycle fixture checks the public core target, independent component settings, all seventeen activation sentinels, reload/restart, whole-bundle toggles, and private Provider teardown. It exercises published Loader/manager contracts; real Source behavior is separately covered by packed Headless and WebUI checks. + +An isolated DSH plugin-manager candidate filters group containers from the displayed component list while retaining declared IDs and child rows. It is **not part of Mnemon's runtime dependencies and has not been released or submitted upstream**. The [patch](./upstream-fix.patch) and [feedback draft](./upstream-discussion-draft.md) are retained for review. Its separate disposable WebUI showed eight running components, supported Documents off/on and whole-bundle off/on, and passed the stricter declared-row lifecycle check ([candidate screenshot](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-native-20260926/upstream-candidate-group-fixed.jpg)). This candidate result is not a claim that official DSH has already fixed the defect. + +A separate, explicitly installed manager-adapter bundle is technically possible through the public `PluginManager.listBundles()` method. An rc.2 service-level probe passed the same lifecycle matrix, but it replaces the Host-wide management service and needs its own version/configuration maintenance. Putting that adapter inside Mnemon makes Mnemon management-required and prevents bundle disable. No adapter package is added by this PR; it has not received WebUI acceptance. + +## Reproduction and checks + +Build and pack Root plus all sixteen plugins using the repository's artifact workflow, then use an empty disposable state directory: + +```sh +MNEMON_CLI_PATH=/absolute/path/to/mnemon node scripts/serve-sidebar-bundle-reproduction.mjs \ + --profile /absolute/path/to/published-dsh-rc2-install \ + --artifacts /absolute/path/to/seventeen-tarballs \ + --state /absolute/path/to/empty-disposable-state +``` + +Optional `--skin-center` and `--skin-market` arguments accept the official plugin tarballs. Authentication URLs remain in the mode-600 private fixture record, not this evidence. + +Passed: `pnpm verify` (1,390 Root tests, eight unrelated opt-in skips), `pnpm verify:plugins` (sixteen external plugin repositories and seventeen artifacts), native CLI integration, two-Native-namespace Runtime archival, and the published floor / rc bundle lifecycle matrix. The final CSS changes also passed 49 focused tests and a complete repeat of `verify`. The Root package is 1,344,131 unpacked bytes, below the unchanged 1,376,000-byte budget. Final installed runtime files were compared against all seventeen recorded tarballs. + +The Runtime / Documents / Memory Spaces frame consistency fix is an independent PR and is not included here. Windows and a real Electron window were not exercised in this run. diff --git a/dsh/docs/pr-assets/sidebar-native-20260926/README.zh-CN.md b/dsh/docs/pr-assets/sidebar-native-20260926/README.zh-CN.md new file mode 100644 index 000000000..fb642012e --- /dev/null +++ b/dsh/docs/pr-assets/sidebar-native-20260926/README.zh-CN.md @@ -0,0 +1,49 @@ +# 原生 Sidebar 与 bundle 验证 + +[English](./README.md) + +基线:`6d79c663262f71c4309cd6db5bd70b204abb143d`(v0.5.15)。实测实现:`8d3a16ec`。环境为 Node 24.20.0、正式发布的 DSH 0.1.7-rc.2、全部 17 个 Mnemon tarball,以及官方 Mnemon CLI 0.2.9。[验证记录](./verification.json) 包含制品哈希和检查结果。 + +## 原生导航 + +main 中,记忆系统在 DSH 原生面板列表外插入自己的入口,字号和间距与“插件”不同;点击“插件”后,记忆浮层还可能遮住已选页面。 + +修复通过 `sidebar.panellist` 提供图标和标签,通过配套 `main` 挂载位及 `layout.selectPanel` 导航。按钮、字体、提示、选中状态和折叠布局都由 DSH 管理。Source 页面树仍由 shell 持有,通过 portal 进入主面板,保留编辑状态与 Source 渲染归属。Better Sidebar 与替代布局的降级路径仍有覆盖。 + +| 修复前 | 修复后 | +|---|---| +| [默认主题:记忆浮层仍遮住插件页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-native-20260926/before-default-panel-switch.jpg) | [默认主题:正确显示插件页](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-native-20260926/after-default-plugin-switch.jpg) | +| [Maid Atelier:导航样式不同、选中状态冲突](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-native-20260926/before-maid-panel-switch.jpg) | [Maid Atelier:原生导航与可用的页面标题](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-native-20260926/after-maid-memory.jpg) | + +[导航测量](./native-alignment.json) 确认两项具有相同字号、行高、内边距、文字起点和图标尺寸。展开时均为 14 px 字号、22 px 行高、7 px / 8 px 内边距;折叠时按钮均为 36 × 36 px,图标为 18 px。原生主面板仅预留一次 `--dsh-frame-top-clearance`;本次 Web 验收没有模拟桌面窗口控件。 + +真实 WebUI 已通过插件/记忆切换、返回对话、新建会话、折叠导航、语言切换及状态保留。运行时记忆新增和编辑可持久化,档案创建、检索和阅读成功,真实 CLI 写入能通过 WebUI 召回([截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-native-20260926/after-native-cli-recall.jpg))。对话中的原生 `mnemon_status` 工具也已完成。仅模型选择由本地脚本固定;这些结果不代表模型质量或外部 Provider 服务验收。 + +Maid Atelier 0.3.2 通过官方市场的 skin-center / market 0.4.2 插件加载,使用独立临时存储。该环境中皮肤中心自身的 Apply 持久化失败,因此通过本地皮肤配置选择已安装皮肤。没有使用个人 DSH 配置或记忆。 + +## 分组诊断与隔离的上游候选补丁 + +正式 DSH 0.1.7-rc.2 把 `cordis:group` 原生容器显示为已关闭组件,但它的子插件实际正常运行。运行清单有意排除 group,因此点击该开关会返回 `unknown-plugin`([修复前](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-native-20260926/before-bundle-toggle-error.jpg))。Mnemon 的稳定分组用于保留旧版核心禁用语义和安全重载;删除分组 ID 会产生重复实例,已排除。 + +提交的生命周期夹具覆盖旧版核心配置目标、子组件独立设置、17 个激活哨兵、重载/重启、整包开关和私有 Provider 释放。它验证正式 Loader/管理器契约;真实 Source 行为由另行执行的制品 Headless 和 WebUI 验证负责。 + +隔离构建的 DSH plugin-manager 候选补丁在展示列表时过滤 group 容器,保留声明 ID 和子组件。它**没有加入 Mnemon 的运行依赖,尚未发布,也未提交上游**。[补丁](./upstream-fix.patch) 和[反馈草稿](./upstream-discussion-draft.md) 保留供审阅。单独临时 WebUI 显示 8 个组件全部运行,档案组件及整包关闭/开启均正常,更严格的声明行生命周期检查也通过([候选环境截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-native-20260926/upstream-candidate-group-fixed.jpg))。这不代表官方 DSH 已经修复。 + +通过公开的 `PluginManager.listBundles()` 方法,技术上可以单独安装一个管理适配 bundle。rc.2 服务层探针已通过同样的生命周期矩阵,但它会替换 Host 全局管理服务,需要单独维护版本与配置兼容。把适配器放进 Mnemon 本身会使 Mnemon 变成不可停用的管理必需组件。本 PR 未新增适配包,该方案也尚未进行 WebUI 验收。 + +## 复现与检查 + +按仓库制品流程构建和打包 Root 与全部 16 个插件,再指定空的临时状态目录: + +```sh +MNEMON_CLI_PATH=/absolute/path/to/mnemon node scripts/serve-sidebar-bundle-reproduction.mjs \ + --profile /absolute/path/to/published-dsh-rc2-install \ + --artifacts /absolute/path/to/seventeen-tarballs \ + --state /absolute/path/to/empty-disposable-state +``` + +可选 `--skin-center` 和 `--skin-market` 接受官方插件 tarball。认证地址仅保存在权限为 600 的临时私有记录中,不进入证据。 + +已通过 `pnpm verify`(Root 1,390 项,8 项无关的可选集成测试跳过)、`pnpm verify:plugins`(16 个外部插件仓库与 17 个制品)、真实 CLI 集成、双 Native 命名空间的运行时归档,以及正式最低版本/rc 的 bundle 生命周期矩阵。最终 CSS 调整另通过 49 项定向测试及完整 `verify` 重跑。Root 解包体积 1,344,131 字节,低于未调整的 1,376,000 字节上限。最终安装环境的运行文件已逐一对照全部 17 个 tarball。 + +Runtime/Documents/Memory Spaces 的框架一致性另行提交独立 PR,不包含在本修复中。本轮未运行 Windows 或真实 Electron 窗口验收。 diff --git a/dsh/docs/pr-assets/sidebar-native-20260926/native-alignment.json b/dsh/docs/pr-assets/sidebar-native-20260926/native-alignment.json new file mode 100644 index 000000000..67c67e9ee --- /dev/null +++ b/dsh/docs/pr-assets/sidebar-native-20260926/native-alignment.json @@ -0,0 +1,56 @@ +{ + "rows": [ + { + "children": [ + { + "height": 16, + "tag": "SPAN", + "width": 16, + "x": 22 + }, + { + "height": 22, + "tag": "SPAN", + "width": 47.0390625, + "x": 46 + } + ], + "current": null, + "fontSize": "14px", + "height": 36, + "label": "Plugins", + "lineHeight": "22px", + "padding": "7px 8px", + "width": 252, + "x": 14 + }, + { + "children": [ + { + "height": 16, + "tag": "SPAN", + "width": 16, + "x": 22 + }, + { + "height": 22, + "tag": "SPAN", + "width": 104.4140625, + "x": 46 + } + ], + "current": "page", + "fontSize": "14px", + "height": 36, + "label": "Memory System", + "lineHeight": "22px", + "padding": "7px 8px", + "width": 252, + "x": 14 + } + ], + "viewport": { + "height": 983, + "width": 1055 + } +} diff --git a/dsh/docs/pr-assets/sidebar-native-20260926/upstream-discussion-draft.md b/dsh/docs/pr-assets/sidebar-native-20260926/upstream-discussion-draft.md new file mode 100644 index 000000000..bf624fcf6 --- /dev/null +++ b/dsh/docs/pr-assets/sidebar-native-20260926/upstream-discussion-draft.md @@ -0,0 +1,81 @@ +# Native group containers appear as unusable bundle component switches + +Local draft only; no upstream post or pull request has been submitted. + +DSH `0.1.7-rc.2` shows a named native `cordis:group` as a disabled component on the Plugins bundle detail page, even though the group's plugins are active. Clicking that switch returns `unknown-plugin` (Chinese UI: `组件启用失败:找不到该插件`). The affected Mnemon Starter shows nine components: eight real plugins and one internal `mnemon-bundle` group. + +This reproduces with the published npm runtime on macOS and Node `24.20.0`. Current upstream master is the rc.2 release commit [`477b4f420553e8a52c2fbccc464d7561b239c443`](https://github.com/deepseek-ai/deepseek-harness/commit/477b4f420553e8a52c2fbccc464d7561b239c443), which has the same behavior. + +## Reproduction + +Create a local bundle with these files, install it into a disposable Web profile with `dsh plugin --profile web add /absolute/path/to/grouped-demo`, and inspect its Plugins detail page. + +`package.json`: + +```json +{ + "name": "grouped-demo", + "version": "1.0.0", + "type": "module", + "dsh": { "bundle": { "patch": "./cordis.patch.yml" } } +} +``` + +`cordis.patch.yml`: + +```yaml +- insert: + - id: demo-group + name: cordis:group + group: true + config: + - id: demo-child + name: ./plugin.mjs +``` + +`plugin.mjs`: + +```js +export function apply(ctx) { + ctx.provide('groupedDemo', true) +} +``` + +Expected: the bundle lists the manageable `demo-child` plugin. The group still owns its children and applies inherited configuration/disable behavior. + +Actual: the bundle also lists `demo-group`. That row has no corresponding manageable plugin and its switch fails. Its apparent off state does not establish that the group's children are inactive. + +## Cause and proposed fix + +[`readPluginInventory`](https://github.com/deepseek-ai/deepseek-harness/blob/477b4f420553e8a52c2fbccc464d7561b239c443/packages/host/plugin-inventory/src/index.ts#L86) deliberately excludes entries with `options.group`. [`declaredRows`](https://github.com/deepseek-ai/deepseek-harness/blob/477b4f420553e8a52c2fbccc464d7561b239c443/packages/boot/plugin-manager/src/index.ts#L645) includes those same native containers in `BundleInfo.rows`, so the page receives a component that `setPluginEnabled` cannot find. + +The proposed fix excludes group containers from the displayed bundle rows while continuing to traverse their children. It retains every inserted row ID, including groups, when classifying external overrides; a bundle patch targeting its own group must not become an external override. + +```diff + const rows: BundleRowInfo[] = [] ++const declared = new Set() + const packages = this.ctx.get('pluginPackages') + for (const row of flatten(composeEntries([patches.filter(item => item.insert !== undefined)]))) { + if (typeof row.id !== 'string' || typeof row.name !== 'string') continue ++ declared.add(row.id) ++ if (row.group) continue + // Existing live entry and display metadata projection. + } +-const declared = new Set(rows.map(row => row.rowId)) +``` + +The fix changes the inventory projection only. Stable IDs, nested patch addressing, disabled expressions, child ownership, bundle selection and component persistence remain with the existing Loader and manager. Flattening the Mnemon composition would leave enabled children waiting for the disabled core. Removing the group ID also failed a live-reload lifecycle check, so neither workaround is included. + +## Local verification + +- A new test in `packages/boot/plugin-manager/tests/manager.spec.ts` failed before the fix because both a group and its nested group appeared in component rows. After the fix, the test verifies selected and deselected bundle rows, a disabled child, real child off/on, bundle teardown, and correct external override classification. +- `pnpm exec vitest run packages/boot/plugin-manager/tests/manager.spec.ts packages/host/plugin-inventory/tests/inventory.spec.ts --maxWorkers=1`: all 88 tests passed. +- Focused V8 coverage of `packages/boot/plugin-manager/src/index.ts`: all 81 owning tests passed, with 100% statements, branches, functions and lines. +- The manager's TypeScript dependency closure compiled, its own runtime bundle built through the repository's normal tsdown configuration, and focused lint passed. +- `pnpm run doc-sync`: all 42 documentation checks passed, including the updated bilingual README pair. +- The existing real WebUI fixture now nests its three plugins under a stable native group; the expected component output is unchanged. A separately isolated official rc.2 installation with only the locally built manager candidate was also exercised through the real WebUI with all 17 packed Mnemon artifacts: the list showed eight real components, Documents could be disabled and re-enabled with its page removed and restored, and toggling the whole bundle off/on changed all eight components to off and back to running. The strict bundle-row diagnostic and the complete lifecycle/restart fixture passed against that candidate. +- Mnemon's separate regression passes against published DSH `0.1.5-rc.1` and `0.1.7-rc.2`: Source/Strategy activation sentinel/core/bundle manager toggles, restart persistence, independent component choices, literal and expression-based legacy core flags, private Provider teardown and duplicate-instance checks. It does not alter the installed DSH packages. + +The local candidate package retains the version `0.1.7-rc.2`; it is not a published upstream release. Its SHA-256 is `bf06f067cf056d71e20a422d3d40957f88d26bbc02a3714cc738c94361ef8513`. + +Implementation and review used OpenAI Codex with GPT-6 Astra. diff --git a/dsh/docs/pr-assets/sidebar-native-20260926/upstream-fix.patch b/dsh/docs/pr-assets/sidebar-native-20260926/upstream-fix.patch new file mode 100644 index 000000000..37dbf5895 --- /dev/null +++ b/dsh/docs/pr-assets/sidebar-native-20260926/upstream-fix.patch @@ -0,0 +1,157 @@ +diff --git a/apps/web/tests/fixtures/plugins/fixture-bundle/cordis.patch.yml b/apps/web/tests/fixtures/plugins/fixture-bundle/cordis.patch.yml +index 8038d9e..e3bf411 100644 +--- a/apps/web/tests/fixtures/plugins/fixture-bundle/cordis.patch.yml ++++ b/apps/web/tests/fixtures/plugins/fixture-bundle/cordis.patch.yml +@@ -1,8 +1,12 @@ + # Web e2e fixture bundle with independently localized plugin exports. + - insert: +- - id: fixture-row +- name: '@fixture/bundle' +- - id: fixture-search +- name: '@fixture/bundle/search' +- - id: fixture-review +- name: '@fixture/bundle/review' ++ - id: fixture-group ++ name: cordis:group ++ group: true ++ config: ++ - id: fixture-row ++ name: '@fixture/bundle' ++ - id: fixture-search ++ name: '@fixture/bundle/search' ++ - id: fixture-review ++ name: '@fixture/bundle/review' +diff --git a/packages/boot/plugin-manager/README.i18n.yaml b/packages/boot/plugin-manager/README.i18n.yaml +index 034b9ef..8689958 100644 +--- a/packages/boot/plugin-manager/README.i18n.yaml ++++ b/packages/boot/plugin-manager/README.i18n.yaml +@@ -15,8 +15,8 @@ + en: 87679c51aa3693f5 + zh: 11684a48a5806afb + /deepseek-ai-dsh-plugin-manager/use-this-package: +- en: b8d7061ee289b2a0 +- zh: 49aba7df4761fb23 ++ en: df0c92f60dc1c945 ++ zh: 68981ea3b89ea5a6 + /deepseek-ai-dsh-plugin-manager/use-this-package/version-compatibility-and-exemptions: + en: 920c0acb3b9feaf3 + zh: ba6e832a256880a7 +diff --git a/packages/boot/plugin-manager/README.md b/packages/boot/plugin-manager/README.md +index ca470bc..6adb934 100644 +--- a/packages/boot/plugin-manager/README.md ++++ b/packages/boot/plugin-manager/README.md +@@ -41,7 +41,7 @@ A plugin toggle updates only `disabled` in the last matching override in the pro + + A selected bundle that cannot load remains in `listBundles` with an `error`; `enabled` records the saved selection, not successful loading. The plugin page shows the error and allows deselection. A broken bundle cannot be enabled. Management bundles remain protected if their files become unreadable. + +-`listBundles` includes optional display `meta` for each bundle and its declared plugin rows, including disabled bundles. The Client selects a language from these values. The separate `description` field is the bundle's raw `package.json.description`; a metadata diagnostic does not block management. The `plugin_manager` tool omits UI display metadata from list results. ++`listBundles` includes optional display `meta` for each bundle and its declared plugin rows, including disabled bundles. Native group containers are omitted; their plugin children remain listed, and patches to a bundle's own groups are not external overrides. The Client selects a language from these values. The separate `description` field is the bundle's raw `package.json.description`; a metadata diagnostic does not block management. The `plugin_manager` tool omits UI display metadata from list results. + + `inspect(spec, options)` reads what a spec names before anything installs: a registry name is asked of the registry through `pnpm view`, run in the profile directory so the same proxy and authentication settings apply as to the install; an absolute path has its `package.json` read; a git address or tarball answers only its form and the `host` it is fetched from. The answer carries the name, version, description, whether the package declares a bundle, and the `registry` that answered, or a `problem`: `invalid-spec`, `already-installed`, `not-found`, `not-a-package`, `not-a-bundle`, `network`, or `unknown`, with the `registries` asked. A caller's `signal` or `inspectTimeoutMs` ends the lookup. + +diff --git a/packages/boot/plugin-manager/README.zh.md b/packages/boot/plugin-manager/README.zh.md +index 24f9351..e734a1a 100644 +--- a/packages/boot/plugin-manager/README.zh.md ++++ b/packages/boot/plugin-manager/README.zh.md +@@ -41,7 +41,7 @@ kind: "package-reference" + + 已选择但无法加载的组合包仍会出现在 `listBundles` 中,并携带 `error`;`enabled` 表示保存的选择,不代表加载成功。插件页面显示错误并允许取消选择。损坏的组合包无法启用。管理组合包的文件变得不可读后仍受保护。 + +-`listBundles` 为各组合包及其声明的插件行提供可选的展示 `meta`,包括已禁用的组合包。Client 从这些值中选择语言。单独的 `description` 字段是该组合包原始的 `package.json.description`;元信息诊断不会阻止管理操作。`plugin_manager` 工具的列表结果不包含 UI 展示元信息。 ++`listBundles` 为各组合包及其声明的插件行提供可选的展示 `meta`,包括已禁用的组合包。原生 group 容器不会列出,其插件子项仍会列出;对组合包自身 group 的 patch 不属于外部覆盖。Client 从这些值中选择语言。单独的 `description` 字段是该组合包原始的 `package.json.description`;元信息诊断不会阻止管理操作。`plugin_manager` 工具的列表结果不包含 UI 展示元信息。 + + `inspect(spec, options)` 在任何东西安装之前读出 spec 指向什么:注册表包名通过 `pnpm view` 询问注册表,在 profile 目录中运行,因而与安装使用同样的代理与认证设置;绝对路径读取其 `package.json`;git 地址或 tarball 只答复自己的形式和它被拉取的 `host`。答复携带名称、版本、描述、该包是否声明组合包,以及作答的 `registry`,否则给出 `problem`:`invalid-spec`、`already-installed`、`not-found`、`not-a-package`、`not-a-bundle`、`network` 或 `unknown`,并附上问过的 `registries`。调用方的 `signal` 或 `inspectTimeoutMs` 会结束查询。 + +diff --git a/packages/boot/plugin-manager/src/index.ts b/packages/boot/plugin-manager/src/index.ts +index ea8a94f..b5b3ee2 100644 +--- a/packages/boot/plugin-manager/src/index.ts ++++ b/packages/boot/plugin-manager/src/index.ts +@@ -625,7 +625,7 @@ export class PluginManager extends TypertRemoteService { + }, { stage: 'remove', target: name }, 'remove') + } + +- /** The rows a bundle's patch inserts and the existing rows it changes; an unreadable patch throws. */ ++ /** The non-group plugin rows a bundle inserts and external rows it changes; an unreadable patch throws. */ + private declaredRows(name: string, info: ProfileManifest): Pick { + const bundle = info.dsh?.bundle + /* v8 ignore next -- bundleManifest answers only manifests that declare a patch */ +@@ -641,9 +641,12 @@ export class PluginManager extends TypertRemoteService { + }) + } + const rows: BundleRowInfo[] = [] ++ const declared = new Set() + const packages = this.ctx.get('pluginPackages') + for (const row of flatten(composeEntries([patches.filter(item => item.insert !== undefined)]))) { + if (typeof row.id !== 'string' || typeof row.name !== 'string') continue ++ declared.add(row.id) ++ if (row.group) continue + const active = live.get(row.id) + const entryId = active?.entryId + const base = active?.baseUrl ?? pathToFileURL(join(dir, 'package.json')).href +@@ -651,7 +654,6 @@ export class PluginManager extends TypertRemoteService { + rows.push({ rowId: row.id, moduleName: row.name, + ...entryId === undefined ? {} : { entryId }, ...meta === undefined ? {} : { meta } }) + } +- const declared = new Set(rows.map(row => row.rowId)) + const overrides = [...new Set(patches.flatMap(item => + item.insert === undefined && typeof item.id === 'string' && !declared.has(item.id) ? [item.id] : []))] + return { rows, overrides } +diff --git a/packages/boot/plugin-manager/src/types.ts b/packages/boot/plugin-manager/src/types.ts +index b87f231..3d53ca4 100644 +--- a/packages/boot/plugin-manager/src/types.ts ++++ b/packages/boot/plugin-manager/src/types.ts +@@ -63,7 +63,7 @@ export interface BundleInfo { + removable: boolean + readOnlyReason?: ReadOnlyReason + error?: ManagementError +- /** The rows the bundle's patch inserts, in declaration order; empty when the patch cannot be read. */ ++ /** The non-group plugin rows the bundle inserts, in declaration order; empty when the patch cannot be read. */ + rows: BundleRowInfo[] + /** Ids of rows the bundle's patch changes without declaring them: the built-in rows it configures or disables. */ + overrides: string[] +diff --git a/packages/boot/plugin-manager/tests/manager.spec.ts b/packages/boot/plugin-manager/tests/manager.spec.ts +index caaf31f..ef8a0e4 100644 +--- a/packages/boot/plugin-manager/tests/manager.spec.ts ++++ b/packages/boot/plugin-manager/tests/manager.spec.ts +@@ -719,6 +719,41 @@ it('addresses children inside profile groups and marks ambiguous ids read-only', + expect((await manager.listPlugins()).find(row => row.entryId === 'include:managed')?.readOnlyReason).toBe('unaddressable') + }) + ++it('lists grouped bundle plugins without exposing native containers as components', async () => { ++ const { ctx, dir, manager, bundle } = await fixture('live', false, (ctx) => { ctx.loader.builtins.group = Group }) ++ bundle('grouped', [{ id: 'group', name: 'cordis:group', group: true, config: [ ++ { id: 'child', name: './plugin.mjs', config: { service: 'child' } }, ++ { id: 'nested-group', name: 'cordis:group', group: true, config: [ ++ { id: 'off-child', name: './plugin.mjs', disabled: true, config: { service: 'offChild' } }, ++ ] }, ++ ] }]) ++ const patchPath = join(dir, 'node_modules', 'grouped', 'cordis.patch.yml') ++ const patches = JSON.parse(readFileSync(patchPath, 'utf8')) as PatchOptions[] ++ patches.push({ id: 'group', disabled: false }, { id: 'managed', config: { service: 'managedProbe' } }) ++ writeFileSync(patchPath, JSON.stringify(patches)) ++ const manifest = readProfileManifest('test', dir) ++ manifest.dependencies = { ...manifest.dependencies, grouped: '1.0.0' } ++ writeFileSync(join(dir, 'package.json'), JSON.stringify(manifest)) ++ const moduleName = pathToFileURL(join(dir, 'node_modules', 'grouped', 'plugin.mjs')).href ++ const rows = [{ rowId: 'child', moduleName }, { rowId: 'off-child', moduleName }] ++ expect((await manager.listBundles()).find(row => row.name === 'grouped')).toMatchObject({ rows, overrides: ['managed'] }) ++ expect(await manager.setBundleEnabled('grouped', true)).toMatchObject({ application: 'applied' }) ++ expect((await manager.listBundles()).find(row => row.name === 'grouped')).toMatchObject({ ++ rows: rows.map(row => ({ ...row, entryId: `include:${row.rowId}` })), overrides: ['managed'], ++ }) ++ const plugins = await manager.listPlugins() ++ const child = plugins.find(row => row.patchId === 'child')! ++ expect(child.enabled).toBe(true) ++ expect(plugins.find(row => row.patchId === 'off-child')?.enabled).toBe(false) ++ expect(await manager.setPluginEnabled(child.entryId, false)).toMatchObject({ application: 'applied' }) ++ expect(ctx.get('child')).toBeUndefined() ++ expect(await manager.setPluginEnabled(child.entryId, true)).toMatchObject({ application: 'applied' }) ++ expect(ctx.get('child')).toBe(true) ++ expect(await manager.setBundleEnabled('grouped', false)).toMatchObject({ application: 'applied' }) ++ expect(ctx.get('child')).toBeUndefined() ++ expect((await manager.listBundles()).find(row => row.name === 'grouped')).toMatchObject({ rows, overrides: ['managed'] }) ++}) ++ + it.each(['', '-g'])('rejects an invalid installation spec before calling pnpm: %j', async (spec) => { + const { manager } = await fixture() + expect(await manager.installBundle(spec)).toMatchObject({ changed: false, application: 'failed', error: { code: 'invalid-spec' } }) diff --git a/dsh/docs/pr-assets/sidebar-native-20260926/verification.json b/dsh/docs/pr-assets/sidebar-native-20260926/verification.json new file mode 100644 index 000000000..9caec3a30 --- /dev/null +++ b/dsh/docs/pr-assets/sidebar-native-20260926/verification.json @@ -0,0 +1,122 @@ +{ + "base": "6d79c663262f71c4309cd6db5bd70b204abb143d", + "implementation": "8d3a16ec", + "dsh": "0.1.7-rc.2", + "node": "24.20.0", + "mnemonCli": "0.2.9", + "model": "local scripted choice; real native mnemon_status tool; no external model API", + "optionalStrategies": [ + "scoped", + "light-context", + "auto-capture" + ], + "officialDshRuntimeUnmodified": true, + "artifacts": [ + { + "file": "dsh-mnemon-0.5.15.tgz", + "sha256": "68f44f5d41e4bfb7f65ab934dc8777f93f3aea256e0bc45cbd41f57671b4f935" + }, + { + "file": "dsh-mnemon-provider-byterover-0.5.4.tgz", + "sha256": "17dcd57cb35c0c81af2b93c92be66c2e6b7e8180d5d1cf634c589281003dddc1" + }, + { + "file": "dsh-mnemon-provider-hindsight-0.5.4.tgz", + "sha256": "739dec0d42689fdbdb8183ba75f7b6afc4be899b585c03c2a85842e0e998a170" + }, + { + "file": "dsh-mnemon-provider-holographic-0.5.4.tgz", + "sha256": "700fa0f94387ee04f5202dbbe0d410afb7114ba2486b25c32d64d7b059245f8c" + }, + { + "file": "dsh-mnemon-provider-honcho-0.5.4.tgz", + "sha256": "db8ffcbbc1df42555295a1ff818e2059b5c09776a0dc4cc9c311779355b06fd7" + }, + { + "file": "dsh-mnemon-provider-mem0-0.5.4.tgz", + "sha256": "ccd61716ead4d9462e15a662edb59c9850d812e8660bc33c4cc574601b816255" + }, + { + "file": "dsh-mnemon-provider-mnemon-native-0.5.5.tgz", + "sha256": "8b839950e17b086578490b9eb3ef438331afa96a85ccd588ddaf7af9649f1f32" + }, + { + "file": "dsh-mnemon-provider-openviking-0.5.6.tgz", + "sha256": "e5b3f9c80c6c450e5cb0e0103f30f8df39550dd980aac70dc4d44e069e2bc570" + }, + { + "file": "dsh-mnemon-provider-retaindb-0.5.4.tgz", + "sha256": "8962cfc5b8e777a86c8816dfd47f32943aa6a1f1973bc8882727b4b430579e3a" + }, + { + "file": "dsh-mnemon-provider-supermemory-0.5.4.tgz", + "sha256": "c117f467d0eb66954ab5b27a8ae08722770699e0f9113fa4bdc9f0724485b508" + }, + { + "file": "dsh-mnemon-source-documents-0.5.6.tgz", + "sha256": "6ff72097bfcf0f02520a8599e9d6c5937e3ee317547fb60209b4188272de2728" + }, + { + "file": "dsh-mnemon-source-memory-spaces-0.5.9.tgz", + "sha256": "42c308d133563a72c946086fd0c2d713eb32528ab30264de6785ab415a8377c7" + }, + { + "file": "dsh-mnemon-source-runtime-0.5.9.tgz", + "sha256": "e17f1b35c3c57063e26783223ddc3164173aa10f01756bc71004191245346bbc" + }, + { + "file": "dsh-mnemon-strategy-auto-capture-0.5.4.tgz", + "sha256": "a653c1ebec85da0fe2c878e42a0eea84e20da877682fa1c9c97f5d8f8f70c074" + }, + { + "file": "dsh-mnemon-strategy-default-three-tier-0.5.6.tgz", + "sha256": "4506f6a025832395ea0fbefeb58c4ddaa34ee0af369f258000619ba9a5f9efd4" + }, + { + "file": "dsh-mnemon-strategy-light-context-0.5.4.tgz", + "sha256": "eac6b61e9652ed54d03dd013bec285afe8714547a27c33ace286439e5958250b" + }, + { + "file": "dsh-mnemon-strategy-scoped-0.5.4.tgz", + "sha256": "c751c30b61f5ae60eca489cda936ed17a5b86dadaae538d9657dd76fe8467843" + } + ], + "checks": { + "verify": { + "rootPassed": 1390, + "rootSkipped": 8, + "skipReason": "opt-in unrelated integration/live suites" + }, + "independentArtifacts": 17, + "independentPluginRepositories": 16, + "nativeCliIntegration": true, + "nativeTwoNamespaceArchive": true, + "bundlePublishedFloorAndRc": true, + "uiRuntimeCreateEditPersist": true, + "uiDocumentsCreateSearchRead": true, + "cliWriteWebUiRecall": true, + "nativeNavigationReturnNewSessionCollapsed": true, + "finalInstalledRuntimeFilesMatchAllArtifacts": true, + "finalMaidHeaderAndPanelSwitch": true + }, + "upstreamCandidate": { + "commit": "c754b50f2da36f9465dd43af7a6d06963af180f3", + "tarballSha256": "bf06f067cf056d71e20a422d3d40957f88d26bbc02a3714cc738c94361ef8513", + "separateDisposableProfile": true, + "published": false, + "unitTests": 88, + "owningFileTests": 81, + "owningFileCoveragePercent": 100, + "docsChecks": 42, + "uiComponents": 8, + "uiDocumentsOffOn": true, + "uiBundleOffOn": true, + "strictLifecycleRegression": true + }, + "package": { + "files": 50, + "packedBytes": 305158, + "unpackedBytes": 1344131, + "unchangedBudgetBytes": 1376000 + } +} diff --git a/dsh/docs/pr-assets/source-page-frames-20260926/README.md b/dsh/docs/pr-assets/source-page-frames-20260926/README.md new file mode 100644 index 000000000..1679b0918 --- /dev/null +++ b/dsh/docs/pr-assets/source-page-frames-20260926/README.md @@ -0,0 +1,95 @@ +# Source page frame verification / Source 页面框架验证 + +Runtime, Documents and Memory Spaces now share Status's outer inset and page scroll container. Memory Spaces owns one fixed title/tab header; its subordinate headings scroll with the content. Source business rendering and bounded readers remain in the independent Source packages. + +运行时、档案和记忆空间现在复用状态页的外层边距与页面滚动容器。记忆空间自行维护固定标题与 Tab 组合头部,下级标题随内容滚动。业务渲染与有界阅读区仍由独立 Source 包负责。 + +## Environment and artifacts / 环境与制品 + +- Published, unmodified DSH `0.1.7-rc.2`; English dark WebUI. / 正式发布、未修改的 DSH `0.1.7-rc.2`,英文深色 WebUI。 +- Baseline / 基线:`6d79c663262f71c4309cd6db5bd70b204abb143d` (`main`, `0.5.15`). +- Initial frame and scroll measurements / 初始框架与滚动测量:`4a5511f7a306447b41d64f6deef3c24ff593226e`. +- Final code / 最终代码:`34da8b03d0bf2832aadd435a3331ea8ff44907e2`. +- Both isolated WebUI environments were reinstalled through the official DSH CLI with all 17 final artifacts; installed runtime JS/CSS hashes matched. Paired after screenshots and the Related after screenshot use this final code. / 两个隔离 WebUI 环境均通过官方 DSH CLI 重新安装全部 17 个最终制品,安装后 runtime JS/CSS 哈希一致。成对对比中的修复后截图与关联区修复后截图均使用最终代码。 +- Root and all 16 independent plugins are packed separately. Exact final artifact hashes are in [SHA256SUMS](SHA256SUMS). Package versions retain their pre-release-test values; hashes identify the tested bytes. / Root 与全部 16 个独立插件分别打包,最终制品的精确哈希见 [SHA256SUMS](SHA256SUMS)。包版本保留发布前测试值,以哈希标识受测字节。 +- Fixtures contain synthetic acceptance data only. Screenshots include temporary fixture paths, not credentials or private memory. / 测试只使用合成验收数据;截图中出现的是临时测试路径,不含凭据或私人记忆。 + +| Artifact / 制品 | Version / 版本 | SHA-256 | +| --- | --- | --- | +| Root `dsh-mnemon` | `0.5.15` | `f46f8ae53727e66958d96f4872dc82cd4bf0807b4e8412dee61d93d62d2fc538` | +| Memory Spaces Source | `0.5.9` | `23f608d5ed2a67d5274cee6cf651ac3b40767e6443eb2eae2c85241a87f7c49f` | + +## Same-size comparison / 同尺寸对比 + +Both runs use a `693 × 983` viewport with the DSH sidebar expanded to `280px`. Coordinates refer to each page's first heading. Status is the reference in the same viewport; the three Source pages now match it exactly. [measurements.json](measurements.json) records final heading coordinates and the initial frame build's canvas heights separately. + +两次均使用 `693 × 983` 视口,DSH 侧栏展开为 `280px`。坐标取各页首个标题;以同一视口中的状态页为基准,三个 Source 页现在与它完全一致。[measurements.json](measurements.json) 分别记录最终标题坐标与初始框架版本的 canvas 高度。 + +| Page / 页面 | Before `(x, y)` / 修复前 | After `(x, y)` / 修复后 | +| --- | --- | --- | +| Status / 状态 | `(292, 123.148)` | `(292, 123.148)` | +| Runtime / 运行时 | `(305, 141.148)` | `(292, 123.148)` | +| Documents / 档案 | `(305, 141.148)` | `(292, 123.148)` | +| Memory Spaces / 记忆空间 | `(308, 135.148)` | `(292, 123.148)` | + +| Page / 页面 | Before / 修复前 | After / 修复后 | +| --- | --- | --- | +| Status / 状态 | ![Status before / 状态页修复前](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/source-page-frames-20260926/before-status.jpg) | ![Status after / 状态页修复后](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/source-page-frames-20260926/after-same-size-status.jpg) | +| Runtime / 运行时 | ![Runtime before / 运行时修复前](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/source-page-frames-20260926/before-runtime.jpg) | ![Runtime after / 运行时修复后](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/source-page-frames-20260926/after-same-size-runtime.jpg) | +| Documents / 档案 | ![Documents before / 档案修复前](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/source-page-frames-20260926/before-documents.jpg) | ![Documents after / 档案修复后](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/source-page-frames-20260926/after-same-size-documents.jpg) | +| Memory Spaces / 记忆空间 | ![Memory Spaces before / 记忆空间修复前](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/source-page-frames-20260926/before-memory-spaces.jpg) | ![Memory Spaces after / 记忆空间修复后](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/source-page-frames-20260926/after-same-size-memory-spaces.jpg) | + +## Scroll and functional checks / 滚动与功能验证 + +At `1280 × 720`, all four primary headings start at `(296, 101.148)`. Scrolling the Memory Spaces canvas by `135.5px` keeps the primary heading at `y=101.148` while Overview moves with the content. Selecting Recall resets only the canvas to `scrollTop=0`. + +在 `1280 × 720` 下,四页主标题均从 `(296, 101.148)` 开始。记忆空间 canvas 滚动 `135.5px` 后,主标题仍位于 `y=101.148`,概览随内容移动。切换到检索后,所属 canvas 回到 `scrollTop=0`。 + +![Memory Spaces after page scrolling / 记忆空间页面滚动后](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/source-page-frames-20260926/after-spaces-scrolled.jpg) + +Real UI actions also created one Runtime entry and a `503 B` Document, then found the Document with the keyword `bounded scroll`. A Native Memory Space was created and activated; the real Mnemon CLI wrote 12 synthetic memories sharing `FrameAcceptance`, and Content plus keyword Recall displayed them. + +真实 UI 还成功创建了一条运行时条目和一个 `503 B` 档案,并通过关键词 `bounded scroll` 检索到档案。已创建并激活 Native 记忆空间,真实 Mnemon CLI 写入了 12 条共享 `FrameAcceptance` 实体的合成记忆,内容页与关键词检索均可显示。 + +![Document creation and keyword search / 档案创建与关键词检索](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/source-page-frames-20260926/after-documents-search.jpg) + +## Related reveal / 关联记忆定位 + +The intermediate frame build exposed a second issue: after keyword Recall, opening a lower result's related memories put its heading at `y=124.094` and close button at `121.719–147.719`, behind the fixed header ending at `y=208.188` (`canvas.scrollTop=473.5`). + +框架修复后的中间版本暴露出另一问题:关键词检索后,从靠下的结果打开关联记忆,标题位于 `y=124.094`,关闭按钮位于 `121.719–147.719`,被底边位于 `y=208.188` 的固定头部遮挡(`canvas.scrollTop=473.5`)。 + +![Related controls hidden before reveal fix / 定位修复前被遮挡的关联操作](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/source-page-frames-20260926/before-related-reveal.jpg) + +Final `34da8b03` real WebUI reveal verification **passed**: the heading is at `y=245.094` and the close button at `242.719–268.719`, below the unchanged header bottom `208.188`; clicking Close succeeds. Automated regressions pass for both header heights, close/unmount cancellation, stale element rejection and two simultaneous Workbench instances. The Source owns its header/pane refs; the optional Host callback writes only the owning canvas's `scrollTop`. + +最终 `34da8b03` 的真实 WebUI 定位验收**通过**:标题位于 `y=245.094`,关闭按钮位于 `242.719–268.719`,均在未变化的头部底边 `208.188` 下方,点击关闭成功。自动回归已覆盖两种头部高度、关闭与卸载取消、失效元素拒绝及两个同时存在的工作台。Source 持有自身头部与关联区 ref,可选 Host 回调只修改所属 canvas 的 `scrollTop`。 + +![Related heading and close control revealed / 定位后可见的关联标题与关闭操作](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/source-page-frames-20260926/after-related-reveal.jpg) + +## Supplemental combined preview / 补充组合预览 + +Integration checkout `68f74fb849214ba3fa4e295eef8f77f72289469d` combines PR #287's Native Sidebar (`29e74522`) with Source commits `4a5511f7` and `34da8b03`. Its own 17 combined artifacts were installed in unmodified DSH `0.1.7-rc.2` and their runtime JS/CSS hashes matched. With the real Maid Atelier skin at `1055 × 983`, all four primary headings remain at `(296, 101.148)`. Native Plugins and Memory System rows both measure `x=14`, `252 × 36px`, `14px` font; 141 UI tests in 11 files, build, type and package checks pass. Switching Recall → Plugins → Memory System preserves Recall; collapsed-sidebar keyword search also returned one real Native memory. The independent PR artifacts remain the final hashes listed above. + +集成 checkout `68f74fb849214ba3fa4e295eef8f77f72289469d` 组合了 PR #287 的 Native Sidebar(`29e74522`)与 Source 提交 `4a5511f7`、`34da8b03`。其专用的 17 个组合制品已安装到未修改的 DSH `0.1.7-rc.2`,runtime JS/CSS 哈希一致。在实际 Maid Atelier 皮肤与 `1055 × 983` 视口下,四页主标题仍均为 `(296, 101.148)`。原生 Plugins 与 Memory System 行均为 `x=14`、`252 × 36px`、`14px` 字体;11 个文件中的 141 项 UI 测试、构建、类型与包检查通过。检索 → Plugins → Memory System 切换后仍保留检索页,侧栏折叠时关键词检索也命中一条真实 Native 记忆。独立 PR 制品仍以上方最终哈希为准。 + +![Supplemental combined preview with Maid Atelier / Maid Atelier 补充组合预览](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/source-page-frames-20260926/combined-maid-memory-spaces.jpg) + +## Compatibility and automated checks / 兼容与自动验证 + +The new scroll callbacks are optional. The final Memory Spaces client was loaded against actual Root `0.5.1` and baseline `0.5.15` client artifacts without either callback: Content, Recall, opening/closing Related and Entities all work. Those older Hosts require manual scrolling; automatic reset and header-aware reveal are unavailable. Updating Root and Memory Spaces together provides the complete fix, without raising the existing Root peer floor. + +新滚动回调均为可选。最终 Memory Spaces client 已分别加载实际 Root `0.5.1` 与基线 `0.5.15` 的 client 制品,在两个回调均缺省时验证内容、检索、打开与关闭关联记忆、实体页均可工作。旧 Host 需要手动滚动,不具备自动重置或避开固定头部的定位能力。同时更新 Root 与 Memory Spaces 可获得完整修复,无需提高现有 Root peer 最低版本。 + +| Check / 检查 | Result / 结果 | +| --- | --- | +| Five new reveal regressions / 五项新增定位回归 | Failed before the fix; pass after it. / 修复前失败,修复后通过。 | +| Focused Root UI suite / Root 定向 UI 测试 | 87 passed / 87 项通过 | +| Independent Memory Spaces client / 独立 Memory Spaces client | 5 passed / 5 项通过 | +| `pnpm run verify` | Passed: 100 Root test files, 1,386 tests; 8 opt-in tests skipped. Workspace checks, deterministic build, real Headless activation and package checks passed. / 通过:Root 100 个测试文件、1,386 项测试;8 项显式启用测试跳过。工作区检查、确定性构建、真实 Headless 激活与制品检查通过。 | +| `pnpm run verify:plugins` | Passed: 16 independent plugin repositories, 17 artifacts, public SDK consumer, Starter installation and three optional Strategies composed in real DSH. / 通过:16 个独立插件仓库、17 个制品、公开 SDK 消费者、Starter 安装及真实 DSH 中三个可选 Strategy 的组合。 | +| Root package budget / Root 制品预算 | `1,375,877 / 1,376,000 B`; unchanged limit. / 上限未调整。 | + +Opt-in skips cover external live services/credentials, a separate published V4 contract fixture, explicit Native integration flags and Windows-only checks. Real Native CLI and WebUI coverage described above is separate from those gated suites. Node `24.20.0` and pnpm `10` were used; the two full verification commands run sequentially. + +跳过项属于需要外部在线服务或凭据、独立已发布 V4 契约夹具、显式 Native 集成标志或 Windows 平台的测试。上面的真实 Native CLI 与 WebUI 验证独立于这些受开关控制的测试。验证使用 Node `24.20.0`、pnpm `10`,两条完整验证命令顺序执行。 diff --git a/dsh/docs/pr-assets/source-page-frames-20260926/SHA256SUMS b/dsh/docs/pr-assets/source-page-frames-20260926/SHA256SUMS new file mode 100644 index 000000000..5f8cef97d --- /dev/null +++ b/dsh/docs/pr-assets/source-page-frames-20260926/SHA256SUMS @@ -0,0 +1,17 @@ +f46f8ae53727e66958d96f4872dc82cd4bf0807b4e8412dee61d93d62d2fc538 dsh-mnemon-0.5.15.tgz +e4842a2f7fa2727b244fef38e39b2d002e5d189fa61cfaa8658ed9f1aedde58d dsh-mnemon-provider-byterover-0.5.4.tgz +739dec0d42689fdbdb8183ba75f7b6afc4be899b585c03c2a85842e0e998a170 dsh-mnemon-provider-hindsight-0.5.4.tgz +700fa0f94387ee04f5202dbbe0d410afb7114ba2486b25c32d64d7b059245f8c dsh-mnemon-provider-holographic-0.5.4.tgz +db8ffcbbc1df42555295a1ff818e2059b5c09776a0dc4cc9c311779355b06fd7 dsh-mnemon-provider-honcho-0.5.4.tgz +ccd61716ead4d9462e15a662edb59c9850d812e8660bc33c4cc574601b816255 dsh-mnemon-provider-mem0-0.5.4.tgz +8b839950e17b086578490b9eb3ef438331afa96a85ccd588ddaf7af9649f1f32 dsh-mnemon-provider-mnemon-native-0.5.5.tgz +e5b3f9c80c6c450e5cb0e0103f30f8df39550dd980aac70dc4d44e069e2bc570 dsh-mnemon-provider-openviking-0.5.6.tgz +8962cfc5b8e777a86c8816dfd47f32943aa6a1f1973bc8882727b4b430579e3a dsh-mnemon-provider-retaindb-0.5.4.tgz +c117f467d0eb66954ab5b27a8ae08722770699e0f9113fa4bdc9f0724485b508 dsh-mnemon-provider-supermemory-0.5.4.tgz +44fbc4b2ec2ec594e683e3d6f3c0f2cb0ca43447e5c35eecfc8bac61f4d49a5c dsh-mnemon-source-documents-0.5.6.tgz +23f608d5ed2a67d5274cee6cf651ac3b40767e6443eb2eae2c85241a87f7c49f dsh-mnemon-source-memory-spaces-0.5.9.tgz +496992c724d7443fd15d142305bd096c0bc7f256a82b41e28bc7e9305f23f96c dsh-mnemon-source-runtime-0.5.9.tgz +a653c1ebec85da0fe2c878e42a0eea84e20da877682fa1c9c97f5d8f8f70c074 dsh-mnemon-strategy-auto-capture-0.5.4.tgz +4506f6a025832395ea0fbefeb58c4ddaa34ee0af369f258000619ba9a5f9efd4 dsh-mnemon-strategy-default-three-tier-0.5.6.tgz +eac6b61e9652ed54d03dd013bec285afe8714547a27c33ace286439e5958250b dsh-mnemon-strategy-light-context-0.5.4.tgz +c751c30b61f5ae60eca489cda936ed17a5b86dadaae538d9657dd76fe8467843 dsh-mnemon-strategy-scoped-0.5.4.tgz diff --git a/dsh/docs/pr-assets/source-page-frames-20260926/measurements.json b/dsh/docs/pr-assets/source-page-frames-20260926/measurements.json new file mode 100644 index 000000000..fa180c4c8 --- /dev/null +++ b/dsh/docs/pr-assets/source-page-frames-20260926/measurements.json @@ -0,0 +1,293 @@ +{ + "dshVersion": "0.1.7-rc.2", + "baseCommit": "6d79c663262f71c4309cd6db5bd70b204abb143d", + "finalCodeCommit": "34da8b03d0bf2832aadd435a3331ea8ff44907e2", + "sameSizeViewport": { + "width": 693, + "height": 983 + }, + "sameSizeFrames": [ + { + "page": "Status", + "before": { + "title": { + "x": 292, + "y": 123.1484375 + }, + "canvas": { + "clientHeight": 874, + "scrollHeight": 2207, + "scrollTop": 0 + } + }, + "after": { + "title": { + "x": 292, + "y": 123.1484375 + } + }, + "initialFrameCanvas": { + "clientHeight": 874, + "scrollHeight": 2207, + "scrollTop": 0 + } + }, + { + "page": "Runtime", + "before": { + "title": { + "x": 305, + "y": 141.1484375 + }, + "canvas": { + "clientHeight": 874, + "scrollHeight": 1166, + "scrollTop": 0 + } + }, + "after": { + "title": { + "x": 292, + "y": 123.1484375 + } + }, + "initialFrameCanvas": { + "clientHeight": 874, + "scrollHeight": 959, + "scrollTop": 0 + } + }, + { + "page": "Documents", + "before": { + "title": { + "x": 305, + "y": 141.1484375 + }, + "canvas": { + "clientHeight": 874, + "scrollHeight": 1439, + "scrollTop": 0 + } + }, + "after": { + "title": { + "x": 292, + "y": 123.1484375 + } + }, + "initialFrameCanvas": { + "clientHeight": 874, + "scrollHeight": 1231, + "scrollTop": 0 + } + }, + { + "page": "Memory Spaces", + "before": { + "title": { + "x": 308, + "y": 135.1484375 + }, + "canvas": { + "clientHeight": 874, + "scrollHeight": 1235, + "scrollTop": 0 + } + }, + "after": { + "title": { + "x": 292, + "y": 123.1484375 + } + }, + "initialFrameCanvas": { + "clientHeight": 874, + "scrollHeight": 1011, + "scrollTop": 0 + } + } + ], + "memorySpacesScroll": { + "viewport": { + "width": 1280, + "height": 720 + }, + "canvasScrollTop": 135.5, + "primaryHeadingY": 101.1484375, + "overviewMovesWithContent": true, + "canvasScrollTopAfterRecall": 0 + }, + "relatedBefore": { + "commit": "4a5511f7", + "viewport": { + "height": 720, + "width": 1280 + }, + "canvasScrollTop": 473.5, + "stickyHeaderBottom": 208.1875, + "headingTop": 124.09375, + "closeButtonTop": 121.71875, + "closeButtonBottom": 147.71875, + "occluded": true + }, + "relatedAfter": { + "commit": "34da8b03", + "status": "passed", + "viewport": { + "height": 720, + "width": 1280 + }, + "stickyHeaderBottom": 208.1875, + "headingTop": 245.09375, + "closeButtonTop": 242.71875, + "closeButtonBottom": 268.71875, + "occluded": false, + "closeClickedSuccessfully": true + }, + "functionalChecks": { + "runtimeCreated": 1, + "documentCreatedBytes": 503, + "documentKeywordSearch": "bounded scroll", + "nativeSyntheticMemoriesWritten": 12, + "nativeSharedEntity": "FrameAcceptance", + "nativeContentAndKeywordRecall": "passed" + }, + "oldRootCompatibility": [ + { + "root": "0.5.1", + "scrollCallbacksAbsent": true, + "scrolling": "manual; automatic reset and reveal are unavailable on these Hosts", + "registrations": 5, + "rendered": [ + "content", + "explore", + "related", + "entities" + ], + "operations": [ + "list", + "status-summary", + "search", + "related", + "entities" + ], + "result": "passed" + }, + { + "root": "0.5.15-main", + "scrollCallbacksAbsent": true, + "scrolling": "manual; automatic reset and reveal are unavailable on these Hosts", + "registrations": 5, + "rendered": [ + "content", + "explore", + "related", + "entities" + ], + "operations": [ + "list", + "status-summary", + "search", + "related", + "entities" + ], + "result": "passed" + } + ], + "initialFrameCommit": "4a5511f7a306447b41d64f6deef3c24ff593226e", + "frameCommit": "34da8b03d0bf2832aadd435a3331ea8ff44907e2", + "installedRuntimeHashesMatched": { + "environments": 2, + "artifactsPerEnvironment": 17, + "method": "official DSH CLI installation followed by per-file runtime JS/CSS hashes" + }, + "verification": [ + { + "command": [ + "pnpm", + "run", + "verify" + ], + "startedAt": "2026-09-26T13:31:59.446Z", + "finishedAt": "2026-09-26T13:33:26.405Z", + "code": 0 + }, + { + "command": [ + "pnpm", + "run", + "verify:plugins" + ], + "startedAt": "2026-09-26T13:33:26.406Z", + "finishedAt": "2026-09-26T13:35:15.952Z", + "code": 0 + } + ], + "postVerificationRepack": { + "artifacts": 17, + "sha256Unchanged": true + }, + "supplementalCombinedPreview": { + "commit": "68f74fb849214ba3fa4e295eef8f77f72289469d", + "includes": [ + "PR #287 native Sidebar at 29e74522", + "Source frame commits 4a5511f7 and 34da8b03" + ], + "dshVersion": "0.1.7-rc.2", + "skin": "Maid Atelier", + "viewport": { + "height": 983, + "width": 1055 + }, + "primaryHeadings": [ + { + "page": "Status", + "x": 296, + "y": 101.1484375 + }, + { + "page": "Runtime", + "x": 296, + "y": 101.1484375 + }, + { + "page": "Documents", + "x": 296, + "y": 101.1484375 + }, + { + "page": "Memory Spaces", + "x": 296, + "y": 101.1484375 + } + ], + "nativePanelRows": [ + { + "font": "14px", + "height": 36, + "text": "Plugins", + "width": 252, + "x": 14 + }, + { + "font": "14px", + "height": 36, + "text": "Memory System", + "width": 252, + "x": 14 + } + ], + "installedRuntimeHashesMatched": 17, + "uiTests": { + "files": 11, + "passed": 141 + }, + "buildTypesPackage": "passed", + "interactions": { + "recallPreservedAcrossPluginsPanel": true, + "collapsedSidebarUsable": true, + "collapsedKeywordRecallNativeMatches": 1 + } + } +} diff --git a/dsh/docs/pr-assets/starter-group-readiness/README.md b/dsh/docs/pr-assets/starter-group-readiness/README.md new file mode 100644 index 000000000..c686b064e --- /dev/null +++ b/dsh/docs/pr-assets/starter-group-readiness/README.md @@ -0,0 +1,51 @@ +# Starter component group + +[中文](README.zh-CN.md) + +Verified on 2026-09-29 (Asia/Shanghai) with isolated profiles and synthetic data. All changes are in dsh-mnemon; DSH was not modified. + +## Problem + +In 0.5.18 and 0.5.19 the Starter had a separate readiness entry, `dsh-mnemon/starter` (`mnemon-starter`), which the Plugins page listed like any component, and the component group waited for its `mnemonStarterReady`. Turning that row off, for example to silence an error after an in-place update on DSH Desktop, left every component waiting: `dsh web` started without the Memory System, and the desktop app failed its start and offered to remove the plugin. The profile patch kept the off switch, so a reinstalled plugin waited again. + +## Change + +`mnemon-bundle` now names `dsh-mnemon/bundle`. It makes the Starter's dependency closure visible, the same preparation as before, and then mounts the children with the Loader's own `cordis:group`. The readiness row and its wait are gone, and a leftover `mnemon-starter` row in a profile patch matches nothing and is ignored. Entry IDs, the core gate, component choices, configuration targets and memory data are unchanged. + +The row could not simply be hidden: DSH lists every entry that a bundle patch inserts with an id and has no hidden flag, and an entry without an id is recreated on every patch reload, which would restart all components. + +| Published 0.5.19 on DSH 0.2.0-rc.1: 11 components, `dsh-mnemon/starter` first | Candidate on DSH 0.2.0-rc.1: 10 components, `dsh-mnemon/bundle` first | +| --- | --- | +| ![Before](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/starter-group-readiness/before-components.png) | ![After](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/starter-group-readiness/after-components.png) | + +English interface: [before](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/starter-group-readiness/before-components-en.png) · [after](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/starter-group-readiness/after-components-en.png). The container row still reads Off, as `cordis:group` did, because DSH's plugin inventory skips group entries ([upstream #649](https://github.com/dsh-external/issues/issues/649)). + +## Acceptance + +The candidate root tarball was installed through **Plugins → Add plugin** on fresh profiles of the published DSH 0.2.0-rc.1 and 0.1.7-rc.2, with the other 17 packages at their 0.5.19 pins, followed by **Enable now**: + +| Check | DSH 0.2.0-rc.1 | DSH 0.1.7-rc.2 | +| --- | --- | --- | +| Enable now without a restart | The Memory System appears; no host warnings or console errors | Same | +| Component list | 10 total · 5 running · 5 off: the container and the four optional strategies | Same | +| Restart | No warnings | No warnings | +| Leftover `mnemon-starter: disabled: true` in the profile patch | Starts normally with the Memory System | Same | +| General strategy turned on, then off | Only that component changes | Not run | + +A functional check on DSH 0.2.0-rc.1 kept one host process from installation to the end. Status reads nominal with Mnemon 0.2.7, and the page is byte-identical to the [v0.5.19 release acceptance](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.19/status.png). A Runtime entry is read back after a reload, a Native memory space is created and activated in the UI, and **Direct recall** finds a fact written by the Mnemon CLI. + +![Direct recall finds the fact the Mnemon CLI wrote](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/starter-group-readiness/recall.png) + +`dsh web` profiles link packages with `nodeLinker: hoisted`, so all 18 packages sit in the profile's own `node_modules` and the preparation step finds them already visible. The layout that needs it is the desktop app's plugin generation, where the profile exposes only the root package. The activation fixture (`tests/bundle-activation.spec.mjs`) reproduces that layout with DSH's own boot and plugin manager: with the native group and no preparation, enabling the bundle after a cold start leaves four components failing to import on both hosts; with the component group, every enabled component starts. The fixture's other cases also pass on both hosts: another bundle's retained routes, the legacy `mnemon` gate and component choices, and the manager's component and bundle toggles. + +## Trade-off: config schema export + +`dsh web --dump-config-schema` walks only native `cordis:group` and `cordis:include` carriers. It now reports `mnemon-bundle` as an unrecognized tree carrier and leaves out Mnemon's component schemas. On stock profiles of both hosts the command already exits 1 with the same error for DSH's own agent presets (`preset-standard`, `preset-ptc`, `preset-minimal`, `preset-cordis`); nothing else changes. Runtime configuration, the Plugins page and profile patches are unaffected. The fixture asserts this exact diagnostic and fails on any other error. + +## Updating + +As before, an in-place update needs a restart: until then, enabling a component after an update from 0.5.18 or 0.5.19 reports `ERR_PACKAGE_PATH_NOT_EXPORTED` for `./bundle`. `dsh-mnemon/starter` stays exported for compositions that still insert it. + +## Record + +[validation.json](validation.json) holds the candidate digest, the base commits, the per-host results and the schema export comparison. The candidate keeps the unreleased package version 0.5.19; the changeset requests a patch release. diff --git a/dsh/docs/pr-assets/starter-group-readiness/README.zh-CN.md b/dsh/docs/pr-assets/starter-group-readiness/README.zh-CN.md new file mode 100644 index 000000000..d52ff259c --- /dev/null +++ b/dsh/docs/pr-assets/starter-group-readiness/README.zh-CN.md @@ -0,0 +1,51 @@ +# Starter 组件组 + +[English](README.md) + +2026-09-29(Asia/Shanghai)在隔离 profile 与合成数据上验证。全部改动都在 dsh-mnemon 中,未修改 DSH。 + +## 问题 + +0.5.18 与 0.5.19 的 Starter 有一个单独的就绪条目 `dsh-mnemon/starter`(`mnemon-starter`),插件页像普通组件一样列出它,组件组要等它提供 `mnemonStarterReady`。关闭这一行(例如在 DSH 桌面版原地更新后,为了消除一条报错)会让全部组件一直等待:`dsh web` 启动后没有记忆系统,桌面版则按启动失败处理并提议移除插件。关闭开关保存在 profile patch 中,重新安装的插件仍会继续等待。 + +## 改动 + +`mnemon-bundle` 改为使用模块 `dsh-mnemon/bundle`。它先让 Starter 的依赖可被解析(与原来相同的准备步骤),再用加载器自己的 `cordis:group` 挂载子组件。就绪行和对它的等待都已去掉,profile patch 中残留的 `mnemon-starter` 行不再对应任何条目,会被忽略。Entry ID、核心总开关、各组件的选择、配置目标与记忆数据均保持不变。 + +这一行无法简单隐藏:DSH 会列出插件包 patch 中每一个带 id 的条目,也没有隐藏标记;而不带 id 的条目在每次 patch 重载时都会被重建,从而重启全部组件。 + +| DSH 0.2.0-rc.1 上已发布的 0.5.19:共 11 个组件,第一行是 `dsh-mnemon/starter` | DSH 0.2.0-rc.1 上的候选包:共 10 个组件,第一行是 `dsh-mnemon/bundle` | +| --- | --- | +| ![改动前](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/starter-group-readiness/before-components.png) | ![改动后](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/starter-group-readiness/after-components.png) | + +英文界面:[改动前](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/starter-group-readiness/before-components-en.png) · [改动后](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/starter-group-readiness/after-components-en.png)。容器行仍与原来的 `cordis:group` 一样显示“已关闭”,因为 DSH 的插件清单会跳过 group 条目([上游 #649](https://github.com/dsh-external/issues/issues/649))。 + +## 验收 + +在正式发布的 DSH 0.2.0-rc.1 与 0.1.7-rc.2 的全新 profile 上,通过**插件 → 添加插件**安装候选根包(其余 17 个包按 0.5.19 固定的版本安装),再点击**立即启用**: + +| 检查 | DSH 0.2.0-rc.1 | DSH 0.1.7-rc.2 | +| --- | --- | --- | +| 不重启直接“立即启用” | 记忆系统出现;宿主无警告,控制台无错误 | 相同 | +| 组件列表 | 共 10 个 · 5 运行中 · 5 已停用:容器与四个默认关闭的可选策略 | 相同 | +| 重启 | 无警告 | 无警告 | +| profile patch 中残留 `mnemon-starter: disabled: true` | 正常启动,记忆系统可用 | 相同 | +| 打开再关闭“通用策略” | 只有该组件变化 | 未测 | + +DSH 0.2.0-rc.1 上的功能验收从安装到结束只使用一个宿主进程。状态页显示系统正常与 Mnemon 0.2.7,页面与 [v0.5.19 发布验收](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/release-v0.5.19/status.png)的截图逐字节一致。运行时记忆刷新后能读回,在界面中创建并激活了 Native 记忆空间,**直接检索**能找到 Mnemon CLI 写入的事实。 + +![直接检索找到 Mnemon CLI 写入的事实](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/starter-group-readiness/recall.png) + +`dsh web` 的 profile 以 `nodeLinker: hoisted` 安装依赖,18 个包都在 profile 自己的 `node_modules` 中,准备步骤发现它们已经可被解析。真正需要这一步的是桌面版的插件 generation 布局:profile 只暴露根包。激活夹具(`tests/bundle-activation.spec.mjs`)用 DSH 自己的启动流程与插件管理器复现这种布局:使用原生 group、不做准备时,冷启动后再启用 bundle,两个宿主上都有四个组件导入失败;使用组件组时,所有已启用的组件都正常启动。夹具的其他用例在两个宿主上同样通过:保留另一个 bundle 的依赖路径、旧版 `mnemon` 总开关与组件选择,以及管理器的组件与 bundle 开关。 + +## 取舍:配置 schema 导出 + +`dsh web --dump-config-schema` 只遍历原生 `cordis:group` 与 `cordis:include`。它现在会把 `mnemon-bundle` 报告为无法识别的树载体,并略去 Mnemon 各组件的 schema。两个宿主的默认 profile 执行该命令本来就以 1 退出,并对 DSH 自带的 agent preset(`preset-standard`、`preset-ptc`、`preset-minimal`、`preset-cordis`)报告同样的错误;其余结果不变。运行时配置、插件页与 profile patch 不受影响。夹具断言的正是这一条诊断,出现其他错误即失败。 + +## 更新 + +与以前一样,原地更新后需要重启:在重启之前,从 0.5.18 或 0.5.19 更新后启用组件会提示 `./bundle` 未导出(`ERR_PACKAGE_PATH_NOT_EXPORTED`)。`dsh-mnemon/starter` 仍然导出,供仍插入它的组合使用。 + +## 记录 + +[validation.json](validation.json) 记录了候选包摘要、基线提交、各宿主结果与 schema 导出对比。候选包沿用未发布的版本号 0.5.19;changeset 请求发布补丁版本。 diff --git a/dsh/docs/pr-assets/starter-group-readiness/validation.json b/dsh/docs/pr-assets/starter-group-readiness/validation.json new file mode 100644 index 000000000..d9fe15beb --- /dev/null +++ b/dsh/docs/pr-assets/starter-group-readiness/validation.json @@ -0,0 +1,165 @@ +{ + "verifiedAt": "2026-09-28T19:14:32Z", + "base": { + "main": "fa43e28087e04c4decd938705841e88f95cd0034", + "stackedOn": "4b2ef349636ca70d0a0b4037ca33500da3a22bfa (#313)" + }, + "candidate": { + "name": "dsh-mnemon", + "version": "0.5.19", + "status": "unreleased PR candidate; the changeset requests a patch release", + "sha256": "cdc59ed345f9d834b6df55c32b02d74fcbf0855329b1bd5bbe728ba53c7aac76", + "installFlow": "Plugins -> Add plugin with the packed root tarball -> Install -> Enable now; the other 17 packages at the 0.5.19 pins from the registry DSH chose" + }, + "baseline": { + "dsh": "0.2.0-rc.1", + "package": "dsh-mnemon 0.5.19 from npm, installed with dsh plugin --profile web add dsh-mnemon@0.5.19", + "components": "11 total · 6 running · 5 off", + "firstRows": [ + "dsh-mnemon/starter (running)", + "cordis:group (off)" + ] + }, + "hosts": { + "0.2.0-rc.1": { + "freshProfileStartup": "no warnings", + "enableNowWithoutRestart": { + "memorySystem": true, + "notices": [], + "hostWarnings": [], + "consoleErrors": [] + }, + "components": "10 total · 5 running · 5 off", + "firstRows": [ + "dsh-mnemon/bundle (off, the container)", + "dsh-mnemon (running)" + ], + "restart": { + "hostWarnings": [], + "memorySystem": true + }, + "leftoverStarterRow": { + "patch": "- id: mnemon-starter / disabled: true", + "hostWarnings": [], + "memorySystem": true + }, + "childToggle": { + "component": "General strategy", + "on": "running", + "off": "off", + "otherComponentsStayedRunning": true, + "memorySystem": true, + "consoleErrors": [] + }, + "functional": { + "hostLaunches": 1, + "status": { + "starter": "0.5.19", + "nominal": true, + "nativeCli": "0.2.7", + "sameAsReleaseV0519Screenshot": true + }, + "runtimeWriteAndReadAfterReload": true, + "nativeSpaceCreatedAndActive": "1 / 1", + "cliRememberAndRecall": true, + "webDirectRecallFindsCliFact": true, + "consoleErrors": [] + } + }, + "0.1.7-rc.2": { + "freshProfileStartup": "no warnings", + "enableNowWithoutRestart": { + "memorySystem": true, + "notices": [], + "hostWarnings": [], + "consoleErrors": [] + }, + "components": "10 total · 5 running · 5 off", + "restart": { + "hostWarnings": [], + "memorySystem": true + }, + "leftoverStarterRow": { + "patch": "- id: mnemon-starter / disabled: true", + "hostWarnings": [], + "memorySystem": true + } + } + }, + "activationFixture": { + "hosts": [ + "0.1.7-rc.2", + "0.2.0-rc.1" + ], + "cases": [ + "isolated cold start, bundle enabled later", + "other bundle's routes retained", + "legacy mnemon gate and component choices", + "manager component and bundle toggles" + ], + "result": "passed on both hosts", + "managerComponentsExcludingGroups": 9, + "generationLayoutComparison": { + "layout": "only the root package visible in the profile, components in a separate generation (as the desktop app projects plugins); bundle not selected at startup, then enabled through the plugin manager", + "nativeGroupWithoutPreparation": { + "0.1.7-rc.2": "4 entries did not activate: mnemon-source-runtime, mnemon-source-memory-spaces, mnemon-strategy-default-three-tier, mnemon-strategy-auto-capture (failed to import)", + "0.2.0-rc.1": "4 entries did not activate: mnemon-source-runtime, mnemon-source-memory-spaces, mnemon-strategy-default-three-tier, mnemon-strategy-auto-capture (failed to import)" + }, + "componentGroup": { + "0.1.7-rc.2": "all enabled components active", + "0.2.0-rc.1": "all enabled components active" + } + } + }, + "configSchemaExport": { + "command": "dsh web --dump-config-schema", + "baseline0519OnDsh020": { + "exit": 1, + "complete": false, + "unrecognizedCarriers": [ + "preset-standard", + "preset-ptc", + "preset-minimal", + "preset-cordis" + ], + "mnemonEntriesWithSchema": [ + "mnemon", + "mnemon-source-runtime", + "mnemon-source-documents", + "mnemon-source-memory-spaces (partial)" + ] + }, + "candidateOnDsh020": { + "exit": 1, + "complete": false, + "unrecognizedCarriers": [ + "preset-standard", + "preset-ptc", + "preset-minimal", + "preset-cordis", + "mnemon-bundle" + ], + "mnemonEntriesWithSchema": [] + }, + "candidateOnDsh017": { + "exit": 1, + "complete": false, + "unrecognizedCarriers": [ + "preset-standard", + "preset-ptc", + "preset-minimal", + "preset-cordis", + "mnemon-bundle" + ], + "mnemonEntriesWithSchema": [] + } + }, + "environment": { + "os": "macOS", + "node": "24.19.0", + "pnpm": "11.19.0", + "browser": "headless Google Chrome over the DevTools protocol, light scheme", + "dshModified": false + }, + "webProfileLayout": "dsh web profiles link packages with nodeLinker: hoisted, so all 18 packages sit in the profile's node_modules and the preparation finds them already visible" +} diff --git a/dsh/docs/pr-assets/starter-group-upgrade/README.md b/dsh/docs/pr-assets/starter-group-upgrade/README.md new file mode 100644 index 000000000..797611572 --- /dev/null +++ b/dsh/docs/pr-assets/starter-group-upgrade/README.md @@ -0,0 +1,50 @@ +# Cold start and upgrades with the component group + +[中文](README.zh-CN.md) + +Verified on 2026-09-29 (Asia/Shanghai) against `main` at `94d1e84f` (#313 and #314), with isolated profiles and synthetic data. DSH was not modified. + +The candidate is 0.5.20 as `pnpm release:version` builds it; it is not published. A local registry serves npm's real metadata with every dsh-mnemon package's publish times moved two days back, plus 0.5.20. That is npm as it will read a day after the release, when 0.5.19 and its components are older than pnpm's one-day minimum release age. A pnpm wrapper sends the npm and npmmirror registries DSH asks to that registry. Every web and command-line run starts from a fresh `HOME`, DSH home and pnpm store. + +## New users + +| Way | DSH 0.2.0-rc.1 | DSH 0.1.7-rc.2 | +| --- | --- | --- | +| Web: **Plugins → Add plugin → dsh-mnemon → Install → Enable now** | Installed in 21.8 s, enabled in 0.8 s, one host process | 13.2 s and 1.1 s | +| Command line: `dsh plugin --profile web add dsh-mnemon`, then `dsh web` | Installed in 9 s, ready in 2 s | 8 s and 1.3 s | +| Headless: `scripts/verify-headless-profile.mjs --package dsh-mnemon@0.5.20` | Passed | Passed | +| Desktop layout: installed into its own generation, the profile links only the root, switched on in the Plugins list | Memory System without a restart | Same | + +Every run ends with Status reading `dsh-mnemon 0.5.20`, nominal, and 10 components with no Starter row, and none reports a host warning or console error. The web runs also write a Runtime entry and read it back after a reload, create and activate a Native memory space, and find a fact written by the Mnemon CLI with **Direct recall**. + +**The release day.** With 0.5.20 published an hour earlier, a plain install picks 0.5.19 on both hosts, and it works. That holds only once 0.5.19 itself is a day old, at 17:31 UTC on 09-29. Until then npm offers 0.5.17 to pnpm 11, because 0.5.18 and 0.5.19 were both released on 09-28. Checked against the real npm at 20:12 UTC on 09-28: `dsh-mnemon` and `dsh-mnemon@latest` install 0.5.17, which DSH 0.2 refuses, and only `dsh-mnemon@0.5.19` installs at once. The installation guide now says a plain install can fall back more than one release, and that DSH 0.1.7 accepts the older release silently. + +## Existing users + +Each profile first ran the older release with a Runtime entry written in the UI, the General strategy on and Project Documents off. For 0.5.18 and 0.5.19 the profile also had the Starter row, either on or turned off. + +| DSH | From | Command, then a restart | Result | +| --- | --- | --- | --- | +| 0.1.7-rc.2 | 0.5.16 | `dsh plugin --profile web update dsh-mnemon` | 0.5.20; choices kept | +| 0.1.7-rc.2 | 0.5.17 | same | Runtime entry and choices kept | +| 0.1.7-rc.2 | 0.5.18, Starter row on | same | Kept; the Starter row is gone | +| 0.1.7-rc.2 | 0.5.19, Starter row on | same | Kept; the Starter row is gone | +| 0.1.7-rc.2 | 0.5.19 pinned, Starter row off | `update --latest` | Recovered from the stuck state | +| 0.2.0-rc.1 | 0.5.19, Starter row on | `update` | Kept; the Starter row is gone | +| 0.2.0-rc.1 | 0.5.19 pinned, Starter row off | `update --latest` | Recovered from the stuck state | + +No run reports a host warning. The Headless upgrade check passes from 0.5.16, 0.5.17, 0.5.18 and 0.5.19 on DSH 0.1.7-rc.2, and from 0.5.19 on 0.2.0-rc.1, with the Runtime memory file unchanged byte for byte. The 0.5.16 web profile names its tab 运行时, so its Runtime continuity comes from this check. + +**Desktop layout.** A 0.5.19 generation with the Starter row turned off reproduces the stuck state on both hosts: no Memory System, all 11 components off, and `mnemon-bundle` waiting for `mnemonStarterReady`. Moving the link to a 0.5.20 generation and restarting brings everything back. + +| Stuck on 0.5.19 | After the 0.5.20 generation and a restart | +| --- | --- | +| ![Stuck](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/starter-group-upgrade/desktop-stuck-0519.png) | ![Updated](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/starter-group-upgrade/desktop-updated-0520.png) | + +**Updating without a restart.** When the link moves while DSH runs and a component is then switched, the Loader keeps the running group's old module. That group waits for the removed readiness service, so the Memory System disappears until DSH restarts: + +![mnemon-bundle (dsh-mnemon/bundle) waiting for mnemonStarterReady after an update without a restart](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/starter-group-upgrade/in-place-no-restart.png) + +The installation guide, the operations table and the compatibility reference now name this message and send it to a restart rather than to the Starter switch, which 0.5.20 no longer has. + +[validation.json](validation.json) holds the candidate digest, the registry setup and every result. diff --git a/dsh/docs/pr-assets/starter-group-upgrade/README.zh-CN.md b/dsh/docs/pr-assets/starter-group-upgrade/README.zh-CN.md new file mode 100644 index 000000000..b7d996b31 --- /dev/null +++ b/dsh/docs/pr-assets/starter-group-upgrade/README.zh-CN.md @@ -0,0 +1,50 @@ +# 组件组的冷启动与升级验收 + +[English](README.md) + +2026-09-29(Asia/Shanghai)针对 `main` 的 `94d1e84f`(#313 与 #314)验证,使用隔离 profile 与合成数据,未修改 DSH。 + +候选包是 `pnpm release:version` 构建出的 0.5.20,尚未发布。本地 registry 提供 npm 的真实元数据,把所有 dsh-mnemon 包的发布时间整体提前两天,并加入 0.5.20。这相当于发布一天后 npm 的样子:0.5.19 及其组件都已超过 pnpm 默认的一天发布期限。一个 pnpm 包装把 DSH 询问的 npm 与 npmmirror 源转到这个 registry。每次网页与命令行测试都从全新的 `HOME`、DSH home 与 pnpm store 开始。 + +## 新用户 + +| 方式 | DSH 0.2.0-rc.1 | DSH 0.1.7-rc.2 | +| --- | --- | --- | +| 网页:**插件 → 添加插件 → dsh-mnemon → 安装 → 立即启用** | 安装 21.8 秒,启用 0.8 秒,只有一个宿主进程 | 13.2 秒与 1.1 秒 | +| 命令行:`dsh plugin --profile web add dsh-mnemon`,再运行 `dsh web` | 安装 9 秒,启动 2 秒 | 8 秒与 1.3 秒 | +| Headless:`scripts/verify-headless-profile.mjs --package dsh-mnemon@0.5.20` | 通过 | 通过 | +| 桌面版布局:装进独立的 generation,profile 只链接根包,在插件列表中打开 | 记忆系统无需重启即出现 | 相同 | + +每次测试结束时,状态页都显示 `dsh-mnemon 0.5.20`、系统正常,组件为 10 个、没有 Starter 行;宿主日志没有警告,控制台没有错误。网页测试还在界面中写入运行时记忆并在刷新后读回,创建并激活 Native 记忆空间,并用**直接检索**找到 Mnemon CLI 写入的事实。 + +**发布当天。** 0.5.20 发布一小时后,两个宿主上的普通安装都选中 0.5.19,可以正常使用。前提是 0.5.19 自身已满一天,也就是 09-29 17:31 UTC 之后。在那之前,由于 0.5.18 与 0.5.19 都在 09-28 发布,npm 给 pnpm 11 的是 0.5.17。09-28 20:12 UTC 对真实 npm 的检查结果:`dsh-mnemon` 与 `dsh-mnemon@latest` 都装到 0.5.17,DSH 0.2 会拒绝它;只有 `dsh-mnemon@0.5.19` 能立即装上。安装指南现在写明普通安装可能退回不止一个版本,并提醒 DSH 0.1.7 会不提示地装上较早的版本。 + +## 老用户 + +每个 profile 先运行旧版本,并在界面中写入一条运行时记忆,打开通用策略、关闭项目档案。0.5.18 与 0.5.19 的 profile 还带有 Starter 行,分别为打开或关闭。 + +| DSH | 旧版本 | 命令(之后重启) | 结果 | +| --- | --- | --- | --- | +| 0.1.7-rc.2 | 0.5.16 | `dsh plugin --profile web update dsh-mnemon` | 0.5.20,选择保留 | +| 0.1.7-rc.2 | 0.5.17 | 同上 | 运行时记忆与选择保留 | +| 0.1.7-rc.2 | 0.5.18,Starter 行打开 | 同上 | 保留;Starter 行消失 | +| 0.1.7-rc.2 | 0.5.19,Starter 行打开 | 同上 | 保留;Starter 行消失 | +| 0.1.7-rc.2 | 固定 0.5.19,Starter 行关闭 | `update --latest` | 从卡住的状态恢复 | +| 0.2.0-rc.1 | 0.5.19,Starter 行打开 | `update` | 保留;Starter 行消失 | +| 0.2.0-rc.1 | 固定 0.5.19,Starter 行关闭 | `update --latest` | 从卡住的状态恢复 | + +所有升级都没有宿主警告。Headless 升级检查在 DSH 0.1.7-rc.2 上从 0.5.16、0.5.17、0.5.18、0.5.19 升级,在 0.2.0-rc.1 上从 0.5.19 升级,全部通过,运行时记忆文件逐字节不变。0.5.16 的网页标签页名为“运行时”,其运行时记忆的延续由这项检查覆盖。 + +**桌面版布局。** 在两个宿主上,关闭了 Starter 行的 0.5.19 generation 都会复现卡住的状态:没有记忆系统,11 个组件全部关闭,`mnemon-bundle` 在等待 `mnemonStarterReady`。把链接改到 0.5.20 的 generation 并重启后恢复。 + +| 卡在 0.5.19 | 换成 0.5.20 的 generation 并重启后 | +| --- | --- | +| ![卡住](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/starter-group-upgrade/desktop-stuck-0519.png) | ![更新后](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/starter-group-upgrade/desktop-updated-0520.png) | + +**更新后不重启。** DSH 运行期间改了链接,再切换一个组件时,加载器会保留运行中组件组的旧模块。这个组件组等待已被移除的就绪服务,记忆系统因此消失,直到重启 DSH: + +![更新后不重启时,mnemon-bundle (dsh-mnemon/bundle) 等待 mnemonStarterReady](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/starter-group-upgrade/in-place-no-restart.png) + +安装指南、运维表格与兼容性参考现在都写明这条提示,处理方法是重启,而不是打开 0.5.20 已经没有的 Starter 开关。 + +[validation.json](validation.json) 记录了候选包摘要、registry 设置与全部结果。 diff --git a/dsh/docs/pr-assets/starter-group-upgrade/validation.json b/dsh/docs/pr-assets/starter-group-upgrade/validation.json new file mode 100644 index 000000000..01382ad63 --- /dev/null +++ b/dsh/docs/pr-assets/starter-group-upgrade/validation.json @@ -0,0 +1,72 @@ +{ + "verifiedAt": "2026-09-28T20:50:48Z", + "revision": "94d1e84fa7d82b1528e3c0ebce749a59f41f7392", + "candidate": { + "name": "dsh-mnemon", + "version": "0.5.20", + "status": "built with pnpm release:version from the revision above; not published", + "sha256": "c446c553c00043d34c3f178e418499689121ec109090f20ac39ecd00276fb342", + "otherPackages": "the 17 components at the pins shared with 0.5.19, from npm" + }, + "registry": { + "afterRelease": "npm's real metadata with every dsh-mnemon* publish time moved 48 hours back, so 0.5.19 and its components are older than a day, plus 0.5.20 published 26 hours earlier", + "releaseDay": "the same, with 0.5.20 published 1 hour earlier", + "wrapper": "a pnpm wrapper sends the npm and npmmirror registries DSH asks to the local registry named in the profile's .npmrc" + }, + "newUsers": { + "web": { + "0.2.0-rc.1": { "flow": "Plugins -> Add plugin -> dsh-mnemon -> Install -> Enable now", "installSource": "chosen by DSH (mainland China mirror)", "installSeconds": 21.8, "enableSeconds": 0.8, "status": "0.5.20, nominal, Mnemon 0.2.7", "runtimeWriteAndReadAfterReload": true, "nativeSpaceActive": "1 / 1", "webDirectRecallFindsCliFact": true, "hostLaunches": 1, "hostWarnings": [], "consoleErrors": [] }, + "0.1.7-rc.2": { "flow": "Plugins -> Add plugin -> dsh-mnemon -> Install -> Enable now", "installSource": "chosen by DSH (mainland China mirror)", "installSeconds": 13.2, "enableSeconds": 1.1, "status": "0.5.20, nominal, Mnemon 0.2.7", "runtimeWriteAndReadAfterReload": true, "nativeSpaceActive": "1 / 1", "webDirectRecallFindsCliFact": true, "hostLaunches": 1, "hostWarnings": [], "consoleErrors": [] } + }, + "commandLine": { + "0.2.0-rc.1": { "command": "dsh plugin --profile web add dsh-mnemon, then dsh web", "installSeconds": 9, "readySeconds": 2, "status": "0.5.20, nominal", "components": "10 total · 5 running · 5 off", "hostWarnings": [] }, + "0.1.7-rc.2": { "command": "dsh plugin --profile web add dsh-mnemon, then dsh web", "installSeconds": 8, "readySeconds": 1.3, "status": "0.5.20, nominal", "components": "10 total · 5 running · 5 off", "hostWarnings": [] } + }, + "headless": { + "script": "scripts/verify-headless-profile.mjs --package dsh-mnemon@0.5.20", + "0.1.7-rc.2": "passed: 37 tools with the 8 representative Mnemon tools, legacy settings import, restart, core gate", + "0.2.0-rc.1": "passed: 37 tools with the 8 representative Mnemon tools, legacy settings import, restart, core gate" + }, + "desktopLayout": { + "layout": "0.5.20 installed into its own generation with pnpm's isolated linker; the profile exposes only a link to the root; not selected at startup", + "0.1.7-rc.2": "switched on in the Plugins list: Memory System without a restart, no notices; after a restart 0.5.20 nominal with 10 components", + "0.2.0-rc.1": "switched on in the Plugins list: Memory System without a restart, no notices; after a restart 0.5.20 nominal with 10 components" + }, + "releaseDay": { + "0.2.0-rc.1": "plain command-line install picks 0.5.19 (0.5.20 is younger than a day); Memory System, 11 components", + "0.1.7-rc.2": "plain command-line install picks 0.5.19 (0.5.20 is younger than a day); Memory System, 11 components" + } + }, + "existingUsers": { + "commandLineUpdate": { + "state": "a runtime entry written in the UI (0.5.17 and later), General strategy on, Project Documents off", + "cases": [ + { "dsh": "0.1.7-rc.2", "from": "0.5.16", "spec": "^0.5.16", "command": "dsh plugin --profile web update dsh-mnemon", "to": "0.5.20", "choicesKept": true, "hostWarnings": [], "note": "0.5.16's tab is named 运行时; runtime continuity covered by the Headless upgrade" }, + { "dsh": "0.1.7-rc.2", "from": "0.5.17", "spec": "^0.5.17", "command": "dsh plugin --profile web update dsh-mnemon", "to": "0.5.20", "runtimeEntryKept": true, "choicesKept": true, "hostWarnings": [] }, + { "dsh": "0.1.7-rc.2", "from": "0.5.18", "starterRow": "on", "spec": "^0.5.18", "command": "dsh plugin --profile web update dsh-mnemon", "to": "0.5.20", "runtimeEntryKept": true, "choicesKept": true, "starterRowGone": true, "hostWarnings": [] }, + { "dsh": "0.1.7-rc.2", "from": "0.5.19", "starterRow": "on", "spec": "^0.5.19", "command": "dsh plugin --profile web update dsh-mnemon", "to": "0.5.20", "runtimeEntryKept": true, "choicesKept": true, "starterRowGone": true, "hostWarnings": [] }, + { "dsh": "0.1.7-rc.2", "from": "0.5.19", "starterRow": "off (stuck: no Memory System, 11 off)", "spec": "0.5.19", "command": "dsh plugin --profile web update --latest dsh-mnemon", "to": "0.5.20", "recovered": true, "choicesKept": true, "hostWarnings": [] }, + { "dsh": "0.2.0-rc.1", "from": "0.5.19", "starterRow": "on", "spec": "^0.5.19", "command": "dsh plugin --profile web update dsh-mnemon", "to": "0.5.20", "runtimeEntryKept": true, "choicesKept": true, "starterRowGone": true, "hostWarnings": [] }, + { "dsh": "0.2.0-rc.1", "from": "0.5.19", "starterRow": "off (stuck: no Memory System, 11 off)", "spec": "0.5.19", "command": "dsh plugin --profile web update --latest dsh-mnemon", "to": "0.5.20", "recovered": true, "choicesKept": true, "hostWarnings": [] } + ] + }, + "headlessUpgrade": { + "script": "scripts/verify-headless-profile.mjs --upgrade-from --package dsh-mnemon@0.5.20", + "0.1.7-rc.2": ["0.5.16", "0.5.17", "0.5.18", "0.5.19"], + "0.2.0-rc.1": ["0.5.19"], + "runtimeMemoryBytesUnchanged": true + }, + "desktopLayout": { + "stuck": "0.5.19 generation with mnemon-starter disabled: no Memory System, 11 off, mnemon-bundle (cordis:group) waiting for mnemonStarterReady; on both hosts", + "updateAndRestart": "link moved to the 0.5.20 generation, then a restart: Memory System, 0.5.20 nominal, 10 components, no warnings; on both hosts", + "inPlaceWithoutRestart": "link moved while DSH ran, then a component switched: the Memory System disappears with mnemon-bundle (dsh-mnemon/bundle): pending (waiting for service: mnemonStarterReady), because a running Loader entry keeps its module; a restart brings 0.5.20 up; on both hosts" + } + }, + "realNpmAt": { + "checkedAt": "2026-09-28T20:12:00Z", + "pnpm": "11.19.0", + "installs": { "dsh-mnemon": "0.5.17", "dsh-mnemon@latest": "0.5.17", "dsh-mnemon@0.5.19": "0.5.19" }, + "reason": "0.5.18 (09-28 13:20Z) and 0.5.19 (09-28 17:31Z) are younger than a day, so pnpm's minimum release age falls back to 0.5.17, which DSH 0.2 refuses; an exact version installs at once" + }, + "environment": { "os": "macOS", "node": "24.19.0", "pnpm": "11.19.0", "mnemonCli": "0.2.7", "browser": "headless Google Chrome over the DevTools protocol", "homes": "a fresh HOME, DSH_HOME and pnpm store for every web and command-line run", "dshModified": false } +} diff --git a/dsh/docs/pr-assets/status-version-maintenance/README.md b/dsh/docs/pr-assets/status-version-maintenance/README.md new file mode 100644 index 000000000..cc6099f28 --- /dev/null +++ b/dsh/docs/pr-assets/status-version-maintenance/README.md @@ -0,0 +1,17 @@ +# Status version maintenance evidence + +Captured on 2026-09-06 from the real DSH 0.1.2-rc.1 WebUI, launched with `scripts/serve-e2e.mjs`. The disposable Profile contains a linked Starter and sixteen official subpackages. Memory directories are empty fixtures; no model requests or production data were used. The source location is a temporary alias. The Host's existing Homebrew CLI was inspected with `--version`; no machine-wide install or update was executed. + +- [Desktop, collapsed](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/status-version-maintenance/desktop-collapsed.png): npm migration command, successful copy feedback, expandable post-install steps, and the subpackage toggle at 1280 × 720. +- [Desktop, expanded](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/status-version-maintenance/desktop-expanded.png): Source and Strategy versions, Starter pins, and maintenance ownership. +- [Mobile, expanded](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/status-version-maintenance/mobile-expanded.png): 390 × 844 bottom sheet with wrapping package names and a fixed footer. The dialog and all sixteen package rows have no horizontal overflow. + +Validation: + +- `pnpm@10.13.1 run verify` passed: bilingual documentation links, types, deterministic builds, all workspace tests, 783 root tests, real Headless activation, package contents and public-entry validation, publint and attw. +- `pnpm@10.13.1 exec node scripts/verify-plugin-artifacts.mjs --skip-build` passed for sixteen independent plugin repositories and seventeen packed artifacts, including real DSH installation of the packed Starter. +- Host regressions exercise npm ownership, Windows launcher invocation, missing/broken installations, exact Profile updates, preserved Starter pins and links, registry failures, concurrent update rejection, and post-update version verification. +- Client regressions exercise missing/offline states, npm guidance and copying, read-only access, update failures, duplicate clicks, package disclosure, and restart reminders. +- The existing opt-in native integration and Windows smoke tests remain skipped on this macOS run. Windows command discovery and invocation are covered by simulated filesystem/process fixtures, not a Windows machine. + +The presentation baseline records the intentional version-maintenance changes separately from the original Source presentation migration baseline. diff --git a/dsh/docs/pr-assets/strategy-extensions-20260901/README.md b/dsh/docs/pr-assets/strategy-extensions-20260901/README.md new file mode 100644 index 000000000..3a435b084 --- /dev/null +++ b/dsh/docs/pr-assets/strategy-extensions-20260901/README.md @@ -0,0 +1,113 @@ +# Additive Strategy plugins — 2026-09-01 verification + +This records implementation verification, not a release or an LLM-quality benchmark. + +## Revisions and scope + +| Item | Revision | +| --- | --- | +| Development branch | `codex/view-concretization` | +| Starting baseline | `721d30651d43fb9942c4bf09349a4a61816780b7` | +| Core contribution lifecycle | `e92eaff` | +| Three-tier slots and Source-qualified retrieval | `ec297c1` | +| Optional plugins and Host write-scope enforcement | `874f8a8` | +| Verified test/artifact harness | `c4c50c9214cd144cf722442c8b697e09877b012c` | + +One selected complete Strategy still owns the View. Optional plugins register +pure, bounded contributions through the existing Fiber-owned `installMemory` +service. Slot semantics belong to the target Strategy, not Core. There is no +second Context service, background scheduler, Source storage migration or default +Starter activation change. Main and experiment branches were not changed; no +push or publication was performed. + +| Optional plugin | Exclusive slot | Effect | +| --- | --- | --- | +| `dsh-mnemon-strategy-scoped` | `selection` | Ordered Source instances and an optional writable subset | +| `dsh-mnemon-strategy-light-context` | `projection` | One shared projection-character ceiling within the Host budget | +| `dsh-mnemon-strategy-auto-capture` | `capture` | Current-turn recording guidance for explicitly named, offered Actions | + +Enabling all three does not change `strategyId`. Conflicting owners of the same +target/slot are rejected atomically. Other complete Strategies do not inherit +these contributions. Disabling one removes its contribution from new turns; +running turns retain their immutable View and lease. Removing selection while +duplicate-role Sources remain restores the normal ambiguity check, not a guess. + +## Verification results + +Environment: macOS, Node `v25.1.0`, pnpm `11.19.0`, pinned DSH `0.1.1-rc.2`. + +| Gate | Observed result | +| --- | --- | +| `pnpm verify` | Exit 0: typechecks, deterministic Root build, plugin builds/tests, full suite, real Headless and package checks | +| Full Root Vitest run | **108 files / 929 tests passed**, 2 files / 2 tests conditionally skipped | +| Deterministic Root build | All 36 generated-file hashes matched | +| Local performance fences | Default and additive profiles each composed 100 three-Source Views within CPU < 2,000 ms and wall < 5,000 ms; unchanged thresholds | +| Real Headless, default | 37 total tools / 7 representative Mnemon tools; preserved settings and idempotent restart | +| Real Headless, additive | All three optional Entries activated; scoped/capture guidance reached the actual DSH model request without changing the selected Strategy | +| Package/public-entry gates | 43 Root packed files, 11 Node-compatible entries, 23 public type dependencies; `publint --strict` and `attw` passed | +| `pnpm verify:plugins` | Exit 0: **16 independent plugin repositories / 17 packed artifacts** | +| Packed real DSH activation | Default-only Starter and a separate install with all three optional packages both passed | +| Independent authoring | Each plugin installed/typechecked/tested/built outside the workspace; an external consumer compiled a new contribution against the packed three-tier SDK, exercised conflict/replacement, and performed local Provider write/recall | +| `pnpm release:check` | Exit 0: 17 coordinated `0.5.0-beta.1` artifacts, `beta` tag; optional plugins are not Starter runtime dependencies | + +The 929-test count already includes plugin tests; independent re-execution is +not added again. Skips are the opt-in real Native binary test and Windows-only +smoke. Upstream UI-primitives missing-source-map warnings remained visible. + +The first full gate exposed old assertions for one Strategy package, two +contribution kinds and a Core-only dependency whitelist. These now require the +four explicit Strategy packages, third contribution kind and only the declared +owner's public SDK (whole-plugin mounting remains test-only). The complete gate +was rerun successfully. Private imports and performance thresholds were not relaxed. + +## Boundary regressions + +- Real Cordis/SDK tests cover load order, mounting before the owner, deterministic + JSON, payload limits, atomic slot conflict, unsupported/inactive targets, + individual unload, failed replacement and pinned old turns. +- Owner tests cover all three contributions, invalid fields, missing Sources, + write narrowing and allocation matrices from tiny budgets through 32 Sources. + Without contributions, original default composition remains intact. +- Identical namespace/query ids across Sources cannot share evidence or Related + admission. Recall still has a shared two-query envelope; Documents and Related + retain their shared quotas. Same-Source empty-read diagnostics survive replay. +- A real Host regression reproduced Runtime capacity archival writing into a + View-read-only Source. Maintenance now checks the offered Action before the + Provider write. Denial leaves Runtime data unchanged; a positive test still + archives when both Sources are writable. Read-only Documents are rejected + before archive preflight starts a worker. Operator management stays separate. +- The Host combination test rejects an unoffered Runtime write, commits a + permitted Provider write, disables only light-context, then recalls the saved + fact with selection/capture still active. +- Packed consumption mounts two Runtime instances and a local Provider, rejects + a competing projection-slot owner, unloads light-context, installs the externally + compiled contribution and preserves the other two contributions. + +After building, repeat the additional real Headless profile with: + +```sh +node scripts/verify-headless-profile.mjs --strategy-extensions true +``` + +Headless uses isolated temporary homes/storage and a loopback model stub. The +artifact runner uses a loopback registry with exact semver manifests, not source +aliases or workspace links. Successful runners remove their temporary test +directories. Personal memory, active profiles and remote accounts were not used. + +## Limits + +- Activation means enabling a DSH Entry, not merely downloading npm files. + Arbitrary complete Strategies cannot be stacked indiscriminately: compatible + distinct slots compose; same-slot conflicts are explicit. +- Capture is guidance, not guaranteed LLM behavior, a hard write counter or a + new autonomous maintenance service. No model-quality improvement is claimed. +- A projection cap is not a token/full-prompt limit, delta injection or + summarization. Runtime has no expansion route; small caps can omit useful hot + memory. Bounded/scoped guidance does not claim a complete store. +- Client and Source storage implementations were unchanged. Automated Client + interaction, copied-data compatibility and local Provider tests passed. This + task did not repeat manual browser clicks or certify live cloud accounts, + real Native/Windows execution, source-only DSH alpha or production latency. + +Usage and author contracts: [English](../../en/development/extensions.md), +[简体中文](../../zh-CN/development/extensions.md). diff --git a/dsh/docs/pr-assets/strategy-names-20260927/README.md b/dsh/docs/pr-assets/strategy-names-20260927/README.md new file mode 100644 index 000000000..b033b0de0 --- /dev/null +++ b/dsh/docs/pr-assets/strategy-names-20260927/README.md @@ -0,0 +1,29 @@ +# Strategy names + +[简体中文](./README.zh-CN.md) + +Tested implementation: `852373e4`, captured with the data directory change stacked on it, which leaves the names unchanged. macOS 15.6, Node 25.1.0, published DSH 0.1.7-rc.2, headless Chrome at 1280 × 860. Each run walks an isolated WebUI fixture from the Starter's defaults with its test model. No personal memory or credentials were used. + +## The main Strategy selector + +The two main Strategies name what they do: the Layered strategy keeps runtime memory resident and reads Documents and Memory Spaces on demand; the General strategy offers every Source and lets the model decide. + +| Light | Dark | +|---|---| +| ![Main strategy menu](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/strategy-names-20260927/names-menu.png) | ![Main strategy menu, dark](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/strategy-names-20260927/dark-names-menu.png) | + +## DSH's component list + +DSH names each row from the package's display metadata, so the list reads the same names as the selector. + +![Component rows](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/strategy-names-20260927/names-rows.png) + +## Verification + +- `tests/plugin-metadata.spec.ts` holds each package's DSH metadata to its declaration; composition, enhancement and page tests use the new names. +- Full `pnpm run verify`, `pnpm run verify:plugins`, `pnpm run verify:docs` and `pnpm run release:intent` passed. +- WebUI: 2 light and 1 dark state captured by a scripted walk with no console errors. + +## Limits + +Type ids (`default-three-tier`, `general`), package names and configuration keys keep their names, so saved choices and data are unaffected. Published release notes keep the names they shipped with. diff --git a/dsh/docs/pr-assets/strategy-names-20260927/README.zh-CN.md b/dsh/docs/pr-assets/strategy-names-20260927/README.zh-CN.md new file mode 100644 index 000000000..315200a62 --- /dev/null +++ b/dsh/docs/pr-assets/strategy-names-20260927/README.zh-CN.md @@ -0,0 +1,29 @@ +# 策略名称 + +[English](./README.md) + +实测实现:`852373e4`,截图时叠加了其上的数据目录改动,名称保持不变。macOS 15.6、Node 25.1.0、已发布的 DSH 0.1.7-rc.2、headless Chrome 1280 × 860。每次运行都从 Starter 默认配置与其测试模型的隔离 WebUI fixture 开始,未使用个人记忆或凭据。 + +## 主策略选择器 + +两个主策略的名称直接说明它们做什么:分层策略让运行时记忆常驻,档案与记忆空间按需读取;通用策略提供全部 Source,由模型决定如何使用。 + +| 浅色 | 深色 | +|---|---| +| ![主策略菜单](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/strategy-names-20260927/names-menu.png) | ![主策略菜单(深色)](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/strategy-names-20260927/dark-names-menu.png) | + +## DSH 组件列表 + +DSH 按包的显示元数据为每一行命名,列表与选择器使用同样的名称。 + +![组件列表](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/strategy-names-20260927/names-rows.png) + +## 验证 + +- `tests/plugin-metadata.spec.ts` 校验各包的 DSH 元数据与其声明一致;组合、增强与组件页测试使用新名称。 +- 完整的 `pnpm run verify`、`pnpm run verify:plugins`、`pnpm run verify:docs` 与 `pnpm run release:intent` 均通过。 +- WebUI:脚本化走查截取 2 个浅色与 1 个深色状态,控制台无错误。 + +## 限制 + +类型 id(`default-three-tier`、`general`)、包名与配置键保持不变,已保存的选择与数据不受影响。已发布的版本说明保留发布时的名称。 diff --git a/dsh/docs/pr-assets/version-update-through-dsh/README.md b/dsh/docs/pr-assets/version-update-through-dsh/README.md new file mode 100644 index 000000000..3f0e7f76c --- /dev/null +++ b/dsh/docs/pr-assets/version-update-through-dsh/README.md @@ -0,0 +1,193 @@ +# One-click update in the desktop app + +[简体中文](./README.zh-CN.md) | [Verification data](./verification.json) + +In the desktop app, **Status → Check versions** found a newer dsh-mnemon but could not update it. The dialog only said 检测到 DSH Profile 安装,但当前找不到 pnpm 命令。, because the version check ran pnpm from PATH and the app's Host has none. The app ships its own pnpm and hands it to DSH's plugin manager. With this change the Starter updates through that plugin manager, so the app's own pnpm installs it. After DSH swaps in the new page, the Memory System reopens on the result. Until DSH restarts, a notice above every Memory System page names the installed version and the one that still runs. + +Tested revision: `b6e463569103c8380d6159ee4108e20d505a8da1`. Baseline: dsh-mnemon 0.5.20 from npm. The runs took place on 2026-10-02 (Asia/Shanghai): +- macOS 15.6 arm64 and Node 24.19.0; +- headless Chrome 154 at 1280×800, zh-CN, light; +- a fresh home, DSH home and pnpm store for each run. + +Profiles and memories are synthetic. + +## How the desktop app was reproduced + +The installed DeepSeek Harness app (0.2.0-rc.2) starts its Host with `runProfile({ packageManager })`. That `packageManager` runs `Contents/Resources/runtime/pnpm/bin/pnpm.mjs` with the app's own executable, and DSH's plugin manager uses it instead of a PATH pnpm. + +These runs start npm DSH 0.2.0-rc.2 the same way: +- a small launcher calls DSH's exported `runCli({ packageManager })` with a copy of the app's pnpm 11.7.0; +- the Host gets the app Host's own PATH, which has no pnpm. + +The app itself was not driven: it owns a fixed port and the user's profile, and its files were only read. + +## Before: published 0.5.20 + +![dsh-mnemon 0.5.20 can update to 0.5.21, but the dialog only says pnpm is missing and offers no Update button](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/version-update-through-dsh/before.jpg) + +The row reads 可更新 0.5.20 → 0.5.21 with 检测到 DSH Profile 安装,但当前找不到 pnpm 命令。 and no **Update** button, as reported. + +## After: update, reopen, restart + +A loopback registry serves npm's real metadata plus this revision's Starter twice: +- as `0.5.21-dshupdate.0`, installed first; +- as `0.5.21`, in place of npm's 0.5.21. + +The version check reads npm's own `latest`, 0.5.21, so the dialog offers that update. Serving this revision as 0.5.21 lets the update land on a page that contains this change, as an update from this release to a later one will. + +| Offered | Updating | +|---|---| +| ![The row offers Update through DSH's own plugin installer](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/version-update-through-dsh/offered.jpg) | ![Updating](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/version-update-through-dsh/updating.jpg) | + +| Reopened with the result | After a restart | +|---|---| +| ![The reopened dialog: dsh-mnemon updated, restart DSH](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/version-update-through-dsh/reopened.jpg) | ![After a restart: up to date, no restart notice](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/version-update-through-dsh/restarted.jpg) | + +1. The dsh-mnemon row reads 由当前 DSH Profile 管理;用 DSH 自己的插件安装方式更新,完成后重启 DSH。 with **Update**. +2. DSH's plugin manager ran `pnpm add dsh-mnemon@0.5.21` with the app's pnpm, and the profile's log ends `Done in 384ms using pnpm v11.7.0`. The profile now lists `"dsh-mnemon": "0.5.21"` and its node_modules holds 0.5.21. The enabled bundles are unchanged. +3. 1.64 s after the update request, the page fetched dsh-mnemon's new `client.js` revision, then the components' clients. DSH's client HMR had swapped in the new page, while the Host kept running the old code. +4. The new page reopened Status and **Check versions**. By 3.75 s the dialog showed **dsh-mnemon 已更新** with the restart notice, and the row read 待重启 · 0.5.21. +5. After DSH was stopped and started again, Status reads dsh-mnemon 0.5.21 / 系统正常. The dialog reads 已是最新, with no restart notice and no **Update** button. + +![The update sequence: offered, updating, reopened while checking, then the result](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/version-update-through-dsh/update-sequence.gif) + +## Why the page reopens + +DSH's shipped Web composition, which the desktop app also runs, includes `dsh-client-hmr`. It polls each plugin's `lib/client.js` and swaps a changed plugin into open pages, which drops the plugin's React state. An in-place update changes the Starter's files, so the Memory System and its dialog used to close about two seconds after the click, often before the update reported back. The pnpm route behaved the same way. + +With npm's real 0.5.21 as the target, whose page lacks this change, the update installed npm's tarball, and its integrity matches npm's. The page then showed DSH's home without the Memory System at 2.5 s. + +Now the dialog records a Starter update in the page's session storage before it starts: +- the page DSH swaps in reopens the Memory System, and Status reopens the dialog; +- the dialog shows the result once the Host reports the restart that version needs; +- the Host's version check waits up to 3 s for an update that is still finishing, so the reopened dialog sees how it ended. + +## DSH 0.1.7-rc.2 + +DSH 0.1.7-rc.2's CLI entry takes no package manager from a launcher, so its plugin manager runs pnpm from PATH. This run used `dsh web` with pnpm 11.19.0 on PATH: +- the update went through DSH's plugin manager (`Done in 562ms using pnpm v11.19.0`); +- the new page arrived 2.42 s after the request, and the dialog reopened with the result by 3.70 s; +- after a restart, the dialog read 已是最新. + +## Without any package manager + +![The row asks for pnpm and offers no Update button](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/version-update-through-dsh/no-package-manager.jpg) + +Here npm DSH 0.2.0-rc.2 was started directly with `dsh web`, without a launcher and without pnpm on PATH, so DSH's own Plugins page cannot install either. The row reads 可更新 with 检测到 DSH Profile 安装,但宿主找不到 pnpm 命令;安装 pnpm 并重启 DSH 后即可在此更新。, and there is no **Update** button. + +## An optional Strategy added on its own + +| Offered | Updated | +|---|---| +| ![dsh-mnemon-strategy-scoped 0.5.4, maintained by the profile, offers Update](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/version-update-through-dsh/strategy-offered.jpg) | ![dsh-mnemon-strategy-scoped updated, restart DSH](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/version-update-through-dsh/strategy-updated.jpg) | + +`dsh-mnemon-strategy-scoped@0.5.4` from npm was added with the packaged-app `dsh plugin add`. It is a DSH bundle the profile maintains (Profile 独立维护). Its **Update** went through DSH's plugin manager to npm's 0.5.5 (`Done in 267ms using pnpm v11.7.0`). Only the Strategy's page changed, so the dialog stayed open, and the enabled bundles were unchanged. + +## Restart reminder + +An update replaces the installed files, but the Host keeps the code it loaded until DSH restarts, while DSH already swaps in the new page. Revision `375d1ba43fae7de1b82e9717ec69dadd1a8375db` adds a notice above every Memory System page that names the installed version and the one that still runs, and Status shows the running version: +- the Host records the version it loaded and compares it with the Starter on disk on each status read, so the notice covers `dsh plugin` too; +- **Check versions** reads the installed Starter from disk as well. + +These runs used a build of that revision, served as above: `0.5.21-dshupdate.0` installed first, and the same build as `0.5.21`. + +| After Check versions updated the Starter | After a restart | +|---|---| +| ![Above Status: dsh-mnemon 0.5.21 is installed, while 0.5.21-dshupdate.0 still runs](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/version-update-through-dsh/reminder-installed.jpg) | ![After a restart: no notice, and the engine card reads dsh-mnemon 0.5.21](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/version-update-through-dsh/reminder-restarted.jpg) | + +**Check versions, on DSH 0.2.0-rc.2 with the packaged-app launcher:** +- DSH's plugin manager installed 0.5.21 (`Done in 598ms using pnpm v11.7.0`). +- Status and Runtime both showed **dsh-mnemon 0.5.21 已安装** with 当前运行的仍是 0.5.21-dshupdate.0;重启 DSH 后生效,桌面版请完全退出后重新打开。 +- The engine card kept the running dsh-mnemon 0.5.21-dshupdate.0. Check versions read 待重启, installed 0.5.21, with no **Update** button. +- After a restart, the notice was gone, the engine card read dsh-mnemon 0.5.21, and the dialog read 已是最新. + +**`dsh plugin add` while the Memory System was open:** +- The packaged-app `dsh plugin --profile web add dsh-mnemon@0.5.21` ran in the same profile while DSH served the page (`Done in 419ms using pnpm v11.7.0`). +- About 0.5 s after it returned, DSH swapped in the new client and the open Memory System closed to DSH's home. This is DSH's client HMR; the dialog's reopening covers only updates started from the dialog. +- Reopened from the sidebar, Status and Runtime showed the same notice. Check versions read 待重启 with 0.5.21 installed and no **Update** button, instead of offering the version already installed. + +![Check versions after dsh plugin add: 0.5.21 installed, restart needed, no Update button](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/version-update-through-dsh/reminder-cli-dialog.jpg) + +**Back to the running version:** `dsh plugin add dsh-mnemon@0.5.21-dshupdate.0` closed the Memory System the same way. Reopened, it showed no notice, and Check versions offered 0.5.21-dshupdate.0 → 0.5.21 again. + +**An optional Strategy updated from Check versions:** +- `dsh-mnemon-strategy-scoped@0.5.4` was added on its own, then updated to 0.5.5 (`Done in 312ms using pnpm v11.7.0`). +- Status and Runtime showed **dsh-mnemon-strategy-scoped 已更新** with 重启 DSH 后生效,桌面版请完全退出后重新打开。 The Starter stayed 0.5.21-dshupdate.0. +- The notice was gone after a restart. + +![Above Runtime: dsh-mnemon-strategy-scoped updated, restart DSH](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/version-update-through-dsh/reminder-strategy.jpg) + +**DSH 0.1.7-rc.2:** `dsh web` ran with pnpm 11.19.0 on PATH. +- Check versions updated the Starter through DSH's plugin manager (`Done in 419ms using pnpm v11.19.0`). +- The same notice appeared on Status and Runtime. +- After a restart, it was gone, and the engine card read 0.5.21. + +None of these runs logged a console error. + +## Design review + +An independent review of this PR found no broken design or contract. It found five behaviors worth fixing, all fixed in `f395626a87bbbc5fea8baef4252f7bf369880084`: +- **Failure messages.** A failed install named the first line of pnpm's output, often a retry warning, and hid the `ERR_PNPM_*` line. The dialog now names the error line, the failure kind DSH read off it, and DSH's log path. +- **Files a failed install leaves.** DSH restores the Profile's `package.json` and lockfile when an install fails, but files it downloaded can stay. Check versions read `node_modules`, so a failed update could look installed and waiting for a restart. Installed is now the exact version the Profile records, for the Starter and for a package the Profile added, with `node_modules` as the fallback. +- **The reopened dialog.** The page DSH swaps in judged success from that same state. The Host now keeps how the last update ended and returns it with each check, and the reopened dialog shows that success or error. +- **A package put back.** A Strategy updated from the dialog and then put back with `dsh plugin` stayed named in the notice until a restart. Each update now remembers the version it replaced. +- **Reload after a restart.** With the dialog left open across a DSH restart, a reload within ten minutes reopened it. Status now waits for the Host, and drops the record once DSH runs the updated version with nothing pending. + +The live runs above were repeated on that revision, on the same setup: +- Check versions on DSH 0.2.0-rc.2 and 0.1.7-rc.2: the notice and 待重启 · 0.5.21 before the restart, no notice and 已是最新 after it. +- `dsh plugin add` and putting the version back: the same results as before. +- An optional Strategy updated and restarted: the same results as before. +- **A real failure.** The Profile's registry served no 0.5.21 while npm named it. Clicking Update failed with: + + `DSH could not install dsh-mnemon@0.5.21: operation-error (no-matching-version: [ERR_PNPM_NO_MATCHING_VERSION] No matching version found for dsh-mnemon@0.5.21 while fetching it from http://127.0.0.1:/); log: /.plugin-manager/logs//pnpm.log` + + No notice followed, and Check versions still read 可更新 · 0.5.21-dshupdate.0 with **Update**. + +No run logged a console error. + +## Automated checks + +`pnpm run verify` passes on the tested revision. It covers: +- docs (2,965 local links), typecheck and the deterministic build; +- build, typecheck and tests for all 17 plugins; +- root tests: 113 files, 1,578 passed, 6 skipped; +- Headless activation and public entries, publint and attw; +- package contents at 1,506,720 unpacked bytes, with the budget moved to 1,509,000. + +On the reminder revision, `pnpm run verify` passes again: docs at 2,997 local links, root tests at 113 files with 1,584 passed and 6 skipped, and package contents at 1,511,189 unpacked bytes, with the budget moved to 1,515,000. + +On the design-review revision `f395626a` it passes with root tests at 113 files, 1,592 passed and 6 skipped, docs at 2,997 local links, and package contents at 1,514,466 unpacked bytes, with the budget moved to 1,517,000. New tests there cover: the error line and log in a failure; the recorded Starter after a failed install that leaves files, with the failure reported; a finished update reported to a swapped-in page; a package put back to the version it replaced; the reopened dialog showing the outcome the Host reports, ignoring an older one; and Status not reopening the dialog once DSH has restarted onto the update. + +New Host tests cover: +- DSH's route without a PATH pnpm, and its preference over one; +- the exact spec with `enabled: false`; +- never another profile; +- the pnpm route without a plugin manager, and no update without any package manager; +- DSH's route with a PATH pnpm where the launcher supplies none; +- failure reasons: the first diagnostic line, the DSH an incompatible release needs, and an installer that throws; +- a version mismatch, and a check that waits for a finishing update; +- a Strategy bundle through DSH while a package that is not a bundle stays with pnpm. + +New Client tests cover: +- the record: written for a Starter update, kept on success, cleared on close or failure, never written for the CLI; +- the reopened dialog, which shows the outcome the Host reports and claims nothing else; +- reopening the workspace only for a fresh record; +- Status reopening the dialog in the workbench. + +New tests for the reminder cover: +- the restart status: an installed Starter however it arrived, and packages Check versions updated on their own; +- a Starter `dsh plugin` installed, which Check versions shows as installed and waiting for the restart, with no update to offer; +- no notice once the Starter on disk is the running one again; +- the status every page reads, with the running version and an installed update; +- the notice on every page, for the Starter and for packages, and none otherwise. + +## Limits + +- The desktop app was reproduced with npm DSH 0.2.0-rc.2 through the same `packageManager` contract and a copy of the app's pnpm. The packaged app itself was not driven. +- The Starter updates on 0.2.0-rc.2 and 0.1.7-rc.2 install this revision served as 0.5.21, so the swap can be shown without a newer release. Another run installs npm's real 0.5.21, whose page lacks the reopening. +- The reopening starts with updates from the release that contains this change. On 0.5.21 or earlier, the desktop app needs one removal and reinstall on its Plugins page, as the guides say. +- The live runs use the default sidebar layout. The builtin layout reopens through the same workspace opener, which unit tests cover. +- Without session storage the update still works; only the reopening is lost. +- After `dsh plugin` installs an update, DSH's client HMR closes an open Memory System. The notice shows once it is opened again. +- DSH's Plugins page, which updates only by removing and installing again, was not run. +- Only the Starter is compared with the version that runs. A package other than the Starter that is updated outside Check versions is not named. diff --git a/dsh/docs/pr-assets/version-update-through-dsh/README.zh-CN.md b/dsh/docs/pr-assets/version-update-through-dsh/README.zh-CN.md new file mode 100644 index 000000000..ef1327b75 --- /dev/null +++ b/dsh/docs/pr-assets/version-update-through-dsh/README.zh-CN.md @@ -0,0 +1,193 @@ +# 桌面版一键更新 + +[English](./README.md) | [验证数据](./verification.json) + +在桌面版中,**状态 → 检查版本**能发现更新的 dsh-mnemon,却无法更新:面板只显示“检测到 DSH Profile 安装,但当前找不到 pnpm 命令。”。原因是版本检查运行 PATH 上的 pnpm,而桌面版的 Host 没有。桌面版自带 pnpm,并把它交给 DSH 的插件管理器。本次改动让 Starter 通过这个插件管理器更新,于是由桌面版自带的 pnpm 完成安装。DSH 换上新页面后,记忆系统会重新打开并显示结果。在 DSH 重启之前,记忆系统每个页面的顶部都会提示已安装的版本与仍在运行的版本。 + +受测 revision:`b6e463569103c8380d6159ee4108e20d505a8da1`。基线:npm 上的 dsh-mnemon 0.5.20。验证于 2026-10-02(北京时间): +- macOS 15.6 arm64 与 Node 24.19.0; +- 无头 Chrome 154,1280×800,zh-CN,浅色; +- 每次运行都使用全新的 home、DSH home 与 pnpm store。 + +profile 与记忆均为合成数据。 + +## 如何复现桌面版 + +已安装的 DeepSeek Harness 桌面版(0.2.0-rc.2)以 `runProfile({ packageManager })` 启动 Host。这个 `packageManager` 用应用自己的可执行文件运行 `Contents/Resources/runtime/pnpm/bin/pnpm.mjs`,DSH 的插件管理器会用它代替 PATH 上的 pnpm。 + +这些运行以同样的方式启动 npm 上的 DSH 0.2.0-rc.2: +- 一个小启动器调用 DSH 导出的 `runCli({ packageManager })`,传入桌面版 pnpm 11.7.0 的副本; +- Host 使用桌面版 Host 自己的 PATH,其中没有 pnpm。 + +没有直接驱动桌面版本身:它占用固定端口和用户的 profile,测试只读取了它的文件。 + +## 修复前:已发布的 0.5.20 + +![dsh-mnemon 0.5.20 可更新到 0.5.21,但面板只提示找不到 pnpm,没有更新按钮](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/version-update-through-dsh/before.jpg) + +该行显示“可更新 0.5.20 → 0.5.21”与“检测到 DSH Profile 安装,但当前找不到 pnpm 命令。”,没有**更新**按钮,与反馈一致。 + +## 修复后:更新、重新打开、重启 + +本机 registry 返回 npm 的真实元数据,并把本 revision 的 Starter 提供两次: +- 作为 `0.5.21-dshupdate.0`,先安装它; +- 作为 `0.5.21`,替代 npm 上的 0.5.21。 + +版本检查读取的是 npm 自己的 `latest`,即 0.5.21,因此面板提供这次更新。把本 revision 作为 0.5.21 提供,更新后换上的页面就包含本次改动,与今后从本版本更新到更高版本的情形相同。 + +| 提供更新 | 更新中 | +|---|---| +| ![该行通过 DSH 自己的插件安装器提供更新](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/version-update-through-dsh/offered.jpg) | ![更新中](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/version-update-through-dsh/updating.jpg) | + +| 重新打开并显示结果 | 重启之后 | +|---|---| +| ![重新打开的面板:dsh-mnemon 已更新,请重启 DSH](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/version-update-through-dsh/reopened.jpg) | ![重启之后:已是最新,没有重启提示](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/version-update-through-dsh/restarted.jpg) | + +1. dsh-mnemon 一行显示“由当前 DSH Profile 管理;用 DSH 自己的插件安装方式更新,完成后重启 DSH。”与**更新**按钮。 +2. DSH 的插件管理器用桌面版的 pnpm 运行 `pnpm add dsh-mnemon@0.5.21`,profile 日志结尾为 `Done in 384ms using pnpm v11.7.0`。profile 现在记录 `"dsh-mnemon": "0.5.21"`,node_modules 中为 0.5.21,已启用的 bundle 不变。 +3. 更新请求发出 1.64 秒后,页面请求了 dsh-mnemon 新修订的 `client.js`,随后是各组件的客户端:DSH 的客户端 HMR 换上了新页面,而 Host 仍在运行旧代码。 +4. 新页面重新打开状态页与**检查版本**。到 3.75 秒时,面板显示 **dsh-mnemon 已更新**与重启提示,该行为“待重启 · 0.5.21”。 +5. 停止并重新启动 DSH 后,状态页显示 dsh-mnemon 0.5.21 / 系统正常;面板显示“已是最新”,没有重启提示,也没有**更新**按钮。 + +![更新过程:提供更新、更新中、重新打开并检查、显示结果](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/version-update-through-dsh/update-sequence.gif) + +## 为什么要重新打开页面 + +DSH 发行版的 Web 组合(桌面版同样运行它)包含 `dsh-client-hmr`。它会轮询每个插件的 `lib/client.js`,把发生变化的插件换进已打开的页面,插件的 React 状态随之丢失。原地更新会改变 Starter 的文件,因此此前在点击约两秒后,记忆系统与面板就会关闭,往往早于更新报告结果。pnpm 路径同样如此。 + +以 npm 上真实的 0.5.21 为目标时,它的页面没有本次改动:更新安装了 npm 的 tarball,完整性校验值与 npm 一致;随后在 2.5 秒时页面回到 DSH 首页,记忆系统已关闭。 + +现在面板会在开始更新前,把 Starter 更新记在页面的 session storage 中: +- DSH 换上的新页面会重新打开记忆系统,状态页再重新打开面板; +- Host 报告该版本需要重启后,面板显示结果; +- Host 的版本检查最多等待 3 秒,让仍在收尾的更新完成,因此重新打开的面板能看到更新的结局。 + +## DSH 0.1.7-rc.2 + +DSH 0.1.7-rc.2 的 CLI 入口不接受启动器提供的包管理器,它的插件管理器运行 PATH 上的 pnpm。本次运行使用 `dsh web`,PATH 上为 pnpm 11.19.0: +- 更新经由 DSH 的插件管理器完成(`Done in 562ms using pnpm v11.19.0`); +- 新页面在请求后 2.42 秒到达,面板在 3.70 秒前重新打开并显示结果; +- 重启后,面板显示“已是最新”。 + +## 没有任何包管理器时 + +![该行提示安装 pnpm,没有更新按钮](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/version-update-through-dsh/no-package-manager.jpg) + +这里直接用 `dsh web` 启动 npm 上的 DSH 0.2.0-rc.2,没有启动器,PATH 上也没有 pnpm,此时 DSH 自己的插件页同样无法安装。该行显示“可更新”与“检测到 DSH Profile 安装,但宿主找不到 pnpm 命令;安装 pnpm 并重启 DSH 后即可在此更新。”,没有**更新**按钮。 + +## 单独安装的可选 Strategy + +| 提供更新 | 已更新 | +|---|---| +| ![由 profile 独立维护的 dsh-mnemon-strategy-scoped 0.5.4 提供更新](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/version-update-through-dsh/strategy-offered.jpg) | ![dsh-mnemon-strategy-scoped 已更新,请重启 DSH](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/version-update-through-dsh/strategy-updated.jpg) | + +来自 npm 的 `dsh-mnemon-strategy-scoped@0.5.4` 通过打包版的 `dsh plugin add` 添加,是由 profile 独立维护的 DSH bundle(Profile 独立维护)。它的**更新**经由 DSH 的插件管理器升到 npm 上的 0.5.5(`Done in 267ms using pnpm v11.7.0`)。只有这个 Strategy 的页面发生变化,所以面板保持打开,已启用的 bundle 不变。 + +## 重启提醒 + +更新会替换已安装的文件,但在 DSH 重启之前,Host 仍运行它已加载的代码,而 DSH 已经换上新页面。Revision `375d1ba43fae7de1b82e9717ec69dadd1a8375db` 在记忆系统每个页面的顶部加了一条提示,写明已安装的版本与仍在运行的版本,状态页则显示正在运行的版本: +- Host 记下自己加载的版本,每次读取状态时与磁盘上的 Starter 比较,因此 `dsh plugin` 的更新同样会提示; +- **检查版本**也从磁盘读取已安装的 Starter。 + +这些运行使用该 revision 的构建,提供方式同上:先安装 `0.5.21-dshupdate.0`,同一构建作为 `0.5.21`。 + +| 检查版本更新 Starter 之后 | 重启之后 | +|---|---| +| ![状态页顶部:dsh-mnemon 0.5.21 已安装,当前运行的仍是 0.5.21-dshupdate.0](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/version-update-through-dsh/reminder-installed.jpg) | ![重启之后:没有提示,记忆引擎卡片显示 dsh-mnemon 0.5.21](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/version-update-through-dsh/reminder-restarted.jpg) | + +**检查版本,DSH 0.2.0-rc.2 加桌面版启动方式:** +- DSH 的插件管理器安装了 0.5.21(`Done in 598ms using pnpm v11.7.0`)。 +- 状态页与运行时记忆页都显示 **dsh-mnemon 0.5.21 已安装** 与“当前运行的仍是 0.5.21-dshupdate.0;重启 DSH 后生效,桌面版请完全退出后重新打开。”。 +- 记忆引擎卡片仍显示正在运行的 dsh-mnemon 0.5.21-dshupdate.0。检查版本显示“待重启”,当前版本 0.5.21,没有**更新**按钮。 +- 重启之后提示消失,记忆引擎卡片显示 dsh-mnemon 0.5.21,面板显示“已是最新”。 + +**记忆系统打开时运行 `dsh plugin add`:** +- DSH 提供页面期间,在同一 profile 中运行桌面版方式的 `dsh plugin --profile web add dsh-mnemon@0.5.21`(`Done in 419ms using pnpm v11.7.0`)。 +- 命令返回约 0.5 秒后,DSH 换上新的客户端,打开着的记忆系统关闭并回到 DSH 首页。这是 DSH 的客户端 HMR;面板的重新打开只覆盖从面板发起的更新。 +- 从侧栏重新打开后,状态页与运行时记忆页显示同样的提示。检查版本显示“待重启”、当前版本 0.5.21,没有**更新**按钮,不会再次提供已经安装的版本。 + +![dsh plugin add 之后的检查版本:当前版本 0.5.21,待重启,没有更新按钮](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/version-update-through-dsh/reminder-cli-dialog.jpg) + +**回到正在运行的版本:**`dsh plugin add dsh-mnemon@0.5.21-dshupdate.0` 同样关闭了记忆系统。重新打开后没有提示,检查版本再次提供 0.5.21-dshupdate.0 → 0.5.21。 + +**从检查版本更新可选 Strategy:** +- 先单独添加 `dsh-mnemon-strategy-scoped@0.5.4`,再更新到 0.5.5(`Done in 312ms using pnpm v11.7.0`)。 +- 状态页与运行时记忆页显示 **dsh-mnemon-strategy-scoped 已更新** 与“重启 DSH 后生效,桌面版请完全退出后重新打开。”。Starter 仍是 0.5.21-dshupdate.0。 +- 重启之后提示消失。 + +![运行时记忆页顶部:dsh-mnemon-strategy-scoped 已更新,请重启 DSH](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/version-update-through-dsh/reminder-strategy.jpg) + +**DSH 0.1.7-rc.2:**`dsh web`,PATH 上有 pnpm 11.19.0。 +- 检查版本通过 DSH 的插件管理器更新了 Starter(`Done in 419ms using pnpm v11.19.0`)。 +- 状态页与运行时记忆页出现同样的提示。 +- 重启之后提示消失,记忆引擎卡片显示 0.5.21。 + +所有运行都没有控制台错误。 + +## 设计复核 + +对本 PR 的独立复核没有发现破坏现有设计或契约的地方,但指出了五处值得修正的行为,都已在 `f395626a87bbbc5fea8baef4252f7bf369880084` 中修正: +- **失败信息。** 安装失败时,面板显示的是 pnpm 输出的第一行,往往是重试警告,真正的 `ERR_PNPM_*` 行被藏起来了。现在面板显示错误行、DSH 判断出的失败类别和 DSH 的日志路径。 +- **安装失败留下的文件。** 安装失败时,DSH 会恢复 Profile 的 `package.json` 与锁文件,但已下载的文件可能留下。“检查版本”此前读取 `node_modules`,失败的更新可能看起来已安装、待重启。现在 Starter 和 Profile 单独添加的包,都以 Profile 记录的精确版本为当前版本,`node_modules` 作为后备。 +- **重新打开的面板。** DSH 换上的新页面此前也依据上述状态判断成败。现在 Host 记住上一次更新的结果,并随每次检查返回,重新打开的面板显示这次的成功或错误。 +- **退回原版本的包。** 在面板中更新的 Strategy,之后用 `dsh plugin` 退回原版本,重启前提示仍会列出它。现在每次更新都会记住被替换的版本。 +- **重启后刷新。** 面板一直开着、DSH 重启后,10 分钟内刷新页面会再次打开面板。现在状态页先等待 Host 的回答;DSH 已运行更新后的版本且没有待重启的内容时,丢弃这条记录。 + +上述实测在该 revision 上以相同环境重跑: +- DSH 0.2.0-rc.2 与 0.1.7-rc.2 上从检查版本更新:重启前显示提示和“待重启 · 0.5.21”,重启后没有提示、显示“已是最新”。 +- `dsh plugin add` 以及退回原版本:结果与此前相同。 +- 更新单独添加的 Strategy 并重启:结果与此前相同。 +- **真实的失败。** Profile 的安装源没有 0.5.21,而 npm 列出了它。点击“更新”失败,面板显示: + + `DSH could not install dsh-mnemon@0.5.21: operation-error (no-matching-version: [ERR_PNPM_NO_MATCHING_VERSION] No matching version found for dsh-mnemon@0.5.21 while fetching it from http://127.0.0.1:<端口>/); log: /.plugin-manager/logs//pnpm.log` + + 之后没有提示,“检查版本”仍显示“可更新 · 0.5.21-dshupdate.0”并提供**更新**。 + +所有运行都没有控制台错误。 + +## 自动检查 + +`pnpm run verify` 在受测 revision 上通过,覆盖: +- 文档(2,965 个本地链接)、类型检查与确定性构建; +- 全部 17 个插件的构建、类型检查与测试; +- 根测试:113 个文件,1,578 通过,6 跳过; +- Headless 激活与公开入口、publint 和 attw; +- 包内容为 1,506,720 字节(解压后),预算调整为 1,509,000。 + +在提醒所在的 revision 上,`pnpm run verify` 再次通过:文档 2,997 个本地链接,根测试 113 个文件、1,584 通过、6 跳过,包内容为 1,511,189 字节(解压后),预算调整为 1,515,000。 + +在设计复核后的 revision `f395626a` 上同样通过:根测试 113 个文件、1,592 通过、6 跳过,文档 2,997 个本地链接,包内容为 1,514,466 字节(解压后),预算调整为 1,517,000。新增测试覆盖:失败时的错误行与日志;安装失败留下文件时仍以 Profile 记录的版本为准,并报告失败;向换上的页面报告已完成的更新;退回被替换版本的包;重新打开的面板显示 Host 报告的结果并忽略更早的结果;DSH 已重启到更新后的版本时,状态页不再打开面板。 + +新增的 Host 测试覆盖: +- PATH 上没有 pnpm 时走 DSH 路径,且 PATH 上有 pnpm 时仍优先走 DSH; +- 精确版本与 `enabled: false`; +- 从不写入其他 profile; +- 没有插件管理器时走 pnpm,没有任何包管理器时不提供更新; +- 启动器不提供包管理器而 PATH 上有 pnpm 时走 DSH 路径; +- 失败原因:诊断的第一行、不兼容版本所需的 DSH,以及安装器直接抛错; +- 版本不一致,以及等待更新收尾的检查; +- Strategy bundle 走 DSH,而不是 bundle 的包仍走 pnpm。 + +新增的 Client 测试覆盖: +- 记录:Starter 更新时写入、成功后保留、关闭或失败时清除,CLI 更新不写入; +- 重新打开的面板只显示 Host 报告的结果,不额外声称更新; +- 仅在记录较新时重新打开工作台; +- 工作台中状态页重新打开面板。 + +新增的提醒测试覆盖: +- 重启状态:无论以何种方式安装的 Starter,以及检查版本单独更新的包; +- `dsh plugin` 安装的 Starter:检查版本显示为已安装、待重启,不再提供更新; +- 磁盘上的 Starter 回到正在运行的版本后不再提示; +- 每个页面读取的状态带有正在运行的版本与已安装的更新; +- 每个页面都显示提示,分别针对 Starter 与子包,其余情况不显示。 + +## 限制 + +- 桌面版通过相同的 `packageManager` 约定、用 npm DSH 0.2.0-rc.2 与桌面版 pnpm 的副本复现,没有直接驱动打包后的应用。 +- 0.2.0-rc.2 与 0.1.7-rc.2 上的 Starter 更新安装的是作为 0.5.21 提供的本 revision,以便在没有更新版本的情况下展示页面替换;另一次运行安装的是 npm 上真实的 0.5.21,其页面没有重新打开的逻辑。 +- 重新打开从包含本次改动的版本起才生效。0.5.21 及更早的版本在桌面版中仍需在插件页移除并重新安装一次,指南中已说明。 +- 实测使用默认的侧栏布局。内置布局通过同一个工作台入口重新打开,由单元测试覆盖。 +- 没有 session storage 时更新照常进行,只是不会重新打开。 +- `dsh plugin` 安装更新后,DSH 的客户端 HMR 会关闭打开着的记忆系统,再次打开后才显示提示。 +- DSH 插件页只能先移除再重新安装,这条路径没有实测。 +- 只有 Starter 会与正在运行的版本比较。在检查版本之外更新的其他包不会列出。 diff --git a/dsh/docs/pr-assets/version-update-through-dsh/verification.json b/dsh/docs/pr-assets/version-update-through-dsh/verification.json new file mode 100644 index 000000000..b06abdf82 --- /dev/null +++ b/dsh/docs/pr-assets/version-update-through-dsh/verification.json @@ -0,0 +1,557 @@ +{ + "date": "2026-10-02", + "revisions": { + "tested": "b6e463569103c8380d6159ee4108e20d505a8da1", + "baseline": "dsh-mnemon 0.5.20 from npm", + "packed": { + "dsh-mnemon-0.5.21.tgz": "d3676247a1ff050ea473c65431566281022371b7d138e90a063f39aed4a1b260", + "note": "pnpm pack of the tested revision; a repack reproduces it. The runs install this tarball with package.json version set to 0.5.21-dshupdate.0 or 0.5.21; nothing else changes." + } + }, + "environment": { + "os": "macOS 15.6 arm64", + "node": "24.19.0", + "browser": "headless Chrome 154, 1280x800, device scale 1, zh-CN, light", + "dsh": [ + "@deepseek-ai/dsh 0.2.0-rc.2 (npm latest and next)", + "@deepseek-ai/dsh 0.1.7-rc.2" + ], + "isolation": "a fresh HOME, DSH_HOME, MNEMON_DATA_DIR, workspace and pnpm store for every run" + }, + "packagedAppReproduction": { + "desktopApp": "DeepSeek Harness 0.2.0-rc.2 in /Applications; files read only, the app was not driven", + "appContract": "dsh-desktop-host calls runProfile({ packageManager: { command: , args: ['--expose-internals', 'Resources/runtime/pnpm/bin/pnpm.mjs'], env: { ELECTRON_RUN_AS_NODE, DSH_DESKTOP_NODE_EXECUTABLE, PATH: 'Resources/runtime/bin:' + PATH } } }); DSH's plugin manager uses profileContext.packageManager before a PATH pnpm. The app's plugin manager is byte-identical to npm DSH 0.2.0-rc.2's.", + "launcher": "node launcher importing @deepseek-ai/dsh/lib/bin.js and calling runCli({ packageManager }) with the same command shape, a copy of the app's pnpm 11.7.0 and a runtime/bin/node shim", + "hostPath": "node 24.19.0 bin, /opt/homebrew/bin, /opt/homebrew/sbin, /usr/local/bin, /usr/bin, /bin, /usr/sbin, /sbin: the app Host directories, none of which holds pnpm", + "registry": "loopback registry with npm metadata; other packages redirect to registry.npmjs.org" + }, + "runs": { + "baseline": { + "host": "DSH 0.2.0-rc.2, packaged-app launcher", + "installed": "dsh-mnemon@0.5.20 from npm", + "status": { + "starter": "0.5.20", + "nominal": true + }, + "row": { + "state": "可更新", + "installed": "0.5.20", + "latest": "0.5.21", + "hint": "检测到 DSH Profile 安装,但当前找不到 pnpm 命令。", + "button": null + } + }, + "a": { + "host": "DSH 0.2.0-rc.2, packaged-app launcher", + "installed": "0.5.21-dshupdate.0 (tested revision)", + "target": "0.5.21 served from the tested revision", + "offered": { + "state": "可更新", + "installed": "0.5.21-dshupdate.0", + "button": "更新" + }, + "starterClientAfterRequestMs": 1638, + "componentClientsAfterRequestMs": 1744, + "firstFrameAfterClickMs": { + "reopenedChecking": 1908, + "result": 3750, + "memorySystemGone": null + }, + "final": { + "dialog": true, + "statusTab": true, + "results": [ + "dsh-mnemon 已更新\n\n请重启 DSH 以加载新的 dsh-mnemon 插件代码;桌面版请完全退出后重新打开。", + "请重启 DSH 以加载新的 dsh-mnemon 插件代码;桌面版请完全退出后重新打开。" + ], + "row": { + "state": "待重启", + "installed": "0.5.21", + "button": null + } + }, + "consoleErrors": [], + "profileAfter": { + "dependencies": { + "dsh-mnemon": "0.5.21" + }, + "bundles": [ + "@deepseek-ai/dsh-base", + "@deepseek-ai/dsh-web-app", + "dsh-mnemon" + ], + "starter": "0.5.21", + "pnpmRuns": [ + "Done in 384ms using pnpm v11.7.0" + ] + }, + "afterRestart": { + "status": { + "starter": "0.5.21", + "nominal": true + }, + "row": { + "state": "已是最新", + "installed": "0.5.21", + "latest": "0.5.21", + "notice": null, + "button": null + }, + "consoleErrors": [] + } + }, + "b": { + "host": "DSH 0.2.0-rc.2, packaged-app launcher", + "installed": "0.5.21-dshupdate.0 (tested revision)", + "target": "npm's 0.5.21, whose page lacks the reopening", + "offered": { + "state": "可更新", + "installed": "0.5.21-dshupdate.0", + "button": "更新" + }, + "starterClientAfterRequestMs": 2801, + "componentClientsAfterRequestMs": 1823, + "firstFrameAfterClickMs": { + "reopenedChecking": null, + "result": null, + "memorySystemGone": 2503 + }, + "final": { + "dialog": false, + "statusTab": false, + "results": [], + "row": null + }, + "consoleErrors": [], + "profileAfter": { + "dependencies": { + "dsh-mnemon": "0.5.21" + }, + "bundles": [ + "@deepseek-ai/dsh-base", + "@deepseek-ai/dsh-web-app", + "dsh-mnemon" + ], + "starter": "0.5.21", + "pnpmRuns": [ + "Done in 1.3s using pnpm v11.7.0" + ] + }, + "lockfile": "resolution: {integrity: sha512-N95kQZ41RjXFTHZo4t5l02DJwpbsmaC8nX1TQExqsS0bsgbsxRzyBawRE2Y8ZHyzgXmwujtmw4zgYzY4yFOFFQ==, tarball: https://registry.npmjs.org/dsh-mnemon/-/dsh-mnemon-0.5.21.tgz}", + "npmIntegrity": "sha512-N95kQZ41RjXFTHZo4t5l02DJwpbsmaC8nX1TQExqsS0bsgbsxRzyBawRE2Y8ZHyzgXmwujtmw4zgYzY4yFOFFQ==" + }, + "c": { + "host": "DSH 0.1.7-rc.2, dsh web with pnpm 11.19.0 on PATH (0.1.7-rc.2 runCli takes no package manager)", + "installed": "0.5.21-dshupdate.0 (tested revision)", + "target": "0.5.21 served from the tested revision", + "offered": { + "state": "可更新", + "installed": "0.5.21-dshupdate.0", + "button": "更新" + }, + "starterClientAfterRequestMs": 2422, + "componentClientsAfterRequestMs": 2476, + "firstFrameAfterClickMs": { + "reopenedChecking": 2489, + "result": 3700, + "memorySystemGone": null + }, + "final": { + "dialog": true, + "statusTab": true, + "results": [ + "dsh-mnemon 已更新\n\n请重启 DSH 以加载新的 dsh-mnemon 插件代码;桌面版请完全退出后重新打开。", + "请重启 DSH 以加载新的 dsh-mnemon 插件代码;桌面版请完全退出后重新打开。" + ], + "row": { + "state": "待重启", + "installed": "0.5.21", + "button": null + } + }, + "consoleErrors": [], + "profileAfter": { + "dependencies": { + "dsh-mnemon": "0.5.21" + }, + "bundles": [ + "@deepseek-ai/dsh-base", + "@deepseek-ai/dsh-web-app", + "dsh-mnemon" + ], + "starter": "0.5.21", + "pnpmRuns": [ + "Done in 562ms using pnpm v11.19.0" + ] + }, + "afterRestart": { + "status": { + "starter": "0.5.21", + "nominal": true + }, + "row": { + "state": "已是最新", + "installed": "0.5.21", + "latest": "0.5.21", + "notice": null, + "button": null + }, + "consoleErrors": [] + } + }, + "d": { + "host": "npm DSH 0.2.0-rc.2 started with dsh web directly: no launcher package manager and no pnpm on PATH", + "installed": "0.5.21-dshupdate.0 (tested revision)", + "status": { + "starter": "0.5.21-dshupdate.0", + "nominal": true + }, + "row": { + "state": "可更新", + "installed": "0.5.21-dshupdate.0", + "latest": "0.5.21", + "hint": "检测到 DSH Profile 安装,但宿主找不到 pnpm 命令;安装 pnpm 并重启 DSH 后即可在此更新。", + "button": null + }, + "consoleErrors": [] + }, + "e": { + "host": "DSH 0.2.0-rc.2, packaged-app launcher", + "added": "dsh-mnemon-strategy-scoped@0.5.4 from npm with dsh plugin add", + "offered": { + "state": "可更新", + "installed": "0.5.4", + "latest": "0.5.5", + "hint": "由当前 DSH Profile 管理;用 DSH 自己的插件安装方式更新,完成后重启 DSH。", + "button": { + "label": "更新", + "disabled": false + } + }, + "result": [ + "dsh-mnemon-strategy-scoped 已更新\n\n请重启 DSH 以加载新的 dsh-mnemon 插件代码;桌面版请完全退出后重新打开。", + "请重启 DSH 以加载新的 dsh-mnemon 插件代码;桌面版请完全退出后重新打开。" + ], + "after": { + "state": "待重启", + "installed": "0.5.5", + "latest": "0.5.5", + "hint": "由当前 DSH Profile 管理;用 DSH 自己的插件安装方式更新,完成后重启 DSH。", + "button": null + }, + "dialogStayedOpen": true, + "profileAfter": { + "dependencies": { + "dsh-mnemon": "0.5.21-dshupdate.0", + "dsh-mnemon-strategy-scoped": "0.5.5" + }, + "bundles": [ + "@deepseek-ai/dsh-base", + "@deepseek-ai/dsh-web-app", + "dsh-mnemon", + "dsh-mnemon-strategy-scoped" + ], + "scoped": "0.5.5", + "pnpmRuns": [ + "Done in 267ms using pnpm v11.7.0" + ] + }, + "consoleErrors": [] + } + }, + "automated": { + "command": "pnpm run verify", + "rootTests": { + "files": 113, + "passed": 1578, + "skipped": 6 + }, + "docsLocalLinks": 2965, + "packageUnpackedBytes": 1506720, + "packageBudget": 1509000 + }, + "restartReminder": { + "revision": "375d1ba43fae7de1b82e9717ec69dadd1a8375db", + "packed": { + "dsh-mnemon-0.5.21.tgz": "12d6d82885b3326d6d2db853dad629c99381bc4512c1117e0f91df492eccf489", + "served": { + "0.5.21-dshupdate.0": "070aa03511c3f4b78f79843008181c90bcceb63165ee56bff84d23e4cf208841", + "0.5.21": "687c65f0115c658f89aafb07c1568e1f8d06b4548623ffa670dc4c36470387cc" + }, + "note": "pnpm pack of the revision, served with package.json version set to 0.5.21-dshupdate.0 (installed first) or 0.5.21; nothing else changes." + }, + "runs": { + "checkVersions": { + "host": "DSH 0.2.0-rc.2, packaged-app launcher", + "before": { + "engine": "dsh-mnemon 0.5.21-dshupdate.0", + "notice": null + }, + "update": { + "result": "dsh-mnemon 已更新\n\n请重启 DSH 以加载新的 dsh-mnemon 插件代码;桌面版请完全退出后重新打开。", + "pnpm": "Done in 598ms using pnpm v11.7.0", + "starterOnDisk": "0.5.21", + "bundles": [ + "@deepseek-ai/dsh-base", + "@deepseek-ai/dsh-web-app", + "dsh-mnemon" + ] + }, + "after": { + "engine": "dsh-mnemon 0.5.21-dshupdate.0", + "notice": { + "title": "dsh-mnemon 0.5.21 已安装", + "text": "dsh-mnemon 0.5.21 已安装 当前运行的仍是 0.5.21-dshupdate.0;重启 DSH 后生效,桌面版请完全退出后重新打开。" + }, + "runtimeNotice": { + "title": "dsh-mnemon 0.5.21 已安装", + "text": "dsh-mnemon 0.5.21 已安装 当前运行的仍是 0.5.21-dshupdate.0;重启 DSH 后生效,桌面版请完全退出后重新打开。" + }, + "checkVersions": { + "state": "待重启", + "installed": "0.5.21", + "latest": "0.5.21", + "updateButton": false, + "notice": "请重启 DSH 以加载新的 dsh-mnemon 插件代码;桌面版请完全退出后重新打开。" + } + }, + "afterRestart": { + "engine": "dsh-mnemon 0.5.21", + "notice": null, + "runtimeNotice": null, + "checkVersions": { + "state": "已是最新", + "installed": "0.5.21", + "latest": "0.5.21", + "updateButton": false, + "notice": null + } + }, + "consoleErrors": [] + }, + "dshPluginAdd": { + "host": "DSH 0.2.0-rc.2, packaged-app launcher; the Memory System stayed open on Status", + "command": "dsh plugin --profile web add dsh-mnemon@0.5.21 (packaged-app launcher, same profile, while DSH served the page)", + "pnpm": "Done in 419ms using pnpm v11.7.0", + "openPage": { + "memorySystem": "closed", + "msAfterCommand": 539, + "note": "DSH client HMR swapped in the new client; the page showed DSH home" + }, + "reopenedFromSidebar": { + "engine": "dsh-mnemon 0.5.21-dshupdate.0", + "notice": { + "title": "dsh-mnemon 0.5.21 已安装", + "text": "dsh-mnemon 0.5.21 已安装 当前运行的仍是 0.5.21-dshupdate.0;重启 DSH 后生效,桌面版请完全退出后重新打开。" + }, + "runtimeNotice": { + "title": "dsh-mnemon 0.5.21 已安装", + "text": "dsh-mnemon 0.5.21 已安装 当前运行的仍是 0.5.21-dshupdate.0;重启 DSH 后生效,桌面版请完全退出后重新打开。" + }, + "checkVersions": { + "state": "待重启", + "installed": "0.5.21", + "latest": "0.5.21", + "updateButton": false, + "notice": "请重启 DSH 以加载新的 dsh-mnemon 插件代码;桌面版请完全退出后重新打开。" + } + }, + "back": { + "command": "dsh plugin --profile web add dsh-mnemon@0.5.21-dshupdate.0", + "pnpm": "Done in 417ms using pnpm v11.7.0", + "openPage": { + "memorySystem": "closed", + "msAfterCommand": 537 + }, + "reopenedFromSidebar": { + "engine": "dsh-mnemon 0.5.21-dshupdate.0", + "notice": null, + "runtimeNotice": null, + "checkVersions": { + "state": "可更新", + "installed": "0.5.21-dshupdate.0", + "latest": "0.5.21", + "updateButton": true, + "notice": null + } + } + }, + "consoleErrors": [] + }, + "strategy": { + "host": "DSH 0.2.0-rc.2, packaged-app launcher; dsh-mnemon-strategy-scoped@0.5.4 added on its own", + "update": { + "result": "dsh-mnemon-strategy-scoped 已更新\n\n请重启 DSH 以加载新的 dsh-mnemon 插件代码;桌面版请完全退出后重新打开。", + "pnpm": "Done in 312ms using pnpm v11.7.0", + "strategyOnDisk": "0.5.5", + "starterOnDisk": "0.5.21-dshupdate.0" + }, + "after": { + "engine": "dsh-mnemon 0.5.21-dshupdate.0", + "notice": { + "title": "dsh-mnemon-strategy-scoped 已更新", + "text": "dsh-mnemon-strategy-scoped 已更新 重启 DSH 后生效,桌面版请完全退出后重新打开。" + }, + "runtimeNotice": { + "title": "dsh-mnemon-strategy-scoped 已更新", + "text": "dsh-mnemon-strategy-scoped 已更新 重启 DSH 后生效,桌面版请完全退出后重新打开。" + } + }, + "afterRestart": { + "engine": "dsh-mnemon 0.5.21-dshupdate.0", + "notice": null, + "runtimeNotice": null + }, + "consoleErrors": [] + }, + "dsh017": { + "host": "DSH 0.1.7-rc.2, dsh web with pnpm 11.19.0 on PATH", + "update": { + "result": "dsh-mnemon 已更新\n\n请重启 DSH 以加载新的 dsh-mnemon 插件代码;桌面版请完全退出后重新打开。", + "pnpm": "Done in 419ms using pnpm v11.19.0", + "starterOnDisk": "0.5.21" + }, + "after": { + "engine": "dsh-mnemon 0.5.21-dshupdate.0", + "notice": { + "title": "dsh-mnemon 0.5.21 已安装", + "text": "dsh-mnemon 0.5.21 已安装 当前运行的仍是 0.5.21-dshupdate.0;重启 DSH 后生效,桌面版请完全退出后重新打开。" + }, + "runtimeNotice": { + "title": "dsh-mnemon 0.5.21 已安装", + "text": "dsh-mnemon 0.5.21 已安装 当前运行的仍是 0.5.21-dshupdate.0;重启 DSH 后生效,桌面版请完全退出后重新打开。" + } + }, + "afterRestart": { + "engine": "dsh-mnemon 0.5.21", + "notice": null, + "runtimeNotice": null + }, + "consoleErrors": [] + } + }, + "automated": { + "command": "pnpm run verify", + "rootTests": { + "files": 113, + "passed": 1584, + "skipped": 6 + }, + "docsLocalLinks": 2997, + "packageUnpackedBytes": 1511189, + "packageBudget": 1515000 + } + }, + "designReview": { + "revision": "f395626a87bbbc5fea8baef4252f7bf369880084", + "fixes": [ + "Failure text names the ERR_PNPM_* line, DSH's failure kind and DSH's log path, not the first output line.", + "Installed is the exact version the Profile records (Starter and profile-added packages), with node_modules as the fallback, so files a failed install leaves are not taken for an update.", + "The Host keeps how the last update ended (VersionStatus.lastUpdate); the reopened dialog shows that success or error.", + "Packages updated from the dialog are compared with the version they replaced; one put back waits for nothing.", + "Status reopens the dialog only after the Host answers, and drops the record once DSH runs the updated version with nothing pending." + ], + "liveRerun": { + "checkVersions020": { + "status": { + "engine": "dsh-mnemon 0.5.21-dshupdate.0", + "reminder": { + "title": "dsh-mnemon 0.5.21 已安装", + "text": "dsh-mnemon 0.5.21 已安装 当前运行的仍是 0.5.21-dshupdate.0;重启 DSH 后生效,桌面版请完全退出后重新打开。" + } + }, + "dialog": { + "state": "待重启", + "installed": "0.5.21", + "latest": "0.5.21", + "updateButton": false, + "notice": "请重启 DSH 以加载新的 dsh-mnemon 插件代码;桌面版请完全退出后重新打开。" + } + }, + "afterRestart020": { + "status": { + "engine": "dsh-mnemon 0.5.21", + "reminder": null + }, + "dialog": { + "state": "已是最新", + "installed": "0.5.21", + "latest": "0.5.21", + "updateButton": false, + "notice": null + } + }, + "dshPluginAdd": { + "after": { + "state": "待重启", + "installed": "0.5.21", + "latest": "0.5.21", + "updateButton": false, + "notice": "请重启 DSH 以加载新的 dsh-mnemon 插件代码;桌面版请完全退出后重新打开。" + }, + "back": { + "state": "可更新", + "installed": "0.5.21-dshupdate.0", + "latest": "0.5.21", + "updateButton": true, + "notice": null + } + }, + "strategy": { + "after": { + "engine": "dsh-mnemon 0.5.21-dshupdate.0", + "reminder": { + "title": "dsh-mnemon-strategy-scoped 已更新", + "text": "dsh-mnemon-strategy-scoped 已更新 重启 DSH 后生效,桌面版请完全退出后重新打开。" + } + }, + "afterRestart": { + "engine": "dsh-mnemon 0.5.21-dshupdate.0", + "reminder": null + } + }, + "dsh017": { + "after": { + "engine": "dsh-mnemon 0.5.21-dshupdate.0", + "reminder": { + "title": "dsh-mnemon 0.5.21 已安装", + "text": "dsh-mnemon 0.5.21 已安装 当前运行的仍是 0.5.21-dshupdate.0;重启 DSH 后生效,桌面版请完全退出后重新打开。" + } + }, + "afterRestart": { + "engine": "dsh-mnemon 0.5.21", + "reminder": null + } + }, + "failedInstall": { + "setup": "DSH 0.2.0-rc.2 packaged-app launcher; the Profile registry serves no dsh-mnemon 0.5.21 while npm names it", + "dialogError": "版本操作失败\n\nDSH could not install dsh-mnemon@0.5.21: operation-error (no-matching-version: [ERR_PNPM_NO_MATCHING_VERSION] No matching version found for dsh-mnemon@0.5.21 while fetching it from http://127.0.0.1:/); log: /.plugin-manager/logs//pnpm.log", + "after": { + "status": { + "engine": "dsh-mnemon 0.5.21-dshupdate.0", + "reminder": null + }, + "dialog": { + "state": "可更新", + "installed": "0.5.21-dshupdate.0", + "latest": "0.5.21", + "updateButton": true, + "notice": null + } + } + }, + "consoleErrors": [] + }, + "automated": { + "command": "pnpm run verify", + "rootTests": { + "files": 113, + "passed": 1592, + "skipped": 6 + }, + "docsLocalLinks": 2997, + "packageUnpackedBytes": 1514466, + "packageBudget": 1517000 + } + } +} diff --git a/dsh/docs/pr-assets/view-20260901/README.md b/dsh/docs/pr-assets/view-20260901/README.md new file mode 100644 index 000000000..2860e4663 --- /dev/null +++ b/dsh/docs/pr-assets/view-20260901/README.md @@ -0,0 +1,65 @@ +# View workspace — 2026-09-01 verification + +Implementation and interaction verification, not a model-quality benchmark or release. + +## Scope + +- Branch: `codex/view-concretization`; starting revision `c2d2f7db74956dffdbf791003bb30fa21ffb9f32`. +- Browser/UI checkpoint: `250463c`; final code checkpoint: `4f88a3f` adds only Host-side validation/isolation for malformed optional editor metadata, plus regressions. It does not change rendering, default composition or model prompts. +- Main remains `a990d45c42d85102ec9390b7df04746d4f6654c9`; no push or publication. +- Tab names: **视图** (Chinese), **View** (English). Shared Sidebar/Builtin page, existing theme tokens, responsive inspection/configuration panels. +- Actual pinned/latest View and read-only draft preview are distinct. No fabricated historical snapshot, model calls, or memory writes in preview. +- Dedicated Strategy Entries optionally export a public, pure configuration descriptor. Native Cordis Entry updates own activation; DSH settings persist Profile-local preferences. No alternate plugin registry or package-YAML rewrite. +- Existing Source/Provider business behavior and the default Starter's activation remain unchanged. Optional strategy plugins still require installation/registration through DSH. + +## Automated checks + +Environment: macOS, Node `v25.1.0`, pnpm `11.19.0`, DSH `0.1.1-rc.2`, Cordis `4.0.1`. + +| Check | Observed result | +| --- | --- | +| Final `VITEST_MAX_THREADS=4 VITEST_MAX_FORKS=4 pnpm verify` at `4f88a3f` | Passed: root/plugin typechecks and builds, tests, deterministic build, real Headless, package/public-entry lint; only verification concurrency is limited | +| Final expanded Vitest suite | **110 files / 972 tests passed**, 2 files / 2 tests conditionally skipped | +| Deterministic root build | All 38 generated files matched | +| Performance fences | 100 three-Source compositions for each default/additive profile; existing CPU < 2,000 ms and wall < 5,000 ms thresholds passed, unchanged | +| Real Headless | 37 total tools / 7 representative Mnemon tools; settings preservation and restart passed | +| Package/public entries | 45 packed root files; 11 Node-compatible entries / 25 public type dependencies; `publint --strict` and `attw` passed | +| `pnpm verify:plugins --skip-build` | Passed after the full build: 16 repositories outside the workspace / 17 exact packed artifacts | +| Independent editor author | External consumer compiled and tested a Strategy configuration descriptor against packed public SDKs; each plugin independently installed/typechecked/tested/built | +| `pnpm release:check` | Passed: coordinated `0.5.0-beta.1`, `beta` tag, no publication | + +Package tests are included in the root count, not added twice. Skips are the opt-in Native binary test and Windows-only smoke. Upstream UI-primitives missing-source-map warnings were not hidden. An earlier full run found the old SystemPrompt-order fixture lacked generation/snapshot fields; the fixture now models those fields and additionally asserts the actual Host snapshot. It was rerun successfully, without weakening the ordering assertion or bypassing production code. + +The unmodified-concurrency gate passed 965 tests at `9bb9973`. A subsequent expanded-suite run passed 966 tests but exceeded the existing additive wall-time fence once: 5,861.92 ms for 100 compositions against a 5,000 ms limit (the CPU fence passed). This failed run is retained as a timing limitation; no production algorithm or threshold was changed to make it pass. Both unchanged performance tests passed in isolation (whole-case durations 1.224 / 1.053 s), in the four-worker 967-test suite (1.450 / 1.365 s), and in the final 972-test gate (1.670 / 1.810 s). These durations include setup/warmup and are not per-request production latency. The pattern is consistent with parallel-load variability, not a guarantee that the unrestricted-worker gate never fluctuates. Headless and package/public-entry checks were rerun successfully after the final suite. + +Additional focused tests cover real native Loader discovery of disabled Entries; read-only preview; immutable old pins; individual toggle preservation; complete Strategy selection and return; Profile-isolated persistence; concurrent revision conflicts; activation/storage rollback; new workspace attachment during a transaction; bounded failure restoration; malformed optional editors; remote/read-channel write rejection; explicit-empty versus omitted fields; stale drafts and asynchronous UI responses; locale subscriptions and lifecycle disposal. Final metadata regressions reject invalid localized labels, Source-role lists, defaults and non-JSON values locally, while the real default View remains available. + +## Real browser checks + +Used the real pinned DSH WebUI with the built packages through `node scripts/serve-e2e.mjs --strategy-extensions`. Its disposable Profile, workspace and memory were separate from personal data; all four conversation turns and the session title used a loopback-only model stub (five requests total). Preview produced no additional model requests. + +| Interaction | Observed result | +| --- | --- | +| No-session View | Honest empty state; configuration and preview still available | +| Enable scoped/light-context/auto-capture together | All three active after explicit save; actual Cordis Entries and Profile settings agreed | +| First actual turn | 512 projected characters, three fragments, three extensions; a real Runtime test note was present | +| Change budget and disable only auto-capture | Preview 384 characters with scoped/light-context; saved first-turn View and generation IDs stayed identical | +| Next actual turn | 384 characters with only scoped/light-context | +| Restart the same disposable Profile | Enabled states and explicit 384-character value restored; no fabricated old snapshot; next actual turn used restored settings | +| Reopen Sidebar after another turn with an unsaved draft | Actual View advanced to turn 4; the draft remained unsaved and did not affect that turn | +| Chinese/English live switch | All five tab labels updated; Chinese 视图 and English View verified | +| Layout | Light/dark appearances, Sidebar/Builtin, wide two-column and 320/360px stacked layouts checked; narrow English buttons corrected and retested with no panel overflow | +| Narrow-screen preview click | Succeeded at 360px; read-only preview remained clearly labelled | +| Existing pages | Runtime note creation, Document creation/Markdown reading, and Memory Spaces navigation worked | +| Source changes after a turn | New Document appeared in fresh preview; the actual pinned snapshot still showed its original zero-Document state | +| Browser errors | Error-level browser log query returned an empty list after final interactions | + +Browser checks caught stale retained Sidebar snapshots, cached Source navigation translations, and narrow English action-label overflow. Fixes use existing visibility/locale lifecycles and CSS; Source Fibers are not remounted to translate labels. + +The test tab was closed, viewport override reset, server/model stub stopped, and the owned `mnemon-web-e2e-OOCIGx` temporary directory removed. No production profile, memory or cloud account was modified. + +## Limits + +This verifies UI and local composition behavior, not real-model recording quality, production latency, live cloud Provider accounts or Native/Windows binaries. Resident character counts are neither tokens nor total prompt cost. View inspection exposes no read grants or provider credentials. Recent snapshots last only for the running Host/agent lifecycle, not as a persistent historical transcript. Strategy conflicts remain explicit; not every independent complete Strategy is an additive plugin. + +Usage: [English](../../en/guides/ui-guide.md), [简体中文](../../zh-CN/guides/ui-guide.md). Authoring: [English](../../en/development/extensions.md), [简体中文](../../zh-CN/development/extensions.md). diff --git a/dsh/docs/zh-CN/README.md b/dsh/docs/zh-CN/README.md new file mode 100644 index 000000000..6761acbf9 --- /dev/null +++ b/dsh/docs/zh-CN/README.md @@ -0,0 +1,44 @@ +# 文档中心 + +**简体中文** | [English](../en/README.md) | [项目首页](../../README.zh-CN.md) + +dsh-mnemon 为 DeepSeek Harness 提供三类记忆:每一轮都会用到的**运行时记忆**、问题需要时才检索的**项目档案**,以及存放在你所选 Provider 上的长期证据**记忆空间**。主策略决定它们如何参与每一轮对话,主策略与增强都在插件页中选择。从默认的分层策略开始即可,日常使用无需管理插件。 + +[![可组合记忆插件页中的记忆组合面板](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/plugin-composition.jpg)](./guides/ui-guide.md#在插件页中) + +## 使用记忆系统 + +| 要完成的事 | 指南 | +|---|---| +| 从零安装并启动:网页、桌面版或命令行,到第一条记忆 | [安装与启动](./guides/installation.md) | +| 保存并用上第一批记忆 | [快速开始](./guides/getting-started.md) | +| 熟悉对话、记忆系统与插件页 | [界面指南](./guides/ui-guide.md) | +| 了解每个组件与策略能做什么 | [能力地图](./guides/capabilities.md) | +| 选择并接入长期记忆后端 | [Provider 指南](./guides/memory-providers.md) | +| 备份、迁移存储或排查故障 | [运维指南](./guides/operations.md) | +| 升级已有安装 | [兼容性与升级](./reference/compatibility.md) | + +## 查阅具体约定 + +| 问题 | 参考 | +|---|---| +| 有哪些设置,在哪里编辑、保存在哪里? | [配置参考](./reference/configuration.md) | +| 数据保存在哪,怎样共享和归档? | [存储模型](./reference/storage-model.md) | +| 读取、写入和整理何时发生? | [生命周期与流程](./reference/workflows.md) | +| 提供哪些工具、命令、RPC 通道与界面区域? | [接口参考](./reference/interfaces.md) | + +## 制作扩展 + +| 要完成的事 | 开发文档 | +|---|---| +| 理解 Source、Strategy、View、归属与界面区域 | [架构设计](./development/architecture.md) | +| 开发 Source、Strategy、增强或 Provider | [插件开发](./development/extensions.md) | +| 让皮肤适配 Mnemon 背景与透明度 | [皮肤开发](./development/skin-integration.md) | +| 构建、测试与真实 WebUI 截图 | [开发与验证](./development/README.md) | +| 为独立包管理版本和发布 | [发布流程](./development/releasing.md) | + +## 最新变化 + +[v0.5.24](./releases/v0.5.24.md) 让实体页统计并列出带有某个实体的全部记忆,选中后立即显示,相关记忆只在需要时查找。[v0.5.23](./releases/v0.5.23.md) 减少了 Runtime 压缩、Document 查询、空间发现、多路召回、批量准备与 Holographic 排名中的重复工作,保留结果顺序和存储格式。[全部版本](./releases/README.md) · [路线图](./roadmap.md) · [历史验收证据](../pr-assets/README.md) + +指南描述当前版本。截图与录屏来自 [v0.5.19 图集](../assets/webui-v0.5.19/README.md),安装步骤来自[安装图集](../assets/install-v0.5.19/README.md);带日期的 PR 记录只证明其标注的代码修订与环境。内部 Host RPC 不属于对外插件 SDK。 diff --git a/dsh/docs/zh-CN/development/README.md b/dsh/docs/zh-CN/development/README.md new file mode 100644 index 000000000..54286e5fd --- /dev/null +++ b/dsh/docs/zh-CN/development/README.md @@ -0,0 +1,173 @@ +# 开发与验证 + +**简体中文** | [English](../../en/development/README.md) | [文档中心](../README.md) + +## 环境与命令 + +插件的 Node engine 下限为 20;锁定的完整 DSH 开发 Profile 是正式发布的 0.1.7-rc.2,需要 Node `^22.19.0 || >=24.0.0`,建议开发使用 Node 24。Root、Source Client 测试和外部制品消费者均使用该依赖族;`dsh-invariants` 闭合 peer 图,`dsh-client-store` 则提供子 Agent projection 适配器使用的公开 selector 类型。CI 另在 Node 20 冒烟导入公开 Node 入口。npm `next` 上的 DSH 0.2.0-rc.1 同样受支持:`tests/dsh-host-compatibility.spec.ts` 对全部 18 个包的清单执行 DSH 自己的安装前兼容检查,覆盖两个运行时;验证 0.2 行为时,按[兼容性说明](../reference/compatibility.md#dsh-02)在隔离目录全局安装 DSH,或把开发依赖临时切换到 0.2.0-rc.1。 + +DSH 0.1.7 UI primitives 在制品中导入 Markdown/高亮依赖,但其已发布 manifest 将这些包列为开发依赖。Root、三个 Source 与外部消费者显式声明完整依赖族,使独立 Client 测试可执行;Host 制品仍使用 DSH 提供的 UI 模块。测试同步使用公开的异步 Agent 工厂及持久化 `assistant/message` 事件。`tests/legacy-session-repair.spec.ts` 对普通和压缩格式的合成历史日志执行已发布 v0 → v3 迁移,检查显式副本修复、冷启动重读和带时间戳的 stream 回放。审计用例覆盖三个旧 Mnemon summary、兼容 v2 descriptor、packed 占位值展开、null→空字符串 delta name,以及具有已记录 provider ID 的闭合工具链;另验证多调用 provenance、owner 引用拒绝、原件及其他插件保留。`pnpm e2e:serve --legacy-session-replay` 还要求实际 WebUI 的回环续写服务器核对历史 wire call/result ID 与正文,匹配后才返回成功。 + +```sh +pnpm install --frozen-lockfile +pnpm run verify +pnpm run verify:plugins +``` + +`verify` 包含类型检查、根包确定性构建、独立插件构建、完整测试集、真实隔离 DSH Headless 和包出口/内容验证。独立插件检查在所有公开制品构建完成后分阶段执行。不要用 `pnpm -r verify` 同时清理重建制品和运行读取它们的测试;整个工作区使用 `pnpm verify`。`verify:plugins` 在工作区**外部**,基于 semver 安装的 tarball 重复验证,并测试外部 Source/Strategy/Provider/Client 消费者;还向真实 DSH 仅安装根包 tarball,从 loopback registry 解析全部十七个官方插件,不使用工作区链接或改写 manifest,再单独验证三个随附增强从默认停用到同时启用。外部消费者还通过完整 Strategy 的打包 SDK 编译自己实现的策略贡献。 + +## 仓库归属 + +```text +src/ + core/ contracts, View compilation, generations, turn leases + sdk/ installMemory, validation, test fixtures + host/ DSH lifecycle, settings, tools, RPC, worker coordination + client/ shared workspace, settings, Source-page SDK +plugins/ + dsh-mnemon-source-runtime/ + dsh-mnemon-source-documents/ + dsh-mnemon-source-memory-spaces/ + dsh-mnemon-strategy-default-three-tier/ # 分层策略,默认主策略 + dsh-mnemon-strategy-general/ # 随附的另一种主策略 + dsh-mnemon-strategy-scoped/ # 随附、默认关闭的选择贡献 + dsh-mnemon-strategy-light-context/ # 随附、默认关闭的投影贡献 + dsh-mnemon-strategy-auto-capture/ # 随附、默认关闭的对话内记录贡献 + dsh-mnemon-provider-*/ +tests/ Host/Core/UI composition and boundary tests +scripts/ reproducible build, artifacts, Headless and Web fixtures +cordis.patch.yml default Starter composition +``` + +根包拥有 Core/SDK、DSH Host 和默认 Starter,不拥有 Source 存储实现。`plugins/` 下每个目录都是可独立发布的项目。默认发行包按公开 semver 依赖全部十七个官方插件。Starter 安装通用策略与三个增强,其 Entry 默认停用;`memoryView.strategyTypeId` 记录由哪个主策略组合 View,各组件开关由 DSH 插件管理器记录。Source/Strategy 通过 peer 使用 Core SDK,策略贡献使用其完整 Strategy 的公开 SDK,Provider 使用 Memory Spaces SDK。peer/开发关系会产生包管理器环依赖提示;生产代码导入边界另有独立检查。 + +不再保留私有工作区包、控制器转发文件、业务 binding 或 compatibility 目录。兼容指用户配置、数据与使用流程,不是延续历史内部符号。 + +插件 Client 测试需要根包的公开浏览器制品时,先构建根包: + +```sh +pnpm build +pnpm --workspace-concurrency=1 -r build +pnpm --filter dsh-mnemon-source-runtime verify +``` + +声明的 peer 版本可用后,任意插件可复制到新仓库,运行自己的 `pnpm install && pnpm verify`。未发布开发使用打包制品及本地 registry 验收,不以仓库源码路径代替公开依赖。 + +## 测试归属与覆盖 + +| 边界 | 测试 | +|---|---| +| Core/SDK | 不可变 View、预算、Strategy 校验、并发回合、grant、租约、换代、清理与性能 | +| 策略贡献 | 独立槽、组合/顺序、卸载、冲突、只读范围、共享配额与真实 Host 激活 | +| Source | 自己的控制器/存储、修订、快照、JSON 操作、Client 点击与实例隔离 | +| Provider | 驱动、凭据、真实能力与故障响应 | +| Memory Spaces | Provider 子节点生命周期、跨 Provider conformance、合并/路由/召回质量、Native 进程串行化 | +| Host | 默认组合、配置/数据范围、工具、监督流程、RPC 权限、回执与体验 | +| 制品 | 所有公开入口、独立安装/构建/测试、外部组合与浏览器制品 | + +工作区身份、Client 平台边界和委派工作区范围测试还会在 Windows 的 Node 22.19 与 24 上运行。它们覆盖真实文件系统错误和 junction 别名,并包含模拟 Windows 对文件后代返回 `ENOENT` 的跨平台回归;该模拟不能替代真实 Windows 运行。 + +`tests/bundle-activation.spec.mjs` 用正式发布的 DSH Loader 启动临时激活计数器,覆盖 Starter 的全部十八个包。它验证旧版 `mnemon` 配置/停用目标及组件独立选择,再调用正式插件管理器:真实组件和 bundle 开关必须在重载、重启后保持,核心总开关必须停止全部运行中的子项,任何包都不能留下重复实例。两组用例都在锁定的宿主上运行;设置 `MNEMON_BUNDLE_TEST_PROFILE=/absolute/profile` 可改为检查另一份安装。夹具子进程不继承 `pnpm exec` 注入的 `NODE_PATH`:DSH 0.2 通过 Node 的搜索路径(包括 `NODE_PATH`)为插件包选路,继承它会解析到本仓库的真实插件而不是夹具包。夹具创建自己的临时 profile,不修改所提供的安装。它验证生命周期契约;真实 Mnemon 行为仍由制品 Headless 和 WebUI 检查负责。最后它核对 DSH 的配置 schema 收集器只把 Starter 的组件组报告为无法识别的树载体(已知取舍,见[兼容性说明](../reference/compatibility.md#安装后直接启用-starter)),不允许出现其他错误。执行 `node --expose-internals tests/fixtures/bundle-activation.mjs /absolute/profile manager --check-declared-rows` 可额外要求每个声明的 bundle 组件都可由管理器操作;该诊断会在[兼容性说明](../reference/compatibility.md#dsh-017-bundle-组件列表)中的 `0.1.7-rc.2` 原生 group 列表缺陷处失败。 + +远程 Provider 使用可控 HTTP 响应;Native 进程测试使用可控命令 runner,另有可选 Windows 二进制冒烟。额外的 opt-in 测试接受经过官方 checksum 校验的 Native 二进制,创建临时记忆空间,通过 View 写入、召回并删除: + +```sh +MNEMON_NATIVE_TEST_CLI=/absolute/path/to/mnemon pnpm --filter dsh-mnemon-source-memory-spaces exec vitest run tests/native-integration.spec.ts +MNEMON_NATIVE_TEST_CLI=/absolute/path/to/mnemon pnpm exec vitest run tests/runtime-capacity-workflow.spec.ts -t 'two same-View Native' +``` + +该测试不会发现个人数据根或安装二进制。这些检查不等于验证过所有真实远端服务或账号配置。Provider Lab 是需要明确启动的独立集成环境。 + +OpenViking 提供显式启用的回环集成测试:`MNEMON_OPENVIKING_TEST_ENDPOINT=http://127.0.0.1:1933 pnpm --filter dsh-mnemon-provider-openviking exec vitest run tests/integration.spec.ts`。只使用临时后端。测试对唯一的中英文合成资料执行创建、精确读回、搜索、浏览和精确删除;普通 CI 跳过该测试。正式发布的 v0.4.20 服务配合本地确定性 embedding 可验证 HTTP、存储和索引契约,不能证明语义模型质量。 + +Runtime 用例在 View 固定后,通过真实 Host 工具创建并激活两个 Native 空间,归档两条完整检查点并验证待新增内容。路由决策由本地脚本固定,不调用模型 API。 + +Agent Teams 矩阵只通过包出口加载正式发布的 DSH 包,并随常规测试在锁定的宿主上运行;`MNEMON_TEAM_TEST_PROFILE=/absolute/profile pnpm exec vitest run tests/agent-team-review-host.spec.ts` 可检查同一 DSH/Teams 版本的另一份安装。测试执行真实 fork/spawn、原生/Code Mode 工具、全部三个 Strategy 增强、子代理执行限制、Runtime 提交与父 Team 工具。[Issue #275 记录](../../pr-assets/issue-275-agent-teams/README.zh-CN.md)提供复现 profile、制品 WebUI 夹具与人工验收证据。 + +可选的 Flash 压力测试使用四个真实 DSH 会话、委派写入者、独立维护任务和临时 Native 存储,保留默认 10 KiB 上限,验证反复归档后的精确原文、命名空间路由和无会话 Web 管理。通过 `DEEPSEEK_API_KEY` 提供 DeepSeek 凭据,通过 `MNEMON_NATIVE_TEST_CLI` 提供已验证的 CLI,然后运行: + +```sh +MNEMON_RUN_FLASH_STRESS=1 MNEMON_FLASH_STRESS_ROUNDS=8 MNEMON_FLASH_STRESS_REPORT=/tmp/mnemon-flash-stress.json pnpm exec vitest run tests/runtime-capacity-flash-stress.spec.ts +``` + +测试逐一核对出站请求与返回模型均为 `deepseek-v4-flash`,使用非思考模式,始终不选择 Pro。模型提交前的文本变化与存储后的变化分别计数,所有项目事实均为合成数据,临时存储和会话在完成后清理。目标授权范围是硬性断言,主题归类匹配度单独作为模型质量指标报告。设置 `MNEMON_FLASH_STRESS_JSON_PROMPT=1` 可重跑带引号 JSON 输入诊断。普通 CI 默认跳过,需要明确授权使用真实 API。 + +另一组自动记忆验收在四个并发 DSH 会话中模拟后端、前端、Android 和运维工作。开发提示包含已确定的决策、纠正和临时诊断,由模型自行选择记忆操作。开发任务实际读写临时 JSON 文件,默认生命周期真实执行空闲审阅;新的召回会话无法访问开发会话历史或配置文件。提供相同的凭据和 CLI 环境变量后运行: + +```sh +MNEMON_RUN_FLASH_QUALITY=1 MNEMON_FLASH_QUALITY_REPORT=/tmp/mnemon-flash-quality.json pnpm exec vitest run tests/runtime-memory-flash-quality.spec.ts +MNEMON_RUN_FLASH_QUALITY=1 MNEMON_FLASH_QUALITY_WAVES=24 MNEMON_FLASH_QUALITY_REPORT=/tmp/mnemon-flash-quality-long.json pnpm exec vitest run tests/runtime-memory-flash-quality.spec.ts -t 'four preconfigured' +``` + +默认每会话 12 轮,扩展工作负载为 24 轮。两者均使用关闭思考的 Flash、guided 召回与写回,以及默认 10,240 字节记忆上限。只将空闲防抖从 30 秒缩短为 5 秒,审阅资格规则不变。测试记录保留事实、临时标记、纠正后的回答、模块归属、禁止写入回合、工具错误和归档。Vitest 成功仅表示实验执行完成:应检查 `finalEvaluation.automatedVerdict`,并逐条审阅过期文档、重复与归属错误,才能判断质量是否验收通过。[2026-09-09 验收报告](../../pr-assets/runtime-memory-quality-flash-20260909/README.zh-CN.md) 同时记录失败发现与通过项。普通 CI 跳过两个真实 API 用例。该模拟不能替代完整应用构建、真人半天工作流或 Windows 验证。 + +生成的真实运行数据放在仓库外,上述命令已采用这种方式。遵循[证据存放规范](../../pr-assets/README.md):提交摘要、复现输入与最小样例,完整脱敏数据作为 PR 附件保存,并注明 revision 和 SHA-256。 + +性能回归对 100 次三 Source View 组合约束 wall/CPU 时间;确定性构建比较所有生成文件 hash。二者不承诺生产网络延迟或 LLM 质量。 + +`pnpm bench:algorithms` 在临时合成存储中测量 N=1/10/100/1000 的本地查询、存储、压缩与召回扩展性,默认写入 `.cache/algorithm-benchmark.json`;可用 `MNEMON_BENCH_OUTPUT` 指定输出,或用 `MNEMON_BENCH_SIZES=1,10,100` 运行小规模测试。运行时不要并行执行其他测试/构建。[算法审计报告](../../pr-assets/algorithm-performance-20261003/README.zh-CN.md)记录工作负载、前后对比、操作计数、复杂度和外部 Provider 边界。普通 CI 使用确定性的等价性/工作量上界测试,不使用该基准的延迟断言。 + +默认组合和三插件组合均运行上述性能门槛。 +[2026-09-01 策略贡献验证](../../pr-assets/strategy-extensions-20260901/README.md) +记录了共存、独立制品、真实 Headless 激活结果及其边界。 + +## 真实 WebUI + +皮肤作者的接入位置、稳定选择器、作用域、旧类名迁移与验收步骤见[皮肤开发与 Mnemon 适配](./skin-integration.md)。 + +```sh +pnpm build +pnpm --workspace-concurrency=1 -r build +pnpm e2e:serve +``` + +夹具输出临时工作区及 loopback URL,隔离 `DSH_HOME`、`MNEMON_DATA_DIR`、工作区和模型端点。会话选择 **Mnemon E2E**:该测试自有 preset 去除 Shell 依赖,保留 Host 记忆工具。模型固定回复,因此这里只检查 UI/传输,不评价真实模型沉淀质量。Ctrl-C 停止并清理合成测试数据。 + +`pnpm e2e:serve --review-evidence --strategy-extensions` 添加按 Agent 注册的合成概览工具及固定的父会话 / 审查调用,检查五个完整分块的继承,并在修复前后尝试相同的外部工具读取;见 [Issue #211 验证记录](../../pr-assets/issue-211-20260911/README.zh-CN.md)。`tests/review-evidence-host.spec.ts` 还覆盖真实 DSH native 与 Code Mode 中,在 Provider 的 start Promise 返回前发生的工具执行。 + +检查无会话 Sidebar、所有一级/二级页面、Runtime 增改删与清空分支、Documents 创建/搜索/读取、Provider 设置与发现、激活、故障态、取消弹窗、存入记忆、布局切换、locale、返回聊天后交互恢复。在“插件 → 可组合记忆”页面中,还要检查主策略选择器、各组件开关与组件页面、DSH 组件行及其打开的页面、默认/自定义存储位置,以及备份导入预览。读写/删除使用临时 Provider 或受控夹具,不能对个人记忆做实验。 + +Issue #233 使用 `node scripts/fixtures/openviking-protocol.mjs` 与 `pnpm e2e:serve --openviking-write`。把 OpenViking 配置为 `http://127.0.0.1:19335`,account/user 为 `default`,不设置 API key,然后在 Mnemon E2E 中发送 `openviking-write-233`。模型夹具只驱动一次真实 Host 和受监督 writer 工具。协议夹具刻意返回有 cosmetic update、但未存储候选正文的抽取结果,用于暴露基线误报;`/__fixture` 明确标注并记录合成请求和文件。后端验收需再对临时真实 OpenViking 服务执行同样流程;协议夹具不能代表真实抽取或语义质量。 + +验证内嵌 Electron Host 时,使用 `pnpm e2e:serve --electron=/absolute/path/to/electron`(macOS 指向 `Electron.app/Contents/MacOS/Electron`)。单独安装测试用 Electron,并通过 `MNEMON_CLI_PATH` 和 `npm_config_prefix` 指定隔离的 npm 安装。夹具将正式发布的 DSH Web 栈运行在 Electron 主进程内,Host 不设置 `ELECTRON_RUN_AS_NODE`。它为正式 Cordis loader 开放 Node internals,无需重新编译或修改 DSH 包。照常用 Ctrl-C 停止。 + +另检查 `displayMode` 实时切换:Sidebar 与会话标签页(`builtin`)不得同时挂载,二者使用同一组 Source 页面。会话标签页的全局/工作区/集中工作区/自定义范围读写及任务遵循所属会话,隐藏范围控件,切换会话时清理旧数据与编辑器。验证旧 `buildin` 规范化,以及原生 Sidebar 皮肤和已支持布局插件下的折叠图标。 + +[2026-09-04 main rebase 验证记录](../../pr-assets/main-rebase-20260904/README.md)列明精确的 v0.4.7/DSH rc.1 revision、registry 与源码覆盖完整测试、独立制品、插件组合重启持久化和真实双入口验证及其限制。 + +[2026-08-30 npm 回归记录](../../pr-assets/npm-sidebar-cli/README.md#简体中文)保留了旧 Taskboard/SSH 与 CLI 调查。已移除的历史夹具不是当前 checkout 的命令;当前 WebUI 验证使用 `pnpm e2e:serve`。 + +区分上游 Profile/传输告警与 Mnemon 故障,不隐藏控制台。 + +文档归档回归使用 `pnpm e2e:serve --document-archive`:创建并启用临时的精确写入记忆空间,新建档案后从工作台归档。标题包含 `REJECT` 时夹具故意选择无效目标,检查档案仍为 active 且没有新增索引,再改名重试。在 Mnemon E2E 对话的三个回合中依次发送 `archive-tool-222 prepare`、`archive-tool-222 update`、`archive-tool-222`,会驱动真实的新建 → 更新 → 归档工具调用,并断言返回的 lineage。只有模型决策由脚本控制,存储、工具、传输和浏览器均为真实实现。搭配旧 Host 构建时,同一夹具可复现旧的回执序号不匹配错误。 + +Runtime 写入范围回归使用 `MNEMON_CLI_PATH=/absolute/path/to/mnemon pnpm e2e:serve --runtime-write-scope --strategy-extensions`,并启动全新临时夹具。在 Mnemon E2E 中发送 `archive-scope-250`。脚本模型先保存两条检查点,在已固定的 View 内创建并激活两个真实 Native 空间,再新增一条越限检查点。基线版本会在空间已激活的情况下拒绝新增;修复后的 Host 会把原文归档到两个有权限的目标,再提交新增内容。发送 `archive-scope-250 retry` 可在新回合重试同一份待新增输入。夹具限制子 Agent 调用次数,且必须收到真实 create/update 回执才完成调用。 + +`pnpm e2e:serve --idle-review --strategy-extensions` 通过确定性回环模型选择验证部分失败。连续发送两个至少 150 字符的合成用户回合,再等待五秒;真实 reviewer 创建一份档案和一条 Runtime 记忆后,夹具故意返回模型错误。刷新记忆系统状态核对两份已提交回执,再发送更多回合,确认每会话一次的上限阻止新增子 Agent。与生产默认值的差别只有防抖和最小尝试间隔(均为 5 秒)以及 1 次会话上限。该夹具不模拟真实 Agent Teams policy;该组合由上文的可选 Agent Teams 矩阵覆盖。不使用个人凭据或记忆。详见[双语复现与证据](../../pr-assets/idle-review-agent-team/README.zh-CN.md)。 + +`pnpm e2e:serve --strict-template` 复现 [#327](https://github.com/omdsh-dev/dsh-mnemon/issues/327)。回环模型的行为与 Ollama 0.33 运行“要求用户查询的聊天模板”时一致: +- 保留系统提示,以及其余消息中能放进窗口的最长后缀,并总是保留最后一条消息; +- 截断后没有用户文本消息时返回 500 `no user query found in messages`; +- 窗口在后台审查发出第 3 次请求时确定。 + +连续发送两个至少 150 字符的用户回合,再等待五秒。修复后,第 4 次审查请求被拒一次,随即带着用户消息重试,审查会创建档案“Review checkpoint storage”,状态页没有审查失败;未修复的构建则在状态页显示后台审查失败,并列出已提交的档案回执。夹具以 `Strict template:` 行输出每次审查请求。与生产默认值的差别只有后台审查的时间设置(5 秒、每会话一次)。详见[双语复现与证据](../../pr-assets/issue-327-subagent-user-turn/README.zh-CN.md)。 + +`pnpm e2e:serve --general-strategy` 启动时已选中通用策略,并停用分层策略。先发送 `general-strategy-check remember`,下一回合再发送 `general-strategy-check recall`。脚本模型检查系统提示中包含通用记忆协议,且 Runtime、项目档案和记忆空间三个 Source 均已接入;随后通过具名 Runtime 工具保存一条事实,并且只有当这条事实以常驻记忆投影出现时,第二回合才答出它。夹具以 `General strategy:` 行输出每项检查。只有模型决策是脚本化的;策略、其 View、Runtime 写入和浏览器均为真实运行。 + +`pnpm e2e:serve --without-mnemon-cli` 把 `cliPath` 指向不存在的文件,即使 PATH 中有 Mnemon CLI,Host 也按未安装处理。用它确认:状态页不显示 Mnemon Native 卡片,版本对话框把 CLI 列为可选,插件页停用向量测试,新建记忆空间从已就绪的 Provider 开始。先连接一个 Provider,例如在“插件 → 可组合记忆”页面中连接 Provider Lab 的服务。 + +`pnpm e2e:serve --docs-demo`(中文)或 `--docs-demo=en` 会预置[文档截图集](../../assets/webui-v0.5.19/README.md)使用的虚构项目 Lumen:用户画像与工作记忆、六份项目档案和三个记忆空间,都在宿主启动前通过各 Source 自己的管理操作写入。包含“结账”的消息会经真实 View 工具完成一次档案检索与两次记忆空间召回后作答;随后包含“记住”的消息(例如新的 LCP 目标)会替换对应的工作记忆条目。脚本只决定模型调用哪些工具,不涉及任何个人数据。加上 `--live-model` 则改由 DeepSeek API 作答,密钥从 `DEEPSEEK_API_KEY` 读取;此时提问会经过真实的模型、任务 Agent 与 Agent 查询,图集即以这种方式采集。界面变化后应使用该夹具重新采集截图集。 + +`pnpm e2e:serve --trusted-host=memory.test:4331` 把该授权主机加入 DSH 的浏览器信任边界,用于以远程页面身份检查 WebUI;再加 `--remote-management` 即设置 `remoteAccess: trusted-host`。同时设置 `MNEMON_E2E_PORT=4331` 使端口一致,在测试浏览器中把该名称解析到 127.0.0.1(Chrome 可用 `--host-resolver-rules="MAP memory.test 127.0.0.1"` 并加 `--no-proxy-server`),再把打印出的启动 URL 中的回环地址换成 `memory.test:4331` 打开。未授权时“插件 → 可组合记忆”页面为只读;授权后,保存会经 API Gateway 持久化。 + +## 发布 + +官方包采用独立版本,Starter 固定经过验证的组合。Changeset、冻结制品、Registry 验证与失败恢复见[发布流程](./releasing.md)。 + +## 文档、存储与历史证据 + +保持中英文页面一致、公开示例可执行。Mermaid 表达归属和流程;真实截图位于 `docs/assets`。不保留空目录占位或历史转发文件。 + +没有明确迁移方案与测试前,保留持久格式及配置键。存储改动须验证锁、原子 rename、修订、损坏输入和复制数据根上的升级/回退。 + +v0.3 benchmark 是冻结的历史结果,不是本架构的性能证明。失效的可执行评估框架已移出工作树;[发布记录](../releases/v0.3.0.md) 链接到固定版本历史源码。当前验收入口为上述脚本。 diff --git a/dsh/docs/zh-CN/development/architecture.md b/dsh/docs/zh-CN/development/architecture.md new file mode 100644 index 000000000..5297f1d2e --- /dev/null +++ b/dsh/docs/zh-CN/development/architecture.md @@ -0,0 +1,123 @@ + + +# 可组合视图记忆架构 + +**简体中文** | [English](../../en/development/architecture.md) | [文档中心](../README.md) + +可组合视图记忆(Composable View Memory)只有三个主要业务概念:**Source(记忆来源)** 拥有记忆及其操作;**Strategy(组合策略)** 提议可用 Source 如何参与;**View(上下文视图)** 是特定范围和场景下交给 LLM 的有界上下文与交互形态。运行时、档案、记忆空间是默认组合,不是 Core 对所有记忆的固定分类。 + +## 归属与装配 + +```mermaid +flowchart TB + Starter["dsh-mnemon 默认安装包 · cordis.patch.yml"] --> Host["宿主 · ctx.mnemonMemory"] + Starter --> Runtime["dsh-mnemon-source-runtime"] + Starter --> Docs["dsh-mnemon-source-documents"] + Starter --> Spaces["dsh-mnemon-source-memory-spaces"] + Starter --> Strategy["dsh-mnemon-strategy-default-three-tier · 分层策略,默认"] + Starter --> General["dsh-mnemon-strategy-general · 可选主策略,默认停用"] + Starter --> Helpers["三种随附增强 · 默认停用"] + Helpers -. 选择 / 投影 / 记录 .-> Strategy + Spaces --> Providers["dsh-mnemon-provider-* · 私有子 Fiber"] +``` + +图中实线表示 Starter 安装归属,虚线表示用户开启后才生效的策略贡献,不是业务调用链。DSH 创建顶层 Entry/Fiber;Source、Strategy 与策略贡献使用同一套 `installMemory(ctx, ...)` SDK 注册,由 Cordis 负责卸载。Core 只提供 `ctx.mnemonMemory`,不替 Memory Spaces 实现 Fiber,也不提供 `ctx.mnemonMemorySpace`。 + +Memory Spaces **自己定义内部 Fiber 与 Provider 协议**。每个 Provider 都来自明确安装并配置的子模块;两个 Source 实例可以使用同名子节点,各自持有独立目录和凭据。不存在扫描依赖自动选择实现、全局 Provider 注册表等隐式装配。 + +借鉴 Spring Boot Starter,默认发行包负责选依赖、给默认配置,不把 Source 业务收回 Core。用户仍只安装 `dsh-mnemon`;17 个插件包可独立开发、测试与发布。Starter 安装全部官方包,其中通用策略与三个策略增强以停用 Entry 随附:增强在开关打开后参与 View,通用策略只有被选为主策略后才组合 View。更换主策略始终需要显式选择。 + +| 归属 | 负责 | 不负责 | +|---|---|---| +| Core | 内部注册、协议校验、不可变 View、预算、运行代与租约 | Provider 驱动、Source 数据格式与存储决策、页面、DSH 生命周期策略 | +| SDK | 小型贡献服务、Source/Strategy 作者契约、安装辅助与限定范围的测试工具 | 引擎/注册表构造器、已安装记录、运行代句柄 | +| Source | 数据权威、facts、投影、grant、查询/修改、可选管理协议与 Client | 其他 Source 的控制器、全局策略选择 | +| Strategy | 确定性的纯函数 `request + facts + 自有槽贡献 → ViewSpec`,拥有槽语义 | 原始数据、凭据、驱动、副作用、扩张权限 | +| Host | scope、阶段 hook、工具/RPC、认证、设置、监督任务 | Source 私有实现与注册表 | +| Starter | 包集合、Entry id、默认配置 | 第二套加载器或运行时 | + +`ctx.mnemonMemory` 是真正受限的服务对象,不是给引擎套一个更窄的 TypeScript 类型。它只暴露一个注册原语,由 `installMemory` 使用;Host 执行能力留在内部。Provider 在自己的 Source 内遵循同样原则,只收到绑定子节点的 `host.install` 能力。公开测试夹具验证这些协议,不交出私有宿主对象。 + +## 内置存储范围 + +存储布局属于根包的 Host 基础设施。同一解析器处理 `global`、`workspace`、`custom` 与 `workspaces`;Core 传递选定的操作范围并执行 View 权限约束。文件系统路径归一化、环境变量与用户目录默认值、工作区目录哈希留在 Host,不新增存储贡献 API 或独立发布包。 + +Host 将解析后的目录交给各默认 Source。Source 继续拥有自己的格式、事务和 Provider 状态;切换布局不搬迁其数据。显式选择全局 USER.md 时仅用户档案使用全局根,项目数据仍留在所选工作区根。设置和只读清单使用同一布局解析器。 + +## 默认插件组合 + +| 插件 | 记忆权威 | 默认 View 贡献 | +|---|---|---| +| `dsh-mnemon-source-runtime` | Runtime JSON、USER/MEMORY 投影、分支过滤与容量 | eager 精确工作上下文 | +| `dsh-mnemon-source-documents` | 受管 Markdown、索引、搜索、修订与归档 | 有界叙事封面与搜索 route | +| `dsh-mnemon-source-memory-spaces` | 记忆空间目录、内部 Provider、能力与召回质量策略 | 有界持久证据封面与 recall/related route | +| `dsh-mnemon-strategy-default-three-tier` | 不存储记忆 | 分层策略:选择三种角色,分配投影、route 与 action | +| `dsh-mnemon-strategy-general` | 不存储记忆 | 可替代分层策略:在同一份预算内接入全部可用 Source,由模型决定路由 | + +九个独立 Provider 插件包为 `dsh-mnemon-provider-{mnemon-native,openviking,honcho,mem0,hindsight,holographic,retaindb,byterover,supermemory}`。Provider 运行在 Memory Spaces **内部**,负责存储/检索驱动,不是 Core 的新贡献种类。Git、Notion、健康记录通常应实现 Source;不同的组合方式应实现 Strategy。 + +未加扩展的分层策略对同一默认角色出现多个实例报歧义错误。可启用 `strategy-scoped` 显式组合多个实例;停用后恢复默认歧义检查,不按加载顺序猜测。`selection`、`projection` 与 `capture` 三个扩展槽是标准槽,两个随附主策略都声明了它们,因此更换主策略后增强仍然生效。Core 只传递有界贡献并执行原有预算/权限校验,不理解这些槽的业务语义。 + +## View 数据流 + +```mermaid +flowchart LR + Facts["Source 能力事实"] --> Strategy["Strategy → ViewSpec"] + Strategy --> Core["Core 校验"] + Core --> Project["Source 投影 + ReadGrant"] + Project --> View["不可变 View"] + View --> Wake["Wake → LLM"] + View --> Route["读取 / 写入 → 所属 Source"] + Route --> Result["证据 Evidence / 回执 Receipt"] +``` + +View 包含投影片段、route、action offer 与仅留在 Host 的 ReadGrant。Wake 只渲染有界模型表示,并包含可调用 route/action 的 schema;不泄露 grant 载荷、控制器或凭据。Evidence 记录来源与一致性,不是另一份持久记忆。 + +Strategy 输出只是提案。Core 校验实例身份、声明能力、route/action 及预算;Host 执行时再次检查当前权限。一致性明确区分:捕获内容的 `exact-snapshot`,以及只能固定命名空间、远端内容仍可变化的 `namespace-pinned-live-read`。后者不冒充历史数据库快照。 + +```mermaid +sequenceDiagram + participant DSH + participant Host + participant Core + participant Source + participant LLM + DSH->>Host: 回合开始(范围、场景) + Host->>Core: 获取当前服务运行代并组合 + Core->>Source: 获取能力事实;按 Strategy 选择调用投影 + Source-->>Core: 片段 + 不透明 ReadGrant + Core-->>Host: 不可变 View + Host->>LLM: 自有插件消息:有界 Wake + 读取/写入入口 + LLM->>Host: 选中的读取/写入入口 + 输入 + Host->>Core: 检查范围、权限与预算 + Core->>Source: 查询 / 修改 + Source-->>LLM: 经 Host 返回有界证据 / 提交回执 + DSH->>Host: 回合结束 + Host->>Core: 释放租约;排空已退役运行代 +``` + +## 生命周期与失败 + +Host 的 `MemoryExecutions` 统一持有 Core 回合及对应的运行时绑定。Agent 生命周期与后台协调器使用同一份所有权:并发维护共享一个 View,新回合等待子任务及工具清理完成后,再选择当前运行时。是否中止闲置整理仍由生命周期决定;执行所有者不会中止无关的已授权写入,也不会增加权限。 + +候选组合先验证,再发布。额外候选被拒绝时,不会悄悄替换 Serving 运行代;显式移除当前依赖的贡献时,该运行代退役,不再接新回合。已开始的回合和操作持有租约,结束后再排空旧 Source 及其私有资源。 + +每回合固定一个不可变 View。写入生成回执,后续回合读取新修订;并发父/子 Agent 不能借用另一回合的 grant。Source/Provider 故障保持局部、可观察;部分成功、失败、取消、已提交不会混为一谈。停用参与不删除数据。 + +子 Agent 在派发时捕获委托 View 及运行代,保留至自身释放,不因父回合结束或 Serving 换代而改变。每次子执行具有独立回合身份与检索预算,不会回退到父 Agent 的最新 View。Wake 以 `dsh-mnemon` 自有插件消息追加在共享上下文之后,不重复注入或插值其他插件的上下文。 + +## WebUI 与管理面 + +默认 Source 在自己的 `presentation/` 中持有双语文案与布局资源,并由自己的 Client 加载;Starter 也通过公开包路径组合这些资源,保留既有可选页面工具的导出。工作台负责导航、容器和主题,不再掌握 Source 专属选择器。这是默认产品的呈现组合,不是 Core 要求或新的 UI 框架。 + +Source 自己拥有可选的 `./client` DSH 模块、页面、管理协议与测试,通过公开 Source 页面 SDK 注册到工作台的 `mnemon.source.page` Slot。Client 生命周期与 React 渲染仍由 DSH 管理。 + +Host 交给页面的是限定实例的管理客户端与脱敏元信息,不是裸 RPC、Host Context 或 LLM grant。读取和带确认、修订栅栏的修改指向一个 Source。档案归档到记忆空间等默认协作由 Host 编排,也只调用公开管理协议。 + +同一个共用工作台提供两个互斥的 DSH 入口。Sidebar 在受支持的 DSH(0.1.7-rc.2 与 0.2.0-rc.2)公开的 `sidebar.panellist` 与 `main` Slot 中注册配对的 `mnemon` 入口。DSH 负责按钮、图标尺寸、标签、选中状态和主面板导航。`shell.overlay` 注册保留 Source 子 Slot 的渲染权限,将工作台 portal 到持久的原生主面板挂载点,从而保留切换面板时的页面状态和独立的 Better Sidebar 挂载点。未提供原生面板契约的替换布局继续使用现有入口及浮层回退。Sidebar 无会话也可打开,保留自己的工作区选择,与 Taskboard/SSH 协调,并通过 `layout.selectPanel(null)` 返回会话。会话标签页(`builtin`)使用 `conversation.view`,读写和任务均使用所属会话的存储范围,不提供独立工作区选择器。两个入口渲染同一组 Source 自有子 Slot,不创建第二个 React root、兜底页面注册表或复制业务页面。 + +## 使用兼容与未来演进 + +默认 Starter 保留存储选择、持久格式、具名工具及使用流程。`displayMode` 选择默认的 `sidebar` 或 `builtin`;Host 接受旧 `buildin`,并在配置可写时通过 DSH 带修订栅栏的设置写入器保存规范拼写。这只修改一项偏好,不改变记忆数据或 Core/Source 契约;其他配置键保留原有语义。这**不意味着**保留私有控制器、旧根包 `kernel/layers/provider-sdk` 入口和历史包装包。当前公开入口见[扩展开发](./extensions.md)。 + +RSI 的边界保持简洁:产出候选 Source/Strategy 制品,用固定 facts/request 测试与回放,审查权限,再按正常装配方式安装和选择。运行代支持验证后的替换与排空,不是自动执行生成代码或自动晋升服务。Cordis 的归属/隔离并非安全沙箱;高风险外部操作仍需单独的授权边界,不能以“记忆”之名自动获得权限。 diff --git a/dsh/docs/zh-CN/development/extensions.md b/dsh/docs/zh-CN/development/extensions.md new file mode 100644 index 000000000..2a90abdbb --- /dev/null +++ b/dsh/docs/zh-CN/development/extensions.md @@ -0,0 +1,250 @@ +# 记忆插件开发 + +**简体中文** | [English](../../en/development/extensions.md) | [文档中心](../README.md) + +先确定归属,再写代码。[plugin-consumer](../../../scripts/fixtures/plugin-consumer) 中的完整示例,会在仓库外只依赖打包制品编译和测试。 + +## 区分贡献的职责 + +| 插件 | 拥有 | 公开依赖 | +|---|---|---| +| `dsh-mnemon-source-*` | 一种记忆权威、投影、检索/修改及可选页面 | Core contracts 与 extension SDK | +| `dsh-mnemon-strategy-*` | 完整 View Strategy,或目标 Strategy 支持的可叠加贡献 | Core contracts、extension SDK;目标 Strategy 的扩展 SDK | +| `dsh-mnemon-provider-*` | Memory Spaces 内部驱动、描述符、能力、连接 schema、图标 | Memory Spaces Provider SDK | + +Source 不需要再实现 Provider;Provider 不需要理解 View 组合;Strategy 只接收 facts,不接收 Source 对象。跨包只引用声明过的公开出口,不进入其他插件的 `src`、控制器、注册表或构建配置。 + +| 入口 | 职责 | +|---|---| +| `dsh-mnemon` | DSH Host 与默认 Starter | +| `dsh-mnemon/core` | 安装 Source 无关服务的 Cordis 插件;不导出引擎,不带 Host/UI | +| `dsh-mnemon/contracts` | Source/Strategy 回调契约,以及 JSON 安全的 manifest、facts、ViewSpec、View、Evidence、Receipt | +| `dsh-mnemon/extension-sdk` | Source/Strategy 定义、生命周期注册与校验 | +| `dsh-mnemon/testing` | 限定范围的组合、route/action、管理测试,JSON 诊断与已构建 Client 制品加载器 | +| `dsh-mnemon/client` | DSH 工作台与 Source 页面 SDK | +| `dsh-mnemon-source-memory-spaces/provider-sdk` | Memory Spaces 自己的 Provider 子模块协议 | +| `dsh-mnemon-source-memory-spaces/testing` | 私有子模块夹具,以及驱动权限/连接夹具 | + +## 公开贡献服务,不公开引擎 + +沿用 DSH 服务/Slot 的思路,扩展点是小而明确的贡献契约。`Context.mnemonMemory` 的类型为 `MnemonMemoryService`,实际对象也只暴露 `installContributions`,并被冻结。作者调用 `installMemory(ctx, contribution, options?)`:由它解析当前 Entry 身份,并将注册清理绑定到该 Fiber;不另建平行注册 API。 + +引擎、已安装记录、注册表、运行代和租约留在内部,`extension-sdk`、`contracts` 与 Core 插件均不导出这些对象。`MemorySourceRuntime` 则有意公开:它描述**你的 Source 要实现的回调**,不是引擎句柄。定义/factory 属于 Host 侧可执行契约;元信息、操作输入和结果保持 JSON 安全。这个服务边界不等于任意 JavaScript 的安全沙箱。 + +## Source 生命周期 + +用 `defineMemorySource` 定义 `MemorySourceDefinition`。manifest 声明 API 版本、type id、包名、角色、一致性和 route/action;factory 接收稳定的实例来源及不可变配置。 + +- `facts(request)`:有界、非敏感的可用性、修订和能力。 +- `project(request)`:与选中修订一致的有界文本及 Source 自己的 ReadGrant。并发快照按请求/scope 捕获,不共享一个可变的“最近快照”。 +- `query`:只收到 `{ id, scope }` 形式的 `view` 和属于本实例的 `grant`,执行 Source 自身范围限制,返回带来源的有界 Evidence。 +- `mutate`:只执行已授权的 action,响应取消,区分已提交、部分成功、失败。它同样只收到 View 身份及可选的本实例 `grant`;读取凭据不等于写授权。 +- 可选 `manage`:独立于模型 grant 的认证人工管理,再次检查确认和精确修订。 +- 可选 `dispose`:运行代租约排空后释放其私有资源。 + +完整 `ComposableMemoryView` 留在 Host 与组合测试中,不传给 Source 回调。Source 不得从 `request.view` 遍历其他实例的投影或凭据;需要自身读取范围的写操作直接使用 `request.grant`。 + +`facts(request, signal)` 和 `project(request, signal)` 的第二个参数是取消信号;网络读取必须传递它,回调不得写入数据。Core 并行读取不同 Source,每次读取默认最多 10 秒;DSH Host 采用现有 `timeoutMs` 配置,独立测试可设置 `MemoryCompositionRunner({ sourceTimeoutMs })`。信号不进入 Strategy 的 JSON 输入。超时或远程异常生成脱敏的 `view.diagnostics`;协议不合法仍直接拒绝。取消整轮不会被当作可选 Source 降级。超时不能中断阻塞事件循环的同步代码,这不是恶意插件沙箱。 + +```ts +import type { Context } from '@deepseek-ai/cordis' +import { defineMemoryPlugin, installMemory } from 'dsh-mnemon/extension-sdk' +import { notesSource } from './source.js' + +export const name = 'dsh-mnemon-source-notes' +export const inject = ['mnemonMemory'] +export const memoryPlugin = defineMemoryPlugin({ + packageName: name, + label: { en: 'Notes', 'zh-CN': '笔记' }, + description: { en: 'Durable personal notes.', 'zh-CN': '长期个人笔记。' }, + roles: ['source'], + provides: [{ id: 'source' }, { id: 'source.durable-evidence' }], +}) +export function apply(ctx: Context): void { + installMemory(ctx, { plugin: memoryPlugin, sources: [notesSource] }) +} +``` + +以上假定 `source.js` 导出定义;完整文件记忆示例见 [external-source.ts](../../../scripts/fixtures/plugin-consumer/src/external-source.ts)。Source 与 Strategy 是职责,不是强制分包/分仓。一个插件可以调用 `installMemory(ctx, { sources: [source], strategies: [strategy] })`,在同一 Fiber 下成批安装、一起卸载,保留不同的实例 key;仅在需要独立复用/替换时拆包。Strategy 仍需显式选择,随包提供不等于覆盖用户的选择。 + +每个可组合插件都应导出一份 JSON 安全的 `memoryPlugin` 描述,并把同一个对象传给 `installMemory`。`roles` 只描述贡献职责,不产生两套插件类型;`provides` 与 `requires` 构成激活关系图。只有两个提供者确实不能同时参与时,才把能力标记为 `exclusive: true`。只声明硬依赖:能够真实降级或空操作的插件,不应强迫用户安装无关依赖。Core 会在运行 Source factory 前校验激活图,并且仍是唯一把图编译为一个 View 的组件。没有这份预发布描述的旧插件保持既有组合行为,但 Host 只能推断有限的身份与关系信息。 + +Entry id 标识实例,type id 标识实现。不能剥掉 Loader 的 include 前缀。没有 Loader 身份的直接 `ctx.plugin()` 挂载,应传 `installMemory(..., { instanceId })`。路径和凭据归实例,不使用模块全局数据库或服务注册表。 + +## 完整 Strategy 与可叠加贡献 + +用 `defineMemoryStrategy` 定义,再以 `{ strategies: [definition] }` 安装。声明确定性、支持角色与上限(与角色无关的 Strategy 只列出 `ANY_MEMORY_SOURCE_ROLE`,即 `'*'`);纯 `compose` 返回 `MemoryViewSpec`,选择准确的 Source key、eager/routed 投影预算和 Source 本地 route/action id。组合阶段不执行网络、存储或凭据访问。 + +可以同时安装多个主策略,每个 View 只由其中一个组合:`memoryView.strategyTypeId` 负责选择;所选主策略被停用且只剩另一个主策略时,Host 改用那一个并报告 `strategy-fallback` 诊断。完整 Strategy 用可选 `extensionSlots` 声明自有的独占扩展槽。小插件使用 `defineMemoryStrategyExtension`,以 `{ strategyExtensions: [definition] }` 安装。启用后向目标 Strategy 的一个槽贡献有界 JSON,停用只撤销自身贡献;不同槽同时参与一个 View,同一目标的重复槽会拒绝注册,不按安装顺序覆盖。目标 Strategy 未被选择时,贡献保持可观察但不执行;不支持的槽会拒绝候选运行代,并保留已有 Serving。 + +Core 只处理目标/槽身份、JSON、64,000 字符上限、确定性回放、生命周期和最终 View 的原有预算/权限;它只定义下文 `selection`、`projection` 与 `capture` 三个标准槽的取值契约,其他槽名归目标 Strategy 所有。扩展回调只接收 request 和权限过滤后的 Source facts,没有 Source 对象、grant 或写入回调。槽语义由目标 Strategy 的公开 SDK 定义。动态回调或槽值在具体回合中不合法时,该回合拒绝,不偷偷回退到忽略插件的 View。 + +### 可选的配置声明 + +专用 Strategy Entry 还可以导出 `memoryStrategyConfiguration`,由 Core SDK 的 `defineMemoryStrategyConfiguration` 定义。它包含中英文展示文案、公开字段(`number`、`text`、`textarea`、`string-list`、`source-list`),以及**与 `apply()` 共用的纯 `create(config)` factory**。factory 只返回一个 Strategy 或扩展贡献以及同一份 `memoryPlugin` 描述,不做 I/O、凭据访问、Source 注册或 Fiber 挂载;完整例子见内置增强包。这只是面向 Host 与未来工具的可选约定,不定义插件身份或激活关系。 + +helper 校验并冻结元信息副本,不执行 factory;返回的 `create(config)` 会校验传入字段与所声明的贡献,省略值的默认行为仍由 factory 负责。`number` 必须是有限整数;列表最多 32 个不重复、非空且不超过 500 字符的字符串;文本默认上限为 4,000 字符。Host 对未使用 helper 的模块复用同一套校验,同时保留 Loader 身份检查和局部错误隔离。 + +普通用户看不到通用的记忆插件发现、依赖图或安装弹窗。每个已安装的记忆组件都出现在**插件 → 可组合记忆**页面的**记忆组合**面板中,位于 DSH 自带的组件列表上方。DSH 插件管理器可用时,它们的启停保存在 DSH profile patch 中,因此插件页的原生组件开关与 Mnemon 控件始终一致;Mnemon 只保存所选主策略和各 Entry 的配置。 + +#### 插件在面板中的呈现 + +面板由各插件自己的声明生成,新插件无需修改 Mnemon 即可被列出、开关与配置: + +| 声明 | 面板中的呈现 | +|---|---| +| `roles` | `strategy` 是**主策略**的一个选项;`source` 在**记忆来源**中有一行,关闭时也保留;`strategy-extension` 在**增强**中有一行;其他角色自成一组 | +| `label`、`description`(`en`、`zh-CN`) | 该行的名称与说明,按页面语言显示;随附组件以同样方式声明;组件详情还会显示包名以及是否随附于 dsh-mnemon | +| 扩展的 `strategyTypeId` | `'*'` 或所选主策略的类型与其他增强并列;其他类型收入“适用于其他主策略”的折叠分组 | +| `requires`、`provides` | 开启组件时,若它需要的能力没有组件提供,会一同开启第一个已安装的提供者;关闭组件时,会一同关闭因此失去所需能力的组件。主策略的最后一个来源不能关闭,该开关会被拒绝。行内以关联标签点名它需要的唯一提供者、以及只依赖它的运行中组件;组件详情列出全部关系 | +| `provides[].exclusive`、扩展的 `slot` | 两个组件声明同一能力且任一方独占,或两个扩展为有交集的主策略填充同一槽位时,不能同时运行:开启一个会关闭另一个 | +| `memoryStrategyConfiguration.fields` | 行内的齿轮,以及组件页中的选项,每个字段一个控件,带名称、说明、默认值与限制;改动后才出现**应用**。`source-list` 提供角色属于 `sourceRoles` 的运行中 Source 实例,以注册它们的组件命名 | + +组件详情说明开关还会带动哪些组件,开关后的提示条会点名实际变化并提供**撤销**。请准确声明 `requires` 与 `provides`:用户正是通过它们看到插件需要什么、不能与什么同时运行。 + +DSH 自己的插件列表从包内的 `locale/en.json` 与 `locale/zh.json`(`meta.title`、`meta.description`)读取名称与说明,需以 `./locale/*.json` 导出并列入 `files`;缺少时显示包名。请与声明中的 `label`、`description` 保持相同文字,与随附包一致,让组件在 DSH 列表与面板中名称相同。 + +#### 组件自己的设置与状态卡片 + +声明的选项不够用时,组件可以在 `./client` 入口把自己的设置加入它的组件页,与随附组件的做法相同:运行时记忆的用户画像范围、记忆空间的 Provider 与嵌入、分层策略的后台任务。Source 还可以提供它运行时在记忆系统“状态”页卡片上显示的内容。 + +```tsx +import { installMemoryComponentUI } from 'dsh-mnemon/client' + +export function apply(ctx) { + ctx.effect(() => installMemoryComponentUI(ctx, { + packageName: 'acme-memory-notes', + settings: ({ component, writable, language }) => , + status: ({ language }) => <>{notesHeadline(language)}

{notesDetail(language)}

, + })) +} +``` + +设置渲染在 `mnemon.component.settings` 区域,以插件声明中的包名为键,位于组件页的状态、关系与声明的选项之后;该组件的行会出现打开它们的齿轮。它只接收页面知道的信息:`component`(`packageName`、`label`、`enabled`)、`writable`、`language`,以及存在时对话的 `sessionId` 与 `workspace`;所需的服务请通过自己的注册注入。注册会等待 dsh-mnemon 的配置出现,调用返回的函数即结束。请遵循页面的交互规则:开关与下拉选择改动即生效,需要输入的值等待你自己的**应用**,且不并入其他分组的保存。 + +状态卡片渲染在 `mnemon.component.status` 区域,同样以包名为键,在组件声明的名称下方显示 `` 中的一行要点与 `

` 中的一行说明。它接收 `component`、`language`,以及存在时记忆系统的 `sessionId` 与 `workspace`,并自行读取自己的 Source。无论是否提供内容,每个 Source 组件都有一张卡片:组件已关闭或其 Source 未运行时显示页面自己的说明,未提供内容时显示正在运行。标签页、卡片与页头都使用组件声明的名称。 + +第三方插件仍走 DSH 原生 Profile/Loader 流程:用准确包名执行 `dsh plugin --profile add <包名>@<版本> --save-exact`,检查其 `peerDependencies` 与 `dsh.bundle.patch`,重启后在 Profile 装配中明确激活。下载 npm 包本身不等于激活,Mnemon 也不会在当前进程热加载新代码。欢迎外部作者按本页契约贡献独立仓库;通用图形化管理会在接口和社区用例稳定后再评估,不是 v0.5 承诺。 + + + +### 标准 View 扩展 + +`dsh-mnemon/extension-sdk` 的 `defineMemoryViewExtension` 以 `ANY_MEMORY_STRATEGY`(`'*'`)为目标:扩展跟随声明了该槽的所选 Strategy,否则保持停用并给出诊断。每个槽只有一个所有者:两个扩展指向同一 Strategy,或其中任一以 `'*'` 为目标时,后注册者会被拒绝,因为切换主策略后二者会在同一个槽相遇。`validateMemoryViewExtension` 与 `memoryViewExtensionValues` 让 Strategy 读取同样经过校验的值。`dsh-mnemon-strategy-default-three-tier/extension-sdk` 保留 `defineThreeTierExtension`,供只作用于该 Strategy 的扩展使用。随附增强使用以下标准槽: + +| 可选插件 | 槽 | 贡献与边界 | +|---|---|---| +| `dsh-mnemon-strategy-scoped` | `selection` | Source key 顺序、可写子集;不创建 Source、不改变其物理存储范围 | +| `dsh-mnemon-strategy-light-context` | `projection` | 一份共享投影上限,只收紧 Host 预算;不是增量注入或摘要压缩 | +| `dsh-mnemon-strategy-auto-capture` | `capture` | 当前对话的记录指引、目标与明确 Action id;不启动后台 Agent、不直接写入 | + +Starter 会安装这三个包并注册为停用的 DSH Entry,因此开关始终可用,但默认组合、预算和提醒保持 v0.4 行为。打开开关后,对应贡献自动参与当前主策略,不改变主策略;关闭只撤销该贡献,不删除任何 Source 数据。通用策略(`dsh-mnemon-strategy-general`)同样接受这三个槽。Runtime 当前没有展开 route;把常驻预算压得很低可能隐藏热记忆,需要针对实际任务评测。 + +`scoped` 的 Source key 若包含 Loader 的 include 前缀,应使用实例目录中的完整 key;省略配置时按角色/key 确定性组合现有实例,不创建新的存储。面板的**选项**可编辑包声明的字段;Profile 配置仍可用于自动化。 + +`scoped` 的 `sourceKeys` 表示优先顺序,`writableSourceKeys` 限定可写子集。自动容量整理同样受本轮可写范围限制;被禁止时保留原数据并报错,不绕过范围向其他 Source 迁移。只有显式人工管理走独立的管理授权。 + +```ts +import type { Context } from '@deepseek-ai/cordis' +import { defineMemoryViewExtension, installMemory } from 'dsh-mnemon/extension-sdk' + +export const inject = ['mnemonMemory'] +export function apply(ctx: Context): void { + installMemory(ctx, { strategyExtensions: [defineMemoryViewExtension({ + typeId: 'my-light-context', packageName: 'dsh-mnemon-strategy-my-light-context', + slot: 'projection', contribute: () => ({ maxProjectionCharacters: 4096 }), + })] }) +} +``` + +扩展不另建后台调度器;对话内写入仍通过已有 Host 工具、授权和 Source 回执。`capture` 不能把 `manage-spaces` 等一般写操作自动当成记录:作者必须指定实际记录用的 Action id。共享检索预算仍按整个执行回合计算,不按 Source 数量倍增;缓存和 Related 准入带 Source 身份,避免同名空间串用证据。停用贡献后重建失败时,不继续使用已撤销策略:新回合不带记忆 View 运行,对话本身照常进行;已固定的旧回合仍按原租约完成。 + +可选的 `createTurn(view)` 返回执行级 `query(request, read)` 策略。它只获得绑定当前 Route 和私有 grant 的 `read(input, narrowerLimits?)`,Core 仍校验输入、有效上限、已分派次数和生命周期。策略可以筛选、重放结果,并用 `Evidence.output` 提供简洁模型输出,但不会获得 Source 对象、写入回调或新增权限。即使继承同一个不可变 View,不同执行轮次也拥有独立策略状态。不提供此钩子时,读取仍经过 Core 边界直接进入 Source。 + +分层策略插件用此钩子实现旧版 Documents 单次查询、Recall 两次查询的共享证据预算、去重与 Related 准入。命名工具和通用 View Route 共用这套策略。Source 保留原始检索、存储和维护能力;显式的 DSH 辅助写入/归档仍由 Host 工作流执行,不成为 Core 的通用后台任务。 + +默认插件的公开 `threeTierActionWorkflow` 纯策略识别 Runtime `mutate` 的容量维护,Host 将具名工具、通用 View Action、子 Agent 与浏览器管理统一接入该流程。它只在选择分层策略时生效,不改变 Source 的独立管理协议,也不向 Core 添加三层存储逻辑。模型写入保留发起回合的 View、实例与权限,归档目标限定在该 View 的可写 Memory Spaces Source 及其定义的写入范围内;多个可写归档 Source 无法唯一确定目标时拒绝归档。浏览器使用已登记工作区对应的 scope、实例与确认修订,无需绑定用户会话。只有需要模型判断时才创建独立维护任务。 + +归档预检时,Host 可向所选 Memory Spaces Source 的 `body-directory` 读取传入 `{ writeScope: { viewId, grant } }`。可选响应 `writeScope: { viewId, sourceInstanceKey, memoryBodyIds }` 与 `remember` 使用相同权限:grant 中已知的命名空间,加上该 View 创建的命名空间。Source 校验 grant 所属实例;Host 校验返回的 View 与 Source 身份,将空范围视为无授权,并在写入前复查权限与当前能力。未返回此字段的 Source 仍使用较窄的已激活命名空间固定范围。响应格式损坏时拒绝操作。Host 不会用其他 Source 或当前目录替代缺失的授权;召回保留原有命名空间固定范围。 + +选中的 Source 默认必需;`required: false` 明确允许该实例在不可用或投影失败时被省略。必需实例失败会拒绝本轮 View,不悄悄切换策略。分层策略对可用 Source 作组合,并将它们标为可选,因此外部读取失败不会带走其他层。缺少必需实例时,Strategy 应明确拒绝,而不是返回一个空选择。 + +[external-strategy.ts](../../../scripts/fixtures/plugin-consumer/src/external-strategy.ts) 是完整的显式选择示例。安装后,它会出现在“插件 → 可组合记忆”页面的**主策略**选择器中,选择结果以 `memoryView.strategyTypeId` 保存;自动化部署可在尚未保存选择时通过 `mnemon.memoryTopology.strategyId` 选择它;多个适用 Strategy 是错误,不采用“后导入覆盖前者”。Profile 显式替换默认 Entry;只安装一个包不等于允许它替换当前组合。 + +可选 `ViewSpec.guidance` 承载 Strategy 的可信 `system`、`routing` 和读写提醒,与 Source 的引用数据分离,经校验后进入 View digest。没有提供时,Host 使用通用路由提示。已有 DSH 命名工具只显示本轮可用性,不重复注入工具目录中的 schema;外部或未绑定的操作仍展示准确 id 和 schema。已有产品工具与人工管理保留;工具存在不代表对应 Source 已进入本轮 View。分层策略的自动后台整理只在选择 `default-three-tier` 时运行,自定义 Strategy 不会隐式触发这项业务流程。 + +## 开放操作,有限执行 + +Source 自己定义操作名、输入 schema、存储、索引和维护方式。不要求五类动作、摘要树、统一表示词汇或多维预算协议。Strategy 按 id、角色或权限能力选择自己理解的公开操作。Core 从 Manifest、动态 Facts 和 Host 权限生成 `MemoryAvailableSource.routes/actions`,作者不重复填写描述。 + +Core 仅约束投影字符数、证据字符数/条数及已分派调用次数;这些是载荷上限,不是完整 LLM prompt 的 token 估算。Source 接收有效上限,自行提供摘录和结构化格式;Core 丢弃超限条目,不伪造摘要或切坏 JSON。已分派失败仍消耗调用次数,不自动重试。 + +每个变更回执必须声明 `completion`:accepted、candidate、committed、partial、failed 或 unknown。只有确认完整提交才能带 `committedAt`,Core 不补造该时间。`createMemoryMutationReceipt(..., completion)` 默认 unknown;Source 确认所请求的持久效果完成后才传 `'committed'`。取消或传输异常不证明没有发生写入;跨 Source 流程也不是原子事务。通用 View 工具和已有产品工具均向模型保留此区别。 + +## Provider 子模块与 Client 页面 + +Provider 使用 Memory Spaces SDK 的 `defineMemorySpaceProvider`。模块只收到限定子节点的 `host.install(ctx, definition)` 能力;私有父 Host、Snapshot 和 Registry 不从 SDK 导出。`installMemorySpaces` 挂载显式子模块,返回 `Promise`,不返回父对象句柄。[external-provider.ts](../../../scripts/fixtures/plugin-consumer/src/external-provider.ts) 会在两个独立父 Source 内以同名子节点测试。 + +通过 Source 的 `/testing` 入口使用 `mountMemorySpaceProvider`,测试真实子节点注册、带实例别名的驱动创建和清理;返回值只有冻结的 descriptor/manifest 元信息、`registered`、`createAdapter` 与 `dispose`。`createMemorySpaceProviderFixture` 单独提供经过验证的连接及限定范围的驱动权限。[发布 API 测试](../../../scripts/fixtures/plugin-consumer/tests/public-api.spec.ts) 展示如何组合二者,不导入私有实现。 + +```yaml +- id: work-spaces + name: dsh-mnemon-source-memory-spaces + config: + dataDir: /absolute/path/to/work-memory + providers: + - use: dsh-mnemon-provider-holographic + instanceId: local-facts +``` + +**Source 自己定义 Provider Fiber**;Core 不提供 Provider factory registry 或第二个 Context 服务。能力声明必须真实:查询型后端不能伪造全量浏览或图关系。 + +声明 `entities` 的 Provider 会为实体页提供数据。默认情况下,Source 用 `list()` 为每个空间建立实体索引:请在请求的上限内返回全部记忆及其 `entities`,一个实体的计数就是列出的记忆中带有它的条数,也就是实体页列出的那些记忆。如果 Provider 的 `list()` 取不全整个空间(例如受服务端分页大小限制),或者不能列举,请实现可选的 `entityIndex(body, signal)`,返回 `{ memories, complete }`:带有实体的记忆,以及是否已包含全部。索引不完整时,实体页会标为部分。声明了 `entities`、但既不能列举也没有 `entityIndex` 的 Provider 显示为仅查询,它的记忆只会作为召回找到的相关记忆出现在实体页。只要空间的 `status()` 统计不变,Source 就沿用该空间的索引,因此统计应随每次写入变化。 + +可选的 `./client` 通过 `dsh-mnemon/client` 的 `installMemorySourceUI` 注册,由 DSH 作为普通 Client 插件加载。页面接收 `MemorySourcePageProps`:选中实例、locale、可写状态和限定范围的 `management.read/mutate`。用 `MemorySourcePageFrame` 复用 locale/appearance;React 不接收 Host Context、驱动、令牌、LLM grant 或传输层。缺少专属页面时有通用管理入口,重复归属和渲染失败局部诊断。 + +工作台负责外层页面边距、最小高度与页面滚动容器。嵌套的 `memoryPageStyles.page` 内容复用这一层框架,不再重复增加视口高度或边距,经过 DSH renderer 包装层时也一样。Source 保留业务布局,可以提供有界阅读区或弹窗。内部页面变化时,在绘制前调用可选的 `onResetScroll` 回调;它只重置所属工作台的 canvas,不应滚动 DSH 祖先节点或其他插件。记忆空间这类包含固定标题与 Tab 组合头部的 Source,通过 `navigation.stickyHeader: false` 避免 Host 同时固定下级标题,并由 Source 自己负责组合头部的 sticky 布局。 + +需要显示某个元素时,将 Source 自有 ref 中的元素传给可选的 `onRevealElement(element, topInset)` 回调。不传 `topInset` 时,元素会停在锁定的页面标题下方;自己固定头部的 Source 则传入实测的头部高度。Host 只滚动所属 canvas,并忽略区域外的元素;不要使用全局 ID 或会移动 DSH 祖先节点的 `scrollIntoView`。关闭选择或卸载 Source 时取消待执行的动画帧。两个滚动回调均为可选:新 Source 仍支持现有 Root peer 最低版本,旧 Host 不提供回调时可手动滚动。 + +用户每次点击工作区顶栏的**刷新**,`refreshKey` 都会变化;页面应在它变化时重新读取数据,而不是自己放置刷新或同步按钮。只有对话中的锚点(例如回合记忆栏里的一条内容)打开页面时,`navigationInput` 才是 `{ seed, nonce }`;点击标签页打开时没有它。默认 Source 把 seed 分别理解为档案 id(项目档案选中它)、召回查询(记忆空间打开后立即执行)或条目文本(运行时记忆高亮并滚动到它)。新的 `nonce` 表示对同一 seed 的又一次请求。 + +`dsh-mnemon/client` 还导出默认 Source 使用的控件,让安装的 Source 与它们外观和行为一致:`SearchField`(带搜索图标的 DSH 输入框)、`SelectField`(带标签的 DSH 选择菜单,支持 `inline`、`size: 'sm'`、`hideLabel` 与 `ariaLabel`)、`WriteReceipt`(写入的结果、摘要与可选的查看操作)和 `TaskAgentTag`(任务 Agent 能否接手)。它们遵循[交互约定](../guides/ui-guide.md#交互约定)。导入这些控件或读取 `refreshKey` 的 Source,应把 `dsh-mnemon` peer 最低版本声明为首个导出它们的 Starter 0.5.19。 + +## 独立仓库验收 + +业务文案和布局应留在 Source 包内。默认 Source 使用公开、仅含数据的 `presentation/locales.json`、`presentation/page.module.css`、`presentation/sidebar.module.css`,以及私有 Client 呈现模块示范这一点;独立构建无需引用仓库中的构建脚本。默认 Source 保留既有页面工具的类名命名空间以维持使用兼容;第三方 Source 可使用自己的命名空间,不必向 Root 添加词条或选择器。 + +每个插件拥有自己的 `package.json`、exports、`src/`、`tests/`、TypeScript/构建/测试配置及 README。声明公开 peer 与开发依赖,发布 Host/Client 制品和类型,不依赖兄弟路径、工作区源码别名、根测试夹具或隐式依赖提升。 + +只有默认 Starter 挂载默认组合;Source/Strategy 包不携带自动激活默认实例的 patch。用户 Profile、Bundle 或明确的父组合负责激活和替换。 + +```ts +import { MemoryCompositionRunner } from 'dsh-mnemon/testing' +import * as notes from './index.js' +import * as focus from './strategy.js' + +const runner = new MemoryCompositionRunner() +try { + await runner.mount(notes, { instanceId: 'work', config: { path: '/tmp/test-notes.txt' } }) + await runner.mount(focus, { + instanceId: 'focus', + config: { sourceKeys: ['source:work'], mode: 'eager' }, + }) + const turn = await runner.beginTurn() + try { + const route = turn.view.routes.find(route => route.sourceRouteId === 'read')! + const evidence = await turn.executeRoute(route.id, {}) + // 断言投影、证据与来源;写操作使用 + // turn.executeAction(offer.id, input, authorize),明确给出测试授权。 + } finally { turn.release() } +} finally { await runner.dispose() } +``` + +正式测试使用唯一临时路径,先释放 turn,再 dispose runner。`runner.inspect()` 只返回 JSON 评估/运行代诊断;`managementClient(sourceKey)` 提供限定实例的人工操作。turn 不暴露租约,runner 不暴露引擎;通过卸载/重挂插件测试替换,不修改内部注册表。dispose 也会释放遗留 turn;在途操作持有自己的租约,完成后才排空。这是在真实 Cordis/Core 上的测试夹具,不是另一套生产 Loader。 + +至少覆盖:正常组合、缺失/歧义依赖、双实例、schema/能力/授权拒绝、并发快照、旧修订、取消/部分失败、卸载排空/重载、持久化、管理与真实页面点击。Provider 另测凭据、真实能力、上游损坏数据、超时与父 Source 内 conformance。 + +插件运行自己的 `pnpm verify`。仓库级 `pnpm verify:plugins` 打包全部 17 个制品,用正常 semver manifest 在工作区外逐个安装、检查、测试和构建,再编译外部消费者;禁止源码 alias、manifest override 和工作区软链接。 + +RSI 应保存可复现候选输入/制品,对照已知组合评估,经明确安装/选择决策晋升。Strategy 回放通过,不代表任意 JavaScript 已被沙箱隔离,也不授予交易、发消息或删除外部数据的权限。 diff --git a/dsh/docs/zh-CN/development/releasing.md b/dsh/docs/zh-CN/development/releasing.md new file mode 100644 index 000000000..ded2e7fff --- /dev/null +++ b/dsh/docs/zh-CN/development/releasing.md @@ -0,0 +1,22 @@ +# 独立包发布流程 + +**简体中文** | [English](../../en/development/releasing.md) | [开发与验证](./README.md) + +通过发布 PR 准备版本,不从功能分支直接发布。README 等进入 npm 制品的文档也需要发布意图;仅站点文档或历史证据的改动不要求包升级。 + +```text +改动与 changeset → 审阅 PR → 版本 PR → main revision +→ 验证并打包变化的包 → npm-release Environment 批准 +→ 按依赖层发布变化的包 → 安装验证完整固定组合 +→ 发布 Starter → Registry 升级 smoke → GitHub Release +``` + +官方包采用独立版本。`dsh-mnemon` Starter 是经过测试的物料清单:它精确固定每个内置插件,而插件 peer dependency 描述当前 minor 线内的兼容范围。只有自身制品或发布元数据变化的插件才升级;只要固定的组合发生变化,Starter 就升级。仅作为反向依赖测试目标的包不会因此重新发布。 + +凡 PR 会改变发布制品,都使用 `pnpm changeset` 添加发布意图;`pnpm release:status` 可预览版本变化,已有的 source CI job 会把 PR 与其精确 base revision 比较,并拒绝任何已变化却未出现在新增 changeset 中的发布包。新增包的 PR 同样要为它添加发布意图;该包随下一个发布 PR 首次发布,版本由这份意图决定。专用发布 PR 运行 `pnpm release:version`,一次性应用意图、同步 Starter 与外部 fixture 的精确依赖、更新 Provider 中生成的版本声明并刷新 lockfile。workspace peer 与开发关系使用 minor 线兼容范围,因此未变化包的 manifest 也保持不变。若变化的插件开始使用同一版本新增的 Starter SDK 导出,发布 PR 还要把该插件的 `dsh-mnemon` peer 下限提高到新的 Starter 版本。`pnpm release:check` 是只读检查,验证完整的混合版本组合、内部兼容范围、Starter 精确版本、仓库元数据和每个包的 npm 通道。 + +手动触发的 npm workflow 接收完整 commit SHA,且该 SHA 必须已经等于 `main`。它从 Git 历史确定上一个发布 revision,只选择版本发生增长的包。在取得 npm 凭证前,workflow 会运行完整 workspace 与独立插件验证,只打包选中的插件和 Starter,并记录完整组合、两个 revision、字节数与 SHA-512 integrity。受保护的 `npm-release` Environment 是 Registry 写入门禁。批准后,变化的插件在依赖安全的层内并发发布;一层在 Registry 可读后才进入依赖它的下一层。随后通过冻结的本地 Starter 安装完整混合版本组合,最后发布 Starter、验证干净 Registry 安装并运行真实升级,之后才创建 GitHub Release。 + +中断的流程可以恢复:Registry 中已有的选中版本只有在 integrity 与冻结 tarball 完全一致时才能复用;同版本内容不同会立即终止。正式包使用 `latest`,预发布显式使用 `alpha`、`beta` 或 `rc` 通道。未变化的包必须已存在于 Registry,并通过完整 Starter 安装接受验证,但不会再次打包或发布。 + +npm 发布不是事务。失败时保留冻结制品并重跑同一 revision;完整性检查会安全跳过匹配版本并继续补齐缺失版本。不能覆盖或撤销不可变版本。只有新 Starter 及其精确组合中的所有版本都可安装,才算发布完成。开发验证不会创建 Git tag、GitHub Release 或 npm 发布。 diff --git a/dsh/docs/zh-CN/development/skin-integration.md b/dsh/docs/zh-CN/development/skin-integration.md new file mode 100644 index 000000000..3149532a5 --- /dev/null +++ b/dsh/docs/zh-CN/development/skin-integration.md @@ -0,0 +1,81 @@ +# 皮肤开发与 Mnemon 适配 + +**简体中文** | [English](../../en/development/skin-integration.md) | [开发与验证](./README.md) + +本指南面向希望让 Mnemon 工作台与宿主皮肤保持一致的作者。Mnemon 提供稳定的页面选择器和背景变量,皮肤负责选择颜色、透明度与可读性。安装一套现有皮肤不会自动启用 Mnemon 专用的玻璃效果;需要由皮肤显式提供覆盖规则。 + +## 公开约定与适用范围 + +Sidebar 与会话标签页共用以下根选择器: + +```css +[data-dsh-plugin="dsh-mnemon"][data-dsh-part="mnemon-view"] +``` + +受支持的背景变量为 `--mn-bg`、`--mn-backdrop` 和 `--mn-surface`。值类型、默认合成、层叠优先级与继承范围以[界面指南的皮肤约定](../guides/ui-guide.md#theme-skin-overrides)为准。未覆盖时保留默认背景,官方主题保持不透明。 + +该约定随 [PR #284](https://github.com/omdsh-dev/dsh-mnemon/pull/284) 引入;测试时应使用包含该变更的构建。较早版本可能没有此根标记,此时规则不会匹配。不要把 CSS-module 哈希类名、未列出的内部变量或 DOM 层级当作兼容接口。 + +背景适配属于皮肤的 Client 样式,不需要新增 Mnemon 设置,也不需要修改 Host、Source、Strategy、Provider 或存储格式。根规则不覆盖其他插件,也不覆盖挂载到 `body` 的 portal 弹窗;弹窗继续使用自己的表面样式。 + +原生 Sidebar 入口与“插件”一样,由 DSH 通过 `sidebar.panellist` 渲染。因此,皮肤的原生面板行规则统一提供颜色、字体、间距、选中状态和折叠表现。Mnemon 只提供标签及跟随请求尺寸的图标;上面的工作台背景契约作用于打开后的页面。 + +替换布局若没有原生面板席位,Mnemon 会在“新建会话”下方插入自己的入口:`[data-dsh-plugin="dsh-mnemon"][data-dsh-part="sidebar-entry"]`。它沿用 DSH 面板行的数值:继承的 14px 字号、主要文字色、36px 行高配 `7px 8px` 内边距与 `0 2px` 外边距、`--dsw-radius-md` 圆角、16px 图标(折叠侧栏为 18px),悬停与打开状态使用同一悬停背景。打开时还会设置 `data-active` 与 `aria-current="page"`。重绘原生面板行的皮肤可以用该选择器保持一致。 + +原生主面板仅在最外层预留一次 DSH 的 `--dsh-frame-top-clearance`,保证桌面窗口控件可用。页面层位于 body 装饰层之上、DSH 窗口控件之下;皮肤不应再给内部工作台叠加窗口标题栏偏移。 + +## 在皮肤中添加覆盖 + +1. 将规则放入皮肤实际加载的 CSS 文件,随皮肤一起安装、切换和卸载。不要修改 Mnemon 的源码或生成的 `lib/` 文件。 +2. 用皮肤自己的激活标记限定范围,再连接完整的 Mnemon 根选择器。这样切换皮肤后,规则就不再命中。 +3. 在 Mnemon 根元素上赋值。仅在 `html` 或其他祖先元素上设置 `--mn-*`,会被根元素自身的默认值覆盖。 + +以 [dsh-web 皮肤中心](https://github.com/zhu1090093659/dsh-skins)的 v2 皮肤格式为例,皮肤激活标记是 `html[data-dsh-skin=""]`。可将适配规则放入 `skin.json` 中 `contributes.patches` 指定的 `patches.css`。若已有该文件,追加规则并保留原有内容;新增时只补充相应字段,不替换完整清单。 + +下面的 `my-glass-skin` 是示例 ID,需要替换为你自己皮肤的 `skin.json` 中的 `id`。示例假定皮肤已经按深色和浅色模式,将 `--dsw-alias-bg-base` 设置为合适的半透明颜色: + +```css +html[data-dsh-skin="my-glass-skin"] +[data-dsh-plugin="dsh-mnemon"][data-dsh-part="mnemon-view"] { + --mn-bg: var(--dsw-alias-bg-base); + --mn-backdrop: var(--mn-bg); + --mn-surface: var(--mn-bg); +} +``` + +此规则让使用 `--mn-surface` 的工作台区域采用皮肤基础色,替换默认的背衬合成。如果基础色完全透明,结果也会完全透明;需要皮肤作者选择合适的透明度。壁纸与模糊效果仍由皮肤负责,这三个变量不会自动创建它们。这里的 dsh-web 文件格式与激活标记属于皮肤中心;其他皮肤系统应采用自己的加载方式和激活范围,保留同一个 Mnemon 根选择器。 + +规则应放在未分层的样式表中,保留两个 Mnemon 属性。仅这两个属性的优先级就高于默认声明,无需依赖样式表加载顺序或添加 `!important`。不要把普通覆盖放进 `@layer`,它会低于 Mnemon 未分层的默认声明。 + +## 从旧类名写法迁移 + +对于已有的 `[class*="_shell"]` 规则,保留皮肤自己的背景计算,把目标替换为带皮肤激活范围的公开根选择器。删除旧的宽泛匹配,避免误改其他插件中同名的内部布局。两个入口使用同一条规则,无需为 Sidebar 和会话标签页各维护一份皮肤代码。 + +例如 Issue #273 使用的 `--pg-panel-rgb`、`--pg-bg-glass` 和 `--pg-glass-floor` 是该皮肤自己的变量;Mnemon 不提供这些变量。迁移时保留皮肤对它们的定义,不要将它们加入 Mnemon 配置。 + +## 真实 WebUI 验证 + +使用[开发与验证指南](./README.md)中的隔离环境,安装准备发布的皮肤及 Mnemon 制品,使用合成记忆完成下列检查: + +| 检查 | 预期结果 | +|---|---| +| 默认主题与停用皮肤 | 保留默认表面,皮肤覆盖不残留 | +| Sidebar 与会话标签页 | 同一个根选择器命中当前工作台,两个入口均可导航 | +| 深色、浅色与窄窗口 | 文本、按钮、输入框清晰;背景和响应式行为符合皮肤设计 | +| Runtime 新增、编辑、刷新 | 操作成功,刷新后记录保留 | +| 弹窗打开与取消 | 弹窗保持可读,关闭后可继续操作工作台 | +| 多插件组合与返回会话 | 其他插件页面及会话输入不被 Mnemon 规则误改 | +| 皮肤切换和重新加载 | 激活范围随皮肤变化,效果与持久化选择一致 | + +在相同页面、数据、明暗模式和窗口尺寸下保留默认与适配后的截图,并记录 Mnemon、DSH、皮肤中心及皮肤的版本。原版皮肤展示与加入适配 CSS 后的展示应明确区分。 + +若覆盖没有生效,依次检查皮肤是否真正激活、根标记是否存在、样式表是否加载,以及开发者工具中的 `--mn-surface` 计算值。规则被划掉时检查选择器和 `@layer`;计算值已改变但看不见壁纸时,检查皮肤自己的背景、遮罩和响应式规则。皮肤安装或设置保存失败,应单独记录,不能据此判断 Mnemon 钩子失效。 + +维护 Mnemon 本身时,还应运行现有的定向回归与文档检查: + +```sh +pnpm exec vitest run tests/client.spec.tsx -t 'accepts scoped skin variables' +pnpm verify:docs +``` + +定向回归覆盖两个入口和其他插件的样式隔离;实际绘制、样式加载顺序和可读性仍需真实浏览器验证。已有复现步骤及前后对照见 [Issue #273 验收记录](../../pr-assets/issue-273-skin-hooks/README.zh-CN.md)。 diff --git a/dsh/docs/zh-CN/guides/capabilities.md b/dsh/docs/zh-CN/guides/capabilities.md new file mode 100644 index 000000000..7d28b3192 --- /dev/null +++ b/dsh/docs/zh-CN/guides/capabilities.md @@ -0,0 +1,108 @@ +# 能力地图 + +**简体中文** | [English](../../en/guides/capabilities.md) | [文档中心](../README.md) + +dsh-mnemon 是 DeepSeek Harness(DSH)的记忆系统。它不把所有知识塞进同一个数据库,而是把高频上下文、完整的项目叙事和长期证据分开保存,由主策略决定它们如何进入每一轮对话,并把九种长期记忆 Provider 纳入同一套工作流。 + +一句话判断:**每轮都要用的上下文放进运行时记忆,完整叙事放进项目档案,跨任务需要的证据放进记忆空间。** + +## 能做什么 + +| 目标 | 位置 | 如何执行 | 会改动数据吗 | +|---|---|---|---| +| 让偏好、约定和环境事实每轮可用 | **运行时记忆** | 宿主维护 `USER.md` 与 `MEMORY.md` 投影 | 会,确认后生效 | +| 保存完整的设计、调查、流程与交接 | **项目档案** | 宿主管理 Markdown、检索、容量与修订 | 新建与编辑会;阅读与检索不会 | +| 在九种后端之一创建长期空间 | **记忆空间 → 概览 → 创建记忆空间** | 由你选择一个已启用的 Provider | 会 | +| 决定任务 Agent 新建空间时用哪个 Provider | **记忆空间 → 新空间的 Provider** | 固定一个 Provider,或先执行宿主规则、再由任务 Agent 在合格候选中选择 | 保存设置时会 | +| 查找原始长期证据 | **记忆空间 → 检索 → 直接检索** | 各已激活空间以原生召回方式并行返回 | 不会 | +| 把证据整理成回答 | **记忆空间 → 检索 → Agent 查询** | 干净的任务 Agent 只拿到有界证据 | 不会 | +| 判断、去重、提炼并写入候选内容 | 回复下方或记忆空间页的**存入记忆** | 干净的任务 Agent 在宿主限定的工具、路径、锁与回执下工作,并给出回执 | 仅当 Agent 决定写入时 | +| 为多个空间生成标题与说明 | **记忆空间 → 概览 → 整理名称与说明** | 每个空间一个独立任务 | 只改本地目录元数据 | +| 把档案移出热容量 | **项目档案 → 归档** | 任务 Agent 先建立冷引用索引,宿主再移动原文件 | 会 | +| 查看一轮对话用了哪些记忆 | 回复下方的**本回合记忆** | 按工具列出读到和写入的档案与记忆,每一条都在所在页面打开 | 不会 | +| 改变记忆的组合方式 | **插件 → 可组合记忆 → 记忆组合** | 开关作用于之后的回合 | 只改配置 | + +## 三类记忆 + +| 记忆 | 适合保存 | 如何进入上下文 | 事实源 | +|---|---|---|---| +| **运行时记忆** | 高频偏好、协作规则与项目事实 | 每轮紧凑注入 | `runtime/memories.json`,Markdown 文件是投影 | +| **项目档案** | 需要保留结构与来源的长篇知识 | 先检索,需要时读取全文 | `documents/index.json` 与受管理的 Markdown | +| **记忆空间** | 跨会话的事实、决策、实体与关系 | 从已激活空间召回有界证据 | Mnemon Native Store 或所选的外部 Provider | + +三者各是一个独立的 Source 插件。运行时记忆与项目档案使用本地存储;记忆空间通过 Provider 切换后端,不影响另外两者。外部 Source 也可以通过同一套公开契约加入。参见[架构设计](../development/architecture.md)。 + +## 策略与增强 + +每一轮由一个**主策略**组合。**增强**通过标准插槽为正在运行的主策略补充能力,可以同时使用。它们都在**插件 → 可组合记忆**中选择。 + +| 组件 | 作用 | 选项 | +|---|---|---| +| **分层策略**(默认) | 运行时记忆常驻;按需检索项目档案、召回记忆空间;执行空闲审查与容量维护 | 组件页中的任务 Agent 模型与空闲审查 | +| **通用策略** | 在同一份预算内提供全部可用来源,由模型决定如何使用;不做自动审查与容量维护 | 常驻 Source;附加指引 | +| **主动记录** | 在回合中提示保留有用的事实,它是提示而不是自动记录器 | 记录目标、记录指引与记录操作 | +| **轻量上下文** | 为常驻内容设置共同上限,它不是 token 计量 | 常驻内容上限(字符) | +| **范围组合** | 按顺序选择参与的来源,并限定可写入的来源;它不会创建存储 | 参与 Source(按优先顺序);允许写入的 Source | + +切换策略不会搬移数据,一轮对话会保持它开始时的组合。这些选择如何保存,见[配置参考](../reference/configuration.md)。 + +## 九种长期记忆 Provider + +| Provider | 形态 | 适合场景 | 作用域 | +|---|---|---|---| +| **Mnemon Native** | 官方本地 CLI 与 SQLite | 完整图谱、精确写入、默认本地优先 | 全局、工作区或自定义根目录 | +| **OpenViking** | HTTP 与 `viking://` | 已有资源树、经验证的精确写入与语义召回 | 目标 URI 与用户身份 | +| **Honcho** | HTTP workspace 与 peers | 团队与 Agent peer 结论 | Provider workspace | +| **Mem0** | Platform 或自托管 HTTP | 已有的 Mem0 用户与 Agent 记忆 | user 与 agent 身份 | +| **Hindsight** | HTTP memory bank | bank、实体与原生图谱 | bank ID | +| **Holographic** | 本地结构化事实文件 | 可审计的本地实体与语义事实 | 跟随工作区,可覆盖路径 | +| **RetainDB** | HTTP project 与 user | 项目与用户两个作用域的记忆 | project 与 user 身份 | +| **ByteRover** | 本地 `brv` CLI | 代码知识与 curate 流程 | 跟随工作区,可覆盖目录 | +| **Supermemory** | HTTP container | 文档摄取与容器级共享 | container tag | + +**插件 → 可组合记忆**中的记忆空间页面保存每个 Provider 可复用的服务设置与开关;记忆系统中的“记忆空间”页管理具体的空间。Provider 在启用前保持关闭。只有 Mnemon Native 需要 Mnemon CLI;缺少 CLI 时,其他已就绪的 Provider 也可以为记忆空间服务。参见 [Provider 指南](./memory-providers.md)。 + +## 谁在工作 + +**宿主,确定性执行。** 状态、直接检索、内容与实体浏览、激活、运行时记忆的普通编辑、档案阅读与编辑都不需要模型,直接经过宿主的 schema、路径、权限、锁、修订、容量、超时与取消检查。 + +**独立任务 Agent。** 它们不复用主对话的历史,也不占用主对话的上下文窗口: + +- **存入记忆**:判断价值、选择范围、去重、提炼并写入,给出回执; +- **Agent 查询**:根据有界的召回证据作答; +- **整理名称与说明**:为每个选中的空间运行一个标题与说明任务; +- **档案归档**:先为冷引用建立索引,宿主再移动原文件; +- **新空间的 Provider(智能选择)**:只有规则留下多个候选时才调用模型。 + +任务 Agent 默认跟随 DSH 新会话的默认链路;也可以在分层策略页面“后台任务”中的**任务 Agent 模型**单独指定 Provider 与模型。任务相互隔离,失败只显示在对应的空间或操作上,不会阻塞整个页面。 + +## 出现在哪里 + +| 位置 | 提供什么 | +|---|---| +| **记忆系统** | 状态、运行时记忆、项目档案与记忆空间,以及每一个确认步骤 | +| **插件页** | 记忆组合、带有各自设置的组件页(包括 Provider)、存储与界面 | +| **对话** | 回合记忆栏、存入记忆,以及跳转到记忆系统的入口 | +| **Headless** | 相同的运行时注入、档案检索、记忆空间工具、工作区路由与受监督写入,没有 WebUI | +| **命令与工具** | `/mnemon` 命令,以及 Agent 使用的最小权限工具 | + +## 存储范围 + +**全局**使用 `MNEMON_DATA_DIR` 或 `~/.mnemon`,本机各工作区共享;**工作区**使用 `/.mnemon`,Mnemon Native、Holographic 与 ByteRover 等本地后端可以跟随它;**自定义**是位于你所选路径的全局范围;**集中存储**在同一个根目录下为每个工作区建立子目录,并可选择全局共享 `USER.md`。切换时不会移动、合并或删除原来的根目录。远程 Provider 的命名空间(workspace、user、bank、project、container、URI)不会随 DSH 工作区改变。参见[存储模型](../reference/storage-model.md)。 + +## 明确不做什么 + +- 历史记忆永远不会凌驾于当前指令、实时工具结果或仓库事实之上; +- Provider 之间的差异如实呈现,缺少图谱、删除或精确写入时不会伪装; +- Provider 凭据不会进入浏览器、任务 Agent 或备份; +- 关闭 Provider 不会删除远程数据,只清理本地目录,重连后重建; +- 切换存储范围不会迁移、合并或删除旧的根目录; +- 不提供跨本地文件与远程 Provider 的分布式事务。 + +## 继续 + +1. [快速开始](./getting-started.md) +2. [熟悉界面](./ui-guide.md) +3. [比较各个 Provider](./memory-providers.md) +4. [生命周期、并发与失败边界](../reference/workflows.md) +5. [兼容性与升级](../reference/compatibility.md) diff --git a/dsh/docs/zh-CN/guides/getting-started.md b/dsh/docs/zh-CN/guides/getting-started.md new file mode 100644 index 000000000..32deb0897 --- /dev/null +++ b/dsh/docs/zh-CN/guides/getting-started.md @@ -0,0 +1,144 @@ +# 快速开始 + +**简体中文** | [English](../../en/guides/getting-started.md) | [文档中心](../README.md) + +本页从装好 dsh-mnemon 的 DSH 开始,一直走到对话真正用上记忆。全程使用默认设置:记忆系统位于侧栏、全局存储、分层策略。日常使用不需要了解 View 或 Strategy。 + +第一次使用?先看[安装与启动](./installation.md):从安装 Node.js 开始,到在插件页一键安装并启用 dsh-mnemon,再到第一条记忆,每一步都有截图。已经安装好了?直接跳到[打开记忆系统](#2-打开记忆系统)。准备升级?请按[兼容性与升级](../reference/compatibility.md)操作。 + +## 1. 命令行安装与升级 + +本节写给熟悉命令行的用户,也包括开发检出、云端访问与 Headless。需要: + +- DSH `0.2.0-rc.2`(npm `latest` 与 `next`)或 `0.1.7-rc.2`,以及 Node.js `^22.19.0 || >=24.0.0` 与 pnpm; +- 一个能够创建独立任务 Agent 的 DSH 模型路由; +- 仅在使用 Mnemon Native 时需要本地的 `mnemon` CLI,各平台的安装方式见[安装与启动](./installation.md#mnemon-cli-的其他安装方式);其他 Provider 连接各自的服务。 + +安装并核对 DSH: + +```sh +npm install -g @deepseek-ai/dsh +dsh --version +npm view @deepseek-ai/dsh dist-tags +``` + +Starter 固定一组经过测试的官方插件组合,详见[兼容性矩阵](../reference/compatibility.md)。Mnemon 的 Node 20 入口检查不代表完整的 Host 兼容。 + +

+任务 Agent 如何启动 + +语义任务优先使用名为 `spawn` 的 DSH Provider,并要求 `toolFilter`、`persona` 与 `depthLimit`。Mnemon 固定注册一个 `mnemon_subagent_result` 工具,并为每个子任务签发可撤销的 `requestId`。子任务返回 `{ requestId, result }`;Host 按该操作的 schema 校验 `result`,拒绝过期或其他子任务提交的结果。可选的后台审查默认通过受保护的 `spawn` 子 Agent 读取有界检查点,完整上下文 `fork` 需要显式选择。参见[审查兼容性与限制](../reference/configuration.md#provider-要求)。 + +
+ +需要完整工作台时安装到 Web profile: + +```sh +dsh plugin --profile web add dsh-mnemon +``` + +开发检出使用绝对路径: + +```sh +dsh plugin --profile web add "link:/absolute/path/to/dsh-mnemon" +``` + +然后启动或重启 profile: + +```sh +dsh --profile web +``` + +如果需要通过云端域名访问 Web profile,不要直接发布 3080 端口。DSH 通过 Host 启动时输出的一次性 URL 建立浏览器会话,并用它认证全部 Mnemon RPC 与 stream。请按[云端 WebUI](./operations.md#cloud-hosted-webui)同时配置 HTTPS 反向代理或访问网关与可信 authority,再打开该启动 URL。 + +升级与卸载: + +```sh +dsh plugin --profile web update dsh-mnemon +dsh plugin --profile web remove dsh-mnemon +``` + +更新后重启 DSH。新版本发布后的 24 小时内,pnpm 11 的 `update` 会停留在已安装的版本,这时请带版本号安装新版本(`dsh plugin --profile web add dsh-mnemon@<版本>`)。卸载只移除插件注册,不删除全局、工作区或自定义目录中的记忆数据。 + +不同 profile 的插件清单彼此独立。一次性任务也需要记忆时,应另行安装到 Headless: + +```sh +dsh plugin --profile headless add dsh-mnemon +dsh --profile headless "回答前先检查持久化的项目上下文。" +``` + +开发检出时把包名替换为 `"link:/absolute/path/to/dsh-mnemon"`。Headless 会挂载与 Web Agent 相同的运行时上下文、档案、记忆空间工具、生命周期提示和受监督写入路径,但不会挂载工作台、对话按钮、RPC 通道或交互式斜杠命令界面。 + +`storageScope=workspace` 时,Headless 直接解析 `<启动命令 cwd>/.mnemon`,不需要 Web 工作区目录。一次性 runner 会在 Agent 进入 idle 后退出,因此尚未开始的评分后台审查会在关闭时取消;任务内已经完成的显式或模型引导写入仍会持久化。 + +## 2. 打开记忆系统 + +在侧栏点击**记忆系统**,默认进入**状态**页。 + +![状态页:每个记忆组件与 Provider](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory-status.jpg) + +请确认: + +- 顶栏显示**已连接**,并写明主策略,默认为“分层策略”; +- 记忆引擎卡片显示 dsh-mnemon 的版本;如果安装了 Mnemon CLI,它会出现在**记忆 Provider** 中; +- 运行时记忆、项目档案与记忆空间各有一张没有错误的卡片; +- 存储根目录与你选择的存储范围一致。 + +即使使用全局存储,项目档案也需要 DSH 工作区。请为对话选择工作区;“等待工作区”表示缺少项目上下文,而不是缺少 CLI。如果缺少 Mnemon CLI,可在 macOS 或 Linux 上运行 `command -v mnemon` 与 `mnemon --version`,在 Windows 上运行 `Get-Command mnemon`。其他问题见[故障排查](./operations.md#故障排查)。 + +## 3. 保存第一批记忆 + +**运行时记忆。** 打开**运行时记忆**,点击**添加记忆**,在用户画像中写一条偏好,或在工作记忆中写一条项目事实。之后的每一轮都会注入它。 + +**一份档案。** 打开**项目档案**,点击**新建档案**,保存一份简短的设计说明或检查清单。问题需要时,Agent 会检索档案。 + +**一个记忆空间。** 打开**记忆空间 → 概览**,点击**创建记忆空间**: + +1. 选择 Provider。安装 CLI 后会出现本地默认的 Mnemon Native;第三方 Provider 需要先在[记忆空间页面](./ui-guide.md#在插件页中)启用。 +2. 起一个范围明确的名称,例如“项目决策”,并说明其中应该保存什么。 +3. 保持激活,对话才能读取它。 + +在空的存储根目录中,第一个 Mnemon Native 空间使用 Mnemon 的 `default` Store ID,同时保留你填写的名称与说明;其他 Provider 上的空间使用各自的 ID。接着点击**存入记忆**,写下稳定且不含机密的内容,点击**交给任务 Agent**;独立任务 Agent 会选择空间、去重并写入,回执写明存到了哪里。 + +**验证一下。** 打开**记忆空间 → 检索**,提一个具体的问题,点击**直接检索**。每条结果都保留所属记忆空间、分类、重要性与分数;需要时可以复制 ID。 + +![在已激活的记忆空间中直接检索](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory-recall.jpg) + +也可以使用对话命令: + +```text +/mnemon status +/mnemon recall <具体的问题> +``` + +## 4. 在对话中使用记忆 + +提一个依赖已保存内容的问题,让 Agent 自己判断是否需要记忆。回复完成后: + +- 如果这一轮用到了记忆,会出现**本回合记忆**,展开后按工具列出读到和写入的档案与记忆,点击一条即在所在页面打开它; +- 回复下的脑形图标是**存入记忆**,打开可编辑的对话框,“取消”不会写入任何内容,交给任务 Agent 后会收到回执。 + +![一条回答用到了工作记忆、项目档案与记忆空间,回合记忆栏列出读到的内容](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/chat-recall.jpg) + +普通对话不会强制召回。当前请求、仓库文件与实时工具结果的优先级高于历史记忆。 + +## 5. 选择记忆的组合方式 + +打开**插件 → 可组合记忆**,或点击记忆系统顶栏的齿轮。 + +![记忆组合面板](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/plugin-composition.jpg) + +- **主策略**:保留**分层策略**,或选择**通用策略**,在同一份预算内提供全部可用来源,由模型决定如何使用。 +- **记忆来源**:运行时记忆、项目档案与记忆空间,各有一个开关。关闭后停止它的上下文、工具与后台任务,但不删除数据。 +- **增强**:主动记录、轻量上下文与范围组合默认关闭,两个主策略都可以使用。 +- **存储**:全局(默认)让所有工作区共用一个目录;工作区把每个工作区的记忆放在它自己的 `.mnemon`;集中存储把每个工作区放在同一个根目录下。**数据目录**在“默认”与“自定义”之间选择。两者的改动都不会搬移已有数据。 +- **界面**:记忆系统可以在侧栏或会话标签页中打开,也可以开关对话中的两个控件。 + +开关与选择器立即生效,存储的改动需要点击**应用**。[界面指南](./ui-guide.md#在插件页中)介绍了每个页面,[配置参考](../reference/configuration.md)列出了它们背后的设置项。 + +## 6. 下一步 + +- 在[界面指南](./ui-guide.md)中熟悉每个页面。 +- 用[存储模型](../reference/storage-model.md)判断内容该放进运行时记忆、项目档案还是记忆空间。 +- 用[运维指南](./operations.md)导出第一个 ZIP 备份,并做好升级前的准备。 +- 用 [Provider 指南](./memory-providers.md)接入长期记忆后端。 diff --git a/dsh/docs/zh-CN/guides/installation.md b/dsh/docs/zh-CN/guides/installation.md new file mode 100644 index 000000000..81ad8bc00 --- /dev/null +++ b/dsh/docs/zh-CN/guides/installation.md @@ -0,0 +1,260 @@ +# 安装与启动 + +**简体中文** | [English](../../en/guides/installation.md) | [文档中心](../README.md) + +本页写给第一次使用的你:从一台还没装过 DeepSeek Harness(DSH)的电脑开始,装好 dsh-mnemon,并让对话用上第一条记忆,大约需要 10 分钟。已经在用 DSH?直接跳到[第 3 步](#3-安装并启用-dsh-mnemon)。 + +![在插件页添加 dsh-mnemon:输入包名、安装、立即启用,侧栏随即出现记忆系统](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/zh-CN/install.gif) + +本页截图与录屏来自真实的 DSH 0.2.0-rc.1 与 dsh-mnemon 0.5.19,采集方式见[安装图集](../../assets/install-v0.5.19/README.md)。 + +## 选择使用方式 + +| 方式 | 适合 | 在哪里安装 dsh-mnemon | +|---|---|---| +| **网页(推荐)** | 在浏览器中使用 DSH,最容易开始 | 网页左侧的**插件 → 添加插件** | +| **桌面版** | 已经装好 DeepSeek Harness 桌面版 | 桌面版左侧的**插件 → 添加插件**,见[桌面版](#6-桌面版) | +| **命令行与 Headless** | 服务器、脚本和一次性任务 | `dsh plugin --profile <名称> add dsh-mnemon`,见[命令行与 Headless](#7-命令行与-headless) | + +三种方式安装的是同一个 dsh-mnemon。默认情况下,记忆保存在本机的 `~/.mnemon`,网页和桌面版共用这份记忆。 + +## 1. 准备 Node.js、pnpm 与 Mnemon CLI + +| 工具 | 用途 | 是否必需 | +|---|---|---| +| [Node.js](https://nodejs.org/) 22.19 或更高(推荐 22 或 24 LTS) | 运行 DSH | 必需 | +| pnpm | DSH 安装插件时调用它;没有它,插件页会提示“没有找到 pnpm,无法安装” | 必需 | +| Mnemon CLI | 记忆空间默认的本地存储 Mnemon Native 需要它;运行时记忆与项目档案不需要 | 推荐,也可以以后再装 | + +装好 Node.js 后,在终端(Windows 上为 PowerShell)中运行: + +```sh +node --version +npm install --global pnpm +npm install --global @mnemon-dev/mnemon@latest +mnemon --version +``` + +两个 `--version` 都打印出版本号即可。Homebrew、Go 与 Windows 手工安装等方式见[Mnemon CLI 的其他安装方式](#mnemon-cli-的其他安装方式)。 + +## 2. 启动 DSH + +```sh +npx @deepseek-ai/dsh web +``` + +DSH 在 `http://127.0.0.1:3080` 启动,并自动在浏览器中打开一个带一次性令牌的地址;终端里也会打印这个地址。使用期间保持终端开着,按 `Ctrl+C` 停止 DSH。 + +- 这条命令使用 npm 上 `latest` 标签的 DSH(撰写本文时为 0.1.7-rc.2)。想用 DSH 0.2 预览版,改用 `npx @deepseek-ai/dsh@next web`。dsh-mnemon 0.5.19 起两者都支持。 +- 经常使用时可以全局安装:`npm install --global @deepseek-ai/dsh`,之后运行 `dsh web` 即可;[命令行方式](#7-命令行与-headless)也需要全局安装的 `dsh`。 + +第一次打开时: + +1. DSH 0.2 会先显示**预览版说明**,点击**继续**。 +2. 在**添加一个 API Key 开始使用**中粘贴你在 [DeepSeek 开放平台](https://platform.deepseek.com/) 创建的 API Key,点击**保存并继续**。也可以先点**稍后配置**,之后在**设置**中填写;安装插件不需要 Key,对话需要。 + +

DSH 首次打开时的“添加一个 API Key 开始使用”对话框,下方是“稍后配置”与“保存并继续”

+ +## 3. 安装并启用 dsh-mnemon + +1. 点击左侧的**插件**,再点击右上角的**添加插件**。 +2. 输入 `dsh-mnemon`,点击**安装**。对话框右侧的**安装源**由 DSH 自动选择:在中国大陆通常是**中国大陆镜像源**,在其他地区是 **npm 官方源**,一般不需要修改。 +3. 几秒后显示**已安装**和版本号,点击**立即启用**。不需要重启 DSH。 +4. 左侧出现**记忆系统**,安装就完成了。 + +| 输入包名并安装 | 安装完成,立即启用 | +|---|---| +| ![“添加插件”对话框中输入了 dsh-mnemon,安装源为中国大陆镜像源](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/zh-CN/install-04-add.jpg) | ![“已安装”对话框显示 dsh-mnemon 版本 0.5.19,下方是“立即启用”](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/zh-CN/install-05-installed.jpg) | + +“已安装”卡片中的简介来自软件包信息,所以是英文。插件列表中的名称与说明会跟随界面语言。 + +习惯命令行时,也可以用下面的命令安装,再按第 2 步启动 DSH;DSH 正在运行时,安装后需要重启它: + +```sh +npx @deepseek-ai/dsh plugin --profile web add dsh-mnemon +``` + +## 4. 确认安装成功 + +点击**记忆系统**,默认打开**状态**页: + +- 顶栏显示**已连接 · 分层策略**; +- **记忆引擎**卡片显示 `dsh-mnemon 0.5.19` 或更新的版本; +- 装好 Mnemon CLI 时,**记忆 Provider** 中的 Mnemon Native 显示“服务就绪”和 CLI 版本。没有装时显示“未找到 Mnemon CLI”,运行时记忆与项目档案照常使用。 + +![记忆系统的状态页:dsh-mnemon 0.5.19、三个记忆层,以及显示 Mnemon 0.2.7 的 Mnemon Native](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/zh-CN/install-07-status.jpg) + +## 5. 保存第一条记忆 + +1. 点击**新会话**,输入一句希望它记住的话,例如:`记住:我在 Lumen 项目负责结账页性能,回答请尽量简洁。` +2. 回复下方出现**本回合记忆 · 写入 1**,展开即可看到这一轮写入的运行时记忆。 +3. 点击这一条,记忆系统会打开**运行时记忆**并高亮它。之后的每一轮对话都会带上它。 + +![输入一句要记住的话,回复下出现“本回合记忆 · 写入 1”,点击条目后在运行时记忆中看到它](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/zh-CN/first-memory.gif) + +| 这一轮写入了什么 | 在记忆系统中查看 | +|---|---| +| ![回复“已记住”,回合记忆栏列出一条运行时记忆更新](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/zh-CN/first-01-reply.jpg) | ![运行时记忆页高亮新写入的用户画像条目](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/zh-CN/first-02-runtime.jpg) | + +模型会自己决定写入的措辞与位置,你看到的文字可能不同。接下来请看[快速开始](./getting-started.md):项目档案、记忆空间,以及把一段回复**存入记忆**。 + +## 6. 桌面版 + +DeepSeek Harness 桌面版的**插件**页与网页相同:**插件 → 添加插件 → 输入 `dsh-mnemon` → 安装 → 立即启用**。 + +- 桌面版使用它自己的 `desktop` profile。从 DSH 0.2 起,命令行不再管理这个 profile,请在桌面版的插件页中安装和管理插件。 +- 桌面版窗口从应用自己的 `dsh-app://app/` 地址加载。dsh-mnemon 0.5.18 及更早的版本会把它当成远程页面,于是记忆系统和插件设置变为只读([#310](https://github.com/omdsh-dev/dsh-mnemon/issues/310))。0.5.19 已修复,更新 dsh-mnemon 即可,不需要修改配置。 +- DSH 的插件页不能更新已安装的插件。请在**记忆系统 → 状态 → 检查版本 → 更新**中更新 dsh-mnemon,它会用应用自己的安装器安装新版本。dsh-mnemon 0.5.21 及更早的版本在桌面版中做不到这一点;从这些版本更新时,在插件页移除 dsh-mnemon 后重新添加一次即可,记忆数据会保留。 +- 在桌面版中更新 dsh-mnemon 后,完全退出应用再重新打开(macOS 上按 `Cmd+Q`),新版本才会加载。更新后出现报错时,处理方法见[常见问题](#常见问题)。 + +## 7. 命令行与 Headless + +全局安装 DSH 后,可以用命令行管理每个 profile 的插件: + +```sh +npm install --global @deepseek-ai/dsh +dsh plugin --profile web add dsh-mnemon +dsh web +``` + +不同 profile 的插件互不影响。一次性任务也要用记忆时,把它另外安装到 Headless profile: + +```sh +dsh plugin --profile headless add dsh-mnemon +dsh --profile headless "回答前先检查持久化的项目上下文。" +``` + +升级与卸载: + +```sh +dsh plugin --profile web update dsh-mnemon +dsh plugin --profile web remove dsh-mnemon +``` + +升级后重启 DSH。新版本发布后的 24 小时内,pnpm 11 的 `update` 会停留在已安装的版本,这时请带版本号安装新版本,例如 `dsh plugin --profile web add dsh-mnemon@0.5.20`。卸载只移除插件,不会删除任何记忆数据。开发检出、云端访问与 Headless 的更多说明见[快速开始](./getting-started.md#1-命令行安装与升级)。 + +## Mnemon CLI 的其他安装方式 + +只有 Mnemon Native 使用 Mnemon CLI。请在运行 DSH 的那台机器上安装。推荐使用第 1 步的 npm 命令(Node.js 22+,macOS、Linux 与 Windows 通用),之后用 `mnemon update` 更新;状态页识别到所属 npm 安装时,也可以用“检查版本”中的更新操作。从 Homebrew、Go 或下载的二进制迁移到 npm 时,让 npm 全局命令目录在 PATH 中优先于旧命令,并同步调整 `MNEMON_CLI_PATH` / `mnemon.cliPath`。改变宿主环境后重启 DSH,再在状态页核对可执行文件路径。 + +macOS 也可以使用 Homebrew Cask: + +```sh +brew install --cask mnemon-dev/tap/mnemon +``` + +macOS 和 Linux 可以通过 Go 安装: + +```sh +go install github.com/mnemon-dev/mnemon@latest +``` + +Windows 手工安装时,官方发行包同时提供 AMD64 与 ARM64 ZIP。下面的 PowerShell 会把 v0.2.9 安装到可自动发现的用户 Programs 目录,并用官方 checksum 校验下载内容: + +```powershell +$version = '0.2.9' +$arch = if ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture -eq 'Arm64') { 'arm64' } else { 'amd64' } +$archiveName = "mnemon_${version}_windows_${arch}.zip" +$releaseBase = "https://github.com/mnemon-dev/mnemon/releases/download/v${version}" +$archive = Join-Path $env:TEMP $archiveName +$checksumFile = Join-Path $env:TEMP "mnemon_${version}_checksums.txt" +Invoke-WebRequest "${releaseBase}/${archiveName}" -OutFile $archive +Invoke-WebRequest "${releaseBase}/checksums.txt" -OutFile $checksumFile +$line = Get-Content $checksumFile | Where-Object { $_.EndsWith(" $archiveName") } | Select-Object -First 1 +if (-not $line) { throw "Checksum entry not found for $archiveName" } +$expected = (($line -split '\s+')[0]).ToLowerInvariant() +$actual = (Get-FileHash -Path $archive -Algorithm SHA256).Hash.ToLowerInvariant() +if ($actual -ne $expected) { throw "Checksum mismatch for $archiveName" } +$installDir = Join-Path $env:LOCALAPPDATA 'Programs\mnemon' +New-Item -ItemType Directory -Force -Path $installDir | Out-Null +Expand-Archive -Path $archive -DestinationPath $installDir -Force +$mnemon = Join-Path $installDir 'mnemon.exe' +& $mnemon --version +``` + +已经装好 Go 工具链时,Windows 上也可以继续使用 Go: + +```powershell +go install github.com/mnemon-dev/mnemon@latest +$mnemonBin = go env GOBIN +if (-not $mnemonBin) { + $mnemonBin = Join-Path (((go env GOPATH) -split ';')[0]) 'bin' +} +$mnemon = Join-Path $mnemonBin 'mnemon.exe' +& $mnemon --version +``` + +Windows 上,dsh-mnemon 会从 `PATH`、导出的 `GOBIN` 或 `GOPATH`、默认 `%USERPROFILE%\go\bin`、`%LOCALAPPDATA%\Programs\mnemon` 和 Program Files 中发现原生 `mnemon.exe`。同时支持官方 npm 的 `mnemon.cmd` 启动器:验证包身份后通过 Node 调用其 JavaScript 入口,全程不使用 shell。其他 `.cmd` 与 `.bat` wrapper 仍不受支持。 + +DSH 内嵌在 Electron 桌面主进程时,经过验证的 npm 启动器会在子进程中以 `ELECTRON_RUN_AS_NODE=1` 运行,覆盖记忆命令、版本检查和 npm 更新,并保留已保存的 embedding 设置。桌面应用自身的环境变量不变。如果桌面壳关闭了 Electron 的 `runAsNode` fuse,请将 `mnemon.cliPath` 指向当前平台的 Mnemon 原生二进制,详见[故障排查](./operations.md#故障排查)。 + +如果 DSH 仍然找不到二进制,请设置 `MNEMON_CLI_PATH`,或把 `mnemon.cliPath` 写入用户设置;不要为此整体替换插件的 profile patch(参见[配置参考](../reference/configuration.md)): + +```yaml +mnemon: + cliPath: 'C:\Users\alice\AppData\Local\Programs\mnemon\mnemon.exe' +``` + +`mnemon status` 会打开有效 Store,可能初始化数据或执行上游迁移,不要把它当作完全无副作用的安装探测。 + +## 常见问题 + +### 安装时提示“dsh-mnemon@… 与 DSH 0.2.0-rc.1 不兼容” + +![插件安装失败:dsh-mnemon@0.5.18 与 DSH 0.2.0-rc.1 不兼容,下方有“编辑”与“重试”](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/zh-CN/trouble-incompatible.jpg) + +DSH 在安装插件前,以及每次启动时,都会检查插件声明支持的 DSH 版本。dsh-mnemon 0.5.19 之前的版本只声明支持 DSH 0.1.7,所以 DSH 0.2 拒绝安装它们。 + +- 先确认 npm 上已有 0.5.19 或更新的版本:`npm view dsh-mnemon version`。 +- 新版本发布后的 24 小时内,pnpm 默认不会选用刚发布的版本,会改装一个较早的版本,于是出现这个提示;一天内连续发布时会退得更早,例如 0.5.17。DSH 0.1.7 会直接装上这个较早的版本而不提示,请在状态页核对版本号。此时点击**编辑**,输入带版本号的 `dsh-mnemon@0.5.24`(以 `npm view` 显示的版本为准),再点击**安装**;命令行为 `dsh plugin --profile web add dsh-mnemon@0.5.24`。也可以等 24 小时后重试。 +- 用带版本号的方式安装后,profile 会固定在这个版本。之后在新版本发布满一天后运行 `dsh plugin --profile web update --latest dsh-mnemon` 升级;一天之内同样带版本号安装新版本。 +- 不要为旧版本执行 `allow-version` 等“接受风险”的操作:旧版本确实没有在 DSH 0.2 上验证过。 + +### 插件页提示“没有找到 pnpm,无法安装” + +运行 `npm install --global pnpm`,然后在新的终端中重新启动 DSH,让 DSH 能在 PATH 中找到 pnpm。命令行对应的提示是 `pnpm was not found; install pnpm and make it available on PATH.` + +### 无法连接安装源 + +点击对话框右侧的**安装源**,在中国大陆镜像源与 npm 官方源之间切换后重试;公司内网可以填写自定义地址。 + +### 状态页显示“未找到 Mnemon CLI” + +按[第 1 步](#1-准备-nodejspnpm-与-mnemon-cli)安装 CLI 后重启 DSH。DSH 会在 PATH 和常见安装目录中查找 `mnemon`;仍然找不到时,按[Mnemon CLI 的其他安装方式](#mnemon-cli-的其他安装方式)设置 `mnemon.cliPath`。 + +### 在另一台设备上打开时,记忆系统是只读的 + +从另一台设备访问 DSH 时(例如通过 `--trusted-host` 配置的地址),记忆系统默认只读,页面会写明原因。确实需要远程管理时,按[运维指南](./operations.md#remote-management)设置 `remoteAccess: trusted-host` 并重启 DSH。桌面版窗口和本机浏览器都不属于远程页面。 + +### 更新后启用时提示 ERR_PACKAGE_PATH_NOT_EXPORTED + +在 DSH 运行期间更新了 dsh-mnemon(例如在桌面版的插件页中更新),接着启用组件,可能看到: + +```text +Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './starter' is not defined by "exports" in …/node_modules/dsh-mnemon/package.json +``` + +新版本已经装好,但正在运行的 DSH 仍按旧版本的包信息加载,找不到新版本才有的入口:从 0.5.17 或更早的版本更新时是 `./starter`,从 0.5.18 或 0.5.19 更新时是 `./bundle`。从 0.5.18 或 0.5.19 更新时,提示也可能是 `mnemon-bundle (dsh-mnemon/bundle): pending (waiting for service: mnemonStarterReady)`,因为正在运行的 DSH 仍保留旧的组件组。两种情况都完全退出 DSH 再重新打开即可:桌面版在 macOS 上按 `Cmd+Q`;命令行按 `Ctrl+C`,再重新运行 `dsh web`。 + +使用 0.5.18 或 0.5.19 时,不要为了消除这条提示而关闭 `dsh-mnemon/starter`,否则会遇到下一条问题。 + +### DSH 提示“waiting for service: mnemonStarterReady” + +![插件页中 dsh-mnemon 的详情:包含的组件共 11 个、11 个已停用,第一行 dsh-mnemon/starter 显示“已关闭”;侧栏没有记忆系统](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/install-v0.5.19/zh-CN/trouble-starter-off.jpg) + +DSH 启动时在终端中打印,或在插件页启用时弹出提示: + +```text +dsh: warning: 1 entry did not activate +mnemon-bundle (cordis:group): pending (waiting for service: mnemonStarterReady) +``` + +侧栏没有**记忆系统**,插件页中 dsh-mnemon 的组件全部显示“已关闭”。这是因为 `dsh-mnemon/starter` 被关闭了:0.5.18 与 0.5.19 由这一行单独在记忆组件加载前准备依赖解析,其余组件都要等它就绪。 + +- 点击**插件 → 可组合记忆**,打开 `dsh-mnemon/starter` 一行的开关。记忆系统随即出现;没有出现时重启 DSH。 +- 桌面版会把它当作启动失败,显示插件恢复页。点击**卸载此插件并继续检测**(记忆数据保留),应用启动后按[第 3 步](#3-安装并启用-dsh-mnemon)重新添加 dsh-mnemon。重新安装 0.5.18 或 0.5.19 时,这个开关仍是关闭的(它保存在 profile 中),因此接着按上一条打开它。 +- 0.5.20 起没有单独的开关:组件组自己准备依赖解析,残留的设置会被忽略。提示中的名称是 `dsh-mnemon/bundle` 时,说明刚更新到 0.5.20 而没有重启,DSH 仍在运行旧的组件组,完全退出 DSH 再重新打开即可。 + +### 准备把 DSH 升级到 0.2 + +先在现有的 DSH 中运行 `dsh plugin --profile web update dsh-mnemon`,把 dsh-mnemon 升级到 0.5.19 或更新的版本,再升级 DSH。否则 DSH 0.2 启动时会停用不兼容的旧版本;记忆数据不受影响,更新插件后即可恢复。更多细节见[兼容性与升级](../reference/compatibility.md)。 diff --git a/dsh/docs/zh-CN/guides/memory-providers.md b/dsh/docs/zh-CN/guides/memory-providers.md new file mode 100644 index 000000000..f670c47f8 --- /dev/null +++ b/dsh/docs/zh-CN/guides/memory-providers.md @@ -0,0 +1,76 @@ +# 长期记忆 Provider + +**简体中文** | [English](../../en/guides/memory-providers.md) | [文档中心](../README.md) + +记忆空间保存 dsh-mnemon 的长期记忆,其后端是可以替换的 Provider:记忆空间契约保持稳定,Provider 负责具体数据面。**Mnemon Native 是官方优先、默认实现**;三方 Provider 是显式选择的集成,适合已经使用其他记忆引擎,或需要不同共享、提炼与召回模型的团队。 + +只有 Mnemon Native 需要 Mnemon CLI。缺少 CLI 时,其他已就绪的 Provider 也可以为记忆空间服务,已保存的 Provider 选择依然有效。 + +![插件中的记忆空间页面:Mnemon Native 与各第三方 Provider](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/plugin-spaces.jpg) + +每个适配器都是独立发布的 `dsh-mnemon-provider-*` 包,由 Memory Spaces Source 作为子插件安装。Starter 随附九个包,外部服务仍需显式配置后启用;不捆绑外部后端服务或 CLI。参见[官方包列表](../../../README.zh-CN.md#官方插件)和 [Provider 作者契约](../development/extensions.md)。 + +## Provider 能力矩阵 + +| Provider | 数据面 | 召回 / 浏览 | 图谱 / 关联 | 写入 | 遗忘 | +|---|---|---|---|---|---| +| **Mnemon Native** | 官方 CLI 操作本地 `mnemon.db` | 支持 / 支持 | 完整类型图谱 / 支持 | 精确写入 | 软删除 | +| **OpenViking** | 已有 HTTP 服务与 `viking://` 记忆根 | 支持 / 支持 | 投影节点 / 不支持 | 经验证的精确正文 | 仅允许精确用户 `.md` 资源的受控硬删除 | +| **Honcho** | v3 工作区 conclusions | 支持 / 支持 | 不支持 / 不支持 | 精确 Peer conclusion | 硬删除 | +| **Mem0** | Platform v3 或自托管 HTTP API | 支持 / 支持 | 不支持 / 不支持 | 异步提炼 | 硬删除 | +| **Hindsight** | Memory bank API 与知识图谱 | 支持 / 支持 | Provider 图谱 / 支持 | 异步 retain | invalidation(软删除) | +| **Holographic** | 本地原子结构化事实文件 | 支持 / 支持 | 实体/语义图谱 / 支持 | 精确事实 | 硬删除 | +| **RetainDB** | Project/User 作用域 HTTP API | 支持 / 支持 | 不支持 / 不支持 | 精确记忆 | 硬删除 | +| **ByteRover** | 本地 `brv` CLI 与知识目录 | 支持 / 不支持 | 不支持 / 不支持 | 异步 curate | 不支持 | +| **Supermemory** | Container 作用域 HTTP API | 支持 / 支持 | 投影节点 / 不支持 | 异步文档摄取 | Provider forget | + +Host 只暴露适配器能够兑现的能力;UI 与 Agent 工具不会伪造缺失的图谱、关联、链接、浏览或删除语义。 + + + +## 服务与记忆空间字段 + +| Provider | 工作区行为 | 记忆空间页面中的服务配置 | 记忆空间中的实例配置 | +|---|---|---|---| +| OpenViking | 保持 Provider 全局作用域 | `endpoint`、`apiKey`、`account`、可选 `discoveryUser` | `targetUri`、`user`、`actorPeerId` | +| Honcho | 保持 Provider 全局作用域 | `endpoint`、`apiKey` | `workspace`、`userId`、`agentId` | +| Mem0 | 保持 Provider 全局作用域 | `endpoint`、`apiKey`、`mode` | `userId`、`agentId`、`rerank` | +| Hindsight | 保持 Provider 全局作用域 | `endpoint`、`apiKey` | `bankId`、`budget` | +| Holographic | 默认随工作区,可由路径覆盖 | `dataPath` | `defaultTrust`、`minTrust` | +| RetainDB | 保持 Provider 全局作用域 | `endpoint`、`apiKey` | `project`、`userId` | +| ByteRover | 默认随工作区,可由目录覆盖 | `cliPath`、`apiKey`、`defaultDirectory` | `workingDirectory` | +| Supermemory | 保持 Provider 全局作用域 | `endpoint`、`apiKey` | `containerTag`、`searchMode` | + +“记忆空间”页面(在**插件 → 可组合记忆**中点击“记忆空间”)的“记忆 Provider”只保存 Provider 服务配置,不创建记忆空间;同一范围内该 Provider 的所有记忆空间复用它。“**记忆空间 → 概览**”负责创建、编辑、启停与删除记忆空间,并只呈现 workspace、user、bank、container、target URI 等实例范围。Host 在调用适配器前合并两层配置。Secret 保存在 `/state/memory-providers.json`,权限为 `0600`;WebUI 只以掩码表示已配置的 Secret,输入新值即可替换。 + +DSH 的“工作区”模式不会统一重写所有 Provider 命名空间。Mnemon Native 自动随工作区切换;Holographic 与 ByteRover 默认使用工作区下的本地路径,但允许显式路径覆盖;其余远程 Provider 继续使用记忆空间中配置的 URI、workspace、user、bank、project 或 container,切换 DSH 工作区不会隐式改写这些身份。 + +## 手动与智能选择 + +手动模式保留原工作流:创建记忆空间、选择一个引擎、完成配置,之后仍使用同一套检索、内容、实体和沉淀入口。 + +智能模式从用户勾选的候选建立 allowlist: + +1. Host 先强制执行数据边界与必需能力; +2. 只剩一个合格 Provider 时,由规则确定性选择; +3. 有多个合格候选时,独立任务 Agent 结合路由说明、软偏好和用户策略 Prompt 判断; +4. Host 再验证结果属于合格集合,并保存选择来源、理由、置信度与候选 ID。 + +连接凭据永远不会进入 selector Prompt。`local-only` 会在模型选择前排除全部远程 Provider。Mnemon CLI 可用时,Mnemon Native 始终是本地候选。 + +## 运维边界 + +- OpenViking 使用 `content/write`、`mode: "create"`、`wait: true` 和来源/分类标签创建新的 `.md` 文件。分类分别映射至 `preferences`、`experiences`(insight/decision)、`events`(context)或 `entities`(fact/general)。只有 URI、字节数、向量索引完成状态和公开完整正文读回均匹配时才返回 stored;记忆文件的语义处理状态允许为 `skipped`。召回与浏览读取完整正文,不把摘要当作原文;这些写入不再使用会话抽取或 LLM 提炼。 +- OpenViking 服务须支持上述正文 API;可选集成测试已覆盖正式发布的 v0.4.20 后端。默认通过 Admin API 发现,需配置 `account` 与能枚举该账号用户的 key。没有 admin 权限的 user key 可在 `endpoint`、`apiKey`、`account` 之外填写 **User Key 所属用户(跳过 Admin)**(`discoveryUser`)。这会显式选择 `viking://user//memories`,通过只读 `GET /api/v1/fs/ls` 验证后同步一个空间。此模式不发送 account/user 身份请求头,由服务端从 key 解析身份;`account` 标识本地映射,不能覆盖 key 所属租户。账号和用户标识应向服务管理员获取。根目录不存在、访问被拒或响应无效时拒绝保存并保留原配置;不会只凭 health 成功保存,也不在 admin 错误后自动降级。云服务可用性与写入权限需要另外验证,确定性测试不代表真实云账号认证。参见上游[身份验证](https://github.com/volcengine/OpenViking/blob/main/docs/en/guides/04-authentication.md)与[托管服务入门](https://github.com/volcengine/OpenViking/blob/main/docs/en/getting-started/02-quickstart.md)。 +- 已有显式 `viking://user//memories` 根保持有效;旧 `viking://user/memories` 通过已配置 `user` 展开,未配置时使用经过身份验证的 system-status 用户信息。user-key 发现使用显式所属用户,拒绝不匹配的空间用户。拒绝危险路径成分以及所选用户根以外的删除。注册表格式不变;降级前用当前版本清空 `discoveryUser` 并使用具有 admin 权限的配置,或禁用服务并恢复兼容配置。远端记忆保持不变。 +- OpenViking 错误、索引未完成和超时不会返回已提交回执。远端文件可能已经存在,错误会附带请求 URI,重试前应检查该文件;不自动回退到抽取或重试。经过验证的回执包含精确文件 `id`,使 Host 能在后续本地归档失败时只删除本次新建索引。结果不明的远端写入保留待检查。升级或回退适配器均不会删除已有远端文件;降级会恢复旧写入行为。 +- WebUI 不直接调用远程服务或本地 CLI;Provider I/O 都留在 Host,统一具备取消、超时、进程输出上限和 shell-disabled 参数执行。 +- “断开”三方记忆空间只删除本地目录登记,不删除底层数据。单条记忆的“遗忘”是另一项按能力开放的操作。 +- Holographic 是对本地结构化事实语义的 TypeScript 适配,使用原子 JSON 存储,并保持独立的数据格式与生命周期实现。 +- Hindsight 使用轻量存活检查,并从 Provider 的 bank stats、实体目录与图谱响应读取真实统计、实体和关系;旧版缺少统计接口时仍可使用召回与图谱表面。 +- ByteRover 只开放聚焦的 `status`、`query` 与 `curate`;不会虚构广域知识树浏览和删除能力。 +- Supermemory 的浏览结果合并已抽取 memory entries 与仍可浏览的 ingested documents,并按 Provider ID 去重;文档未完成抽取时也不会从“内容”页消失。 +- Mnemon Pack 包含 Mnemon Native 记忆空间、运行时与档案;三方连接、凭据、本地三方 Store 与远程数据都不进入 Pack。 +- 外部产品的可用性、价格、隐私、保留策略和许可证由各自运营方决定。把私有记忆发送给远程 Provider 前应先评估这些边界。 + +来源归属与许可证边界见[第三方声明](../../../THIRD_PARTY_NOTICES.md)。 diff --git a/dsh/docs/zh-CN/guides/operations.md b/dsh/docs/zh-CN/guides/operations.md new file mode 100644 index 000000000..8822229dc --- /dev/null +++ b/dsh/docs/zh-CN/guides/operations.md @@ -0,0 +1,326 @@ +# 运维、安全与故障排查 + +**简体中文** | [English](../../en/guides/operations.md) | [文档中心](../README.md) + +## 健康检查 + +打开记忆系统的“状态”页。使用 Mnemon Native 时再检查它的二进制,其他 Provider 不需要: + +```sh +command -v mnemon +mnemon --version +``` + +Windows PowerShell: + +```powershell +Get-Command mnemon -ErrorAction SilentlyContinue +Test-Path "$env:LOCALAPPDATA\Programs\mnemon\mnemon.exe" +``` + +```text +/mnemon status +``` + +![状态页:每个记忆组件、Provider 与存储区域](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory-status.jpg) + +状态页显示 dsh-mnemon 与 Mnemon 的版本、每个记忆组件一张卡片、各个 Provider,以及当前实际目录。`mnemon status` 会打开有效 Store,上游 CLI 可能初始化数据或执行迁移,因此不是完全无副作用的只读探测。 + +如果 OpenViking 提示 `/api/v1/admin/*` access restrictions,在**插件 → 可组合记忆**的记忆空间页面填写 **User Key 所属用户(跳过 Admin)**(`discoveryUser`)、账号标识、服务地址与 user API key。这会用数据 API 验证所选记忆根;根目录被拒绝或不存在时保留原配置。此校验不证明写入权限,也不改变 key 绑定的身份。留空仍使用 admin 发现。参见 [Provider 边界与降级步骤](./memory-providers.md#运维边界)。 + +## 版本检查与更新 + +状态页的“检查版本”打开“检查与更新版本”面板: + +- **dsh-mnemon**:当前版本是所属 Profile 记录的版本(状态页显示正在运行的版本),更新查询 npm `latest`;已安装的 beta/alpha/rc 同时查询自身通道,也可升级到更高的正式版。稳定版用户不会自动进入预发布通道。 +- **Mnemon CLI**:本地版本来自 `mnemon --version`,最新版本来自官方 `@mnemon-dev/mnemon` npm 包。只有 Mnemon Native 需要它,未安装时显示为可选并给出安装命令。 + +检查只读,不会自动安装。只有发现更高版本并安全识别安装来源时才显示“更新”:Mnemon 支持官方 npm 启动器、Homebrew Cask / Formula 与 `go install`;dsh-mnemon 支持当前 DSH Profile 中的 npm 安装。`link:` / `file:` 开发版本与无法识别的手工安装只显示说明,避免覆盖源码。 + +dsh-mnemon 通过 DSH 自己的插件安装器更新,效果与 `dsh plugin add dsh-mnemon@<版本>` 相同;Profile 单独安装的可选 Strategy 也是如此。安装器使用 Profile 自己的包管理器,桌面版中即应用自带的 pnpm,因此 PATH 上不需要 pnpm;同时沿用 Profile 的安装源设置与备用源,并等待 Profile 的锁。保留新版本之前,DSH 会检查它声明支持的 DSH 版本。安装失败时,DSH 恢复 Profile 的 `package.json` 与锁文件,面板显示 pnpm 的错误行和 DSH 的日志路径,已启用的插件保持不变;安装已下载的文件可能留下,“检查版本”仍显示 Profile 记录的版本。通过 DSH 的安装器,Host 只更新自己所运行的 Profile。DSH 未提供插件安装器时,Host 在所属 Profile 中运行 PATH 上的 pnpm。既没有应用自带的 pnpm、PATH 上也没有 pnpm 时,面板会提示安装 pnpm 并重启 DSH。dsh-mnemon 0.5.21 及更早的版本只有 pnpm 这一种方式:在桌面版中从这些版本更新时,需要在插件页移除 dsh-mnemon 后重新添加一次,记忆数据会保留。 + +npm 更新要求当前启动器属于现有 npm 所报告的全局安装目录;不同 Node/npm 环境或启动器故障会显示修复指引。首次安装或迁移使用 `npm install --global @mnemon-dev/mnemon@latest`,后续使用 `mnemon update`。命令在 DSH 宿主运行,需要 Node.js 22+。修改 PATH 或 CLI 配置后,重新检查并核对面板中的可执行文件路径。 + +在 Windows 桌面宿主中,记忆操作和版本检查会直接调用已识别的官方 Mnemon npm 安装所锁定的原生程序,避免 npm 启动器额外弹出控制台窗口,并保留隐藏子进程、超时、取消和已保存的嵌入设置。界面显示的安装路径和 npm 更新归属仍指向原启动器。对于未知包布局或缺失的原生依赖,继续使用启动器回退路径及其修复诊断。 + +展开 dsh-mnemon 子包列表,可按 Source、Strategy、Provider 查看版本。主包固定的依赖随主包更新,仅当前所属 Profile 中独立安装的包支持单独更新;源码链接保持原维护方式。包更新串行执行,重新检查或重开面板仍保留待重启提示。只读连接可检查版本与复制命令;页面更新需要管理权限且开启 `writeEnabled`。 + +Go 更新还要求当前执行文件确实位于本机 Go 的安装输出位置(`GOBIN`,或 `GOPATH` 第一项的 `bin` 目录),且未配置交叉编译目标。不能仅因下载的二进制包含 Go 构建信息就认定它由 Go 管理。CLI 更新后还会核验当前执行文件已达到所检查的版本,才报告成功。 + +更新命令由 Host 固定选择:浏览器不能传入命令或参数,执行禁用 shell,并限制时间与输出。插件更新在所属 profile 中安装已检查的精确版本,确认实际安装版本后才报告成功,避免固定 beta 版本未变却提示已更新。更新完成后界面自动重新检查两个组件并刷新状态。Mnemon CLI 从下一次调用起生效;dsh-mnemon 在 DSH 重启后才加载新插件代码:停止并重新运行 `dsh web`,或完全退出桌面版(macOS 上按 `Cmd+Q`)后重新打开。在此之前 Host 仍运行之前的版本,而 DSH 会在文件变化后立即载入新的记忆系统页面;该页面会重新打开**检查版本**,显示这次更新以及需要的重启。DSH 重启之前,记忆系统每个页面的顶部都会提示已安装的版本和仍在运行的版本,无论更新来自检查版本还是 `dsh plugin`;单独更新的子包也会列出。状态页显示的是正在运行的版本,**检查版本**显示已安装的版本,不会再次提供这次更新。 + + + +## 旧会话恢复 + +支持的宿主为 DSH `0.1.7-rc.2` 与 `0.2.0-rc.2`,参见[兼容性矩阵](../reference/compatibility.md)。升级 Mnemon 后重启 Web Profile:Starter 补丁为拥有路由的 `connection` Entry 同时声明 `webRuntime` 和 `webServer`,恢复此前在“记忆系统”或其配置页返回 HTTP 405 的全部七个 RPC 通道。绕过 Starter 独立安装 Host 的自定义 Profile,也应在自己的 connection Entry 声明这两个依赖,并保留自定义组合原有的其他依赖。不修改 DSH 包源码;浏览器认证和 Mnemon grant 仍然生效。 + +另一项 `source summary requires notice form; source v0 artifact remains unchanged` 错误来自旧版 Mnemon 写入的 DSH 会话消息。新消息已移除 recall/instructions 中不合法的 summary;更新插件不会改写现有会话。修复单个受影响日志时: + +1. 停止所属 DSH 进程,单独备份完整会话存储根及全部 generation;Mnemon Pack 不包含这些会话。以 DSH 错误中的准确 `raw log` 路径为准,不扫描或改写其他会话。 +2. 安装修复后的 Mnemon,对**备份副本**执行下面的预览命令。`.jsonl.zstd` 使用 Node `22.19+` 或 `24+`;普通 `.jsonl` 也支持 Node 20。 +3. 将结果写入会话目录外的新路径,核对 `repairedMessages`、`repairedDescriptors`、`expandedToolChunkRows`、`expandedToolChunks`、SHA-256 报告和 `blockers`。预览计数表示候选修改;发现已知不支持的形状时退出码为 1,副本请求返回 `mode: "refused"` 且不生成输出。已有输出文件会被拒绝。 +4. 先在一次性的 Profile 副本中,用修复副本替换对应 `session.jsonl` 或 `session.jsonl.zstd`,保留原始备份。让 DSH 加载、续写,重启后再验证会话。副本验证通过后,才在已停止的原 Profile 中执行同样的明确替换。命令本身绝不替换输入或正在使用的会话。 + +```sh +dsh-mnemon-repair-session --input /backup/session.jsonl.zstd +dsh-mnemon-repair-session --input /backup/session.jsonl.zstd --output /backup/repaired-session.jsonl.zstd +``` + +可执行命令随 `dsh-mnemon` 安装;Profile 内安装可在该目录使用 `pnpm exec dsh-mnemon-repair-session`。源码 checkout 使用 `node bin/repair-legacy-session.mjs`。下表两种 stream 规范化本身不改 ID;同一日志也可叠加经过证明的身份恢复。副本修复仅覆盖经过契约审计的历史形状: + +| 历史形状 | 副本行为 | +|---|---| +| `dsh-mnemon` instructions summary 为 `Optional memory recall and remember reminder`;recall summary 为 `Memory View snapshot` 或 `Runtime memory snapshot` | 仅移除该 source 的 `summary` 成员,保留其他插件消息和陌生 summary。 | +| 严格符合旧版封闭字段集合且组合参数满足冻结 v3 契约的 subagent descriptor v2 | 仅将版本值改为 3,保留 provider、成对模型字段、persona、label 和 toolFilter;不添加默认值或 reasoning effort。多余字段、不成对模型和其他不支持的版本会被拒绝。 | +| 严格符合旧版结构、字符串 `id: ""` 或 `name: ""` 的 `tool-call-chunks` | 展开为原来的 raw delta 事件,保留 ID、name 是否存在、参数字符串、序号和时间戳。不丢弃 chunk,provenance 引用无需重编号。已经是 raw 的空字符串 delta 保持字节不变。 | +| 严格符合结构、自有成员为 `name: null` 的 raw 或 packed 工具 delta | 保留成员,将值改为 `""`;packed 行按原逻辑序号与时间展开。ID、参数和持久消息不变。两种值均不改变组装状态,保留成员可维持 token 计时。`normalizedToolChunkNames` 统计受影响的逻辑 delta 数。 | +| 完成的 `deepseek-official` stream 已记录唯一非空 provider ID,后续空 ID 覆盖了持久工具链身份 | 核对完整有序 chunk provenance、按 index 组装的 block、相同 name/参数和唯一 call/result 关联后,仅恢复该已记录 ID。保留首个候选之前的空占位值、消息 UUID、正文及其他插件内容;支持逐条证明的多工具,以及 text/reasoning block。不生成 ID。 | +| 没有已记录 ID、身份冲突、中断、待修 owner 对象结构未知,或 approval/dispatch/plugin/消息副本引用未闭合 | 报告 blocker 并拒绝输出。任意空 ID 历史及 owner replay state 仍需原写入方恢复。 | + +ID 检查使用原 stream 完整的 `sourceEventSeqs`,包括 packed 逻辑成员。result provenance 必须精确指向对应 call;字段缺省时,仅允许没有其他 attempt 的唯一单调用 step。显式错误引用不能使用此回退。通过序号或消息 ID 关联的额外引用会跨 step 检查;普通 compaction 序号引用与无关插件仍可原样保留。`recoveredToolIdentityChains` 统计恢复的链,`recoveredToolIdentityFields` 统计逻辑身份字段,`toolIdentityRecoveryRefusals` 用有数量上限的位置样例解释无法证明的形状。 + +工具只接受 v0、合法 UTF-8 JSON 记录和完整的普通 Zstandard 帧;原始输入、解压输入和展开输出均限制为 128 MiB。格式损坏、不完整帧、修改或身份判断路径中的歧义重复键、无法安全表示的 delta 序号或时间会在发布输出前被拒绝。仅改写匹配的 summary/version/name/identity 成员和需要展开的 packed 行,其他解压字节均保留。诊断统计每类已知 blocker 的全部次数,并最多列出十个行号/事件/字段路径样例,不输出消息正文。`migrationValidated: false` 明确表示扫描器不会修复其他损坏,也不保证任意会话都能迁移;最终仍以安装的官方 DSH 加载器和 stream 回放为准。参见 [issue #251 契约审计与验证](../../pr-assets/issue-251-legacy-repair/README.zh-CN.md)。 + +DSH 以写权限打开旧会话时,会迁移为不可变的 v3 generation。Mnemon Runtime、档案与记忆空间维持原有格式。回滚 DSH 时,在独立旧版本 Profile 中恢复升级前会话备份;不要让旧 DSH 打开 v3 generation。参见[验证与截图](../../pr-assets/issue-223-dsh-015/README.zh-CN.md)。 + +## 备份与恢复 + + + +### 推荐:插件页 ZIP + +**插件 → 可组合记忆**页面“存储”组中的“备份与迁移”针对**当前有效根**工作: + +- **导出 ZIP**:包含 Runtime、Documents 和全部 Mnemon Native Memory Spaces;三方连接、本地外部 Store 与远程数据不进入包; +- **导入 ZIP**:先预检,再合并到当前有效根; +- 包内包含 `manifest.json`、SHA-256 清单和三类数据摘要; +- 导出与导入持有组件锁,Memory Space 仍有未 checkpoint 的 WAL 时会拒绝; +- 导入检查路径、数量、压缩 / 展开大小、JSON schema、Document 哈希、registry 与 SQLite 头; +- 合并先写 staging,再替换目标组件;提交失败会恢复导入前目录。 + +当前 UI 只提供安全合并,不做“覆盖一切”: + +- Runtime 按目标与内容去重; +- 相同 Document ID + 相同内容跳过,ID 冲突且内容不同则生成新 ID; +- 相同 Memory Space ID + 相同数据库跳过,内容不同则生成新 ID。 + +导入受 `writeEnabled` 控制,只读部署会拒绝。ZIP 包含私有记忆,应加密、限制访问并验证恢复。Provider 凭据保存在 `state/memory-providers.json`(`0600`),不会进入 ZIP。已保存的凭据值也不会经管理通道返回;若要备份连接,需要按下述离线快照保护整个 `state/`。 + +![安全导入前经过校验的备份预览](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/plugin-backup-preview.jpg) + +### 恢复演练 + +1. 在“存储”中将“数据目录”设为“自定义”,填写一个隔离目录并应用。 +2. 确认状态页显示的当前实际读写根正是该目录。 +3. 在“备份与迁移”中选择备份,阅读预检摘要后执行导入。 +4. 在状态页检查 Runtime、Documents、Memory Spaces 与目录。 +5. 用一个聚焦查询验证直接检索,再阅读一份档案。 +6. 验证无误后再决定是否切换正式范围。 + +不要在没有备份时把恢复直接指向唯一生产根。 + +### 文件系统级快照 + +需要保留预留 `state` 或做离线完整快照时,可以停止所有使用该根的 DSH / Mnemon 进程后复制: + +```text +/runtime +/documents +/data +/state # 若存在;不在内置 ZIP 的三类数据组件中 +``` + +复制完成后生成文件清单或校验和,并在隔离路径演练恢复。不要在多个进程仍写入时把普通目录复制当作一致快照。 + +## 切换存储范围 + +保存 `global` / `workspace` / `custom` / `workspaces` 后,Host 先初始化新运行图,再原子切换;页面自动重新读取,但**不会迁移数据**: + +```text +旧范围 -- 保存设置 --> 新的空目录或既有目录 + +不自动复制 +不自动合并 +不自动删除 +``` + +推荐迁移流程:在旧范围导出 ZIP → 切换到新范围并确认显示目录 → 导入 ZIP → 验证。工作区模式下先确认查看工作区与会话执行工作区是否是预期目标。 + +使用 `workspaces` 时,备份整个集中目录可覆盖所有工作区,导出 Pack 则只覆盖所选工作区。移动或重命名工作区会产生新路径哈希;恢复旧数据需要操作者显式执行。 + + +现有回合和已委托的子 Agent activation 可能仍使用旧运行图。迁移或停用其数据前,应等待它们结束或取消这些任务。父回合结束本身不会释放异步子任务的委托;新创建或冷恢复的 activation 会捕获自己获准使用的 generation。 + + + +## 云端 WebUI + +DSH 0.1.7-rc.2 与 0.2.0-rc.2 是支持的 registry 安装目标。页面、每个 RPC 与每条 stream 都通过 Host 输出的启动 token URL 建立同一份、与 authority 绑定的浏览器会话。`--trusted-host` 仍只是 Host/Origin 防线,不能替代 HTTPS 或部署层访问控制。 + +1. 在反向代理或访问网关终止 HTTPS,并只向预期用户开放公网入口。把同源的 `/` 与 `/api` 流量(包括 stream)代理到 `http://127.0.0.1:3080`,同时保留外部 `Host` authority。 +2. 使用外部 authority 启动回环服务。参数应为裸 `host[:port]`,不是 URL: + + ```sh + dsh web --trusted-host memory.example.com --no-open + ``` + + 公网入口使用非默认端口时,应传入准确 authority,例如 `memory.example.com:8443`。DSH 会刻意拒绝 `--host 0.0.0.0`;请让服务保持在回环地址,只由代理或 SSH tunnel 访问。 +3. 如果浏览器还没有该公网 authority 的有效 Cookie,请使用终端里以 `dsh web: ...` 输出的启动 token URL。经过反向代理时,只把其中的回环 origin 替换成公网 HTTPS origin,保留 `/` 路径与 `?token=...` query。例如把 `http://127.0.0.1:3080/?token=...` 转为 `https://memory.example.com/?token=...`。该 URL 等同凭据,不要放入日志、Issue 或聊天。DSH 会把它交换为 HttpOnly、SameSite Cookie,再重定向到干净的 `/`;尚未过期且 authority 相同的 Cookie 可以跨 Host 重启继续使用。 +4. 打开干净的公网 URL,确认“状态”和**插件 → 可组合记忆**页面都能加载,并且整页刷新后仍保持认证。远程设置默认只读;需要管理时,先应用[显式远程管理授权](#remote-management)、重启 DSH,再验证一次有意的小范围保存。 + +HTTP 403 可能来自 Host/Origin 不匹配,或旧远程 Client 仍调用独立通道。请检查 `--trusted-host`、公网 authority 与代理路由,再升级 dsh-mnemon、重启 DSH 并刷新浏览器;远程 Mnemon 调用使用已认证 API Gateway。HTTP 401 需要恢复 Host 浏览器认证或配对。若返回远程管理需要 `remoteAccess: trusted-host`,则是另一个 Mnemon 授权检查;浏览器认证成功本身不授予管理权限。 + +### 停用完整 Starter + +既有的 `mnemon` Entry 继续作为整个 Starter 的生命周期总开关。需要停用 Mnemon 时,在 profile patch 中加入以下配置并重启 DSH;不需要逐项停用 Source 或 Strategy: + +```yaml +- id: mnemon + disabled: true +``` + +该开关会同时停用 Core/Host、三个随附 Source、两个主策略和三个可选 Strategy 增强,不会卸载包或删除记忆数据。删除该覆盖项,或把它改为 `false`,再重启 DSH,即可重新启用完整 Starter。 + + + +### 远程管理 + +对于已认证的远程客户端,`remoteAccess: trusted-host` 授予管理操作;远程读取与小范围激活不需要该授权。没有该授权时,远程页面的记忆系统与插件设置会写明只读的原因。本机浏览器与 DSH 桌面版窗口不是远程客户端,不需要该授权。仅为预期的已认证用户配置远程管理权限。需要运行更早的 DSH 时,请配套使用针对它验证过的 Mnemon 版本及其升级前的会话备份,参见[兼容性矩阵](../reference/compatibility.md)。 + +1. 打开 `~/.dsh/profiles/web/cordis.patch.yml`;如果设置了 `DSH_HOME`,则路径为 `$DSH_HOME/profiles/web/cordis.patch.yml`。如果已经有顶层 `- id: mnemon`,请直接修改该项,不要添加重复项。如果初始化文件仍以 `[]` 结尾,请用下面的完整配置行替换它;否则把该行追加到现有顶层 YAML 列表: + + ```yaml + - id: mnemon + config: + routingGuidance: true + lifecycleEnabled: true + recallMode: guided + writebackMode: guided + idleReviewMs: 30000 + tabEnabled: true + writeEnabled: true + remoteAccess: trusted-host + timeoutMs: 10000 + defaultRecallLimit: 10 + embedding: + enabled: false + endpoint: http://localhost:11434 + model: nomic-embed-text + recallQuality: + policy: strict-v1 + lowScoreThreshold: 0.25 + highScoreThreshold: 0.6 + candidateMultiplier: 3 + maxMediumResults: 4 + maxUnknownResults: 2 + ``` + + Profile patch 会替换目标行的完整 `config`,不会只深度合并一个字段。请保留已有自定义项;插件升级后用 `dsh web --dump-default-config` 对照它,避免遮蔽新增的包内默认值。 +2. 运行 `dsh web --dump-config` 检查最终配置树。确认最后的 `mnemon` 行包含 `remoteAccess: trusted-host`,并且 stderr 没有报告无法匹配 `mnemon` 目标。 +3. 使用同一条 `--trusted-host` 命令启动 DSH。每次修改 `remoteAccess` 后都要重启,因为 Mnemon 只在启动时捕获该策略。最后通过已认证远程连接验证“状态”、设置加载和一次有意的小范围保存。 + +## 安全边界 + +### 进程 + +- CLI 使用 `spawn(command, args, { shell: false })`,不拼接 shell。 +- 每次调用的 stdout + stderr 合计限制 2 MiB。Mnemon Native 在内容列表、图谱,以及运行时记忆归档前的逐字比对中会读取整个记忆空间,这些读取随 Store 增长,上限为 128 MiB。 +- 调用失败时会写明原因:只有启动失败才提示安装 Mnemon 或设置 `mnemon.cliPath`;超时、取消和输出超出上限会各自说明。 +- 每次调用受 `timeoutMs` 与 AbortSignal 控制;取消先 `SIGTERM`,1.5 秒后 `SIGKILL`。 +- 单个 Runner 内调用串行;跨 DSH 进程仍依赖 Mnemon / SQLite 并发语义。 + +### 文件 + +- Runtime、Documents 和 Pack 操作使用进程内队列或组件锁。 +- lock 默认等待 5 秒,超过 30 秒才视为 stale。 +- 写入使用临时文件、staging 与 rename。 +- Runtime revision 阻止过期压缩覆盖;Document revision 阻止移动已更新原文。 +- `sourcePaths` 不能逃出发起会话工作区,也不能指向受管 Documents 目录。 + +### Web 与模型 + +- DSH 负责远程 RPC 与 stream 的认证或配对;Mnemon 的 API Gateway 映射对管理操作另行要求 `remoteAccess: trusted-host`,本地回环客户端与 DSH 桌面版窗口(`dsh-app://app/`)使用各自的通道。 +- Provider 目录和管理响应始终脱敏;界面只显示已配置字段名,不返回已保存凭据值。 +- WebUI 依据 Host 返回的可写 settings snapshot 判断产品能力,不再根据传输位置猜测权限;设置通道不可用时会显示明确诊断,而不是空白页。 +- WebUI 不直接读取 SQLite、启动进程、调用远程 Provider 或指定任意更新命令;Provider 网络访问只发生在 Host。 +- worker 使用 persona、工具白名单与 `maxDepth: 1`。固定结果工具仅接受当前子任务的可撤销请求 ID,并按每次操作的 schema 校验结果。 +- 蒸馏和 supervised writeback worker 不能调用 `mnemon_forget`;后台审查的档案写入只开放仅创建工具,不能覆盖用户原文,也不能通过归档腾出容量。这些限制默认启用,无需增强插件。 +- 空闲审查每轮只写一层:建了项目档案后,修改工作记忆会被拒绝;修改工作记忆后,新建项目档案会被拒绝。`idleReview.runtimeMemory: false` 会完全收回它的运行时记忆工具。 +- 查询、候选、档案正文与历史记忆全部按不可信数据处理。 + +这些边界不是秘密扫描器。当前没有确定性的凭据检测;不要提交密钥、token、私钥和原始敏感日志。 + +### 安全问题报告 + +按 [SECURITY.md](../../../SECURITY.md) 私下报告漏洞,不要创建公开 issue。数据丢失、路径穿越、锁 / revision 绕过、子 Agent 隔离破坏和 WebUI 记忆内容注入都在范围内。 + +## 故障排查 + +`mnemon.cliPath` 接受显式路径,也接受按 Host 的 PATH 查找的命令名。DSH 运行时,若二进制安装或恢复到既有搜索目录,点击记忆系统顶栏的**刷新**即可更新可用状态,无需重启;Host 进程环境变量的变化仍需重启。状态与版本检查解析同一个配置命令。 + +| 现象 | 检查与处理 | +|---|---| +| 插件详情中 `mnemon-bundle` 容器行(0.5.20 起名为 `dsh-mnemon/bundle`,此前为 `cordis:group`)显示“已关闭”,点击提示“找不到该插件” | DSH `0.1.7-rc.2` 的已知容器展示问题(`0.2.0-rc.1` 同样显示为已关闭)。先检查“记忆系统 → 状态”和实际组件;正常时可继续使用。整套组合用顶层 bundle 或 `mnemon` 核心组件开关,不要重置数据或删除分组。参见[原因、处理步骤与修复边界](../reference/compatibility.md#dsh-017-bundle-组件列表)和[上游 #649](https://github.com/dsh-external/issues/issues/649)。 | +| Windows 切换会话时终端窗口闪现 | 更新 Starter,或独立安装的 Runtime Source,然后重启 DSH Host。Runtime 的 Git 分支检测会隐藏控制台窗口;Git 失败、超时或 HEAD 分离时仍回退到不按分支筛选的 Runtime 视图。 | +| Mnemon 不可用 | macOS/Linux 运行 `command -v mnemon`、`mnemon --version`;Windows PowerShell 运行 `Get-Command mnemon`、`Test-Path "$env:LOCALAPPDATA\Programs\mnemon\mnemon.exe"`。设置 `MNEMON_CLI_PATH` 或 `mnemon.cliPath` 后重启 | +| Electron 桌面 Host 无法运行 npm CLI 脚本 | 经过验证的 npm 启动器仅在子进程中设置 `ELECTRON_RUN_AS_NODE=1`。如果桌面壳关闭了 [Electron `runAsNode` fuse](https://www.electronjs.org/docs/latest/tutorial/fuses#runasnode),该变量会被忽略;请将 `mnemon.cliPath` 指向官方原生二进制(Windows 为 `mnemon.exe`)。npm 自动更新仍需要 Host 能够运行 JavaScript 启动器 | +| Headless Agent 没有 Mnemon 工具 | 插件按 profile 独立安装;运行 `dsh plugin --profile headless add dsh-mnemon`,Web profile 的安装不会自动带入 | +| 找不到“记忆系统”入口 | 检查 `tabEnabled=true`;`displayMode=sidebar` 使用侧边栏,`displayMode=builtin` 使用已打开会话的标签页。本地 link 先 `pnpm run build` 再重启 profile | +| 旧的 `buildin` 偏好打开了会话标签页 | 它会保存为 `builtin`;如需独立入口,请在**界面**中选择**侧边栏**。记忆范围与已存数据不变 | +| 状态正常但召回为空 | 检查 active 记忆空间、存储范围、查看目录、会话实际目录和查询是否足够聚焦 | +| 顶部提示目录未对齐 | 工作台正在查看另一个工作区;确认是否为预期范围。工作台任务在查看工作区执行,对话工具仍使用所属会话范围 | +| 设置看起来没有变化 | 开关与选择器立即生效,需要输入的值要点击**应用**;写入失败时控件旁会显示原因并恢复为已保存的值 | +| 自定义目录被拒绝 | 使用绝对路径、`~` 或 `~/...` | +| `memoryBodyId is required...` | active 数量不是恰好 1;显式选择目标 | +| `memory space is not active for reading` | 在概览激活目标;写入 inactive 可以,读取不行 | +| Provider 错误 | 审查需要受 guard 保护的本地子 Agent;默认有界 `spawn`,可选择 `fork`。DSH/Teams 0.1.7-rc.2 可选择 `idleReview.agentTeams: scoped`,默认仍为 `pause`;重试前核对部分写入回执 | +| 本地模型(例如 Ollama 上的 Qwen3.x)下后台审查报 `no user query found in messages` | 工具结果填满模型的上下文窗口后,Ollama 截断时丢掉了子代理的任务提示。0.5.22 起,Mnemon 会给被拒的子代理步骤追加一条用户消息并重试([#327](https://github.com/omdsh-dev/dsh-mnemon/issues/327)),请更新 dsh-mnemon。调大 Ollama 的上下文长度(`OLLAMA_CONTEXT_LENGTH` 或模型的 `num_ctx`)也能避免截断。审查可能在失败前已创建项目档案,请核对部分写入回执 | +| Runtime replace 超容量 | 缩短 replacement 或先显式整理;自动维护只处理 add 溢出 | +| Document source path 被拒绝 | 路径必须在会话工作区内,且不能引用受管 Documents 目录 | +| CLI timeout | 增大 `timeoutMs`;大 Store 的状态与图谱可能超过 10 秒 | +| lock timeout | 检查其他写进程,不要删除仍属于活跃进程的 lock | +| ZIP 导出提示 WAL busy | 等待 Memory Space 写入完成并重试;不要绕过未 checkpoint WAL 检查 | +| ZIP 导入 checksum / schema 失败 | 备份损坏或格式不兼容;保留当前根,不要手工解压覆盖 | +| 更新按钮不出现 | 当前已是最新、远程检查失败,或安装来源是 link / 手工模式;按面板提示沿原方式更新 | +| 已认证远程页面能读取或激活记忆空间,但不能保存设置或执行其他写入 | 默认管理限制;确需远程管理时,保留当前配置、在本地设置 `remoteAccess: trusted-host` 并重启 DSH | +| DSH 桌面版中运行时记忆、记忆空间与插件设置均为只读,而 Agent 工具仍能写入 | dsh-mnemon 0.5.18 及更早版本把桌面窗口当成远程页面([#310](https://github.com/omdsh-dev/dsh-mnemon/issues/310));在桌面版插件页更新到 0.5.19 或更新版本,无需修改 `remoteAccess` | +| DSH 0.2 安装或启动时提示 dsh-mnemon 不兼容 | 安装到的是 0.5.19 之前的版本;更新 dsh-mnemon,新版本发布 24 小时内按[安装与启动](./installation.md#常见问题)安装带版本号的包 | +| DSH 运行期间更新 dsh-mnemon 后,启用组件提示 `ERR_PACKAGE_PATH_NOT_EXPORTED`(`Package subpath './starter'` 或 `'./bundle'`) | 正在运行的进程仍按旧版本的包信息加载;完全退出并重新启动 DSH(桌面版按 `Cmd+Q`)。使用 0.5.18 或 0.5.19 时不要关闭 `dsh-mnemon/starter`,见[安装与启动](./installation.md#更新后启用时提示-err_package_path_not_exported) | +| `mnemon-bundle (cordis:group): pending (waiting for service: mnemonStarterReady)`,或桌面版启动失败并显示插件恢复页 | 0.5.18 或 0.5.19 的 `dsh-mnemon/starter` 被关闭;在**插件 → 可组合记忆**中打开它,或更新到 0.5.20(没有单独的就绪开关)。桌面版在恢复页卸载后重新添加 dsh-mnemon,见[安装与启动](./installation.md#dsh-提示waiting-for-service-mnemonstarterready) | +| 更新后提示 `mnemon-bundle (dsh-mnemon/bundle): pending (waiting for service: mnemonStarterReady)` | 更新到 0.5.20 后没有重启,正在运行的 DSH 仍保留旧的组件组;完全退出并重新启动 DSH,见[安装与启动](./installation.md#更新后启用时提示-err_package_path_not_exported) | +| DSH 重启或 authority 改变后 Mnemon RPC 返回 401 | 打开 `dsh web` 输出的启动 URL,让一次性 token 建立新的、与 authority 绑定的浏览器 Cookie | + +## 已知限制 + +### 功能只读不等于磁盘只读 + +`writeEnabled=false` 禁用语义 mutation 与 Pack 导入,但启动可能初始化 / 修复 Runtime 投影,Document 搜索更新 `lastAccessedAt`,Mnemon 读命令也可能迁移数据库。 + +### Documents 的共享范围 + +`global` 与 `custom` 可能让多个工作区共享同一 Document index;记录没有独立 workspace ownership 字段。`sourcePaths` 只在写入时相对发起会话 cwd 校验。 + +### 跨系统事务 + +“先冷索引、后移动”保护 active 原文,但不是跨 Mnemon SQLite 与文件系统的可回滚分布式事务。索引后发生 revision 冲突时可能保留重复引用;系统选择保留数据。 + +### 后台水位 + +活动评分、最近 checkpoint 与重试状态未持久化;Host 重启会清空未处理活动。失败退避、熔断和人工重试入口尚未实现。 + +### 版本与国际化 + +支持 DSH 0.1.7-rc.2 与 0.2.0-rc.2,参见[兼容性矩阵](../reference/compatibility.md)。Web 界面为中英文双语,但命令、工具卡、兼容元数据和部分错误仍未完全国际化。 + +## 文档归档恢复 + +文档归档不再要求子代理为 remember/recall 回执编号。旧版本失败后若留下冷索引而文档仍为 active,重试可复用精确路径与当前内容哈希一致的索引;文档更新后需要匹配新修订的索引。异步提取或不支持安全删除的 Provider 会在写入前被拒绝。清理失败会报告目标空间和本次新建的 id;确认结果前应保留已有数据。 + +## 运行时归档恢复 + +容量归档要求已激活的记忆空间,其 Provider 必须支持精确写入和安全删除。Hindsight 等异步提取目标会在任何归档写入前被排除。如果没有合适的目标,请激活支持这些能力的记忆空间,或增大 `runtimeMemory.memoryLimitBytes`;被拒绝的 mutation 不会改变现有热记忆。直接向 Provider 写入仍保持原有的异步行为。 + +对话归档使用所选 Source 的写入范围:回合中激活的已知空间,以及当前 View 创建的空间,都可作为归档目标。在 View 固定之后由其他操作创建的空间,需要新回合才能使用。空范围始终不授权任何空间。目标错误会区分目录为空、View 范围为空或排除了当前空间、Provider 能力不支持,并报告目录、授权和可写数量。失败时待新增内容尚未保存;修正所报告的原因后,请在新回合重试同一份工具输入。已有热记忆会保留。 + +归档回执或本地提交失败时,Host 仅尝试删除回执能证明由本次操作新建的条目,保留跳过或复用的既有条目。本地提交报错后,如果 Runtime 修订已变更或无法读取,则提交结果不确定:保留归档条目,避免丢失已提交的记忆。清理失败会标出剩余目标空间和条目 id。Provider 请求失败且没有返回回执时,远端结果可能不确定,因此这不是分布式事务;重试前应检查对应 Provider。 diff --git a/dsh/docs/zh-CN/guides/ui-guide.md b/dsh/docs/zh-CN/guides/ui-guide.md new file mode 100644 index 000000000..5736accbe --- /dev/null +++ b/dsh/docs/zh-CN/guides/ui-guide.md @@ -0,0 +1,292 @@ +# 界面指南 + +**简体中文** | [English](../../en/guides/ui-guide.md) | [文档中心](../README.md) + +dsh-mnemon 出现在三个地方:对话里能看到每一轮读到和写入了哪些记忆,也能把回复存入记忆;**记忆系统**用来浏览、检索和编辑已保存的内容;**插件**中的“可组合记忆”页面负责组合记忆与修改设置。 + +| 位置 | 在这里做什么 | 如何打开 | +|---|---|---| +| [在对话中](#在对话中) | 查看一条回复读到和写入的记忆,一键打开它;把回复存入记忆 | 每条已完成回复的下方 | +| [记忆系统](#记忆系统) | 查看状态;浏览、检索、编辑运行时记忆、项目档案与记忆空间 | 侧栏中的**记忆系统**,或会话标签页 | +| [在插件页中](#在插件页中) | 选择策略与增强;设置组件、存储与界面 | **插件 → 可组合记忆**,或记忆系统顶栏的齿轮 | + +本页的录屏与截图来自 [v0.5.19 图集](../../assets/webui-v0.5.19/README.md):真实 WebUI 的浅色界面,预置的虚构项目 Lumen,回答、写入与回执都由真实的 DeepSeek 模型生成;等待模型的片段以 4 倍或 6 倍速播放,画面右下角会标出。 + +有两条规则处处适用:**开关与选择器改动后立即生效**,写入失败时控件恢复为已保存的值,并在旁边说明原因;**需要输入的值等待所在分组的“应用”**,有改动时才出现,并说明应用后会发生什么。 + +## 在对话中 + +![提问后展开回合记忆栏,点击档案检索到的档案,直接在项目档案中打开它](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/recall.gif) + +### 回合记忆栏 + +用到记忆的回复末尾会有一行**本回合记忆**,前面是记忆系统的脑形标识,后面是这一轮的召回、档案检索与写入次数。展开后每个工具一行:左边用平常的话写出工具做了什么,例如**项目档案检索**、**记忆空间召回 ×2**,悬停可以看到工具本身的名称;右边列出它读到或写入的内容。 + +![回答同时用到工作记忆、项目档案与记忆空间,回合记忆栏展开后列出读到的两份档案和一条记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/chat-recall.jpg) + +- 点击工具名称,打开记忆系统中对应的页面。 +- 点击一条内容,直接在它所在的位置打开:档案在**项目档案**中选中;记忆空间中的记忆在**检索**页作为查询立即执行;运行时记忆的条目在**运行时记忆**中高亮并滚动到它。 +- 每个工具最多列出三条,其余以“另 N 条”汇总;只有 id、没有文字的内容不列出。窄屏上内容排在工具名称下方。 +- 没有记忆活动的回合不显示这一行。 + +纠正某个事实时,Agent 会替换对应的运行时记忆,回合记忆栏记下这次写入;点击这条写入即可看到替换后的条目。[观看纠正录屏](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/correct.mp4)。 + +| 纠正写入运行时记忆 | 点击写入,运行时记忆中高亮该条目 | +|---|---| +| ![“记住:本周结账页目标改为 p75 LCP 低于 2.2 秒”,回合记忆栏显示一次写入](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/chat-correction.jpg) | ![运行时记忆页高亮被替换的“本周重点”条目](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory-runtime.jpg) | + +临时进度、猜测和密钥不会被写入。 + +### 存入记忆 + +![在回复下点击脑形图标,编辑候选内容,交给任务 Agent,收到回执后在记忆空间中查看新记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/save.gif) + +每条已完成回复的操作栏里都有脑形的**存入记忆**,与记忆系统使用同一个标识(旁边数据库形状的图标是 DSH 的用量,与记忆无关)。点击后读取这条回复并打开对话框: + +- 标题是**存入记忆**,下方一行说明任务 Agent 会做什么;**任务 Agent 就绪**标签表示可以提交。 +- **候选内容(可编辑)**预先填入整条回复,通常只保留要记住的那一句。**取消**不会改动任何数据。 +- **交给任务 Agent** 后,一个独立的任务 Agent 判断是否值得保存、选择合适的记忆空间、去重、提炼并写入,不占用当前对话。 +- 完成后显示回执:结果标签(**已存入**、**已更新**、**已提交,等待 Provider 确认**、**已跳过**、**部分完成**、**未完成**或**失败**),任务 Agent 用候选内容的语言写出存到了哪个空间,以及**在记忆空间中查看**。此时**取消**变为**关闭**;只有修改了候选内容才能再次提交。 + +| 编辑候选内容 | 任务 Agent 的回执 | +|---|---| +| ![存入记忆对话框:任务 Agent 就绪,候选内容已改为一句事实](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/chat-save.jpg) | ![回执:已存入“Lumen 项目”,并提供在记忆空间中查看](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/chat-save-receipt.jpg) | + +记忆空间页顶部的**存入记忆**打开的是同一个对话框。回合记忆栏与存入记忆按钮默认开启,可以在[界面](#界面)中关闭。以会话标签页方式打开时,这些入口会打开来源会话中的记忆系统标签;分屏里有多个候选标签时,请手动打开目标标签。 + +## 记忆系统 + +![依次查看状态、运行时记忆、项目档案、记忆空间概览与图谱,再用 Agent 查询得到带引用的回答](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory.gif) + +记忆系统有四个页面,名称来自提供它们的组件:**状态**、**运行时记忆**、**项目档案**和**记忆空间**;安装的 Source 也会以同样方式加入自己的页面。顶栏显示连接状态与正在组合记忆的主策略(例如“已连接 · 分层策略”)、存储范围、**刷新**与打开插件页的齿轮。**刷新**重新读取当前页面,所以各页面不再单独放置刷新或同步按钮。 + +记忆系统默认位于侧栏,回来时停留在原来的页面。从对话里的回合记忆栏打开时,页面会定位到那一条内容;在顶栏切换标签页则打开干净的页面。在**界面 → 记忆系统入口**中选择**会话标签页**,即可改为在会话内打开,页面完全相同。**返回会话**或 Esc 回到对话。 + +### 状态 + +![状态页:记忆引擎、每个记忆组件一张卡片、正在运行的 Provider 与存储域](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory-status.jpg) + +第一行每个组件一张卡片:先是记忆引擎及其版本,然后是各个 Source 保存的内容。下方的**记忆 Provider** 只列出正在运行的 Provider 及其记忆空间的连接状态,其余汇总为一行“另有 N 个 Provider 未启用”,旁边的**前往配置**打开插件页;再往下是存储域。各区域独立加载,一处失败不会遮住其他内容。 + +只有记忆需要处理时,页面上方才会出现带**前往配置**的提示: + +- **记忆未生效**:没有主策略在运行,对话会在没有记忆的情况下继续。 +- **所选主策略没有运行**:暂时由另一个策略组合记忆。 +- **最近的组件变更没有生效**:继续使用之前的组合。 + +**检查版本**只读取信息:它会说明 Mnemon CLI 是缺失、最新还是可更新,并给出可复制的 npm 命令;还会列出每个子包的已安装、已发布与锁定版本。dsh-mnemon 有新版本时,**更新**会通过 DSH 自己的插件安装器安装,桌面版同样可用。更新包后请重启 DSH;重启之前,每个页面顶部都会提示已安装的版本。 + +### 运行时记忆 + +运行时记忆是每一轮都会注入的紧凑上下文。**用户画像**(`USER.md`)记录你是谁、你的偏好与协作规则;**工作记忆**(`MEMORY.md`)记录项目事实、环境、决策与经验。每张卡片显示已用大小与上限。从回合记忆栏打开时,本回合写入的条目带有高亮边框,见[上文的纠正示例](#回合记忆栏)。 + +- 条目按时间倒序排列,可以按目标与文本筛选;编辑旧条目不会改变它的位置。**添加记忆**打开添加运行时记忆的对话框。 +- 条目应简短、相互独立、会被反复用到;临时进度和原始日志不应放在这里。 +- 工作记忆条目可以限定 git 分支,只有会话工作区处于这些分支时才会注入;页面和磁盘上的文件不受影响。 +- 工作记忆达到上限时,宿主会先把最旧的条目归档到记忆空间,再添加新条目。 + +
+编辑、容量与分支细节 + +“编辑”和“移除”在所属目标中按完整内容匹配条目,因此可以只修改简短的 `X`,而不影响 `EGO_LINUX_CHROME`。同一目标中完全相同的条目无法区分,会被拒绝且不改动数据。 + +容量归档移动的是原始条目。某一批路由失败或返回无效提案时,这一批会写入符合条件的默认记忆空间或第一个符合条件的空间,摘要中会记录原因。当前回合创建或激活的空间,在激活并支持写入后也可以接收归档。没有可用目标时,已有条目保持不变,待添加的条目也不会保存;排除原因后重新提交即可。 + +Agent 写入用户画像时,`branches` 必须省略或为空。对工作记忆执行替换时,`branches: []` 会清除分支限定,省略则保持原样。面向模型的快照会为每个条目附上重要性与天数,只作用于新的一轮,不改变已保存的文本。参见[投影格式与上限](../reference/storage-model.md#事实源和投影)。 + +
+ +### 项目档案 + +![项目档案:从回合记忆栏打开,目录中选中“架构决策:事件摄取改用队列”](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory-documents.jpg) + +项目档案保存完整的叙事:设计、调查、流程与交接。请先选择 DSH 工作区;即使使用全局存储,档案也属于某个工作区。 + +- 在**活跃档案**与**归档**之间切换;阅读器保留标题、检索描述、来源、修订、哈希、大小与完整 Markdown。 +- 检索会匹配标题、描述与正文;列表按创建时间倒序,更新旧档案不会把它移到最前。 +- 项目中的源文件永远不会被修改,档案是受管理的副本。 +- 归档会先在记忆空间中为档案建立索引,再移动原文件,因此冷数据仍然可以检索到;目标空间必须已激活,并支持精确写入与安全遗忘。 + +
+归档与后台审查细节 + +显式归档或容量维护时,一个没有记忆工具的独立任务 Agent 会提出摘要和一个符合条件的记忆空间。宿主校验后补充冷存储路径与内容哈希,写入索引并记录来源,然后才移动原文件。提案无效时不改动任何数据。移动失败时,宿主只删除刚刚创建的索引;已有且经过验证的索引会被复用,不会被删除。清理失败或 Provider 结果不确定时,请先检查报告中的目标再重试。 + +后台审查不会修改已有档案:它先检索,跳过已覆盖的内容,只在有大量新知识时另建补充档案;容量不足时直接跳过。它复用已完成对话中已有的证据,不能重新打开文件。 + +
+ +### 记忆空间 + +![记忆空间概览:目录中两个已激活、一个未激活的空间](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory-spaces.jpg) + +记忆空间是某个 Provider 上一个具名的长期证据范围。**概览**以卡片列出各个空间:卡片上的开关决定 dsh-mnemon 是否读取它,标签写明所用的 Provider(例如 Mnemon Native);点击卡片只重连这一个空间。右上角是**存入记忆**与**新空间的 Provider**,概览旁边是**检索**、**内容**与**实体**。 + +![已激活空间的图谱,选中了 ClickHouse 实体](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory-graph.jpg) + +卡片下方的图谱合并了各个已激活空间能提供的内容:记忆、实体以及它们之间的连接。选中空间、实体或记忆,右侧会显示它的上下文,图中是 ClickHouse 实体与**围绕它检索**。布局、拖动与重置只改变显示方式。底部统计空间、记忆与实体的数量,以及当前绘制的元素数。 + +每个空间都会声明其 Provider 真正能提供什么:Mnemon Native 提供带类型的关系;Hindsight 与 Holographic 提供各自的图谱;没有关系边的 Provider 只贡献内容;ByteRover 这类仅支持查询的 Provider 要等你提问。不支持的能力不会被凭空补出来。 + +- **创建记忆空间**时总是由你选择 Provider,就绪的 Provider 排在前面;未启用的 Provider 可在[记忆空间页面](#memory-spaces-page)中启用。 +- **新空间的 Provider** 只影响任务 Agent 需要新建空间的时候:**固定 Provider** 让新空间总是建在你指定的 Provider 上;**智能选择规则**先由宿主执行数据边界与能力要求,再由任务 Agent 在合格候选中选择。已有空间仍按名称与说明路由。 +- **整理名称与说明**会对选中的每个已激活空间取样,由独立任务 Agent 建议标题与说明;各空间独立运行,失败只显示在对应卡片上。 + +#### 检索 + +| 直接检索 | Agent 查询 | +|---|---| +| ![直接检索:已激活空间返回原文,带分数与来源](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory-recall.jpg) | ![Agent 查询:综合四条记忆作答,引用按内容列出](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory-agent.jpg) | + +**直接检索**不经过 Agent,直接返回各个已激活空间的原始证据,保留各 Provider 自己的分数、空间与分类。**Agent 查询**先做同样的检索,再把证据交给只读的任务 Agent 作答;回答用证据的内容说话,下方的**引用**按内容列出它用到的记忆,不显示 id。各 Provider 并行返回,一处失败不会遮住其他结果。**查看关联**与**忘记**只在 Provider 支持时出现。带着查询打开检索页时(来自回合记忆栏、**围绕它检索**或**查看关联**),查询会立即执行。具体的问题通常比零散的关键词更可靠。 + +#### 内容与实体 + +| 内容 | 实体 | +|---|---| +| ![内容:刚存入的记忆出现在列表中](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory-content.jpg) | ![实体:选中 ClickHouse,列出提到它的记忆](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory-entities.jpg) | + +**内容**在 Provider 可以枚举时列出记忆,查询型 Provider 需要输入关键词后才显示内容。**实体**只使用真正的实体索引,目前是 Mnemon Native、Hindsight 与 Holographic。每个实体的计数,是各激活空间中带有该实体的记忆数,列表包含全部实体。选中一个实体,会立即按重要性列出正是这些记忆。其下的**查找相关记忆**会在不带该实体的记忆中召回与它相关的那些。召回较慢,因此只在需要时运行:展开后,在本次浏览器会话中切换到其他实体时保持展开,直到收起为止。输入的名称没有任何记忆带有时,相关记忆会自动显示。 + +#### 存入记忆 + +![记忆空间页的存入记忆对话框,与对话中的完全相同](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory-remember.jpg) + +这里的**存入记忆**与对话中的是同一个对话框:写下候选内容,交给任务 Agent,收到回执后可以在**内容**中查看。只有确实需要限定目标、分类或重要性时,才展开**高级选项**。 + +### 窄屏 + +| 对话 | 记忆空间 | 配置 | +|---|---|---| +| ![390 像素宽的对话:回合记忆栏的内容排在工具下方](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/narrow-chat.jpg) | ![390 像素宽的记忆空间页面](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/narrow-spaces.jpg) | ![390 像素宽的记忆组合面板](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/narrow-plugin.jpg) | + +在 390 像素宽度下,侧栏变为 DSH 的图标栏,页面内容纵向排列,较长的名称会被截断。这些截图不代表完整的手机支持,参见[已知限制](../reference/compatibility.md)。 + +## 在插件页中 + +![打开插件中的可组合记忆,切换主策略菜单,查看分层策略与记忆空间的组件页,再滚动到存储与界面](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/plugins.gif) + +![插件中的可组合记忆页面:标题、记忆组合与组件行](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/plugin-composition.jpg) + +DSH 0.1.7 把插件配置放在“插件”中该插件自己的页面里。“可组合记忆”页面依次是**记忆组合**、**存储**与**界面**,下面是 DSH 列出的 Starter 所含组件;顶部的**打开记忆系统**可以返回工作区。每个组件自己的设置都在它的组件页中。 + +| 位置 | 包含 | 生效方式 | +|---|---|---| +| 记忆组合 | 主策略;每个记忆来源与增强各一个开关 | 立即生效,作用于之后的回合 | +| 存储 | 存储范围、数据目录、备份与迁移 | 范围与目录点击其**应用**;导入、导出 ZIP 立即执行 | +| 界面 | 记忆系统入口、回合记忆栏、存入记忆按钮 | 立即生效 | +| 运行时记忆页面 | 用户画像范围 | 立即生效 | +| 记忆空间页面 | 记忆 Provider 与 Mnemon Native 嵌入 | 开关立即生效;连接参数点击**应用** | +| 分层策略页面 | 任务 Agent 模型与空闲审查 | 选择立即生效;审查参数点击**应用** | + +在其他窗口保存的改动,或直接编辑 Profile 的结果,无需刷新即可显示。 + +### 记忆组合 + +![主策略选择器:分层策略与通用策略](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/plugin-strategy-menu.jpg) + +- **主策略**是一个选择器,列出每个已安装的主策略及其说明。**分层策略**让运行时记忆常驻,按需读取项目档案与记忆空间,并执行后台维护;**通用策略**在同一份预算内提供全部可用来源,由模型决定如何使用,不做自动审查与容量维护。 +- 下面是**记忆来源**与**增强**,每个组件一行,显示状态、开关,有自己设置的组件还有齿轮。标签写明这一行依赖的组件或依赖它的组件,点击即可打开对应页面。 +- 只有记忆没有按你的选择组合时,才会出现一行问题说明,并给出能解决它的那个开关。 + +每个开关都作用于之后的回合,已经开始的回合保留原有的 View。打开一个组件时,它依赖的组件会一起打开,不能与它共存的组件会被关闭;关闭一个组件时,依赖它的组件也会关闭;选择一个主策略时,其他主策略会被关闭。最后一个运行中的来源不能关闭。开关影响到其他组件或改变了组合方式时,DSH 会弹出提示,并提供**撤销**。 + +### 组件页 + +![分层策略页面及其后台任务](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/plugin-layered.jpg) + +行上的齿轮、**主策略**旁边的齿轮,以及任何位置出现的组件名称,都会打开该组件的页面。页面显示组件的状态与开关、它是随附组件还是来自已安装的包、它依赖什么、谁依赖它、不能与谁共存、声明的选项以及它自己的设置;关联组件在原处打开,可以**返回**。选项会按宿主的限制校验,有改动时才出现**应用**,**恢复默认**可以还原单个选项。 + + + +![记忆空间页面:Mnemon Native 与各第三方 Provider](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/plugin-spaces.jpg) + +- **运行时记忆页面**:**用户画像范围**。“全局共享”把全局的 `USER.md` 与工作区或自定义目录中的 `MEMORY.md` 组合使用,不移动任何一方。 +- **记忆空间页面**:**记忆 Provider**。Mnemon Native 排在第一位,展开后只包含它的嵌入设置;每个第三方 Provider 都有自己的开关且默认关闭,打开后才显示服务地址、密钥等字段。“全局”或“工作区”标签表示实际作用范围。OpenViking 的 **User Key 所属用户(跳过 Admin)** 用于没有 Admin 权限的密钥,参见[其限制](./memory-providers.md#运维边界)。 +- **分层策略页面**:**后台任务**。**任务 Agent 模型**默认跟随 DSH 新会话的默认链路,也可以**指定模型**,选择一对完整的 Provider 与模型;它作用于存入记忆、Agent 查询、整理名称与说明、新空间的智能选择与档案归档,不会改变主对话的模型。**空闲审查**、**写入运行时记忆**、审查方式与 Agent Teams 兼容模式立即生效,**审查参数**点击**应用**后生效。关闭**写入运行时记忆**后,审查只创建项目档案。审查只在分层策略组合的回合之后运行,参见[配置](../reference/configuration.md#provider-要求)。 + +
+Mnemon Native 嵌入 + +展开 Mnemon Native 卡片。**由 DSH 管理嵌入配置**打开后,已保存的服务地址、模型、协议和可选密钥会成为 Mnemon 子进程的唯一配置,包括不读取 shell 启动文件的桌面端启动;这些字段随后显示,并通过**应用**一起生效。自动协议选择把以 `/v1` 结尾的地址视为 OpenAI 兼容,其他地址需要明确选择协议。地址中不能包含凭据、查询参数或片段。记忆与查询文本会发送到所配置的服务。**测试状态**检查的是已保存的值,而不是未保存的草稿。开启前请阅读[嵌入配置](../reference/configuration.md)。 + +
+ +### 存储 + +![存储:范围、默认数据目录、备份,以及界面设置](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/plugin-storage.jpg) + +**存储**先列出把数据保存在这个目录中的组件,点击名称即可打开对应的组件页。 + +- **存储范围**:**全局**让所有工作区共用一个目录;**工作区**把每个工作区的记忆放在它自己的 `.mnemon` 中;**集中存储 · 按工作区隔离**在同一个根目录下为每个工作区建立 `workspaces/<工作区路径哈希>/`。 +- **数据目录**:在**默认**与**自定义**之间选择,这一行只显示记忆正在使用的一个目录。默认目录是 `MNEMON_DATA_DIR` 或 `~/.mnemon`;自定义时填写绝对路径或以 `~/` 开头的路径,集中存储下它是根目录。输入框留空只表示尚未填写,回到默认目录请选择**默认**。 +- 范围与目录一起点击**应用**,应用前会说明后果:记忆改为读写新位置,已有数据不会被移动、合并或删除。 + +![备份与迁移:导出的 ZIP 在安全导入前的预览](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/plugin-backup-preview.jpg) + +**备份与迁移**把当前数据目录中“存储”所列组件的数据导出为 ZIP,不包含第三方 Provider 的数据与凭据。导入时先显示校验后的内容预览,**安全导入**把它合并到当前目录。参见[备份与恢复](./operations.md#备份与恢复)。 + +### 界面 + +- **记忆系统入口**:**侧边栏**(默认)或**会话标签页**。 +- **回合记忆栏**与**存入记忆按钮**分别开关对话中的两个控件。 + +### DSH 组件列表 + +![DSH 组件列表:每个组件以自己声明的名称显示](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/plugin-rows.jpg) + +配置下方,DSH 以组件声明的名称和说明列出 Starter 所含的组件。`mnemon-bundle` 是 Starter 的分组条目(0.5.20 起显示为 `dsh-mnemon/bundle`;上图来自 0.5.19,当时名为 `cordis:group`,其上方的 `dsh-mnemon/starter` 是当时单独的就绪条目),它显示“已关闭”不影响任何功能。点击组件名称会打开 DSH 为它提供的页面,其中是与记忆组合面板相同的组件页。 + +![DSH 为分层策略提供的页面](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/plugin-row-page.jpg) + +## 查看的工作区与执行的工作区 + +| 概念 | 由谁选择 | 影响 | +|---|---|---| +| **查看的工作区** | 记忆系统顶栏 | 页面显示和编辑哪个 `/.mnemon` | +| **执行的工作区** | 当前对话的工作目录 | 对话工具、命令与生命周期钩子使用哪个根目录 | + +你可以在项目 A 的对话中查看项目 B。对话仍然作用于 A;从记忆系统发起的存入记忆、Agent 查询、整理名称与说明与档案归档,会以限定于 B 的任务 Agent 运行。远程 Provider 的命名空间不会随 DSH 工作区变化。全局与自定义存储只对应一个根目录,无需对齐。 + + + +## 主题与皮肤覆盖 + +皮肤作者可以先阅读[皮肤开发与 Mnemon 集成](../development/skin-integration.md),其中有示例、从生成类名迁移的方法以及在真实 WebUI 中的验证步骤。本节定义受支持的契约。 + +侧栏与会话标签页使用同一个工作区根元素:`[data-dsh-plugin="dsh-mnemon"][data-dsh-part="mnemon-view"]`。请直接在该元素上设置以下自定义属性;设置在祖先元素上的值会被工作区默认值覆盖。生成的 CSS module 类名不是公开选择器。 + +| 属性 | 可接受的值与用途 | 默认值 | +|---|---|---| +| `--mn-bg` | 基础表面的 CSS 颜色 | `var(--dsw-alias-bg-base)` | +| `--mn-backdrop` | 默认分层表面中位于基础色之后的 CSS 颜色 | `var(--dsw-alias-bg-overlay, var(--mn-bg))` | +| `--mn-surface` | 工作区、顶栏与画布使用的 CSS `background` 值 | 基础渐变叠加在背景渐变之上,并以基础色兜底 | + +默认表面为 `linear-gradient(var(--mn-bg), var(--mn-bg)), linear-gradient(var(--mn-backdrop), var(--mn-backdrop)) var(--mn-bg)`。当皮肤把宿主基础色设为透明时,这层叠加保证文字依然清晰;官方主题保持不透明。皮肤可以整体替换这一组合,例如: + +```css +[data-dsh-plugin="dsh-mnemon"][data-dsh-part="mnemon-view"] { + --mn-bg: rgb(232 241 249 / 74%); + --mn-backdrop: var(--mn-bg); + --mn-surface: var(--mn-bg); +} +``` + +请在未分层(unlayered)的样式表中使用这对选择器。它的优先级 `(0,2,0)` 高于默认声明的 `(0,1,0)`,无论皮肤样式先于还是晚于 Mnemon 加载都能生效。单独的 `[data-dsh-part="mnemon-view"]` 与默认值优先级相同,取决于加载顺序;写在 `@layer` 中的普通声明低于未分层的默认值。需要激活范围时,请加上皮肤自己的祖先选择器。挂载到 body 的对话框与其他插件的根元素不在此范围内。替换 `--mn-surface` 意味着由皮肤自行负责可读性与透明度,它不会改变设置、记忆数据或 Provider 行为。 + +## 交互约定 + +记忆相关的界面在各处遵循同一套约定: + +- **一个标识**:对话中的回合记忆栏、存入记忆按钮与侧栏的记忆系统都使用脑形标识;DSH 的数据库形状图标表示用量,与记忆无关。 +- **一个名称,一个对话框**:写入长期记忆的操作在各处都叫**存入记忆**,打开同一个对话框,由任务 Agent 完成并给出回执。 +- **名称优先于标识符**:界面用标题、内容与空间名称指代记忆,不显示原始 id 或路径;需要时卡片上的**复制 ID** 仍然可用。 +- **统一的控件**:搜索框、选择项、开关与对话框按钮使用 DSH 的输入框、选择器、开关与描边“取消”;菜单与提示显示在对话框上方。 +- **刷新只在顶栏**:**刷新**重新读取当前页面;各页面不再单独放置刷新或同步按钮。 +- **操作的轻重**:主要操作使用 DSH 主按钮,编辑等次要操作使用描边按钮;红色只用于删除、断开、归档与忘记。 +- **说明一行**:各页与各分组的说明控制在一行以内,细节放在对应的指南中。 +- **状态只列运行中的部分**:状态页列出正在运行的 Provider,其余汇总为一行,并提供前往配置。 + +另外:记忆空间的开关只决定 dsh-mnemon 是否读取它,与 Mnemon CLI 的默认 Store 无关;删除 Mnemon Native 空间需要确认,外部空间使用“断开”,不会改动 Provider 中的数据;反复点击(包括选择已选中的值)都不会让页面变空白。 + +下一步:[能力地图](./capabilities.md) · [快速开始](./getting-started.md) · [Provider 指南](./memory-providers.md) · [配置参考](../reference/configuration.md) diff --git a/dsh/docs/zh-CN/reference/compatibility.md b/dsh/docs/zh-CN/reference/compatibility.md new file mode 100644 index 000000000..ce0159e1b --- /dev/null +++ b/dsh/docs/zh-CN/reference/compatibility.md @@ -0,0 +1,105 @@ +# 兼容性与升级 + +**简体中文** | [English](../../en/reference/compatibility.md) | [文档中心](../README.md) + +Starter 固定经过测试的官方插件组合。下表记录验证范围,不承诺所有上游版本或账号配置都已验证。 + +| 组件 | 基线 | 已验证的范围 | +|---|---|---| +| DSH | `0.2.0-rc.2`(npm `latest` 与 `next`)、`0.1.7-rc.2` | 两个受支持的宿主;`0.2.0-rc.1` 也可安装。0.1.7-rc.2 是锁定的开发基线:正式发布的契约、WebUI 与隔离 Headless 激活、profile 设置与旧设置恢复、Session V4 中的生产者专属消息、插件管理器激活以及 Agent Teams 审查矩阵。DSH 0.2 的验证范围见[下文](#dsh-02) | +| Node.js | `22.19`、`24` | 分别用于源码 CI 与打包制品 CI;开发要求 `^22.19.0 || >=24.0.0` | +| Node.js 20 | 仅公开包入口导入 | 不代表 DSH Host 能在 Node 20 运行 | +| Mnemon Native CLI | `0.2.9` | 显式启用的真实 CLI 与临时数据测试;CLI 需要另外安装 | +| 三方 Provider | 适配契约与夹具 | 不代表真实云账号一致性或上游服务可用性已验证 | + +Root 的 DSH peer(`dsh-app-boot` 为可选)与记忆空间 Source 的 `dsh-client-ui-primitives` peer 均为 `^0.1.7-rc.2 || ^0.2.0-rc.1`。五个 Strategy 包使用新的扩展 SDK,要求 `dsh-mnemon ^0.5.17`;各 Source 保持 `^0.5.1`,Provider 则依赖记忆空间 Source。不再支持更早的 DSH:升级宿主前,请继续使用 dsh-mnemon `v0.5.16`,它是最后一个在 DSH `0.1.5-rc.1` 至 `0.1.7-alpha.1` 上验证的版本。较早的 Headless 和 WebUI 记录仅保留为历史证据。 + +“本回合记忆”以稳定 ID 注册到 DSH 的 `conversation.chat.turnTail` list 插槽,并在读取或展示活动前检查回合是否已完成。侧栏入口与**插件 → 可组合记忆**页面跟随 DSH 公开的默认/主会话 binding;会话标签页入口和 Better Sidebar 保留显式所属会话。DSH 0.1.7 将动态设置保存在 profile Config 中,并在“插件”中每个插件的页面编辑其配置;Mnemon 的配置页通过它写入,并按下述流程恢复保留的旧偏好。记忆数据和 Provider 格式不变。 + +参见[DSH 0.1.7 设置验证](../../pr-assets/issue-267-settings-migration/README.zh-CN.md)、[RC/alpha 验证与前后对比截图](../../pr-assets/issue-261-dsh-slots/README.zh-CN.md)、[DSH 0.1.5 验证](../../pr-assets/issue-223-dsh-015/README.zh-CN.md)、[宿主兼容证据](../../pr-assets/dsh-rc1-compat/README.md)、[升级证据](../../pr-assets/main-rebase-20260904/README.md)与[当前开发检查](../development/README.md)。机制测试通过不是 LLM 质量评测通过;特定 OS 与真实 CLI 检查在没有对应环境时可能跳过。 + +[v0.5.19 浅色图集](../../assets/webui-v0.5.19/README.md)覆盖双语桌面页面、插件页,以及 390 × 844 下的对话、记忆空间与记忆组合面板,该宽度下较长的名称会截断。它没有复测所有 Host 设置页或真实手机,因此不宣称完整支持手机;历史 v0.5.2 在 390px 下的布局问题保留[原版本证据](../../pr-assets/documentation-refresh/README.md)。 + +## DSH 0.2 + +DSH 在安装插件前,以及每次启动 profile 时,都会用自身版本检查插件所有 `@deepseek-ai/dsh` 与 `@deepseek-ai/dsh-*` peer 范围,预发布版本也参与比较。`^0.1.7-rc.2` 不包含 0.2.0,因此 dsh-mnemon 0.5.18 及更早的版本在 DSH 0.2 上安装时会被判为不兼容而拒绝,已安装的会在启动时停用。0.5.19 起,这些 peer 同时接受 `^0.2.0-rc.1`;`tests/dsh-host-compatibility.spec.ts` 对全部 18 个包的清单执行 DSH 自己的检查,覆盖 0.1.7-rc.2、0.2.0-rc.1 与 0.2.0-rc.2。 + +**升级顺序。** 先在现有 DSH 中把 dsh-mnemon 更新到 0.5.19 或更新的版本,再升级 DSH。若先升级了 DSH,旧版本会被停用,记忆数据不受影响;更新插件后即可恢复。不要用 `allow-version` 为旧版本放行。 + +**已验证的范围(0.2.0-rc.2)。** [v0.5.22 发布验收](../../pr-assets/release-v0.5.22/README.zh-CN.md)在全新的 0.2.0-rc.2 profile 上通过**添加插件**安装发布包,无需重启即可启用,并检查状态页、运行时记忆、Native 空间以及 CLI 与 WebUI 共享的检索。[侧栏入口记录](../../pr-assets/issue-318-sidebar-entry/README.zh-CN.md)显示其面板行与图标尺寸与 0.1.7-rc.2 相同。 + +**已验证的范围(0.2.0-rc.1)。** 全局安装正式 DSH 0.2.0-rc.1 后,从空白 profile 通过命令行与插件页两种方式安装、界面中“立即启用”无需重启、状态页、运行时记忆写入、真实模型的首轮对话与存入记忆、Headless 任务,以及桌面版窗口与远程页面的读写路由;另将全部 DSH 开发依赖切换到 0.2.0-rc.1:类型检查、构建、全部插件测试与 Headless 验证通过,根测试中只有核对锁定开发基线本身的断言不同。截图见[安装图集](../../assets/install-v0.5.19/README.md)。 + +**首次安装时的宿主行为。** + +- 插件页的**添加插件**会在用户未选择过时测速,在中国大陆通常默认使用**中国大陆镜像源**,其他地区使用 **npm 官方源**。 +- pnpm 11 默认不选用发布不足 24 小时的版本。新版本发布当天,直接安装 `dsh-mnemon` 可能装上旧版本,并在 DSH 0.2 上被判为不兼容;此时安装带版本号的 `dsh-mnemon@<版本>`,或等待 24 小时,见[安装与启动](../guides/installation.md#常见问题)。带版本号安装会固定版本,之后用 `dsh plugin --profile web update --latest dsh-mnemon` 升级。在同样的 24 小时内,`update` 与 `update --latest` 都会停留在已安装的版本,已有安装也要带版本号安装新版本。 +- 安装结果卡片显示软件包的英文简介;插件列表中的名称与说明跟随界面语言。 +- `desktop` profile 归桌面版所有,DSH 0.2 的命令行拒绝管理它;桌面版用户在应用的插件页中安装和管理插件。 + +## 桌面版窗口 + +DSH 桌面版窗口从应用自己的 `dsh-app://app/` 地址加载,而不是回环地址;DSH 0.1.7 桌面版也不为页面声明传输方式。dsh-mnemon 0.5.18 及更早的版本因此把桌面窗口当成远程页面:所有调用经 API Gateway,默认的 `remoteAccess: read-only` 使运行时记忆、记忆空间与插件设置在界面中只读,而 Agent 工具仍可写入([#310](https://github.com/omdsh-dev/dsh-mnemon/issues/310))。 + +0.5.19 起,应用自己提供的页面(非 `http:` / `https:` 地址)使用 Mnemon 的本地通道;只有 DSH 为页面声明了不持有 Host 的传输方式时,才仍按远程页面处理。DSH 0.2 桌面版声明 `ownsHost: true`,同样使用本地通道。从其他设备打开的页面继续经 API Gateway、默认只读,并在记忆系统与插件设置中写明需要 `remoteAccess: trusted-host` 与重启 DSH。本地通道与 `/api` 使用 DSH 相同的 Host/Origin 校验与浏览器会话认证。 + +验证方式:模拟桌面壳的 Electron 窗口注册与官方桌面版相同的 `dsh-app` 标准安全协议,把请求代理到只监听 127.0.0.1 的 Host。已发布的 0.5.18 在 DSH 0.1.7-rc.2 上复现了只读(13 次 Mnemon 调用全部经 API Gateway);修复后在 DSH 0.1.7-rc.2 与 0.2.0-rc.1(`ownsHost: true`)上均可添加运行时记忆,写入经本地 `/dsh-mnemon-write`,记忆空间与插件设置可编辑;通过可信 authority 打开的远程页面仍然只读并显示原因。 + +## Desktop profile generation + +Desktop 可能删除插件 generation 内的私有 `@deepseek-ai/*` 包,改用宿主自己的框架版本。Mnemon 使用宿主公开的 Volatile API 构建动态设置 schema,DSH `0.1.7-rc.2` 通过 Cosmokit `1.8.5` 提供这些 API。宿主若携带不含这些 API 的旧版 Cosmokit(例如 `1.8.3`),即属于不受支持的宿主,请在该环境继续使用 dsh-mnemon `v0.5.16`。 + +受影响的 `0.5.13` 安装中,Desktop 的回退分支会将缺少 `createVolatile` 导出的真实错误隐藏为 `Cannot find package 'dsh-mnemon'`。平铺 `mnemon-bundle` 不能修复该导入失败。Starter 保留 group、根停用总开关、独立 Source/Strategy 选择、私有 Provider 子项和已有设置命名空间。在所属 profile 更新 Mnemon 后重启即可;本修复不需要迁移记忆或配置。 + +参见[原版 Desktop 复现与验收记录](../../pr-assets/issue-274-profile-generation/README.zh-CN.md)。 + +## 安装后直接启用 Starter + +DSH `0.1.7-rc.2` 的 Desktop generation 目录和 pnpm 安装可能只在 profile 中暴露 `dsh-mnemon` 根包。各组件已在它的依赖树中安装,但如果宿主启动时未选中该 bundle,首次启用仍可能使用启动时的包解析表,导致 Runtime、Documents、Memory Spaces 和默认 Strategy 抛出 `ERR_MODULE_NOT_FOUND`。 + +Starter 的组件组 `mnemon-bundle`(模块 `dsh-mnemon/bundle`)先准备依赖解析,再用 DSH 自己的 `cordis:group` 挂载子组件。它调用宿主公开的包解析服务,保留其他 bundle 启动时的依赖路径,并由宿主拒绝不兼容的模块重绑定;不平铺组件包、不改写 profile 链接、不修改 DSH。已有 Entry ID、核心停用总开关、各组件的独立选择和记忆数据均保持不变。0.5.18 与 0.5.19 由单独的就绪条目 `dsh-mnemon/starter`(`mnemon-starter`)完成这一步,组件组等它提供 `mnemonStarterReady`;0.5.20 起两者合为一个条目,插件页不再显示单独的就绪行,组件组也不会因为它被关闭而一直等待。 + +Mnemon 同时在自己的传输注入作用域内注册 Web RPC,已运行的官方 Connection 无需重启。使用 bundle 或核心开关控制整套组合。在 0.5.18 与 0.5.19 中关闭 `dsh-mnemon/starter`,`mnemon-bundle` 会一直等待 `mnemonStarterReady`:`dsh web` 照常启动但没有记忆系统,桌面版则按启动失败处理,见[安装与启动](../guides/installation.md#dsh-提示waiting-for-service-mnemonstarterready)。更新到 0.5.20 后,profile patch 中残留的 `- id: mnemon-starter` 不再对应任何条目,宿主会忽略它(桌面版日志提示 `patch: entry "mnemon-starter" not found`),可以删除这两行。 + +代价在 DSH 的配置 schema 导出:`dsh web --dump-config-schema` 只识别原生 `cordis:group` 与 `cordis:include`,会把 `mnemon-bundle` 报告为无法识别的树载体,并略去其中各组件的 schema,与 DSH 自带的 agent preset 相同。运行时配置、插件页与 profile patch 不受影响。 + +官方 WebUI 首次安装后,可以在同一宿主进程中点击“立即启用”;已安装但停用的 bundle 也可直接启动。更新或卸载 Node 已加载过的包时,仍须遵循 DSH 的正常重启要求,本修复不替换已加载模块。例如原地更新后不重启就启用组件,会提示新版本才有的入口未导出(`ERR_PACKAGE_PATH_NOT_EXPORTED`):从 0.5.17 或更早的版本更新时是 `./starter`,从 0.5.18 或 0.5.19 更新时是 `./bundle`。加载器不会为运行中的条目更换模块,因此从 0.5.18 或 0.5.19 更新时,也可能是运行中的旧组件组一直等待已移除的 `mnemonStarterReady`(`mnemon-bundle (dsh-mnemon/bundle): pending …`)。重启后两者都会恢复,见[升级验收记录](../../pr-assets/starter-group-upgrade/README.zh-CN.md)。参见[安装与启用验收记录](../../pr-assets/desktop-live-activation/README.zh-CN.md)与[组件组验收记录](../../pr-assets/starter-group-readiness/README.zh-CN.md)。 + +## DSH 0.1.7 bundle 组件列表 + +DSH `0.1.7-rc.2` 的“插件 → dsh-mnemon”详情页会把 `mnemon-bundle` 内部容器列为“已关闭”的组件(0.5.20 起显示为 `dsh-mnemon/bundle`,此前为 `cordis:group`),DSH `0.2.0-rc.1` 的显示相同。9 个实际组件全部运行时,列表显示“共 10 个 · 9 运行中 · 1 已停用”;0.5.18 与 0.5.19 多一行就绪条目,对应为“共 11 个 · 10 运行中 · 1 已停用”。点击容器开关会返回 `unknown-plugin`,中文界面提示“组件启用失败:找不到该插件”。参见[原始截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/pr-assets/sidebar-native-20260926/before-bundle-toggle-error.jpg)与[上游问题 #649](https://github.com/dsh-external/issues/issues/649)。 + +这是宿主的展示与管理清单不一致:bundle 声明列表包含原生 group,但可管理插件清单明确排除了 group。该行的“已关闭”不代表 Mnemon 核心或其子插件已停用,也不能据此判断记忆读写是否正常。 + +遇到这一现象时: + +1. 查看“记忆系统 → 状态”以及实际 Source、Strategy 组件的状态。如果只有内部容器误显示关闭,而所需组件和读写正常,可以继续使用;实际组件报错或读写失败仍需单独排查。 +2. 停用或恢复整套组合时,使用顶层 `dsh-mnemon` bundle 开关,或对应 `mnemon` 条目的核心组件开关;不要使用 `mnemon-bundle` 容器行的开关。可组合记忆配置页在组件列表上方也注明了这一点。 +3. 保留已有配置和记忆。此显示问题不需要重置数据,也不需要迁移配置或记忆。 + +Starter 保留稳定的 group ID 和已有 `mnemon` 配置目标。停用核心会停止其 Source、Strategy 和私有 Provider 子项;重新启用后,各组件恢复各自的独立选择。移除 group 会让仍启用的依赖项等待缺失的核心;将 group 改为匿名条目则可能在 profile 重载后留下旧实例。不要通过删除容器、稳定 ID 或修改分组声明来隐藏这一行。[正式宿主生命周期回归](../development/README.md#测试归属与覆盖)在不修改已安装宿主的前提下,验证管理器持久化、重启及旧版字面值/表达式停用标志。 + +已在独立环境验证的[上游候选补丁](../../pr-assets/sidebar-native-20260926/upstream-fix.patch)会过滤容器展示并保留实际子插件;它未包含在正式 DSH `0.1.7-rc.2` 或 Mnemon `v0.5.17` 中。本地管理适配器方案需要接管 DSH 全局插件管理服务,把它内置于 Mnemon 会影响 Mnemon 的独立停用,因此未随插件提供。修复进展以 [#649](https://github.com/dsh-external/issues/issues/649) 及后续 DSH 发布说明为准;候选环境的截图不代表正式宿主已修复。 + +## DSH 0.1.7 设置恢复 + +DSH 0.1.7 用基于 Config 的表单替代了 `settings.register()` 和 `settings-file`。Mnemon 提供动态 Config 字段,现有界面操作通过宿主检查 revision 的 profile 写入器持久化;修改传输权限仍需正常重载插件。 + +Mnemon 消息使用 Session V3、V4 均接受的生产者专属来源 `dsh-mnemon`。过滤和去重仍识别历史包装,以及 DSH 迁移后的 `plugin:dsh-mnemon` 消息;本改动不会重写已有 Session 文件。 + +启动时,Mnemon 可将 `/settings.yaml.imported` 中保留的备份恢复到可编辑的根 `mnemon` 条目,包括根设置、`mnemon-ui` 对话开关,以及仅属于当前 profile 的精确 `mnemon-view-*` / `mnemon-plugins-*` 命名空间。当前 profile 的显式值优先,包括显式清空的插件选择;已有 Source 和 Strategy 条目保持原样;Strategy 条目使用动态表达式时,会保留原表达式并跳过该条目的旧覆盖值,避免将当前求值固定下来。恢复后会在根 Config 中记录 `legacySettingsImported: true`,备份文件保持不变。 + +若 Mnemon 启动时原 `settings.yaml` 仍存在,由 DSH 执行导入;完成后再重启一次该 profile,恢复 Mnemon 专属偏好。DSH 未公开导入完成信号,因此 Mnemon 等待新宿主进程,避免并发写入。只读或归属不明确的根条目不会被修改;相关旧设置节损坏时,不做部分导入,也不标记完成。请保留备份,参考启动诊断后再修复;不会猜测自定义根 ID 或其他 profile 的 hash。 + +回滚 DSH 时,保留 profile patch 和旧设置备份,并随旧宿主重新安装 dsh-mnemon `v0.5.16`。0.1.7 上后来修改的设置不会自动反向导出到旧设置文件,需要恢复对应备份或在旧宿主重新设置;Runtime、档案、记忆空间与 Provider 数据不受影响。 + +## 升级默认安装 + +1. 导出 Mnemon Pack;需要保留 Provider 连接时,按[运维指南](../guides/operations.md#备份与恢复)保护额外备份。 +2. 在每个使用它的 DSH Profile 中升级 `dsh-mnemon`。Starter 安装精确的插件版本;不要单独更新其子包后,就假定混合组合已经验证。 +3. 安装新包代码后重启 DSH。检查“记忆系统 → 状态”,再读取一条已有 Runtime、档案及已激活记忆空间。 +4. 写入前确认存储范围。切换范围只是选择另一份数据权威,不会迁移数据。 + +v0.5 保留默认 v0.4 的存储、配置与侧栏工作流。可选的会话标签页入口(`builtin`)使用同一组 Source 页面,旧 `buildin` 拼写会规范化。通用策略与三个记忆增强默认关闭。插件页中的记忆组合负责列出、开关和配置已安装的组件;没有单独的 View 页。 + +独立插件作者使用声明的 peer 范围与公开出口;旧的私有控制器导入不属于受支持的升级表面。自定义组合需要独立于 Starter 验证,参见[插件开发](../development/extensions.md)与[v0.5.0 发布边界](../releases/v0.5.0.md)。 diff --git a/dsh/docs/zh-CN/reference/configuration.md b/dsh/docs/zh-CN/reference/configuration.md new file mode 100644 index 000000000..d8cceafea --- /dev/null +++ b/dsh/docs/zh-CN/reference/configuration.md @@ -0,0 +1,469 @@ +# 配置参考 + +**简体中文** | [English](../../en/reference/configuration.md) | [文档中心](../README.md) + +## 配置位置与生效方式 + +配置即 DSH profile 中的 `mnemon` 条目。DSH 的 settings 服务把用户值写入 profile patch: + +```text +$DSH_HOME/profiles//cordis.patch.yml +``` + +Web profile 的 patch 通常是 `~/.dsh/profiles/web/cordis.patch.yml`。旧宿主留下的 `settings.yaml` 会被导入 profile 一次,并保留为 `settings.yaml.imported`,参见[DSH 0.1.7 设置恢复](./compatibility.md#dsh-017-设置恢复)。当前全部配置标记为 `live` 生效;改动生效时会先初始化候选运行图,再原子切换 Host 服务。 + +执行中的回合保留已固定的运行图。已经派发的子 Agent 保留委托运行图直到本次 activation 销毁,即使父回合已结束;后续父回合和新委托的 activation 使用新 generation。保存设置不会静默扩大既有任务的 Recall 权限。 + +与 DSH 0.1.7 对所有自带配置的插件一样,Web 界面在“插件”中该插件自己的页面编辑 Mnemon 的设置,而不在“设置”中。[界面指南](../guides/ui-guide.md#在插件页中)展示了每个部分。 + +| 界面位置 | 设置 | 保存为 | +|---|---|---| +| 记忆组合 | 主策略;每个记忆来源与增强各一个开关;各组件声明的选项 | `memoryView.strategyTypeId` 与 `memoryView.entries..config`;组件的开关状态由 DSH 插件管理器写为对应 Entry 的 `disabled` 行 | +| 存储 | 存储范围、数据目录、备份与迁移 | `storageScope`、`dataDir` | +| 界面 | 记忆系统入口、回合记忆栏、存入记忆按钮 | `displayMode`、`conversationInteraction.turnBar`、`conversationInteraction.saveAction` | +| 运行时记忆页面 | 用户画像范围 | `runtimeUserScope` | +| 记忆空间页面 | 记忆 Provider;Mnemon Native 嵌入(自动、Ollama 或 OpenAI 兼容) | `state/memory-providers.json` 中的 Provider 注册表;`embedding` | +| 分层策略页面 | 任务 Agent 模型;空闲审查 | `taskAgentModel`;`idleReview` | + +开关与选择器立即生效;存储目录、嵌入连接、审查参数与组件选项等需要输入的值要点击**应用**。Provider 可复用的服务字段(服务地址、凭据、可执行文件)保存在 Provider 注册表中,而不是 Mnemon 的 YAML;启用或保存 Provider 时,会把它已有的命名空间发现到**记忆空间 → 概览**,关闭时只移除这些本地映射。其他高级项需要直接修改 YAML。 + +OpenViking user key 没有 admin 权限时,可组合服务字段 `discoveryUser`、`endpoint`、`apiKey`、`account`,显式发现单个用户的记忆空间;`discoveryUser` 留空仍枚举 admin。服务字段保存在 Memory Spaces 的 Provider 注册表,不是新的 Mnemon 顶层 YAML 设置。参见 [OpenViking 配置与兼容性](../guides/memory-providers.md#运维边界)。 + +## 完整示例 + +```yaml +mnemon: + storageScope: global # global | workspace | custom | workspaces + runtimeUserScope: storage # storage | global + # dataDir: ~/mnemon-data # custom 时必填 + # cliPath: /opt/homebrew/bin/mnemon + # store: legacy-store # 兼容发现提示,不是常规路由目标 + timeoutMs: 10000 + defaultRecallLimit: 10 + runtimeMemory: + memoryLimitBytes: 10240 + userLimitBytes: 4096 + maintenanceMaxTokens: 8192 + embedding: + enabled: false + endpoint: http://localhost:11434 + model: nomic-embed-text + memoryTopology: + layers: + runtime: { enabled: true } + documents: { enabled: true } + memory-spaces: { enabled: true } + recallQuality: + policy: strict-v1 + lowScoreThreshold: 0.25 + highScoreThreshold: 0.6 + candidateMultiplier: 3 + maxMediumResults: 4 + maxUnknownResults: 2 + routingGuidance: true + lifecycleEnabled: true + recallMode: guided + writebackMode: guided + idleReviewMs: 30000 + idleReview: + enabled: true + runtimeMemory: true # false:只建项目档案 + provider: spawn + fallback: spawn + agentTeams: pause # pause | scoped + minIntervalMs: 300000 + maxPerSession: 20 + maxContextChars: 24000 + maxTokens: 4096 + tabEnabled: true + writeEnabled: true + taskAgentModel: + mode: inherit # inherit | fixed + # provider: deepseek # fixed 时必填 + # model: deepseek-chat # fixed 时必填 + remoteAccess: read-only # 远程管理:read-only | trusted-host +``` + +## 选项 + +| 配置 | 默认值 | 范围 | 实现语义 | +|---|---:|---|---| +| `storageScope` | `global` | `global` / `workspace` / `custom` / `workspaces` | 统一控制 Runtime、Documents、Memory Spaces 和预留 state 根目录 | +| `runtimeUserScope` | `storage` | `storage` / `global` | 让 USER.md 跟随当前存储根,或叠加全局 USER.md,同时保持项目 MEMORY.md 与其他层使用所选范围 | +| `dataDir` | 未设置 | 绝对路径、`~` 或 `~/...` | `custom` 时必填;`workspaces` 时为可选集中根目录;旧配置只设置它时自动解析为 `custom` | +| `cliPath` | 自动发现 | 可执行路径 | 显式指定 Mnemon CLI | +| `store` | 未设置 | `[A-Za-z0-9][A-Za-z0-9_-]*` | 用于旧 Store 的兼容发现/首选提示;语义操作由 Memory Space 路由 | +| `timeoutMs` | `10000` | 100–120000 ms | 单次 CLI 硬超时 | +| `defaultRecallLimit` | `10` | 1–50 | 服务和 UI 默认召回条数;不同入口可能再收紧 | +| `runtimeMemory.memoryLimitBytes` | `10240` | 1–1048576 字节 | 完整 `MEMORY.md` 投影的 UTF-8 字节上限 | +| `runtimeMemory.userLimitBytes` | `4096` | 1–1048576 字节 | 完整 `USER.md` 投影的 UTF-8 字节上限 | +| `runtimeMemory.maintenanceMaxTokens` | `8192` | 1–1000000 tokens | Runtime 迁移与压缩 worker 的完成 token 预算;不改变项目档案归档与元信息维护预算 | +| `embedding` | `{ enabled: false, endpoint: http://localhost:11434, model: nomic-embed-text, apiKey: '', protocol: auto }` | enabled + HTTP(S) endpoint + model + 可选 apiKey + protocol(auto/ollama/openai) | 开启后,Host 为每个 Mnemon CLI 子进程注入保存的 endpoint、模型、API Key 与协议覆盖;endpoint 以 `/v1` 结尾时 Mnemon 自动使用 OpenAI 兼容协议并以 Bearer 头携带 apiKey,`protocol: openai` 可对非 `/v1` 端点显式指定;关闭后不干预既有 Host 环境和 Mnemon 默认值 | +| `memoryTopology.layers..enabled` | 三个默认层为 `true` | boolean | 旧版记忆层标记。记忆组合中 Source 的开关会打开或关闭它的 DSH Entry,并同时恢复这个标记;读不到组件时才使用它。关闭 Source 不会删除或迁移数据 | +| `memoryTopology.layers..participation` | 每层各自的默认值 | `recall`、`write`、`projection`、`maintenance` 分别取 `off` / `manual` / `automatic` | 分层策略下每一层如何参与各类工作 | +| `memoryView.strategyTypeId` | 未设置 | Strategy type id,例如 `default-three-tier` 或 `general` | 当前选择的主策略,由**主策略**选择器写入,优先于 `memoryTopology.strategyId` | +| `memoryView.entries..config` | `{}` | 组件声明的选项 | 在组件页保存的选项。开关状态不在这里:DSH 插件管理器把它写为 profile patch 中对应 Entry 的 `disabled` 行,早先保存的选择会一次性迁移过去 | +| `persistenceStrategy` | `{ mode: manual }` | `manual` / `automatic`、`providerId`、`prompt`、`rules` | 新空间的 Provider:任务 Agent 新建记忆空间时固定使用一个 Provider,或按允许的 Provider、数据边界、必需能力与偏好智能选择;已有空间仍按名称与说明路由 | +| `recallQuality.policy` | `strict-v1` | 已注册策略 ID | 在召回正文序列化给 Agent 或客户端前执行的确定性策略 | +| `recallQuality.lowScoreThreshold` | `0.25` | 0–1,低于高分阈值 | `strict-v1` 会移除低于此边界的标准化分数结果 | +| `recallQuality.highScoreThreshold` | `0.6` | 0–1,高于低分阈值 | 保留结果达到此边界时标记为高相关度 | +| `recallQuality.candidateMultiplier` | `3` | 1–5 | 在过滤前扩大各 Provider 候选请求,最多不超过服务的 50 条上限 | +| `recallQuality.maxMediumResults` | `4` | 0–50 | `strict-v1` 在全部高相关度结果之后最多接纳的中相关度条数 | +| `recallQuality.maxUnknownResults` | `2` | 0–50 | `strict-v1` 在有分数证据之后最多接纳的无 score 或未知量纲条数 | +| `routingGuidance` | `true` | boolean | 是否注册额外的分层路由 system section | +| `lifecycleEnabled` | `true` | boolean | 是否启用 pre-step cue 和评分后台审查 | +| `recallMode` | `guided` | `guided` / `off` | 是否在每个会话注入一次可持续复用的按需 recall cue;不移除显式召回 | +| `writebackMode` | `guided` | `guided` / `off` | 是否在每个会话注入一次可持续复用的热记忆 cue,并启用评分加 dirty admission 的后台审查;不移除显式写入 | +| `idleReviewMs` | `30000` | 5000–600000 ms | 达标后需要连续空闲的时间 | +| `idleReview.enabled` | `true` | boolean | 自动审查独立开关 | +| `idleReview.runtimeMemory` | `true` | boolean | 允许审查修改运行时记忆(USER.md 与 MEMORY.md);`false` 时只创建项目档案 | +| `idleReview.provider` | `spawn` | `spawn` / `fork` | 有界检查点或继承父上下文 | +| `idleReview.fallback` | `spawn` | `spawn` / `skip` | 仅启动前处理 fork 缺失/不兼容 | +| `idleReview.agentTeams` | `pause` | `pause` / `scoped` | 安装 Team 工具时暂停,或显式启用受 guard 保护的审查;已验证 DSH/Teams 0.1.7-rc.2 | +| `idleReview.minIntervalMs` | `300000` | 5000–86400000 ms | 两次尝试的最短间隔 | +| `idleReview.maxPerSession` | `20` | 0–200 | 当前加载父 Agent 的尝试上限;包括失败/取消 | +| `idleReview.maxContextChars` | `24000` | 1000–1000000 | spawn 检查点字符上限 | +| `idleReview.maxTokens` | `4096` | 128–131072 | 每次模型响应输出上限 | +| `displayMode` | `sidebar` | `sidebar` / `builtin`;兼容旧值 `buildin` | 记忆系统入口:独立 Sidebar 或会话内标签页,共用同一工作台;旧拼写自动迁移为 `builtin` | +| `tabEnabled` | `true` | boolean | 是否挂载所选入口和工作台;关闭后 Host RPC、命令和 Agent 工具保持注册 | +| `writeEnabled` | `true` | boolean | 是否暴露语义写工具、写 RPC 和写命令 | +| `taskAgentModel` | `{ mode: inherit }` | `inherit` / `fixed` | 整理名称与说明、Agent 查询、存入记忆和档案归档使用的独立任务 Agent,以及空闲复盘 worker 的模型路由;`fixed` 必须同时保存 `provider` 与 `model`,并会钉住对应的写入、证据问答、Provider 选择、迁移、压缩、归档和元信息维护 worker。对话中的 Recall 与 Related 是 Host 直接读取,不使用该路由 | +| `remoteAccess` | `read-only` | `read-only` / `trusted-host` | 非 loopback Mnemon 管理授权,仅启动时读取,由 API Gateway 映射执行 | +| `conversationInteraction.turnBar` | `true` | boolean | 回复下方的回合记忆栏,立即生效 | +| `conversationInteraction.saveAction` | `true` | boolean | 已完成回复上的**存入记忆**操作及其确认框,立即生效 | + +所有值都保存在 `mnemon` 条目下并实时生效:界面的 `mnemon-ui` 设置范围保存为 `conversationInteraction`,`mnemon-view` 设置范围保存为 `memoryView`。存储根只会在新运行图初始化成功后原子切换。 + +### Runtime Memory 容量与维护预算 + +长会话可以提高两类热记忆的字节上限和有界迁移/压缩 worker 预算,无需再补丁修改生成的包文件: + +```yaml +mnemon: + runtimeMemory: + memoryLimitBytes: 20480 + userLimitBytes: 10240 + maintenanceMaxTokens: 32768 +``` + +默认值保持已发布版本的 10240 / 4096 / 8192 行为。保存后会构建新的运行图,后续 Runtime 读取、写入、容量维护与 Mnemon Pack 校验统一使用这组上限。已有条目与 `memories.json` 格式不变。若把字节上限降低到当前用量以下,不会删除数据;Runtime 视图会显示超容状态,后续写入需要先压缩或重新提高上限。回滚只需删除该配置块或恢复默认值。 + +存储字节上限计算条目正文和分隔符。模型快照中的重要性与时间跨度注释不占用存储容量,但会占用 Strategy 已有的投影字符预算;它们不增加排序、相关性过滤或独立配置。 + +运行时记忆页面显示每个文件的已用大小与上限。 + +![运行时记忆:USER.md 与 MEMORY.md 及其默认上限](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/memory-runtime.jpg) + +### Mnemon Native 嵌入 + +从 Finder 或 Dock 启动的 macOS 应用通常不会继承交互式 shell 启动文件。在 Mnemon Native 中开启“由 DSH 管理嵌入配置”,即可让保存值成为每个 Mnemon 子进程的权威配置: + +```yaml +mnemon: + embedding: + enabled: true + endpoint: http://127.0.0.1:11434 + model: qwen3-embedding:0.6b +``` + +OpenAI 兼容服务器把 endpoint 指到 `/v1` 基础 URL 即可,Mnemon 会自动从 Ollama 协议切换到 OpenAI 协议(`/v1/embeddings`);需要认证的服务填写 `apiKey`,它将以 Bearer 头注入。不以 `/v1` 结尾的兼容端点用 `protocol: openai` 显式指定协议: + +```yaml +mnemon: + embedding: + enabled: true + endpoint: http://127.0.0.1:8080/api + model: bge-m3-mlx-8bit + apiKey: sk-... + protocol: openai +``` + +Host 会复制正常进程环境,然后只在子进程中覆盖 `MNEMON_EMBED_ENDPOINT`、`MNEMON_EMBED_MODEL`、`MNEMON_EMBED_API_KEY` 与 `MNEMON_EMBED_PROTOCOL`(`protocol: auto` 时不注入协议变量,由 Mnemon 按 `/v1` 自动探测);不会修改桌面会话、`launchctl`、shell 文件或 Mnemon 持久数据。保存后会切换到新的运行图,后续调用无需重启 DSH 即可使用新值。`enabled: false` 或省略 `embedding` 时,dsh-mnemon 不注入覆盖值,原有继承环境与 Mnemon 内建默认值保持不变。`MNEMON_EMBED_DIMENSIONS` 仍属于可通过 Host 环境继承的高级配置。 + +Endpoint 必须是不含凭据、查询参数或片段的 HTTP(S) 绝对 URL。Mnemon 会把记忆与查询正文发给该服务;apiKey 与其他设置一样保存在 DSH 设置文件中,远程明文 HTTP 会暴露传输内容,请使用受信任的回环地址或 HTTPS。“测试状态”会针对当前默认 Store 执行实际生效的 `mnemon embed --status`,只报告嵌入服务可达性、模型、Mnemon 报告的解析协议与嵌入覆盖率,不会回填或改写记忆。有未保存编辑时必须先保存,避免把草稿值误报成已生效。 + +### 记忆来源开关 + +每个记忆 Source 在**记忆组合**中有一个开关。它通过 DSH 插件管理器打开或关闭该 Source 的 DSH Entry,并在 profile patch 中保存为对应 Entry 的 `disabled` 行。打开表示允许主策略在需要时使用该 Source,并不强制每一轮召回或写入;关闭会同时停止它的上下文注入、工具、后台任务与数据面 Web/RPC 操作。 + +![记忆组合:每个记忆来源与增强各有一个开关](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/webui-v0.5.19/zh-CN/plugin-composition.jpg) + +关闭是可逆的路由状态,不是删除。它在记忆系统中的页面会保留并标记为**未运行**,不读取数据面;状态页与管理目录仍可查看。重新打开后使用原来的目录与数据。最后一个运行中的 Source 不能关闭。旧版的 `memoryTopology.layers..enabled` 标记会随 Source 一起恢复,只在读不到组件时使用。 + +WebUI 从实时管理目录读取 Source 实例,新增 Source 无须修改前端。Source type id 匹配配置,Strategy 选择精确的实例 key。设置更新有修订栅栏,候选组合验证成功后才会替换。Core 与 Source 边界会再次检查能力、范围与当前授权。 + +### 召回质量策略 + +`strict-v1` 是面向 Agent 的安全默认值:仅对明确声明为 0–1 标准化相关度的 Provider,在正文进入 Agent 前移除非正分和低于阈值的结果;随后在请求上限内保留全部高相关度结果,默认最多保留 4 条中相关度结果和 2 条无 score 或未知量纲结果,不再用较弱证据补满 limit。`balanced-v1` 把低分结果放在主要证据之后,`exhaustive-v1` 为直接检查保留有限分数结果。超出声明范围的分数按未知量纲处理,不伪造成置信度。跨 Provider 排序继续使用倒数排名融合。 + +这些是 Memory Spaces Source 自己维护的纯函数召回质量策略,配置只能选择已随该 Source 提供的策略 ID。内部注册函数不是公开插件 API;自定义检索实现应通过 Source/Provider 的公开契约接入。非法候选上限、决策或选择会回退到 `strict-v1`;配置未知 ID 会拒绝候选运行图。过滤计数通过结构化的 `source.quality` 返回,不会拼接进 Agent hint。 + +### 浏览器认证 + +DSH 负责浏览器认证或配对,以及 Host/Origin 校验。远程页面使用命名空间 API Gateway,本地回环客户端与 DSH 桌面版窗口(`dsh-app://app/`)使用各自的通道;只有 DSH 为页面声明了不持有 Host 的传输方式时,应用页面才按远程页面处理。Mnemon 网关映射单独执行 `remoteAccess`:`read-only` 允许普通读取、小范围激活和设置查看,但拒绝写入、ZIP 操作、View 变更与设置修改;没有 `trusted-host` 授权时,设置快照报告 `writable: false`。修改这项仅在启动时读取的策略后请重启 DSH。DSH `trustedHosts` 不能代替 HTTPS 或部署层访问控制。`writeEnabled=false` 是产品级只读模式,不能代替传输层认证。 + +完整的代理、启动 token、可信 authority、重启与验证流程见[云端 WebUI](../guides/operations.md#cloud-hosted-webui)。 + +## 存储范围 + +### `global` + +```text +MNEMON_DATA_DIR when non-empty + otherwise ~/.mnemon +``` + +适合希望多个工作区共享 Runtime、Documents 和 Memory Spaces 的用户;其他 Mnemon-enabled Agent 使用相同根时,也可以共享其中的 Mnemon Memory Spaces。 + +### `workspace` + +```text +Agent / 工具 / 生命周期:resolve(currentSession.header.cwd, ".mnemon") +Web 工作台查看:resolve(workspaceRegistry.get(selectedWorkspaceId).path, ".mnemon") +``` + +每个 DSH 工作区拥有独立的记忆根,包含运行时记忆、项目档案与记忆空间。对话 Agent、模型工具、命令和生命周期按当前会话的 cwd 路由;Web 发起的独立任务 Agent 则显式使用工作台选择的 Host 已登记工作区,不能提交任意路径。因此,没有选中主会话时,整理名称与说明、Agent 查询、存入记忆和档案归档仍会写入记忆系统顶栏选定的工作区。 + +Headless 没有 `workspaceRegistry`;其新 session 的 cwd 就是启动 `dsh --profile headless ...` 的目录,因此 `workspace` 直接解析为 `<启动命令 cwd>/.mnemon`。 + +### 全局 USER.md 与工作区项目记忆同时生效 + +若要跨仓库共享用户级协作要求,同时隔离项目事实,可在**插件 → 可组合记忆**页面把存储范围选为“工作区”,并在“运行时记忆”页面把用户画像范围选为“全局共享”,或配置: + +```yaml +mnemon: + storageScope: workspace + runtimeUserScope: global +``` + +此后每回合会把全局根(设置 `MNEMON_DATA_DIR` 时使用该目录,否则使用 `~/.mnemon`)的 `USER.md` 与 `/.mnemon` 的 `MEMORY.md` 一起投影。`target=user` 变更和本地 USER.md 压缩只写全局事实源;`target=memory`、Documents、Memory Spaces 与 Provider state 仍留在工作区。全局 MEMORY.md 和工作区 USER.md 条目会完整保留在磁盘,但在该模式下不进入投影。 + +切换设置不会复制、合并或删除条目;改回 `runtimeUserScope: storage` 后,原工作区 USER.md 会重新可见。Mnemon Pack 仍表示一个所选存储根,因此工作区 Pack 不会暗中带入独立的全局 USER.md;重要的全局档案需要单独备份全局根。 + +### `workspaces` + +这是 Starter 的内置模式,无需额外安装插件。在“存储范围”中选择“集中存储 · 按工作区隔离”,如需自己的集中根目录,在同组“数据目录”中选择“自定义”。 + +```yaml +mnemon: + storageScope: workspaces + dataDir: ~/central-memory # 可选;默认 MNEMON_DATA_DIR 或 ~/.mnemon + runtimeUserScope: global # 可选;只共享 USER.md +``` + +四个 area(`runtime`、`data`、`documents`、`state`)都保存在 `<集中根>/workspaces/<规范工作区路径的 SHA-256>/`。现存符号链接别名解析到同一 ID,不同工作区路径相互隔离。移动或重命名会选择新 ID,不会自动迁移。侧栏跟随所选的已登记工作区,会话标签页和 Headless 跟随所属会话 cwd。即使集中根为自定义目录,全局 USER.md 仍使用 `MNEMON_DATA_DIR` 或 `~/.mnemon`。 + +工作区身份解析允许尚未创建的目录后代,包括 Unicode 名称和符号链接别名下的后代。若路径位于现存文件之下,所有平台(包括 Windows)都会在计算存储 ID 之前以 `ENOTDIR` 拒绝。有效 ID 保持不变,既有存储不会被移动、改写或删除。 + +切换范围不会迁移、合并或删除旧根。ZIP Pack 仍只包含当前工作区根;要保留全部工作区,请备份整个集中目录。远端 Provider 命名空间仍遵循自身共享语义。 + +### `custom` + +```yaml +mnemon: + storageScope: custom + dataDir: /absolute/path/to/mnemon-data +``` + +也允许 `~` 和 `~/...`。相对路径会被拒绝。 + +### 选择跨 Agent 共享范围 + +| 目标 | 推荐范围 | 说明 | +|---|---|---| +| 本机多个 Agent 共享长期记忆 | `global` | 各方统一使用 `~/.mnemon` 或同一个 `MNEMON_DATA_DIR` | +| 多个 Agent 共享指定数据根 | `custom` | 各方显式配置同一个绝对目录,便于隔离和备份 | +| 只在一个项目内共享 | `workspace` | 各方都需要把 Mnemon 根对齐到该项目的 `/.mnemon` | + +Mnemon Native 通过 `data//mnemon.db` 与其他 Mnemon-enabled Agent 原生互操作;三方引擎通过配置的 Provider 作用域互操作。Runtime、Documents、DSH 激活状态和 UI 元数据仍属于 dsh-mnemon 管理范围。见[长期记忆 Provider](../guides/memory-providers.md)。 + +第三方服务配置、记忆空间范围配置与 Secret 保存在当前范围根目录的 `state/memory-providers.json`,不会写入 profile patch。服务配置由同一 Provider 的多个记忆空间复用;运行时才与单个记忆空间配置合并。Mnemon Native 的 ZIP 只包含 Runtime、Documents 与原生记忆空间;第三方服务数据、连接凭据和本地三方 Store 不进入该 ZIP。 + +## CLI 发现优先级 + +```text +config.cliPath + -> executable MNEMON_CLI_PATH + -> each PATH directory + -> Windows: GOBIN/mnemon.exe + first GOPATH/bin/mnemon.exe, or ~/go/bin/mnemon.exe + %LOCALAPPDATA%/Programs/mnemon/mnemon.exe + %ProgramFiles%/mnemon/mnemon.exe + -> Unix: ~/.local/bin/mnemon + /opt/homebrew/bin/mnemon + /usr/local/bin/mnemon + /usr/bin/mnemon +``` + +显式 `cliPath` 会被采用;既可以填写完整路径,也可以填写 PATH 中的命令名(例如 `mnemon`)。状态检查与实际调用使用同一套发现规则,并在重新检查时识别安装或移除的 CLI,无需重启 DSH。若显式指定的命令不可执行,实际调用会返回启动错误,不会悄悄改用其他 CLI。Windows 自动发现接受普通 `.exe` 文件及经过验证的官方 npm `mnemon.cmd` 启动器。npm 启动器通过 Node 调用,全程不使用 shell;任意其他 `.cmd` 与 `.bat` wrapper 仍被排除。迁移至 npm 后,请将显式 CLI 配置指向 npm 启动器,并在“状态 → 检查版本”核对可执行文件路径。 + +## 兼容 Store 提示优先级 + +```text +config.store + -> MNEMON_STORE + -> /active + -> default +``` + +Memory Space 目录建立后,长期语义操作使用明确的记忆空间 ID,不依赖全局 active Store 进行路由。 + +## 后台任务 Agent 的模型路由 + +整理名称与说明、Agent 查询、记忆系统与对话中的存入记忆和档案归档会创建一个无会话历史的独立顶层任务 Agent。它使用当前查看工作区作为 cwd;即使没有选中主 Agent session,也能落到左上角选定工作区。任务完成后 Agent 会被释放。 + +默认的 `inherit` 先使用 DSH“创建新会话”时的默认 Provider / Model;该路由不可用时才沿用当前可用主 Agent 的完整模型路由。在“分层策略”页面的“后台任务 → 任务 Agent 模型”中选择“指定模型”后,会保存完整的 Provider + Model,并只覆盖 Mnemon 后台任务,不改变对话主 Agent。独立任务 Agent 内部如需语义判断,仍可调度受限 worker;该 worker 继承任务 Agent 的模型路由。 + +```yaml +mnemon: + taskAgentModel: + mode: fixed + provider: deepseek + model: deepseek-chat +``` + +DSH 的实时模型目录会报告各模型的输入模态,选择器为支持图片的模型标记**图片输入**。选中它不代表 Mnemon 后台任务会摄取图片:整理名称与说明、Agent 查询、存入记忆、智能选择与档案归档仍只提交文本与有界证据。在主对话中,dsh-mnemon 追加生命周期指引时会保留 DSH 管理的图片块及其附件引用,活动阈值只计算文本块。原始图片字节不会复制进运行时记忆、项目档案或记忆空间。 + +## Provider 要求 + +普通 worker 会优先选择 `spawn`;如果没有该名称,可以选择另一个具备全部能力的 provider: + +```text +toolFilter = true +persona = true +depthLimit = true +``` + +后台审查默认使用不继承父上下文的 `spawn` Provider,并从父 Agent 当前公开 surface 构建有界检查点。仅收录完整可见文本消息与成功工具结果,不重建被省略或不可用的上下文。`idleReview.provider: fork` 保留完整父上下文审查,并要求 `inheritsParentContext=true`。fork 缺失或不兼容时,`idleReview.fallback` 在启动前选择 `spawn` 或 `skip`;已启动的运行绝不通过其他 Provider 重试。 + +审查要求本地子 Agent 发布、`agents.isOwnedBy` 和 `agent.ctx.tools.guard`;guard 同样阻止无关的 own-scope 插件工具与 Code Mode 子调用。`maxContextChars` 限制 spawn 检查点;`maxTokens` 限制每次模型响应,不限制 fork 继承输入或多步运行的总用量。 + +**审查写入什么。**每条知识只进一层。项目记录(设计、实现细节、路径、端口、范围约定、交接说明等)最多写成一份项目档案。工作记忆(MEMORY.md)只收用户明确说过的简短规则,例如约定、纠正、环境事实或工具特性,且项目档案和已有条目都未涵盖;USER.md 收身份与偏好。guard 让每轮审查只写一层:建了项目档案后,修改工作记忆会被拒绝;修改工作记忆后,新建项目档案会被拒绝;USER.md 的修改不受影响。`idleReview.runtimeMemory: false` 会从审查中收回 `mnemon_runtime_memory`,审查只创建项目档案。审查从不写入记忆空间:工作记忆在容量整理时归档进去,项目档案在冷归档时建立索引;**存入记忆**与 `mnemon_remember` 则直接写入。 + +`idleReviewMs` 仍是连续空闲防抖时间。独立的 `minIntervalMs` 限制尝试间隔,失败与取消也计入;`maxPerSession` 限制当前加载的父 Agent 的尝试次数,零表示暂停。清空或压缩上下文时 DSH 会替换该 Agent;这与重启 Host、卸载后重新打开 Agent 一样,都会获得新的内存内预算。运行结束通过 DSH 公开 API dispose。持久会话历史保留:已发布 Host 没有插件范围的归档/TTL 契约,Mnemon 不删除会话文件或其他插件的 Agent。 + +**Agent Teams 兼容:**现有配置默认保留 `idleReview.agentTeams: pause`。同时检测到公开 `agentTeams` 服务与父 Agent 作用域的 `spawn_teammate` 工具时,在创建子代理前暂停。仅加载 TeamService 不会暂停审查。 + +DSH 与全部官方 Agent Teams 组件均为 **0.1.7-rc.2** 时,可在分层策略页面(**插件 → 可组合记忆**)的空闲审查中选择**受限子代理审查**,或设置 `idleReview.agentTeams: scoped`。该已发布 Team 策略兼容有界 spawn 与显式 fork。此模式仍检查父子归属、本地子代理发布、`maxDepth: 1` 和单调的审查工具白名单(包括 Code Mode 子调用)。审查子代理仍不能使用 Team 工具或再次委派;父 Agent 保留 Teams。不会猜测包版本或移除其他插件的策略。缺少 guard/归属能力或策略报错时,运行失败且不通过 fallback 重放。保留 `pause` 可在安装 Team 工具时不运行审查;也可设置 `idleReview.enabled: false`,只关闭审查,保留 Teams、召回与主动写入。 + +有界 spawn 按公开的扁平 `user/message` 事件读取真实用户证据,仅保留已完成检查点之前、当前可见的完整消息;注入的召回、摘要和没有真实用户来源的消息不会被提升为用户断言。配置的字符预算内,明确决策与禁止写入指令都以整条消息保留。 + +失败的审查仍计为失败。如果失败前已有写入提交,工作区显示子运行 id 和已提交变更回执元数据,包括 Code Mode 外层失败前已提交的内部工具。不自动回滚或重放。手动重试前请核对该运行、档案 id 或 Runtime revision。后续审查仍遵守冷却和会话预算。只需停用此维护流程时,可在分层策略页面关闭**空闲审查**,或设置 `idleReview.enabled: false`。 + + +## 只读配置 + +```yaml +mnemon: + writeEnabled: false +``` + +效果: + +- 不注册模型写工具; +- 写与激活 RPC 保持注册,在 Host 边界拒绝记忆 mutation; +- `/mnemon remember` 和 `/mnemon forget` 拒绝; +- Source 管理与模型 action 同样不能绕过 Host 的写入限制。 + +它是“功能只读”,不是文件系统只读模式:Runtime 控制器仍可能初始化或修复投影,Document 搜索会更新 LRU 访问时间,Mnemon 读命令也可能触发上游数据库迁移。不要把 `writeEnabled=false` 用作只读挂载的安全承诺。 + +## 开关交互 + +```text +writeEnabled=false + -> overrides all explicit semantic writes + +writebackMode=off + -> no write cue, no scored review + -> explicit writes remain when writeEnabled=true + +recallMode=off + -> no recall cue + -> explicit recall remains + +lifecycleEnabled=false + -> no lifecycle cues or review + -> UI, commands, and explicit tools remain + +routingGuidance=false + -> removes only mnemon:routing + -> runtime-memory context remains +``` + +## 入口位置:`displayMode` 与 `tabEnabled` + +记忆系统默认使用 Sidebar:从 DSH 左侧栏打开独立主内容区工作台,使用无 Mnemon Logo 的 DSH 官方风格极简皮肤。设置 `displayMode: builtin`,或在**界面 → 记忆系统入口**中选择**会话标签页**,即可把同一个工作台放进当前会话的 `conversation.view` 标签页。页面、导航、弹窗和样式全部共用,不维护另一套 builtin 界面。主题作者可在两种位置使用[受支持的表面选择器与自定义属性](../guides/ui-guide.md#theme-skin-overrides)。 + +侧栏“记忆系统”的样式、折叠图标和选中状态与 DSH 原生“插件”入口一致。重复点击仍保持打开;**返回会话**或 Esc 会回到当前会话。选择“插件”、其他原生面板或“新建会话”会切换主面板。与任务看板、SSH 切换时,同时同步面板可见性和入口状态,即使其他插件的激活通知遗漏,点击也能重新打开记忆系统。 + +会话标签页隐藏页眉中的存储模式标记、工作区选择和对齐控件。所有读取、写入和独立任务请求都通过现有 Host 路由跟随所属会话: + +| `storageScope` | 会话标签页的实际读写根 | +|---|---| +| `global` | 共享 `MNEMON_DATA_DIR` 或 `~/.mnemon`,不受会话工作区影响 | +| `workspace` | 当前会话的 `/.mnemon`;切换会话时自动跟随各自工作区 | +| `custom` | 配置的 `dataDir`,不受会话工作区影响 | +| `workspaces` | 所属会话在 `<集中根>/workspaces/<工作区路径哈希>/` 下的子目录 | + +既有 `runtimeUserScope: global` 例外仍让 USER.md 保持全局。切换入口不会改变范围、迁移记忆数据或恢复旧 builtin 导航。选择后立即切换入口。 + +规范拼写统一为 **`builtin`**。历史上的 `displayMode: buildin` 偏好仍会被识别,运行时与界面统一使用 `builtin`。Host 启动及配置外部热更新时,通过 DSH 带修订号保护的设置接口自动写回这一个字段;旧客户端 RPC 提交 `buildin` 也直接保存为 `builtin`。其他字段和配置注释保持不变,并发期间用户明确选择的 Sidebar 优先,不会被迁移覆盖。 + +在**插件 → 可组合记忆**页面保存后,当前界面会立即更新。回环页面上,Client 还会跟随 DSH 的设置文档:在其他窗口保存,或 Host 重新加载了对 profile 的修改,都无需刷新浏览器即可看到。远程页面不接收 DSH 的设置镜像,需要刷新才能看到别处的修改。 + +如果旧值只来自组合 profile,迁移保存一条规范的用户设置覆盖,不直接改写 profile 文件。只读设置仍识别旧拼写,但不会绕过只读限制写盘;落盘失败会记录在 Host 日志中,不会关闭已归一化的入口。 + +`tabEnabled=false` 会实时移除所选入口和工作台;重新开启后恢复配置的入口位置,两种入口不会同时出现。Host RPC、命令和工具保持注册,运行中的 Agent 或命令不会因界面开关而失效。对话中的回合记忆栏与存入记忆仍由各自的界面开关控制,并跳转到所选入口。 + +## Profile patch 覆盖 + +包内 `cordis.patch.yml` 提供默认 config 行。DSH profile 的同 ID 配置可能整体覆盖这行。不要在 profile 的最终 patch 中只增加 `cliPath`;请改用 `MNEMON_CLI_PATH` 或用户设置 `mnemon.cliPath`。确因其他原因需要自定义 profile patch 时,应保留仍需启用的全部键,而不是假设深合并。 + +已认证网关管理所需的 `remoteAccess` 覆盖属于这种整行自定义。请使用[远程管理操作步骤](../guides/operations.md#remote-management)中的完整、可随升级核对的示例,保留当前配置的其他字段;单独的 `config: { remoteAccess: trusted-host }` 片段会替换这些字段。 + +## 常见配置 + +工作区隔离: + +```yaml +mnemon: + storageScope: workspace +``` + +在各会话内使用相同的工作区隔离: + +```yaml +mnemon: + storageScope: workspace + displayMode: builtin +``` + +显式指定 Windows CLI 路径: + +```yaml +mnemon: + cliPath: 'C:\Users\alice\AppData\Local\Programs\mnemon\mnemon.exe' +``` + +自定义数据盘和较长 CLI 超时: + +```yaml +mnemon: + storageScope: custom + dataDir: /Volumes/AgentData/mnemon + timeoutMs: 30000 +``` + +保留显式工具、关闭生命周期行为: + +```yaml +mnemon: + lifecycleEnabled: false +``` + +仅关闭后台写回判断: + +```yaml +mnemon: + writebackMode: off +``` diff --git a/dsh/docs/zh-CN/reference/interfaces.md b/dsh/docs/zh-CN/reference/interfaces.md new file mode 100644 index 000000000..e12512763 --- /dev/null +++ b/dsh/docs/zh-CN/reference/interfaces.md @@ -0,0 +1,237 @@ +# Web、Headless、工具、命令与 RPC + +**简体中文** | [English](../../en/reference/interfaces.md) | [文档中心](../README.md) + +本页是集成参考。日常使用请先看 [Sidebar 与对话交互指南](../guides/ui-guide.md)。 + +## 记忆空间命名与兼容性 + +产品统一使用**记忆空间(memory space)**。Source 导出 `MemorySpace`、`MemorySpaceView`、`MemorySpaceCatalog` 及对应的请求和元数据类型;已发布的 `MemoryBody*` 类型作为弃用别名保留,数据形状不变。 + +既有 v0.5.x 标识,如 `mnemon_memory_bodies`、`mnemon_memory_body_create`、`body-directory` 和 `memoryBodyId` / `memoryBodyIds`,仍表示记忆空间。为兼容已安装的工具、Source 页面、Provider 适配器和持久化的 Document/Pack 来源信息,这些标识保持稳定;`.dsh-memory-bodies.json` 文件名与其中的 `bodies` 字段也继续保留。术语调整不会重命名用户创建的空间、修改 ID、移动数据库或改变访问授权。 + +## 用户界面入口 + +| 入口 | 默认 | 说明 | +|---|---:|---| +| Sidebar | 是 | 左侧栏的“记忆系统”工作台:状态、运行时、档案、记忆空间 | +| 会话标签页 | 否 | 在会话内打开同一工作台,自动跟随所属会话的范围 | +| 本回合记忆 | 是 | 已完成回合下方的记忆工具摘要;展开后可跳到对应页面 | +| 存入记忆 | 是 | 已定稿助手回复旁的操作;确认后调用监督写入 | +| 插件页中的 dsh-mnemon 页面 | 是 | 记忆组合、组件页面、存储、界面与备份 | +| `/mnemon` | — | 对话命令入口 | +| 模型工具 | — | Root Agent 的结构化读写入口 | + +`displayMode` 选择 `sidebar`(默认)或 `builtin`(会话标签页);`tabEnabled` 控制所选入口是否显示。会话标签页不展示 Sidebar 的范围控件。对话内两个快捷入口在**界面**中分别开关,通过 `mnemon-ui` 设置范围保存。 + +## Profile 能力面 + +| 能力 | Web | Headless | +|---|---:|---:| +| 运行时上下文与生命周期提示 | 是 | 是 | +| 模型工具与独立任务 Agent | 是 | 是 | +| `workspace` 范围按 Agent cwd 路由 | 是 | 是 | +| Sidebar / 对话操作 | 是 | 否 | +| Host 到客户端 RPC | 是 | 否 | +| Agent idle 后的延迟评分审查 | Host 持续运行时执行 | 一次性进程退出时取消 | + +Headless 会获得完整模型工具面。它把命令行任务作为普通用户消息提交,不提供交互式斜杠命令分发;Agent 进入 idle 前已经完成的显式和模型引导写入会持久化。 + +模型工具、生命周期 hook 与系统调度都使用 `automatic` trigger;Web/RPC 中由用户直接发起的数据面操作使用 `manual` trigger。因此把某层设为 `manual` 会保留人工管理能力,同时拒绝模型工具和自动投影。`memory-system` 与 `status` 属于控制面观察,即使某层关闭也仍可读取。 + +## 模型工具 + +### 只读工具 + +| 工具 | 用途 | Root Agent 路径 | +|---|---|---| +| `mnemon_status` | CLI、配置、存储与目录聚合状态 | 直接服务 | +| `mnemon_memory_bodies` | 读取记忆空间目录、Provider 能力、健康与可用统计 | 直接服务 | +| `mnemon_recall` | 从一个或多个 active Provider 召回;异构结果排名融合 | 受 pinned Source 权限约束的 Host 直接服务 | +| `mnemon_related` | 在 `capabilities.related=true` 的记忆空间中遍历关系 | 受 pinned Source 权限约束的 Host 直接服务;Root 默认两跳 | +| `mnemon_document_search` | 确定性搜索受管档案 | Documents 控制层 | + +“只读”表示不修改受管正文或长期语义内容。`mnemon_document_search` 命中后仍会更新 `lastAccessedAt`,用于 LRU 排序,因此功能只读不等于磁盘只读。 + +### `writeEnabled=true` 时的工具 + +| 工具 | 用途 | Root Agent 路径 | +|---|---|---| +| `mnemon_runtime_memory` | `add` / `replace` / `remove` 热记忆 | 确定性控制;add 溢出时可能启动 worker | +| `mnemon_document_manage` | 创建、更新或归档档案 | 创建/更新确定性;归档使用 worker | +| `mnemon_document_create` | 新建独立档案,不更新或归档已有档案 | 确定性 Source `create` Action;供后台审查使用 | +| `mnemon_remember` | 按 Provider 语义沉淀洞察,回执区分已接受与持久提交 | `spawn` write worker | +| `mnemon_link` | 在支持能力的 Provider 中建立 typed relationship | `spawn` write worker | +| `mnemon_forget` | 在支持能力的 Provider 中按精确 ID 删除 | 仅限显式操作的 `spawn` write worker;自主蒸馏不含此工具 | +| `mnemon_memory_body_create` | 由 Agent 创建独立 Mnemon Native 记忆空间;第三方连接只由用户在 WebUI 管理 | `spawn` write worker | +| `mnemon_memory_body_update` | 更新名称、说明或 active | `spawn` write worker | +| `mnemon_memory_body_merge` | 非破坏性合并 Mnemon Native 记忆空间 | `spawn` write worker | + +worker 内调用同名工具时直接进入服务层,不再递归委派。 + +自主 write worker(蒸馏与 supervised writeback 运行)自行决定写入内容,因此宿主在其硬性工具白名单中排除了 `mnemon_forget`:遇到重复或冲突时只能跳过或写入修正后的条目,不能删除已有记忆。请求目标由用户或用户侧流程显式给出的操作(`/mnemon forget`、`mnemon_link` 与记忆空间管理)保留完整 write 工具面。 + +后台审查可以搜索档案并调用 `mnemon_document_create`,不能调用 `mnemon_document_manage` 或 `mnemon_view_action`。已有档案覆盖候选时跳过;有新增知识时,新建一份独立档案并引用相关已有档案 ID。Source 的 `create` Action 接收 `title`、`content`,以及可选的 `description`、`sourcePaths`、`sessionIds`;拒绝 `action`、`id` 等额外字段。容量不足时拒绝创建,不归档已有档案。已有的用户档案和 Agent 创建档案都受此保护;显式编辑继续使用原有管理路径。 + +`mnemon_runtime_memory` 的 `target=memory` 写入支持可选 `branches` 数组(git 分支名)。带分支范围的条目只在会话 workspace 处于所列分支上时投影进每回合 Runtime 快照;无分支标签的条目始终投影。`replace` 时提供 `branches` 修改范围,传空数组清除范围,省略则保持当前范围;该参数对 `target=user` 会被拒绝。 + +## 工具准入建议 + +- **运行时**:明确偏好、稳定项目约定、环境事实和高频经验。 +- **档案**:具有完整结构和理由的设计、调查、流程、复盘或交接。 +- **记忆空间**:需要跨任务保留,或适合图关系与深召回的稳定事实、决策和洞察。 +- **跳过**:问题、猜测、临时进度、完成日志、原始输出、秘密和可轻易从仓库重新发现的普通事实。 + +`mnemon_forget` 是破坏性语义操作;只有用户明确要求,或内容已确认错误 / 过时时才应执行。宿主通过把该工具排除出自主 worker 的白名单来强制这一点;用户明确要求时主代理仍可调用。 + +## `/mnemon` 命令 + +```text +/mnemon +/mnemon status +/mnemon recall <查询> +/mnemon related <完整记忆 ID> +/mnemon remember <内容> +/mnemon forget <精确 ID> +``` + +- 空 `/mnemon` 等价于 `status`。 +- `status` 是确定性读取,不启动模型。 +- `recall`、`related` 通过命令所在 live Agent 的限定 Source 路由直接读取,不启动 worker;`remember`、`forget` 使用该 Agent 作为写入 worker 的 parent。 +- 命令 recall 最多返回 10 条。 +- `forget` 必须接收一个不含空格的精确 ID。 +- `forget` 仅在收到 `forgotten` 回执时报告删除成功;跳过、失败或未确认的结果返回错误并附 worker 摘要。 + +## 界面注册 + +Mnemon 在 DSH 中展示的一切都注册到 DSH 自己的界面区域。对话内的注册都是增量的,不替换 DSH 官方渲染。 + +| DSH 区域 | 注册 | 行为 | +|---|---|---| +| `conversation.chat.turnTail` | list,`id=dsh-mnemon/turn-tail` | 通过 `turn-activity` 汇总完成回合中的 `mnemon_*` 调用,以及工具活动元数据中每次调用读到或写入的内容;无活动或未完成回合不渲染 | +| `conversation.chat.assistant-actions` | list,`id=mnemon-save` | 通过 `assistant-message` 读取已定稿文本;只在用户确认后调用 `supervise` | +| `conversation.session.header.lineage` | DSH 官方条目的低优先级副本 | 任务 Agent 会话页眉只统计该 Agent 自己的 token,不含其 fork 来源的日志 | +| `plugins.bundle.config` | keyed,`dsh-mnemon` | 插件页中 dsh-mnemon 页面上的全部配置 | +| `plugins.row.config` | keyed,`dsh-mnemon#` | 每个随包组件的行打开的页面,含该组件注册的设置 | +| `plugins.detail.actions` | list,`id=dsh-mnemon/open-workspace` | 从插件详情页打开记忆系统 | + +Mnemon 自己声明两个 keyed 区域,都以组件包名为键:`mnemon.component.settings` 渲染在该组件的页面上,`mnemon.component.status` 是它在状态页上的卡片。官方组件与第三方组件都通过这两个区域注册,见[插件开发](../development/extensions.md)。 + +`assistant-message` 读取的候选可编辑,长回复会按界面上限截取;确认后会启动独立任务 Agent,写入结果以它的落定回执为准。 + +## 工作区路由 + +Web 工作台请求携带 `sessionId` 和可选 `workspaceId`。Host 只接受 `workspaceRegistry` 已登记的 ID: + +- 确定性读取与人工维护可以路由到 `workspaceId` 选择的查看根; +- Agent、工具、命令和生命周期仍按 `sessionId` 对应 Agent cwd 路由; +- `status.workspaceContext` 返回 selected / effective roots 与 `aligned`; +- 工作台发起的独立任务 Agent 使用查看工作区,不要求当前主会话与其对齐;对话工具仍使用所属会话的 cwd 和本轮 View,不借用工作台的查看范围。 + +Headless 等没有 Web 工作区目录的 profile 不提供任意查看目标;Agent 执行仍直接根据 session cwd 路由 `workspace` 范围。 + +## RPC 通道 + +RPC 是 DSH Host 与插件客户端之间的内部桥,不是稳定外部 HTTP API。页面与组件插件应使用 `dsh-mnemon/client` 提供的限定范围客户端,见[插件开发](../development/extensions.md)。 + +| 通道 | 承载 | 远程页面 | +|---|---|---| +| `/dsh-mnemon-read` | 状态、目录、检索与对话读取 | 允许 | +| `/dsh-mnemon-activation` | 开关单个记忆空间 | 允许 | +| `/dsh-mnemon-write` | 其余所有 mutation | 需要 `remoteAccess: trusted-host` | +| `/dsh-mnemon-pack` | 备份导出与导入 | 需要 `remoteAccess: trusted-host` | +| `/dsh-mnemon-settings` | Host 与界面设置 | `get` 允许;`mutate` 需要 `remoteAccess: trusted-host` | +| `/dsh-mnemon-view` | 记忆组合读取 | 允许 | +| `/dsh-mnemon-view-settings` | 保存记忆组合、安装组件 | 需要 `remoteAccess: trusted-host` | + +回环页面直接调用这些通道,由 DSH 浏览器会话认证。远程页面经 DSH API Gateway 到达同一组处理器:通道 `/api`,endpoint 为 `dshMnemon/read`、`dshMnemon/activation`、`dshMnemon/write`、`dshMnemon/pack`、`dshMnemon/settings`、`dshMnemon/view` 与 `dshMnemon/viewWrite`。Gateway 负责 Host/Origin 校验、浏览器配对与响应封装;Mnemon 只额外施加上表中的 `remoteAccess` 授权,并在启动时确定。见[远程管理](../guides/operations.md#远程管理)。 + +### 读通道 + +| Endpoint | 行为 | +|---|---| +| `status` / `status-summary` | 服务、版本、生命周期、档案、存储与工作区上下文及当前 Memory System 描述符;`status-summary` 不等待任何 Provider I/O | +| `memory-system` | Serving/候选评估、脱敏 Source 实例描述符与当前参与配置 | +| `versions` | Mnemon CLI 与 dsh-mnemon 的当前 / 最新版本和安装来源 | +| `task-agent-models` | 独立任务 Agent 可用的模型 | +| `runtime-memory` | 运行时快照 | +| `documents` / `document` / `document-search` | 档案目录、正文与确定性搜索 | +| `graph` / `bodies` / `body-directory` | active 多空间图谱投影、含 Provider 能力的记忆空间目录与快速目录投影 | +| `body-reconnect` | 清除短期健康状态并刷新单个记忆空间,不修改持久数据 | +| `provider-services` | 脱敏的 Provider 服务目录;可包含已配置的凭据字段名,绝不包含凭据值 | +| `embedding-status` | Mnemon 嵌入模型、可用性与默认 Store 覆盖率 | +| `list` / `entities` | 内容列表与实体聚合 | +| `search` / `agent-search` / `related` | 直接检索、证据回答与关系遍历 | +| `turn-activities` / `turn-activity` | 会话或单回合的记忆工具活动 | +| `assistant-message` | 按 messageId 读取已定稿助手文本 | +| `source-management-catalog` / `source-management-read` / `source-assistance` | 通用 Source 管理:实例目录、声明的读取操作与只读辅助检索 | + +### 激活通道 + +`/dsh-mnemon-activation` 的请求 schema 比写通道更窄。`body` endpoint 只接受 `memoryBodyId`、布尔值 `active` 和常规 session / workspace 路由字段;`source-assistance` 以经确认的 `activation` 操作接受同样两个字段,仅作用于 Memory Spaces 实例。两者只控制记忆空间是否参与 DSH 读取与路由,不接受元信息、Provider 连接、凭据、删除或持久记忆 mutation。只读模式会在 Host 边界拒绝。 + +### 写通道 + +| Endpoint | 行为 | +|---|---| +| `runtime-memory` | 热记忆 mutation | +| `document` | create / update / archive | +| `supervise` | 用独立任务 Agent 处理候选并返回落定回执 | +| `remember` / `link` / `forget` | 长期语义写入、关系与软删除 | +| `body-create` / `body-update` / `body-delete` / `body-merge` | 创建或连接、编辑、确认后的 Native 删除或远程断开,以及合并记忆空间 | +| `body-metadata-maintain` | 由任务 Agent 刷新 1 到 20 个 active 记忆空间的名称与说明 | +| `provider-service-update` | 更新单个 Provider 服务;凭据仅在 Host 保存,响应脱敏 | +| `source-management-mutate` / `source-assistance` | 通用 Source 管理 mutation,以及按 Source 当前修订确认的 Host 辅助操作 | +| `version-update` | 更新明确组件;Host 固定命令与参数 | + +`provider-services` 通过读通道返回脱敏目录。设置编辑器只知道哪些凭据字段已经配置;保存新的值或显式清除字段,不回读已保存的凭据值。 + +`writeEnabled=false` 时激活通道与写通道仍保持注册,但所有 mutation 都在 Host 边界拒绝。浏览器也会根据 Host settings snapshot 在传输前禁用 mutation 控件。 + +### 备份通道 + +| Endpoint | 行为 | +|---|---| +| `target` | 当前有效根、范围,以及“默认位置”对应的默认根 | +| `export` | 导出完整、带 manifest 与 SHA-256 校验的 ZIP | +| `inspect` | 解析并校验待导入 ZIP,返回组件与占用预览 | +| `import` | 把 ZIP 安全合并到当前有效根;只读模式拒绝 | + +备份包含私有记忆;调用方必须把已认证 DSH 浏览器会话视为完整 Host 权限,并单独保护导出的归档。 + +### 设置通道 + +`/dsh-mnemon-settings` 为两个命名空间提供 `get` 与 `mutate`:`mnemon` 管理 Host 与存储设置,`mnemon-ui` 管理 `turnBar` 与 `saveAction`,保存为 `conversationInteraction`。mutation 携带 settings revision,防止覆盖并发编辑。插件页中的 dsh-mnemon 页面(`plugins.bundle.config`)在回环页面和远程页面上都通过该通道读写。回环页面还会跟随 DSH `ctx.configForms` 中 `mnemon` 条目的 revision,在其他页面或 profile 重新加载改动它时重新读取该通道。 + +### 记忆组合通道 + +| 通道 | Endpoint | 行为 | +|---|---|---| +| `/dsh-mnemon-view` | `dashboard` | 已安装组件、当前主策略、当前会话的 View 与回合活动,以及此处能否安装组件 | +| `/dsh-mnemon-view` | `preview` | 按当前修订校验拟保存的组合,不写入 | +| `/dsh-mnemon-view` | `inspect-plugin` | 读取组件包的 registry manifest:版本、`dsh-mnemon` peer 范围,以及当前 Profile 是否已安装 | +| `/dsh-mnemon-view-settings` | `apply` | 保存经确认的组合:主策略、组件开关与选项 | +| `/dsh-mnemon-view-settings` | `install-plugin` | 用 DSH CLI 把经确认的包版本安装到当前 Profile | + +`apply` 把主策略与各组件选项保存在 `mnemon-view` 设置范围(保存为 `memoryView`);组件开关由 DSH 插件管理器写入 profile patch。见[配置参考](./configuration.md)。 + +## npm 导出与扩展服务 + +Core 发布 `ctx.mnemonMemory: MnemonMemoryService`,实际服务只提供 `installContributions`。Source/Strategy 作者通过 `inject = ['mnemonMemory']` 与 `installMemory` 注册,不获得引擎、控制器或注册表。各包的公开子入口以其 `package.json#exports` 为准;Source 可提供自己的 `./contracts` 和可选 `./client`。 + +| 入口 | 职责 | +|---|---| +| `dsh-mnemon` | DSH Host 与默认 Starter | +| `dsh-mnemon/core` | 不带 Host/UI 的、Source 无关的 `ctx.mnemonMemory` 服务 | +| `dsh-mnemon/contracts` | JSON 安全的 manifest、facts、ViewSpec、View、Evidence、Receipt | +| `dsh-mnemon/extension-sdk` | Source/Strategy 定义、生命周期注册与校验 | +| `dsh-mnemon/testing` | 真实 Cordis 组合测试夹具与已构建 Client 制品加载器 | +| `dsh-mnemon/client` | DSH 工作台与 Source 页面 SDK | +| `dsh-mnemon-source-memory-spaces/provider-sdk` | Memory Spaces 自己的 Provider 子模块协议 | +| `dsh-mnemon-source-memory-spaces/testing` | Provider 驱动测试夹具 | + +通用模型工具 `mnemon_view_route`、`mnemon_view_action` 只执行当前 View 内的 route/offer;原有具名工具保留默认工作流。浏览器管理使用 `source-management-catalog`、`source-management-read`、`source-management-mutate` 与可选 `source-assistance`,Host/Source 检查实例、确认、修订与当前权限。内部 RPC 名称不是插件 SDK,页面应使用限定实例的管理客户端。见[插件开发](../development/extensions.md)。 + +## 国际化范围 + +主要 Sidebar 工作台、插件页中的配置与对话内入口支持中文和英文,并跟随 DSH locale 实时切换。品牌名、工具名和配置键不翻译。`/mnemon` 命令、模型工具卡、部分 Host 错误与兼容元数据尚未完全国际化。 diff --git a/dsh/docs/zh-CN/reference/storage-model.md b/dsh/docs/zh-CN/reference/storage-model.md new file mode 100644 index 000000000..540326ead --- /dev/null +++ b/dsh/docs/zh-CN/reference/storage-model.md @@ -0,0 +1,229 @@ +# 存储与记忆模型 + +**简体中文** | [English](../../en/reference/storage-model.md) | [文档中心](../README.md) + +## 为什么分三类记忆 + +默认 Starter 用三个独立 Source 处理不同的访问模式,再由分层策略把它们组合进每一轮。这是默认组合,不是 Core 的固定分类,也不限制第三方 Source 的形态: + +| 问题 | 存放位置 | 原因 | +|---|---|---| +| 下一轮必须直接知道什么? | Runtime Memory | 极小、直接进入 prompt | +| 哪份设计或流程需要快速完整阅读? | active Documents | 保留 Markdown 结构,不必做深召回 | +| 哪些历史事实和关系应跨会话存在? | Memory Spaces | 独立数据库、图关系、按需召回 | +| 长文已不常用但仍需追溯怎么办? | archived Documents | Mnemon 留索引,冷层保留原文 | + +推荐查询梯度: + +```text +current request and repository facts + | + v +Runtime Memory already in prompt + | + v +search active Documents + | + v +recall active Memory Spaces + | + v +follow an exact cold reference when full text is required +``` + +## 跨 Agent 共享边界 + +共享不意味着广播对话或文件。多个 Agent 都集成 Mnemon,且能够访问相同存储根与可识别的 Store 时,可以共享 Native 长期事实。三方 Provider 通过自己的 workspace、user、bank、project 或 container 范围共享;Runtime 与 Documents 不会自动进入其他 Agent 的上下文。 + +`global` 适合本机公共根,`custom` 适合显式约定的目录,`workspace` 将共享限制在项目。多进程并发依赖 Provider 自身的一致性保证;离线复制或直接修改本地数据库前,应停止所有使用者,参见[运维指南](../guides/operations.md#备份与恢复)。 + +## 统一根目录 + +```text +/ ++-- runtime/ +| +-- memories.json +| +-- USER.md +| +-- MEMORY.md ++-- documents/ +| +-- index.json +| +-- active/ +| +-- archived/ ++-- data/ +| +-- .dsh-memory-bodies.json +| +-- / +| +-- mnemon.db ++-- state/ + +-- memory-providers.json # 第三方连接控制面,0600,不进入 Mnemon Pack +``` + +`storageScope` 决定整个根,而不只是 Mnemon 数据库。`workspace` 范围会为每个已登记 DSH 工作区解析独立的 `/.mnemon`。显式启用的 `runtimeUserScope=global` 是唯一的分根例外:Runtime 从全局根读取 USER.md,MEMORY.md 与其他所有组件仍留在所选根。工作台任务使用查看工作区;对话工具与生命周期使用所属会话的 cwd 和已固定的 View。`state/memory-providers.json` 保存第三方 endpoint、目标 URI、身份和可选凭据;文件权限为 `0600`,Host 只返回已配置字段名,不回传凭据值。 + +`workspaces` 布局把四个 area 集中在 `<集中根>/workspaces/<工作区路径哈希>/`;Host 解析目录时不创建文件或修改旧根。只有显式 `runtimeUserScope: global` 会把 USER.md 放在该工作区子目录之外。 + + + + +## 运行时记忆 + +### 语义 + +- `target=user`:身份、角色、长期偏好、习惯、沟通风格和明确协作要求。 +- `target=memory`:项目、环境、决策、约定、工具特性和可复用经验。 +- `importance=critical|normal|low`:记录的重要性,在模型投影中可见,也用于整理时的保留优先级。 +- `branches`(可选,仅 `target=memory`):限定该条目在每回合 Runtime 快照中投影到的 git 分支名列表;没有分支列表的条目在所有分支可见。 + +当前不实现 `daily` target。 + +### 事实源和投影 + +在单个根内,`runtime/memories.json` 是唯一事实源。启用 `runtimeUserScope=global` 后,有效快照只组合全局事实源中的 `target=user` 条目与所选事实源中的 `target=memory` 条目。两个 JSON 文件和两组 Markdown 投影始终保持完整且不改写;过滤只发生在有效 Runtime controller。每条记录包含: + +```text +content +created_at +updated_at +target +importance +branches(可选) +``` + +`branches` 是 `target=memory` 条目的可选 git 分支名列表。没有 `branches`(或为空列表)的条目在所有分支可见。当会话的 workspace 是位于分支 `B` 的 git 工作树时,每回合的 Runtime 快照会隐藏 `branches` 列表不包含 `B` 的 `memory` 条目;非 git 工作区和 detached HEAD 下所有条目都会投影。`USER.md` 条目永远不携带 `branches`。 + +`USER.md` 和 `MEMORY.md` 是完整事实源的确定性派生文件。每个条目被归一成单行,条目之间使用单独一行的 `§` 分隔;`§` 是保留字符。启动和 prompt 组装时,控制层会从 JSON 修复缺失或被手工修改的投影。分支过滤只作用于 prompt 投影,不作用于这两个文件。 + +Runtime Source 会在面向模型的快照中,为每条正文添加一行元数据: + +```text +[importance=critical; created=14d; updated=2d] +偏好简洁回复。 +``` + +`created` 和 `updated` 表示距存储时间戳经过的完整 24 小时天数,在捕获投影时统一计算。不到一天显示 `0d`;未来时间戳显示 `future`,无法解析的时间戳显示 `unknown`。用户档案位于全局根时,两个根使用同一个捕获时间。当前回合保持已捕获的天数;下一回合会重新计算,即使存储 revision 没有变化。 + +这些行是正文之外的注释。`old_text` / `oldText` 只能匹配正文,不包含元数据行。记录的重要性和时间跨度不会覆盖当前指令。JSON、磁盘 Markdown、正文匹配、条目顺序和存储容量均保持不变。注释计入已有的模型投影字符预算,因此短条目较多时,即使默认 Strategy 也可能截断或省略条目。其指引明确说明快照受预算限制,缺失不代表删除;light-context 可以进一步降低预算。 + +### 操作 + +- `add` 写入独立新事实,完全相同的内容不会重复添加。 +- `replace` 和 `remove` 先在请求的 `target` 内,用规范化后的 `old_text` 匹配完整正文,再替换或移除整个条目。唯一的精确匹配优先,即使其他条目也包含同样的文本。 +- 没有完整正文匹配时,仍可用唯一子串定位条目。 +- 零命中、重复的精确匹配和有歧义的子串匹配均拒绝,不改动存储,也不执行模糊修改。分支标签控制投影,不用于选择 mutation 的目标条目。 + +### 容量 + +| 目标 | 默认上限 | 维护方式 | +|---|---:|---| +| `USER.md` | 4 KiB | 本地、无工具 worker 保守合并,不进入 Memory Space | +| `MEMORY.md` | 10 KiB | Host 精确归档已提交条目,再确定性装填热记忆余量 | + +两个上限都可以通过 `runtimeMemory.userLimitBytes` 与 `runtimeMemory.memoryLimitBytes` 调整,见 [Runtime Memory 容量与维护预算](./configuration.md#runtime-memory-容量与维护预算)。分层策略下,合法写入超过上限时才触发容量维护;其他策略会直接拒绝该写入。具名工具、通用 Action、后台子 Agent 和 Web 管理共用同一 Host 流程;Web 写入按所选存储范围执行,不要求打开用户会话。归档失败会保留热记忆并返回错误。底层 Source 独立使用时仍只执行自己的存储操作,自定义 Strategy 不会隐式继承默认归档。 + +容量按存储正文和条目分隔符的实际 UTF-8 字节计算,不包含仅用于 prompt 的元数据。单条内容最大 8 KiB。当 `add`、`replace` 或 `remove` 遇到容量溢出时,Host 会在任何 Provider 写入前重新检查源 revision。只有一个可写 Memory Space 时完全不调用模型;存在多个空间时,worker 只读取有界路由摘录并返回目标 id,不重写记忆内容。Mnemon Native 先从只读命名空间快照复用完全相同的原文,合并批内相同条目,再按目标空间通过 schema-v1 draft 和 `--no-diff` 各导入一次剩余原文,避免内容相似但不同的事实被跳过或相互覆盖。其他 Provider 继续使用适配器定义的写入语义。Host 要求每个源条目都有一条精确终态回执(跳过的重复项还必须有精确 Recall 证据),随后按重要性和字节预算选择热记忆保留项,并在原 revision fence 下把余量与待处理变更一次提交。Provider 无法与本地文件共享同一事务,因此稍后的 revision 冲突或并发外部写入可能留下已经归档的重复项;现有热记忆仍受修订检查保护。 + + + +## 项目档案 + +### 用途 + +Documents 保存比单条记忆更完整、又希望快速阅读的项目知识,例如: + +- 架构设计和理由; +- 有证据的调查结论; +- 操作流程、发布清单和故障复盘; +- 实现交接与长期维护说明。 + +用户画像、普通聊天、临时进度、原始大日志和秘密不应进入 Documents。 + +### 控制面 + +`documents/index.json` 是元数据事实源,管理 ID、标题、description、状态、文件名、来源路径、session、时间、revision、SHA-256、大小和 Memory Space 引用。Markdown 托管副本带有生成的 frontmatter。 + +`sourcePaths`: + +- 只能指向当前会话工作区内部; +- 只作为来源引用,不会被插件修改; +- 当前实现不要求路径实际存在; +- 不允许指向受管 `documents/` 目录自身。 + +### 范围 + +Documents 的物理共享范围由 `storageScope` 决定: + +- `workspace` / `workspaces`:通常随项目隔离; +- `global` / `custom`:多个工作区可能共享同一个 `documents/index.json`。 + +因此“项目档案”表示内容类型,不保证天然按工作区物理隔离。当前会话工作区只约束新写入的 `sourcePaths`。 + +### 容量与冷热分层 + +| 项目 | 限制 | +|---|---:| +| 单份正文 | 最大 2 MiB | +| active 总量 | 最大 10 MiB,包含生成后的 frontmatter | +| archived 总量 | 不计 active 上限 | + +创建或更新前会计算真实投影大小。空间不足时按 `lastAccessedAt`、再按 `updatedAt` 选择最久未访问的 active 文档;先写入/验证 Mnemon 冷引用,再在 revision 未变化时迁移原文。 + +默认搜索只覆盖 active。搜索会更新命中文档的 `lastAccessedAt`,因此它对正文只读,但会写索引元数据。 + + + + + +## 记忆空间 + +记忆空间是长期记忆的统一语义与路由单位,具体数据面由 Provider 决定: + +```text +id Host 生成或沿用已发现的 Mnemon Store 名 +name 人类可读名称 +description 路由边界:什么属于这里、何时召回 +active 是否参与 DSH 读取与路由 +provider mnemon-native 或已登记的三方引擎 +location 本地 Store/CLI 作用域,或远程 endpoint + Provider 作用域 +``` + +### 读写边界 + +- Mnemon 原生层在初始化后至少保留一个 Store,并通过 `/active` 选择一个默认 Store;普通 Mnemon Agent 仍按这套单 Store 语义工作。 +- dsh-mnemon 的激活状态是独立控制面:任意 0..N 个记忆空间可以激活,全部未激活也不会改变 Mnemon 默认 Store 或远程数据。 +- 召回与浏览只使用已激活记忆空间;图谱、实体、关联、链接和删除由 Provider 能力决定。 +- 指定未激活记忆空间进行读取会被拒绝。 +- 写入可以选择任何支持 `remember` 的已登记记忆空间;回执会反映 Provider 的精确写入或异步提炼语义。 +- 对未激活目标写入成功后,插件自动激活它。 +- 没有显式目标且激活数量不是 1 时,确定性服务要求调用方先选择目标。 + +### 创建、发现和合并 + +- 未初始化的空根可以保持零 Store;第一次显式创建记忆空间时使用 Mnemon 原生 `default` ID,名称与路由说明仍由用户决定,后续创建使用 Host 生成的 UUID。 +- 初始化后不能删除最后一个原生 Store,但可以将最后一个记忆空间设为未激活。删除 Mnemon 默认 Store 时,插件会先切换到另一个现存 Store。 +- 既有 `/data//mnemon.db` 会被发现并登记,不移动数据库。 +- 合并通过 Mnemon import 把来源内容导入目标;来源数据库保留,默认只将来源设为未激活。 +- Pack 替换不能把已初始化的 Store 集合清空;替换后若原默认 Store 已不存在,插件会选择一个现存 Store 修复原生默认指针。 +- `forget` 是按精确 ID 的软删除,不等于删除数据库文件。 +- 用户可在既有“创建记忆空间”弹窗选择 Mnemon Native 或任意已登记三方引擎,也可在智能模式中显式加入已配置候选;断开只删除本地连接登记,不删除 Provider 数据。 +- 智能 placement 的允许列表、数据边界与必需能力是 Host 强制规则;软偏好与 Prompt 只用于多个合格候选之间的语义选择,不能绕过硬规则。决策回执与记忆空间元数据一起保存。 +- 合并仍只适用于 Mnemon Native。图谱、关系、浏览、精确/异步写入以及硬/软/不支持删除都以各 Provider 声明能力为准;UI 与 Agent 不会假装补齐缺失行为。见 [Provider 能力矩阵](../guides/memory-providers.md)。 + +### 跨 Agent 可见性 + +`mnemon.db` 是 Mnemon 原生数据面,不是 dsh-mnemon 私有格式。其他 Mnemon-enabled Agent 在使用同一个 `storageRoot` 和 Store 时,可以访问同一份长期记忆。dsh-mnemon 也会发现磁盘上兼容的 Store;其 DSH 专有名称、说明和激活状态仍由 `.dsh-memory-bodies.json` 管理。 + +三方可见性由各自 Provider 作用域决定,例如服务与 URI、workspace/peers、bank、project/user、知识目录或 container。任何 Provider 的共享都不延伸到 `runtime/` 或 `documents/`;不能把“共享第三层记忆”表述为自动共享完整 DSH 上下文。 + +## 四类关系 + +Mnemon Native 保留 `temporal`、`semantic`、`causal` 和 `entity` 关系;Hindsight 投影 Provider 图谱,Holographic 生成本地实体/语义关系。没有图谱边的 Provider 只贡献有界无边节点,适配器不会伪造关系。记忆空间页会按能力隐藏不适用的关联、链接、浏览与遗忘动作。 + +## 数据权威表 + +| 数据 | 权威源 | 派生/缓存 | +|---|---|---| +| 热记忆 | `runtime/memories.json` | `USER.md`、`MEMORY.md` | +| Documents | `documents/index.json` + 托管 Markdown | excerpt、搜索排序、状态聚合 | +| Mnemon Native 目录 | `data/.dsh-memory-bodies.json` + 磁盘 Store | Web 状态聚合 | +| 第三方 Provider 连接 | `state/memory-providers.json` | 脱敏的 Provider 能力与状态 | +| 长期记忆 | Mnemon `mnemon.db` 或远程 Provider | 图谱投影、跨 Provider 排名融合 | +| 审查水位 | Host 进程内存 | 状态页快照;尚未持久化 | diff --git a/dsh/docs/zh-CN/reference/workflows.md b/dsh/docs/zh-CN/reference/workflows.md new file mode 100644 index 000000000..27193dbc5 --- /dev/null +++ b/dsh/docs/zh-CN/reference/workflows.md @@ -0,0 +1,334 @@ +# 生命周期与核心流程 + +**简体中文** | [English](../../en/reference/workflows.md) | [文档中心](../README.md) + +本页描述默认的**分层策略**。**通用策略**在同一份预算内提供全部可用 Source,由模型决定如何路由:具名工具服务各自的 Source,其余路由通过 `mnemon_view_route` 读取、`mnemon_view_action` 修改。它除各路由自身的上限外没有召回准入信封,不做 Runtime 容量维护与归档,也不做空闲审查。见[策略与增强](../guides/capabilities.md#策略与增强)。 + +## 每轮上下文 + +分层策略提供稳定的路由指引、静态 Runtime Memory 协议,以及按需更新的 Wake 快照消息: + +- `mnemon:routing`:system prompt section;当 `routingGuidance=true` 时提供简短的分层查询边界; +- `mnemon:runtime-memory-protocol`:system prompt section,只包含不变的 Runtime Memory 语义与写入规则。它只在 eager Runtime Source 参与自动投影时出现,记忆变更前后保持逐字节一致; +- Wake 快照:由当前 root 回合固定的不可变 View 渲染,以 `source.plugin=dsh-mnemon`、`form=recall` 的自有消息承载。Runtime Memory 提供带 revision 的完整 USER/MEMORY 状态快照,不再重复静态协议;Documents 与 Memory Spaces 只贡献有界封面,不注入完整目录。 + +Host 只在 Wake 内容变化时追加新的 user-role 插件消息,不再重新发送其他插件的共享上下文。快照保持完整状态,不构造依赖旧消息的补丁链;静态协议留在 system 前缀,Source 文本按原文传递而不做模板插值。回合、子 Agent 与运行代的所有权见[架构说明](../development/architecture.md#生命周期与失败)。 + +生命周期会在 Host 组装 System Prompt 前固定 View,并让本回合所有模型 step 使用同一个 View: + +```text +turn/start + -> 进入 system-prompt/assemble hook + -> beginTurn(根回合 + 操作范围) + -> Source 能力事实 → Strategy 提案 ViewSpec → 校验 → Source 投影 + -> 固定 Source 修订/摘要与仅留在 Host 的权限 + -> 生成有界 Wake + -> 继续 Host 提示词组装 + -> 让静态协议 section 与已固定的 Runtime Source 对齐 + -> 将变化后的 Wake 作为本插件的完整快照消息追加 + +agent/pre-step(step=1) + -> 为新回合取消待执行/执行中的后台审查 + -> 仅标记一次 Prime + -> 每个会话至多追加一次简短 recall/writeback cue + -> 主 Agent 决定是否调用记忆工具 +``` + +Source snapshot 不执行语义召回。Prime 只初始化路由状态,不执行异步 CLI 状态查询。 + +子 Agent 在 `agent/created`、driver 启动前捕获并保留存活父 Agent 的固定 View 与运行图。自己的各个回合固定这一被保留的 View,即使父回合已结束或已进入更新的 generation。子 Agent activation 销毁时释放委托;冷恢复重新获取委托;Host 显式创建且没有父模型回合的后台子任务生成新的 scoped View。子 Agent 拥有自己的回合权限,但不安装 root 专属的 cue 或空闲审查。详见[权限生命周期](../development/architecture.md#生命周期与失败)。 + +## Agent 召回 + +```text +根 Agent 或子 Agent 调用 mnemon_recall(query,可选 memoryBodyIds) + | + v +解析当前执行 Agent 自己固定的回合与保留的运行时 + | + v +读取 Host 上已固定的记忆空间 Source 状态 + | + v +校验请求 ID 是已固定集合的子集;未指定时使用全部已固定的激活 ID + | + v +记忆空间 Source 并发检索已授权的 Provider 命名空间 + | + v +质量归一化 + 倒数排名融合 + | + v +丢弃低相关项;首次最多准入 4 条 / 3,600 字符 + | + v +LLM 判断 evidence 是否足够 + | 足够 | 不足 + v v +直接回答,不再 Recall 显式提交一个不同查询 + | + v + 再检索一次、去重并关闭 Recall + | + v +两次在当前执行 Agent 回合共享至多 6 条、每条 1,200 字符、 +总正文 4,800 字符的总预算 +``` + +这一准入信封是分层策略的每回合策略;通用策略只应用各路由自身的上限与调用预算。 + +模型工具不暴露 `category`、`source` 或 `intent` 过滤器:模型猜错过滤条件不能遮住精确证据。Recall 并非强制执行,普通 root 回合是 0 次 Provider 查询。LLM 主动调用后,Host 允许一个首次查询;只有 LLM 查看 evidence 后仍认为不足,才允许再提交一个实质不同的精炼查询。同查询和并发重复请求会 join 或重放;第三个不同查询只重放最新 evidence,不再到达 Provider。随后至多执行一次 Related,而且只能使用两次 Recall 任一已准入的 `memoryBodyId + id`;重复 Related 同样重放结果。 + +Recall、Related 和单次 Documents 搜索槽位按执行中的 Agent 回合计预算。同一回合内并发调用共享状态,兄弟任务、后续回合和冷恢复的 activation 不会共享缓存 evidence 或占用彼此的预算。重放结果限于本次请求的 Memory Space 子集。Document search 另有独立边界:最多 4 条记录、每条最多 2,600 个查询附近字符、总正文最多 6,000 字符。模型侧 Memory Space 目录最多 16 项,`mnemon_status` 只返回紧凑健康汇总。完整记录、Provider 设置、路径和逐 Space 统计仍由 Web/RPC 控制面读取,不进入对话历史。 + +如果用户已经提供当前事实,或仓库可以直接回答,Agent 不应为了“展示记忆”而召回。 + +## Web 检索和 Agent 查询 + +Web “检索”页与模型工具路径不同: + +```text +直接检索 + -> RPC 读取通道 + -> 限定到 Source 的管理检索 + -> 原始证据 + +Agent 查询 + -> 同一条确定性直接检索路径 + -> 派发不含 Mnemon 工具的任务 Agent + -> 仅根据已提供的证据回答 + -> Host 将引用限定到实际的 memoryBodyId/id 对 +``` + +“实体”和“内容”页也经 Source 管理协议执行确定性读取,不需要第二个模型。“内容”使用 Provider 的只读 browse 契约,不冒充语义 Recall。 + +## 显式长期写入 + +根 Agent 或 `/mnemon remember` 的长期写入流程: + +```text +长期记忆候选 + | + v +派发写入任务 Agent + | + +-- 列出记忆空间 + +-- 选择最小的适用范围 + +-- 需要查重或检查冲突时召回 + +-- 只有反复出现的独立领域才创建新范围 + +-- 按请求执行 remember / link / forget / merge + v +结构化回执 +``` + +空存储根首次创建 Memory Space 时使用 Mnemon 原生 `default` ID,后续 ID 由 Host 生成。向 inactive 目标写入成功后会激活它。这里的激活只影响 DSH 路由;来源数据库的合并是非破坏性的。 + +运行时 `add` / `replace` / `remove` 和 Document `create` / `update` 不需要模型做存储 I/O;它们通过 coordinator 进入确定性控制层。容量维护和归档才启动专用 worker。 + +## Runtime add:正常路径 + +```text +请求 + -> 规范化内容 + -> 获取进程内队列与文件锁 + -> 重新加载 memories.json + -> 校验唯一匹配 / 重复 / 容量 + -> 写入临时 JSON 与 Markdown 投影 + -> 重命名投影文件 + -> 重命名 memories.json,作为提交标记 + -> 返回紧凑回执 +``` + +`replace` 和 `remove` 在请求的目标内,优先用 `old_text` 唯一匹配完整正文;没有精确匹配时才使用唯一子串。重复的完整正文仍视为有歧义。只有请求中的 add 或增大正文的 replace 会超过目标上限时,才触发容量维护。 + +## USER.md 容量整理 + +容量整理与下文的 MEMORY.md 归档都属于分层策略。使用通用策略时,超出 Runtime 上限的写入会被直接拒绝。 + +```text +USER 新增后超过 4 KiB + | + v +保存修订与已提交条目的快照 + | + v +派发无工具权限的本地压缩任务 + | + v +返回压缩条目 + sourceIndexes + | + v +Host 校验: + - 每个来源索引恰好出现一次 + - 没有重复或越界索引 + - 重要性未降低 + - 候选符合 Host 字节预算 + - 修订仍是当前版本 + | + +-- 无效/冲突 -> 保留原始数据 + | + v +按 UTF-8 字节确定性装填 + | + v +重试待处理的新增操作 +``` + +用户画像不会被发送到 Memory Spaces。worker 没有任何工具权限。 + +## MEMORY.md 归档与压缩 + +```text +分层策略下,本次 MEMORY 写入将超过配置上限(默认 10 KiB) + | + v +snapshot revision + 可归档的已提交 entries +(排除待提交 add,以及正被 replace/remove 的 entry) + | + v +Host 在本次操作的 Source / 命名空间范围内选择已有、active、可写的 Memory Spaces + | + v +单个目标:Host 直接路由 +多个目标:独立无工具 worker 只返回 source-index 路由 + 模型执行失败时,Host 在合格目标中确定性兜底 + | + v +Host 校验精确 source coverage、目标、权限与源修订 + | + +-- 无效/revision 已变 -> 不写 Provider;保留 Runtime + | + v +Host 把每条原始 entry 精确写入规划的已有 Space + - committed receipt -> 绑定目标 digest + - skipped -> 必须取得完全一致的 Recall evidence + | + v +Host 按重要性与字节预算保留原始热记忆条目 + | + v +CAS compactAndMutate(revision, compaction, original mutation, lineage) + -> JSON 与两个 Markdown projection 作为一次本地提交落盘 +``` + +planner 没有数据面工具,不能创建 Memory Space。每次写入都由 Host 发起;只有每条已提交来源都有可验证的 durable destination 后,Runtime 才会变化。若远端写入后发生很晚的跨进程 revision 冲突,原 hot entries 仍会保留;远端 Provider 无法共享本地文件锁,因此已提交的 durable copy 会作为安全重复保留,而不会被破坏性回滚。 + +## Documents 创建、更新和归档 + +```text +创建/更新请求 + | + v +按渲染后的 UTF-8 字节执行 capacityPlan + | + +----+----+ + | | + 未超限 溢出 + | | + v v + 提交 选择最久未访问的活跃档案 + | + v + 保存档案与修订的快照 + | + v + 派发归档任务 Agent + | + v + 写入并验证紧凑的 Mnemon 冷引用 + 包含标题、摘要、计划路径、SHA-256 + | + +----+----+ + | | + 失败 回执有效 + | | + v v + 保持活跃 检查修订 + | + +----+----+ + | | + 冲突 一致 + | | + v v + 保持活跃 将文件移入归档 + | + v + 重试原始修改 +``` + +人工归档使用同一条“先索引、后迁移”路径。Mnemon 索引已经成功但 revision 冲突时不会回滚索引,因此可能出现安全的重复引用,而不会丢失 active 原文。 + +## 确定性活动评分和后台审查 + +完成的 turn 累计四种信号: + +```text +score = + min(floor(totalUserCharacters / 50), 3) + + completedTurnCount + + min(floor(completedToolResults / 5), 2) + + toolDiversityScore + +toolDiversityScore: + 不同工具数 < 3 -> 0 + 不同工具数 = 3 -> 1 + 不同工具数 >= 4 -> 2 + +score >= 5 时达到门槛 +``` + +达到门槛并不代表一定写入: + +```text +已完成的回合 + | + v +score >= 5 ? -- 否 --> 为后续回合保留累计活动 + | + 是 + | + v +Host 判断是否存在待整理内容 + - 当前轮明确要求不写记忆 -> 停止 + - 持久化意图、累计 >=320 用户字符、 + >=600 助手字符或已完成非 Mnemon 工作 -> 继续 + | + v +等待 idleReviewMs(默认 30 秒),且距上次尝试 +至少 idleReview.minIntervalMs(默认 5 分钟) + | + +-- 新回合 --> 取消定时器/任务,保留累计活动 + | + v +确认 Agent 已空闲、已有 turn/end、该回合由分层策略组合, +且未达到 idleReview.maxPerSession + | + v +就已完成的回合启动任务 Agent + - spawn(默认):有界检查点,上限 idleReview.maxContextChars + - fork:继承父 Agent 上下文 + | + v +保守的整理决策,每轮只写一层 + - 先看档案:先检索;项目记录至多新建一份独立档案, + 不更新或归档已有档案 + - 热记忆:只收录用户新提出的、明确且持久的陈述; + MEMORY.md 只收档案未涵盖的简短规则,不收项目记录 + (idleReview.runtimeMemory: false 时关闭) + - 建了档案就不能再改工作记忆,反之亦然 + - 不提供记忆空间写入工具 + | + +-- 已完成(含跳过)--> 清空累计活动 + | + +-- 失败/中止 --------> 保留累计活动 +``` + +admission 有意只使用结构信号,不调用 LLM 分类;因此达到 activity 门槛但没有 dirty candidate 的普通 checkpoint 不会启动后台模型。审查 Agent 的工具恰好是 `mnemon_document_search`、`mnemon_runtime_memory` 与 `mnemon_document_create`;`idleReview.runtimeMemory` 为 `false` 时不提供运行时记忆工具。审查 guard 让每轮只写一层:它放行的第一次新建档案或工作记忆修改决定本轮所写的层,另一层随后被拒绝,即使第一次调用失败也是如此;USER.md 的修改不受影响。“至多一份”档案或一次热记忆修改仍由 persona 约束,不是 Host mutation counter。审查的产出之后才进入记忆空间:工作记忆在容量整理时归档,项目档案在冷归档时建立索引。安装了 Agent Teams 工具时,除非设置 `idleReview.agentTeams: scoped`,审查会暂停;暂停期间状态页会给出提示。后台水位尚未持久化,Host 重启会丢失未处理的累计信号。 + +## 配置开关的关系 + +- `recallMode=off`:不再注入 recall cue,显式 `mnemon_recall` 仍可用。 +- `writebackMode=off`:关闭写回 cue 和评分后台审查,显式写入仍由 `writeEnabled` 决定。 +- `lifecycleEnabled=false`:关闭生命周期提醒和审查,不移除显式工具或 Web 入口。 +- `routingGuidance=false`:只移除额外路由 section;Runtime Memory context 仍注册。 +- `writeEnabled=false`:移除语义写工具和写 RPC,拒绝写命令;它不是文件系统只读挂载保证。 diff --git a/dsh/docs/zh-CN/releases/README.md b/dsh/docs/zh-CN/releases/README.md new file mode 100644 index 000000000..9968b0de0 --- /dev/null +++ b/dsh/docs/zh-CN/releases/README.md @@ -0,0 +1,41 @@ +# 发布历史 + +**简体中文** | [English](../../en/releases/README.md) | [文档中心](../README.md) + +已有安装先看[兼容性与升级](../reference/compatibility.md)。以下记录描述各自发布版本;旧 UI 与 benchmark 结果不代表当前能力。 + +## v0.5 + +- [v0.5.24:完整的实体计数与按需查找的相关记忆](./v0.5.24.md) +- [v0.5.23:记忆压缩、查询与多路召回性能优化](./v0.5.23.md) +- [v0.5.22:应用内更新、会话标签页与 Ollama 上的子代理](./v0.5.22.md) +- [v0.5.21:大型 Native 记忆空间与更精简的热记忆](./v0.5.21.md) +- [v0.5.20:不再有单独的 Starter 开关](./v0.5.20.md) +- [v0.5.19:支持 DSH 0.2、桌面版管理记忆与新手安装指引](./v0.5.19.md) +- [v0.5.18:安装后直接启用与桌面 CLI 静默运行](./v0.5.18.md) +- [v0.5.17:可组合策略与组件设置](./v0.5.17.md) +- [v0.5.16:中文元数据、原生 Sidebar 与 Source 滚动](./v0.5.16.md) +- [v0.5.15:USER 工具写入、Runtime 元数据与皮肤接口](./v0.5.15.md) +- [v0.5.14:Desktop 加载、OpenViking User Key 与 Agent Teams 审查](./v0.5.14.md) +- [v0.5.13:工作区、Builtin 与 DSH 0.1.7 兼容修复](./v0.5.13.md) +- [v0.5.12:兼容 DSH RC 与 Alpha](./v0.5.12.md) +- [v0.5.11:隐藏 Windows Git 分支查询窗口](./v0.5.11.md) +- [v0.5.10:精确记忆写入与自动审阅限制](./v0.5.10.md) +- [v0.5.9:运行时归档目标与旧会话恢复](./v0.5.9.md) +- [v0.5.8:运行时归档安全与审查可靠性](./v0.5.8.md) +- [v0.5.7:DSH 0.1.5 兼容与文档归档](./v0.5.7.md) +- [v0.5.6:内置工作区存储与记忆可靠性](./v0.5.6.md) +- [v0.5.5:远程 RPC 路由与浅色素材](./v0.5.5.md) +- [v0.5.4:统一记忆空间表达](./v0.5.4.md) +- [v0.5.3:更清晰的版本维护](./v0.5.3.md) + + +## v0.4 + + + +## v0.3 + + + +## v0.2 diff --git a/dsh/docs/zh-CN/releases/v0.2.0.md b/dsh/docs/zh-CN/releases/v0.2.0.md new file mode 100644 index 000000000..ff8eb296f --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.2.0.md @@ -0,0 +1,37 @@ +# v0.2.0:可插拔的三层记忆系统 + +**简体中文** | [English](../../en/releases/v0.2.0.md) | [能力地图](../guides/capabilities.md) + +v0.2.0 将 dsh-mnemon 从 Mnemon 工作台升级为长期记忆 Provider 控制面。运行时记忆和项目档案仍由 dsh-mnemon 可靠管理;第三层记忆空间现在可以在 Mnemon、OpenViking、Honcho、Mem0、Hindsight、Holographic、RetainDB、ByteRover 与 Supermemory 之间选择。 + +## 本次升级 + +- 九种 Provider 进入同一套服务配置、记忆空间实例、激活、检索、内容、实体、沉淀和状态观察工作流; +- Provider 默认关闭,设置页只管理服务,记忆空间页管理实例; +- 支持人工指定与规则 + Prompt 驱动的智能 Provider 选择; +- AI 元信息、Agent 查询、沉淀记忆和档案归档使用无会话历史的独立顶层任务 Agent; +- 后台任务 Agent 可以跟随 DSH 新会话模型,也可以单独指定 Provider 与模型; +- 记忆空间首次进入时全量同步,之后按记忆空间按需重连,不再每 15 秒阻塞轮询; +- 状态、记忆空间、内容和检索采用并发、渐进式加载,局部失败不再阻塞整页; +- Sidebar 默认一级顺序统一为**状态、运行时、档案、记忆空间**; +- 全局、工作区与自定义范围扩展到支持该语义的本地 Provider,同时保留远程 Provider 自己的命名空间; +- 对话内“本回合记忆”和“存入记忆”继续复用原有用户心智,并能精确跳转到新的多 Provider 页面。 + +## 从 0.1.x 升级 + +1. 备份当前 Mnemon 根目录或通过设置页导出 Mnemon Pack; +2. 更新 dsh-mnemon 并重启对应 DSH profile; +3. 打开**设置 → 记忆系统**,确认展示形态、存储位置和后台任务 Agent 模型路由; +4. 需要三方 Provider 时逐个启用并保存服务配置;它们默认保持关闭; +5. 打开**记忆空间 → 概览**完成首次发现与同步,检查每个实例的作用域和激活状态; +6. 用原始检索和 Agent 查询各验证一次,再进行写入或迁移。 + +升级不会自动迁移、合并或删除 `~/.mnemon`、工作区 `.mnemon`、自定义根或远程 Provider 数据。关闭三方 Provider 只清理 dsh-mnemon 的本地目录元数据;重新连接会从 Provider 重建。 + +## 推荐基线 + +- Node.js 20 或更新版本; +- DeepSeek Harness 0.1.0-rc.6; +- 使用 Mnemon Native 时推荐 Mnemon 0.2.3 或更新版本。 + +完整操作范围见[能力地图](../guides/capabilities.md),字段和部署要求见[长期记忆 Provider](../guides/memory-providers.md),备份与故障处理见[运维指南](../guides/operations.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.2.1.md b/dsh/docs/zh-CN/releases/v0.2.1.md new file mode 100644 index 000000000..d76fe6710 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.2.1.md @@ -0,0 +1,23 @@ +# v0.2.1:运行时记忆中的字面花括号 + +**简体中文** | [English](../../en/releases/v0.2.1.md) | [能力地图](../guides/capabilities.md) + +v0.2.1 是一次聚焦 system prompt 组装稳定性的补丁发布。运行时记忆现在可以包含精确字面文本 `{{}}`,不会再导致整轮运行失败。 + +## 修复 + +- `USER.md` 或 `MEMORY.md` 中的精确字面 `{{}}` 会原样保留在组装后的 `mnemon:runtime-memory` section 中; +- 同一段记忆里的合法 prompt 变量仍会正常插值; +- 其他 system prompt section 的变量和行为保持不变。 + +该修复只将空花括号字面量通过 Mnemon 专用 prompt 变量传递。宿主插值器不会再次扫描变量替换值,因此模型最终仍会看到原始 `{{}}` 文本,同时不会放宽真实 prompt 变量引用的严格校验。 + +## 文档 + +仓库首页更新为实时多记忆空间关系图快照,并更清晰地说明不同 Provider 的可观察范围。 + +## 升级 + +更新 dsh-mnemon 后重启对应 DSH profile 即可。本补丁不会迁移、改写或删除运行时记忆、项目档案、记忆空间或 Provider 数据。 + +当前次版本引入的完整三层记忆系统能力见 [v0.2.0 发布说明](./v0.2.0.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.2.10.md b/dsh/docs/zh-CN/releases/v0.2.10.md new file mode 100644 index 000000000..6376d6058 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.2.10.md @@ -0,0 +1,31 @@ +# v0.2.10:可靠更新与子 Agent 结果交接 + +**简体中文** | [English](../../en/releases/v0.2.10.md) | [能力地图](../guides/capabilities.md) + +v0.2.10 是一次聚焦可靠性的补丁发布,覆盖可信远程管理、应用内包更新、记忆系统设置与委派记忆操作。 + +## 修复 + +- 可信远程 WebUI 现在根据 Host 授予的设置能力控制记忆写入与助手消息保存,不再根据传输位置判断。明确授权的远程管理可以正常使用,只读会话仍保持受限。 +- 应用内 dsh-mnemon 更新器现在会在实际拥有该安装的 DSH profile 内执行 `pnpm update`,避免无关进程工作目录导致 `ERR_PNPM_NO_PKG_MANIFEST`。 +- 记忆系统对话框现在可以通过设置桥保存完整的已有 `persistenceStrategy` 对象;不受支持的顶层字段仍会被拒绝。 +- 当子 Agent 遗漏 UUID 结果工具交接时,Mnemon 可以从权威的成功终态工具回执恢复受约束的结果。已经成功的召回与写入不再误报 `memory subagent completed without recording its result`。 + +## 安全与兼容性 + +- UUID 结果工具仍是首选完成通道,严格工具白名单保持不变;无关工具、失败回执、子 Agent 异常停止和外层 Code Mode 失败仍然保持失败关闭。 +- 已提交的写操作不会被重试,从而避免重复副作用。Code Mode 内部回执只有在外层 `run_code` 成功后才会被接受。 +- 存储格式、Provider 连接格式、RPC 权限、公开工具 schema 和配置键均未改变,已有数据无需迁移。 +- 更新 dsh-mnemon 并重启受影响的 DSH profile 即可。 + +## 仓库维护 + +- 双语 Issue Forms、PR 模板、贡献指南和自动贡献检查现在共同定义仓库维护流程,不改变已安装的运行时。 + +## 验证 + +- 本地完整测试套件 351 项通过;Windows 专用 smoke test 只会在非 Windows 本地环境跳过。 +- 定向覆盖可信远程控制、profile 内版本更新、持久化策略设置、Native 工具回执、Code Mode 成功与失败交接、失败关闭路径以及写操作不重试。 +- 类型检查、确定性构建、Headless 激活、发布包校验、publint、attw,以及 Ubuntu 与 Windows CI 均通过。 + +上一个补丁版本见 [v0.2.9 发布说明](./v0.2.9.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.2.11.md b/dsh/docs/zh-CN/releases/v0.2.11.md new file mode 100644 index 000000000..abb01121f --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.2.11.md @@ -0,0 +1,33 @@ +# v0.2.11:响应式且可靠的弹窗交互 + +**简体中文** | [English](../../en/releases/v0.2.11.md) | [能力地图](../guides/capabilities.md) + +v0.2.11 是一次聚焦用户界面的补丁发布,确保 Mnemon 弹窗在手机、窄侧栏和矮横屏视口中保持可用。 + +## 改进 + +- Mnemon 工作区弹窗现在统一采用固定页眉、独立滚动正文和固定操作区。创建、编辑、元数据维护、沉淀策略、沉淀记忆、运行时记忆、项目档案、版本检查、全文预览及危险操作确认均无需滚动整个弹窗即可操作。 +- 窄视口中的工作区弹窗以适配安全区的底部 Sheet 呈现;动态视口单位、横屏边距、滚动边界和 44px 触控目标覆盖手机与粗指针平板设备。 +- 危险操作确认会固定显示取消与确认按钮,较长的记忆内容、路径或档案摘要在正文区域内独立滚动。 +- 对话中的“保存到记忆”弹窗现在会受移动端和矮视口边界约束,同时保留 DSH 宿主原生外观。 + +## 无障碍与可靠性 + +- 共享工作区弹窗现在会约束键盘焦点、选择安全的初始控件、关闭后将焦点归还触发入口,并在异步内容就绪后把焦点移交给相关操作。 +- 只读加载期间仍可关闭版本检查;关闭时会使未完成请求失效,延迟返回的结果不会重新打开或更新已关闭弹窗。 +- 全文预览现在使用共享响应式弹窗契约,包含可访问标题与描述、焦点约束和归还、安全区处理以及适合触控的关闭按钮。 +- 写入操作繁忙时仍会阻止关闭和重复提交,只读加载不再被误判为不可中断的变更。 + +## 安全与兼容性 + +- 存储格式、Provider 连接、配置键、RPC 权限、公开工具 schema 和服务端行为均未改变,已有数据无需迁移。 +- 更新 dsh-mnemon 并重启受影响的 DSH profile 即可;如需回滚,正常降级包版本即可恢复此前的客户端呈现。 + +## 验证 + +- 本地完整测试套件共 38 个文件、358 项测试通过;Windows 专用 smoke test 只会在非 Windows 本地环境跳过。 +- 定向交互覆盖焦点进入与归还、焦点圈定、异步焦点迁移、关闭守卫、延迟响应丢弃、固定操作区、安全区、动态视口尺寸和触控目标。 +- 已在隔离的 DSH WebUI 中检查全部 16 个弹窗交互;版本流程包含加载态和结果态,因此共捕获 17 个移动端状态,并补充竖屏、窄屏和矮横屏前后对比。 +- 类型检查、确定性构建、Headless 激活、发布包校验、publint、attw,以及 Ubuntu 与 Windows CI 均通过。 + +上一个补丁版本见 [v0.2.10 发布说明](./v0.2.10.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.2.12.md b/dsh/docs/zh-CN/releases/v0.2.12.md new file mode 100644 index 000000000..0811b1597 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.2.12.md @@ -0,0 +1,36 @@ +# v0.2.12:顶层移动端弹层与稳定拖拽 + +**简体中文** | [English](../../en/releases/v0.2.12.md) | [能力地图](../guides/capabilities.md) + +v0.2.12 是 v0.2.11 的聚焦修复版本,解决真实手机上的弹窗遮挡,让移动端 Sheet 动画保持连续,并把顶部把手升级为可靠的下拉关闭控件。 + +## 修复 + +- 全部 15 个 Mnemon 工作区操作弹窗现在统一使用共享 `MnemonDialog` 组件,并通过页面 body 顶层 portal 渲染。DSH 导航栏及其他宿主堆叠上下文不再覆盖 Sheet 边缘或拦截其中的控件。 +- 移动端 Sheet 提供真实的 28px 顶部拖拽区。下拉超过距离阈值或完成明确的向下甩动时关闭弹窗,较短手势则平滑回到原位。 +- 页眉关闭、遮罩关闭、Escape、底部取消和拖拽关闭共用同一条协调退出路径,只有可见退出动画完成后才移除弹窗状态。 +- 长表单继续保持固定页眉与操作区,正文可在竖屏、窄视口和矮横屏中独立滚动。 + +## 动效与可靠性 + +- 进入、退出和回弹只使用适合合成器处理的 `transform` 与 `opacity` 动画。关闭或第二次拖拽会从当前可见位置继续,避免手势与动画状态切换时跳变。 +- 全局 pointer 跟踪确保手指离开把手后仍能完成手势;丢失 pointer capture、pointer 取消、窗口失焦、应用切换和页面可见性变化都会恢复到稳定静止状态。 +- 新拖拽会使旧回弹清理失效,因此快速再次抓取 Sheet 时不会被前一次延迟完成的动画打断。 +- 弹窗打开期间锁定背景滚动,并在所有关闭路径后恢复。繁忙写入仍阻止关闭;减少动态效果偏好和不支持 Web Animations 的浏览器使用安全的即时回退。 +- 现有焦点圈定、初始焦点、异步内容就绪后的焦点移交和关闭后的焦点归还继续由共享组件统一处理。 + +## 安全与兼容性 + +- 存储格式、Provider 连接、配置键、RPC 权限、公开工具 schema、凭据和服务端行为均未改变,已有数据无需迁移。 +- 手势只在移动端 Sheet 布局的顶部把手生效,弹窗正文仍可原生纵向滚动,桌面弹窗行为保持不变。 +- 更新 dsh-mnemon 并重启受影响的 DSH profile 即可;如需回滚,正常降级包版本即可恢复 v0.2.11 的客户端行为。 + +## 验证 + +- 本地完整验证共 369 项测试通过;Windows 专用 smoke test 只会在非 Windows 本地环境跳过。 +- 8 项定向弹窗回归覆盖 body portal 与滚动锁、动画关闭时序、距离关闭、短拖拽回弹、失焦恢复、快速二次抓取隔离、busy 守卫和减少动态效果回退。 +- 结构检查覆盖最高顶层、全局 pointer 监听、粗指针尺寸和全部共享弹窗调用点。 +- 已在 390×844 和 667×375 的真实浏览器交互中检查展开的宿主导航栏、左边缘命中、竖横屏滚动、拖拽关闭、回弹、快速再次抓取、底部取消和版本检查状态。 +- 类型检查、确定性构建、Headless 激活、发布包校验、publint、attw,以及 Ubuntu 与 Windows CI 均通过。 + +上一个补丁版本见 [v0.2.11 发布说明](./v0.2.11.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.2.13.md b/dsh/docs/zh-CN/releases/v0.2.13.md new file mode 100644 index 000000000..443913e17 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.2.13.md @@ -0,0 +1,28 @@ +# v0.2.13:DSH rc.8 兼容升级 + +**简体中文** | [English](../../en/releases/v0.2.13.md) | [能力地图](../guides/capabilities.md) + +v0.2.13 完成 dsh-mnemon 对 DSH 0.1.0-rc.8 的兼容验证,并接入更完整的子 Agent 失败诊断;Mnemon 数据、配置与公开工具保持不变。 + +## 更新 + +- 开发与发布依赖统一锁定为精确的 DSH 0.1.0-rc.8 包族。客户端 peer 范围继续接受 rc.6 及后续兼容的 0.1 版本,rc.8 则作为推荐且完整验证的基线。 +- 本地或远端子任务失败时,优先使用 rc.8 新增的有界 Provider 诊断。诊断文本仍经过既有的空白折叠、长度限制与凭据脱敏边界,之后才进入面向操作者的错误信息。 +- 旧版本地子任务继续使用事件历史回退,因此本次兼容升级不强制运行时必须提供新的诊断字段。 +- 快速开始补充了 rc.8 尚未进入 npm `latest` 期间通过 `@deepseek-ai/dsh@next` 安装的说明。 + +## 安全与兼容性 + +- Runtime、Documents、Memory Spaces、Provider 数据、配置键、RPC 权限与公开工具 schema 均未改变,已有 Mnemon 数据无需迁移。 +- DSH rc.8 的 SQLite 不兼容调整只涉及可选的 `@deepseek-ai/dsh-session-persistence-sqlite` 会话后端。内置 profile 默认不启用,dsh-mnemon 也使用独立存储根;只有手工挂载过该后端的部署需要备份并重建对应的 DSH 会话数据库。 +- rc.8 的多模态输入、文件/会话引用、命名 Codex 实例与 Windows 持久 PowerShell 会话属于 Host 能力。dsh-mnemon 现有界面继续工作,也不会扩大子 Agent 工具权限。 +- 通过 `next` 标签更新 DSH、更新 dsh-mnemon,并重启受影响的 DSH profile。本版本不执行数据迁移,因此仍可正常降级插件包。 + +## 验证 + +- 本地完整验证共 370 项测试通过;Windows 专用 smoke test 只会在非 Windows 本地环境跳过。 +- 真实隔离的 DSH 0.1.0-rc.8 Web profile 可加载插件,显示全部 8 个 Provider 适配器,正常渲染状态页和设置页,浏览器控制台无错误。 +- 类型检查、确定性构建、Headless 激活、发布包校验、publint、attw 与冻结锁文件安装均通过。 +- rc.8 UI primitives 包会产生仅限开发期的 source map 缺失警告;已发布 JavaScript、类型、构建与运行行为不受影响。 + +上一个补丁版本见 [v0.2.12 发布说明](./v0.2.12.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.2.14.md b/dsh/docs/zh-CN/releases/v0.2.14.md new file mode 100644 index 000000000..2dc9c6ff6 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.2.14.md @@ -0,0 +1,27 @@ +# v0.2.14:透明皮肤下清晰可读的侧边栏 + +**简体中文** | [English](../../en/releases/v0.2.14.md) | [能力地图](../guides/capabilities.md) + +v0.2.14 是一次聚焦 Web UI 的补丁发布:当 DSH 皮肤把基础背景设为透明时,Mnemon 侧边栏工作台仍能保持清晰可读。 + +## 修复 + +- Mnemon 挂载工作区、共享视图表面与侧边栏专用界面现在优先使用 `--dsw-alias-bg-overlay`;主题未提供该令牌时,继续回退到 `--dsw-alias-bg-base`。 +- 回退链统一覆盖侧边栏外壳、页眉、导航、页面表面、弹窗、底部操作区、画布与更新提示,避免背后的对话内容和皮肤全屏角色图层透显到工作台中。 +- 默认主题未定义 overlay 令牌时仍使用原有基础表面,因此默认外观保持不变。 + +## 安全与兼容性 + +- 本次只修改 Client CSS。Host 行为、运行时记忆、项目档案、记忆空间、Provider、配置、RPC 权限、存储格式、凭据与已有数据均不变。 +- maid-atelier 等皮肤仍可保留透明的对话画布,同时通过自身的高不透明 overlay 颜色保证 Mnemon 可读;插件没有引入任何皮肤专用选择器或依赖。 +- 更新 dsh-mnemon 后重启受影响的 DSH profile 即可。如需回滚,可正常降级到 v0.2.13,无需回滚数据。 + +## 验证 + +- 两个真实隔离的 DSH 0.1.0-rc.8 Web profile 分别加载 v0.2.13 与本版本,并共同启用 maid-atelier 固定提交 `d0419da`;同视口截图确认工作台由完全透明恢复为高不透明表面,浅色与暗色模式均清晰可读。 +- 本地完整验证共 371 项测试通过;Windows 专用 smoke test 只会在非 Windows 本地环境跳过。 +- 定向 CSS 测试同时检查源码与构建产物中的 overlay → base 回退链,并防止侧边栏重新出现只使用透明 base 的表面。 +- 类型检查、确定性构建、Headless 激活、发布包校验、publint、attw,以及 Ubuntu 与 Windows CI 均通过。 +- 本版本修复 [issue #56](https://github.com/omdsh-dev/dsh-mnemon/issues/56)。 + +上一个补丁版本见 [v0.2.13 发布说明](./v0.2.13.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.2.15.md b/dsh/docs/zh-CN/releases/v0.2.15.md new file mode 100644 index 000000000..9b75cd9e5 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.2.15.md @@ -0,0 +1,38 @@ +# v0.2.15:DSH rc.2 兼容与更稳健的任务 Agent + +**简体中文** | [English](../../en/releases/v0.2.15.md) | [能力地图](../guides/capabilities.md) + +v0.2.15 完成对 DSH 0.1.1-rc.2 的验证,让多模态 Host 内容安全经过现有生命周期而不存储图片字节,并包含 v0.2.14 之后合并的任务 Agent 与高级布局修复。 + +## DSH 0.1.1-rc.2 兼容 + +- 10 个直接 `@deepseek-ai/dsh*` 开发依赖均精确固定到 0.1.1-rc.2。lockfile 与官方发布依赖图一致:未改动的内部包会有意保留在 rc.1,且不包含更旧的 DSH 预发布版本线。 +- 对外的 UI-primitives peer 范围继续接受兼容的 0.1.0 Host,也接受从 rc.1 开始的 0.1.1 预发布版本线(包括 rc.2)。 +- 实时模型目录现在保留 DSH `inputModalities`。设置页会把第一方 `deepseek-official/deepseek-v4-flash-vision-exp` 路由标记为**图片输入**。 +- 生命周期处理接受 DSH 持久图片 attachment 引用,追加指引时保留所有原始内容块,活动阈值只计算文本。Mnemon 的后台元信息、召回、写入、沉淀、选择与归档任务仍为纯文本。 +- DSH rc.2 优先复用 DeepSeek Files API 上传,并根据选定模型规范化图片尺寸与格式。这些上传仍由 DSH 管理;dsh-mnemon 不会把原始图片字节复制进运行时记忆、项目档案或记忆空间。 + +## 修复 + +- 配置的后台任务 Agent 模型路由现在会传到 coordinator 的每一次委托,包括空闲复盘、召回、写入、问答、Provider 选择、迁移、USER 压缩、档案归档和元信息维护。 +- 元信息维护会跳过标题或描述不满足本地长度边界的模型候选;同一结果中的有效候选仍会应用,被省略的记忆空间保留现有元信息,而意外或重复的标识仍会直接失败。 +- 侧边栏入口发现和工作区接管现在同时识别 DSH 高级模式 frame class 与经典 pane 布局。 + +## 安全与升级 + +- Mnemon 持久化格式、RPC 权限、凭据处理、公开工具 schema 和数据迁移均无变化。 +- 发布包保持 v0.2 表面:只包含根插件、Client wrapper 与 package metadata,不包含 v0.3 kernel、topology、workspace package 或 SDK 入口。 +- 发布包继续为较旧且兼容的 DSH Host 保留 Node.js `>=20` engine。完整 DSH 0.1.1-rc.2 profile 需要 Node.js `^22.19.0 || >=24.0.0`,因为 Host 使用 `Promise.withResolvers` 和 Node Zstd API。 +- 可选的 DSH SQLite 会话后端与 Mnemon 存储相互独立,且使用 schema 17。手工启用它的部署需要遵循上游备份与重建要求;内置 profile 默认不启用。 +- 更新 DSH 与 dsh-mnemon 后,重启每个受影响的 profile。降级 package 不需要回滚 Mnemon 数据。 + +## 验证 + +- 完整 `pnpm run verify` 链共 380 项测试通过;Windows 专用 smoke test 只在本地非 Windows 环境跳过。 +- 双构建的 65 个生成文件完全一致;隔离 Headless 激活暴露 35 个总工具,其中 5 个代表性 Mnemon 工具全部存在。 +- 发布包校验共 72 个文件,只保留 v0.2 根入口和 Client 代码入口;publint strict 与 attw 通过。 +- 发布前检查 frozen lockfile 安装和 package version/tag 一致性。 +- GitHub CI 覆盖 Ubuntu Node.js 22.19 与 24,以及 Windows Node.js 24。 +- DSH UI primitives 只产生已知的开发期缺失 source map 警告。 + +上一个补丁版本见 [v0.2.14 发布说明](./v0.2.14.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.2.16.md b/dsh/docs/zh-CN/releases/v0.2.16.md new file mode 100644 index 000000000..622b04fa7 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.2.16.md @@ -0,0 +1,30 @@ +# v0.2.16:原子化运行时记忆容量维护 + +**简体中文** | [English](../../en/releases/v0.2.16.md) | [能力地图](../guides/capabilities.md) + +v0.2.16 让运行时记忆容量恢复变得完整且原子化。由 `add`、`replace` 或 `remove` 引起的超限,现在可以在归档或本地压缩已审查条目的同时应用原始操作,不会暴露部分完成的结果,也不会接受未经验证的归档。 + +## 修复 + +- 容量维护现在覆盖已有的三种运行时变更操作。MEMORY.md 使用 32,768 token 预算的归档 worker;USER.md 始终走严格的本地压缩路径,绝不会发送到记忆空间。 +- Host 从精确文件 revision 生成维护计划,再在同一文件锁内一次性提交压缩结果和原始变更。并发写入、非法结果或最终仍超限时,运行时 JSON、MEMORY.md 与 USER.md 都会与操作前保持逐字节一致。 +- `replace` 或 `remove` 会在归档和压缩前排除匹配到的废弃条目。待添加或待替换的新内容不参与压缩,因此旧内容不会被归档或重新引入,新内容也不会被重复写入。 +- MEMORY.md 迁移必须把每个从 1 开始编号的已提交源条目恰好映射一次,且目标集合必须与返回的记忆空间 id 集合完全一致。每个目标都必须处于激活状态、使用已启用且支持 remember 的 Provider,并有成功的 `mnemon_remember` 回执或真实 `mnemon_recall` 证据。 +- 失败、等待、排队或其他暂态 Provider 回执会被拒绝。Code Mode 内部产生的结果,只有在外层运行成功后才计为有效。 + +## 兼容性与数据安全 + +- 持久化格式、RPC 权限、凭据处理、公开工具 schema 与外部变更 action 均无变化。`planMaintenance` 和 `compactAndMutate` 只是 Host 内部协调方法;公开的运行时操作仍是 `add`、`replace` 与 `remove`。 +- 现有 schema version 1 运行时文件与记忆空间无需迁移。更新 dsh-mnemon 后重启受影响的 DSH profile 即可;降级 package 不需要回滚数据。 +- 本版本关闭了“预先压缩已落盘、随后旧变更重试失败”的数据丢失窗口,其中包括压缩后 `oldText` 已消失而导致替换失败的实际情形。 + +## 验证 + +- 完整 `pnpm run verify` 链共 393 项测试通过;Windows 专用 smoke test 只在本地非 Windows 环境跳过。同一发布树在 Node.js 22.19、24 与 25 上全部通过。 +- 回归测试覆盖真实 controller 的 add、replace、remove 超限恢复,旧版已超限文件的 remove,废弃条目排除,USER.md 隐私,迁移源精确覆盖,非法、未激活、已禁用和不可写目标,虚假或暂态回执,重复或重新引入内容的压缩结果,跨 controller revision 冲突,最终仍超限,以及每种被拒绝事务后的文件逐字节一致性。 +- 双构建的 65 个生成文件完全一致;隔离 Headless 激活暴露 35 个总工具,其中 5 个代表性 Mnemon 工具全部存在。发布包校验共 72 个文件;publint strict 与 attw 通过。 +- GitHub CI 在 Ubuntu Node.js 22.19 与 24、Windows Node.js 24 上全部通过,其中包括 Mnemon 发行包下载与默认发现 smoke test。 + +最初的容量修复由 [Zn-Dk](https://github.com/Zn-Dk) 在 [PR #67](https://github.com/omdsh-dev/dsh-mnemon/pull/67) 中贡献;合并时追加了维护者的原子性与证据校验加固,并保留了双方各自的提交。 + +上一个补丁版本见 [v0.2.15 发布说明](./v0.2.15.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.2.2.md b/dsh/docs/zh-CN/releases/v0.2.2.md new file mode 100644 index 000000000..a9efb2282 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.2.2.md @@ -0,0 +1,26 @@ + + +# v0.2.2:远程记忆空间激活 + +**简体中文** | [English](../../en/releases/v0.2.2.md) | [能力地图](../guides/capabilities.md) + +v0.2.2 修复了远程受信 DSH 页面激活记忆空间失败的问题,并紧缩了周边 RPC 权限边界。 + +## 修复 + +- 激活或停用记忆空间现在使用专用的 trusted-host `/dsh-mnemon-activation` 控制通道,不再误走 loopback-only 通用写通道; +- 激活处理器只接受记忆空间 ID、布尔激活状态和常规 session/workspace 路由字段。元信息、Provider 配置、凭据、删除与持久记忆写入无法越过该边界; +- 只有旧 Host 明确报告新激活端点不存在时,客户端才回退到旧写端点;`403` 或业务失败不会经更宽通道重试; +- 远程 WebUI 保留激活能力,但会在发起传输前禁用编辑、删除、重连、记忆沉淀、档案/运行时 mutation、版本更新、设置、备份与 Provider 配置。 + +## 权限审计 + +- trusted-host Provider 服务目录现已脱敏,绝不返回已保存的凭据值; +- Provider 私密设置只能通过 loopback 写通道提供给本地设置编辑器; +- 消息沉淀、设置、备份导入/导出、Provider 服务控制与版本更新现在都应用同一 loopback 预检规则。 + +## 兼容与升级 + +更新 dsh-mnemon 后重启对应 DSH profile。在 loopback 连接上,浏览器客户端对 v0.2.2 之前的 Host 仍保持激活与 Provider 私密目录读取兼容,但只会在精确的端点缺失错误下回退;远程激活需要更新后的 Host 边界。本补丁不会迁移、改写或删除运行时记忆、项目档案、记忆空间或 Provider 数据。 + +上一个补丁版本见 [v0.2.1 发布说明](./v0.2.1.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.2.4.md b/dsh/docs/zh-CN/releases/v0.2.4.md new file mode 100644 index 000000000..231f696af --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.2.4.md @@ -0,0 +1,23 @@ +# v0.2.4:受信远程设置 + +**简体中文** | [English](../../en/releases/v0.2.4.md) | [能力地图](../guides/capabilities.md) + +v0.2.4 补齐了 v0.2.3 引入的受信远程管理链路,并修复「设置 → 记忆系统」空白页。 + +## 修复 + +- 以 Host 返回的可写 settings snapshot 作为浏览器管理能力的权威信号;不再仅因连接非 loopback 就将其判定为只读。 +- Provider 服务设置与 Mnemon Pack 控件使用同一组经 Host 授权的管理通道。 +- 两个设置命名空间都不可用时,页面会显示可操作的授权诊断,不再返回空内容。 + +## 安全边界 + +- `remoteAccess` 仍默认为 `read-only`,且不能通过 Web 设置修改。 +- 显式设置 `remoteAccess: trusted-host` 时,Host 会在启动时一起提升 `/dsh-mnemon-write`、`/dsh-mnemon-settings` 和 `/dsh-mnemon-pack`。 +- 部署还必须为远程页面提供身份验证,配置 DSH Connection `trustedHosts` 白名单,保持同源,并重启 Host。不要在未认证的公网端点开启远程管理。 + +## 升级 + +更新 dsh-mnemon 后重启对应 DSH profile。本补丁不会迁移、改写或删除运行时记忆、项目档案、记忆空间、Provider 数据或已保存凭据。 + +上一个已发布补丁版本见 [v0.2.2 发布说明](./v0.2.2.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.2.5.md b/dsh/docs/zh-CN/releases/v0.2.5.md new file mode 100644 index 000000000..3a0f3dc74 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.2.5.md @@ -0,0 +1,28 @@ +# v0.2.5:缓存稳定的运行时记忆 + +**简体中文** | [English](../../en/releases/v0.2.5.md) | [能力地图](../guides/capabilities.md) + +v0.2.5 通过 DSH 原生 runtime context snapshot 投射运行时记忆。更新 `USER.md` 或 `MEMORY.md` 时,不再重写 system prompt,也不会使其可复用前缀失效。 + +## 改进 + +- 全局及 Agent 级 `mnemon:runtime-memory` 贡献改用 `systemPrompt.context()`,不再使用可变的 system prompt section。 +- DSH 会在每个模型步骤前重新计算运行时记忆,并且只在投射内容发生变化时追加新的 user-role snapshot;稳定的 system prompt、工具定义和之前的会话内容仍可缓存。 +- Agent 级工作区路由继续按需解析,因此通过 `mnemon_runtime_memory` 写入的内容会在下一个模型步骤可见,而无需重建 system prompt。 + +## 兼容性 + +- 存储布局、schema 和工具契约均未改变,无需迁移数据。 +- 字面量 `{{...}}` 文本仍会跳过占位符插值。 +- 运行时记忆现在遵循 DSH 标准的 runtime context 配置与抑制行为。 + +## 验证 + +- 完整验证套件覆盖 system prompt 稳定性、runtime snapshot 刷新、花括号字面量处理和 Agent 级工作区路由。 +- 确定性构建、headless 检查、包检查以及 Ubuntu、Windows CI 均通过。 + +## 升级 + +更新 dsh-mnemon 后重启对应 DSH profile。现有运行时记忆数据保持不变。 + +上一个补丁版本见 [v0.2.4 发布说明](./v0.2.4.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.2.6.md b/dsh/docs/zh-CN/releases/v0.2.6.md new file mode 100644 index 000000000..95ba1c939 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.2.6.md @@ -0,0 +1,34 @@ +# v0.2.6:远端安全的 UI 可靠性 + +**简体中文** | [English](../../en/releases/v0.2.6.md) | [能力地图](../guides/capabilities.md) + +v0.2.6 提升记忆系统在受信任远端部署、慢 Provider 以及快速切换工作区或页面时的读取与异步交互可靠性。 + +## 修复 + +- 单独刷新记忆空间卡片改用受信任读 RPC;远端只读部署不再出现“整体同步成功、卡片更新却 `Failed to fetch`”。 +- HTTP Provider 会立即响应请求建立前已经取消的 signal;OpenViking 的回退延时也会使用规范错误拒绝。 +- Provider 设置、档案阅读器、关联记忆遍历和实体视图会忽略旧作用域或旧交互的响应,不再覆盖最新 UI 状态。 +- 环境不支持 `scrollIntoView` 时,关联记忆滚动会安全降级。 + +## 部署边界 + +- 浏览器代码保持传输中立:RPC 与部署 URL 均交给 DSH 宿主 connection,不自行拼接根相对 API、插件或静态资源路径。 +- 完整的 `/prefix/` 托管仍需 DSH 为外壳、静态资源、插件包、RPC/API 和 WebSocket 提供统一 base URL;宿主级兼容工作已记录在 Roadmap。 + +## Roadmap + +- 新增 Cordis / DSH 服务注入、热重载与 dispose、延迟注册 structured-output 工具和 `toolFilter` 隔离的契约测试方向。 +- 新增显式宿主能力声明与真实 URL 子路径部署矩阵;本版本不会通过放宽子 Agent 工具隔离规避问题。 + +## 验证 + +- 完整测试套件 330 项通过;Windows 专用 smoke test 只会在非 Windows 本地环境跳过。 +- 确定性构建、Headless 激活、包检查以及 Ubuntu、Windows CI 均通过。 +- 真实 WebUI 覆盖记忆系统状态页、记忆空间概览与卡片刷新、快速页面切换和快速实体选择,浏览器无错误或警告。 + +## 升级 + +更新 dsh-mnemon 后重启对应 DSH profile。存储布局、schema、Provider 配置和工具契约均未改变,无需迁移数据。 + +上一个补丁版本见 [v0.2.5 发布说明](./v0.2.5.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.2.7.md b/dsh/docs/zh-CN/releases/v0.2.7.md new file mode 100644 index 000000000..5f94d7cd1 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.2.7.md @@ -0,0 +1,30 @@ +# v0.2.7:隔离的子 Agent 结果通道 + +**简体中文** | [English](../../en/releases/v0.2.7.md) | [能力地图](../guides/capabilities.md) + +v0.2.7 在保持严格子 Agent 工具隔离的前提下,恢复 DSH 0.1.0-rc.6 上的监督式记忆操作。 + +## 修复 + +- 每次 Mnemon 委派运行都会注册一个名称唯一的继承结果工具,其参数严格匹配当前操作的结果 schema。 +- 该次运行专属的结果工具会显式加入现有 `toolFilter`;召回、写入、审查、Provider 选择、迁移、压缩、档案归档和元数据任务继续保留原有硬工具 allowlist。 +- 父级会校验结果 schema 和子 Agent 身份,拒绝重复或缺失的完成调用,处理嵌套 code-mode 完成,并在运行结束后注销工具和事件监听器。 +- 该实现不再依赖 DSH 延迟注入的 `structured_output` 路径;后者在子 Agent 工具过滤后可能不可达,并导致 `memory subagent stopped with error`。 + +## 兼容性 + +- 修复完全位于 dsh-mnemon 内部,不要求修改 DSH。 +- 公共工具契约、存储布局、记忆空间 schema、Provider 配置和持久化数据均未改变。 +- 问题影响共享的委派结果通道,因此本次修复覆盖全部监督式 Mnemon 操作,而不只限于 `mnemon_recall` 和 `mnemon_remember`。 + +## 验证 + +- 完整测试套件 334 项通过;Windows 专用 smoke test 只会在非 Windows 本地环境跳过。 +- 确定性构建、Headless 激活、包检查以及 Ubuntu、Windows CI 均通过。 +- 真实 WebUI 覆盖 Agent Query 与对话内 `mnemon_recall`:子 Agent 只调用获准的读取工具,随后调用唯一结果工具,并将经过校验的结构化结果返回父 Agent。 + +## 升级 + +更新 dsh-mnemon 后重启对应 DSH profile,无需迁移数据。 + +上一个补丁版本见 [v0.2.6 发布说明](./v0.2.6.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.2.8.md b/dsh/docs/zh-CN/releases/v0.2.8.md new file mode 100644 index 000000000..1ad7de26f --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.2.8.md @@ -0,0 +1,37 @@ +# v0.2.8:有界召回质量 + +**简体中文** | [English](../../en/releases/v0.2.8.md) | [能力地图](../guides/capabilities.md) + +v0.2.8 新增确定性、插件式的召回质量层,在较弱证据进入 Agent 上下文之前对其进行限制。 + +## 新增 + +- 召回新增 `strict-v1`、`balanced-v1` 和 `exhaustive-v1` 策略,并提供自定义确定性策略注册表。 +- Provider 会显式声明 score 是否为归一化相关度。未知或未声明的 score 量纲只作为补充证据,不会被不安全地套用绝对阈值。 +- 召回来源返回结构化质量统计,包括候选数、保留数、选中数、丢弃数、无 score 数和未知量纲数。 +- 质量过滤前会在有界范围内扩展候选,使严格策略能从更大的候选池中选择更强证据。 + +## 默认行为 + +- 默认使用 `strict-v1`。score 小于等于零,以及归一化 score 低于 `0.25` 的结果,会在序列化给 Agent 之前被丢弃。 +- 高置信度结果(`score >= 0.6`)在请求上限内全部可选;中等置信度结果最多四条,无 score 或未知量纲结果最多两条。 +- 即使请求额度尚未用满,召回也不会再使用较弱证据强行补满。 +- 可通过 `recallQuality` 配置阈值、候选扩展倍数、中等结果预算、未知结果预算和策略。 + +## 兼容性与调优 + +- 持久化记忆数据和记忆空间布局不变,无需迁移。 +- 本版本会有意改变默认召回选择。需要更宽松的低分补充时可使用 `balanced-v1`;需要保留原有 top-N 行为时可使用 `exhaustive-v1`。 +- 自定义策略会校验候选上限、结果资格、重复项和返回上限;不安全的自定义行为会回退到 `strict-v1`。 + +## 验证 + +- 完整测试套件 343 项通过;Windows 专用 smoke test 只会在非 Windows 本地环境跳过;Ubuntu 和 Windows CI 均通过。 +- 在隔离的 28 条记忆基准中,可回答问题的上下文减少 48.3%,无答案问题的上下文减少 60.4%。 +- 实测取舍为 Recall@10 从 100% 降至 85.4%,nDCG@10 从 0.948 变为 0.923。优先追求最大覆盖率的部署应选择或回退到更宽松的策略。 + +## 升级 + +更新 dsh-mnemon 后重启对应 DSH profile。若部署优先追求最大召回而不是有界上下文,请检查 `recallQuality` 配置。 + +上一个补丁版本见 [v0.2.7 发布说明](./v0.2.7.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.2.9.md b/dsh/docs/zh-CN/releases/v0.2.9.md new file mode 100644 index 000000000..de4c79435 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.2.9.md @@ -0,0 +1,29 @@ +# v0.2.9:运行时记忆字面量安全 + +**简体中文** | [English](../../en/releases/v0.2.9.md) | [能力地图](../guides/capabilities.md) + +v0.2.9 让运行时记忆可以安全承载任意字面 `{{...}}` 文本,并完整关闭 issue #37 报告的残余插值崩溃路径。 + +## 修复 + +- `USER.md` 和 `MEMORY.md` 中的每个 `{{` 起始定界符现在都会通过专用、非递归的 prompt 变量传递。空组、非 ASCII、含空格、合法变量名、未知变量名、相邻、嵌套和未闭合组都会原样到达模型。 +- 运行时记忆不再展开 `{{model}}` 等原本合法的宿主变量;记忆是被引用的数据,不是扩展 prompt 模板的入口。 +- Mnemon 静态 system section 与其他插件仍使用宿主的严格插值器,真实 prompt 引用的格式错误仍会明确失败。 + +## 委派运行加固 + +- 动态运行时记忆快照、召回证据、托管档案内容和 Provider 选择数据现在通过委派运行请求传递,不再拼入子 Agent 的 system persona。 +- 子 Agent persona 只保留静态职责、工具边界与完成协议。用户可控文本无法再在归档、压缩、证据回答或 Provider 选择流程中成为 system-prompt 插值点。 + +## 兼容性 + +- 存储文件、schema、工具、RPC 契约和配置均未改变,无需迁移数据。 +- 已有字面花括号会被逐字保留;更新 dsh-mnemon 并重启受影响的 DSH profile 即可。 + +## 验证 + +- 回归覆盖空组、多语言、空格、合法与未知变量名、相邻与嵌套组、转义变量名自身以及未闭合组。 +- 完整测试套件 343 项通过;Windows 专用 smoke test 只会在非 Windows 本地环境跳过。 +- 确定性构建、Headless 激活、发布包校验以及 Ubuntu、Windows CI 均通过。 + +上一个补丁版本见 [v0.2.8 发布说明](./v0.2.8.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.3.0.md b/dsh/docs/zh-CN/releases/v0.3.0.md new file mode 100644 index 000000000..73317e6fe --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.3.0.md @@ -0,0 +1,108 @@ +# v0.3.0:可组合记忆内核 + +**简体中文** | [English](../../en/releases/v0.3.0.md) | [扩展开发](../development/extensions.md) + +v0.3.0 把原有三层系统提升为可组合记忆内核,同时保持一个安装包、默认三层行为、公开工具与现有数据格式兼容。Runtime、Documents、Memory Spaces 现在是三个可启停 Layer;现有路由由 `default-three-tier` Strategy 表达,Provider 构造通过 Registry/Factory 解耦。 + +## 你需要做什么 + +| 读者 | 行动 | +|---|---| +| 已有用户 | 备份、更新、重启并核对三个默认 Layer;无需迁移数据,也无需新增 View 操作 | +| 新用户 | 第一次走通流程时保持三个默认 Layer 全部开启 | +| 运维者 | 切换范围、Layer 或扩展后,检查设置保存结果与状态页 generation | +| 扩展作者 | 只从 `dsh-mnemon/*` 公共子路径导入,并通过 `mnemonMemory` / `MemoryBoot` 注册 contribution | + +## 交付内容 + +- 新增 wire-safe Contracts、`MemoryCatalog`、原子 `MemoryTopologyManager`、`MemoryKernel`、Guard、`MemoryPlan` 与 `MemoryReceipt`。 +- Catalog、Topology 和 Guard 分别带代际边界;组件或 Guard 变化后,旧 Plan 在执行前被拒绝并要求重新规划;已接受的 Plan 会被原子 claim 一次,顺序或并发重放都不能重复数据面步骤。 +- 普通设置面只为每个 Layer 提供一个总开关。开启表示允许默认策略按需使用,不会强制 Recall;关闭同时停止上下文、模型工具、后台处理和数据面 Web/RPC。 +- Kernel 内部继续区分能力与触发来源并执行 Guard;这些约束属于权威控制面,不作为 v0.3 普通用户的细粒度配置项。 +- 新增 `MemoryBoot` 与 Cordis `mnemonMemory` 服务(`MemoryExtensionHost` 保留为 v0.3 预发布别名)。扩展可以在 Host 挂载前或运行中贡献 Layer、Adapter、Strategy、Guard 与 `MemorySource`;热卸载推动新 generation,Source readiness 以事务方式验证。 +- 设置页通过 `memory-system` RPC 读取实时描述符并动态渲染 Layer,不再依赖固定前端枚举。新发现 Layer 默认关闭;保存只写变化 Layer 的 `enabled` 布尔值。 +- 新增 Strategy manifest、权限边界和 deterministic replay 原语,为后续模型生成策略的 shadow/canary/回退流程建立制品接口。 +- 源码拆为多个 private workspace,但 npm 仍只发布 `dsh-mnemon`,并新增 `contracts`、`kernel`、`extension-sdk`、`provider-sdk`、`strategy-sdk`、默认 Layer/Strategy 等子路径。 +- Root Agent 的每个用户回合固定一个不可变、轻量的 `MemoryTurnView`。有界 `Wake` 完整保留 Runtime 投影,并只提供 Documents 与 Memory Spaces 的数量封面;同一回合的每个模型 step 使用相同 Source revision/digest。完整 Document 与 Memory Space ID 集合留在 Host,不受 Wake 预算截断。 +- Recall 与 related 读取从 root 回合派生权限(child agent 沿 `parentSession`),根据 Host 固定状态校验可选 Memory Space ID,然后直接调用 `MnemonService`。模型侧没有 View/node ID、Zoom、View capability、完整目录或已选 ID 回显,也没有第二个 Recall LLM。Recall 仍由 LLM 驱动:普通回合是 0 次 Provider 查询;首次查询后,只有 LLM 判断 evidence 不足时,才可显式提交一个实质不同的精炼查询。同查询重放,第三个查询不能到达 Provider,也不创建 child Agent。两次共享至多 6 条、每条 1,200 字符、总正文 4,800 字符的 envelope,首次准入会为恢复路径预留容量;随后另可进行一次独立有界的 Related。模型侧不暴露容易误判的语义过滤器,并丢弃低相关项、限制 medium/unknown evidence。 +- 已授权的 Mnemon Native smart search 如果在质量筛选后丢失结构化精确锚点,或聚焦查询缺少足够词面覆盖,会执行一次有界本地 keyword 恢复。恢复结果先按阈值校验、去重并排在泛化结果之前,再经过同一质量策略与输出 envelope;不会重复请求远程 Provider,也不改变 Recall 调度或模型调用次数。两次由 LLM 选择的查询各自保留一个有界 medium/unknown 准入槽,使精炼保持有效,但不会放宽共享的 6 条/4,800 字符 envelope。 +- 模型侧 Document search、Memory Space catalog 和 status 各自具有硬上限。同一 root 回合的 root、child 与并行调用共享一个 Documents 搜索槽,后续调用只返回简短的未执行提示,不再访问磁盘。Document search 使用有界汉字双字 token,使自然中文查询能够命中相关段落,而不会退化为一个无法匹配的整句 token;聚焦查询同时要求最低 token 覆盖,以过滤偶然的局部重叠。完整记录、Provider 设置、文件路径、详细统计和未启用 Provider 行继续留在 Web/RPC 控制面,不再累积到对话历史。 +- Runtime 写入继续支持本回合新提供的事实和显式保存/纠正请求;但纯读取问题不会把 Documents、Recall 或 Related 返回的证据复制、提升或摘要进热记忆,除非用户明确要求进行这种提升。 +- 兼容 Controller 与 Provider 的成功 Authority mutation 统一生成 `MemoryReceipt`,回执在回合边界合并,使下一个回合重新快照 TurnView;当前回合保持不可变。 +- MEMORY.md 容量维护只让无工具 planner 规划路由和压缩候选。Host 把目标限制为已有、active、可写的 Space,逐条精确写入原始 entry,验证 committed receipt 或完全一致的 Recall evidence,最后把 compaction 与原 add/replace/remove 作为一次 revision-bound 本地提交。待提交或正被 replace/remove 的 entry 不会作为旧来源归档;coverage、字节预算或 revision 任一无效都保留 Runtime 并失败关闭。 +- 后台审查先经过确定性的 dirty-candidate 和当前轮 no-memory admission。达到 activity 门槛但没有持久化信号的普通对话不会启动 child;简短 recall/writeback cue 每个会话至多出现一次。 + +## Benchmark 与行为验证 + +以下结果固定于 2026-08-24 的产品候选 `6066aa796326a98575e142d68883b11b09d38f13`;它与身份整理前实际运行评测的 `95a4a450e62098614cbb443d12f6d675f5e75402` 具有完全相同的文件树。正式基线为 `dsh-mnemon@0.2.16` / `e5aae00797772bee34f414820b545d21391b84c7`,运行于 DSH `0.1.1-rc.2`,真实模型为 `deepseek-v4-flash`。行为门禁属于发布证据;真实 Provider 的 token 与延迟变化是该环境下的观测结果,不是跨模型、网络和硬件的 SLA。 + +- 最终 v0.3 完整场景 27/27 完成。额外的两组稳定 Recall 门禁各运行 20 个全新隔离 session,语义与工具策略合计 40/40;没有 Runtime/Documents/Memory Space 写入,没有为 Recall 预取 Memory Space Catalog,也没有 child Agent。 +- Recall 仍由 LLM 决定是否调用:无须持久历史的 steady-state 为 16/16 且 0 次 memory tool;误导措辞组仅 1/20 由 LLM 发起一次实质不同的 refinement,第三个 query 仍受 Host 上限阻止。 + +| 场景 | 正确性 | v0.2.16 → v0.3 token 中位 | 用户 wall 中位 | 调度变化 | +|---|---|---:|---:|---| +| Autonomous Recall | 10/10 → 10/10 | 279,077 → 99,736(-64.26%) | 86.95s → 14.87s(-82.90%) | child 13 → 0 | +| Isolated Recall | 5/5 → 5/5 | 119,352 → 22,609(-81.06%) | 47.68s → 8.59s(-81.99%) | child 9 → 0 | +| Capacity maintenance | baseline 完成 2/2;v0.3 3/3 | 137,879 → 37,275(-72.97%) | 58.88s → 28.27s(-51.98%) | child 5 → 1 | +| Steady-state | 16/16 → 16/16 | 85,650 → 84,741(-1.06%) | 8.39s → 8.38s | 两版均 0 memory tool | + +| 上下文与检索 | v0.2.16 | v0.3.0 | 判读 | +|---|---:|---:|---| +| realistic corpus 首请求 | 10,198 tokens | 10,167 tokens | 固定上下文没有新增 token 税 | +| 80 Documents + 80 Spaces 首请求 | 10,200 tokens | 10,169 tokens;Wake 4,158 字符 | 标题、ID、描述和正文不随目录规模进入 Prompt | +| direct retrieval hit@1 | 41/60 | 60/60 | 长尾命中改善 | +| direct retrieval hit@6 | 45/60 | 60/60 | v0.3 的 60 个 expected 查询全部命中 | +| result 正文中位 | 304 字符 | 304 字符 | 没有通过扩大结果正文换取命中率 | +| 本地检索 P95 | 43.22ms | 62.73ms | 增加 19.51ms,是有界本地恢复的明确代价 | + +双向 canonical data 验证为 4/4,不需要迁移。另有 20 个 absent 查询,两版都可能返回候选,因此非空结果不能被当作事实存在证明。 + +上表是本次冻结结果的公开发布记录。公开的[评测 Harness 说明](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.3.0/scripts/evaluation/v0.3/README.md)记录场景、方法、安全边界与复跑命令;每次运行会把机器可读指标写入指定输出目录。原始 Provider trace、request/session event 与本机路径刻意不提交;理解公开结论或复跑 Harness 不依赖任何私有工程归档。 + +## 升级与数据兼容 + +从 v0.2.16 升级不需要迁移数据。Runtime JSON/Markdown、Documents、Memory Space registry、Provider service registry、Mnemon Native Store 与远程 Provider 数据格式均未改变。 + +TurnView、Source 权限、Turn pin 和待发布 Receipt 是 Host 运行态,不写入上述用户数据格式。现有设置、命令、RPC、WebUI 和用户操作流程不要求理解或手动管理 View;Recall 会自动使用固定的 Source 状态,不新增交互。 + +未配置 `memoryTopology` 时自动得到与旧版本等价的三层默认拓扑:三个 Layer 均启用,内部使用固定兼容策略。已有 `storageScope`、`dataDir`、Provider、召回质量、生命周期、工具和界面设置继续生效。 + +建议升级步骤: + +1. 按现有流程备份当前 Mnemon root 或导出 Mnemon Pack; +2. 更新 `dsh-mnemon` 并重启每个使用它的 DSH profile; +3. 在**设置 → 记忆系统 → 记忆层**确认三个默认 Layer; +4. 在**状态**检查 Catalog/Topology generation 和 Provider; +5. 分别打开三个 Sidebar Tab,并用一轮对话确认 Runtime 注入与按需 Recall。 + +`enabled=false` 只停止参与,不会删除或迁移数据。Sidebar Tab 保留并显示“已关闭”;重新启用会使用原目录。降级到 v0.2.16 前建议备份,并从 YAML 移除只对 v0.3.0 有意义的 Layer 开关;本版本没有改变持久数据,因此包级回退不需要反向数据迁移。v0.2.x 会忽略这些开关并恢复旧版硬编码参与行为,但 canonical data 仍可读取。 + +## 扩展作者迁移 + +- 只从 `dsh-mnemon/*` 公共子路径导入,不依赖仓库内 `src/` 或 `packages/` 路径。 +- 把组件注册交给 `mnemonMemory.register()` 返回的 disposer;不要维护进程外的第二个 Catalog。 +- 为每个参与自动投影的扩展 Layer 提供 `MemorySource`。只有必须进入每轮 Wake 且本身有界的文本才使用 `eager`;较大记忆使用 `routed`,只提供有界封面与 Host-only 路由状态。 +- Strategy 只提出 JSON-safe steps,不持有数据面句柄;使用 manifest 限权,并用 replay 覆盖禁用层、自动/手动、预算与越权案例。 +- executor 尊重 `AbortSignal`,让 Kernel 生成 `succeeded`、`partial`、`failed` 或 `cancelled` 回执。 +- 收到 stale-plan 错误后重新规划,不要重放旧步骤。 + +仓库内原有 `src/memory-system/*` 只作为源码兼容 facade;第三方应使用 npm 子路径。 + +## 安全边界与当前限制 + +- Guard 只能拒绝,Strategy 无法放宽 Host 或参与模式;Guard 集合变化会使旧 Plan 失效。 +- Cordis isolate 管理作用域和卸载,不隔离恶意 JavaScript。Layer executor、Provider Adapter 和 Strategy 必须来自受信任包。 +- 本版本不会自动执行模型刚生成的代码;只交付 manifest、权限封装、replay 和 Kernel 权威校验。 +- 任意新引擎可以先作为独立 Layer/Adapter 接入;Memory Spaces 中全新的 Provider 设置卡、动态连接 schema 与持久 registry 注册尚未泛化,九个现有 Provider 继续由内置 Catalog 管理。 +- 现有 controller 数据面保持兼容,并已统一受 Layer 参与 gate 约束;尚未全部改走持久 `MemoryPlan` 的兼容路径会把已提交 Authority 变更桥接为同一个 `MemoryReceipt` 合约,不建立第二种回执。 + +## 验证范围 + +发布验证覆盖 TypeScript、完整 Vitest、确定性双构建、所有公共子路径导入、声明解析、真实隔离 Headless profile 激活、发布包内容、publint 与 attw。新增回归重点覆盖 Catalog/Topology generation、热注册与卸载、Guard 变化、stale Plan、部分回执、三层开关的 8 种组合、描述符驱动设置页及 Sidebar 停用态。 + +- 本地完整套件 `472` 项通过;`1` 项 Windows 专用 smoke test 只在当前非 Windows 环境跳过。 +- 双构建比较 `103` 个生成文件并确认 hash 一致;10 个 Node-compatible 公共入口可直接导入,Client 入口按浏览器 wrapper 验证。 +- 隔离 Headless profile 成功激活并向模型请求暴露 35 个总工具,其中 5 个代表性 Mnemon 工具全部存在。 +- 发布包为 110 个文件,publint strict 与 attw ESM profile 均通过。DSH `0.1.1-rc.2` UI primitives 仍只产生上游缺失 source map 的开发期警告。 + +上一个版本见 [v0.2.16 发布说明](./v0.2.16.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.3.1.md b/dsh/docs/zh-CN/releases/v0.3.1.md new file mode 100644 index 000000000..4bd69bc44 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.3.1.md @@ -0,0 +1,30 @@ +# v0.3.1:实体查阅与侧边栏体验修整 + +**简体中文** | [English](../../en/releases/v0.3.1.md) | [能力地图](../guides/capabilities.md) + +v0.3.1 是 v0.3 可组合记忆版本线的一次聚焦 Web UI 补丁:大型实体集合可以更快查阅,长实体名不会再挤出行内频次,记忆系统入口与兄弟侧边栏插件对齐,同时 README 的固定徽章更新为实时公开项目指标。 + +## 更新内容 + +- 实体查阅页会随着输入实时筛选已加载的高频实体列表。匹配采用本地、忽略大小写且适配 locale 的包含判断;输入过程不会新增 RPC 或 Provider 请求,原有“查阅”提交行为保持不变,Escape 只清除实时筛选。 +- 长实体名保持单行省略,频次固定在每一行的右侧边界内;Sidebar 分页和可见结果总数使用筛选后的集合。 +- 记忆系统侧边栏入口与当前 SSH、任务板入口统一为 36px 行几何、18px 图标、固定 24px 图标盒、相同间距与激活颜色,并在折叠栏中使用一致的圆形按钮形态。 +- 中英文 README 顶部改为展示实时 npm 版本与总下载量、最新 GitHub Release、Star、许可证和对应语言的 dshfind 徽章,不再维护固定产品计数。 + +感谢 [@kiligzzz](https://github.com/kiligzzz) 在 [PR #72](https://github.com/omdsh-dev/dsh-mnemon/pull/72) 与 [PR #73](https://github.com/omdsh-dev/dsh-mnemon/pull/73) 中完成实体列表修复和实时筛选,也感谢 [@ch3n4y](https://github.com/ch3n4y) 在 [PR #76](https://github.com/omdsh-dev/dsh-mnemon/pull/76) 中完成侧边栏入口对齐。 + +## 兼容性与升级 + +本补丁只修改 Client UI 行为、CSS、测试、截图与 README 展示。Host 行为、可组合记忆内核、配置键、RPC 权限、公开工具 schema、持久化格式、Provider、凭据,以及已有 Runtime、Document 与 Memory Space 数据均不变。 + +无需数据迁移。更新 `dsh-mnemon` 后重启每个使用它的 DSH profile,再重新打开记忆系统界面即可。回滚到 `0.3.0` 不需要反向迁移。 + +## 验证 + +- 完整 TypeScript 与 Vitest 验证通过,共 473 项测试;本地非 Windows 环境只跳过一项 Windows 专用 smoke test。 +- 可复现双构建验证 103 个生成文件哈希完全一致。 +- 隔离的真实 Headless profile 成功激活,模型请求可见 35 个工具,其中包含 5 个代表性 Mnemon 工具。 +- 发布包包含 110 个文件;10 个 Node 兼容公开入口均可成功导入,publint strict 与 attw ESM profile 全部通过。 +- 发布提交通过 `git diff --check`。 + +可组合记忆架构与冻结的 Benchmark 记录见 [v0.3.0 发布说明](./v0.3.0.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.3.2.md b/dsh/docs/zh-CN/releases/v0.3.2.md new file mode 100644 index 000000000..a394fc966 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.3.2.md @@ -0,0 +1,28 @@ +# v0.3.2:确定性运行时与托管嵌入配置 + +**简体中文** | [English](../../en/releases/v0.3.2.md) | [能力地图](../guides/capabilities.md) + +v0.3.2 从三个方面加强 v0.3 可组合记忆版本线:Runtime 容量恢复改为确定且由 Host 掌控,Mnemon Native 嵌入配置无需 shell 设置即可在桌面会话中生效,不变的 Runtime Memory 协议也不再随每次变化重复进入动态上下文尾部。 + +## 更新内容 + +- Runtime 容量恢复在只有一个可写记忆空间时不再调用模型;存在多个目标时,worker 只接收有界摘录用于路由。Host 按目标批量导入未经改写的原始条目,为每个 source 校验唯一终态回执,通过 revision fence 后再按确定性规则保留热记忆并提交待处理变更。外部只完成部分写入时,本地 Runtime 原文保持不变且可安全重试。[PR #78](https://github.com/omdsh-dev/dsh-mnemon/pull/78) 由此解决 [Issue #70](https://github.com/omdsh-dev/dsh-mnemon/issues/70)。 +- Mnemon Native 设置现在可由 DSH 管理兼容 Ollama 的嵌入 Endpoint 与模型。保存值只注入 Mnemon 子进程,因此从 Finder 或 Dock 启动的 macOS 会话不再依赖 shell 文件、`launchctl` 或 LaunchAgent。真实的 `mnemon embed --status` 检查会展示服务可达性、实际模型与 Store 覆盖率。[PR #79](https://github.com/omdsh-dev/dsh-mnemon/pull/79) 由此解决 [Issue #69](https://github.com/omdsh-dev/dsh-mnemon/issues/69)。 +- 3,450 字节的不变 Runtime Memory 读写协议现在位于稳定的 system-prompt section。动态 runtime context 仍是完整替换快照,包含 revision、条目数、UTF-8 用量以及 USER.md 与 MEMORY.md 原文;移动协议和原分隔符后,每个发生变化的动态尾部减少 3,452 字节,同时没有引入脆弱的增量链。[PR #80](https://github.com/omdsh-dev/dsh-mnemon/pull/80) 完成 [Issue #40](https://github.com/omdsh-dev/dsh-mnemon/issues/40) 的安全第一阶段;上游增量上下文能力仍由保持开放的 Issue 继续跟踪。 + +## 兼容性与升级 + +Runtime、档案、记忆空间、Provider、配置、RPC 与工具均没有需要迁移的持久化格式变更。新增托管嵌入配置默认关闭;省略或禁用时,现有继承环境保持完全不变。启用后,Mnemon 会把记忆与查询正文发送到配置的 Endpoint;远程部署应使用可信 HTTPS 服务。Endpoint 输入拒绝凭据、查询字符串与片段,状态操作也不能为单次调用选择任意目标。 + +Runtime 投影继续使用最新完整快照替换,保留 resume、fork、compaction、删除与上下文裁剪语义。Provider 批量写入接口为可选能力,现有 Provider 继续回退到逐条 `remember`。更新 `dsh-mnemon` 后重启每个使用它的 DSH profile,再重新打开记忆系统界面即可。回滚到 `0.3.1` 不需要反向迁移。 + +## 验证 + +- 完整 TypeScript 与 Vitest 验证通过,共 485 项测试;本地非 Windows 环境只跳过一项 Windows 专用 smoke test。 +- 可复现双构建验证 103 个生成文件哈希完全一致。 +- 隔离的真实 Headless profile 成功激活,模型请求可见 35 个工具,其中包含 5 个代表性 Mnemon 工具。 +- 发布包包含 110 个文件,unpacked 大小为 1,673,026 字节,低于强制的 1,680,000 字节上限;10 个 Node 兼容公开入口均可成功导入,publint strict 与 attw ESM profile 全部通过。 +- 官方 Mnemon v0.2.3 Native CLI 往返验证保留高密度归档 source 原文;官方 Mnemon v0.2.5 状态集成同时确认保存的 Endpoint 会覆盖冲突的继承值,并报告实际嵌入模型。 +- 发布变更通过 `git diff --check`。 + +上一轮 Web UI 补丁见 [v0.3.1 发布说明](./v0.3.1.md);可组合记忆架构与冻结的 Benchmark 记录见 [v0.3.0 发布说明](./v0.3.0.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.3.3.md b/dsh/docs/zh-CN/releases/v0.3.3.md new file mode 100644 index 000000000..6c2c74ef2 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.3.3.md @@ -0,0 +1,31 @@ +# v0.3.3:修正 Checkpoint 子代理用量 + +**简体中文** | [English](../../en/releases/v0.3.3.md) | [能力地图](../guides/capabilities.md) + +v0.3.3 是一个聚焦 WebUI 正确性的补丁版本。通过 checkpoint 启动的子代理,其目录行现在只报告该子代理自身产生的 Provider token 用量,不再把 fork 继承的父会话已完成历史重复计入。 + +## 修复内容 + +- fork 子代理会继承父会话的持久事件前缀,而 DSH 通用 `tokenUsage` 投影按设计会统计完整日志。官方子代理下拉此前把这个累计值当作子任务本次用量,因此父会话使用 8.7K token、子代理使用 7K token 时,子代理行会错误显示 15.7K。Mnemon 现在维护一个以最新持久 `subagent/descriptor` 为起点的独立投影,同一 turn 与 step 的重复 usage sample 使用替换语义,并且只向官方子代理目录组件提供这个局部值。[PR #87](https://github.com/omdsh-dev/dsh-mnemon/pull/87) 完成了该修复。 +- 对于没有持久 descriptor 的旧子会话,兼容层继续使用官方累计值。DSH 内核、checkpoint 与 fork 行为、通用完整日志 `tokenUsage`、官方 UI 源码、导航、语言包与样式均保持不变。 + +## 文档 + +- 中英文 README 顶部现在会在已有的 package 与仓库徽章旁展示实时 dshfind 下载量,由 [PR #82](https://github.com/omdsh-dev/dsh-mnemon/pull/82) 完成。 + +## 兼容性与升级 + +Runtime、档案、记忆空间、Provider、配置、RPC、工具与持久化格式均没有需要迁移的变化。DSH profile 未组合投影服务时,局部投影保持可选;官方子代理 lineage slot 不存在时,客户端适配层不会产生作用。 + +更新 `dsh-mnemon` 后重启每个使用它的 DSH profile,再重新打开受影响的父会话即可。现有 session log 无需改写,局部用量会在读取时从持久 descriptor 开始折叠。回滚到 `0.3.2` 不需要回滚数据,但 checkpoint 子代理下拉会再次显示继承后的完整日志累计值。 + +## 验证 + +- 完整 TypeScript 与 Vitest 验证通过,共 492 项测试;本地非 Windows 环境只跳过一项 Windows 专用 smoke test。 +- 可复现双构建验证 105 个生成文件哈希完全一致。 +- 隔离的真实 Headless profile 成功激活,模型请求可见 35 个工具,其中包含 5 个代表性 Mnemon 工具。 +- 发布包包含 112 个文件,packed 大小为 371,923 字节,unpacked 大小为 1,685,781 字节,低于强制的 1,690,000 字节上限;10 个 Node 兼容公开入口均可成功导入,publint strict 与 attw ESM profile 全部通过。 +- 使用未修改的 DeepSeek Harness 官方提交 `b150a551b8d465e31e418e1b2eaf5e79bbb7d28e` 启动真实 Web 组合后,同一份 checkpoint 子代理测试数据在 Mnemon 适配前显示 15.7K token,适配后显示 7K,父会话仍保留自身 8.7K 的完整日志值。 +- 发布变更通过 `git diff --check`。 + +上一轮 Runtime 与托管嵌入配置补丁见 [v0.3.2 发布说明](./v0.3.2.md);可组合记忆架构与冻结的 Benchmark 记录见 [v0.3.0 发布说明](./v0.3.0.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.3.4.md b/dsh/docs/zh-CN/releases/v0.3.4.md new file mode 100644 index 000000000..cc8eaef08 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.3.4.md @@ -0,0 +1,33 @@ +# v0.3.4:作用域运行时记忆与容量控制 + +**简体中文** | [English](../../en/releases/v0.3.4.md) | [能力地图](../guides/capabilities.md) + +v0.3.4 明确了 Runtime Memory 跨存储根、Git 分支与容量预算的边界,同时增强多目标归档的容错,并让会话 rewind 后的引导式记忆提示能够恢复。 + +## 新增内容 + +- `mnemon.runtimeUserScope: global` 现在可将全局根中的用户档案与所选工作区或自定义根中的工作记忆组合使用。用户写入与 USER 压缩保持全局范围,项目写入、MEMORY 压缩、档案、记忆空间和 Provider 状态仍属于所选范围;默认 `storage` 模式保持原有单根行为。[PR #103](https://github.com/omdsh-dev/dsh-mnemon/pull/103) 由此解决 [Issue #77](https://github.com/omdsh-dev/dsh-mnemon/issues/77)。 +- 工作记忆条目现在可通过模型工具、RPC 与 Web 新增/编辑表单携带可选 Git `branches` 列表。每回合只在当前检出分支匹配时投影带范围条目;无范围条目、用户档案、非 Git 工作区、detached HEAD 与分支探测失败均保持原有全部可见行为。完整 JSON 与 Markdown 事实源保持可见且不变,压缩会保留范围,归档则生成 `branch:` 标签。[PR #91](https://github.com/omdsh-dev/dsh-mnemon/pull/91) 由此解决 [Issue #93](https://github.com/omdsh-dev/dsh-mnemon/issues/93)。 +- `mnemon.runtimeMemory` 现在可配置完整 `MEMORY.md`、`USER.md` 的 UTF-8 字节上限,以及 Runtime 迁移和压缩 worker 的 completion token 预算。默认值仍为 10,240 字节、4,096 字节与 8,192 token;经过校验的自定义上限由 Runtime 写入、Pack 操作和后台维护共同使用。[PR #102](https://github.com/omdsh-dev/dsh-mnemon/pull/102) 由此解决 [Issue #96](https://github.com/omdsh-dev/dsh-mnemon/issues/96)。 + +## 修复内容 + +- 当模型无法完成多目标 Runtime 归档路由时,Host 现在会把该批次确定性路由到默认可写记忆空间,不再放弃整个容量事务。调用方取消仍会在无 Provider 写入时中止,非法路由仍无法通过 Host 校验;失败与成功批次混合时,会保留第一个成功委派任务的归因,且不会重复计算迁移次数。[PR #84](https://github.com/omdsh-dev/dsh-mnemon/pull/84) 完成了该修复。 +- rewind 从模型可见 surface 移除引导式记忆提示后,下一次 first step 会重新组合该提示。没有 surface 投影的 Host 继续使用原有会话级兜底,提示文案、顺序与 guided-turn 统计保持不变。[PR #88](https://github.com/omdsh-dev/dsh-mnemon/pull/88) 完成了该修复。 + +## 兼容性与升级 + +本次升级不需要持久化迁移。现有配置保持 v0.3.3 行为:用户档案继续跟随 `storageScope`,Runtime 条目只有显式设置后才按分支过滤,容量与 worker 上限也只有显式覆盖后才变化。启用全局档案叠加时,两个根都保留完整的现有 JSON 与 Markdown 数据;切换设置不会复制、合并或删除条目。工作区 Mnemon Pack 会刻意排除独立的全局用户档案,因此使用此模式时应单独备份全局根。 + +更新 `dsh-mnemon` 后重启每个使用它的 DSH profile,再重新打开记忆系统界面即可。回滚到 `0.3.3` 无需反向迁移;旧版本会忽略新增配置键并保留磁盘数据,但分支范围条目会暂时在所有分支可见,恢复 v0.3.4 后重新生效。 + +## 验证 + +- 完整 TypeScript 与 Vitest 验证通过,共 522 项测试;本地非 Windows 环境只跳过一项 Windows 专用 smoke test。 +- 可复现双构建验证 106 个生成文件哈希完全一致。 +- 隔离的真实 Headless profile 成功激活,模型请求可见 35 个工具,其中包含 5 个代表性 Mnemon 工具。 +- 发布包包含 113 个文件,packed 大小为 378,158 字节,unpacked 大小为 1,713,376 字节,低于强制的 1,720,000 字节上限;10 个 Node 兼容公开入口均可成功导入,publint strict 与 attw ESM profile 全部通过。 +- 定向回归覆盖双根 authority 与 Pack 排除、分支投影与范围保留、自定义容量校验、路由兜底与取消,以及 rewind 后提示恢复。PR #102 与 #103 保留的隔离 Web 证据确认了配置后容量显示和全局档案范围选择。 +- 发布变更通过 `git diff --check`。 + +上一轮 checkpoint 子代理用量补丁见 [v0.3.3 发布说明](./v0.3.3.md);可组合记忆架构与冻结的 Benchmark 记录见 [v0.3.0 发布说明](./v0.3.0.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.3.5.md b/dsh/docs/zh-CN/releases/v0.3.5.md new file mode 100644 index 000000000..3c159ed7d --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.3.5.md @@ -0,0 +1,29 @@ +# v0.3.5:双版本 DSH Host 兼容 + +**简体中文** | [English](../../en/releases/v0.3.5.md) | [能力地图](../guides/capabilities.md) + +v0.3.5 在保留 DSH 0.1.1-rc.2 可复现 npm 基线的同时,新增对 GitHub-only DSH 0.1.2-alpha.1 预览版的源码兼容。同一个插件构建现在无需解析版本、检查函数参数数量或按 capability 分支,即可跨越两代 Host。 + +## 兼容性 + +- Web client 适配 alpha 拆分后的职责:Session 与 Workspace 状态继续由 controller/renderer 服务提供,renderer 接管 slots,Chat 专用 slot contract 不再依赖已移除的 owner type,Workspace 选择不再依赖 `recentWorkspaceId`,第三方 locale ID 也可安全透传。 +- package 不再直接导入已移除的 `@deepseek-ai/dsh-client-runtime` 入口;DSH client 顺序依赖改为 `@deepseek-ai/dsh-client-ui-renderer`,同时保持已发布 rc.2 依赖图可复现。 +- 通用 Mnemon Connection channel 继续兼容 alpha 的 ApiProxy 到 Remote/gateway 迁移,child-local 子代理 token 用量投影也继续与 DSH 完整日志指标隔离。 +- Host RPC 注册统一使用一种无条件调用形态:Mnemon 始终传入 rc.2 所需的末尾 authority 对象;alpha 的双参数 JavaScript 实现会忽略额外参数,并应用统一认证的浏览器会话边界。 + +## 安全与升级 + +本次升级不需要迁移持久化数据或 Provider 数据。DSH 0.1.1-rc.2 仍会使用仅启动时读取的 `remoteAccess`:管理通道默认保持 `loopback`,只有显式配置为 `trusted-host` 时才会整体提升。DSH 0.1.2-alpha.1 会忽略该 transport 参数,并要求所有通道通过启动 token 建立的浏览器会话。保留这一设置,使同一配置既能前进到 alpha,也能回滚到 rc.2,同时不削弱 rc.2 的默认安全边界。 + +更新 `dsh-mnemon` 后,重启每个使用它的 DSH profile,再重新打开记忆系统界面。Registry 安装应继续使用 DSH 0.1.1-rc.2;alpha 只是源码验证目标,不是 npm 安装目标。回滚 dsh-mnemon 到 0.3.4 不需要数据转换。 + +## 验证 + +- 完整 TypeScript 与 Vitest 验证通过,共 524 项测试;本地非 Windows 环境只跳过一项 Windows 专用 smoke test。 +- 真实 Connection 兼容测试会对已安装 rc.2 实现与源码链接的 alpha 实现注册同一个三参数调用。 +- CI 在 Node.js 22.19 和 24 上验证 Linux,在 Node.js 24 上验证 Windows,并通过独立 Node.js 24 任务链接上游 `dsh-v0.1.2-alpha.1` tag 的精确提交 `cd5ef814`。 +- 可复现双构建验证 106 个生成文件哈希完全一致;隔离的真实 Headless profile 成功激活,模型请求可见 35 个工具,其中包含 5 个代表性 Mnemon 工具。 +- 发布包包含 113 个文件,packed 大小为 378,527 字节,unpacked 大小为 1,714,783 字节;10 个 Node 兼容公开入口均可成功导入,publint strict 与 attw ESM profile 全部通过。 +- 发布变更通过 `git diff --check`。 + +兼容实现与证据见 [PR #105](https://github.com/omdsh-dev/dsh-mnemon/pull/105);上一版 Runtime Memory 范围与容量发布见 [v0.3.4 发布说明](./v0.3.4.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.3.6.md b/dsh/docs/zh-CN/releases/v0.3.6.md new file mode 100644 index 000000000..0a8fe0366 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.3.6.md @@ -0,0 +1,32 @@ +# v0.3.6:可靠的异步 Recall 与 Web 工作流 + +**简体中文** | [English](../../en/releases/v0.3.6.md) | [能力地图](../guides/capabilities.md) + +v0.3.6 完成异步子 Agent Recall 生命周期修复,并汇总 v0.3.5 以来已合并的 Web 导航、CLI 发现、主题、Runtime Memory 和托管 embedding 改进。已发布 DSH 基线仍为 0.1.1-rc.2,同一插件同时验证仅提供源码的 0.1.2-alpha.1 预览版。 + +## 主要变化 + +- **稳定的异步 Recall。** 子任务保留创建时捕获的 View 和运行时授权,不受父任务结束、父任务后续回合、View 回收或设置切换影响。每个执行回合拥有自己的 Recall、Related 和 Documents 预算与缓存;回合内并发调用共享限制,兄弟任务和冷恢复 activation 不串用其他任务的证据或预算。缓存重放仍严格限制在请求的 Memory Space 子集中。[#130](https://github.com/omdsh-dev/dsh-mnemon/pull/130) 完成了 [#116](https://github.com/omdsh-dev/dsh-mnemon/pull/116) 的部分修复。 +- **由插件自身持有 Runtime Memory 快照。** Mnemon 使用归属于插件自己的消息提供变化后的运行时快照,不再放入 Host 共用的 runtime-context 投影。记忆变化不再导致其他插件的稳定上下文片段被重复发送。[#107](https://github.com/omdsh-dev/dsh-mnemon/pull/107) +- **可靠的记忆系统导航与 CLI 检查。** Panel snapshot 方法在新版 DSH client 下保留正确的 store receiver;记忆系统与其他面板之间的切换以 Host 实际可见状态为准。配置为命令名的 CLI 会通过 PATH 解析,使状态检查和版本检查保持一致。[#113](https://github.com/omdsh-dev/dsh-mnemon/pull/113)、[#127](https://github.com/omdsh-dev/dsh-mnemon/pull/127) +- **一致的默认主题与弹窗。** 记忆系统画布遵循 Host 官方明暗背景;共用弹窗使用 layer-2 表面并提供画布底层,透明页脚避免半透明主题重复叠色。[#126](https://github.com/omdsh-dev/dsh-mnemon/pull/126)、[#129](https://github.com/omdsh-dev/dsh-mnemon/pull/129) +- **托管 embedding 凭据与协议选择。** Native embedding 设置支持可选 API key,以及 `auto`、`ollama` 或 `openai` 协议覆盖,包括没有 `/v1` 后缀的 OpenAI-compatible endpoint。关闭覆盖时不改变现有 Host 环境。[#117](https://github.com/omdsh-dev/dsh-mnemon/pull/117) +- **可复现验证。** alpha 任务在 pnpm 运行前检查期间保留显式源码链接;新增真实 DSH Agent loop 与 JSONL 冷恢复测试覆盖异步生命周期。发布包体积检查容纳新增 embedding 与委托回合实现,测试夹具仍不进入 npm 包。[#121](https://github.com/omdsh-dev/dsh-mnemon/pull/121)、[#130](https://github.com/omdsh-dev/dsh-mnemon/pull/130) + +## 安全与升级 + +无需迁移 Runtime、Documents、Memory Space 或 Provider 数据。既有 embedding 配置继续有效,API key 与协议覆盖均为可选。保存的密钥属于 DSH 设置,不应复制到发布日志、截图或记忆内容。模型 Recall 缺少有效执行回合 pin 时会被拒绝,不再继承父任务的最新 View。 + +已经运行的子任务会保留原运行时,直到该 activation 销毁;停用旧数据位置前,应等待这些任务完成或取消它们。更新 `dsh-mnemon` 后,重启所有使用它的 DSH profile,并重新打开记忆系统。Registry 安装继续使用 DSH 0.1.1-rc.2;alpha 是源码验证目标,不是 npm 安装目标。 + +回滚到 0.3.5 不需要数据转换,但会恢复此前的 Recall 与 Web 行为,并失去本版 embedding 密钥和协议控制。依赖新覆盖项之前,请保留设置备份。 + +## 验证 + +- 本地 Node.js 24 完整验证通过 574 项测试,在 macOS 上仅跳过一项 Windows 专用测试。异步修复新增 29 项回归用例,包含真实 DSH Agent loop、工具流水线和 JSONL 冷恢复。 +- CI 覆盖 Linux Node.js 22.19 与 24、Windows Node.js 24,以及精确源码链接的 `dsh-v0.1.2-alpha.1` tag。已合并实现在准备发布前通过全部四项任务。 +- 可复现双构建验证 107 个生成文件;隔离的真实 Headless profile 暴露 35 个工具,其中包含 5 个代表性 Mnemon 工具。发布包内容检查、10 个 Node-compatible 公开入口导入、publint strict 与 attw ESM profile 全部通过。 +- 保留既有真实 npm WebUI 的[导航与 CLI 前后对照](../../pr-assets/npm-sidebar-cli/README.md#简体中文)及[弹窗表面验证](../../pr-assets/dialog-surfaces/README.md)。这些记录标明了精确的发布前补丁构建,不会被重新标注为本版发布截图。 +- 发布变更通过 `git diff --check`;不提交生成的 `lib/` 或测试专用数据。 + +上一版双 Host 兼容发布见 [v0.3.5 发布说明](./v0.3.5.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.4.0.md b/dsh/docs/zh-CN/releases/v0.4.0.md new file mode 100644 index 000000000..0f0088eed --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.4.0.md @@ -0,0 +1,37 @@ +# v0.4.0:Sidebar 唯一入口的记忆系统 + +**简体中文** | [English](../../en/releases/v0.4.0.md) | [能力地图](../guides/capabilities.md) + +v0.4.0 将 Sidebar 设为记忆系统的唯一工作台入口,移除 builtin 展示模式和展示形态配置,保留 v0.3 架构与记忆数据格式。完整的 view-based 升级独立计划于 v0.5 发布,不包含在本版中。 + +## 主要变化 + +- **唯一工作台入口。** 记忆系统只从 DSH 左侧栏打开,不再注册 `conversation.view` 内嵌标签页;保留既有 Sidebar 页面、导航、弹窗与工作区选择。[#128](https://github.com/omdsh-dev/dsh-mnemon/pull/128) +- **移除展示形态选择。** 删除展示模式配置、中英文文案,以及 builtin 专用的外观、导航、内联编辑和样式分支,统一使用精简的 Sidebar 展示。 +- **保留独立的实时显示开关。** `tabEnabled=false` 实时隐藏 Sidebar 入口和工作台,重新开启后恢复 Sidebar。Host RPC、命令和 Agent 工具始终保持注册;对话内的本回合记忆和存入记忆仍由 `mnemon-ui` 独立控制。 +- **保留 v0.3.6 修复。** 异步 Recall 委托、回合独立预算、Runtime Memory 快照归属、面板导航、CLI 发现与 Host 主题弹窗修复继续有效。本版不引入新的 view-based 核心、SDK 或独立插件架构。 + +## 升级与兼容性 + +这是明确的展示行为与配置兼容性变化,不是数据格式迁移。 + +1. **builtin 用户切换到 Sidebar。** 旧配置值的实际拼写为 `buildin`。包含 `displayMode` 的既有 YAML 和用户设置仍可加载,包括该旧值;字段会被忽略,工作台统一使用 Sidebar。无需手工清理,也不会自动重写已有配置文件。 +2. **调整类型集成和设置写入方。** `Config.displayMode` 与 `ResolvedConfig.displayMode` 已移除。新的设置 RPC 更新若包含 `displayMode`,会被拒绝;请从配置对象和更新请求中移除该字段,不要将旧配置的读取兼容理解为继续支持写入。 +3. **保留记忆数据。** Runtime、Documents、Memory Space registry、Provider 数据、存储路径、凭据与权限边界保持不变,不迁移或删除记忆数据。 +4. **显式选择次版本升级。** 在所属 DSH profile 中安装 `dsh-mnemon@0.4.0`,重启使用它的 profile,再重新打开记忆系统。限制为 `^0.3.x` 的依赖不会自动选择 v0.4.0。升级前请备份设置和数据。 + +已发布 DSH 基线仍为 0.1.1-rc.2;DSH 0.1.2-alpha.1 继续作为仅提供源码的兼容性验证目标,不是 npm 安装目标。 + +## 回滚 + +在同一 profile 中重新安装 `dsh-mnemon@0.3.6` 并重启,无需转换数据。如果保留了 `displayMode: buildin`,该偏好在旧版上会重新生效;若不希望恢复 builtin,请在旧版中删除该字段或选择 Sidebar。 + +## 验证 + +- 本地 macOS / Node.js 25.1.0 的完整 `pnpm run verify` 通过 582 项测试,仅跳过一项 Windows 专用测试。可复现双构建匹配 107 个生成文件;隔离的真实 Headless 激活暴露 35 个工具,其中包含 5 个代表性 Mnemon 工具。发布包内容检查覆盖 114 个文件,10 个 Node-compatible 公开入口导入、publint strict 和 attw ESM profile 全部通过。 +- Sidebar-only 回归覆盖旧 `buildin` / `sidebar` 配置、展示形态选择器消失、实时显示开关,以及拒绝新的 `displayMode` 写入。 +- 将 v0.4.0 tarball 安装到隔离的真实 DSH Web profile,并保留旧 `displayMode: buildin`:Sidebar 打开、返回与重开,无展示选择器的设置页、Runtime 弹窗,以及 680px 布局和 Escape 关闭均通过,浏览器控制台无错误。该 UI 专用夹具使用合成数据,并刻意让原生 CLI 不可用;不宣称完成真实 Mnemon CLI 或真实模型端到端验证。 +- 已合并实现在准备发布前通过 Linux Node.js 22.19 与 24、Windows Node.js 24,以及源码链接 DSH 0.1.2-alpha.1 的 CI。 +- [Sidebar-only 设置截图](https://raw.githubusercontent.com/omdsh-dev/dsh-mnemon/2296898ddde876aa49a5dd68165d6c40c069fcd1/docs/assets/screenshots/settings-sidebar-only.png)是实现 PR 的发布前证据,不会重新标注为 npm 发布版截图;既有 Web 导航与弹窗证据继续保留原版本标记。 + +上一版:[v0.3.6](./v0.3.6.md)。后续架构工作:[对应版本 Roadmap](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.4.0/docs/zh-CN/roadmap.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.4.1.md b/dsh/docs/zh-CN/releases/v0.4.1.md new file mode 100644 index 000000000..77e605bca --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.4.1.md @@ -0,0 +1,33 @@ +# v0.4.1:可靠的设置订阅与跨时区 Pack 导出 + +**简体中文** | [English](../../en/releases/v0.4.1.md) | [能力地图](../guides/capabilities.md) + +v0.4.1 是 Sidebar 唯一入口的 v0.4 系列补丁,修复 DSH settings store 回调绑定和 Mnemon Pack ZIP 时间戳,不改变记忆架构、配置契约或数据格式。 + +## 修复 + +- **记忆系统不再因依赖 `this` 的 settings store 而崩溃。** 工作台和存入记忆操作都先以稳定回调包装 `subscribe` 与 `getSnapshot`,再传给 React;保留真实宿主 store 的 `this`,避免以未绑定方法调用。[#133](https://github.com/omdsh-dev/dsh-mnemon/pull/133)、[#136](https://github.com/omdsh-dev/dsh-mnemon/issues/136) +- **UTC 以西时区可以正常导出 Pack。** 将 ZIP 条目的本地日期字段固定为 1980-01-01,避免 UTC 起点在本地落入超出范围的 1979 年;相同数据和导出时间在不同时区生成的归档字节保持一致。[#137](https://github.com/omdsh-dev/dsh-mnemon/pull/137)、[#112](https://github.com/omdsh-dev/dsh-mnemon/issues/112) +- **回归测试覆盖真实故障形态。** 设置测试使用依赖 `this` 的类方法;Pack 测试覆盖完整包、运行时、档案和记忆空间四种导出范围,在 UTC、洛杉矶、UTC-12、UTC+14 和加尔各答比较完整归档字节。 + +## 升级与兼容性 + +在所属 DSH profile 中安装 `dsh-mnemon@0.4.1`,重启该 profile,再重新打开记忆系统。从 v0.3.x 升级的用户还需阅读 [v0.4.0 Sidebar-only 兼容性说明](./v0.4.0.md#升级与兼容性)。 + +- Runtime、Documents、Memory Space 数据、Pack manifest 与 checksum、RPC 权限、存储路径和 Provider 凭据保持不变;已有 Pack 仍可读取,不需要迁移或删除数据。 +- 仅调整 ZIP 条目的固定时间戳。UTC 导出保留原日期字段,其他时区现在也使用相同字段。 +- 已发布依赖基线仍为 DSH 0.1.1-rc.2;DSH 0.1.2-alpha.1 继续作为仅提供源码的兼容性目标。 +- 保留 v0.4.0 的 Sidebar 唯一入口工作流,不包含独立计划的 v0.5 view-based 架构升级。 + +## 回滚 + +在同一 profile 中重新安装 `dsh-mnemon@0.4.0` 并重启。无需转换数据,已有 Pack 仍可读取,但 settings store 崩溃和依赖时区的导出行为也会恢复。 + +## 验证 + +- 在 macOS arm64 / Node.js 25.1.0 / pnpm 11.19.0 上运行 `TZ=America/Los_Angeles pnpm run verify`:588 项测试通过,跳过一项 Windows 专用冒烟测试。可复现双构建匹配 107 个文件;隔离的真实 Headless 激活暴露 35 个工具,其中包含 5 个代表性 Mnemon 工具。发布包检查覆盖 114 个文件,10 个 Node-compatible 公开入口、publint strict 与 attw 全部通过。 +- 完整 Pack 套件分别在 UTC、洛杉矶、UTC-12 和 UTC+14 的独立进程中通过 16/16。 +- 将实际 v0.4.1 tarball 安装到隔离的 npm DSH 0.1.1-rc.2 Web profile,使用 Web UI 0.3.6 和经 checksum 校验的 Mnemon 0.2.5:四个记忆子页正常渲染,存入记忆弹窗可打开并取消,设置页在洛杉矶时区报告 ZIP 生成成功,浏览器控制台无错误。浏览器下载完成事件未被捕获,因此未独立验证下载文件落盘。夹具仅使用合成数据和本地固定模型回复,不调用付费模型或个人记忆。 +- 两个实现 PR 均在合并前通过 Linux Node.js 22.19 与 24、Windows Node.js 24,以及源码链接 DSH 0.1.2-alpha.1 CI。 + +上一版:[v0.4.0](./v0.4.0.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.4.2.md b/dsh/docs/zh-CN/releases/v0.4.2.md new file mode 100644 index 000000000..e35623b33 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.4.2.md @@ -0,0 +1,40 @@ +# v0.4.2:共用 Builtin 入口与规范配置 + +**简体中文** | [English](../../en/releases/v0.4.2.md) | [配置参考](../reference/configuration.md) + +v0.4.2 为 Sidebar 优先迭代的记忆工作台恢复可选会话入口,不恢复独立的旧版界面,也不包含计划中的 v0.5 view-based 架构。[#142](https://github.com/omdsh-dev/dsh-mnemon/pull/142)、[#139](https://github.com/omdsh-dev/dsh-mnemon/issues/139) + +## 变更 + +- **同一个工作台,两种展示位置。** 默认仍为 Sidebar;设置中选择 Builtin 后,在会话标签页展示相同页面、弹窗和样式,保存后实时切换,两个入口不会同时挂载。 +- **自动对应会话范围。** Builtin 隐藏范围标记、工作区选择和对齐控件。全局范围继续共享,工作区范围跟随所属会话 cwd,自定义范围使用 `dataDir`;读写与独立任务复用现有 Host 路由,全局 USER.md 例外保持不变。 +- **规范拼写统一为 `builtin`。** 历史 `buildin` 仍可输入,但运行时和界面统一为 `builtin`,并通过 DSH 带修订号保护的设置接口写回。覆盖启动、外部配置热更新和旧客户端 RPC 写入,保留注释与其他字段;并发期间用户较新的 Sidebar 选择优先。 +- **安全的生命周期。** `tabEnabled` 控制所选入口;对话快捷操作打开对应位置,切换会话清理旧数据和编辑器,不改变已存记忆。 + +## 升级与兼容性 + +在所属 DSH profile 中安装 `dsh-mnemon@0.4.2`,然后重启该 profile。如需在各会话内展示工作台: + +```yaml +mnemon: + displayMode: builtin + storageScope: workspace +``` + +- **保留的旧偏好会重新生效。** v0.4.0–v0.4.1 忽略 `displayMode: buildin`,v0.4.2 会恢复该偏好并保存为 `builtin`。希望继续使用独立入口时请选择 Sidebar;未设置展示位置时仍默认 Sidebar。 +- 仅存在于 profile 的旧值通过规范用户设置覆盖,不直接改写 profile 文件。只读设置仍识别旧拼写,但不绕过权限;落盘失败会记录日志,不关闭已归一化入口。 +- Runtime、Documents、Memory Spaces、Pack 格式、存储路径、Provider 凭据与 RPC 权限保持不变。唯一自动迁移的是展示偏好,不是记忆数据;继续包含 v0.4.1 的修复。 +- 已发布 DSH 0.1.1-rc.2 仍为依赖基线;DSH 0.1.2-alpha.1 继续作为仅提供源码的兼容性目标。 + +## 回滚 + +在同一 profile 中重新安装 `dsh-mnemon@0.4.1` 并重启;它会忽略 `displayMode` 并回到 Sidebar,无需转换数据。若回退到 v0.3.x,请先改用旧拼写 `buildin` 或 `sidebar`,这些版本不接受规范值 `builtin`。 + +## 验证 + +- v0.4.2 发布候选通过完整 `pnpm run verify`:630 项测试通过,1 项 Windows 专用测试在 macOS 跳过;108 个构建文件确定性检查、35 个 Headless 工具及 5 个代表调用、115 文件发布包、10 个 Node 兼容入口、严格 publint 与 attw 检查均通过。 +- 将实际 v0.4.2 tarball 安装到隔离的 DSH 0.1.1-rc.2 / Web-all 0.3.6 profile,并使用官方 Mnemon 0.2.5 CLI。启动后旧 `buildin` 自动保存为 `builtin`;界面显示 0.4.2,隐藏 Builtin 范围控件,实际读写根对应所属工作区。四个主页面、三个创建弹窗的打开与取消、中英文切换,以及从已安装 tarball 导入全部 10 个入口均通过。未提交记忆或组件更新;模型仅使用本地固定响应,未测试付费模型或远程 Provider。 +- 实现 PR #142 在合并前通过 Linux Node.js 22.19/24、Windows Node.js 24 和源码链接 DSH 0.1.2-alpha.1 CI。 +- [实现验证记录](../../pr-assets/builtin-139/README.md)包含未修改的 npm 0.4.1 对照版、真实 Mnemon 0.2.5 状态、三种存储范围、十轮页面往返及保持不变的记忆文件哈希;其中截图明确来自发布前本地构建,不冒充已发布的 0.4.2 包。 + +上一版:[v0.4.1](./v0.4.1.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.4.3.md b/dsh/docs/zh-CN/releases/v0.4.3.md new file mode 100644 index 000000000..8116bb781 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.4.3.md @@ -0,0 +1,30 @@ +# v0.4.3:折叠侧栏图标对齐 + +**简体中文** | [English](../../en/releases/v0.4.3.md) + +v0.4.3 修复记忆系统折叠后的侧栏图标,并保持原有展开样式。[Issue #141](https://github.com/omdsh-dev/dsh-mnemon/issues/141)、[PR #144](https://github.com/omdsh-dev/dsh-mnemon/pull/144)。 + +## 修复 + +- 原生 DSH 正确应用折叠入口的居中与隐藏文字规则,不再依赖第三方 Web UI 的 frame 属性。 +- 折叠图标使用与相邻控件一致的 primary label 颜色;SVG 画布调整为 20px,补偿图案内部留白,使可见尺寸协调。 +- 展开时保留原有 18px SVG、间距、文字和 secondary 颜色,兼容 Web UI 0.3.9。修复只修改一个 CSS 文件,没有新增 JavaScript 或状态。 + +## 升级与兼容性 + +在所属 DSH profile 中安装 `dsh-mnemon@0.4.3`,然后重启。Sidebar 与可选 Builtin 入口保持 [v0.4.2 的行为与兼容规则](./v0.4.2.md#升级与兼容性)。 + +没有新增配置或迁移。记忆数据、存储路径、Pack 格式、Provider 凭据、RPC 权限和依赖均不变。DSH 0.1.1-rc.2 仍为 registry 基线;DSH 0.1.2-alpha.1 继续作为源码兼容性目标。 + +## 验证 + +- 发布候选通过完整 `pnpm run verify`:630 项测试通过,1 项 Windows 专用测试在 macOS 跳过;108 个构建文件确定性检查、35 个 Headless 工具及 5 个代表调用、全部 10 个 Node 兼容入口、publint 与 attw 均通过。 +- 将实际 115 文件 tarball 安装到隔离的 DSH 0.1.1-rc.2 原生与 Web UI 0.3.9 profile,使用官方 Mnemon 0.2.6 CLI。版本 0.4.3、中英文四个主页面、原生浅色与深色图标、折叠居中及 20px SVG、展开时保留 18px SVG,以及从两个安装包导入全部 10 个入口均通过。未观察到浏览器控制台错误;既有依赖 source-map 和 Web UI `session/list` 警告仍存在。验证使用合成数据与本地模型桩,没有访问个人记忆或调用付费模型。 + +[实现验证记录](../../pr-assets/sidebar-icon/README.md)覆盖原生浅色与深色主题、Web UI 0.3.9、折叠与展开、页面导航及图标几何测量。截图和原始测量数据不进入 npm 包,既有包内容校验会检查发布文件白名单。 + +## 回滚 + +在同一 profile 中重新安装 `dsh-mnemon@0.4.2` 并重启,无需转换数据;折叠图标会恢复此前的外观。 + +上一版:[v0.4.2](./v0.4.2.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.4.4.md b/dsh/docs/zh-CN/releases/v0.4.4.md new file mode 100644 index 000000000..7d356e50a --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.4.4.md @@ -0,0 +1,33 @@ +# v0.4.4:DSH 会话投影接口兼容 + +**简体中文** | [English](../../en/releases/v0.4.4.md) + +v0.4.4 修复旧版 DSH 宿主读取会话历史时出现的 `Cannot read properties of undefined (reading 'parse')` 错误。[Issue #147](https://github.com/omdsh-dev/dsh-mnemon/issues/147)、[PR #149](https://github.com/omdsh-dev/dsh-mnemon/pull/149)。 + +## 修复 + +- 子 Agent 本地 token 用量投影同时提供旧版 `schema` / `view` 和新版 `stateSchema` / `wire` 接口,共用同一个 wire schema 和 view 函数。 +- 两代 registry 均支持活跃会话、脱离会话后的历史回放和检查点恢复。子 Agent 总量仍排除父级与祖先用量,同一步骤的重复样本继续替换而非累加。 +- 安全忽略格式错误的事件载荷,同时保持与 DSH 0.1.2-alpha.1 源码接口兼容。 + +## 升级与兼容性 + +在所属 DSH profile 中安装 `dsh-mnemon@0.4.4`,然后重启。DSH 0.1.1-rc.2 仍为 registry 开发基线;DSH 0.1.2-alpha.1 仍为源码兼容性目标。旧版兼容性测试使用已发布的 DSH 0.1.0-rc.8 投影 registry。 + +投影状态仍为 `stateVersion: 1`,存储格式不变,无需迁移或删除缓存。记忆数据、存储路径、Pack 格式、Provider 凭据、RPC 权限、设置和生产依赖均不变。旧版 registry 别名仅为开发测试夹具,不作为运行时依赖发布。 + +Sidebar 与可选 Builtin 入口保持 [v0.4.3 的行为](./v0.4.3.md)。本补丁不包含 UI 布局改动或 v0.5 开发内容。 + +## 验证 + +兼容性测试以真实会话运行新旧 DSH registry,覆盖空快照、活跃与脱离会话回放、重复用量样本、检查点恢复、跨代检查点、变更通知和过期状态重算。可复现命令见[开发指南](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.4.4/docs/zh-CN/development.md)。 + +- 发布候选在 Node 24.18.0 与 pnpm 10.13.1 下通过完整 `pnpm run verify`:647 项测试通过,1 项 Windows 专用测试在 macOS 跳过。108 个构建文件的确定性检查、包含 35 个工具与 5 个代表性 Mnemon 工具的 Headless 激活、设置迁移与重启、10 个 Node 兼容公开入口、publint 和 attw 均通过。 +- 使用已发布的新旧 ApiProxy 和投影 registry 完成 32 次真实 HTTP 对照请求。未修改的 v0.4.3 源码复现了旧版历史尾部请求失败;v0.4.4 通过普通与子 Agent 会话、活跃与脱离会话历史、分页对照,并保持新版宿主的投影结果正确。 +- 将包含 115 个文件的 tarball 安装到两个隔离的 DSH 0.1.1-rc.2 Web profile,分别使用原生界面和 Web UI 0.3.9,并搭配官方 Mnemon CLI 0.2.6。两次安装的全部文件均与打包产物一致,10 个 Node 入口均可导入。版本 0.4.4、中英文四个主页面、任务看板往返均通过;原生界面的合成会话在浏览器重载后仍能完整读取历史。未观察到浏览器错误;Web UI 既有的 Host `session/list` 警告仍存在。测试仅使用临时数据和本地模型桩,没有访问个人记忆或调用付费模型。 + +## 回滚 + +在同一 profile 中重新安装 `dsh-mnemon@0.4.3` 并重启,无需转换数据,但旧版 DSH 宿主可能再次遇到会话历史读取失败。 + +上一版:[v0.4.3](./v0.4.3.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.4.5.md b/dsh/docs/zh-CN/releases/v0.4.5.md new file mode 100644 index 000000000..ef6366ce0 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.4.5.md @@ -0,0 +1,29 @@ +# v0.4.5:Better Sidebar 兼容性 + +**简体中文** | [English](../../en/releases/v0.4.5.md) + +v0.4.5 让已安装 `dsh-better-sidebar` 的用户可以在其中打开记忆系统,同时保留原有的 DSH 普通 Sidebar 入口。[Issue #153](https://github.com/omdsh-dev/dsh-mnemon/issues/153)、[PR #157](https://github.com/omdsh-dev/dsh-mnemon/pull/157)。 + +## 修复 + +- 默认 `displayMode: sidebar` 下,Mnemon 现在使用 Better Sidebar 文档化的可选 `registerTab` 服务。单实例 Tab 使用稳定 id `dsh-mnemon:memory`,并与 DSH 普通 Sidebar 入口复用同一个记忆系统视图。 +- 注册不依赖激活顺序:Better Sidebar 可以先于或晚于 Mnemon 加载;服务替换、卸载或热重载时也会正确注销并恢复,不产生重复入口。 +- Better Sidebar Tab 使用该 Tab 传入的会话和工作区作用域,不会错误沿用全局当前会话。设置 `tabEnabled: false` 或 `displayMode: builtin` 时仍不会注册此 Tab。 + +## 升级与兼容性 + +在所属 DSH profile 中安装 `dsh-mnemon@0.4.5`,然后重启。Better Sidebar 仍为可选组件,不新增运行时依赖。本次验证的兼容目标为 `dsh-better-sidebar@0.18.1-alpha.0`;未提供该服务的 profile 继续保留原有独立 Sidebar 行为。 + +记忆数据、存储路径、Pack 格式、Provider 凭据、RPC 权限、设置、生产依赖和会话投影状态均不变,无需迁移或删除缓存。[v0.4.4 的会话投影兼容性](./v0.4.4.md)保持不变;本补丁不包含无关的 v0.5 开发内容。 + +## 验证 + +实现与发布候选在 Node 24.18.0 与 pnpm 10.13.1 下通过完整 `pnpm run verify`:649 项测试通过,1 项 Windows 专用测试在 macOS 跳过。109 个构建文件的确定性检查、包含 35 个工具与 5 个代表性 Mnemon 工具的 Headless 激活、设置迁移与重启、10 个 Node 兼容公开入口、116 个文件的包内容、publint 和 attw 均通过。PR #157 的 Linux Node 22.19/24、Windows Node 24 和 DSH alpha 源码 CI 任务也全部通过。 + +两个真实隔离 WebUI profile 覆盖了两种客户端激活顺序。DSH 0.1.1-rc.2 与 `@linxin666/dsh-web-all@0.3.6` 环境先加载 Better Sidebar、后加载 Mnemon;DSH 0.1.2-alpha.3 环境先加载 Mnemon。两者都保留且仅保留 1 个普通 Sidebar 记忆系统行,Better Sidebar 菜单也仅出现 1 个入口;作用域视图正常渲染,新浏览器标签页均为 0 个控制台 error、0 个 warning。报告版本环境还通过了刷新恢复验证。详见[环境记录与截图](../../pr-assets/better-sidebar-153/README.md)。 + +## 回滚 + +在同一 profile 中重新安装 `dsh-mnemon@0.4.4` 并重启,无需转换数据。Better Sidebar 可能在自身持久化布局中保留已打开的 `dsh-mnemon:memory` Tab;回滚后它可能暂时显示为 orphaned tab,可直接关闭,原有 DSH 普通 Sidebar 入口仍可使用。 + +上一版:[v0.4.4](./v0.4.4.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.4.6.md b/dsh/docs/zh-CN/releases/v0.4.6.md new file mode 100644 index 000000000..343cc8baa --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.4.6.md @@ -0,0 +1,29 @@ +# v0.4.6:DSH Session 事件接口兼容 + +**简体中文** | [English](../../en/releases/v0.4.6.md) + +v0.4.6 修复 DSH 0.1.2-alpha.4 新会话出现的 `agent.session.events is not iterable` 错误,同时覆盖最新 alpha.5 接口,并继续以 DSH 0.1.1-rc.2 作为稳定、推荐的基线。[Issue #156](https://github.com/omdsh-dev/dsh-mnemon/issues/156)、[PR #159](https://github.com/omdsh-dev/dsh-mnemon/pull/159)。 + +## 修复 + +- Session 事件读取现在检测能力而非版本字符串。稳定版 DSH 0.1.1-rc.2 继续使用 `events[]`;DSH 0.1.2-alpha.4 与 alpha.5 使用 `snapshotEvents()` 和 `eventAt()`。 +- 生命周期回放、回合检测与子 Agent 诊断共用同一兼容层,使完整读取与索引读取在受支持版本上遵循同一份持久历史契约。 +- 遇到未知的未来 Session 形态时会明确失败,不会静默地把持久历史当作空数据。 + +## 升级与兼容性 + +在所属 DSH profile 中安装 `dsh-mnemon@0.4.6`,然后重启。DSH 0.1.1-rc.2 仍是稳定的 registry 基线与推荐安装目标;DSH 0.1.2-alpha.4 和最新 alpha.5 是预览兼容目标。兼容路径依据 Session 实际提供的能力选择,不依赖预发布版本标签。 + +记忆数据、存储路径、Pack 格式、Provider 凭据、RPC 权限、设置、生产依赖和会话投影状态均不变,无需迁移或删除缓存。[v0.4.4 的投影兼容性](./v0.4.4.md)与 [v0.4.5 的 Better Sidebar 集成](./v0.4.5.md)保持不变;本补丁不包含无关的 v0.5 开发内容。 + +## 验证 + +实现与发布候选在 Node 24.18.0 与 pnpm 10.13.1 下通过完整 `pnpm run verify`:654 项测试通过,1 项 Windows 专用测试在 macOS 跳过。110 个构建文件的确定性检查、Headless 激活、10 个 Node 兼容公开入口、包内容检查、publint 和 attw 均通过。PR #159 的 Linux Node 22.19/24、Windows Node 24 与源码链接 DSH 0.1.2-alpha.5 CI 任务也全部通过。 + +隔离的真实 WebUI profile 先在 DSH alpha.4 与已发布 Mnemon 0.4.4 上复现错误,再使用本修复分别在 alpha.4、alpha.5 与稳定 rc.2 上完成合成会话。三个修复后场景的浏览器控制台均为 0 个 error、0 个 warning,Host 日志也没有 `error`、`warn` 或 `not iterable` 匹配。详见[环境记录与截图](../../pr-assets/session-events-156/README.md)。 + +## 回滚 + +在同一 profile 中重新安装 `dsh-mnemon@0.4.5` 并重启,无需转换数据。在 DSH 0.1.2-alpha.4 或 alpha.5 上回滚后,原 Session 接口错误可能再次出现;如需继续对话,应同时将 DSH 恢复为稳定的 0.1.1-rc.2。 + +上一版:[v0.4.5](./v0.4.5.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.4.7.md b/dsh/docs/zh-CN/releases/v0.4.7.md new file mode 100644 index 000000000..2143c2406 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.4.7.md @@ -0,0 +1,44 @@ +# v0.4.7:DSH 0.1.2-rc.1 兼容 + +**简体中文** | [English](../../en/releases/v0.4.7.md) + +v0.4.7 将已发布的 DSH 0.1.2-rc.1 提升为 dsh-mnemon 稳定的 registry 与开发基线,同时保留对其直接前序 DSH 0.1.2-alpha.5 tag 的完整源码验证,以及 DSH 0.1.1-rc.2 上的真实 WebUI 向后回归。[PR #161](https://github.com/omdsh-dev/dsh-mnemon/pull/161)、[PR #162](https://github.com/omdsh-dev/dsh-mnemon/pull/162)。 + +## 兼容性变更 + +- 直接使用的 22 个 DSH 开发依赖全部精确固定到 `0.1.2-rc.1`。显式加入 Store 与 Invariants,闭合已发布的类型和 peer 依赖图,不再依赖传递安装的偶然结果。 +- 子 Agent token selector 改从公开 owner `@deepseek-ai/dsh-client-store` 导入 `SnapshotSelectorHook`,UI Slots package 继续提供 `StoredEntry`。这只是类型边界修正,不改变运行时 token 统计。 +- Lockfile 只包含一套一致的 rc.1 预发布依赖群。pnpm 11 release-age 豁免只精确枚举 lockfile 中实际存在的 rc.1 package,后续发布的 `@deepseek-ai` package 仍会继续隔离。 +- DSH 0.1.2-alpha.5 继续由源码覆盖 job 验证,并包含 Store 与 Invariants。基于能力的 Session 读取继续同时支持 alpha.5/rc.1 的 snapshot 接口与 rc.2 的 `events[]` 属性,不解析 package 版本。 + +## 云端 WebUI 认证 + +当前部署指南已切换到 DSH 0.1.2-rc.1 transport:页面、每个 RPC 与每条 stream 都使用 Host 启动 token URL 和签名、绑定 authority 的 Cookie 所建立的浏览器会话。`--trusted-host` 仍只是 Host/Origin 防线;公网入口仍需 HTTPS 与部署层访问控制。 + +上一条 DSH 0.1.1-rc.2 流程作为显式回滚路径保留。它的逐方法 authority 层仍要求启动时 Mnemon `remoteAccess: trusted-host` 覆盖,才能从远端使用设置、备份、Provider 连接和宽泛 mutation 通道。DSH 0.1.2-rc.1 与 alpha.5 会忽略这一保留的兼容设置。 + +## 升级与兼容性 + +安装稳定版 DSH 0.1.2-rc.1,在每个所属 profile 中安装 `dsh-mnemon@0.4.7`,然后重启这些 profile: + +```sh +npm install -g @deepseek-ai/dsh@0.1.2-rc.1 +dsh plugin --profile web add dsh-mnemon@0.4.7 +dsh plugin --profile headless add dsh-mnemon@0.4.7 +``` + +DSH 各 profile 的插件清单彼此独立,只需执行适用于当前安装的 profile 命令。记忆数据、存储路径、Pack 格式、Provider 凭据、运行时默认配置、生产依赖、公开 package exports 与 Session 投影状态均不变,无需迁移或删除缓存。 + +## 验证 + +实现与发布候选在 Node 24.18.0 与 pnpm 10.13.1 下通过完整 `pnpm run verify`:654 项测试通过,1 项 Windows 专用测试在 macOS 跳过。110 个构建文件的确定性检查、隔离 Headless 激活、10 个 Node-compatible 公开入口、117 文件发布包内容检查(383,848 packed bytes;1,707,083 unpacked bytes)、publint 与 attw 均通过。 + +PR #162 的 Linux Node 22.19/24、Windows Node 24、源码链接 DSH 0.1.2-alpha.5 与双语贡献规范 job 全部通过。其 pnpm 11.7 registry 安装按精确 release-age 豁免集合检查了 936 个依赖条目。 + +隔离的 DSH 0.1.2-rc.1 WebUI profile 完成状态加载、记忆系统导航、合成对话、只读保存弹窗与刷新持久化;另一套 DSH 0.1.1-rc.2 profile 完成同一对话与刷新回归。浏览器 warning/error 日志与对应 Host 诊断均为空。截图显示插件版本 0.4.6,是因为它们记录的是本次仅改版本号的发布准备之前、实际可执行的兼容实现 commit;之后没有修改运行时源码。详见[环境记录与截图](../../pr-assets/dsh-rc1-compat/README.md)。 + +## 回滚 + +在相同 profile 中重新安装 `dsh-mnemon@0.4.6` 并重启,无需转换数据。v0.4.6 发布时尚未以 DSH 0.1.2-rc.1 为基线;如果需要完整经过验证的回滚,也应把 DSH 恢复到原 0.1.1-rc.2 基线。远程暴露该 profile 时,请使用 rc.2 专用的云端步骤。 + +上一版:[v0.4.6](./v0.4.6.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.5.0-rc.1.md b/dsh/docs/zh-CN/releases/v0.5.0-rc.1.md new file mode 100644 index 000000000..871e8eaa2 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.5.0-rc.1.md @@ -0,0 +1,43 @@ +# v0.5.0-rc.1:Composable Memory Plugins + +**简体中文** | [English](../../en/releases/v0.5.0-rc.1.md) | [插件开发](../development/extensions.md) + +v0.5 以 RC 形式引入 View-based 执行模型与 Source/Strategy 扩展 SDK。本版将插件架构主要面向开发者开放;普通用户继续沿用 v0.4 的 Sidebar 工作流。 + +## 范围与兼容边界 + +- `dsh-mnemon` 仍是单包安装的 Starter。Runtime、Documents、Memory Spaces、默认 Strategy、三个 Strategy 增强与九种 Provider 拆为十六个独立插件。三个增强随 Starter 安装但默认停用。既有存储格式、使用流程和日常工具仍是用户侧兼容约束;相对 v0.4,界面只在“设置 → 记忆系统”增加主动记录、轻量上下文、范围组合三个开关,不增加 View 页或插件管理入口。不迁移或删除记忆数据。 +- Core 只暴露 `ctx.mnemonMemory`;作者通过 `dsh-mnemon/extension-sdk` 注册 Source/Strategy,通过 `dsh-mnemon/testing` 验证。Source 自有页面使用 Client SDK 与 DSH 生命周期。Memory Spaces 自己拥有 Provider SDK 与子 fiber,不增加 Core 级 Space 服务。 +- Source 与 Strategy 是贡献职责,不强制分包:同一插件可在一个 Fiber 下成批注册二者。私有索引和维护仍归插件。操作保持开放,不要求统一记忆分类或摘要树。 +- 每轮固定一个不可变 View。Source 操作只收到 View 身份/作用域与本 Source 的 read grant,不收到其他 Source 的投影和授权。Read grant 或 action offer 都不是写入许可。 +- Source facts/projection 读取有超时边界与协作式取消。被选中的 Source 默认必需;只有显式 `required: false` 才允许缺席。故障的可选 Source 不再提供 route/action,并返回脱敏诊断解释缺席原因。公共指导跟随当前 View;自动三层复盘只属于 `default-three-tier`。 + +扩展 SDK 仍处于预发布阶段,在 v0.5 稳定版前可能调整。官方包固定同一组已测试版本;外部作者应明确声明并测试兼容 peer 版本。早期实验 Source 若读取完整 `request.view`,需改用 `request.view.id`、`request.view.scope` 与自己的 `request.grant`。内部路径、历史符号和 Host RPC 不属于稳定扩展 API。 + +变更回执必须说明完成状态:已受理和候选不等于已提交记忆,只有确认提交才能携带 `committedAt`。回执辅助函数默认 `unknown`;详见[操作与结果契约](../development/extensions.md)。 + +## 升级与回退 + +尝试 RC 前停止写入,备份完整有效记忆根、工作区 Documents、DSH profile/配置和单独管理的 Provider 数据。在隔离 DSH profile 中用副本验证,不将 smoke test 指向个人记忆。本版不引入数据格式迁移。 + +需要显式选择;`rc` 通道不会替换 npm `latest`: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.0-rc.1 +``` + +Headless profile 将 `web` 改为 `headless`,然后重启 DSH。Starter 安装匹配的十六个插件,普通用户不需要逐个安装。稳定版用户不会通过 npm `latest` 隐式进入 RC。 + +采用副本前,确认原 Runtime 条目、Documents 检索/阅读、Provider 选择、召回/写入回执和设置正常。需要回退时,停止 DSH,同时恢复旧 profile 与配套数据备份,再启动。不能假定旧版本能理解实验性第三方插件新写入的数据。 + +## 有意保留的 RC 限制 + +默认 Host 通过 `memoryTopology.strategyId` 选择一个完整 Strategy,组合 root-turn View。随附但默认停用的 `strategy-scoped`、`strategy-light-context`、`strategy-auto-capture` 可以通过该 Strategy 声明的不同槽同时贡献;用户只看到三个行为开关。同槽冲突明确报错,权限与预算仍共享,不增加后台 worker。Source/Strategy 插件契约在文档中开放并欢迎独立仓库贡献,但 v0.5 不提供通用发现、安装或依赖图界面。本版也不承诺通用多场景调度、自动发现/选择 Strategy、插件进程沙箱或自主 RSI 部署。Generation 替换与隔离作者测试为未来 RSI 提供基础,但不是自主进化闭环。外部插件执行受信任 JavaScript;取消无法抢占同步或不配合的代码。 + +发布门槛包括全工作区验证、所有独立插件仓库、纯 tarball 安装、外部 Source/Strategy/Provider/Client 测试、复制数据兼容、性能和真实 WebUI 检查。受控 HTTP 测试不代表所有线上 Provider 账号均已验证;固定模型响应也不衡量真实模型的记忆质量。参阅[验证与成组发布](../development/README.md)。 + +## 关联的社区需求 + +- `strategy-auto-capture` 为 [Issue #110](https://github.com/omdsh-dev/dsh-mnemon/issues/110) 所描述的流程提供可选的当前回合主动记录策略。它没有引入无人监督的后台写入,因此更广义的需求继续保持开放并收集反馈。 +- `strategy-light-context` 针对 [Issue #40](https://github.com/omdsh-dev/dsh-mnemon/issues/40) 的上下文成本问题提供有界投影策略,但不宣称实现带 revision 的增量注入;依赖上游能力的部分继续保持开放。 +- `strategy-scoped` 围绕 [Issue #77](https://github.com/omdsh-dev/dsh-mnemon/issues/77) 最初提出的全局/项目组合场景,进一步提供 Source 顺序与可写子集控制。 diff --git a/dsh/docs/zh-CN/releases/v0.5.0.md b/dsh/docs/zh-CN/releases/v0.5.0.md new file mode 100644 index 000000000..a62bf99cc --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.5.0.md @@ -0,0 +1,47 @@ +# v0.5.0:Composable Memory Plugins + +**简体中文** | [English](../../en/releases/v0.5.0.md) | [插件开发](../development/extensions.md) + +v0.5.0 以 npm 正式版引入 View-based 执行模型与 Source/Strategy 扩展 SDK。插件架构主要面向开发者开放;普通用户继续沿用 v0.4 的 Sidebar 工作流。 + +## 范围与兼容边界 + +- `dsh-mnemon` 仍是单包安装的 Starter。Runtime、Documents、Memory Spaces、默认 Strategy、三个 Strategy 增强与九种 Provider 拆为十六个独立插件。三个增强随 Starter 安装但默认停用。既有存储格式、使用流程和日常工具仍是用户侧兼容约束;相对 v0.4,界面只在“设置 → 记忆系统”增加主动记录、轻量上下文、范围组合三个开关,不增加 View 页或插件管理入口。不迁移或删除记忆数据。 +- Core 只暴露 `ctx.mnemonMemory`;作者通过 `dsh-mnemon/extension-sdk` 注册 Source/Strategy,通过 `dsh-mnemon/testing` 验证。Source 自有页面使用 Client SDK 与 DSH 生命周期。Memory Spaces 自己拥有 Provider SDK 与子 fiber,不增加 Core 级 Space 服务。 +- Source 与 Strategy 是贡献职责,不强制分包:同一插件可在一个 Fiber 下成批注册二者。私有索引和维护仍归插件。操作保持开放,不要求统一记忆分类或摘要树。 +- 每轮固定一个不可变 View。Source 操作只收到 View 身份/作用域与本 Source 的 read grant,不收到其他 Source 的投影和授权。Read grant 或 action offer 都不是写入许可。 +- Source facts/projection 读取有超时边界与协作式取消。被选中的 Source 默认必需;只有显式 `required: false` 才允许缺席。故障的可选 Source 不再提供 route/action,并返回脱敏诊断解释缺席原因。公共指导跟随当前 View;自动三层复盘只属于 `default-three-tier`。 + +`dsh-mnemon/extension-sdk`、`dsh-mnemon/testing`、`dsh-mnemon/contracts` 以及 Source 自有 SDK 的文档化导出,是 v0.5 支持的扩展表面。官方包固定同一组已测试的 `0.5.0` 版本;外部作者应明确声明并测试兼容 peer 版本。内部路径、历史符号和 Host RPC 不属于公共扩展 API;未来 pre-1.0 次版本若调整扩展表面,会通过发布说明和迁移指引明确处理。 + +变更回执必须说明完成状态:已受理和候选不等于已提交记忆,只有确认提交才能携带 `committedAt`。回执辅助函数默认 `unknown`;详见[操作与结果契约](../development/extensions.md)。 + +## 升级与回退 + +升级前停止写入,备份完整有效记忆根、工作区 Documents、DSH profile/配置和单独管理的 Provider 数据。在隔离 DSH profile 中用副本验证,不将 smoke test 指向个人记忆。v0.5.0 不引入数据格式迁移。 + +可以显式安装正式版,也可以使用 npm `latest` 通道: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.0 +``` + +Headless profile 将 `web` 改为 `headless`,然后重启 DSH。Starter 安装匹配的十六个插件,普通用户不需要逐个安装。已经精确锁定 RC 的安装需要主动选择 `0.5.0` 才会离开预发布版本线。 + +采用副本前,确认原 Runtime 条目、Documents 检索/阅读、Provider 选择、召回/写入回执和设置正常。需要回退时,停止 DSH,同时恢复旧 profile 与配套数据备份,再启动。不能假定旧版本能理解第三方插件新写入的数据。 + +## v0.5 有意保留的边界 + +默认 Host 通过 `memoryTopology.strategyId` 选择一个完整 Strategy,组合 root-turn View。随附但默认停用的 `strategy-scoped`、`strategy-light-context`、`strategy-auto-capture` 可以通过该 Strategy 声明的不同槽同时贡献;用户只看到三个行为开关。同槽冲突明确报错,权限与预算仍共享,不增加后台 worker。Source/Strategy 插件契约在文档中开放并欢迎独立仓库贡献,但 v0.5 不提供通用发现、安装或依赖图界面。本版也不承诺通用多场景调度、自动发现/选择 Strategy、插件进程沙箱或自主 RSI 部署。Generation 替换与隔离作者测试为未来 RSI 提供基础,但不是自主进化闭环。外部插件执行受信任 JavaScript;取消无法抢占同步或不配合的代码。 + +发布门槛包括全工作区验证、所有独立插件仓库、纯 tarball 安装、外部 Source/Strategy/Provider/Client 测试、复制数据兼容、性能检查和真实 WebUI 覆盖。受控 HTTP 测试不代表所有线上 Provider 账号均已验证;固定模型响应也不衡量真实模型的记忆质量。参阅[验证与成组发布](../development/README.md)。 + +## 相对 RC 的变化 + +运行时实现、UI、配置和持久化格式相对 `v0.5.0-rc.1` 均未改变。正式版包含已用于 RC 发布路径的 Registry 校验修正,并让同一套冻结、可恢复的发布流程根据稳定语义选择 npm `latest`、创建普通 GitHub Release。 + +## 关联的社区需求 + +- `strategy-auto-capture` 为 [Issue #110](https://github.com/omdsh-dev/dsh-mnemon/issues/110) 提供可选的当前回合主动记录策略,但不引入无人监督的后台写入。 +- `strategy-light-context` 针对 [Issue #40](https://github.com/omdsh-dev/dsh-mnemon/issues/40) 的上下文成本问题提供有界投影策略,但不宣称实现带 revision 的增量注入;依赖上游能力的部分继续保持开放。 +- `strategy-scoped` 围绕 [Issue #77](https://github.com/omdsh-dev/dsh-mnemon/issues/77) 最初提出的全局/项目组合场景,进一步提供 Source 顺序与可写子集控制。 diff --git a/dsh/docs/zh-CN/releases/v0.5.1.md b/dsh/docs/zh-CN/releases/v0.5.1.md new file mode 100644 index 000000000..ff7faf9c4 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.5.1.md @@ -0,0 +1,34 @@ +# v0.5.1:Desktop 与回合可靠性 + +**简体中文** | [English](../../en/releases/v0.5.1.md) + +v0.5.1 是 `dsh-mnemon` Starter 与十六个官方插件的协调补丁版本。它修复两个在稳定版 DSH 0.1.2-rc.1 上复现的问题,并保持 v0.5.0 的可组合架构、配置、扩展表面与已有数据不变。 + +## 修复 + +- Desktop launcher 现在通过正式 Sidebar 新会话控件的实际父元素插入。嵌套的 `dshp-panelArea` 不再触发 `insertBefore` `NotFoundError`,该挂载异常也不会再把记忆系统设置页级联降级为只读。[Issue #176](https://github.com/omdsh-dev/dsh-mnemon/issues/176)、[PR #177](https://github.com/omdsh-dev/dsh-mnemon/pull/177)。 +- 普通用户回合开始前会中止并等待进行中的 Idle Review 完成清理,再创建新的 Turn View 并绑定 Agent runtime;清理后还会重新检查 abort 与 turn 状态。这样既保留 runtime 独占所有权,也不会再用 `already pinned` 拒绝用户回合。[Issue #172](https://github.com/omdsh-dev/dsh-mnemon/issues/172)、[PR #178](https://github.com/omdsh-dev/dsh-mnemon/pull/178)。 + +## 升级与兼容性 + +在所属 DSH profile 中安装精确稳定版并重启: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.1 +``` + +Headless profile 将 `web` 改为 `headless`。Starter 会安装全部十六个匹配的 `0.5.1` 插件,普通用户不需要逐个安装;稳定 npm 通道仍是 `latest`。 + +本版本不迁移配置、RPC 权限、凭据、存储路径、记忆格式、Document 格式、Provider 数据或扩展契约,现有 v0.5.0 profile 与数据可直接使用。与任何升级一样,请先停止写入并备份有效记忆根、工作区 Documents、DSH profile/配置和单独管理的 Provider 数据。 + +## 验证 + +两个问题都先在一次性的真实 DSH 0.1.2-rc.1 WebUI profile 中复现。修复后的 Desktop profile 保持设置可写并成功保存 `storageScope=workspace`;修复后的生命周期 profile 在刻意挂起 Idle Review 时仍正常完成第三轮对话。两个实现 PR 都保留了修复前后截图,夹具仅使用合成数据。 + +Sidebar/设置与生命周期定向回归通过,随后完成整个 workspace 验证。发布流水线还会验证确定性构建、Node 20 全部公开入口、十七个 packed 制品、十六个独立安装的插件仓库、外部 SDK 消费者、真实 packed DSH 组合,以及基于 Registry 的 v0.4.7 升级。PR CI 收敛为两个互补重型 job 与有界四 worker 制品队列,同时不删除这些发布门禁。[PR #179](https://github.com/omdsh-dev/dsh-mnemon/pull/179)。 + +## 回滚 + +在同一 profile 中重新安装 `dsh-mnemon@0.5.0` 并重启 DSH,无需转换数据;但嵌套 Desktop 设置故障与 Idle Review pin 竞态可能再次出现。如果升级后已发生写入,应恢复与旧版本匹配的升级前 profile/数据备份。 + +上一版:[v0.5.0](./v0.5.0.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.5.10.md b/dsh/docs/zh-CN/releases/v0.5.10.md new file mode 100644 index 000000000..015a0e897 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.5.10.md @@ -0,0 +1,44 @@ +# v0.5.10:精确记忆写入与自动审阅限制 + +**简体中文** | [English](../../en/releases/v0.5.10.md) + +v0.5.10 包含 [#256](https://github.com/omdsh-dev/dsh-mnemon/pull/256) 的 Runtime 匹配修复、[#234](https://github.com/omdsh-dev/dsh-mnemon/pull/234) 的 OpenViking 写入验证,以及 [#257](https://github.com/omdsh-dev/dsh-mnemon/pull/257) 的自动审阅限制与 Agent Teams 兼容处理。 + +## Runtime 与 OpenViking 写入 + +Runtime 替换和删除优先匹配唯一的完整内容,再尝试唯一的子串匹配。因此,仅含 `X` 的条目可以与 `EGO_LINUX_CHROME` 共存并正常编辑或删除。重复的完整匹配仍要求消除歧义,拒绝操作时保留已有数据。 + +OpenViking 将原文写入唯一的新文件,验证索引状态和内容逐字回读后,再返回用于 Host 回执与归档补偿的作用域内文件身份,不再把提取响应当作原文已存储的证明。搜索和浏览返回完整内容。账户隔离、旧 owner-root 解析和删除范围均经过验证;未完成或结果不确定的写入会返回包含候选 URI 的错误。合并历史保留原贡献者的提交。缺少所需内容写入与读取契约的服务器会明确报错,不迁移设置或既有内容。 + +## 自动审阅 + +自动审阅具有独立开关,默认最小间隔为五分钟,每个已加载的父 Agent 最多尝试 20 次。默认 spawn 路径限制上下文大小;fork 需要显式选择,可在子 Agent 启动前回退或跳过。单次模型响应的 token 上限不等于整个任务的用量上限。Agent 卸载或 Host 重启后,尝试预算重新计算;既有会话历史保留。 + +同时启用受影响的已发布 Agent Teams 服务和工具时,自动审阅会在创建子 Agent 前暂停,并说明组合不兼容的原因。这一处理避免自动审阅触发故障,并未修复上游 Team 策略或手动委派。失败的审阅可以展示已经提交的写入回执,便于核对,不会重放整个任务。 + +## 发布包与升级 + +| 包 | 版本 | +| --- | --- | +| `dsh-mnemon` | **0.5.10** | +| `dsh-mnemon-provider-openviking` | **0.5.5** | +| `dsh-mnemon-source-memory-spaces` | **0.5.8** | +| `dsh-mnemon-source-runtime` | **0.5.7** | + +Starter 精确固定完整的十六插件组合,其余十三个插件保持原版本。Mnemon CLI 继续独立维护。 + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.10 +``` + +重启 DSH 并刷新浏览器;Headless Profile 将命令中的 `web` 替换为 `headless`。完整 DSH Profile 需要 Node `^22.19.0 || >=24.0.0`。 + +## 验证 + +整合实现通过 1,162 项根包测试,5 项 opt-in 跳过,并通过插件测试、真实 Mnemon 0.2.8 CLI、Headless 激活与重启、确定性构建和公共包检查。十六个独立插件、十七个制品通过安装与组合验证,包含同时启用三个可选 Strategy 扩展。真实 OpenViking 0.4.20 通过创建、读取、搜索和删除检查;已发布 DSH 0.1.5-rc.2 与 Agent Teams 0.1.5-alpha.2 的组合矩阵验证了兼容性暂停。 + +真实 WebUI 前后对比截图和实际验证的版本保留在 [Runtime 报告](../../pr-assets/issue-217-exact-runtime-match/README.zh-CN.md)、[OpenViking 报告](../../pr-assets/issue-233-openviking-20260916/README.zh-CN.md)及[自动审阅报告](../../pr-assets/idle-review-agent-team/README.zh-CN.md)。模型回复和 embedding 使用确定性本地夹具,不代表真实模型判断或语义召回质量。 + +发布流程验证版本化工作区与独立制品,先发布三个变化的插件,再发布 Starter;完整 Registry 组合和从 v0.4.7 开始的真实升级检查通过后,才创建 GitHub Release。 + +上一版:[v0.5.9](./v0.5.9.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.5.11.md b/dsh/docs/zh-CN/releases/v0.5.11.md new file mode 100644 index 000000000..15ed80c1d --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.5.11.md @@ -0,0 +1,30 @@ +# v0.5.11:隐藏 Windows Git 分支查询窗口 + +**简体中文** | [English](../../en/releases/v0.5.11.md) + +v0.5.11 包含 [#260](https://github.com/omdsh-dev/dsh-mnemon/pull/260),修复 [#259](https://github.com/omdsh-dev/dsh-mnemon/issues/259)。Runtime 查询 Git 分支时会隐藏 Windows 控制台,避免 GUI 宿主刷新分支记忆时短暂弹出终端窗口。Git 命令、两秒超时、分支过滤与失败回退均保持不变。 + +## 发布包与升级 + +| 包 | 版本 | +| --- | --- | +| `dsh-mnemon` | **0.5.11** | +| `dsh-mnemon-source-runtime` | **0.5.8** | + +Starter 精确固定完整的十六插件组合,其余十五个插件保持原版本。无需迁移配置或已有记忆;Mnemon CLI 继续独立维护。 + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.11 +``` + +重启 DSH 并刷新浏览器;Headless Profile 将命令中的 `web` 替换为 `headless`。完整 DSH Profile 需要 Node `^22.19.0 || >=24.0.0`。 + +## 验证 + +四轮真实 Windows 进程测试中,原版每轮出现 104–105 个可见控制台窗口,修复后均为零。每轮从已确认无控制台的父进程执行 104 次真实 Git 调用,并验证分支结果与失败回退。新增回归测试在修复前失败,修复后全部 59 项 Runtime 测试通过。 + +实现通过 1,162 项根包测试,5 项 opt-in 跳过,并完成真实 Mnemon CLI 0.2.8、macOS WebUI 操作、Headless 启动与重启,以及十六个插件、十七个制品的组合验证。三个可选 Strategy 同时启用。[双语证据报告](../../pr-assets/issue-259-windows-git-console/README.zh-CN.md)保留验证版本、前后截图与可下载 Windows 观测数据,明确区分 Windows 进程验证和 macOS WebUI 检查,不声称已完整重放报告者的 Windows 11 Electron 环境。 + +发布流程验证版本化制品,先发布 Runtime,再发布 Starter;完整 Registry 组合和从 v0.4.7 开始的真实升级检查通过后,才创建 GitHub Release。 + +上一版:[v0.5.10](./v0.5.10.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.5.12.md b/dsh/docs/zh-CN/releases/v0.5.12.md new file mode 100644 index 000000000..f66917e7f --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.5.12.md @@ -0,0 +1,29 @@ +# v0.5.12:兼容 DSH RC 与 Alpha + +**简体中文** | [English](../../en/releases/v0.5.12.md) + +v0.5.12 包含 [#263](https://github.com/omdsh-dev/dsh-mnemon/pull/263),修复 [#261](https://github.com/omdsh-dev/dsh-mnemon/issues/261)。回合记忆条使用 DSH 0.1.6-alpha.2 的 list 插槽要求的稳定 ID,同时保留 0.1.5-rc.2 的 chain selector;仅在回合完成后读取、展示记忆活动。注册成功后,“存入记忆”入口也能正常挂载。 + +Sidebar 与设置页改用 DSH 公开的默认/主会话 binding。Builtin 与 Better Sidebar 保留显式所属会话,包括无会话状态,避免切换工作区时借用其他会话的记忆范围。 + +## 发布包与升级 + +本次仅新发布 **`dsh-mnemon` 0.5.12**。十六个配套插件及其在 Starter 中的精确版本固定均不变。无需迁移配置或已有记忆;Mnemon CLI 继续独立维护。 + +Client 的兼容下限调整为 **DSH 0.1.5-rc.1**。Root 的两个框架 peer 均声明 `^0.1.5-rc.1 || ^0.1.6-alpha.2`,已明确验证 0.1.5-rc.2 和 0.1.6-alpha.2。缺少公开 UI Session 服务的旧版 DSH 应继续使用此前验证过的 Mnemon 版本。 + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.12 +``` + +重启 DSH 并刷新浏览器;Headless Profile 将命令中的 `web` 替换为 `headless`。完整 DSH Profile 需要 Node `^22.19.0 || >=24.0.0`。 + +## 验证 + +两套 DSH 版本均通过完整生产公开类型检查、正常 npm 制品安装、真实 WebUI 工具调用、Sidebar/Builtin 跳转、记忆条开关与页面重载。alpha 额外验证了两个独立插槽贡献同时存在,以及切换工作区后的记忆隔离。制品 Headless 检查涵盖十七个 Mnemon 工具、真实 Runtime 写入与状态、新进程持久化,以及禁用 Root 后工具数归零。 + +实现通过 1,183 项根包测试,5 项 opt-in 真实 Flash 测试跳过,并完成真实 Mnemon Native CLI 0.2.8 集成及十六插件/十七制品组合与升级验证。此次 macOS 验证跳过真实 OpenViking 和 Windows 专用检查。[双语证据报告](../../pr-assets/issue-261-dsh-slots/README.zh-CN.md)保留原始前后截图、制品完整性与复现脚本。模型响应来自明确的本地 OpenAI/Anthropic 夹具,不代表真实模型质量评测。 + +发布流程再次验证版本化制品,安装完整 Registry 固定组合,发布 Starter,并检查从 v0.4.7 开始的真实升级,通过后才创建 GitHub Release。 + +上一版:[v0.5.11](./v0.5.11.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.5.13.md b/dsh/docs/zh-CN/releases/v0.5.13.md new file mode 100644 index 000000000..5cd810b00 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.5.13.md @@ -0,0 +1,45 @@ +# v0.5.13:工作区、Builtin 与 DSH 0.1.7 兼容修复 + +**简体中文** | [English](../../en/releases/v0.5.13.md) + +v0.5.13 包含三项修复: + +- [#268](https://github.com/omdsh-dev/dsh-mnemon/pull/268) 修复 [#266](https://github.com/omdsh-dev/dsh-mnemon/issues/266):Windows 工作区路径如果位于普通文件之下,会在计算存储标识前以 `ENOTDIR` 拒绝。合法工作区标识、junction、Unicode 路径及尚未创建的目录后代保持原有行为;已有存储目录不会被移动或删除。 +- [#269](https://github.com/omdsh-dev/dsh-mnemon/pull/269) 修复 [#265](https://github.com/omdsh-dev/dsh-mnemon/issues/265):Builtin 记忆页打开时,仅隐藏所属对话的宽度拖拽手柄,避免手柄截获记忆页输入;返回 Chat 后恢复宽度拖拽。常驻输入框仍可使用,规则不作用于 Sidebar 或其他对话。 +- [#270](https://github.com/omdsh-dev/dsh-mnemon/pull/270) 修复 [#267](https://github.com/omdsh-dev/dsh-mnemon/issues/267):适配 DSH 0.1.7-alpha.1 基于 Config 的实时设置、新版图标导出及由生产者标识来源的 Session 消息。设置写入保留修订检查与 Profile 归属约束;历史 Mnemon 消息及官方迁移后的消息格式仍参与去重。 + +## 发布包与升级 + +本次仅将 **`dsh-mnemon` 升级至 0.5.13**。十六个配套插件及其在 Starter 中的精确版本固定均不变。Mnemon CLI 继续独立维护。 + +Client 的兼容下限仍为 **DSH 0.1.5-rc.1**。框架 peer 范围为 `^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-alpha.1`;证据覆盖 0.1.5-rc.2、0.1.6-alpha.2 和 0.1.7-alpha.1,使用的公开 DSH 包均未修改。 + +升级前请备份 DSH Profile 配置、旧设置文件及相关 Mnemon 数据。 + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.13 +``` + +重启 DSH 并刷新浏览器;Headless Profile 将命令中的 `web` 替换为 `headless`。完整 DSH Profile 需要 Node `^22.19.0 || >=24.0.0`。 + +## 设置恢复与回滚 + +在 DSH 0.1.7 上,补充恢复读取保留的 `settings.yaml.imported`,不改动其原始字节。当前显式设置及原生 Strategy 选择优先于旧偏好,已有 Profile 表达式仍保留为表达式;配置重新协调后,所属 Profile 的 Source 与 Strategy 选择仍会保留。 + +如果启动时仍存在原始 `settings.yaml`,DSH 会先执行自己的导入。Mnemon 的补充 UI/View 恢复延后到下一次 Host 冷启动,因此升级可能需要第二次完整停止并启动 DSH。刷新浏览器或重新挂载插件不能完成这一步延后恢复。 + +自动恢复要求使用标准 `mnemon` 根 Entry,并精确匹配所属 Profile 的旧命名空间;不会推测自定义根 Entry 的归属或借用其他 Profile 的后缀。存在歧义或无效的输入会留在保留备份中,供人工核查。 + +新 Profile 设置不会回写旧设置文件。降级需要对应配置备份,并手动核对备份之后的更改;没有自动逆向导出。Runtime、Documents 和 Memory Spaces 的数据格式不变。 + +## 验证 + +实现证据包括原生 Windows 前后对比、真实 Builtin 命中测试及输入框/Chat 操作,以及三组受支持 DSH 的制品 WebUI 设置保存、Runtime 写入和 Session 恢复。模型响应来自明确的 loopback 夹具,无需外部模型 API key;存储、工具与浏览器操作均为真实执行。 + +- [Windows 工作区证据](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.13/docs/pr-assets/issue-266-workspace-ancestor/README.zh-CN.md) +- [Builtin 宽度手柄证据](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.13/docs/pr-assets/issue-265-builtin-width-handles/README.zh-CN.md) +- [DSH 设置、迁移与 Session 证据](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.13/docs/pr-assets/issue-267-settings-migration/README.zh-CN.md) + +发布工作流必须完成版本化制品、完整 Registry 固定组合及从 v0.4.7 开始的真实升级检查,之后才创建 GitHub Release。 + +上一版:[v0.5.12](./v0.5.12.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.5.14.md b/dsh/docs/zh-CN/releases/v0.5.14.md new file mode 100644 index 000000000..f00fa600d --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.5.14.md @@ -0,0 +1,45 @@ +# v0.5.14:Desktop 加载、OpenViking User Key 与 Agent Teams 审查 + +**简体中文** | [English](../../en/releases/v0.5.14.md) + +v0.5.14 包含三项修复: + +- [#277](https://github.com/omdsh-dev/dsh-mnemon/pull/277) 修复 [#274](https://github.com/omdsh-dev/dsh-mnemon/issues/274):旧版 Desktop 宿主的 Cosmokit 不提供 Volatile API 时,Profile bundle 仍可正常加载。新版宿主继续使用实时设置,保留 Starter 分组、组件开关与已有设置命名空间。 +- [#276](https://github.com/omdsh-dev/dsh-mnemon/pull/276) 修复 [#271](https://github.com/omdsh-dev/dsh-mnemon/issues/271):OpenViking 绑定用户的 API Key 无需 Admin 权限即可发现自身记忆命名空间。请显式填写账户和 **User Key 所属用户(跳过 Admin)**;发现流程先校验限定范围的数据 API,再保存。替换配置被拒绝时,原有配置与凭据保留;默认 Admin 发现方式仍可使用。 +- [#278](https://github.com/omdsh-dev/dsh-mnemon/pull/278) 修复 [#275](https://github.com/omdsh-dev/dsh-mnemon/issues/275):新增显式受限模式,让空闲审查在已验证的 DSH 与官方 Agent Teams 0.1.7-rc.1 组合中运行。审查器在有界检查点内保留完整的真实用户决定,只能使用授权范围内的记忆工具;审查器内部仍禁止 Team 委派,父级 Team 可以继续工作。 + +## 包版本与升级 + +| 包 | 原版本 | 新版本 | +|---|---|---| +| `dsh-mnemon` | 0.5.13 | **0.5.14** | +| `dsh-mnemon-provider-openviking` | 0.5.5 | **0.5.6** | +| `dsh-mnemon-source-memory-spaces` | 0.5.8 | **0.5.9** | + +其余十四个配套包版本不变,Starter 精确固定经过验证的完整组合。Mnemon CLI 仍独立维护,本地验证使用官方 CLI 0.2.9。 + +备份 DSH Profile 配置及相关 Mnemon 数据,然后升级: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.14 +``` + +重启 DSH 并刷新浏览器。Headless Profile 将命令中的 `web` 换为 `headless`。Client 下限仍为 DSH 0.1.5-rc.1,完整 DSH Profile 要求 Node `^22.19.0 || >=24.0.0`。 + +## Agent Teams 兼容性与数据 + +**默认仍为 `idleReview.agentTeams: pause`。** 使用已验证的 DSH/Teams 0.1.7-rc.1 组合后,手动选择 `scoped`。旧版 DSH 0.1.5-rc.2 与 Teams 0.1.5-alpha.2 仍可能以 `TEAM_NOT_MEMBER` 拒绝审查器;本版不会在该组合上自动开启受限审查,也不会绕过其他插件的策略。 + +Runtime、Documents 与 Memory Spaces 存储格式不变,保留 Profile 所有权、修订检查和多插件隔离。降级会恢复旧版行为,需要结合备份的 Profile 核对新增可选设置;本版没有增加逆向迁移。 + +## 验证 + +以下记录保留原始故障、真实 WebUI 操作结果及截图,覆盖 Desktop generation 加载与冷启动、OpenViking 限定范围发现与失败保存保留,以及具有完整用户证据的受限 Team 审查器和仍可工作的父级 Team。Provider 和模型回复使用明确标注的本地夹具,浏览器操作、DSH 工具、存储和 CLI 操作真实执行;这些夹具不证明真实 OpenViking 云账户行为或在线模型质量。 + +- [Desktop Profile 证据](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.14/docs/pr-assets/issue-274-profile-generation/README.zh-CN.md) +- [OpenViking 发现证据](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.14/docs/pr-assets/issue-271-user-key/README.zh-CN.md) +- [Agent Teams 与检查点证据](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.14/docs/pr-assets/issue-275-agent-teams/README.zh-CN.md) + +发布工作流在创建 GitHub Release 前验证版本化制品、全部十六个独立插件、完整 Registry 固定组合及 v0.4.7 的真实升级。 + +上一版:[v0.5.13](./v0.5.13.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.5.15.md b/dsh/docs/zh-CN/releases/v0.5.15.md new file mode 100644 index 000000000..2adc67ff7 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.5.15.md @@ -0,0 +1,46 @@ +# v0.5.15:USER 工具写入、Runtime 元数据与皮肤接口 + +**简体中文** | [English](../../en/releases/v0.5.15.md) + +v0.5.15 包含以下改进: + +- [#282](https://github.com/omdsh-dev/dsh-mnemon/pull/282) 修复 [#281](https://github.com/omdsh-dev/dsh-mnemon/issues/281):`mnemon_runtime_memory` 在 `target=user` 时接受模型工具层附带的空占位参数 `branches: []`,恢复 USER 新增、替换和移除。非空 USER 分支仍被拒绝,MEMORY 的分支设置与清空行为保持不变。 +- [#283](https://github.com/omdsh-dev/dsh-mnemon/pull/283) 解决 [#280](https://github.com/omdsh-dev/dsh-mnemon/issues/280):Runtime 的模型上下文投影包含已存储的重要性,以及按天计算的创建、更新年龄。默认 Strategy 明确说明快照受上下文预算限制,缺少某条记录不代表它已被删除。 +- [#284](https://github.com/omdsh-dev/dsh-mnemon/pull/284) 实现 [#273](https://github.com/omdsh-dev/dsh-mnemon/issues/273):Sidebar 与 Builtin 提供稳定的 Mnemon 根选择器,支持覆盖 `--mn-bg`、`--mn-backdrop` 和 `--mn-surface`。[皮肤开发手册](../development/skin-integration.md)包含 dsh-web 接入、旧类名迁移与验证步骤。 + +## 包版本与升级 + +| 包 | 原版本 | 新版本 | +|---|---|---| +| `dsh-mnemon` | 0.5.14 | **0.5.15** | +| `dsh-mnemon-source-runtime` | 0.5.8 | **0.5.9** | +| `dsh-mnemon-strategy-default-three-tier` | 0.5.5 | **0.5.6** | + +其余十四个配套包版本不变,Starter 精确固定完整组合。Mnemon CLI 独立维护,相关修复的本地验证使用官方 CLI 0.2.9。 + +备份 DSH Profile 配置及相关 Mnemon 数据,然后升级: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.15 +``` + +重启 DSH 并刷新浏览器。Headless Profile 将命令中的 `web` 换为 `headless`。Client 下限仍为 DSH 0.1.5-rc.1,完整 DSH Profile 要求 Node `^22.19.0 || >=24.0.0`。 + +## 兼容性与数据 + +Runtime 的原始正文、JSON/Markdown 存储格式、精确匹配和容量计算保持不变;新增元数据由 Source 在编译模型投影时生成,不需要数据迁移。每个回合仍使用固定快照,Scoped、Light Context 与 Auto Capture 可继续组合使用。 + +官方主题保留默认不透明背景。现有皮肤需要显式使用公开选择器与变量,才能自定义 Mnemon 背景;安装皮肤不会自动启用玻璃效果。挂载到 `body` 的弹窗保持独立样式,皮肤适配不改变 Provider 或数据权限。 + +## 验证 + +下列记录保留原始故障、回归测试、真实 WebUI 操作和对比截图。验证使用隔离的正式 DSH 0.1.7-rc.2、完整十七个 Mnemon 制品和官方 CLI。模型决策由本地回环夹具固定;工具调用、存储、浏览器操作和 CLI 操作真实执行,不代表在线模型质量或 Windows WebUI 已验收。 + +- [USER 工具写入与 CLI 证据](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.15/docs/pr-assets/issue-281-user-branches/README.zh-CN.md) +- [Runtime 元数据与多插件投影证据](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.15/docs/pr-assets/issue-280-runtime-metadata/README.zh-CN.md) +- [皮肤接口、默认背景与双入口证据](https://github.com/omdsh-dev/dsh-mnemon/blob/v0.5.15/docs/pr-assets/issue-273-skin-hooks/README.zh-CN.md) +- [v0.5.15 版本化组合与 WebUI 冒烟](../../pr-assets/release-v0.5.15/README.zh-CN.md) + +发布工作流在创建 GitHub Release 前,验证合并后的 workspace、十六个独立插件、冻结制品、完整 Registry 固定组合和 v0.4.7 的真实升级。 + +上一版:[v0.5.14](./v0.5.14.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.5.16.md b/dsh/docs/zh-CN/releases/v0.5.16.md new file mode 100644 index 000000000..cb98ffad2 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.5.16.md @@ -0,0 +1,44 @@ +# v0.5.16:中文元数据、原生 Sidebar 与 Source 滚动 + +**简体中文** | [English](../../en/releases/v0.5.16.md) + +v0.5.16 包含三项界面与制品修复: + +- [#286](https://github.com/omdsh-dev/dsh-mnemon/pull/286) 为 DSH 中的主插件增加中文名称与描述,并保留英文 manifest 回退。打包制品会检查 locale 资源,仅修改 locale 的变更也纳入发布校验。原贡献者提交 [`d16d017`](https://github.com/omdsh-dev/dsh-mnemon/commit/d16d017aa5bb0c3bbbdb40dc5a95554266b6522a) 原样保留,校验与发布维护通过后续提交补充。 +- [#287](https://github.com/omdsh-dev/dsh-mnemon/pull/287) 将记忆系统接入 DSH 原生 Sidebar 与主面板。切换到 Plugins 后显示所选页面,返回时保留 Source 状态。原生行样式、折叠导航和标题可见性已覆盖受测的官方外观与 Maid Atelier 皮肤,继续支持 Better Sidebar 和替换布局的回退路径。 +- [#288](https://github.com/omdsh-dev/dsh-mnemon/pull/288) 让运行时、档案和记忆空间的边距与滚动容器对齐状态页。记忆空间滚动时保留标题与 Tab,切换 Tab 只重置所属 canvas;打开关联记忆时,将关联标题与关闭按钮定位到固定头部下方。 + +## 包版本与升级 + +| 包 | 原版本 | 新版本 | +|---|---|---| +| `dsh-mnemon` | 0.5.15 | **0.5.16** | +| `dsh-mnemon-source-memory-spaces` | 0.5.9 | **0.5.10** | + +其余十五个配套插件版本不变。完整修复需要同时升级 Root 与 Memory Spaces Source,Starter 已精确固定全部十七个包的组合。Mnemon CLI 继续独立维护。 + +备份 DSH Profile 配置及相关 Mnemon 数据,然后升级: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.16 +``` + +重启 DSH 并刷新浏览器。Headless Profile 将命令中的 `web` 换为 `headless`。Client 下限仍为 DSH 0.1.5-rc.1,完整 Profile 要求 Node `^22.19.0 || >=24.0.0`。 + +## 兼容性与数据 + +无需持久化迁移,已存记忆、存储格式和 Provider 权限保持不变。Source 的 `onResetScroll` 与 `onRevealElement` 回调均为可选,兼容旧 Host;旧 Host 需要手动滚动。现有 Root peer 最低版本不变。 + +正式 DSH 0.1.7-rc.2 仍可能在子组件运行时将 `cordis:group` 容器显示为 Off,其界面开关会返回 `unknown-plugin`。本版不修改 DSH,也不安装替代插件管理服务的适配器。本地上游候选补丁仅作为排查证据保留,不随 Mnemon 运行。 + +## 验证 + +原始 PR 记录包含回归检查、打包制品验证和真实 WebUI 证据: + +- [中文元数据与英文回退](../../pr-assets/pr-286-plugin-metadata/README.zh-CN.md) +- [原生 Sidebar、皮肤与状态保留](../../pr-assets/sidebar-native-20260926/README.zh-CN.md) +- [Source 框架、滚动与可见的关联操作](../../pr-assets/source-page-frames-20260926/README.md) + +版本化的全部十七个制品已通过真实 Flash WebUI 与官方 CLI 验收:Runtime 写入及容量归档、Documents 检索及归档、Native 沉淀/召回/关联、Sidebar 状态保留、页面框架对齐和 Host 冷重启。十三次真实 Flash 请求全部返回 HTTP 200;重启后五个 Runtime/Documents 文件哈希、四条 Native 记忆和八条关系保持不变。Root 测试 1,421 项通过,十六个独立插件仓库、外部消费者及从 v0.4.7 升级均通过。截图、制品标识、本机并发重跑情况与验证范围见 [v0.5.16 发布证据](../../pr-assets/release-v0.5.16/README.zh-CN.md)。 + +上一版:[v0.5.15](./v0.5.15.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.5.17.md b/dsh/docs/zh-CN/releases/v0.5.17.md new file mode 100644 index 000000000..598339c36 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.5.17.md @@ -0,0 +1,60 @@ +# v0.5.17:可组合策略与组件设置 + +**简体中文** | [English](../../en/releases/v0.5.17.md) + +v0.5.17 包含 v0.5.16 后合入的十三个 PR。Starter 现在精确固定十八个包,其中包括新增的通用策略。 + +- **策略与记忆组合**([#294](https://github.com/omdsh-dev/dsh-mnemon/pull/294)、[#299](https://github.com/omdsh-dev/dsh-mnemon/pull/299)、[#301](https://github.com/omdsh-dev/dsh-mnemon/pull/301)):在插件页选择分层策略或通用策略,依据组件声明组合 Source 与增强,并联动处理依赖开关。通用策略接入可用 Source,由模型决定如何使用;标准 selection、projection、capture 增强可以跟随任一主策略。没有主策略运行时,对话可不带记忆继续。 +- **组件设置与界面**([#297](https://github.com/omdsh-dev/dsh-mnemon/pull/297)、[#298](https://github.com/omdsh-dev/dsh-mnemon/pull/298)、[#300](https://github.com/omdsh-dev/dsh-mnemon/pull/300)、[#302](https://github.com/omdsh-dev/dsh-mnemon/pull/302)):在 DSH 插件页配置 Mnemon,每个组件拥有自己的设置页,并可提供状态卡片。开关和选择器立即生效,输入值通过“应用”保存。数据目录明确区分“默认”和“自定义”。界面采用 DSH 原生控件和主题令牌。 +- **可选 CLI 与平等的 Provider**([#295](https://github.com/omdsh-dev/dsh-mnemon/pull/295)):仅 Mnemon Native 需要 Mnemon CLI。Native 不可用时,其他已就绪的 Provider 可继续提供记忆空间;已保存的 Provider 选择仍然生效。 +- **命名与导航**([#290](https://github.com/omdsh-dev/dsh-mnemon/pull/290)、[#292](https://github.com/omdsh-dev/dsh-mnemon/pull/292)、[#303](https://github.com/omdsh-dev/dsh-mnemon/pull/303)):保留“可组合记忆 (dsh-mnemon)”,将分层记忆描述为默认组合,侧栏使用细线大脑图标。Git 忽略本地凭据和工具状态。 +- **宿主兼容与说明**([#293](https://github.com/omdsh-dev/dsh-mnemon/pull/293)、[#291](https://github.com/omdsh-dev/dsh-mnemon/pull/291)):以正式发布的 DSH 0.1.7-rc.2 契约为基线,移除旧宿主兼容路径,并说明上游 `cordis:group` 显示问题。 + +- **通用策略读取回执**:发布验收发现 Source 证据缺少可选 `score` 或 `revision` 时,DSH 会拒绝工具返回值。Host 现在为 `mnemon_recall` 和 `mnemon_related` 保留字段的缺省语义,同时保留零分数及已有修订值。 + +## 升级与兼容 + +**本版支持 DSH 0.1.7-rc.2。更早的宿主请继续使用 v0.5.16。** 完整 Profile 需要 Node `^22.19.0 || >=24.0.0`;包的 Node 入口导入下限仍为 20。 + +备份 Profile 配置和记忆数据后,安装完整 Starter: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.17 +``` + +升级后重启 DSH 并刷新浏览器;Headless Profile 将命令中的 `web` 替换为 `headless`。Mnemon CLI 独立维护版本,仅使用 Mnemon Native 时需要。 + +记忆存储格式不变。DSH 插件管理器可用时,旧组件选择会迁移到 Profile patch。修改数据目录不会搬移已有记忆,更换位置时需要显式复制或恢复数据。回滚时除数据备份外,也应保留 Profile 配置备份。 + +五个策略包使用本次新增的标准 View 扩展 SDK,因此将 `dsh-mnemon` peer 下限提高到 `^0.5.17`。请整体升级 Starter 固定的组合;独立安装插件时也必须满足该依赖下限。 + +DSH 0.1.7-rc.2 仍可能将运行中组件的 `cordis:group` 容器显示为关闭,点击该容器开关可能返回 `unknown-plugin`。本版不修改 DSH,参见[兼容性说明](../reference/compatibility.md#dsh-017-bundle-组件列表)与[上游 issue #649](https://github.com/dsh-external/issues/issues/649)。 + +## 包版本 + +| Package / 包 | Previous / 上版 | New / 本版 | +|---|---|---| +| `dsh-mnemon` | 0.5.16 | **0.5.17** | +| `dsh-mnemon-provider-byterover` | 0.5.4 | **0.5.5** | +| `dsh-mnemon-provider-hindsight` | 0.5.4 | **0.5.5** | +| `dsh-mnemon-provider-holographic` | 0.5.4 | **0.5.5** | +| `dsh-mnemon-provider-honcho` | 0.5.4 | **0.5.5** | +| `dsh-mnemon-provider-mem0` | 0.5.4 | **0.5.5** | +| `dsh-mnemon-provider-mnemon-native` | 0.5.5 | **0.5.6** | +| `dsh-mnemon-provider-openviking` | 0.5.6 | **0.5.7** | +| `dsh-mnemon-provider-retaindb` | 0.5.4 | **0.5.5** | +| `dsh-mnemon-provider-supermemory` | 0.5.4 | **0.5.5** | +| `dsh-mnemon-source-documents` | 0.5.6 | **0.5.7** | +| `dsh-mnemon-source-memory-spaces` | 0.5.10 | **0.5.11** | +| `dsh-mnemon-source-runtime` | 0.5.9 | **0.5.10** | +| `dsh-mnemon-strategy-auto-capture` | 0.5.4 | **0.5.5** | +| `dsh-mnemon-strategy-default-three-tier` | 0.5.6 | **0.5.7** | +| `dsh-mnemon-strategy-general` | New / 新增 | **0.5.1** | +| `dsh-mnemon-strategy-light-context` | 0.5.4 | **0.5.5** | +| `dsh-mnemon-strategy-scoped` | 0.5.4 | **0.5.5** | + +## 验证 + +[本版验收记录](../../pr-assets/release-v0.5.17/README.zh-CN.md)记录版本化制品、功能验证、截图及范围限制。此前 PR 的截图保留为历史证据,不代替本次组合验收。 + +上一个版本:[v0.5.16](./v0.5.16.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.5.18.md b/dsh/docs/zh-CN/releases/v0.5.18.md new file mode 100644 index 000000000..4f7967909 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.5.18.md @@ -0,0 +1,39 @@ +# v0.5.18:安装后直接启用与桌面 CLI 静默运行 + +**简体中文** | [English](../../en/releases/v0.5.18.md) + +本补丁版包含 v0.5.17 后合入的两项修复: + +- **安装启用无需重启 DSH**([#308](https://github.com/omdsh-dev/dsh-mnemon/pull/308)):Desktop 与官方 WebUI 使用隔离依赖时,宿主启动时未选中的 bundle 也能正常加载默认 Source 和 Strategy。Mnemon 先准备依赖解析,再启动原生分组,并在自己的注入作用域中注册 Web RPC。官方 WebUI 安装后可直接点击“立即启用”。 +- **Native CLI 静默运行**([#306](https://github.com/omdsh-dev/dsh-mnemon/pull/306)、[#305](https://github.com/omdsh-dev/dsh-mnemon/issues/305)):对经过验证的 Mnemon npm 安装,记忆操作与版本检查直接调用 Native 可执行文件,避免 npm 启动器在 Windows 上额外弹出控制台窗口。更新仍由 npm 管理,并继续兼容其他 CLI 安装方式。 + +## 升级与兼容 + +支持的宿主仍为 **DSH 0.1.7-rc.2**。完整 Profile 需要 Node `^22.19.0 || >=24.0.0`;包的 Node 入口导入下限仍为 20。旧宿主请继续使用 v0.5.16。 + +可在官方 WebUI 的“添加插件”中安装 `dsh-mnemon@0.5.18`,然后点击“立即启用”。也可使用 CLI: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.18 +``` + +Headless Profile 将命令中的 `web` 替换为 `headless`。如果运行中的宿主已经加载旧版,请在升级后重启一次 DSH 并刷新浏览器,以加载新代码;新版首次安装和启用无需重启。更新或卸载已加载过的包,仍须遵循宿主正常的重启要求。 + +本版不需要迁移记忆存储。核心和组件开关保留现有行为与保存的选择。请保持新增的 Starter 就绪组件启用,使用 bundle 或核心开关控制整套组合。其他已加载插件保留其依赖路径,DSH 源码和共享 Connection 实例保持不变。 + +上游 `cordis:group` 显示与开关问题仍记录在[兼容性说明](../reference/compatibility.md#dsh-017-bundle-组件列表)中,不代表运行中的 Source 或 Strategy 已停止。 + +## 变化的包 + +| 包 | 上版 | 本版 | +| --- | --- | --- | +| `dsh-mnemon` | 0.5.17 | **0.5.18** | +| `dsh-mnemon-source-memory-spaces` | 0.5.11 | **0.5.12** | + +Starter 仍精确固定 17 个组件包,另外 16 个组件版本不变。 + +## 验证 + +参见[版本化发布验收](../../pr-assets/release-v0.5.18/README.zh-CN.md)、[启用回归证据](../../pr-assets/desktop-live-activation/README.zh-CN.md)和[桌面 CLI 验收](../../pr-assets/issue-305-desktop-console/README.md)。发布流程会验证完整固定组合、Registry 安装和真实升级,全部通过后才创建 GitHub Release。 + +上一个版本:[v0.5.17](./v0.5.17.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.5.19.md b/dsh/docs/zh-CN/releases/v0.5.19.md new file mode 100644 index 000000000..2808a1dc3 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.5.19.md @@ -0,0 +1,47 @@ +# v0.5.19:支持 DSH 0.2、桌面版管理记忆与新手安装指引 + +**简体中文** | [English](../../en/releases/v0.5.19.md) + +本补丁版包含 v0.5.18 后合入的两个 PR: + +- **支持 DSH 0.2.0-rc.1**([#311](https://github.com/omdsh-dev/dsh-mnemon/pull/311)):DSH 在安装插件前以及每次启动 profile 时,都会检查所有 `@deepseek-ai/dsh` 与 `@deepseek-ai/dsh-*` peer 范围;此前的版本只声明支持 DSH 0.1.7,因此被 DSH 0.2 拒绝。Starter 与记忆空间 Source 现在接受 `^0.1.7-rc.2 || ^0.2.0-rc.1`,并由测试对两个运行时执行 DSH 自己的检查。 +- **在 DSH 桌面版中管理记忆**([#310](https://github.com/omdsh-dev/dsh-mnemon/issues/310)、[#311](https://github.com/omdsh-dev/dsh-mnemon/pull/311)):桌面版窗口从应用自己的 `dsh-app://app/` 地址加载,Mnemon 曾把它当作远程页面,在默认的 `remoteAccess: read-only` 下,记忆系统与插件设置只读,而 Agent 工具仍可写入。应用窗口现在使用 Mnemon 的本地通道。从其他设备打开的页面保持默认只读,并会写明需要的授权与重启。 +- **全新安装后的状态页**([#311](https://github.com/omdsh-dev/dsh-mnemon/pull/311)):Mnemon Native 会写出 CLI 版本,不再一直显示等待版本信息。 +- **记忆交互保持一致**([#307](https://github.com/omdsh-dev/dsh-mnemon/pull/307)):回合记忆栏与“存入记忆”使用记忆系统的脑形标识;回合记忆栏写出每个工具做了什么,点击条目即在所在位置打开。对话与记忆空间页的“存入记忆”是同一个对话框,带任务 Agent 就绪标签,回执使用候选内容的语言。Mnemon 使用 DSH 的输入框、选择器与对话框按钮,对话框位于 DSH 的弹窗层级。工作区刷新会重新加载当前页面,状态页列出正在运行的 Provider,Native Provider 统一称为 Mnemon Native。 +- **文档**([#307](https://github.com/omdsh-dev/dsh-mnemon/pull/307)、[#311](https://github.com/omdsh-dev/dsh-mnemon/pull/311)):新增[安装与启动](../guides/installation.md),从一台空白电脑开始,经网页、桌面版或命令行一直走到第一条记忆;README 与各指南展示由真实模型录制的交互。 + +## 升级与兼容 + +支持的宿主为 **DSH 0.1.7-rc.2**(npm `latest`)与 **DSH 0.2.0-rc.1**(npm `next`)。完整 Profile 需要 Node `^22.19.0 || >=24.0.0`;包的 Node 入口导入下限仍为 20。旧宿主请继续使用 v0.5.16。 + +在**插件 → 添加插件**中安装 `dsh-mnemon@0.5.19`,然后点击**立即启用**。也可使用 CLI: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.19 +``` + +Headless Profile 将命令中的 `web` 替换为 `headless`。发布后的 24 小时内请带上版本号:pnpm 11 不会选用发布不足一天的版本,会改装上一个版本,而 DSH 0.2 会拒绝它。24 小时后直接输入 `dsh-mnemon` 即可安装 0.5.19。带版本号安装会固定版本,之后用 `dsh plugin --profile web update --latest dsh-mnemon` 升级。详见[安装与启动](../guides/installation.md#常见问题)。 + +- **升级到 DSH 0.2:** 先更新 dsh-mnemon,再升级 DSH。若 DSH 0.2 启动时遇到旧版本,会将其停用;记忆数据不受影响,更新后即可恢复。 +- **桌面版:** 在应用的插件页中更新。无需修改 `remoteAccess`;仅为绕过 #310 而添加的 `remoteAccess: trusted-host` 可以删除。 +- 如果运行中的宿主已经加载旧版,请重启一次 DSH 并刷新浏览器。全新安装与“立即启用”无需重启。 + +本版不需要迁移记忆存储或配置。插件作者请注意:`dsh-mnemon/client` 的 Source 页面 SDK 新增 `SearchField`、`SelectField`、`WriteReceipt`、`TaskAgentTag` 与 `MemorySourcePageProps.refreshKey`,使用它们的三个官方 Source 现在要求 `dsh-mnemon ^0.5.19`。 + +## 变化的包 + +| 包 | 上版 | 本版 | +| --- | --- | --- | +| `dsh-mnemon` | 0.5.18 | **0.5.19** | +| `dsh-mnemon-source-runtime` | 0.5.10 | **0.5.11** | +| `dsh-mnemon-source-documents` | 0.5.7 | **0.5.8** | +| `dsh-mnemon-source-memory-spaces` | 0.5.12 | **0.5.13** | +| `dsh-mnemon-provider-mnemon-native` | 0.5.6 | **0.5.7** | + +Starter 仍精确固定 17 个组件包,另外 13 个组件版本不变。 + +## 验证 + +参见两个受支持宿主上的[版本化发布验收](../../pr-assets/release-v0.5.19/README.zh-CN.md)、[桌面版窗口证据](../../pr-assets/issue-310-desktop-window/README.zh-CN.md)、[安装图集](../../assets/install-v0.5.19/README.md)与 [v0.5.19 真实模型图集](../../assets/webui-v0.5.19/README.md)。发布流程会验证完整固定组合、Registry 安装和真实升级,全部通过后才创建 GitHub Release。 + +上一个版本:[v0.5.18](./v0.5.18.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.5.2.md b/dsh/docs/zh-CN/releases/v0.5.2.md new file mode 100644 index 000000000..f053d4f24 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.5.2.md @@ -0,0 +1,50 @@ +# v0.5.2:Starter 原子停用与独立发版 + +**简体中文** | [English](../../en/releases/v0.5.2.md) + +v0.5.2 修复 [Issue #185](https://github.com/omdsh-dev/dsh-mnemon/issues/185) 报告的 v0.5 Starter 生命周期回归,同时完成 Source、Strategy 与 Provider 从统一版本转为独立版本发布的一次性迁移。 + +## 修复 + +既有的 `mnemon` Entry 继续作为公开配置目标,并重新成为完整 Starter 的生命周期总开关: + +```yaml +- id: mnemon + disabled: true +``` + +该覆盖项现在会同时停用 Core/Host、Runtime、Documents、Memory Spaces、默认三层 Strategy 和三个可选 Strategy 增强。下游四个 Entry 不再等待缺失的 `mnemonMemory`,因此 Mnemon 停用时 DSH Host 与 WebUI 仍可正常启动。 + +所有既有 Entry id 和配置目标保持有效。停用 Starter 不会卸载包、调用 Provider、迁移存储或删除记忆数据。删除该覆盖项,或把它改为 `false`,再重启 DSH,即可重新启用完整 Starter。[PR #186](https://github.com/omdsh-dev/dsh-mnemon/pull/186)。 + +## 独立包发布 + +本版本会把 `dsh-mnemon` 与十六个官方插件一次性全部发布为 `0.5.2`。这是因为独立版本迁移修改了每个包的已发布依赖元数据,所以首次切换需要全量发布。 + +从 v0.5.2 之后开始,只有自身发布制品发生变化的包才升级。`dsh-mnemon` 继续作为聚合 Starter 与经过完整验证的精确 BOM;任一固定插件版本变化时,Starter 也会升级。发布 workflow 会相对上一版本计算选择集,按依赖层有界并发发布选中的插件,完成 Registry 回读验证后最后发布 Starter。[PR #181](https://github.com/omdsh-dev/dsh-mnemon/pull/181)。 + +## 升级与兼容性 + +在所属 DSH profile 中安装精确稳定版并重启: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.2 +``` + +Headless profile 将 `web` 改为 `headless`。v0.5.2 Starter 会精确固定经过验证的十六个 `0.5.2` 官方插件,普通用户不需要逐个安装;稳定 npm 通道仍是 `latest`。 + +本版本不迁移 RPC 权限、凭据、存储路径、记忆格式、Document 格式、Provider 数据或扩展契约,现有 v0.5.x profile 与数据可直接使用。与任何升级一样,请先停止写入并备份有效记忆根、工作区 Documents、DSH profile/配置和单独管理的 Provider 数据。 + +## 验证 + +报告中的故障已在 DSH 0.1.2-rc.1、Node.js 22.23.2 环境中,通过 profile 保留的 `disabled: true` 精确复现。修复前 Runtime、Documents、Memory Spaces 和默认 Strategy 都会持续等待 `mnemonMemory`;同一配置在只有单体 Entry 的 v0.4.7 中看起来不会影响启动。 + +修复后的构建通过完整 workspace 校验、全部包构建/类型检查/测试、确定性输出、公开入口与包内容检查、真实 DSH Headless 激活,以及隔离的 Linux Node.js 22.23.2 验证。一次性真实 WebUI profile 正常启动,并显示八个 Mnemon Entry 全部停用;浏览器没有错误,模型也没有收到任何 Mnemon 工具。实现 PR 保留了截图和精确回归夹具。 + +发布 workflow 还会冻结并验证选定制品,独立安装每个插件,编译外部 SDK 消费者,验证真实 Starter 组合,从 npm 回读全部包,并对从 v0.4.7 升级执行 Registry 冒烟测试。 + +## 回滚 + +在同一 profile 中重新安装 `dsh-mnemon@0.5.1` 并重启 DSH,无需转换数据。如果回滚后 profile 仍保留停用的 `id: mnemon`,v0.5.1 的 pending Entry 故障可能重新出现;启动旧 Host 前,应删除该覆盖项,或显式停用全部四个依赖的 Source/Strategy Entry。 + +上一版:[v0.5.1](./v0.5.1.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.5.20.md b/dsh/docs/zh-CN/releases/v0.5.20.md new file mode 100644 index 000000000..898bebb76 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.5.20.md @@ -0,0 +1,41 @@ +# v0.5.20:不再有单独的 Starter 开关 + +**简体中文** | [English](../../en/releases/v0.5.20.md) + +本补丁版包含 v0.5.19 后合入的三个 PR: + +- **Starter 的就绪步骤并入组件组**([#314](https://github.com/omdsh-dev/dsh-mnemon/pull/314)):0.5.18 与 0.5.19 在插件页单独列出一行就绪条目 `dsh-mnemon/starter`,组件组要等它就绪。关闭这一行(例如为了消除更新后的一条报错)会让全部组件一直等待 `mnemonStarterReady`:`dsh web` 启动后没有记忆系统,桌面版则按启动失败处理并提议移除插件。组件组 `mnemon-bundle` 现在使用模块 `dsh-mnemon/bundle`,由它自己准备 Starter 的依赖解析,再用 DSH 自己的 `cordis:group` 挂载各组件。“立即启用”仍然无需重启,那一行已经去掉,处于卡住状态的 profile 更新并重启后即可恢复。 +- **更新后的恢复说明**([#313](https://github.com/omdsh-dev/dsh-mnemon/pull/313)、[#315](https://github.com/omdsh-dev/dsh-mnemon/pull/315)):[安装与启动](../guides/installation.md#常见问题)补充了更新后不重启出现的 `ERR_PACKAGE_PATH_NOT_EXPORTED`、`waiting for service: mnemonStarterReady` 与桌面版的插件恢复页,并说明 pnpm 的一天期限可能退回不止一个版本。 + +## 升级与兼容 + +支持的宿主不变:**DSH 0.1.7-rc.2**(npm `latest`)与 **DSH 0.2.0-rc.1**(npm `next`)。完整 Profile 需要 Node `^22.19.0 || >=24.0.0`。旧宿主请继续使用 v0.5.16。 + +在**插件 → 添加插件**中安装 `dsh-mnemon@0.5.20`,然后点击**立即启用**。也可使用 CLI: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.20 +``` + +发布后的 24 小时内请带上版本号。pnpm 11 不会选用发布不足一天的版本;由于 0.5.18 与 0.5.19 都在 2026-09-28 发布,在北京时间 2026-09-30 01:31(UTC 09-29 17:31)之前它会退回 0.5.17,而 DSH 0.2 会拒绝这个版本。带版本号安装会固定版本,之后用 `dsh plugin --profile web update --latest dsh-mnemon` 升级。 + +- **已有安装:** 发布后的 24 小时内运行 `dsh plugin --profile web add dsh-mnemon@0.5.20`,因为在此之前 pnpm 11 的 `update` 与 `update --latest` 都会停留在已安装的版本;之后运行 `dsh plugin --profile web update dsh-mnemon`(带版本号安装的用 `update --latest`)即可。然后重启 DSH。桌面版在应用的插件页中更新,然后完全退出并重新打开应用。在重启之前,从 0.5.18 或 0.5.19 更新后会提示 `mnemon-bundle (dsh-mnemon/bundle): pending (waiting for service: mnemonStarterReady)`,因为正在运行的加载器仍保留旧的组件组。 +- profile patch 中残留的 `- id: mnemon-starter` 不再对应任何条目,会被忽略。桌面版日志会提示 `patch: entry "mnemon-starter" not found`,可以删除这两行。`dsh-mnemon/starter` 仍然导出,供仍插入它的组合使用。 +- 插件页中的容器行显示为 `dsh-mnemon/bundle`,不再是 `cordis:group`,并与以前一样显示为已关闭([上游 #649](https://github.com/dsh-external/issues/issues/649))。 +- DSH 的配置 schema 导出(`dsh web --dump-config-schema`)只遍历原生 group,现在会把 `mnemon-bundle` 报告为无法识别的树载体,并略去 Mnemon 各组件的 schema,与 DSH 自带的 agent preset 相同。运行时配置与 profile patch 不受影响。 + +本版不需要迁移记忆存储或配置。 + +## 变化的包 + +| 包 | 上版 | 本版 | +| --- | --- | --- | +| `dsh-mnemon` | 0.5.19 | **0.5.20** | + +Starter 仍然固定 17 个组件包,版本都没有变化。 + +## 验证 + +[版本化发布验收](../../pr-assets/release-v0.5.20/README.zh-CN.md)在两个受支持的宿主上通过“添加插件”安装发布包。[组件组验收记录](../../pr-assets/starter-group-readiness/README.zh-CN.md)对比了改动前后的插件页。[冷启动与升级验收](../../pr-assets/starter-group-upgrade/README.zh-CN.md)覆盖网页、命令行、Headless 与桌面版布局,以及从 0.5.16 到 0.5.19 的升级。发布流程在创建 GitHub Release 之前,会验证完整的固定组合、Registry 安装与真实升级。 + +上一版本:[v0.5.19](./v0.5.19.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.5.21.md b/dsh/docs/zh-CN/releases/v0.5.21.md new file mode 100644 index 000000000..eb3460fd7 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.5.21.md @@ -0,0 +1,57 @@ +# v0.5.21:大型 Native 记忆空间与更精简的热记忆 + +**简体中文** | [English](../../en/releases/v0.5.21.md) + +本补丁版本包含 v0.5.20 之后合并的三个 PR: + +- **大型 Native 空间恢复可用**([#321](https://github.com/omdsh-dev/dsh-mnemon/pull/321),issue [#320](https://github.com/omdsh-dev/dsh-mnemon/issues/320))。 + - Mnemon Native 在三处读取整个记忆空间:内容列表、图谱,以及运行时记忆满容量归档前的逐字比对。此前每条 Mnemon 命令的输出上限都是 2 MiB,而约 1,000 条记忆的空间会返回 3–9 MB。 + - 在 900 条记忆的空间上,内容页显示 0 条记忆,图谱显示“连接不可用”;满容量时添加工作记忆会失败并提示安装 Mnemon,工作记忆因此无法腾出空间。 + - 现在这几次整库读取的上限为 128 MiB,其余命令仍为 2 MiB。失败时会写明原因:只有启动失败才提示安装 Mnemon。 +- **空闲审查每轮只写一层**([#322](https://github.com/omdsh-dev/dsh-mnemon/pull/322),issue [#319](https://github.com/omdsh-dev/dsh-mnemon/issues/319))。 + - 空闲审查是唯一无人值守的写入者,此前会把稳定的项目事实和决定写入每轮都会加载的 MEMORY.md,同一轮审查可能把同一知识同时写成项目档案和工作记忆。 + - 现在审查先考虑项目档案。MEMORY.md 只收用户明确说过、且项目档案和已有条目都未涵盖的简短规则,例如约定、纠正、环境事实或工具特性;设计、实现细节、路径、端口等项目记录不会进入。 + - 审查 guard 让每轮只写一层:建了项目档案后,修改工作记忆会被拒绝;修改工作记忆后,新建项目档案会被拒绝。USER.md 的修改不受影响。 + - 分层策略页面的**空闲审查**中新增**写入运行时记忆**开关(`idleReview.runtimeMemory`,默认开启),关闭后审查只创建项目档案。 +- **兜底侧栏入口与 DSH 原生行一致**([#323](https://github.com/omdsh-dev/dsh-mnemon/pull/323),issue [#318](https://github.com/omdsh-dev/dsh-mnemon/issues/318))。 + - 在没有 DSH 原生面板席位的布局中,Mnemon 会自行插入“记忆系统”入口,而它沿用了旧样式:与 DSH 的行并排时字更小、偏灰,并向右缩进 8px。 + - 现在它沿用 DSH 面板行的数值:继承的 14px 主要文字色、相同的内边距、外边距与圆角、16px 图标(折叠时 18px)以及 DSH 的高亮;打开时还会标记 `aria-current="page"`。有原生席位的布局不受影响。 + +## 升级与兼容性 + +支持的宿主:**DSH 0.2.0-rc.2**(npm `latest` 与 `next`)和 **DSH 0.1.7-rc.2**。完整 profile 需要 Node `^22.19.0 || >=24.0.0`。更早的宿主请继续使用 v0.5.16。 + +在**插件 → 添加插件**中安装 `dsh-mnemon@0.5.21`,然后选择**立即启用**。也可以使用命令行: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.21 +``` + +发布后 24 小时内请带上版本号。pnpm 11 不会选用发布不足一天的版本,在此之前普通安装或更新会得到 0.5.20,两个宿主都接受它。带版本号安装会固定版本,之后可用 `dsh plugin --profile web update --latest dsh-mnemon` 升级。 + +- **已有安装:**发布后 24 小时内运行 `dsh plugin --profile web add dsh-mnemon@0.5.21`,之后 `dsh plugin --profile web update dsh-mnemon` 即可(带版本号安装的用 `update --latest`),然后重启 DSH。桌面版在应用的插件页中更新后完全退出并重新打开。 +- **空闲审查:**已有 profile 在新规则下继续写入运行时记忆;关闭**写入运行时记忆**可让审查只写项目档案。本版本之前审查写入的 MEMORY.md 条目会保留,直到手动删除。 +- **大型空间:**新上限需要新的记忆空间 Source 与 Mnemon Native 同时生效,Starter 已锁定两者;在旧版记忆空间宿主上,Mnemon Native 0.5.8 仍使用 2 MiB 上限。 + +无需迁移记忆数据或配置。 + +## 变更的包 + +| 包 | 原版本 | 新版本 | +| --- | --- | --- | +| `dsh-mnemon` | 0.5.20 | **0.5.21** | +| `dsh-mnemon-source-memory-spaces` | 0.5.13 | **0.5.14** | +| `dsh-mnemon-provider-mnemon-native` | 0.5.7 | **0.5.8** | + +Starter 锁定 17 个组件包,其余 15 个不变。 + +## 验证 + +[版本化发布验收](../../pr-assets/release-v0.5.21/README.zh-CN.md)在两个受支持的宿主上通过“添加插件”安装发布包。每项修复另有修复前后记录: +- [大型 Native 空间](../../pr-assets/issue-320-native-store-dumps/README.zh-CN.md); +- [空闲审查只写一层](../../pr-assets/issue-319-review-layers/README.zh-CN.md); +- [兜底侧栏入口](../../pr-assets/issue-318-sidebar-entry/README.zh-CN.md)。 + +发布流程会在创建 GitHub Release 前验证完整的锁定组合、Registry 安装与真实升级。 + +上一版本:[v0.5.20](./v0.5.20.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.5.22.md b/dsh/docs/zh-CN/releases/v0.5.22.md new file mode 100644 index 000000000..4b03b5b15 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.5.22.md @@ -0,0 +1,56 @@ +# v0.5.22:应用内更新、会话标签页与 Ollama 上的子代理 + +**简体中文** | [English](../../en/releases/v0.5.22.md) + +本补丁版本包含 v0.5.21 之后合并的三个 PR: + +- **检查版本 → 更新在桌面版中可用**([#325](https://github.com/omdsh-dev/dsh-mnemon/pull/325))。 + - 此前 Host 在所属 Profile 中运行 PATH 上的 pnpm 来更新 dsh-mnemon。桌面版的 Host 在 PATH 上找不到 pnpm,面板只能提示缺少 pnpm;DSH 的插件页又不能更新已安装的插件。 + - 现在更新通过 DSH 自己的插件安装器完成,效果与 `dsh plugin add dsh-mnemon@<版本>` 相同:使用 Profile 自己的包管理器(桌面版中即应用自带的 pnpm),沿用 Profile 的安装源设置、备用源与锁;安装检查到的精确版本,不受 pnpm 24 小时发布窗口的限制。Profile 单独安装的可选 Strategy 也以相同方式更新。 + - 保留新版本之前,DSH 会检查它声明支持的 DSH 版本。安装失败时恢复 Profile 的文件,面板显示 pnpm 的错误行与 DSH 的日志;已启用的插件保持不变,DSH 列出新版本后才报告成功。 + - 插件文件一变化,DSH 就会载入新的记忆系统页面,此前这会在更新报告结果之前关闭面板。现在新页面会重新打开状态页与面板,显示这次更新以及需要的重启。 + - DSH 重启之前,记忆系统每个页面的顶部都会提示已安装的版本与仍在运行的版本,无论更新来自检查版本还是 `dsh plugin`。状态页显示正在运行的版本,检查版本显示已安装的版本,不再提供 `dsh plugin` 已经安装的更新。 +- **会话的 Agent 未加载时,会话标签页中的记忆系统也能使用**([#326](https://github.com/omdsh-dev/dsh-mnemon/pull/326))。 + - 此前 Mnemon 只能通过已加载的 Agent 找到会话所属的工作区。对于 DSH 已显示但尚未恢复的会话(例如所用 Agent 预设已不在 profile 中),状态页显示“等待工作区”,项目档案报 `memory Source is unavailable in the requested management scope`。 + - 使用工作区或集中存储时,该标签页中的运行时记忆和记忆空间用的是 DSH 启动时所在的目录;这样保存的条目仍留在那个目录中。 + - 现在 Host 通过 DSH 的工作区注册表找到会话所属的工作区,无论其 Agent 是否已加载,注册表都列有该会话。新建项目档案直接写入;项目档案已满时,由会话所属工作区中的任务 Agent 先归档最久未用的档案;新建记忆空间时也由任务 Agent 选择 Provider。这两项此前都会报“current DSH agent is not live”。 +- **聊天模板要求用户查询时,记忆子代理可以自行恢复**([#328](https://github.com/omdsh-dev/dsh-mnemon/pull/328),issue [#327](https://github.com/omdsh-dev/dsh-mnemon/issues/327))。 + - 在 Ollama 0.33 上使用 Qwen3.x 时,后台审查和其他记忆子代理会报 `no user query found in messages`,有时审查已经创建了项目档案。子代理唯一的用户消息是它的任务提示;工具结果填满上下文窗口后,Ollama 截断时丢掉这条提示、保留了其后的工具结果。 + - 现在服务端以这种方式拒绝子代理的请求时,Mnemon 会在这一步追加一条简短的用户消息并立即重试;该子代理之后的工具续轮也以同样的消息结尾。从不拒绝的服务端上的子代理和主会话,发出的请求与之前完全相同。调大 Ollama 的上下文长度也能避免截断。 + +## 升级与兼容性 + +支持的宿主:**DSH 0.2.0-rc.2**(npm `latest` 与 `next`)和 **DSH 0.1.7-rc.2**。完整 profile 需要 Node `^22.19.0 || >=24.0.0`。更早的宿主请继续使用 v0.5.16。 + +在**插件 → 添加插件**中安装 `dsh-mnemon@0.5.22`,然后选择**立即启用**。也可以使用命令行: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.22 +``` + +发布后 24 小时内请带上版本号。pnpm 11 不会选用发布不足一天的版本,在此之前普通安装或更新会得到 0.5.21,两个宿主都接受它。带版本号安装会固定版本,之后可用 `dsh plugin --profile web update --latest dsh-mnemon` 升级。 + +- **已有安装:**发布后 24 小时内运行 `dsh plugin --profile web add dsh-mnemon@0.5.22`,之后 `dsh plugin --profile web update dsh-mnemon` 即可(带版本号安装的用 `update --latest`),然后重启 DSH。 +- **桌面版:**DSH 的插件页不能更新已安装的插件,0.5.21 及更早的版本也无法在桌面版中自行更新。请更新一次:在插件页移除 dsh-mnemon,再添加 `dsh-mnemon@0.5.22`,然后完全退出应用并重新打开,记忆数据会保留。从 0.5.22 起,可在**记忆系统 → 状态 → 检查版本 → 更新**中安装之后的版本。 +- **重启提示:**它由正在运行的 Host 报告,因此从 0.5.22 再更新时才会出现。从旧版本装上 0.5.22 后,请照常重启 DSH。 + +无需迁移记忆数据或配置。 + +## 变更的包 + +| 包 | 原版本 | 新版本 | +| --- | --- | --- | +| `dsh-mnemon` | 0.5.21 | **0.5.22** | + +Starter 锁定 17 个组件包,均未变化。 + +## 验证 + +[版本化发布验收](../../pr-assets/release-v0.5.22/README.zh-CN.md)在两个受支持的宿主上通过“添加插件”安装发布包。每项修复另有修复前后记录: +- [通过 DSH 安装器更新](../../pr-assets/version-update-through-dsh/README.zh-CN.md); +- [Agent 未加载的会话标签页](../../pr-assets/conversation-tab-workspace/README.zh-CN.md); +- [要求用户查询的聊天模板与子代理](../../pr-assets/issue-327-subagent-user-turn/README.zh-CN.md)。 + +发布流程会在创建 GitHub Release 前验证完整的锁定组合、Registry 安装与真实升级。 + +上一版本:[v0.5.21](./v0.5.21.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.5.23.md b/dsh/docs/zh-CN/releases/v0.5.23.md new file mode 100644 index 000000000..5111ca108 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.5.23.md @@ -0,0 +1,56 @@ +# v0.5.23:记忆压缩、查询与多路召回性能优化 + +**简体中文** | [English](../../en/releases/v0.5.23.md) + +本补丁版本发布 [PR #330](https://github.com/omdsh-dev/dsh-mnemon/pull/330) 中的算法优化,减少数据量增长时的重复扫描和分配: + +- **运行时记忆压缩:**一次索引原内容,按三个优先级稳定分组,并增量计算 UTF-8 字节数;从平均文本长度 L 下的 O(N²L) 降为 O(N+B),B 为总文本字节数。 +- **项目档案查询:**只为最终返回的档案生成摘要,保留实时正文读取、评分、结果顺序和最近访问时间更新。 +- **记忆空间发现与多路召回:**使用请求内 Map/Set 合并空间目录、解析固定空间和汇总质量计数器;保留完整候选排序、权限检查、失败处理与 Provider 调用顺序。 +- **批量写入准备:**按目标复用同步解析,使用可追加的分组数组和 Set,消除反复复制与扫描;Provider 持久化行为不变。 +- **Holographic 查询:**精确计算 token 集合交集,并以稳定的有界堆选择前 k 项,将有限分数的排名成本从 O(M log M) 降为 O(M log k+k log k)。小集合与非有限分数沿用原排序路径。 + +## 性能证据 + +同一机器、同一依赖下测试 N=1、10、100,并补充 N=1000。以下是 N=1000 时的每次操作中位数: + +| 本地操作 | 优化前 | 优化后 | +| --- | ---: | ---: | +| Runtime 压缩 | 41.347 ms | 4.047 ms | +| Document 搜索 | 40.484 ms | 22.165 ms | +| 多路召回编排 | 254.008 ms | 9.834 ms | +| 固定空间召回编排 | 2154.052 ms | 9.400 ms | + +这些是合成数据、预热文件缓存下的本地延迟;召回使用本地 adapter 夹具,不代表远程 Provider 或 LLM 的端到端延迟。复杂度结论同时来自代码分析与确定性工作量测试,不能单凭耗时证明。完整规模矩阵、参数、保留同阶的路径与测量限制见[算法审计报告](../../pr-assets/algorithm-performance-20261003/README.zh-CN.md)及其原始指标。 + +## 升级与兼容性 + +从 v0.5.22 可使用**记忆系统 → 状态 → 检查版本 → 更新**,完成后重启 DSH。命令行安装或更新到精确版本: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.23 +``` + +安装后重启正在运行的 DSH;新安装可在插件页选择**立即启用**。明确指定版本可避免包管理器的发布等待窗口影响版本选择。较早的桌面安装升级方式见[兼容性与升级](../reference/compatibility.md)。 + +沿用 v0.5.22 的 DSH 兼容范围,完整开发 Profile 使用 Node `^22.19.0 || >=24.0.0`。存储格式、配置、权限与 UI 流程不变,无需迁移已有记忆;不新增 Starter SDK 导出,插件 peer 下限不变。 + +## 变更的包 + +| 包 | 原版本 | 新版本 | +| --- | --- | --- | +| `dsh-mnemon` | 0.5.22 | **0.5.23** | +| `dsh-mnemon-source-runtime` | 0.5.11 | **0.5.12** | +| `dsh-mnemon-source-documents` | 0.5.8 | **0.5.9** | +| `dsh-mnemon-source-memory-spaces` | 0.5.14 | **0.5.15** | +| `dsh-mnemon-provider-holographic` | 0.5.5 | **0.5.6** | + +Starter 精确锁定这四个插件的新版本,其余 13 个组件版本不变。外部依赖的锁定版本与 peer 解析图保持不变。 + +## 验证 + +算法变更在合并前通过 2063 项本地测试(9 项可选集成测试跳过),以及独立插件、打包安装、真实 Headless、Node 20/22/24 与 Windows 的对应 CI 检查。等价性回归覆盖稳定排序、UTF-8 容量边界、实时文件读取、质量计数器、权限和有序批量回执。 + +版本 PR 重新验证版本化组合。发布流水线在创建 GitHub Release 前检查冻结制品、Registry 完整性、全部 17 个插件的安装组合,以及从 v0.5.16 的真实 Registry 升级。实际执行结果见仓库的 [Publish to npm 工作流](https://github.com/omdsh-dev/dsh-mnemon/actions/workflows/publish.yml)。 + +上一版本:[v0.5.22](./v0.5.22.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.5.24.md b/dsh/docs/zh-CN/releases/v0.5.24.md new file mode 100644 index 000000000..c47c6e83b --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.5.24.md @@ -0,0 +1,53 @@ +# v0.5.24:完整的实体计数与按需查找的相关记忆 + +**简体中文** | [English](../../en/releases/v0.5.24.md) + +本补丁版本发布 [PR #332](https://github.com/omdsh-dev/dsh-mnemon/pull/332)。在**记忆系统 → 记忆空间 → 实体**中,实体旁的数字与选中后列出的内容对不上:计数 22 的实体可能只列出 5 条,计数 5 的实体却可能列出 9 条。 + +- **计数来自全部记忆。** 实体列表把 Provider 状态中每个空间出现最多的 20 个实体相加,多数实体因此缺失,部分计数偏少。现在每个空间用 Provider 列出的记忆建立一份实体索引:实体的计数就是带有它的记忆数,列表包含全部实体。不同写法按概览图谱的方式合并,两处因此一致。 +- **选中后正好列出这些记忆。** 选中实体时走的是给 Agent 用的召回,其质量策略最多保留四条中相关结果,空出的名额被不带该实体的记忆补上。现在选中后立即列出带有该实体的全部记忆,按重要性排序,分页显示。 +- **相关记忆按需查找。** 列表下方的**查找相关记忆**会在不带该实体的记忆中召回与它相关的那些。召回每个空间约需半秒,因此只在需要时运行。展开后,在本次浏览器会话中切换到其他实体时保持展开,直到收起为止。输入的名称没有任何记忆带有时,相关记忆会自动显示。 +- **加载更平稳。** 各空间在索引读取完成前先行显示;读取耗时较长时才显示占位;对较早选择的迟到响应会被丢弃;删除的记忆立即从列表中移除。 +- **Provider。** `list()` 取不全整个空间的 Provider,可以实现可选的 `entityIndex(body)`,见[扩展开发](../development/extensions.md)。第一方 Provider 无需改动。 + +## 实测 + +合成的 Mnemon Native 空间,在 DSH 0.2.0-rc.2 与 0.1.7-rc.2 的 WebUI 中: + +| | v0.5.23 | v0.5.24 | +| --- | --- | --- | +| 列出的实体 | 63 个中的 52 个 | 63 个全部 | +| `Atlas`:计数 / 列出的记忆 | 22 / 20,其中 3 条不带该实体 | 22 / 22 | +| `WebUI`:计数 / 列出的记忆 | 3 / 8 | 4 / 4 | +| 点击后第一条记忆出现 | 400–1,891 毫秒 | 91–99 毫秒 | + +在四个各 500 条记忆的空间上,选中实体原先在 1.5–1.7 秒后显示 20 条结果;现在 7–10 毫秒后显示带有该实体的全部记忆(各次运行中为 855、211 与 86 条),需要时查找相关记忆用时 1.5–1.8 秒。全部运行结果,包括报告所用导出数据的副本,见[实体页记录](../../pr-assets/entities-complete-index/README.zh-CN.md)。 + +## 升级与兼容性 + +从 v0.5.22 可使用**记忆系统 → 状态 → 检查版本 → 更新**,完成后重启 DSH。命令行安装或更新到精确版本: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.24 +``` + +安装后重启正在运行的 DSH;新安装可在插件页选择**立即启用**。明确指定版本可避免 pnpm 发布后 24 小时的等待窗口选中较早的版本。较早的桌面安装升级方式见[兼容性与升级](../reference/compatibility.md)。 + +支持的宿主仍为 **DSH 0.2.0-rc.2**(npm `latest` 与 `next`)和 **DSH 0.1.7-rc.2**;完整 Profile 使用 Node `^22.19.0 || >=24.0.0`。存储格式与配置不变,无需迁移已有记忆。实体索引保存在内存中,空间变化后重新建立。不新增 Starter SDK 导出,插件 peer 下限不变。 + +## 变更的包 + +| 包 | 原版本 | 新版本 | +| --- | --- | --- | +| `dsh-mnemon` | 0.5.23 | **0.5.24** | +| `dsh-mnemon-source-memory-spaces` | 0.5.15 | **0.5.16** | + +Starter 精确锁定 Memory Spaces 的新版本,其余 16 个组件版本不变,外部依赖的锁定版本也保持不变。 + +## 验证 + +[版本化发布验收](../../pr-assets/release-v0.5.24/README.zh-CN.md)在两个支持的宿主上通过添加插件安装发布包,并检查实体页。修复本身另有[修复前后记录](../../pr-assets/entities-complete-index/README.zh-CN.md)。 + +发布流水线在创建 GitHub Release 前验证完整的锁定组合、Registry 安装与真实升级。 + +上一版本:[v0.5.23](./v0.5.23.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.5.3.md b/dsh/docs/zh-CN/releases/v0.5.3.md new file mode 100644 index 000000000..45c2d7fb7 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.5.3.md @@ -0,0 +1,50 @@ +# v0.5.3:更清晰的版本维护 + +**简体中文** | [English](../../en/releases/v0.5.3.md) + +v0.5.3 改进状态页版本维护,明确 Host 执行与 Source 展示的归属,并更新官方包内的文档。包含 [PR #190](https://github.com/omdsh-dev/dsh-mnemon/pull/190)、[PR #191](https://github.com/omdsh-dev/dsh-mnemon/pull/191) 和 [PR #192](https://github.com/omdsh-dev/dsh-mnemon/pull/192)。 + +## 版本维护 + +- 版本弹窗区分未安装、版本不可读、可更新、已是最新版、本地版本较新和等待重启等状态。Registry 查询失败时,仍可查看本地安装指引。 +- Mnemon CLI 安装与迁移引导使用官方 npm 包 `@mnemon-dev/mnemon`。Host 确认活动 launcher 属于当前 npm 安装后,可更新由 npm 管理的 CLI;继续支持 Homebrew 和 Go 更新。 +- Native 执行与版本检查共享 Memory Spaces 包的公开 CLI 发现入口。经过验证的 Windows npm launcher 通过 Node 执行,不调用任意 shell 脚本。 +- dsh-mnemon 子包默认折叠,展开后可查看 Source、Strategy、Provider 版本、Starter 固定版本与所属 Profile。独立安装的包可在该 Profile 中更新;Starter 依赖随 Starter 更新,源码链接保持不变。 +- 更新遵循管理权限和只读配置,拒绝并发写入,核验实际安装版本,并持续提醒重启,直到 Host 重新启动。 + +## 归属与文档 + +Host 回合与维护操作共享明确的执行归属和清理流程,覆盖后台向前台交接,并保留公开执行辅助逻辑的 Node 20 兼容性。Source 包自行维护默认页面文案和布局资产;Memory Spaces 配置与可选 Strategy 校验使用各自归属的公开辅助函数,保留既有默认行为。 + +紧凑工作台标题不再与较长的翻译文案重叠。已发布 README 与双语文档进一步说明可组合记忆边界、安装方式、独立包与数据范围,包含真实 DSH 截图和已验证的导航链接。 + +## 包版本与升级 + +本次 Starter 与十六个官方插件均升级到 `0.5.3`,因为各包的发布内容均有变化,包括 README。包仍采用独立版本;后续不会仅因 Starter 升级而重新发布未变化的包。 + +在所属 DSH Profile 中安装精确版本并重启: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.3 +``` + +Headless Profile 将 `web` 改为 `headless`。Starter 通过 npm `latest` 通道安装经过验证的精确插件组合。Mnemon CLI 单独在 Host 所在机器的终端中安装或迁移,要求 Node.js 22 或更高版本: + +```sh +npm install --global @mnemon-dev/mnemon@latest +mnemon --version +``` + +如果仍命中旧 CLI,先检查 `PATH`、`MNEMON_CLI_PATH` 与配置中的 `cliPath`,再重启 DSH。参见[快速开始](../guides/getting-started.md)和[运维指南](../guides/operations.md)。 + +## 兼容性、验证与回滚 + +既有 Entry id、配置、存储路径、记忆格式与 Provider 数据继续有效,无需数据迁移。升级前按运维指南备份有效记忆根、工作区 Documents、DSH Profile/配置和单独管理的 Provider 数据。 + +实现已通过 783 个根测试、workspace 验证、确定性构建、真实隔离 DSH Headless 激活,以及十六个插件独立仓库和十七个打包制品验证。[状态页证据](../../pr-assets/status-version-maintenance/README.md)记录了真实桌面和移动端版本弹窗检查。Windows launcher 使用模拟文件系统和进程夹具覆盖;显式启用的真实 Native 与 Windows 二进制测试需要各自环境。本次不消除[已记录的窄屏设置页限制](../reference/compatibility.md)。 + +发布 workflow 会验证并冻结版本制品,先发布插件依赖层、后发布 Starter,从 npm 安装完整组合,并在创建 GitHub Release 前对从 v0.4.7 升级执行 Registry 冒烟测试。 + +回滚时,在同一 Profile 中安装 `dsh-mnemon@0.5.2` 并重启 DSH,无需转换数据。若单独更新过 Profile 中独立维护的包或 CLI,需要分别恢复它们。 + +上一版:[v0.5.2](./v0.5.2.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.5.4.md b/dsh/docs/zh-CN/releases/v0.5.4.md new file mode 100644 index 000000000..e36270ce7 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.5.4.md @@ -0,0 +1,42 @@ +# v0.5.4:统一记忆空间表达 + +**简体中文** | [English](../../en/releases/v0.5.4.md) + +v0.5.4 将持久化记忆单元统一称为 **记忆空间**,英文对应 **memory space**。本版包含 [PR #194](https://github.com/omdsh-dev/dsh-mnemon/pull/194),覆盖产品文案、代码模型、公开 SDK 命名、响应式布局和文档。 + +## 统一表达与界面布局 + +- 工作台、设置、无障碍标签、Agent 指引、命令输出、随包 README 和双语指南使用一致术语。单条记忆、运行时记忆和项目档案继续保留各自含义。 +- 代码规范命名包括 `MemorySpace`、`MemorySpaceRegistry`,以及 `createSpace`、`mergeSpaces` 等服务操作。 +- 窄屏目录操作在可用宽度内换行,Provider 状态标签与存储标识允许换行,导航间距适配更长的标签。中英文目录与创建流程均在 390 × 844 下检查。 +- 当前文档使用更新后的桌面和窄屏截图。可编辑的中文示意图采用新术语;历史截图、测量和录屏保留原始像素及来源记录。 + +## 兼容性与现有数据 + +既有 v0.5.x 集成继续可用。旧模型名称与服务方法保留为兼容别名;Provider factory 在旧上下文之外接受规范 authority 名称,HTTP Provider 的旧 protected 字段继续兼容现有子类。 + +已发布的工具/RPC 名称、`memoryBodyId(s)` 等 wire 字段、registry 文件名与 JSON 结构、Document/Pack lineage 和 revision 编码沿用原有拼写,这些标识仍指记忆空间。用户自行设置的名称保持原样,包括使用旧中文术语的名称。不需要迁移数据;ID、数据位置、Provider 连接、管理授权与只读限制保持兼容。 + +## 发布包与升级 + +Starter 和全部十六个官方插件均升级到 `0.5.4`,因为各包都有代码或随包文档变化。独立版本机制继续生效,Starter 精确固定这组经过验证的组合。 + +在实际使用的 DSH Profile 中安装,然后重启 DSH: + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.4 +``` + +Headless Profile 将 `web` 替换为 `headless`。Mnemon CLI 继续独立维护,本版不修改其版本;安装和更新指引继续采用官方 `@mnemon-dev/mnemon` npm 包。参见[快速开始](../guides/getting-started.md)与[运维指南](../guides/operations.md)。 + +回滚时,在同一 Profile 安装 `dsh-mnemon@0.5.3` 并重启 DSH,无需转换数据。若另行更新了独立安装的插件,需要分别恢复。 + +## 验证与证据 + +实现已通过 783 项根测试、300 项插件测试、确定性构建、工作区类型检查、真实隔离 DSH Headless 激活、包检查,以及十六个插件独立仓库和十七个 tarball 的验证。外部 SDK 夹具同时编译新旧模型名称,并验证覆写旧 protected Provider 字段的子类。两项 opt-in Native/平台实机集成测试需要单独环境,本地已跳过。 + +[真实双语 UI 证据](../../assets/memory-space-terminology/README.md)使用可丢弃的合成数据,覆盖创建、编辑、激活、浏览、设置、语言切换和取消。十八张原始 JPEG 截图在版本化之前采集,基于 v0.5.3 加入本次实现;这些改动现已纳入 v0.5.4,截图版本标识对应采集时的基线。未评估模型质量或云端 Provider 行为。窄屏修复不代表已经覆盖所有设置页面,其他[已记录的兼容性限制](../reference/compatibility.md)仍适用。 + +发布流程验证版本化工作区和独立包,冻结制品完整性,先发布插件依赖层、后发布 Starter,从 npm 安装完整组合,并在创建 GitHub Release 前对从 v0.4.7 升级执行 Registry 冒烟测试。 + +上一版:[v0.5.3](./v0.5.3.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.5.5.md b/dsh/docs/zh-CN/releases/v0.5.5.md new file mode 100644 index 000000000..7ee9adc29 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.5.5.md @@ -0,0 +1,39 @@ +# v0.5.5:远程 RPC 路由与浅色素材 + +**简体中文** | [English](../../en/releases/v0.5.5.md) + +v0.5.5 修复已认证的远程 WebUI 调用可能收到 HTTP 403 的问题,并更新文档中的记忆空间截图与录制。本版包含 [PR #197](https://github.com/omdsh-dev/dsh-mnemon/pull/197) 和 [PR #196](https://github.com/omdsh-dev/dsh-mnemon/pull/196)。 + +## 远程页面使用已认证网关 + +远程浏览器对状态、Source 目录、设置、View、激活和管理操作的调用,改为使用 DSH 命名空间 API Gateway。本地回环客户端继续使用既有 RPC 通道。检测也覆盖 `isLoopback` 表示 Host 归属、但页面实际来自远程 origin 的远程桌面连接。 + +DSH 继续负责 Host/Origin 检查和浏览器认证或配对。Mnemon 对网关写操作、ZIP 操作、View mutation 和设置修改,另行要求启动配置 `remoteAccess: trusted-host`。没有该授权时,设置仍可读取,但返回 `writable: false`;普通读取与小范围激活继续可用。原有 `writeEnabled` 限制仍然生效。参见[远程部署](../guides/operations.md#cloud-hosted-webui)与[配置说明](../reference/configuration.md#浏览器认证)。 + +网关映射使用正式发布的 Typert protocol peer 契约,复用既有处理器,保留取消信号并校验返回的 RPC envelope,不改变 Provider 契约或存储格式。 + +## 浅色截图与录制 + +[双语浅色图集](../../assets/webui-v0.5.4/README.md)包含 60 张原始 JPEG 和两段约 42 秒的连续演示。采集前先将 Mnemon Pack 安全导入临时隔离存储,覆盖记忆空间、检索、实体、图谱、弹窗、npm CLI 引导、子包展开、设置、备份预览与窄屏布局。 + +随包 README 与当前指南均改用此图集。截图保留真实的 **v0.5.4 采集版本**,不将其标为 v0.5.5 远程传输修复的证据。142 份历史媒体的原始哈希保持不变;来源 ZIP、数据库、凭据和原始日志不随仓库分发。 + +## 发布包与升级 + +本次仅将 **`dsh-mnemon` 升级到 `0.5.5`**。十六个官方插件继续使用 `0.5.4`,由 Starter 精确固定;未变化的插件包不重复发布。 + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.5 +``` + +重启 DSH 并刷新浏览器,让新 Client 与 Host 路由同时生效。Headless Profile 将 `web` 替换为 `headless`。Mnemon CLI 继续通过 npm 上的 `@mnemon-dev/mnemon` 单独安装与维护。 + +不需要迁移记忆数据;既有 ID、Provider 连接、配置格式与存储数据保持兼容。远程管理需要上述显式授权,早期 0.1.2 Host 文档将 `remoteAccess` 仅描述为回滚选项的说法不再适用于该网关路径。回滚时在同一 Profile 安装 `dsh-mnemon@0.5.4` 并重启 DSH,十六个固定子包版本本就一致。 + +## 验证与边界 + +已合并的 RPC 修复通过本地、已配对远程、管理拒绝与受信管理路径测试。其脱敏的真实配对 HTTPS 冒烟测试确认,状态、Source 目录和设置请求通过网关成功返回;精确环境见 #197。素材审计检查了全部 60 张截图和 333 个视频源画面,旧标签 OCR 匹配为零,两段 MP4 均可成功解码。 + +发布门禁覆盖版本化工作区、独立插件制品、完整混合版本 Starter 安装、冻结制品完整性、npm 回读以及从 v0.4.7 升级。传输和 UI 机制测试不代表模型质量或真实云 Provider 行为;窄屏与 Host 设置页的已知限制继续按图集中的范围记录。 + +上一版:[v0.5.4](./v0.5.4.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.5.6.md b/dsh/docs/zh-CN/releases/v0.5.6.md new file mode 100644 index 000000000..c3f8f2048 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.5.6.md @@ -0,0 +1,54 @@ +# v0.5.6:内置工作区存储与记忆可靠性 + +**简体中文** | [English](../../en/releases/v0.5.6.md) + +v0.5.6 在 Core/Host 中加入集中式工作区存储,并包含 v0.5.5 之后合并的记忆可靠性修复:[#216](https://github.com/omdsh-dev/dsh-mnemon/pull/216)、[#212](https://github.com/omdsh-dev/dsh-mnemon/pull/212)、[#213](https://github.com/omdsh-dev/dsh-mnemon/pull/213)、[#214](https://github.com/omdsh-dev/dsh-mnemon/pull/214) 和 [#199](https://github.com/omdsh-dev/dsh-mnemon/pull/199)。 + +## 集中式工作区存储 + +在设置 → 记忆系统中选择**集中工作区**。Core/Host 将 Runtime、Documents、Memory Spaces 和 state 路由到 `/workspaces//`。可选根目录接受绝对路径或用户主目录相对路径;清空后使用 `MNEMON_DATA_DIR` 或 `~/.mnemon`。独立的 USER.md 设置仍可保留全局用户档案。 + +该能力内置于主包。Sidebar 检查所选的已注册工作区,Builtin 与 Headless 操作跟随所属会话。已有符号链接别名共享身份,移动或重命名工作区则选择新身份。切换范围保留旧数据,不自动迁移、合并或删除;远程 Provider namespace 保留自身的共享语义。详见[配置](../reference/configuration.md#workspaces)。 + +较长的记忆空间路径现在限制在当前列宽内;标签换行时,工作区选择与对齐操作仍然可用。 + +## 记忆与 CLI 修复 + +- 默认 Strategy 对命名工具、通用 Action、子 Agent 和浏览器管理统一执行 Runtime 容量归档。维护过程保留所选 Source、namespace、revision 检查与排队回合的所属关系。 +- Native 批量归档保留不同但相似的事实,仅复用完全相同的已有内容,并在 Runtime 压缩前验证导入回执。Host 与 Memory Spaces 的子进程读取保留跨输出块的 UTF-8 字符。 +- 自主写入 worker 不再调用 `mnemon_forget`;空闲审阅可创建独立 Document,但不能覆盖或归档已有 Document。用户显式操作保留写入工具,失败的 forget 回执不再声称删除成功。 +- Runtime 条目,以及活动、归档和搜索中的 Documents 均按创建时间从新到旧排列,加载更多之前也保持此顺序。编辑旧条目不会改变其创建日期。 +- Electron Host 下经验证的 npm JavaScript 启动器以 Node 模式运行,修复 CLI 版本检测、记忆操作、归属检查和更新,同时保留 embedding 覆盖配置与原生可执行文件行为。 + +## 包版本与升级 + +| 包 | 版本 | +|---|---| +| `dsh-mnemon` | `0.5.6` | +| `dsh-mnemon-source-runtime` | `0.5.5` | +| `dsh-mnemon-source-documents` | `0.5.5` | +| `dsh-mnemon-source-memory-spaces` | `0.5.5` | +| `dsh-mnemon-provider-mnemon-native` | `0.5.5` | +| `dsh-mnemon-strategy-default-three-tier` | `0.5.5` | + +其余十一个官方插件仍固定为 `0.5.4`。Starter 安装经过验证的十六插件精确组合。Mnemon CLI 仍通过 `@mnemon-dev/mnemon` 独立维护。 + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.6 +``` + +重启 DSH 并刷新浏览器;Headless Profile 将 `web` 换成 `headless`。DSH 基线保持 0.1.2-rc.1,完整 profile 需要 Node `^22.19.0 || >=24.0.0`。 + +已有存储格式保持兼容,启用新范围不会移动已有记忆。Pack 仅导出当前选中的根目录;如需保留所有工作区,应备份整个集中目录。回滚到 `dsh-mnemon@0.5.5` 前,请从新的 `workspaces` 范围切换到旧版支持的布局并保留备份。旧根目录仍留在磁盘上;回滚不会合并不同根目录中的数据。 + +## 验证与限制 + +发布门禁运行版本化工作区完整测试、独立包验证、打包后的 Starter 安装、冻结制品完整性检查、npm 回读和从 v0.4.7 开始的真实 Registry 升级。 + +[版本化发布验证](../../pr-assets/release-v0.5.6/README.zh-CN.md) 记录本地 1,176 个通过的测试、17 个打包制品,以及设置、0.5.6 版本和刷新后记忆的三张新 WebUI 截图。 + +[Core WebUI 报告](../../pr-assets/issue-189-core-workspaces/README.zh-CN.md) 保留九张截图,覆盖范围校验、双工作区隔离、全局 USER.md、真实 Native 存储、Host 重启、Sidebar/Builtin 路由、中文深色、英文浅色及 390 像素布局。[Electron 证据](../../pr-assets/issue-201-electron-cli/README.md)、[创建时间排序证据](../../pr-assets/issue-202-created-order/README.md) 和 [Document 保护证据](../../pr-assets/pr199-document-protection/README.md) 保留各自的原始测试 revision。 + +[Flash 容量测试](../../pr-assets/runtime-capacity-flash-20260909/README.zh-CN.md) 与[自动记忆验收](../../pr-assets/runtime-memory-quality-flash-20260909/README.zh-CN.md) 是已记录的有限实验。后者经用户审阅接受,同时保留部分替换、陈旧文档、临时内容留存及召回不完整等后续改进项。本版不声称已解决这些模型质量问题。常规 CI 跳过需显式开启的真实模型检查;Windows 二进制冒烟测试需要 Windows。 + +上一版:[v0.5.5](./v0.5.5.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.5.7.md b/dsh/docs/zh-CN/releases/v0.5.7.md new file mode 100644 index 000000000..8c3fccdeb --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.5.7.md @@ -0,0 +1,46 @@ +# v0.5.7:DSH 0.1.5 兼容与文档归档 + +**简体中文** | [English](../../en/releases/v0.5.7.md) + +v0.5.7 修复 DSH 0.1.5-rc.1 下记忆系统的 HTTP 405,提供受影响旧会话的显式副本修复工具,并使用 Host 验证的回执完成文档归档。包含 [#226](https://github.com/omdsh-dev/dsh-mnemon/pull/226) 和 [#224](https://github.com/omdsh-dev/dsh-mnemon/pull/224)。 + +## DSH 兼容与旧会话 + +Starter 补充已发布 DSH 0.1.5-rc.1 Profile 注册记忆系统 RPC 所需的 Connection 依赖。设置、状态和 Source 操作通过正常的鉴权传输加载。保留此前支持的 DSH peer 范围,开发与发布检查使用已发布的 0.1.5-rc.1 依赖族。 + +新生成的 Mnemon recall 和 instructions 消息不再包含会被 DSH 会话迁移拒绝的旧 summary 元数据。新增 `dsh-mnemon-repair-session` 命令,可预览受影响的 v0 JSONL 或 JSONL.zstd 文件,并写入独立修复副本。它仅移除两个已知的历史 Mnemon summary 字段,保留其余解码字节,并拒绝覆盖文件或处理不支持的输入。 + +已有会话不会被自动修复。先停止 DSH、备份整个会话根目录,再遵循[旧会话修复流程](../guides/operations.md#dsh-015-兼容与旧会话恢复)。先验证副本,再显式替换会话;后续格式迁移由 DSH 自身完成。保留原始备份用于回滚。普通 JSONL 修复支持 Node 20,压缩文件修复需要 Node 22.19 或受支持的更新 Node 版本。 + +## 文档归档 + +归档 worker 只提出索引摘要和一个现有的可用记忆空间,不写入数据。Host 在索引前验证提案与当前访问策略,补充精确的冷存储路径和内容 hash,并使用自身的持久回执构建 lineage。无效提案会让文档保持 active,且不产生索引。 + +完全匹配的已有索引可以复用。归档文档提交失败时,Host 补偿清理新建的索引。归档弹窗说明目标必须支持同步写入和删除,操作期间切换为只读也会被执行逻辑尊重。文档原始内容继续保存在冷存储中。 + +## 包版本与升级 + +| 包 | 版本 | +|---|---| +| `dsh-mnemon` | `0.5.7` | +| `dsh-mnemon-source-runtime` | `0.5.6` | +| `dsh-mnemon-source-documents` | `0.5.6` | +| `dsh-mnemon-source-memory-spaces` | `0.5.6` | + +Mnemon Native Provider 和 Default Three-tier Strategy 保持 `0.5.5`,其余十一个官方插件保持 `0.5.4`。Starter 固定完整的十六插件组合。Mnemon CLI 仍通过 `@mnemon-dev/mnemon` 独立维护。 + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.7 +``` + +重启 DSH 并刷新浏览器;Headless Profile 将 `web` 换成 `headless`。完整 DSH 0.1.5-rc.1 Profile 需要 Node `^22.19.0 || >=24.0.0`。Runtime、Documents 和 Memory Space 存储格式保持不变。降级 Mnemon 不会撤销 DSH 的会话迁移;回到旧版 DSH 时应使用保留的迁移前备份。 + +## 验证 + +[版本化发布报告](../../pr-assets/release-v0.5.7/README.zh-CN.md) 记录 1,218 项通过的测试、十六个独立插件仓库、十七个打包制品,以及 0.5.7 版本、成功归档和刷新后 Native 索引的三张新 WebUI 截图。 + +发布 workflow 验证版本化工作区、独立插件和打包升级,冻结四个变化制品,检查 npm 完整性与完整组合的干净安装,并在创建 GitHub Release 前测试从 v0.4.7 开始的 Registry 升级。 + +[DSH 兼容报告](../../pr-assets/issue-223-dsh-015/README.zh-CN.md) 保留十一张真实 WebUI 截图,包含 HTTP 405 复现及修复会话在重启后的重读。[文档归档报告](../../pr-assets/issue-222-document-archive/README.md) 保留十三张截图,覆盖成功归档、无效提案、重试、冷存储原文、召回与工具 lineage。这些报告保留各自的原始测试 revision。Loopback 模型夹具用于验证应用行为,不评价模型质量;真实模型与特定平台的 opt-in 测试单独执行。 + +上一版:[v0.5.6](./v0.5.6.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.5.8.md b/dsh/docs/zh-CN/releases/v0.5.8.md new file mode 100644 index 000000000..0610b22da --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.5.8.md @@ -0,0 +1,40 @@ +# v0.5.8:运行时归档安全与审查可靠性 + +**简体中文** | [English](../../en/releases/v0.5.8.md) + +v0.5.8 汇集已合入 [#241](https://github.com/omdsh-dev/dsh-mnemon/pull/241)、[#242](https://github.com/omdsh-dev/dsh-mnemon/pull/242)、[#243](https://github.com/omdsh-dev/dsh-mnemon/pull/243)、[#245](https://github.com/omdsh-dev/dsh-mnemon/pull/245)、[#246](https://github.com/omdsh-dev/dsh-mnemon/pull/246)、[#247](https://github.com/omdsh-dev/dsh-mnemon/pull/247) 的六项修复。[#244](https://github.com/omdsh-dev/dsh-mnemon/pull/244) 为 v0.5.7 已有的旧会话和 WebUI 兼容修复补充回归证据。 + +## 运行时归档 + +任何写入开始前,归档目标必须支持精确同步写入和安全删除。Host 再次核对所有目标及当前 Runtime revision,按目标保留回执,并且仅补偿清理能够证明本次新建的条目。去重或更新的记录会被保留,本地提交结果不确定时也不会删除归档副本。 + +无效路由提案按整批回退到经过验证的默认目标,保留此前有效批次和完整原文。路由期间被停用的目标会在 Provider 写入和 Runtime 变更之前被拒绝。若 Provider 已部分写入远端后抛错,却没有返回任何回执,仍无法保证跨存储回滚。 + +## 委派审查与 WebUI + +- 在 Host 生命周期内固定结果工具名称与结构。每次运行使用可撤销请求,校验子 Agent 归属、结果结构、取消、并发隔离与卸载。 +- 复用完整继承的审查证据。子 Agent 执行守卫覆盖其他插件直接注册的工具及 Code Mode 子调用;证据不完整时仍可进行有界的档案搜索。 +- 在记忆系统工作区和 Host 日志中显示后台审查失败。警告跨普通轮次保留,直到审查成功或会话重置;上下文窗口错误提供处理提示。 +- 让 Mnemon 侧栏入口在 ORCA LINK 皮肤下保持可见、可点击,覆盖侧栏收起布局。 + +本版验证了工具定义保持稳定,不声称测得模型缓存命中、token 节省或费用降低。 + +## 包版本与升级 + +仅 `dsh-mnemon` Starter 从 `0.5.7` 升级为 `0.5.8`,使用 npm 的 `latest` 通道。十六个独立版本插件保持已验证版本:Runtime、Documents、Memory Spaces Source 为 `0.5.6`;Mnemon Native Provider 和 Default Three-tier Strategy 为 `0.5.5`;其余十一个为 `0.5.4`。Mnemon CLI 继续独立维护。 + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.8 +``` + +重启 DSH 并刷新浏览器;Headless Profile 将 `web` 换成 `headless`。完整 DSH Profile 需要 Node `^22.19.0 || >=24.0.0`。已有存储格式和支持的 DSH peer 范围保持兼容。 + +## 验证 + +[版本化发布报告](../../pr-assets/release-v0.5.8/README.zh-CN.md) 记录 1,301 项通过测试、十六个独立插件、十七个制品,以及 Runtime 持久化和 0.5.8 / Native CLI 状态的新 WebUI 截图。 + +已合入修复包含真实 WebUI 对比、真实 Mnemon CLI 与多插件组合回归。详见[归档预检](../../pr-assets/issue-240-runtime-archive/README.md)、[路由回退](../../pr-assets/issue-235-runtime-routing/README.md)、[固定结果工具](../../pr-assets/issue-239-result-tool/README.md)、[审查守卫](../../pr-assets/issue-211-20260911/README.zh-CN.md)、[审查警告](../../pr-assets/issue-210-review-warning/README.md)、[ORCA 侧栏](../../pr-assets/issue-232-orca-sidebar/README.md)及[已有兼容修复](../../pr-assets/issues-225-230-20260911/README.zh-CN.md)记录。截图保留实际测试 revision;确定性的 loopback 模型验证应用行为,不评价模型质量。 + +发布 workflow 验证版本化工作区、十六个独立插件和十七个打包制品,冻结唯一变化的 Starter 制品,核对完整 npm 组合并执行从 v0.4.7 开始的真实 Registry 升级,最后创建 GitHub Release。 + +上一版:[v0.5.7](./v0.5.7.md)。 diff --git a/dsh/docs/zh-CN/releases/v0.5.9.md b/dsh/docs/zh-CN/releases/v0.5.9.md new file mode 100644 index 000000000..68db67256 --- /dev/null +++ b/dsh/docs/zh-CN/releases/v0.5.9.md @@ -0,0 +1,38 @@ +# v0.5.9:运行时归档目标与旧会话恢复 + +**简体中文** | [English](../../en/releases/v0.5.9.md) + +v0.5.9 包含 [#252](https://github.com/omdsh-dev/dsh-mnemon/pull/252) 的运行时归档目标修复、[#253](https://github.com/omdsh-dev/dsh-mnemon/pull/253) 的旧会话恢复,以及此前已合并的 [#249](https://github.com/omdsh-dev/dsh-mnemon/pull/249) 侧栏文字对齐修复。 + +## 运行时归档 + +触发容量维护的写入,现在可以归档到发起 View 在召回固定后启用或创建的合格空间。Host 向拥有该空间的 Memory Spaces Source 查询写入范围,并在每批写入前重新检查准确的 Source、View、活跃目标与取消状态。召回范围仍然固定,旧 Source 保留保守回退。拒绝操作时保留已有 Runtime 条目与待写入内容。 + +## 旧会话恢复 + +显式副本修复 CLI 支持三个已知旧 Mnemon summary、兼容的 v2 子 Agent descriptor、packed 空字符串占位值和 null delta name。它保留流组装、序号与 provenance、token 时序,以及其他插件的内容。 + +对于被旧 DeepSeek 适配器清空 ID 的完整工具链,修复只能恢复同一条流中已经记录的唯一 provider ID,并要求调用与结果正文一致、所有归属引用闭合。不会生成新 ID。缺少候选、身份冲突或引用无法确认时,仍拒绝输出整个副本。原文件与已有输出路径不会被覆盖。预览及写入新副本的命令见[运维指南](../guides/operations.md)。 + +尚未取得报告者原始的 Windows source-build 日志进行直接验证,因此 [Issue #251](https://github.com/omdsh-dev/dsh-mnemon/issues/251) 保持开启,等待这些文件的恢复确认。 + +## 发布包与升级 + +- `dsh-mnemon`:**0.5.9**。 +- `dsh-mnemon-source-memory-spaces`:**0.5.7**。 + +Starter 精确固定完整的十六插件组合,其余十五个插件版本与支持的 DSH peer 范围保持不变。Mnemon CLI 独立维护。 + +```sh +dsh plugin --profile web add dsh-mnemon@0.5.9 +``` + +重启 DSH 并刷新浏览器;Headless Profile 将命令中的 `web` 替换为 `headless`。完整 DSH Profile 需要 Node `^22.19.0 || >=24.0.0`。升级包不会自动改写旧会话文件,需要时使用显式修复命令。 + +## 验证 + +整合实现通过 1,452 项测试,6 项 opt-in 跳过,并完成真实 Mnemon 0.2.8 CLI、十六个独立插件仓库和十七个打包制品检查。[运行时报告](../../pr-assets/issue-250-runtime-write-scope/README.zh-CN.md)与[旧会话报告](../../pr-assets/issue-251-legacy-repair/README.zh-CN.md)保留 26 张前后对比和冷重开截图、哈希及实际验证的版本。旧会话通过真实 WebUI 续聊,同时校验发给模型的原工具 ID、参数和结果。模型响应采用确定性回环夹具。 + +发布流程验证版本化工作区与独立制品,只冻结两个变化的包,先发布 Memory Spaces Source,再发布 Starter;完整 Registry 安装和从 v0.4.7 开始的真实升级检查通过后,才创建 GitHub Release。 + +上一版:[v0.5.8](./v0.5.8.md)。 diff --git a/dsh/docs/zh-CN/roadmap.md b/dsh/docs/zh-CN/roadmap.md new file mode 100644 index 000000000..b859da428 --- /dev/null +++ b/dsh/docs/zh-CN/roadmap.md @@ -0,0 +1,39 @@ + + +# 路线图 + +**简体中文** | [English](../en/roadmap.md) | [文档中心](./README.md) + +本页区分已交付基础和剩余工作。未来条目是方向,不是版本承诺。 + +## 已交付的基础 + +- 独立 Source、Strategy、Provider 包,小型公共贡献服务、Source 自有页面,以及每个执行回合唯一的不可变 View。 +- 以分层策略为默认主策略、通用策略为可选主策略的 Starter,以及可与任一主策略配合的三个可选增强。 +- Source 内部的 Provider 描述符、连接 schema、脱敏和子 Fiber 注册。 +- 公开契约测试、仓库外独立制品验证、隔离 Headless/WebUI 夹具,以及选择性包发布。 + +实际范围和限制见[架构](./development/architecture.md)、[开发与验证](./development/README.md)与[发布流程](./development/releasing.md)。 + +## 下一步:可靠性与恢复 + +- 持久化后台审查水位与待处理活动,重启后恢复,并增加有界退避和显式重试。 +- 完善整理中断时的幂等和取消,保留部分写入及 Source 修订证据。 +- 在记忆准入前增加确定性的敏感内容防线;现有模型指引不是秘密扫描器。 +- 扩展真实 WebUI 与故障注入覆盖:范围、并发编辑、容量、远端部署路径、Provider 恢复和受支持 OS 组合。 +- 扩展[已验证兼容矩阵](./reference/compatibility.md),包括明确授权的真实 Provider 测试;适配器夹具并不足够。 + +## 后续:受控整理与扩展成长 + +- 可审阅的跨会话长期整理、冲突与衰减候选,以及明确执行的遗忘。 +- 更完整的多组件备份/恢复演练,损坏元数据和缺失投影的修复工具。 +- 在持久格式确需改变时提供明确的数据迁移和回滚流程。 +- 更清楚的后台审查历史与诊断,以及 Host 错误和命令输出的国际化。 +- 在 DSH 提供相应能力后复用通用目录选择器,跟踪 [dsh-external/issues#603](https://github.com/dsh-external/issues/issues/603)(私有仓库,需具备访问权限;未登录时返回 404)。 +- 为 RSI 提供候选 Source/Strategy 评估与受控晋升:保留输入、制品、权限及对照结果,再明确决定安装。 + +## 保持的边界 + +当前 UI 表达记忆行为,不提供通用插件市场或 View 画布。外部插件遵循 DSH 安装流程与公开作者契约。 + +Cordis 归属不是沙箱。当前不自动执行模型生成代码,不承诺通用删除、跨 Provider 事务、主动通知守护进程,或无需适配即可接入所有三方记忆插件。 diff --git a/dsh/go.mod b/dsh/go.mod new file mode 100644 index 000000000..f2dad0932 --- /dev/null +++ b/dsh/go.mod @@ -0,0 +1,7 @@ +// dsh/ holds the DeepSeek Harness plugin workspace (TypeScript, pnpm). It has +// no Go code. Declaring a module boundary here keeps the whole workspace out of +// the root module's zip, so `go install github.com/mnemon-dev/mnemon@latest` +// downloads only the CLI. +module github.com/mnemon-dev/mnemon/dsh + +go 1.24.6 diff --git a/dsh/locale/en.json b/dsh/locale/en.json new file mode 100644 index 000000000..6da7422b1 --- /dev/null +++ b/dsh/locale/en.json @@ -0,0 +1,3 @@ +{ + "meta": {} +} diff --git a/dsh/locale/zh.json b/dsh/locale/zh.json new file mode 100644 index 000000000..2c7c39337 --- /dev/null +++ b/dsh/locale/zh.json @@ -0,0 +1,6 @@ +{ + "meta": { + "title": "可组合记忆 (dsh-mnemon)", + "description": "面向 DeepSeek Harness 的可组合视图记忆。记忆来源与策略可插拔,开箱即用提供分层记忆。" + } +} diff --git a/dsh/package.json b/dsh/package.json new file mode 100644 index 000000000..20c140eb3 --- /dev/null +++ b/dsh/package.json @@ -0,0 +1,265 @@ +{ + "name": "dsh-mnemon", + "version": "0.5.24", + "description": "Composable, view-based memory for DeepSeek Harness. Pluggable sources and strategies, with layered memory out of the box.", + "keywords": [ + "deepseek-harness", + "deepseek-harness-plugin", + "dsh-plugin", + "agent-memory", + "llm-memory", + "persistent-memory", + "long-term-memory", + "runtime-memory", + "working-memory", + "cross-session-memory", + "multi-agent-memory", + "shared-agent-memory", + "memory-orchestration", + "memory-provider", + "pluggable-memory", + "context-management", + "project-memory", + "project-documents", + "document-search", + "semantic-recall", + "local-first", + "mnemon", + "openviking", + "honcho", + "mem0", + "hindsight", + "holographic-memory", + "retaindb", + "byterover", + "supermemory" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/mnemon-dev/mnemon.git", + "directory": "dsh" + }, + "homepage": "https://github.com/mnemon-dev/mnemon/tree/master/dsh#readme", + "bugs": { + "url": "https://github.com/mnemon-dev/mnemon/issues" + }, + "license": "MIT", + "publishConfig": { + "access": "public", + "tag": "latest" + }, + "engines": { + "node": ">=20" + }, + "type": "module", + "main": "lib/index.js", + "types": "lib/types/src/index.d.ts", + "typesVersions": { + "*": { + "contracts": [ + "lib/types/src/core/contracts/index.d.ts" + ], + "extension-sdk": [ + "lib/types/src/sdk/index.d.ts" + ] + } + }, + "exports": { + ".": { + "types": "./lib/types/src/index.d.ts", + "default": "./lib/index.js" + }, + "./core": { + "types": "./lib/types/src/core/plugin.d.ts", + "default": "./lib/core.js" + }, + "./client": { + "types": "./lib/types/src/client/index.d.ts", + "default": "./lib/client.js" + }, + "./contracts": { + "types": "./lib/types/src/core/contracts/index.d.ts", + "default": "./lib/contracts.js" + }, + "./extension-sdk": { + "types": "./lib/types/src/sdk/index.d.ts", + "default": "./lib/extension-sdk.js" + }, + "./testing": { + "types": "./lib/types/src/sdk/testing.d.ts", + "default": "./lib/testing.js" + }, + "./package.json": "./package.json", + "./locale/*.json": "./locale/*.json", + "./starter": { + "types": "./lib/types/src/starter.d.ts", + "default": "./lib/starter.js" + }, + "./bundle": { + "types": "./lib/types/src/bundle.d.ts", + "default": "./lib/bundle.js" + } + }, + "files": [ + "locale", + "lib", + "bin/repair-legacy-session.mjs", + "cordis.patch.yml", + "README.md", + "README.zh-CN.md", + "THIRD_PARTY_NOTICES.md", + "SECURITY.md", + "LICENSE" + ], + "dsh": { + "bundle": { + "patch": "./cordis.patch.yml" + }, + "client": { + "inject": [ + "@deepseek-ai/dsh-client-connection", + "@deepseek-ai/dsh-client-ui-conversation", + "@deepseek-ai/dsh-client-ui-layout", + "@deepseek-ai/dsh-client-ui-sidebar", + "@deepseek-ai/dsh-client-ui-renderer", + "@deepseek-ai/dsh-client-ui-settings", + "@deepseek-ai/dsh-client-ui-session", + "@deepseek-ai/dsh-client-locale" + ], + "platform": "web" + } + }, + "scripts": { + "build": "tsdown && tsc -p tsconfig.types.json && node scripts/link-bundle-declarations.mjs", + "prepublishOnly": "pnpm run verify", + "lint:package": "publint --strict && attw --pack . --profile esm-only --quiet", + "typecheck": "tsc -p tsconfig.json --noEmit", + "test": "vitest run", + "test:root": "vitest run --dir tests", + "bench:algorithms": "vitest run --config scripts/benchmark-algorithms.config.ts", + "verify:build": "node scripts/verify-deterministic-build.mjs", + "verify:headless": "node scripts/verify-headless-profile.mjs", + "verify:package": "node scripts/verify-package-contents.mjs && node scripts/verify-public-entries.mjs && pnpm run lint:package", + "verify:plugins": "node scripts/verify-plugin-artifacts.mjs", + "verify:docs": "node scripts/verify-docs.mjs", + "e2e:serve": "node scripts/serve-e2e.mjs", + "verify": "pnpm run verify:docs && pnpm run typecheck && pnpm run verify:build && pnpm --workspace-concurrency=4 -r build && pnpm -r typecheck && pnpm -r test && pnpm run test:root && pnpm run verify:headless && pnpm run verify:package", + "changeset": "changeset", + "release:intent": "node scripts/check-release-intent.mjs", + "release:status": "changeset status", + "release:version": "changeset version && node scripts/sync-release-metadata.mjs && pnpm install --lockfile-only", + "release:check": "node scripts/release.mjs --check" + }, + "dependencies": { + "@deepseek-ai/cosmokit": "^1.8.5", + "@deepseek-ai/schemastery": "^3.18.4", + "dsh-mnemon-provider-byterover": "0.5.5", + "dsh-mnemon-provider-hindsight": "0.5.5", + "dsh-mnemon-provider-holographic": "0.5.6", + "dsh-mnemon-provider-honcho": "0.5.5", + "dsh-mnemon-provider-mem0": "0.5.5", + "dsh-mnemon-provider-mnemon-native": "0.5.8", + "dsh-mnemon-provider-openviking": "0.5.7", + "dsh-mnemon-provider-retaindb": "0.5.5", + "dsh-mnemon-provider-supermemory": "0.5.5", + "dsh-mnemon-source-documents": "0.5.9", + "dsh-mnemon-source-memory-spaces": "0.5.16", + "dsh-mnemon-source-runtime": "0.5.12", + "dsh-mnemon-strategy-auto-capture": "0.5.5", + "dsh-mnemon-strategy-default-three-tier": "0.5.7", + "dsh-mnemon-strategy-general": "0.5.1", + "dsh-mnemon-strategy-light-context": "0.5.5", + "dsh-mnemon-strategy-scoped": "0.5.5", + "fflate": "^0.8.3", + "yaml": "^2.9.0", + "zod": "^4.4.3" + }, + "peerDependencies": { + "@deepseek-ai/cordis": "^4.0.4", + "@deepseek-ai/dsh-app-boot": "^0.1.7-rc.2 || ^0.2.0-rc.1", + "@deepseek-ai/dsh-client-ui-primitives": "^0.1.7-rc.2 || ^0.2.0-rc.1", + "@deepseek-ai/dsh-typert-protocol": "^0.1.7-rc.2 || ^0.2.0-rc.1", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@arethetypeswrong/cli": "^0.18.5", + "@changesets/cli": "^2.31.1", + "@deepseek-ai/cordis": "^4.0.4", + "@deepseek-ai/dsh": "0.1.7-rc.2", + "@deepseek-ai/dsh-agent": "0.1.7-rc.2", + "@deepseek-ai/dsh-agent-loop": "0.1.7-rc.2", + "@deepseek-ai/dsh-api-gateway": "0.1.7-rc.2", + "@deepseek-ai/dsh-app-boot": "0.1.7-rc.2", + "@deepseek-ai/dsh-client-connection": "0.1.7-rc.2", + "@deepseek-ai/dsh-client-locale": "0.1.7-rc.2", + "@deepseek-ai/dsh-client-store": "0.1.7-rc.2", + "@deepseek-ai/dsh-client-ui-chat": "0.1.7-rc.2", + "@deepseek-ai/dsh-client-ui-conversation": "0.1.7-rc.2", + "@deepseek-ai/dsh-client-ui-layout": "0.1.7-rc.2", + "@deepseek-ai/dsh-client-ui-plugin-manager": "0.1.7-rc.2", + "@deepseek-ai/dsh-client-ui-primitives": "0.1.7-rc.2", + "@deepseek-ai/dsh-client-ui-renderer": "0.1.7-rc.2", + "@deepseek-ai/dsh-client-ui-session": "0.1.7-rc.2", + "@deepseek-ai/dsh-client-ui-settings": "0.1.7-rc.2", + "@deepseek-ai/dsh-client-ui-sidebar": "0.1.7-rc.2", + "@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.2", + "@deepseek-ai/dsh-client-ui-tool": "0.1.7-rc.2", + "@deepseek-ai/dsh-invariants": "0.1.7-rc.2", + "@deepseek-ai/dsh-llm": "0.1.7-rc.2", + "@deepseek-ai/dsh-session": "0.1.7-rc.2", + "@deepseek-ai/dsh-session-format-v0-to-v1": "0.1.7-rc.2", + "@deepseek-ai/dsh-session-persistence-jsonl": "0.1.7-rc.2", + "@deepseek-ai/dsh-session-projection": "0.1.7-rc.2", + "@deepseek-ai/dsh-session-query": "0.1.7-rc.2", + "@deepseek-ai/dsh-subagent": "0.1.7-rc.2", + "@deepseek-ai/dsh-subagent-spawn-in-process": "0.1.7-rc.2", + "@deepseek-ai/dsh-system-prompt": "0.1.7-rc.2", + "@deepseek-ai/dsh-tools": "0.1.7-rc.2", + "@deepseek-ai/dsh-typert-protocol": "0.1.7-rc.2", + "@deepseek-ai/dsh-typert-registry": "0.1.7-rc.2", + "@deepseek-ai/dsh-util-code-language": "0.1.7-rc.2", + "@deepseek-ai/dsh-util-workspace-path": "0.1.7-rc.2", + "@shikijs/langs": "^4.3.1", + "@testing-library/react": "^16.3.0", + "@types/node": "^22.0.0", + "@types/react": "^18.3.0", + "@types/react-dom": "^18.3.0", + "anser": "^2.3.5", + "clsx": "^2.1.1", + "diff": "^9.0.0", + "immer": "^10.1.1", + "jsdom": "^26.1.0", + "katex": "^0.16.47", + "lightningcss": "^1.29.0", + "mdast-util-from-markdown": "^2.0.3", + "mdast-util-gfm": "^3.1.0", + "mdast-util-math": "^3.0.0", + "micromark-core-commonmark": "^2.0.3", + "micromark-extension-gfm": "^3.0.0", + "micromark-extension-math": "^3.1.0", + "micromark-factory-space": "^2.0.1", + "micromark-util-character": "^2.1.1", + "micromark-util-classify-character": "^2.0.1", + "micromark-util-sanitize-uri": "^2.0.1", + "micromark-util-symbol": "^2.0.1", + "publint": "^0.3.23", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "semver": "^7.8.5", + "shiki": "^4.3.1", + "simple-icons": "16.31.0", + "tsdown": "^0.22.0", + "typescript": "^5.9.0", + "vitest": "^3.2.0", + "zustand": "~4.4.7" + }, + "bin": { + "dsh-mnemon-repair-session": "bin/repair-legacy-session.mjs" + }, + "peerDependenciesMeta": { + "@deepseek-ai/dsh-app-boot": { + "optional": true + } + } +} diff --git a/dsh/plugins/dsh-mnemon-provider-byterover/LICENSE b/dsh/plugins/dsh-mnemon-provider-byterover/LICENSE new file mode 100644 index 000000000..e32e112dc --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-byterover/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 dsh-external contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dsh/plugins/dsh-mnemon-provider-byterover/README.md b/dsh/plugins/dsh-mnemon-provider-byterover/README.md new file mode 100644 index 000000000..10cee7b50 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-byterover/README.md @@ -0,0 +1,27 @@ +# dsh-mnemon-provider-byterover + +An adapter for the local ByteRover `brv` CLI and its knowledge directory. + +Install and configure `brv` separately. This driver exposes status, query and curate; it does not invent full enumeration or deletion. + +## Use + +The default `dsh-mnemon` Starter already installs this package. Enable and configure the service on the `dsh-mnemon` page under **Plugins**, then inspect its synchronized Memory Spaces. + +For a custom composition, install this package alongside `dsh-mnemon-source-memory-spaces` and include it in that Source's `config.providers`: + +```yaml +providers: + - use: dsh-mnemon-provider-byterover + instanceId: byterover +``` + +This is a **Memory Spaces child module**, not a top-level Source or a complete Strategy. It registers through `dsh-mnemon-source-memory-spaces/provider-sdk`; each parent Source owns its child Fibers, connection settings and lifetime. Credentials stay on the Host. + +[Provider setup and capability matrix](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/guides/memory-providers.md) · [中文指南](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/zh-CN/guides/memory-providers.md) + +## Develop independently + +From a source checkout with the declared dependencies installed, run `pnpm verify`. Tests, build and public exports belong to this package, without importing another package's controllers or repository configuration. Use the Source's public `/testing` fixtures for child registration and driver conformance; live service tests require a separately authorized environment. + +[Plugin author guide](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/development/extensions.md) diff --git a/dsh/plugins/dsh-mnemon-provider-byterover/package.json b/dsh/plugins/dsh-mnemon-provider-byterover/package.json new file mode 100644 index 000000000..3a234139e --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-byterover/package.json @@ -0,0 +1,55 @@ +{ + "name": "dsh-mnemon-provider-byterover", + "version": "0.5.5", + "description": "ByteRover Provider child for the Memory Spaces Source.", + "license": "MIT", + "type": "module", + "engines": { + "node": ">=20" + }, + "exports": { + ".": { + "types": "./lib/index.d.ts", + "default": "./lib/index.js" + }, + "./descriptor": { + "types": "./lib/descriptor.d.ts", + "default": "./lib/descriptor.js" + }, + "./package.json": "./package.json" + }, + "files": [ + "lib", + "README.md" + ], + "scripts": { + "typecheck": "tsc --noEmit", + "build": "tsdown", + "test": "vitest run", + "verify": "pnpm typecheck && pnpm test && pnpm build" + }, + "peerDependencies": { + "@deepseek-ai/cordis": "^4.0.1", + "dsh-mnemon-source-memory-spaces": "^0.5.1" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "dsh-mnemon-source-memory-spaces": "^0.5.11", + "tsdown": "^0.22.0", + "typescript": "^5.9.0", + "vitest": "^3.2.0" + }, + "publishConfig": { + "access": "public", + "tag": "latest" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/mnemon-dev/mnemon.git", + "directory": "dsh/plugins/dsh-mnemon-provider-byterover" + }, + "homepage": "https://github.com/mnemon-dev/mnemon/tree/master/dsh/plugins/dsh-mnemon-provider-byterover#readme", + "bugs": { + "url": "https://github.com/mnemon-dev/mnemon/issues" + } +} diff --git a/dsh/plugins/dsh-mnemon-provider-byterover/src/descriptor.ts b/dsh/plugins/dsh-mnemon-provider-byterover/src/descriptor.ts new file mode 100644 index 000000000..b04444405 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-byterover/src/descriptor.ts @@ -0,0 +1,66 @@ +import type { MemoryProviderDescriptor } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { ICON } from './icon.ts' + +/** Browser-safe metadata owned by this Provider. */ +export const descriptor: MemoryProviderDescriptor = { + "id": "byterover", + "label": "ByteRover", + "icon": { + "kind": "data-url", + "value": ICON + }, + "kind": "local", + "workspaceBinding": "optional-override", + "summary": "Local-first hierarchical knowledge tree accessed through the brv CLI.", + "summaryI18nKey": "overview.providerSummary.byterover", + "origin": "third-party", + "capabilities": { + "search": true, + "browse": false, + "graph": false, + "entities": false, + "related": false, + "remember": true, + "link": false, + "forget": false, + "writeMode": "async-extracting", + "deletionMode": "unsupported" + }, + "fields": [ + { + "key": "cliPath", + "label": "brv executable", + "scope": "service", + "input": "path", + "required": false, + "defaultValue": "brv", + "i18nKey": "overview.providerField.cliPath" + }, + { + "key": "defaultDirectory", + "label": "Default knowledge directory", + "scope": "service", + "role": "global-location", + "input": "path", + "required": false, + "discoveryDefaultFrom": "workingDirectory", + "i18nKey": "overview.providerField.defaultDirectory" + }, + { + "key": "workingDirectory", + "label": "Knowledge directory", + "scope": "memory", + "input": "path", + "required": false, + "i18nKey": "overview.providerField.workingDirectory" + }, + { + "key": "apiKey", + "label": "Cloud API key", + "scope": "service", + "input": "secret", + "required": false, + "i18nKey": "overview.providerApiKey" + } + ] +} diff --git a/dsh/plugins/dsh-mnemon-provider-byterover/src/driver.ts b/dsh/plugins/dsh-mnemon-provider-byterover/src/driver.ts new file mode 100644 index 000000000..2ee1a012e --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-byterover/src/driver.ts @@ -0,0 +1,145 @@ +import { createHash } from 'node:crypto' +import { mkdirSync } from 'node:fs' +import { basename, isAbsolute, join, resolve } from 'node:path' +import type { JsonValue } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import type { MemorySpaceAuthority } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { runProcess, type ProcessRunner } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import type { Insight, MemoryBody as MemorySpace, MemoryGraphSnapshot, MemoryListRequest, MemoryProviderConnection, RememberRequest, SearchRequest } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { NORMALIZED_RELEVANCE_SCORE, type MemoryProviderAdapter, type ProviderBodyStatus as ProviderSpaceStatus, type ProviderMemorySpace, type ProviderSearchResult } from 'dsh-mnemon-source-memory-spaces/provider-sdk' + +interface ByteRoverProviderOptions { + process?: ProcessRunner + queryTimeoutMs?: number + curateTimeoutMs?: number +} + +export class ByteRoverProvider implements MemoryProviderAdapter { + readonly id = 'byterover' as const + readonly scoreSemantics = NORMALIZED_RELEVANCE_SCORE + private readonly process: ProcessRunner + private readonly queryTimeoutMs: number + private readonly curateTimeoutMs: number + private readonly statusCache = new Map() + private readonly statusInFlight = new Map>() + + constructor(private readonly memorySpaces: MemorySpaceAuthority, options: ByteRoverProviderOptions = {}) { + this.process = options.process ?? runProcess + this.queryTimeoutMs = options.queryTimeoutMs ?? 10_000 + this.curateTimeoutMs = options.curateTimeoutMs ?? 120_000 + } + + async discover(connection: MemoryProviderConnection): Promise { + const configured = String(connection.defaultDirectory ?? '').trim() + const existingDirectory = this.memorySpaces.list() + .find(body => (body.provider.typeId ?? body.provider.id) === this.id)?.provider.settings.workingDirectory + const directory = configured === '' + ? String(existingDirectory ?? '').trim() || join(this.memorySpaces.runner.effectiveDataDir(), 'state', 'byterover', 'default') + : isAbsolute(configured) + ? configured + : resolve(this.memorySpaces.runner.effectiveDataDir(), configured) + return [{ + externalId: directory, + name: basename(directory) || 'ByteRover', + description: `ByteRover knowledge directory at ${directory}`, + connection: { workingDirectory: directory }, + }] + } + + async status(body: MemorySpace, signal?: AbortSignal): Promise { + if (signal !== undefined) return this.checkStatus(body, signal) + const cached = this.statusCache.get(body.id) + if (cached !== undefined && Date.now() - cached.checkedAt < 60_000) return cached.value + const running = this.statusInFlight.get(body.id) + if (running !== undefined) return running + const pending = this.checkStatus(body) + this.statusInFlight.set(body.id, pending) + try { + const value = await pending + this.statusCache.set(body.id, { checkedAt: Date.now(), value }) + return value + } finally { + if (this.statusInFlight.get(body.id) === pending) this.statusInFlight.delete(body.id) + } + } + + invalidateStatus(memoryBodyId?: string): void { + if (memoryBodyId === undefined) this.statusCache.clear() + else this.statusCache.delete(memoryBodyId) + } + + private async checkStatus(body: MemorySpace, signal?: AbortSignal): Promise { + try { + await this.run(body, ['status'], 15_000, signal) + return { healthy: true } + } catch (error) { + return { healthy: false, error: error instanceof Error ? error.message : String(error) } + } + } + + async search(body: MemorySpace, request: SearchRequest, signal?: AbortSignal): Promise { + const output = await this.run(body, ['query', '--', request.query.slice(0, 5_000)], this.queryTimeoutMs, signal) + if (output.length < 20) return { results: [], hint: 'ByteRover found no relevant memories.' } + const content = output.length > 8_000 ? `${output.slice(0, 8_000)}\n\n[... truncated]` : output + return { + results: [{ + id: `byterover:${createHash('sha256').update(content).digest('hex').slice(0, 24)}`, + content, + category: 'context', + source: 'external', + score: 1, + }], + } + } + + async graph(body: MemorySpace): Promise { + this.connection(body) + return { nodes: [], edges: [], generatedAt: new Date().toISOString() } + } + + async list(body: MemorySpace, request: MemoryListRequest, signal?: AbortSignal): Promise { + if (request.query === undefined || request.query.trim() === '') { + this.connection(body) + return [] + } + return (await this.search(body, { + query: request.query, + ...(request.limit === undefined ? {} : { limit: request.limit }), + }, signal)).results + } + + async remember(body: MemorySpace, request: RememberRequest, signal?: AbortSignal): Promise { + await this.run(body, ['curate', '--', request.content], this.curateTimeoutMs, signal) + return { action: 'stored', provider: this.id, summary: 'ByteRover curated the memory into its knowledge tree.' } + } + + private connection(body: MemorySpace): Record { + if ((body.provider.typeId ?? body.provider.id) !== this.id) throw new Error(`ByteRover cannot serve provider ${body.provider.id}`) + return this.memorySpaces.providerConnection(body.id, body.provider.id) + } + + private async run(body: MemorySpace, args: string[], timeoutMs: number, signal?: AbortSignal): Promise { + const connection = this.connection(body) + const command = String(connection.cliPath ?? 'brv') + const configuredDirectory = String(connection.workingDirectory ?? connection.defaultDirectory ?? '').trim() + const defaultDirectory = join(this.memorySpaces.runner.effectiveDataDir(), 'state', 'byterover', 'default') + const cwd = configuredDirectory === '' + ? defaultDirectory + : isAbsolute(configuredDirectory) + ? configuredDirectory + : resolve(this.memorySpaces.runner.effectiveDataDir(), configuredDirectory) + mkdirSync(cwd, { recursive: true, mode: 0o700 }) + const apiKey = String(connection.apiKey ?? '').trim() + const result = await this.process(command, args, { + timeoutMs, + maxOutputBytes: 256 * 1024, + ...(signal === undefined ? {} : { signal }), + cwd, + label: 'ByteRover', + env: { ...process.env, ...(apiKey === '' ? {} : { BRV_API_KEY: apiKey }) }, + }) + const stdout = result.stdout.trim() + const stderr = result.stderr.trim() + if (result.exitCode !== 0) throw new Error(stderr || stdout || `ByteRover exited with code ${String(result.exitCode)}`) + return stdout + } +} diff --git a/dsh/plugins/dsh-mnemon-provider-byterover/src/icon.ts b/dsh/plugins/dsh-mnemon-provider-byterover/src/icon.ts new file mode 100644 index 000000000..9c4496b0e --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-byterover/src/icon.ts @@ -0,0 +1,2 @@ +/** Browser-safe brand asset shipped by this Provider, not by the Host. */ +export const ICON = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzYiIGhlaWdodD0iMzYiIHZpZXdCb3g9IjAgMCAzNiAzNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE4IDM2QzI3Ljk0MTEgMzYgMzYgMjcuOTQxMSAzNiAxOEMzNiA4LjA1ODg3IDI3Ljk0MTEgMCAxOCAwQzguMDU4ODggMCAwIDguMDU4ODcgMCAxOEMwIDI3Ljk0MTEgOC4wNTg4OCAzNiAxOCAzNloiIGZpbGw9ImJsYWNrIi8+CjxwYXRoIGQ9Ik0xOCAxLjgwNjY0QzI2Ljk0NjkgMS44MDY2NCAzNC4yMDAxIDkuMDU5OTEgMzQuMjAwMiAxOC4wMDY4QzM0LjIwMDIgMjYuOTUzOCAyNi45NDcgMzQuMjA3IDE4IDM0LjIwN0M5LjA1MzIyIDM0LjIwNjggMS43OTk4IDI2Ljk1MzcgMS43OTk4IDE4LjAwNjhDMS43OTk5MSA5LjA2MDA4IDkuMDUzMjkgMS44MDY5MiAxOCAxLjgwNjY0Wk0xOCAzLjYwNjQ1QzEwLjA0NzQgMy42MDY3MiAzLjYwMDY5IDEwLjA1NDIgMy42MDA1OSAxOC4wMDY4QzMuNjAwNTkgMjUuOTU5NiAxMC4wNDczIDMyLjQwNyAxOCAzMi40MDcyQzIwLjE1NjIgMzIuNDA3MiAyMi4yMDE3IDMxLjkzMjQgMjQuMDM4MSAzMS4wODNDMjMuMzgzMSAyOS4zNTM3IDIwLjUwNzIgMjYuNzU1OSA5LjgyNTIgMjUuMTgyNkM5Ljg2Njg0IDI1LjE4MzIgMjMuOTY4NiAyNS4zNzggMjUuMDc4MSAzMC41NDg4QzI2LjQ1MzEgMjkuNzcxMiAyNy42ODcyIDI4Ljc3NDUgMjguNzMzNCAyNy42MDU1QzI5LjExNTUgMjUuNjEzOSAyOC4zNTA0IDE5LjQ5OTYgNS45NzE2OCAxNi43MjY2QzUuOTcxNjggMTYuNzI2NiAyOS4zNjE4IDE3LjQyMTggMzAuMTI3OSAyNS43NzM0QzMxLjIxMTUgMjQuMDg0OSAzMS45NTA3IDIyLjE1NTQgMzIuMjUgMjAuMDgzQzMxLjczODggMTcuNDQyNyAyOC4wMjg2IDEyLjg1NDEgOS40OTYwOSAxMC41NTc2QzkuNTY5NTggMTAuNTU5OCAyNy4zMDQ2IDExLjEwMDcgMzIuMzQ2NyAxNi43Njg2QzMyLjIwOTMgMTUuMTU2NiAzMS44MDcxIDEzLjYyMDcgMzEuMTgyNiAxMi4yMDQxQzMwLjE2NSAxMC4zNTQyIDI2LjY2NiA3LjgzOTUzIDE1LjY5ODIgNi40ODA0N0MxNS42OTgyIDYuNDgwNDcgMjQuNTUwOSA2Ljc0NDgyIDI5LjQwNTMgOS4yMTU4MkMyNi43NzE4IDUuODA0NTMgMjIuNjQyNyAzLjYwNjQ1IDE4IDMuNjA2NDVaIiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K" diff --git a/dsh/plugins/dsh-mnemon-provider-byterover/src/index.ts b/dsh/plugins/dsh-mnemon-provider-byterover/src/index.ts new file mode 100644 index 000000000..5439e03f8 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-byterover/src/index.ts @@ -0,0 +1,24 @@ +import { MEMORY_SPACE_PROVIDER_API_VERSION, defineMemorySpaceProvider, defineMemorySpaceProviderDefinition } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { ByteRoverProvider } from './driver.ts' +import { descriptor } from './descriptor.ts' + +export { ByteRoverProvider, descriptor } + +export const definition = defineMemorySpaceProviderDefinition({ + manifest: { + apiVersion: MEMORY_SPACE_PROVIDER_API_VERSION, kind: 'provider', + typeId: descriptor.id, packageName: 'dsh-mnemon-provider-byterover', version: '0.5.5', + label: descriptor.label, icon: descriptor.icon, summary: descriptor.summary, + ...(descriptor.summaryI18nKey === undefined ? {} : { summaryI18nKey: descriptor.summaryI18nKey }), + origin: descriptor.origin, locality: descriptor.kind, workspaceBinding: descriptor.workspaceBinding, + capabilities: descriptor.capabilities, fields: descriptor.fields, + secrets: descriptor.fields.filter(field => field.input === 'secret').map(field => field.key), + scoreSemantics: 'normalized-relevance', + }, + create: context => new ByteRoverProvider(context.memorySpaces ?? context.memoryBodies, { queryTimeoutMs: context.config.timeoutMs }), +}) + +export default defineMemorySpaceProvider({ + id: descriptor.id, + apply(ctx, host) { host.install(ctx, definition) }, +}) diff --git a/dsh/plugins/dsh-mnemon-provider-byterover/tests/driver.spec.ts b/dsh/plugins/dsh-mnemon-provider-byterover/tests/driver.spec.ts new file mode 100644 index 000000000..60b4f5ade --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-byterover/tests/driver.spec.ts @@ -0,0 +1,45 @@ +import { mkdtempSync, rmSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join, resolve } from 'node:path' +import { afterEach, describe, expect, it, vi } from 'vitest' +import { createMemorySpaceProviderFixture } from 'dsh-mnemon-source-memory-spaces/testing' +import type { ProcessRunner } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { ByteRoverProvider, descriptor } from '../src/index.ts' + +const temporaryDirectories: string[] = [] +afterEach(() => { for (const directory of temporaryDirectories.splice(0)) rmSync(directory, { recursive: true, force: true }) }) + +describe('standalone byterover data plane', () => { + it('runs ByteRover through a shell-free scoped CLI boundary', async () => { + const dataDir = mkdtempSync(join(tmpdir(), 'dsh-mnemon-byterover-')) + temporaryDirectories.push(dataDir) + const calls: Array<{ command: string; args: readonly string[]; options: Parameters[2] }> = [] + const process = vi.fn(async (command, args, options) => { + calls.push({ command, args, options }) + if (args[0] === 'status') return { stdout: 'ByteRover ready', stderr: '', exitCode: 0 } + if (args[0] === 'query') return { stdout: 'Past context: use a staged rollout before production deployment.', stderr: '', exitCode: 0 } + if (args[0] === 'curate') return { stdout: 'Curated', stderr: '', exitCode: 0 } + return { stdout: '', stderr: 'unknown', exitCode: 1 } + }) + + const { authority: registry, body } = createMemorySpaceProviderFixture(descriptor, { cliPath: '/opt/byterover/bin/brv', workingDirectory: 'brv-space', apiKey: 'brv-secret' }, { dataDir, instanceId: 'work-account' }) + const provider = new ByteRoverProvider(registry, { process, queryTimeoutMs: 1_000, curateTimeoutMs: 2_000 }) + + await expect(provider.status(body)).resolves.toEqual({ healthy: true }) + await expect(provider.status(body)).resolves.toEqual({ healthy: true }) + expect(calls.filter(call => call.args[0] === 'status')).toHaveLength(1) + const recalled = await provider.search(body, { query: 'How should we deploy?' }) + expect(recalled.results).toEqual([expect.objectContaining({ id: expect.stringMatching(/^byterover:/), category: 'context', score: 1 })]) + expect(JSON.stringify(recalled)).not.toContain('brv-secret') + await expect(provider.remember(body, { content: 'Always use staged rollout.' })).resolves.toMatchObject({ action: 'stored' }) + await expect(provider.graph(body)).resolves.toMatchObject({ nodes: [], edges: [] }) + + expect(calls[1]).toMatchObject({ command: '/opt/byterover/bin/brv', args: ['query', '--', 'How should we deploy?'] }) + expect(calls[1]?.options.cwd).toBe(resolve(dataDir, 'brv-space')) + expect(calls[1]?.options.env?.BRV_API_KEY).toBe('brv-secret') + expect(calls[1]?.options.label).toBe('ByteRover') + provider.invalidateStatus(body.id) + await expect(provider.status(body)).resolves.toEqual({ healthy: true }) + expect(calls.filter(call => call.args[0] === 'status')).toHaveLength(2) + }) +}) diff --git a/dsh/plugins/dsh-mnemon-provider-byterover/tests/provider.spec.ts b/dsh/plugins/dsh-mnemon-provider-byterover/tests/provider.spec.ts new file mode 100644 index 000000000..26a0145ef --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-byterover/tests/provider.spec.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from 'vitest' +import type { MemorySpaceAuthority } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { mountMemorySpaceProvider } from 'dsh-mnemon-source-memory-spaces/testing' +import module, { definition, ByteRoverProvider, descriptor } from '../src/index.ts' + +describe('independent byterover Provider', () => { + it('installs complete immutable metadata in one parent-owned child', async () => { + const mounted = await mountMemorySpaceProvider(module, { instanceId: 'work', config: undefined }) + try { + expect(mounted.descriptor).toMatchObject({ id: 'work', typeId: 'byterover', label: descriptor.label }) + expect(Object.isFrozen(mounted.manifest)).toBe(true) + const authority: MemorySpaceAuthority = { runner: { effectiveDataDir: () => '/unused' }, list: () => [], providerConnection: () => ({}) } + expect(mounted.createAdapter({ memoryBodies: authority, config: { timeoutMs: 200 } }).id).toBe('work') + expect(mounted.registered).toBe(true) + expect(definition.manifest.secrets).toEqual(descriptor.fields.filter(field => field.input === 'secret').map(field => field.key).sort()) + } finally { await mounted.dispose() } + expect(mounted.registered).toBe(false) + }) + + it('constructs its own driver from a structural authority, without the main repository', () => { + const authority: MemorySpaceAuthority = { runner: { effectiveDataDir: () => '/unused' }, list: () => [], providerConnection: () => ({}) } + const adapter = definition.create({ memoryBodies: authority, config: { timeoutMs: 200 }, providerInstanceId: 'byterover', manifest: definition.manifest }) + expect(adapter).toBeInstanceOf(ByteRoverProvider) + expect(adapter.id).toBe('byterover') + }) +}) diff --git a/dsh/plugins/dsh-mnemon-provider-byterover/tsconfig.json b/dsh/plugins/dsh-mnemon-provider-byterover/tsconfig.json new file mode 100644 index 000000000..badeba7ca --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-byterover/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2024", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "verbatimModuleSyntax": true, + "allowImportingTsExtensions": true, + "skipLibCheck": true, + "types": [ + "node" + ] + }, + "include": [ + "src/**/*.ts", + "tests/**/*.ts", + "tsdown.config.ts", + "vitest.config.ts" + ] +} diff --git a/dsh/plugins/dsh-mnemon-provider-byterover/tsdown.config.ts b/dsh/plugins/dsh-mnemon-provider-byterover/tsdown.config.ts new file mode 100644 index 000000000..2d868cbd8 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-byterover/tsdown.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'tsdown' + +export default defineConfig({ + entry: ['src/index.ts', 'src/descriptor.ts'], outDir: 'lib', format: 'esm', platform: 'node', + target: 'es2024', dts: true, clean: true, fixedExtension: false, deps: { neverBundle: true }, +}) diff --git a/dsh/plugins/dsh-mnemon-provider-byterover/vitest.config.ts b/dsh/plugins/dsh-mnemon-provider-byterover/vitest.config.ts new file mode 100644 index 000000000..5a6724c22 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-byterover/vitest.config.ts @@ -0,0 +1,3 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ test: { environment: 'node' } }) diff --git a/dsh/plugins/dsh-mnemon-provider-hindsight/LICENSE b/dsh/plugins/dsh-mnemon-provider-hindsight/LICENSE new file mode 100644 index 000000000..e32e112dc --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-hindsight/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 dsh-external contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dsh/plugins/dsh-mnemon-provider-hindsight/README.md b/dsh/plugins/dsh-mnemon-provider-hindsight/README.md new file mode 100644 index 000000000..dac572d2a --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-hindsight/README.md @@ -0,0 +1,27 @@ +# dsh-mnemon-provider-hindsight + +An adapter for Hindsight banks, entities, relationships and retained evidence. + +Requires a Hindsight endpoint and bank scope. Statistics availability depends on the upstream deployment. + +## Use + +The default `dsh-mnemon` Starter already installs this package. Enable and configure the service on the `dsh-mnemon` page under **Plugins**, then inspect its synchronized Memory Spaces. + +For a custom composition, install this package alongside `dsh-mnemon-source-memory-spaces` and include it in that Source's `config.providers`: + +```yaml +providers: + - use: dsh-mnemon-provider-hindsight + instanceId: hindsight +``` + +This is a **Memory Spaces child module**, not a top-level Source or a complete Strategy. It registers through `dsh-mnemon-source-memory-spaces/provider-sdk`; each parent Source owns its child Fibers, connection settings and lifetime. Credentials stay on the Host. + +[Provider setup and capability matrix](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/guides/memory-providers.md) · [中文指南](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/zh-CN/guides/memory-providers.md) + +## Develop independently + +From a source checkout with the declared dependencies installed, run `pnpm verify`. Tests, build and public exports belong to this package, without importing another package's controllers or repository configuration. Use the Source's public `/testing` fixtures for child registration and driver conformance; live service tests require a separately authorized environment. + +[Plugin author guide](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/development/extensions.md) diff --git a/dsh/plugins/dsh-mnemon-provider-hindsight/package.json b/dsh/plugins/dsh-mnemon-provider-hindsight/package.json new file mode 100644 index 000000000..d8429d496 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-hindsight/package.json @@ -0,0 +1,55 @@ +{ + "name": "dsh-mnemon-provider-hindsight", + "version": "0.5.5", + "description": "Hindsight Provider child for the Memory Spaces Source.", + "license": "MIT", + "type": "module", + "engines": { + "node": ">=20" + }, + "exports": { + ".": { + "types": "./lib/index.d.ts", + "default": "./lib/index.js" + }, + "./descriptor": { + "types": "./lib/descriptor.d.ts", + "default": "./lib/descriptor.js" + }, + "./package.json": "./package.json" + }, + "files": [ + "lib", + "README.md" + ], + "scripts": { + "typecheck": "tsc --noEmit", + "build": "tsdown", + "test": "vitest run", + "verify": "pnpm typecheck && pnpm test && pnpm build" + }, + "peerDependencies": { + "@deepseek-ai/cordis": "^4.0.1", + "dsh-mnemon-source-memory-spaces": "^0.5.1" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "dsh-mnemon-source-memory-spaces": "^0.5.11", + "tsdown": "^0.22.0", + "typescript": "^5.9.0", + "vitest": "^3.2.0" + }, + "publishConfig": { + "access": "public", + "tag": "latest" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/mnemon-dev/mnemon.git", + "directory": "dsh/plugins/dsh-mnemon-provider-hindsight" + }, + "homepage": "https://github.com/mnemon-dev/mnemon/tree/master/dsh/plugins/dsh-mnemon-provider-hindsight#readme", + "bugs": { + "url": "https://github.com/mnemon-dev/mnemon/issues" + } +} diff --git a/dsh/plugins/dsh-mnemon-provider-hindsight/src/descriptor.ts b/dsh/plugins/dsh-mnemon-provider-hindsight/src/descriptor.ts new file mode 100644 index 000000000..9cd67d441 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-hindsight/src/descriptor.ts @@ -0,0 +1,83 @@ +import type { MemoryProviderDescriptor } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { ICON } from './icon.ts' + +/** Browser-safe metadata owned by this Provider. */ +export const descriptor: MemoryProviderDescriptor = { + "id": "hindsight", + "label": "Hindsight", + "icon": { + "kind": "data-url", + "value": ICON + }, + "kind": "remote", + "workspaceBinding": "provider-global", + "summary": "Knowledge-graph memory with entity resolution, observations, multi-strategy recall, and reflection.", + "summaryI18nKey": "overview.providerSummary.hindsight", + "origin": "third-party", + "capabilities": { + "search": true, + "browse": true, + "graph": true, + "entities": true, + "related": true, + "remember": true, + "link": false, + "forget": true, + "writeMode": "async-extracting", + "deletionMode": "soft" + }, + "fields": [ + { + "key": "endpoint", + "label": "Endpoint", + "scope": "service", + "input": "url", + "required": true, + "defaultValue": "https://api.hindsight.vectorize.io", + "i18nKey": "overview.providerEndpoint" + }, + { + "key": "apiKey", + "label": "API key", + "scope": "service", + "input": "secret", + "required": false, + "i18nKey": "overview.providerApiKey" + }, + { + "key": "bankId", + "label": "Memory bank", + "scope": "memory", + "input": "text", + "required": true, + "defaultValue": "dsh", + "i18nKey": "overview.providerField.bankId" + }, + { + "key": "budget", + "label": "Recall budget", + "scope": "memory", + "input": "select", + "required": true, + "defaultValue": "mid", + "options": [ + { + "value": "low", + "label": "Low", + "i18nKey": "overview.providerOption.low" + }, + { + "value": "mid", + "label": "Medium", + "i18nKey": "overview.providerOption.mid" + }, + { + "value": "high", + "label": "High", + "i18nKey": "overview.providerOption.high" + } + ], + "i18nKey": "overview.providerField.budget" + } + ] +} diff --git a/dsh/plugins/dsh-mnemon-provider-hindsight/src/driver.ts b/dsh/plugins/dsh-mnemon-provider-hindsight/src/driver.ts new file mode 100644 index 000000000..e1262c896 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-hindsight/src/driver.ts @@ -0,0 +1,235 @@ +import { descriptor } from './descriptor.ts' +import { randomUUID } from 'node:crypto' +import type { JsonValue } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import type { MemorySpaceAuthority } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import type { + EdgeType, + Insight, + MemoryBody as MemorySpace, + MemoryGraphEdge, + MemoryGraphNode, + MemoryGraphSnapshot, + MemoryListRequest, + RememberRequest, + SearchRequest, +} from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { HttpMemoryProvider, firstArray, jsonArray, jsonNumber, jsonObject, jsonString, type HttpProviderOptions } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { NORMALIZED_RELEVANCE_SCORE, type MemoryProviderAdapter, type ProviderBodyStatus as ProviderSpaceStatus, type ProviderMemorySpace, type ProviderSearchResult } from 'dsh-mnemon-source-memory-spaces/provider-sdk' + +function insight(value: unknown): Insight | undefined { + const item = jsonObject(value) + const id = jsonString(item?.id) + const content = jsonString(item?.text) ?? jsonString(item?.content) ?? jsonString(item?.label) + if (id === undefined || content === undefined) return undefined + const scores = jsonObject(item?.scores) + const score = jsonNumber(scores?.final) ?? jsonNumber(item?.score) + const createdAt = jsonString(item?.mentioned_at) ?? jsonString(item?.date) ?? jsonString(item?.occurred_start) + const rawEntities = item?.entities + const entities = Array.isArray(rawEntities) + ? rawEntities.filter((entry): entry is string => typeof entry === 'string') + : typeof rawEntities === 'string' + ? rawEntities.split(',').map(entry => entry.replace(/\s*\([^)]*\)\s*$/u, '').trim()).filter(Boolean) + : [] + const tags = jsonArray(item?.tags).filter((entry): entry is string => typeof entry === 'string') + return { + id, + content, + category: jsonString(item?.type) ?? jsonString(item?.fact_type) ?? 'general', + source: 'external', + ...(score === undefined ? {} : { score }), + ...(createdAt === undefined ? {} : { createdAt }), + ...(entities.length === 0 ? {} : { entities }), + ...(tags.length === 0 ? {} : { tags }), + } +} + +function edgeType(value: unknown): EdgeType | undefined { + return value === 'temporal' || value === 'semantic' || value === 'causal' || value === 'entity' ? value : undefined +} + +export class HindsightProvider extends HttpMemoryProvider implements MemoryProviderAdapter { + readonly id = 'hindsight' as const + readonly scoreSemantics = NORMALIZED_RELEVANCE_SCORE + + constructor(memorySpaces: MemorySpaceAuthority, options: HttpProviderOptions = {}) { + super(memorySpaces, { label: descriptor.label, ...options }) + } + + async discover(connection: Record, signal?: AbortSignal): Promise { + const payload = await this.requestConnection(connection, '/v1/default/banks', { headers: this.headers(connection), signal }) + return firstArray(payload, 'banks', 'items').flatMap(value => { + const item = jsonObject(value) + const id = jsonString(item?.bank_id) ?? jsonString(item?.id) + if (id === undefined) return [] + const description = jsonString(item?.mission)?.trim() + || jsonString(item?.description)?.trim() + || `Hindsight memory bank ${id}` + return [{ + externalId: id, + name: jsonString(item?.name) ?? id, + description, + connection: { bankId: id, budget: 'mid' }, + }] + }) + } + + async status(body: MemorySpace, signal?: AbortSignal): Promise { + try { + const connection = this.connection(body) + await this.request(body, '/health/live', { headers: this.headers(connection), signal }) + try { + const [statsPayload, entitiesPayload] = await Promise.all([ + this.request(body, `${this.bankPath(connection)}/stats`, { headers: this.headers(connection), signal }), + this.request(body, `${this.bankPath(connection)}/entities?limit=100&offset=0`, { headers: this.headers(connection), signal }), + ]) + const stats = jsonObject(statsPayload) ?? {} + const byFactType = jsonObject(stats.nodes_by_fact_type) ?? {} + const byCategory = Object.fromEntries(Object.entries(byFactType).flatMap(([category, count]) => { + const value = jsonNumber(count) + return value === undefined ? [] : [[category, value]] + })) + const operations = jsonObject(stats.operations_by_status) ?? {} + const topEntities = firstArray(entitiesPayload, 'items').flatMap(value => { + const item = jsonObject(value) + const entity = jsonString(item?.canonical_name) + const count = jsonNumber(item?.mention_count) + return entity === undefined || count === undefined ? [] : [{ entity, count }] + }) + return { + healthy: true, + stats: { + totalInsights: jsonNumber(stats.total_nodes) ?? 0, + deletedInsights: 0, + edgeCount: jsonNumber(stats.total_links) ?? 0, + oplogCount: Object.values(operations).reduce((total, value) => total + (jsonNumber(value) ?? 0), 0), + dbSizeBytes: 0, + byCategory, + topEntities, + }, + } + } catch { + // Older Hindsight deployments may expose recall and graph APIs without + // the newer statistics surfaces. Liveness remains sufficient there. + return { healthy: true } + } + } catch (error) { + return { healthy: false, error: error instanceof Error ? error.message : String(error) } + } + } + + async search(body: MemorySpace, request: SearchRequest, signal?: AbortSignal): Promise { + const connection = this.connection(body) + const payload = await this.request(body, `${this.bankPath(connection)}/memories/recall`, { + headers: this.headers(connection), + json: { + query: request.query, + budget: String(connection.budget ?? 'mid'), + max_tokens: Math.min(Math.max((request.limit ?? 10) * 400, 400), 8_000), + types: ['world', 'experience', 'observation'], + prefer_observations: true, + }, + signal, + }) + return { results: firstArray(payload, 'results', 'items').map(insight).filter((item): item is Insight => item !== undefined).slice(0, request.limit ?? 10) } + } + + async list(body: MemorySpace, request: MemoryListRequest, signal?: AbortSignal): Promise { + const connection = this.connection(body) + const params = new URLSearchParams({ limit: String(Math.min(Math.max(request.limit ?? 200, 1), 1000)), offset: '0', state: 'valid' }) + if (request.query !== undefined && request.query.trim() !== '') params.set('q', request.query.trim()) + const payload = await this.request(body, `${this.bankPath(connection)}/memories/list?${params}`, { headers: this.headers(connection), signal }) + return firstArray(payload, 'items', 'results').map(insight).filter((item): item is Insight => item !== undefined) + .filter(item => request.category === undefined || item.category === request.category) + } + + async graph(body: MemorySpace, signal?: AbortSignal): Promise { + const connection = this.connection(body) + const payload = jsonObject(await this.request(body, `${this.bankPath(connection)}/graph?limit=1000`, { headers: this.headers(connection), signal })) ?? {} + const nodes: MemoryGraphNode[] = jsonArray(payload.nodes).flatMap(value => { + const item = jsonObject(value) + const data = jsonObject(item?.data) ?? item + const projected = insight(data) + return projected === undefined ? [] : [{ ...projected, color: jsonString(data?.color) ?? '#6574d9' }] + }) + const edges: MemoryGraphEdge[] = jsonArray(payload.edges).flatMap(value => { + const item = jsonObject(value) + const data = jsonObject(item?.data) ?? item + const sourceId = jsonString(data?.from) ?? jsonString(data?.source) + const targetId = jsonString(data?.to) ?? jsonString(data?.target) + if (sourceId === undefined || targetId === undefined) return [] + const rawType = jsonString(data?.type) ?? jsonString(data?.linkType) + const type = edgeType(rawType) + return [{ + sourceId, + targetId, + label: rawType ?? 'related', + color: type === 'causal' ? '#e74c3c' : type === 'entity' ? '#2ecc71' : type === 'temporal' ? '#aaaaaa' : '#3498db', + ...(type === undefined ? {} : { type }), + }] + }) + return { nodes, edges, generatedAt: new Date().toISOString() } + } + + async related(body: MemorySpace, id: string, depth: number, _edge?: EdgeType, signal?: AbortSignal): Promise { + const graph = await this.graph(body, signal) + let frontier = new Set([id]) + const visited = new Set([id]) + for (let level = 0; level < depth; level += 1) { + const next = new Set() + for (const edge of graph.edges) { + if (frontier.has(edge.sourceId) && !visited.has(edge.targetId)) next.add(edge.targetId) + if (frontier.has(edge.targetId) && !visited.has(edge.sourceId)) next.add(edge.sourceId) + } + for (const value of next) visited.add(value) + frontier = next + } + return graph.nodes.filter(node => node.id !== id && visited.has(node.id)).map(({ color: _color, ...node }) => node) + } + + async remember(body: MemorySpace, request: RememberRequest, signal?: AbortSignal): Promise { + const connection = this.connection(body) + const operationId = randomUUID() + const payload = jsonObject(await this.request(body, `${this.bankPath(connection)}/memories`, { + headers: this.headers(connection), + json: { + items: [{ + content: request.content, + context: request.category ?? 'dsh-mnemon', + metadata: { source: 'dsh-mnemon' }, + ...(request.tags === undefined ? {} : { tags: request.tags }), + ...(request.entities === undefined ? {} : { entities: request.entities.map(text => ({ text })) }), + }], + async: true, + operation_id: operationId, + }, + signal, + })) ?? {} + return { + action: 'queued', + provider: this.id, + summary: 'Hindsight queued the content for structured memory extraction.', + operationId: jsonString(payload.operation_id) ?? operationId, + ...(jsonNumber(payload.items_count) === undefined ? {} : { itemsCount: jsonNumber(payload.items_count)! }), + } + } + + async forget(body: MemorySpace, id: string, signal?: AbortSignal): Promise { + const connection = this.connection(body) + await this.request(body, `${this.bankPath(connection)}/memories/${encodeURIComponent(id)}`, { + method: 'PATCH', + headers: this.headers(connection), + json: { state: 'invalidated', reason: 'Forgotten from dsh-mnemon' }, + signal, + }) + return { action: 'invalidated', provider: this.id, id } + } + + private bankPath(connection: Record): string { + return `/v1/default/banks/${encodeURIComponent(String(connection.bankId))}` + } + + private headers(connection: Record): HeadersInit { + const token = String(connection.apiKey ?? '').replace(/^Bearer\s+/iu, '') + return token === '' ? {} : { Authorization: `Bearer ${token}` } + } +} diff --git a/dsh/plugins/dsh-mnemon-provider-hindsight/src/icon.ts b/dsh/plugins/dsh-mnemon-provider-hindsight/src/icon.ts new file mode 100644 index 000000000..0f94a59a8 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-hindsight/src/icon.ts @@ -0,0 +1,2 @@ +/** Browser-safe brand asset shipped by this Provider, not by the Host. */ +export const ICON = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAAA2CAYAAABkxd/2AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAARGVYSWZNTQAqAAAACAABh2kABAAAAAEAAAAaAAAAAAADoAEAAwAAAAEAAQAAoAIABAAAAAEAAABIoAMABAAAAAEAAAA2AAAAANbTGEEAABzqSURBVGgF7Xt3nJXFuf/MvO20bWd3YZcqoiIgGEVjjOWCGhRiRVljLL9YIuQXo0YFRYpHESwQNeovVzSoAe/VgPViibGBNRKJF0URIx027LK7Z8spb5253+c9nKW4yuLllj9+w+fs22aeeeY7zzxtBsb+f/lWBPi3fu3io2JMDLzz+aEOEz/MiaBMV9a6hN/6zvqpP23oovr/yCvicfjcR47IsMgRSrkJoWf/PtjOvv/SlCnpfWVonwAannqiTwOPpDp49FxPj1iMKx4oKU3f3dTbb77v4tXrH0stTrn7ysT+rH/Y3fcP3KYnbssofYynGxElXF7quYEh/XU57szNXn/tgn3pr9sADU0tPGgzL3+qwywbrgQ3DLdDcckDzzB1xT2V4Mouy2y/54fDI7csrqsL9oWJ/VW38t4Fgx0V/GvWjBzGpNKZ9BiXSmnc4L6lS+7lnBKn7d7WyddOw8AhaHsvYu9VCjW2iOSvO6yKIww34HE786Slt53kcv+oEpW/OhL4rZkgEm2IVd/wxlrj5O7S3J/1UqmU0D1nek5PDNc9pidz9utxl9dJxx8RYe6lMSezSudGNGuWXtfj7t+O6W7f3QLosGnPDXSVfgFXTBgqv/CAhnVXfs8prT/GyOtH/v2jR8r97ERDer6vRS3lOBdiarotmd1ldG/15kcra3LKOFkpKUq87PIau/X8Mbl1r5wWsSPnrlz2VB/ecTFX7hZpxCIZy7qyuzx2CyBm6MN9Ta/QZFaVsvTjvHefA9dwbcmnsvzNT/oNuXBAad8lhvK2iYAzqQeHPDzvSn1vA9rf37lfMTinJap1jFxJ55kHWXv7Mr3XEyt4+avLDv+nuWtaW1dZjnqdY8Q+YwNPnTMn1h0eugWQY+YEkxrT3Igq9azcNs9MtnJ2gB2IGLON8qP0Vim5FJIJpikjYv6jl9adzvdnHcXzrlA2kxBzziynpLaeZy3Rry0SKWnzcnGWSklTKV8xl5mBlYjpBxnd6b9bAPmOvZmpXOALS9TrVT/vX//ce7313OikahvdkrcefLRDjnRErJoZPgsC7aNLUym7O53vzzp9Ivwzg3mbpdCYw8WYG/7RS/lG5gI913SFw7JTRsxbVNZmiVGYSab7rLW8bWW3eOwWQGqD/CQunQ/8iM1yQl7yVc3pkztcu6FJ1z+P9sqNs1nkEdPTDM13mc8jb+7PgXeX1gfXXdESlWyZ4Vssb0ZP/jCenHOsE8/WffKXfykVSWtNe9vDvtIPFMxgOleLHu/mJHYLoA2PX2pXcHa95XjbAz0WyRqVt6d55ceGXbrakSX/GmhuX8eE6Qjs/EGW9kl3B7W/60nDeN8VAZOB0pWoufavuvW3RUN+9F4Tsz7OGvE6ZWo8mmtb3ctveKi7fXcLICK2PnXO8lovP96Suc8EzzFlJCJMZxUMM6bZZsGrUEJvC/LdWtvdZXBf6pme6XFyb7jLlC9YjlnVrqYd6UX0CkMGrNTOvVOTbar7dB886m4DRIxumHnmspKO9cdWuulxUbdliY/FLEEB1h+mQTBXjxqNKtp/Xwa1P+s6un2YDhdVCwzmGDYcfcUivsOq8i0v9fbS43+yRp68NjV11b70+Z39lZ+lHossFjXLbc0cFpDfrEDK4qwin3mq5dYzftpdT3VfmP22upV3PHx02oz9SUkzyZQDlBTTRJTFnfQjrTddORH8yG9r/03f9kmCdiXyeOpSO8r9Z0zID80UE/h5kmV144za2544ate6/9X3fVK/T2a1xAOKR5OKlpcWMFOazPBcLy7UQ98VHOL7OwNEjXvy/BsqYIHicHt4gLiHMdcw4mmemD3iynn/LbqINE6HEXnA1xLHMBdPtOahHH2NYmn/o9PLVnxKvH7X8jWPl2RhcOqtfusMM1Hh5XPbUqes/6blMiS2ZcWG9vhaJkoPYdIPoz/uSuZpJaesrs3PAFPTvytj3WlH4FTPfuyajGX8RAYOVvkOTgOdcUOxEuU8+fCEh71vojUylYqsK+vRS+PMEB5Pr530i8Y964JioVBntTMWn+byHjfkAvMIV3gl+JiJquDTsqDxwS2zz1/cFVA1t/7x+mbed66ncoVlpgzGSZq452uBO9G75cz5xT72vA6/YUG8MVnaHzmBQz0hqmzJIIuSxbls11mwudzf+uXK1KQm9Nul/kje+tDluUj1PJcrDYkF6EH8SIxZhEWD7Kb+suPIL26+qnnPfo+7a3LJGnPQz9uE9n/g2aJ/3ZQGb46o3Hs1HY0zv5ox47Nim06AklP+NCNnWTNss4dmeLAAXo55JjSMsOAgp1mZn568/fZxc/cEaWhqUXKDLH0vZ8QPZXJHKghMKiGYLqSXzKdvH5jZMueDe6/LU6dj7r/fWtVUeVpGN+rywjzC49oAJvQI0zRIAKYJRUDrq8CVUZZN65yv1IT2Vpnv/GnttEtWFPuPz3z0FM+yXvCVFlMqAr0DQVEeVCEMvWWyhJ2e1HHTFXNDgrv8OWL2o9Ub9eDJ9mjpyb7UmYXIDAEAs01MiZ5hJR35ht5u9uzV067/CzULAep304unbNN7vIbcDuKU9i1J2fIQ4pXVaRYZlTO0nwfCsCJBLqgOmsdsml332i79hbe1M184rUmVvOhhlJx5AIdmE7pA6PBaGYt56RWuCp4N9GitYPykgGtDAgODwrLgALUAS5Hqjie044julAaJBNiml0Mk6C61lP8s+rCa9cQUxswaHmRZwKJQppIFBDDAiTstKw+vlie8d/nlHUWqdB2/aLz25/Ujn7bNnmd7yBUZPL+mR5BdGHVl64Zo7FRf005TWtSI5dtWDmzoOO6TuZOyFPzyMs28wbWirCSfXl/pNI5d/5vzvlw8nvHxi9lzPaY++1FaVD7i6NVGmvGr0M/r+O02pvrpZ71addtLqbReNlMGmA4EtoIsG5yzAOFzu1kxAiHSCOVjDUnJJH7MgUCRzmCk4GlZ7Eay8IjsANFQAAohJqKZ6GhbBaMF6ktMAFlPX4+GbSkMVbrBNJlvqHSDS9+7/IrdwEEltuKrfxqYN2KjqS28/k8HceeUTyb9qhE9k2b/XcU9j07Ncj4zbyQO39TTH413z4nDJr3UM89KRpAfo/zsH9aXnPdlcvqfnrts8Oura25aUtc4a9yCmJSfYczwTM0hQ1NvxamzXQuGoZqmj51V4rb/xuCk943CcAUtOegmLFnpQFICSBeGSp4u4wASsx46maEgE1goIU6F+8I3vMNsh74NpMVDxG6HIJNRCLAcORN4Rv6QmdxWyVzrlI0zrvg4pLXHH4NZx3E9EYNLy8qUc++g/pXNfWb/7sGa38z/S+K2+cOHeuq3wunYqIwYYsr4KGouuMWhE3VDYP0Ghvb5D9vuKc855qlZq/fBvlJnog7nQmvmnILfAALf+jXLR4Q456qVLZ9cnmtbZCpIBERGcugwHsHg8Aui8AToSs/kAWDSSHJIuYaFQKF3O4Ai0DolC/oJbSQ0hlRWCAwBLIUPiUEMCNMeh+jWZNtubpw+8fECvS7+alaZD7K6tFmt1Dau+XxbbbNmTdge63F0Wdz80bs3Xp5FHrlV5x6LeKycKIi8bndw7tjQUCzw9KPfv/e6tLDMS8pyTdP7iaZr+05dUhv43uFkvgXnie3MSnTRdfiKI+fyA/Mfl8ZU7lMYFowd+iMg6SXAMOMaTDEp01AZg1PyWcLVtevyKgJU7AXPoCXoh1RF6JSGnyz85fB3ID0wJlWe/9jGGRPuotrFlntefS5N8mddI8q+0rTjV6au3sqD3LmJjq23tTVvWnjwzKcGmKq0lw+XxWNyG7UXF99yTpOheZ9IzFxgJCbGb3z19Pv6LHguPeuE2VusXmWNWuK3Lo9WQZrRs4hGfUzjt5S/srLzsppxsITJVbSEAAyjpYZZoYF2/mRxHKRLACYBVZQYwog+k6IPlT1wBBAyBBf06B3pJx/0sPY99NWki5FHzn606ltYY62BGhDAcPjQhY4wflV75+/HZm/+1ZL2yRNv6V3VR99sOvdkDbPasgNl8dwbRItYYf2nvXxSg6x42dYNS/j5AAtjDQbXLIU+zDNj5dzNopYOGyVYT7fhkq13nLmQ2u1Z+sxccvB2FX3L4WZvchzDUYY9UEcYMZYehWyFXmlp4YFyoOQ3QeGSmdehTwLEUZQXJLwUTTkmDwnL0FJhGwfLrJMIvoVIMmFarMxNP9gy5eKr8ZVe7launDfPeLw99oGrxUawAAZCh4X1WV4PnL94htfOlTgm0KI1NIcRu+21H+SeGrs0tRQhOMrG28e+GeFsQsR3bW7GNdsoGeIYlSfAapQzoF1gAZBBE3YI89oRqXld5nNbA2Omp1k7wCHKO1mlAYdgkJ4hjRrqIRoogKJqAnpJi0IoYgCKLBMUPcCh8SO0whVCD8Qk6bTQEBD9nUX6HstqsQkHzPrDaTvf7rx7ptU/I9CNIxlZWQIcVk8LzKhvlI9ievlZgR6pYQac1Hz2Y8vRJhA41Bp4FYr9zvyVVSee/ZpCZKX7rhcU5gCaFdqWZix8htnWzVrHjfa7/IiKl5cvX05iEJYeM174UUYrTSnpGdiJCgdd+EIgoISSQvPhAyJYH8ygBnE3kGSr8NsaSln+q6jMr40oe5OmvCyWKDo2Ywymm7zk0IkEDY0MwG4F9KkL8AfnVMvq/veT553wdO7FVzPFamV3Lzwyo0fnB9KoEFjaBDywgNuO5JoJ6yq9jCm1zaWOfKIs3/HLxlt+sbnYdgf3xcfCNZV6S0/ljGjSyI7NiOgCT8XMUORZO6giQSZMFvXTb/X2W6etvmv8hz1nvV7VZsvXHKt0GBY4iITg4wquiwoZV3L8dIGcsOd0YBfkbcjL8+WGvvz76S/XLpg7CTmKgrDWpVL6cqO20pDJYZD9U1uFOdbXzUNVAEMBV0HCcQznK2SX7gpPMKQwmBqLOs7S3n52SoWfsz83y0YjBXsNdGwvjpQwsUMuS8gLtzNlvpxRokpfdvLpzVtTE/IAZCdpUO4SoLDfQre859RXFm43ai5EzIKmecwkFCotE91kEbc9G+FyZV7oPV2NDeQQX4nvxZAhpBMChD4RXRvShjfe8lhEqvvXzazbLcruP3PRgCDwB0GJbu+bb9yw/I6dMdSgyfNLtpWVnu8wM+UY0d4KSbCC+aMedkoUDQaWFhKCCZTtEFUP+c0yiyPKDzjakKHAoiH+dcQX1W77FfU3TpxPVL6pdOnTFCsTmn24f6sZ5E5xRKQnkzBg1AmJqedAw5nxPLd+GDp9sCTYFUPTIsNF7LHedY3FvfSmmG9fU3/7+OeL9Ol60OQn+jSWRO5oYLGzfT2eiPpNLUoGZ+DT+8V6a+4OQ4bfD5700LIt5RV3Zc3ycySBRN46CvVLkXwoHVg+CilhB5t5vqlh+7kdih/RPWIGsqqkv8gtsPIdz1xpJxemQgrf/KdTB31Tlfa3n2ypOeasjdD+ZwaaphdiJ9IlBACBQeYbjJKeCqWFKJGugBKE5VGmQeB8clB26+lr77ykc9BUa9C0BQMaIqXPZ8yqU30F44nxRGTm1nW3X/YUfd+zNL3/Ysv5Y455cXPOqvZ1awSyFOGgeeiVY65pRqkR5afQt05rCeCErBG7ymQ8EsGuWcvf4vntl7ycmtC6Zx97PtNI91q2zjlrcY1Tf4MVBB0Ce4VaaMJJ+JCsD/U8sUVggRwslIbUOXEVmILF/MzKXnbTOf8+Z+LfUaGzoAVvNkpmZSLVR0gXihIBro55rwjUhs5KXdwsnDQp+9Cqp3+RsDPzsBGOLgkkOKTwjQoSggUEXQVLQXKF91jy+M5UnCWQBinNt79dmWs/pyl1Y30X5L/2qlsAUauNd5/zYB9v+ygjcJYxeKLITgAoOIC0tOC7CASVYaG0hQ5zDGXMWS5r8vzFX9552brCx51/j0w9WZtTsZOkS0sTUT2cPg8JjmbN/PXht9/TmwAcPmvBYUelHhm5s1Xhrm7x4uBA1nZd3E+/RoIRSgykiMCg5U9T5cIgkCMlYa50pDIs1tGoROO0E/NfjalPXbOpQGnvf0P6e6+2s8b48Yu09weXj3QDcVGripzhGdHK8GuAHBLMt4KTFy41HoPj1vpk26zRP93ZeufdAamnz95qVjznhQYPAEPiJMw4x9GMuN+2Os70D9r0kjGlbvt7Dalz60gmd7Yu3FXMfvy4Nl71jmJeuLhxHAeCS8BAugEQdlqzlgpWRHmwuKfX+sKqab/cvCeNvT1/q5LuqvHixeHZnzfw7Y2BU5/v2xxEZ7fr5RfRrIXRNwkSySX2XwIhasYvWqR1dV4oB89XkpccKg4T4FA4ApDhu2a0qsFZoQ+G3kXCW8Az7LooPaYrDxmnsC2BQ/MNKcI1ppzcgSo/bsbfXnqDJG571yT2+rbbS6wrSuXbG5qxyg+XIVNgjAZL653mU2VZTo+N/PPnPS/vqq0vzRXM9dqQSUB9HHQifQFkQ5ApfYEUicKywXGWhq6kZ/z48Vre4VcXdBDNM7XfIb24uroV3S79kQROV/13991/CqAtlX3v6tB7DGNQikgmhTNH655EHXle6AKN5xX/zSE3PvW1A0vNU0/dUh3kXtBgcgnY0EUgNwJKHud4IAiwQk7giMD92jbxIoDz9rCz7wus+DiF/BDFcRyhS8GDR//gxfcFb9Hj1xw86/99r7tgdFXvOwOUnLRkdItWeTllB8MB0eyRPaV5JGcRVwbmPcES243SJ/vMeP6cXRnAKlD9zZbJEa/hY13HFg0sDHauw5+OeCuCN/EgN2f7rAtW7NpuJLz8nx05/r5mq/wqnP+j+YD+okmhgJeyApSDwrBg5n29JNYko/dd+Z/YgqJ1sc/lkOvfqmq0yp7w9HgfjuRTWDgtLZIk+tEbABQuNclsPR7Ji+i5lSfUVfU77ty/Nr37xzCBv/XNJdkDjz3+acWtDiyyAxIqgyNq+Va0+zzut0+5ho+7f9TSAjWieGDq2UO+EOoR20xcIoE8MiDAAVlKiv4xKaHfhXsOPgT5YJgwhCgHbCjJN+XeXPIh0djXQlP+rYVWNclCsRI9J25edm/OSF7DPMRmKNhYKIAh8xgbmSVIEKQhBCzcjilQ0eGzIJbaaGjpf447mccbZv+0sUh7RCoVi4heAywRtL457RdbQ8JEG1T6zP7DwVlp1tmy5NeOGU8qpF7J7yHfS9FmPIrwyReCUocVpFiQnEQkTygPiOR8pq00yJ3XPOWy18PK+IOqYmlqpBi1I2ovvt/z2iVAI1OLEp87VaOzKnIm0uLl8DM2RbTgU1/l7A4jdpaSsXOZj4gcuV34ywWAQo+aLBENGVMbXjGIsAfIR6iIoWngLXOEvprrbCmR/rsmd9/wA7HycMtrtPx8OtHYJtM9qpOrGHIzhn9s1tNOtDUxUppGUnoYVghZ6BuANp4BFE4j01Z8mEMrmPkgfCZzGlDKBIZDMNuNCvvfokx95HNRJTx1CBZ0D0c4q6LMfqh+yv/9W3GydgXpawANvX7RkM16n0ezZo9jAjKfIB3qFnIKSQHDAWQegCEA4PfQX1rzJuKfAP5HgAMDpHuQpcc3iDkCx3AglPcJC/auyP3H4kZ6GRKFWn4ejpyXhgXLILcNjy8odblR6SPVEU41aFH6hXLaGjYByAmlfDTeAAjQwj8JySFVbwSB5+m6gePbBX7D9AgUHgkcWDGRlOZQBw7tR8GpRMIdNFrzVXbm8UM97bqlOHNQ4LPwdzcddMT1z/TfJGr/3G4mDwN5FnFas6afbwlkEMORcfLkgBFJDr4iW1NoDHAwfcmg+W4wscLXzGMUxWZk7sE4h6dNAwl/dME9faXlQdZPkecLJj1hRPEr87hR4XE9FmYNqS1+BEChPSXMCkYgDJppwkg6KKxAQJyQ7le9hTve5n4i0OJDcCQxBMCgPkjEIPVSwtppLku4Tkepl23xmFeCfX3D1RJHNwlZe+zxw17esHQpDT8shGtY0Jx/YdZc3xHpOQCMB6bXPDdmuIMHeFuGlpn2SEM6n7EwD0PzBJahBAMNBtayW4TM/Oofd5xx0wnV7uSIzD/PhQEfEIMjjxZFkNkmQDoHWoAolEwCkx7pR7NOcR4GtPMlbkmKqdCSCkEm4CAOeJSI4XwExAnfaa/089d+dXPdsh/bQy6Ms+zDwtKwKOETYTsTEwwyaCyEF5G5mw9KtB86yms4LM5aTzJk9i0f6iKnaZd/Gh9AmYTO0gnQqPGL4jhbMxZksBvqvndwdMvURJAfZJf3Ojz9Rf37iUh8IrQNEkIwnwhYsd2vkHOpTwatl+VnjXoQXatXrh7rRPz81XE3/e8WxKyQZ8aMkYULpaqz33BwhadwxIXn0E2gtxh5532xzY569AhJpr0wLaATHIizArsNPtVVG2654GX6vDg11D0+svnquOM8JjQjIGc0XJEwElHpvJK58aI7xZZc5ToWP7F1LXu3r/CugURlaLe3Q0TOJhrF0gnQ6j7JHq7UayglWeGlX2pqq8X/y0g+2+CaL/TurX+/JfW9D8pkZh15riQRyCjW9wvSlzTf9uMXisTo2jLr1M1ldtMFyvNWKCMOGaeFCMVMUlAUoqJEFEEjgSmWXYEhxbHb845KGqQMy1IaJuJit7WXnb5q/S0XLKRJKpJ55eqrncMag2sN117G8L8lJKSZdF+5z0J+N5RU/mFtac0zgwaZZ6+97qLPka5dQydkccqjJ4h0zkYnQFUmTs2GPr/HLOmXJbTtGSjPtdiP21wRk9mjrlxiuQz70yhmYHulSk5cc+e4N4sM7Xrdes9PvoyLhnPi0n1W001H4MQXBbI7+yUWiIciH7sOje53sBUCSHU62Sx8Azgqwj1L5b5Mso6L18+86Ild+y/ef3BvXT4h5C8NP1+PUwdYYbC7htebKOpCf9WT/io/MOov/VnKwCHrOE2gTg5WKkVVwtLZ85Ce8e2GxjbSbmiLVnIGzvkZ/XMbThmWbT12VX3ll59VJC/MavEDaB0Dy9carGWhOBcJ7XltnlW39bjE55dVuE13wtY0wBkkhFAIAOqffui+c847ecJ7vKQ69K2oo4pg6iKISDNXYdv/coBoPK1h+k9eRK1vLM03n/dFqfRS5FiTVc5x/fy+dz1Z2+BWT5+UeWDE2s0tH/35wP7jkKE5yMDElAvtXdoALRLclStWe8OHExqN5AOS+0aF2/JXbsgHfM634f/JnNwhzQmuHi/XlWuXq/SYpjvh43az9Jn+2rB2yeZ0GKXHQ8WbUvo6qSiyQeG4w4zgDmKhb1O4p9AV9qcgUJrmm8p2LR58OMDL/m5lqg6nPMJFu1cucETHrBfO063xih9zdF3qZ97Wzdy8wElsypvOSCy9GwIeKRMs29TfyZ/0xYxLO/PluwE0HoTecA/557QouRDBJ5xaWB/MojTAqoThFZZT6Wy6f/ucU25Cw8653yuHqEBH8jZWHDyaaf7pWaWf7ljxJCkFpHBhhxCZkrcXckOuAYIEAaRE6Ib7RpBPR5VcXsmcPx7Zuu7fFu84a9Sdfot1Bkz9ff/GaI9nc7GSYUK20UZ/qOyxOYpJ0pTQ8naZ33Zty+SfPVxsQ9fdAKIXY+5/2XqnvmqqqyKX4qhCUmkRoQUdEpaiSeP2gwe1rL5vxcMTyJ//TgWo8t6p1/qmfXZUUgQner43IK0negEfnGiDTFHgKTh2e2RLQrNXwU/5qJqp5WtS524As52i/106pxz4hvKSm4QvzsYMlCAM4dD3OFYkt5YG7XO2Tb14/p4T/zWAih33+/WLB8po8gfYeu6X8DLr40798k/m1q0vft+fV0ToiZa4G0sw2uJmLNEQz74699Tcnszurz6Hpp4Y4pnB0diqqon6YhVc3o9W/S/6L6X7a5z/LXT+AwIMQBEymimcAAAAAElFTkSuQmCC" diff --git a/dsh/plugins/dsh-mnemon-provider-hindsight/src/index.ts b/dsh/plugins/dsh-mnemon-provider-hindsight/src/index.ts new file mode 100644 index 000000000..14d387f43 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-hindsight/src/index.ts @@ -0,0 +1,24 @@ +import { MEMORY_SPACE_PROVIDER_API_VERSION, defineMemorySpaceProvider, defineMemorySpaceProviderDefinition } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { HindsightProvider } from './driver.ts' +import { descriptor } from './descriptor.ts' + +export { HindsightProvider, descriptor } + +export const definition = defineMemorySpaceProviderDefinition({ + manifest: { + apiVersion: MEMORY_SPACE_PROVIDER_API_VERSION, kind: 'provider', + typeId: descriptor.id, packageName: 'dsh-mnemon-provider-hindsight', version: '0.5.5', + label: descriptor.label, icon: descriptor.icon, summary: descriptor.summary, + ...(descriptor.summaryI18nKey === undefined ? {} : { summaryI18nKey: descriptor.summaryI18nKey }), + origin: descriptor.origin, locality: descriptor.kind, workspaceBinding: descriptor.workspaceBinding, + capabilities: descriptor.capabilities, fields: descriptor.fields, + secrets: descriptor.fields.filter(field => field.input === 'secret').map(field => field.key), + scoreSemantics: 'normalized-relevance', + }, + create: context => new HindsightProvider(context.memorySpaces ?? context.memoryBodies, { requestTimeoutMs: context.config.timeoutMs }), +}) + +export default defineMemorySpaceProvider({ + id: descriptor.id, + apply(ctx, host) { host.install(ctx, definition) }, +}) diff --git a/dsh/plugins/dsh-mnemon-provider-hindsight/tests/driver.spec.ts b/dsh/plugins/dsh-mnemon-provider-hindsight/tests/driver.spec.ts new file mode 100644 index 000000000..b28fce694 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-hindsight/tests/driver.spec.ts @@ -0,0 +1,86 @@ +import { mkdtempSync, rmSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { afterEach, describe, expect, it, vi } from 'vitest' +import { createMemorySpaceProviderFixture } from 'dsh-mnemon-source-memory-spaces/testing' +import type { MemoryProviderConnection, MemoryProviderId } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { HindsightProvider, descriptor } from '../src/index.ts' + +const temporaryDirectories: string[] = [] +afterEach(() => { for (const directory of temporaryDirectories.splice(0)) rmSync(directory, { recursive: true, force: true }) }) + +async function providerBody(providerId: MemoryProviderId, connection: MemoryProviderConnection) { + if (providerId !== descriptor.id) throw new Error('Wrong Provider fixture') + const dataDir = mkdtempSync(join(tmpdir(), 'mnemon-hindsight-driver-')) + temporaryDirectories.push(dataDir) + const { authority: registry, body } = createMemorySpaceProviderFixture(descriptor, connection, { dataDir, instanceId: 'work-account' }) + return { registry, body } +} + +function response(payload: unknown, status = 200): Response { + return new Response(payload === undefined ? null : JSON.stringify(payload), { status, headers: { 'Content-Type': 'application/json' } }) +} + +describe('standalone hindsight data plane', () => { + it('discovers its native namespaces independently', async () => { + const fetchMock = vi.fn(async () => response({ banks: [{ bank_id: 'bank-1', name: 'Product bank', mission: 'Hindsight source metadata.' }] })) + const hindsight = await providerBody('hindsight', { endpoint: 'https://hindsight.example', bankId: 'old', budget: 'mid' }) + await expect(new HindsightProvider(hindsight.registry, { fetch: fetchMock }).discover({ endpoint: 'https://hindsight.example' })).resolves.toEqual([ + expect.objectContaining({ externalId: 'bank-1', name: 'Product bank', description: 'Hindsight source metadata.' }), + ]) + }) + + it('maps Hindsight recall, graph traversal, asynchronous retain, and soft forget', async () => { + const requests: Array<{ url: string; init?: RequestInit }> = [] + const fetchMock = vi.fn(async (url, init) => { + requests.push({ url: String(url), ...(init === undefined ? {} : { init }) }) + const path = new URL(String(url)).pathname + if (path === '/health/live') return response({ status: 'alive', version: '0.9.1' }) + if (path.endsWith('/stats')) return response({ total_nodes: 3, total_links: 2, nodes_by_fact_type: { world: 2, observation: 1 }, operations_by_status: { completed: 4 } }) + if (path.endsWith('/entities')) return response({ items: [{ canonical_name: 'Alice', mention_count: 3 }], total: 1 }) + if (path.endsWith('/memories/recall')) return response({ results: [{ id: 'hs-1', text: 'Alice uses TypeScript.', type: 'world', entities: ['Alice'], scores: { final: 0.93 } }] }) + if (path.endsWith('/memories/list')) return response({ items: [{ id: 'hs-1', text: 'Alice uses TypeScript.', type: 'world' }], total: 1 }) + if (path.endsWith('/graph')) return response({ + nodes: [ + { data: { id: 'hs-1', text: 'Alice', entities: 'Alice' } }, + { data: { id: 'hs-2', text: 'TypeScript', color: '#42a5f5' } }, + { data: { id: 'hs-3', text: 'Node.js' } }, + ], + edges: [ + { data: { source: 'hs-1', target: 'hs-2', linkType: 'entity' } }, + { data: { source: 'hs-2', target: 'hs-3', linkType: 'semantic' } }, + ], + }) + if (path.endsWith('/memories') && init?.method === 'POST') return response({ operation_id: 'op-1', items_count: 1 }) + if (path.endsWith('/memories/hs-1') && init?.method === 'PATCH') return response({ state: 'invalidated' }) + throw new Error(`unexpected ${init?.method ?? 'GET'} ${path}`) + }) + const { registry, body } = await providerBody('hindsight', { + endpoint: 'https://api.hindsight.vectorize.io', apiKey: 'hs-secret', bankId: 'alice/profile', budget: 'high', + }) + const provider = new HindsightProvider(registry, { fetch: fetchMock }) + + await expect(provider.search(body, { query: 'language', limit: 3 })).resolves.toEqual({ + results: [expect.objectContaining({ id: 'hs-1', category: 'world', score: 0.93, entities: ['Alice'] })], + }) + await expect(provider.list(body, { limit: 25 })).resolves.toEqual([expect.objectContaining({ id: 'hs-1' })]) + await expect(provider.related(body, 'hs-1', 2)).resolves.toEqual([ + expect.objectContaining({ id: 'hs-2' }), expect.objectContaining({ id: 'hs-3' }), + ]) + await expect(provider.status(body)).resolves.toEqual({ + healthy: true, + stats: expect.objectContaining({ totalInsights: 3, edgeCount: 2, oplogCount: 4, byCategory: { world: 2, observation: 1 }, topEntities: [{ entity: 'Alice', count: 3 }] }), + }) + await expect(provider.remember(body, { content: 'Alice ships TypeScript.', category: 'decision', tags: ['dsh'], entities: ['Alice'] })).resolves.toMatchObject({ action: 'queued', operationId: 'op-1', itemsCount: 1 }) + await expect(provider.forget(body, 'hs-1')).resolves.toMatchObject({ action: 'invalidated', id: 'hs-1' }) + + expect(new URL(requests[0]!.url).pathname).toBe('/v1/default/banks/alice%2Fprofile/memories/recall') + expect(new Headers(requests[0]?.init?.headers).get('Authorization')).toBe('Bearer hs-secret') + expect(JSON.parse(String(requests[0]?.init?.body))).toMatchObject({ query: 'language', budget: 'high', types: ['world', 'experience', 'observation'] }) + expect(JSON.parse(String(requests[6]?.init?.body))).toMatchObject({ + items: [{ content: 'Alice ships TypeScript.', context: 'decision', tags: ['dsh'], entities: [{ text: 'Alice' }] }], + async: true, + }) + expect(JSON.parse(String(requests[7]?.init?.body))).toEqual({ state: 'invalidated', reason: 'Forgotten from dsh-mnemon' }) + }) +}) diff --git a/dsh/plugins/dsh-mnemon-provider-hindsight/tests/provider.spec.ts b/dsh/plugins/dsh-mnemon-provider-hindsight/tests/provider.spec.ts new file mode 100644 index 000000000..b9717d930 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-hindsight/tests/provider.spec.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from 'vitest' +import type { MemorySpaceAuthority } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { mountMemorySpaceProvider } from 'dsh-mnemon-source-memory-spaces/testing' +import module, { definition, HindsightProvider, descriptor } from '../src/index.ts' + +describe('independent hindsight Provider', () => { + it('installs complete immutable metadata in one parent-owned child', async () => { + const mounted = await mountMemorySpaceProvider(module, { instanceId: 'work', config: undefined }) + try { + expect(mounted.descriptor).toMatchObject({ id: 'work', typeId: 'hindsight', label: descriptor.label }) + expect(Object.isFrozen(mounted.manifest)).toBe(true) + const authority: MemorySpaceAuthority = { runner: { effectiveDataDir: () => '/unused' }, list: () => [], providerConnection: () => ({}) } + expect(mounted.createAdapter({ memoryBodies: authority, config: { timeoutMs: 200 } }).id).toBe('work') + expect(mounted.registered).toBe(true) + expect(definition.manifest.secrets).toEqual(descriptor.fields.filter(field => field.input === 'secret').map(field => field.key).sort()) + } finally { await mounted.dispose() } + expect(mounted.registered).toBe(false) + }) + + it('constructs its own driver from a structural authority, without the main repository', () => { + const authority: MemorySpaceAuthority = { runner: { effectiveDataDir: () => '/unused' }, list: () => [], providerConnection: () => ({}) } + const adapter = definition.create({ memoryBodies: authority, config: { timeoutMs: 200 }, providerInstanceId: 'hindsight', manifest: definition.manifest }) + expect(adapter).toBeInstanceOf(HindsightProvider) + expect(adapter.id).toBe('hindsight') + }) +}) diff --git a/dsh/plugins/dsh-mnemon-provider-hindsight/tsconfig.json b/dsh/plugins/dsh-mnemon-provider-hindsight/tsconfig.json new file mode 100644 index 000000000..badeba7ca --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-hindsight/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2024", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "verbatimModuleSyntax": true, + "allowImportingTsExtensions": true, + "skipLibCheck": true, + "types": [ + "node" + ] + }, + "include": [ + "src/**/*.ts", + "tests/**/*.ts", + "tsdown.config.ts", + "vitest.config.ts" + ] +} diff --git a/dsh/plugins/dsh-mnemon-provider-hindsight/tsdown.config.ts b/dsh/plugins/dsh-mnemon-provider-hindsight/tsdown.config.ts new file mode 100644 index 000000000..2d868cbd8 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-hindsight/tsdown.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'tsdown' + +export default defineConfig({ + entry: ['src/index.ts', 'src/descriptor.ts'], outDir: 'lib', format: 'esm', platform: 'node', + target: 'es2024', dts: true, clean: true, fixedExtension: false, deps: { neverBundle: true }, +}) diff --git a/dsh/plugins/dsh-mnemon-provider-hindsight/vitest.config.ts b/dsh/plugins/dsh-mnemon-provider-hindsight/vitest.config.ts new file mode 100644 index 000000000..5a6724c22 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-hindsight/vitest.config.ts @@ -0,0 +1,3 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ test: { environment: 'node' } }) diff --git a/dsh/plugins/dsh-mnemon-provider-holographic/LICENSE b/dsh/plugins/dsh-mnemon-provider-holographic/LICENSE new file mode 100644 index 000000000..e32e112dc --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-holographic/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 dsh-external contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dsh/plugins/dsh-mnemon-provider-holographic/README.md b/dsh/plugins/dsh-mnemon-provider-holographic/README.md new file mode 100644 index 000000000..343595008 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-holographic/README.md @@ -0,0 +1,27 @@ +# dsh-mnemon-provider-holographic + +Local structured-fact storage with trust metadata, entities and related evidence. + +No external server is required. Choose a data path when a Source-local default is not appropriate; this adapter owns its JSON format. + +## Use + +The default `dsh-mnemon` Starter already installs this package. Enable and configure the service on the `dsh-mnemon` page under **Plugins**, then inspect its synchronized Memory Spaces. + +For a custom composition, install this package alongside `dsh-mnemon-source-memory-spaces` and include it in that Source's `config.providers`: + +```yaml +providers: + - use: dsh-mnemon-provider-holographic + instanceId: holographic +``` + +This is a **Memory Spaces child module**, not a top-level Source or a complete Strategy. It registers through `dsh-mnemon-source-memory-spaces/provider-sdk`; each parent Source owns its child Fibers, connection settings and lifetime. Credentials stay on the Host. + +[Provider setup and capability matrix](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/guides/memory-providers.md) · [中文指南](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/zh-CN/guides/memory-providers.md) + +## Develop independently + +From a source checkout with the declared dependencies installed, run `pnpm verify`. Tests, build and public exports belong to this package, without importing another package's controllers or repository configuration. Use the Source's public `/testing` fixtures for child registration and driver conformance; live service tests require a separately authorized environment. + +[Plugin author guide](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/development/extensions.md) diff --git a/dsh/plugins/dsh-mnemon-provider-holographic/package.json b/dsh/plugins/dsh-mnemon-provider-holographic/package.json new file mode 100644 index 000000000..f6cbb9244 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-holographic/package.json @@ -0,0 +1,55 @@ +{ + "name": "dsh-mnemon-provider-holographic", + "version": "0.5.6", + "description": "Holographic Provider child for the Memory Spaces Source.", + "license": "MIT", + "type": "module", + "engines": { + "node": ">=20" + }, + "exports": { + ".": { + "types": "./lib/index.d.ts", + "default": "./lib/index.js" + }, + "./descriptor": { + "types": "./lib/descriptor.d.ts", + "default": "./lib/descriptor.js" + }, + "./package.json": "./package.json" + }, + "files": [ + "lib", + "README.md" + ], + "scripts": { + "typecheck": "tsc --noEmit", + "build": "tsdown", + "test": "vitest run", + "verify": "pnpm typecheck && pnpm test && pnpm build" + }, + "peerDependencies": { + "@deepseek-ai/cordis": "^4.0.1", + "dsh-mnemon-source-memory-spaces": "^0.5.1" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "dsh-mnemon-source-memory-spaces": "^0.5.15", + "tsdown": "^0.22.0", + "typescript": "^5.9.0", + "vitest": "^3.2.0" + }, + "publishConfig": { + "access": "public", + "tag": "latest" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/mnemon-dev/mnemon.git", + "directory": "dsh/plugins/dsh-mnemon-provider-holographic" + }, + "homepage": "https://github.com/mnemon-dev/mnemon/tree/master/dsh/plugins/dsh-mnemon-provider-holographic#readme", + "bugs": { + "url": "https://github.com/mnemon-dev/mnemon/issues" + } +} diff --git a/dsh/plugins/dsh-mnemon-provider-holographic/src/descriptor.ts b/dsh/plugins/dsh-mnemon-provider-holographic/src/descriptor.ts new file mode 100644 index 000000000..479a96a7d --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-holographic/src/descriptor.ts @@ -0,0 +1,62 @@ +import type { MemoryProviderDescriptor } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { ICON } from './icon.ts' + +/** Browser-safe metadata owned by this Provider. */ +export const descriptor: MemoryProviderDescriptor = { + "id": "holographic", + "label": "Holographic", + "icon": { + "kind": "data-url", + "value": ICON + }, + "kind": "local", + "workspaceBinding": "optional-override", + "summary": "Local structured fact memory with trust scoring, entity resolution, and compositional retrieval.", + "summaryI18nKey": "overview.providerSummary.holographic", + "origin": "third-party", + "capabilities": { + "search": true, + "browse": true, + "graph": true, + "entities": true, + "related": true, + "remember": true, + "link": false, + "forget": true, + "writeMode": "exact", + "deletionMode": "hard" + }, + "fields": [ + { + "key": "dataPath", + "label": "Fact store path", + "scope": "service", + "role": "global-location", + "input": "path", + "required": false, + "i18nKey": "overview.providerField.dataPath" + }, + { + "key": "defaultTrust", + "label": "Default trust", + "scope": "memory", + "input": "number", + "required": true, + "defaultValue": 0.5, + "min": 0, + "max": 1, + "i18nKey": "overview.providerField.defaultTrust" + }, + { + "key": "minTrust", + "label": "Minimum recall trust", + "scope": "memory", + "input": "number", + "required": true, + "defaultValue": 0.3, + "min": 0, + "max": 1, + "i18nKey": "overview.providerField.minTrust" + } + ] +} diff --git a/dsh/plugins/dsh-mnemon-provider-holographic/src/driver.ts b/dsh/plugins/dsh-mnemon-provider-holographic/src/driver.ts new file mode 100644 index 000000000..2bdea174b --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-holographic/src/driver.ts @@ -0,0 +1,257 @@ +import { randomUUID } from 'node:crypto' +import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs' +import { basename, dirname, isAbsolute, join, resolve } from 'node:path' +import { topScored } from './top-scored.ts' +import type { JsonValue } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import type { MemorySpaceAuthority } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import type { + Insight, + MemoryBody as MemorySpace, + MemoryBodyStats as MemorySpaceStats, + MemoryGraphSnapshot, + MemoryListRequest, + MemoryProviderConnection, + RememberRequest, + SearchRequest, +} from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { NORMALIZED_RELEVANCE_SCORE, type MemoryProviderAdapter, type ProviderBodyStatus as ProviderSpaceStatus, type ProviderMemorySpace, type ProviderSearchResult } from 'dsh-mnemon-source-memory-spaces/provider-sdk' + +interface HolographicFact { + id: string + content: string + category: string + tags: string[] + entities: string[] + trustScore: number + createdAt: string + updatedAt: string +} + +interface HolographicStore { + version: 1 + facts: HolographicFact[] +} + +const WORD = /[\p{L}\p{N}_-]+/gu +const CJK = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]+/gu +const QUOTED = /["“”'‘’]([^"“”'‘’]{2,100})["“”'‘’]/gu +const CAPITALIZED = /\b([A-Z][\p{L}\p{N}_-]+(?:\s+[A-Z][\p{L}\p{N}_-]+)*)\b/gu + +function clampTrust(value: number): number { + return Math.min(1, Math.max(0, value)) +} + +function terms(value: string): Set { + const normalized = value.toLocaleLowerCase() + const output = new Set((normalized.match(WORD) ?? []).filter(token => token.length > 1)) + for (const run of normalized.match(CJK) ?? []) { + for (const character of run) output.add(character) + for (let index = 0; index < run.length - 1; index += 1) output.add(run.slice(index, index + 2)) + } + return output +} + +function overlap(left: Set, right: Set): number { + if (left.size === 0 || right.size === 0) return 0 + let shared = 0 + const [smaller, larger] = left.size <= right.size ? [left, right] : [right, left] + for (const value of smaller) if (larger.has(value)) shared += 1 + return shared / (left.size + right.size - shared) +} + +function extractEntities(content: string, supplied: string[] = []): string[] { + const values = [...supplied] + for (const match of content.matchAll(QUOTED)) values.push(match[1]!) + for (const match of content.matchAll(CAPITALIZED)) values.push(match[1]!) + return [...new Set(values.map(value => value.trim()).filter(value => value.length >= 2 && value.length <= 100))].slice(0, 50) +} + +function insight(fact: HolographicFact, score?: number): Insight { + return { + id: fact.id, + content: fact.content, + category: fact.category, + importance: fact.trustScore, + tags: fact.tags, + entities: fact.entities, + source: 'external', + createdAt: fact.createdAt, + ...(score === undefined ? {} : { score }), + } +} + +export class HolographicProvider implements MemoryProviderAdapter { + readonly id = 'holographic' as const + readonly scoreSemantics = NORMALIZED_RELEVANCE_SCORE + + constructor(private readonly memorySpaces: MemorySpaceAuthority) {} + + async discover(connection: MemoryProviderConnection): Promise { + const configured = String(connection.dataPath ?? '').trim() + const path = configured === '' + ? join(this.memorySpaces.runner.effectiveDataDir(), 'state', 'holographic', 'store.json') + : isAbsolute(configured) + ? configured + : resolve(this.memorySpaces.runner.effectiveDataDir(), configured) + const label = basename(path).replace(/\.json$/iu, '') || 'Holographic' + return [{ + externalId: path, + name: label === 'store' ? 'Holographic' : label, + description: `Holographic fact store at ${path}`, + connection: { defaultTrust: 0.5, minTrust: 0.3 }, + }] + } + + async status(body: MemorySpace): Promise { + try { + const store = this.load(body) + return { healthy: true, stats: this.stats(store) } + } catch (error) { + return { healthy: false, error: error instanceof Error ? error.message : String(error) } + } + } + + async search(body: MemorySpace, request: SearchRequest): Promise { + const store = this.load(body) + const connection = this.connection(body) + const minTrust = clampTrust(Number(connection.minTrust ?? 0.3)) + const queryTerms = terms(request.query) + const query = request.query.toLocaleLowerCase() + const limit = Math.min(Math.max(request.limit ?? 10, 1), 50) + const results = topScored(store.facts.flatMap(fact => { + if (fact.trustScore < minTrust || (request.category !== undefined && fact.category !== request.category)) return [] + const lexical = overlap(queryTerms, terms(`${fact.content} ${fact.tags.join(' ')} ${fact.entities.join(' ')}`)) + const phrase = fact.content.toLocaleLowerCase().includes(query) ? 1 : 0 + const entity = fact.entities.some(value => query.includes(value.toLocaleLowerCase())) ? 1 : 0 + const relevance = Math.max(lexical, phrase * 0.9, entity * 0.8) + return relevance <= 0 ? [] : [{ fact, score: relevance * fact.trustScore }] + }), limit) + return { results: results.map(result => insight(result.fact, result.score)) } + } + + async list(body: MemorySpace, request: MemoryListRequest): Promise { + if (request.query !== undefined && request.query.trim() !== '') { + return (await this.search(body, { + query: request.query, + ...(request.category === undefined ? {} : { category: request.category }), + ...(request.limit === undefined ? {} : { limit: request.limit }), + })).results + } + const store = this.load(body) + const minTrust = clampTrust(Number(this.connection(body).minTrust ?? 0.3)) + return store.facts + .filter(fact => fact.trustScore >= minTrust && (request.category === undefined || fact.category === request.category)) + .sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)) + .slice(0, Math.min(Math.max(request.limit ?? 200, 1), 1000)) + .map(fact => insight(fact)) + } + + async graph(body: MemorySpace): Promise { + const facts = (await this.list(body, { limit: 500 })) + const entities = [...new Set(facts.flatMap(fact => fact.entities ?? []))] + return { + nodes: [ + ...facts.map(fact => ({ ...fact, color: '#6574d9', kind: 'memory' as const })), + ...entities.map(entity => ({ id: `entity:${encodeURIComponent(entity)}`, content: entity, color: '#2ecc71', kind: 'entity' as const })), + ], + edges: facts.flatMap(fact => (fact.entities ?? []).map(entity => ({ + sourceId: fact.id, + targetId: `entity:${encodeURIComponent(entity)}`, + label: 'entity', + color: '#2ecc71', + type: 'entity' as const, + }))), + generatedAt: new Date().toISOString(), + } + } + + async related(body: MemorySpace, id: string, _depth: number): Promise { + const store = this.load(body) + const source = store.facts.find(fact => fact.id === id) + if (source === undefined) return [] + const sourceEntities = new Set(source.entities.map(value => value.toLocaleLowerCase())) + const sourceTerms = terms(source.content) + return topScored(store.facts.flatMap(fact => { + if (fact.id === id) return [] + const sharedEntities = fact.entities.filter(value => sourceEntities.has(value.toLocaleLowerCase())).length + const score = Math.max(sharedEntities === 0 ? 0 : Math.min(1, 0.5 + sharedEntities * 0.2), overlap(sourceTerms, terms(fact.content))) * fact.trustScore + return score <= 0 ? [] : [{ fact, score }] + }), 20).map(result => insight(result.fact, result.score)) + } + + async remember(body: MemorySpace, request: RememberRequest): Promise { + const store = this.load(body) + const existing = store.facts.find(fact => fact.content === request.content.trim()) + if (existing !== undefined) return { action: 'skipped', provider: this.id, id: existing.id, summary: 'Holographic already contains this fact.' } + const now = new Date().toISOString() + const connection = this.connection(body) + const fact: HolographicFact = { + id: `holo-${randomUUID()}`, + content: request.content.trim(), + category: request.category ?? 'general', + tags: [...new Set(request.tags ?? [])], + entities: extractEntities(request.content, request.entities), + trustScore: clampTrust(Number(request.importance ?? connection.defaultTrust ?? 0.5)), + createdAt: now, + updatedAt: now, + } + store.facts.push(fact) + this.save(body, store) + return { action: 'stored', provider: this.id, id: fact.id, summary: 'Holographic stored the structured fact.' } + } + + async forget(body: MemorySpace, id: string): Promise { + const store = this.load(body) + const before = store.facts.length + store.facts = store.facts.filter(fact => fact.id !== id) + if (store.facts.length === before) throw new Error(`unknown Holographic fact: ${id}`) + this.save(body, store) + return { action: 'deleted', provider: this.id, id } + } + + private connection(body: MemorySpace): Record { + if ((body.provider.typeId ?? body.provider.id) !== this.id) throw new Error(`Holographic cannot serve provider ${body.provider.id}`) + return this.memorySpaces.providerConnection(body.id, body.provider.id) + } + + private path(body: MemorySpace): string { + const configured = String(this.connection(body).dataPath ?? '').trim() + if (configured === '') return join(this.memorySpaces.runner.effectiveDataDir(), 'state', 'holographic', 'store.json') + return isAbsolute(configured) ? configured : resolve(this.memorySpaces.runner.effectiveDataDir(), configured) + } + + private load(body: MemorySpace): HolographicStore { + const path = this.path(body) + if (!existsSync(path)) return { version: 1, facts: [] } + const value = JSON.parse(readFileSync(path, 'utf8')) as Partial + if (value.version !== 1 || !Array.isArray(value.facts)) throw new Error(`invalid Holographic fact store: ${path}`) + return { version: 1, facts: value.facts } + } + + private save(body: MemorySpace, store: HolographicStore): void { + const path = this.path(body) + mkdirSync(dirname(path), { recursive: true, mode: 0o700 }) + const temporary = `${path}.${process.pid}.${randomUUID()}.tmp` + writeFileSync(temporary, `${JSON.stringify(store, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 }) + renameSync(temporary, path) + chmodSync(path, 0o600) + } + + private stats(store: HolographicStore): MemorySpaceStats { + const byCategory: Record = {} + const entityCounts = new Map() + for (const fact of store.facts) { + byCategory[fact.category] = (byCategory[fact.category] ?? 0) + 1 + for (const entity of fact.entities) entityCounts.set(entity, (entityCounts.get(entity) ?? 0) + 1) + } + return { + totalInsights: store.facts.length, + deletedInsights: 0, + edgeCount: store.facts.reduce((total, fact) => total + fact.entities.length, 0), + oplogCount: 0, + dbSizeBytes: Buffer.byteLength(JSON.stringify(store)), + byCategory, + topEntities: [...entityCounts].map(([entity, count]) => ({ entity, count })).sort((left, right) => right.count - left.count).slice(0, 20), + } + } +} diff --git a/dsh/plugins/dsh-mnemon-provider-holographic/src/icon.ts b/dsh/plugins/dsh-mnemon-provider-holographic/src/icon.ts new file mode 100644 index 000000000..f10a7bf71 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-holographic/src/icon.ts @@ -0,0 +1,2 @@ +/** Browser-safe brand asset shipped by this Provider, not by the Host. */ +export const ICON = "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMzYgMzYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHJlY3Qgd2lkdGg9IjM2IiBoZWlnaHQ9IjM2IiByeD0iOSIgZmlsbD0iIzE1MTcyOSIvPjxwYXRoIGQ9Ik0xOCA3LjUgMjguNSAxOCAxOCAyOC41IDcuNSAxOCAxOCA3LjVaIiBzdHJva2U9IiNBRUI3RkYiIHN0cm9rZS13aWR0aD0iMS41Ii8+PHBhdGggZD0iTTE4IDExLjUgMjQuNSAxOCAxOCAyNC41IDExLjUgMTggMTggMTEuNVoiIHN0cm9rZT0iIzdGOENGNCIgc3Ryb2tlLXdpZHRoPSIxLjUiLz48Y2lyY2xlIGN4PSIxOCIgY3k9IjE4IiByPSIyLjc1IiBmaWxsPSIjRENFMEZGIi8+PC9zdmc+" diff --git a/dsh/plugins/dsh-mnemon-provider-holographic/src/index.ts b/dsh/plugins/dsh-mnemon-provider-holographic/src/index.ts new file mode 100644 index 000000000..8add1b552 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-holographic/src/index.ts @@ -0,0 +1,24 @@ +import { MEMORY_SPACE_PROVIDER_API_VERSION, defineMemorySpaceProvider, defineMemorySpaceProviderDefinition } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { HolographicProvider } from './driver.ts' +import { descriptor } from './descriptor.ts' + +export { HolographicProvider, descriptor } + +export const definition = defineMemorySpaceProviderDefinition({ + manifest: { + apiVersion: MEMORY_SPACE_PROVIDER_API_VERSION, kind: 'provider', + typeId: descriptor.id, packageName: 'dsh-mnemon-provider-holographic', version: '0.5.6', + label: descriptor.label, icon: descriptor.icon, summary: descriptor.summary, + ...(descriptor.summaryI18nKey === undefined ? {} : { summaryI18nKey: descriptor.summaryI18nKey }), + origin: descriptor.origin, locality: descriptor.kind, workspaceBinding: descriptor.workspaceBinding, + capabilities: descriptor.capabilities, fields: descriptor.fields, + secrets: descriptor.fields.filter(field => field.input === 'secret').map(field => field.key), + scoreSemantics: 'normalized-relevance', + }, + create: context => new HolographicProvider(context.memorySpaces ?? context.memoryBodies), +}) + +export default defineMemorySpaceProvider({ + id: descriptor.id, + apply(ctx, host) { host.install(ctx, definition) }, +}) diff --git a/dsh/plugins/dsh-mnemon-provider-holographic/src/top-scored.ts b/dsh/plugins/dsh-mnemon-provider-holographic/src/top-scored.ts new file mode 100644 index 000000000..c1141c3ef --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-holographic/src/top-scored.ts @@ -0,0 +1,36 @@ +/** Stable descending score selection. Keep sort semantics for non-finite legacy scores. */ +export function topScored(values: T[], limit: number): T[] { + const count = Math.min(values.length, Math.max(0, Math.trunc(limit) || 0)) + if (count === 0) return [] + if (values.length <= Math.max(64, count * 2) || values.some(value => !Number.isFinite(value.score))) { + return values.sort((left, right) => right.score - left.score).slice(0, count) + } + // The input position breaks ties exactly as stable Array.sort does. + const compare = (left: number, right: number) => values[right]!.score - values[left]!.score || left - right + const heap: number[] = [] + for (let index = 0; index < values.length; index++) { + if (heap.length < count) { + let child = heap.length + heap.push(index) + while (child > 0) { + const parent = (child - 1) >>> 1 + if (compare(heap[parent]!, index) >= 0) break + heap[child] = heap[parent]! + child = parent + } + heap[child] = index + } else if (compare(index, heap[0]!) < 0) { + // The root is the worst retained row; restore that invariant after replacing it. + let parent = 0 + while (parent * 2 + 1 < count) { + let child = parent * 2 + 1 + if (child + 1 < count && compare(heap[child + 1]!, heap[child]!) > 0) child += 1 + if (compare(index, heap[child]!) >= 0) break + heap[parent] = heap[child]! + parent = child + } + heap[parent] = index + } + } + return heap.sort(compare).map(index => values[index]!) +} diff --git a/dsh/plugins/dsh-mnemon-provider-holographic/tests/driver.spec.ts b/dsh/plugins/dsh-mnemon-provider-holographic/tests/driver.spec.ts new file mode 100644 index 000000000..0e32678a0 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-holographic/tests/driver.spec.ts @@ -0,0 +1,60 @@ +import { mkdtempSync, rmSync, statSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { afterEach, describe, expect, it } from 'vitest' +import { createMemorySpaceProviderFixture } from 'dsh-mnemon-source-memory-spaces/testing' +import { HolographicProvider, descriptor } from '../src/index.ts' + +const temporaryDirectories: string[] = [] +afterEach(() => { for (const directory of temporaryDirectories.splice(0)) rmSync(directory, { recursive: true, force: true }) }) + +describe('standalone holographic data plane', () => { + it('preserves exact Jaccard scores for unequal token sets and Unicode', async () => { + const dataDir = mkdtempSync(join(tmpdir(), 'dsh-mnemon-holographic-score-')) + temporaryDirectories.push(dataDir) + const { authority, body } = createMemorySpaceProviderFixture(descriptor, { defaultTrust: 1, minTrust: 0 }, { dataDir }) + const provider = new HolographicProvider(authority) + await provider.remember(body, { content: 'alpha gamma delta' }) + await provider.remember(body, { content: '存储 查询' }) + expect((await provider.search(body, { query: 'alpha beta' })).results.map(row => row.score)).toEqual([1 / 4]) + expect((await provider.search(body, { query: 'alpha beta gamma delta epsilon' })).results.map(row => row.score)).toEqual([3 / 5]) + expect((await provider.search(body, { query: '存储 beta' })).results.map(row => row.score)).toEqual([3 / 7]) + expect((await provider.search(body, { query: 'unknown terms' })).results).toEqual([]) + expect((await provider.search(body, { query: '' })).results.map(row => row.score)).toEqual([0.9, 0.9]) + }) + + it('stores Holographic facts locally with trust, entities, graph projection, related recall, and hard forget', async () => { + const dataDir = mkdtempSync(join(tmpdir(), 'dsh-mnemon-holographic-')) + temporaryDirectories.push(dataDir) + const dataPath = join(dataDir, 'facts', 'memory.json') + + const { authority: registry, body } = createMemorySpaceProviderFixture(descriptor, { dataPath, defaultTrust: 0.7, minTrust: 0.3 }, { dataDir, instanceId: 'work-account' }) + const provider = new HolographicProvider(registry) + + const first = await provider.remember(body, { + content: 'TypeScript service uses SQLite for durable state.', + category: 'decision', + tags: ['architecture'], + entities: ['TypeScript', 'SQLite'], + }) as { id: string } + const second = await provider.remember(body, { + content: 'SQLite backups run daily before deployment.', + category: 'fact', + entities: ['SQLite'], + }) as { id: string } + + await expect(provider.remember(body, { content: 'TypeScript service uses SQLite for durable state.' })).resolves.toMatchObject({ action: 'skipped', id: first.id }) + await expect(provider.search(body, { query: 'TypeScript storage', limit: 5 })).resolves.toEqual({ + results: [expect.objectContaining({ id: first.id, category: 'decision', entities: ['TypeScript', 'SQLite'] })], + }) + await expect(provider.related(body, first.id, 2)).resolves.toEqual([expect.objectContaining({ id: second.id })]) + await expect(provider.graph(body)).resolves.toMatchObject({ + nodes: expect.arrayContaining([expect.objectContaining({ id: 'entity:SQLite', kind: 'entity' })]), + edges: expect.arrayContaining([expect.objectContaining({ sourceId: first.id, targetId: 'entity:SQLite', type: 'entity' })]), + }) + await expect(provider.status(body)).resolves.toMatchObject({ healthy: true, stats: { totalInsights: 2, edgeCount: 3 } }) + expect(statSync(dataPath).mode & 0o777).toBe(0o600) + await expect(provider.forget(body, first.id)).resolves.toMatchObject({ action: 'deleted' }) + await expect(provider.list(body, {})).resolves.toHaveLength(1) + }) +}) diff --git a/dsh/plugins/dsh-mnemon-provider-holographic/tests/provider.spec.ts b/dsh/plugins/dsh-mnemon-provider-holographic/tests/provider.spec.ts new file mode 100644 index 000000000..33c935bfa --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-holographic/tests/provider.spec.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from 'vitest' +import type { MemorySpaceAuthority } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { mountMemorySpaceProvider } from 'dsh-mnemon-source-memory-spaces/testing' +import module, { definition, HolographicProvider, descriptor } from '../src/index.ts' + +describe('independent holographic Provider', () => { + it('installs complete immutable metadata in one parent-owned child', async () => { + const mounted = await mountMemorySpaceProvider(module, { instanceId: 'work', config: undefined }) + try { + expect(mounted.descriptor).toMatchObject({ id: 'work', typeId: 'holographic', label: descriptor.label }) + expect(Object.isFrozen(mounted.manifest)).toBe(true) + const authority: MemorySpaceAuthority = { runner: { effectiveDataDir: () => '/unused' }, list: () => [], providerConnection: () => ({}) } + expect(mounted.createAdapter({ memoryBodies: authority, config: { timeoutMs: 200 } }).id).toBe('work') + expect(mounted.registered).toBe(true) + expect(definition.manifest.secrets).toEqual(descriptor.fields.filter(field => field.input === 'secret').map(field => field.key).sort()) + } finally { await mounted.dispose() } + expect(mounted.registered).toBe(false) + }) + + it('constructs its own driver from a structural authority, without the main repository', () => { + const authority: MemorySpaceAuthority = { runner: { effectiveDataDir: () => '/unused' }, list: () => [], providerConnection: () => ({}) } + const adapter = definition.create({ memoryBodies: authority, config: { timeoutMs: 200 }, providerInstanceId: 'holographic', manifest: definition.manifest }) + expect(adapter).toBeInstanceOf(HolographicProvider) + expect(adapter.id).toBe('holographic') + }) +}) diff --git a/dsh/plugins/dsh-mnemon-provider-holographic/tests/top-scored.spec.ts b/dsh/plugins/dsh-mnemon-provider-holographic/tests/top-scored.spec.ts new file mode 100644 index 000000000..be166c6be --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-holographic/tests/top-scored.spec.ts @@ -0,0 +1,35 @@ +import { describe, expect, it } from 'vitest' +import { topScored } from '../src/top-scored.ts' + +describe('bounded score selection', () => { + it('matches stable full sorting across sizes, ties, directions and limits', () => { + for (const n of [0, 1, 10, 64, 65, 100, 1000]) { + for (const scores of [ + Array.from({ length: n }, (_, i) => i), + Array.from({ length: n }, (_, i) => n - i), + Array.from({ length: n }, (_, i) => (i * 37) % 11), + Array.from({ length: n }, () => 0.5), + ]) { + const values = scores.map((score, id) => ({ score, id })) + for (const limit of [0, 1, 2, 10, 20, 50, 99, 1001, 3.5, NaN, Infinity]) { + expect(topScored([...values], limit)).toEqual([...values].sort((a, b) => b.score - a.score).slice(0, limit)) + } + } + } + }) + + it('retains the legacy sort result for non-finite scores', () => { + for (const score of [NaN, Infinity, -Infinity]) for (const position of [0, 37, 99]) { + const values = Array.from({ length: 100 }, (_, i) => ({ id: i, score: i === position ? score : i % 7 })) + expect(topScored([...values], 10)).toEqual([...values].sort((a, b) => b.score - a.score).slice(0, 10)) + } + }) + + it('bounds ranking work when the requested result count is small', () => { + let reads = 0 + const n = 10_000 + const values = Array.from({ length: n }, (_, i) => ({ id: i, get score() { reads += 1; return i } })) + expect(topScored(values, 10).map(row => row.id)).toEqual(Array.from({ length: 10 }, (_, i) => n - i - 1)) + expect(reads).toBeLessThan(n * 20) + }) +}) diff --git a/dsh/plugins/dsh-mnemon-provider-holographic/tsconfig.json b/dsh/plugins/dsh-mnemon-provider-holographic/tsconfig.json new file mode 100644 index 000000000..badeba7ca --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-holographic/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2024", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "verbatimModuleSyntax": true, + "allowImportingTsExtensions": true, + "skipLibCheck": true, + "types": [ + "node" + ] + }, + "include": [ + "src/**/*.ts", + "tests/**/*.ts", + "tsdown.config.ts", + "vitest.config.ts" + ] +} diff --git a/dsh/plugins/dsh-mnemon-provider-holographic/tsdown.config.ts b/dsh/plugins/dsh-mnemon-provider-holographic/tsdown.config.ts new file mode 100644 index 000000000..2d868cbd8 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-holographic/tsdown.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'tsdown' + +export default defineConfig({ + entry: ['src/index.ts', 'src/descriptor.ts'], outDir: 'lib', format: 'esm', platform: 'node', + target: 'es2024', dts: true, clean: true, fixedExtension: false, deps: { neverBundle: true }, +}) diff --git a/dsh/plugins/dsh-mnemon-provider-holographic/vitest.config.ts b/dsh/plugins/dsh-mnemon-provider-holographic/vitest.config.ts new file mode 100644 index 000000000..5a6724c22 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-holographic/vitest.config.ts @@ -0,0 +1,3 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ test: { environment: 'node' } }) diff --git a/dsh/plugins/dsh-mnemon-provider-honcho/LICENSE b/dsh/plugins/dsh-mnemon-provider-honcho/LICENSE new file mode 100644 index 000000000..e32e112dc --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-honcho/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 dsh-external contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dsh/plugins/dsh-mnemon-provider-honcho/README.md b/dsh/plugins/dsh-mnemon-provider-honcho/README.md new file mode 100644 index 000000000..80ee75732 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-honcho/README.md @@ -0,0 +1,27 @@ +# dsh-mnemon-provider-honcho + +An adapter for Honcho workspace conclusions and peer-scoped durable memory. + +Requires a configured Honcho service, workspace and peer identities. It does not synthesize graph capabilities. + +## Use + +The default `dsh-mnemon` Starter already installs this package. Enable and configure the service on the `dsh-mnemon` page under **Plugins**, then inspect its synchronized Memory Spaces. + +For a custom composition, install this package alongside `dsh-mnemon-source-memory-spaces` and include it in that Source's `config.providers`: + +```yaml +providers: + - use: dsh-mnemon-provider-honcho + instanceId: honcho +``` + +This is a **Memory Spaces child module**, not a top-level Source or a complete Strategy. It registers through `dsh-mnemon-source-memory-spaces/provider-sdk`; each parent Source owns its child Fibers, connection settings and lifetime. Credentials stay on the Host. + +[Provider setup and capability matrix](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/guides/memory-providers.md) · [中文指南](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/zh-CN/guides/memory-providers.md) + +## Develop independently + +From a source checkout with the declared dependencies installed, run `pnpm verify`. Tests, build and public exports belong to this package, without importing another package's controllers or repository configuration. Use the Source's public `/testing` fixtures for child registration and driver conformance; live service tests require a separately authorized environment. + +[Plugin author guide](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/development/extensions.md) diff --git a/dsh/plugins/dsh-mnemon-provider-honcho/package.json b/dsh/plugins/dsh-mnemon-provider-honcho/package.json new file mode 100644 index 000000000..3e832f089 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-honcho/package.json @@ -0,0 +1,55 @@ +{ + "name": "dsh-mnemon-provider-honcho", + "version": "0.5.5", + "description": "Honcho Provider child for the Memory Spaces Source.", + "license": "MIT", + "type": "module", + "engines": { + "node": ">=20" + }, + "exports": { + ".": { + "types": "./lib/index.d.ts", + "default": "./lib/index.js" + }, + "./descriptor": { + "types": "./lib/descriptor.d.ts", + "default": "./lib/descriptor.js" + }, + "./package.json": "./package.json" + }, + "files": [ + "lib", + "README.md" + ], + "scripts": { + "typecheck": "tsc --noEmit", + "build": "tsdown", + "test": "vitest run", + "verify": "pnpm typecheck && pnpm test && pnpm build" + }, + "peerDependencies": { + "@deepseek-ai/cordis": "^4.0.1", + "dsh-mnemon-source-memory-spaces": "^0.5.1" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "dsh-mnemon-source-memory-spaces": "^0.5.11", + "tsdown": "^0.22.0", + "typescript": "^5.9.0", + "vitest": "^3.2.0" + }, + "publishConfig": { + "access": "public", + "tag": "latest" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/mnemon-dev/mnemon.git", + "directory": "dsh/plugins/dsh-mnemon-provider-honcho" + }, + "homepage": "https://github.com/mnemon-dev/mnemon/tree/master/dsh/plugins/dsh-mnemon-provider-honcho#readme", + "bugs": { + "url": "https://github.com/mnemon-dev/mnemon/issues" + } +} diff --git a/dsh/plugins/dsh-mnemon-provider-honcho/src/descriptor.ts b/dsh/plugins/dsh-mnemon-provider-honcho/src/descriptor.ts new file mode 100644 index 000000000..fd2142bfd --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-honcho/src/descriptor.ts @@ -0,0 +1,75 @@ +import type { MemoryProviderDescriptor } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { ICON } from './icon.ts' + +/** Browser-safe metadata owned by this Provider. */ +export const descriptor: MemoryProviderDescriptor = { + "id": "honcho", + "label": "Honcho", + "icon": { + "kind": "data-url", + "value": ICON + }, + "kind": "remote", + "workspaceBinding": "provider-global", + "summary": "Cross-session user modelling, peer profiles, dialectic reasoning, and persistent conclusions.", + "summaryI18nKey": "overview.providerSummary.honcho", + "origin": "third-party", + "capabilities": { + "search": true, + "browse": true, + "graph": false, + "entities": false, + "related": false, + "remember": true, + "link": false, + "forget": true, + "writeMode": "exact", + "deletionMode": "hard" + }, + "fields": [ + { + "key": "endpoint", + "label": "Endpoint", + "scope": "service", + "input": "url", + "required": true, + "defaultValue": "https://api.honcho.dev", + "i18nKey": "overview.providerEndpoint" + }, + { + "key": "apiKey", + "label": "API key", + "scope": "service", + "input": "secret", + "required": false, + "i18nKey": "overview.providerApiKey" + }, + { + "key": "workspace", + "label": "Workspace", + "scope": "memory", + "input": "text", + "required": true, + "defaultValue": "dsh", + "i18nKey": "overview.providerField.workspace" + }, + { + "key": "userId", + "label": "User peer", + "scope": "memory", + "input": "text", + "required": true, + "defaultValue": "dsh-user", + "i18nKey": "overview.providerField.userId" + }, + { + "key": "agentId", + "label": "Agent peer", + "scope": "memory", + "input": "text", + "required": true, + "defaultValue": "dsh", + "i18nKey": "overview.providerField.agentId" + } + ] +} diff --git a/dsh/plugins/dsh-mnemon-provider-honcho/src/driver.ts b/dsh/plugins/dsh-mnemon-provider-honcho/src/driver.ts new file mode 100644 index 000000000..4c1c02716 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-honcho/src/driver.ts @@ -0,0 +1,146 @@ +import { descriptor } from './descriptor.ts' +import type { JsonValue } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import type { MemorySpaceAuthority } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import type { Insight, MemoryBody as MemorySpace, MemoryListRequest, RememberRequest, SearchRequest } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { HttpMemoryProvider, firstArray, jsonObject, jsonString, type HttpProviderOptions } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import type { MemoryProviderAdapter, ProviderBodyStatus as ProviderSpaceStatus, ProviderMemorySpace, ProviderSearchResult } from 'dsh-mnemon-source-memory-spaces/provider-sdk' + +function insight(value: unknown): Insight | undefined { + const item = jsonObject(value) + const id = jsonString(item?.id) + const content = jsonString(item?.content) + if (id === undefined || content === undefined) return undefined + const observer = jsonString(item?.observer_id) ?? jsonString(item?.observer) + const observed = jsonString(item?.observed_id) ?? jsonString(item?.observed) + const createdAt = jsonString(item?.created_at) ?? jsonString(item?.createdAt) + const entities = [observer, observed].filter((entry): entry is string => entry !== undefined) + return { + id, + content, + category: jsonString(item?.level) ?? 'insight', + source: 'external', + ...(createdAt === undefined ? {} : { createdAt }), + ...(entities.length === 0 ? {} : { entities }), + } +} + +export class HonchoProvider extends HttpMemoryProvider implements MemoryProviderAdapter { + readonly id = 'honcho' as const + + constructor(memorySpaces: MemorySpaceAuthority, options: HttpProviderOptions = {}) { + super(memorySpaces, { label: descriptor.label, ...options }) + } + + async discover(connection: Record, signal?: AbortSignal): Promise { + const payload = await this.requestConnection(connection, '/v3/workspaces/list?page=1&size=100', { + headers: this.headers(connection), + json: {}, + signal, + }) + return firstArray(payload, 'items', 'results').flatMap(value => { + const item = jsonObject(value) + const id = jsonString(item?.id) + if (id === undefined) return [] + const metadata = jsonObject(item?.metadata) + return [{ + externalId: id, + name: jsonString(metadata?.name) ?? jsonString(metadata?.title) ?? id, + description: jsonString(metadata?.description) ?? `Honcho workspace ${id}`, + connection: { workspace: id, userId: '*', agentId: '*' }, + }] + }) + } + + async status(body: MemorySpace, signal?: AbortSignal): Promise { + try { + await this.list(body, { limit: 1 }, signal) + return { healthy: true } + } catch (error) { + return { healthy: false, error: error instanceof Error ? error.message : String(error) } + } + } + + async search(body: MemorySpace, request: SearchRequest, signal?: AbortSignal): Promise { + const connection = this.connection(body) + const payload = await this.request(body, `${this.basePath(connection)}/conclusions/query`, { + headers: this.headers(connection), + json: { + query: request.query, + top_k: Math.min(request.limit ?? 10, 100), + // Honcho semantic query requires both peers even when discovery mapped + // the workspace-wide body with wildcard scope. Use the same stable + // DSH peer defaults as explicit writes so native recall remains valid. + filters: this.scope(connection, true), + }, + signal, + }) + return { results: firstArray(payload, 'items', 'results').map(insight).filter((item): item is Insight => item !== undefined) } + } + + async list(body: MemorySpace, request: MemoryListRequest, signal?: AbortSignal): Promise { + const connection = this.connection(body) + const limit = Math.min(Math.max(request.limit ?? 200, 1), 100) + const payload = await this.request(body, `${this.basePath(connection)}/conclusions/list?page=1&size=${limit}`, { + headers: this.headers(connection), + json: { + filters: { + ...this.scope(connection), + ...(request.category === undefined ? {} : { level: request.category }), + }, + }, + signal, + }) + return firstArray(payload, 'items', 'results').map(insight).filter((item): item is Insight => item !== undefined) + } + + async remember(body: MemorySpace, request: RememberRequest, signal?: AbortSignal): Promise { + const connection = this.connection(body) + const payload = await this.request(body, `${this.basePath(connection)}/conclusions`, { + headers: this.headers(connection), + json: { + conclusions: [{ + content: request.content, + observer_id: String(connection.agentId) === '*' ? 'dsh' : String(connection.agentId), + observed_id: String(connection.userId) === '*' ? 'dsh-user' : String(connection.userId), + session_id: null, + }], + }, + signal, + }) + const created = firstArray(payload, 'items', 'results').map(jsonObject).find(item => item !== undefined) + return { + action: 'stored', + provider: this.id, + summary: 'Honcho stored an explicit peer conclusion.', + ...(jsonString(created?.id) === undefined ? {} : { id: jsonString(created?.id)! }), + } + } + + async forget(body: MemorySpace, id: string, signal?: AbortSignal): Promise { + const connection = this.connection(body) + await this.request(body, `${this.basePath(connection)}/conclusions/${encodeURIComponent(id)}`, { + method: 'DELETE', + headers: this.headers(connection), + signal, + }) + return { action: 'deleted', provider: this.id, id } + } + + private basePath(connection: Record): string { + return `/v3/workspaces/${encodeURIComponent(String(connection.workspace))}` + } + + private scope(connection: Record, requirePeers = false): Record { + const agentId = String(connection.agentId) + const userId = String(connection.userId) + return { + ...(agentId === '*' ? requirePeers ? { observer_id: 'dsh' } : {} : { observer_id: agentId }), + ...(userId === '*' ? requirePeers ? { observed_id: 'dsh-user' } : {} : { observed_id: userId }), + } + } + + private headers(connection: Record): HeadersInit { + const token = String(connection.apiKey ?? '').replace(/^Bearer\s+/iu, '') + return token === '' ? {} : { Authorization: `Bearer ${token}` } + } +} diff --git a/dsh/plugins/dsh-mnemon-provider-honcho/src/icon.ts b/dsh/plugins/dsh-mnemon-provider-honcho/src/icon.ts new file mode 100644 index 000000000..35923538c --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-honcho/src/icon.ts @@ -0,0 +1,2 @@ +/** Browser-safe brand asset shipped by this Provider, not by the Host. */ +export const ICON = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjUwIiBoZWlnaHQ9IjY1MCIgdmlld0JveD0iMCAwIDY1MCA2NTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSI2NTAiIGhlaWdodD0iNjUwIiBmaWxsPSIjMTAxNDQ3Ii8+CjxnIGZpbHRlcj0idXJsKCNmaWx0ZXIwX2RkXzExXzgpIj4KPHBhdGggZD0iTTMyNS41NiA4MEMyMzguNTkgODAgMTU3LjU0IDEyNi41OSAxMTMuNjIgMjAxLjY5Qzk0LjE4MDEgMjEzLjUxIDgxLjAwMDEgMjIxLjc4IDgwLjczMDEgMjIxLjkyQzQyLjgyMDEgMjQyLjU4IDIzLjkwMDEgMjYyLjI5IDE0LjYyMDEgMjc3LjlDNy40MTAxMSAyNzguMDMgMS4xNzAxIDI4My4zNyAwLjE1MDEwMSAyOTAuNzNDLTEuMDA5OSAyOTguODggNC42ODAxIDMwNi40NCAxMi44NDAxIDMwNy41OEMyMS4wMTAxIDMwOC43NCAyOC41NTAxIDMwMy4wNSAyOS43MTAxIDI5NC44OEMzMC4zMTAxIDI5MC42OCAyOS4wNzAxIDI4Ni42MyAyNi42MzAxIDI4My41NUM0MC4zNjAxIDI2MS40IDY5LjMyMDEgMjQzLjE4IDg3LjA2MDEgMjMzLjUyQzkwLjExMDEgMjMxLjg2IDI2MS44OSAxMjcuODEgMjc0Ljc0IDEzMC4zOEMyNzYuODkgMTMwLjgxIDI4MC4wMyAxMzEuOTcgMjgyLjUgMTM3LjA4QzI4OS4zIDE1MS4xOCAyMjYuNzggMTkyLjk1IDIxNC4wMyAyMDAuNTlDMjAxLjgyIDIwNy45IDE5MS4wNiAyMTUuNzEgMTgxLjc1IDIyNC4wMUMxNjIuMDIgMjQxLjU5IDE0OC44NSAyNjEuMzIgMTQyLjQ1IDI4Mi45N0MxMzguNiAyOTYuMDIgMTI3LjYzIDMzMy42IDEwMS4wMiAzNDIuOTRDOTkuMTMwMSAzMzguNzQgOTUuMzMwMSAzMzUuNDYgOTAuNDcwMSAzMzQuNDZDODIuNDAwMSAzMzIuNzcgNzQuNDkwMSAzMzcuOTUgNzIuODIwMSAzNDYuMDJDNzEuMjIwMSAzNTMuNjYgNzUuNzgwMSAzNjEuMTQgODMuMTIwMSAzNjMuMzRDMTAxLjQ5IDQ4Mi40NyAyMDMuNTEgNTcwLjc3IDMyNS41NiA1NzAuNzdDNDYwLjg2IDU3MC43NyA1NzAuOTUgNDYwLjY5IDU3MC45NSAzMjUuMzhDNTcwLjk1IDE5MC4wNyA0NjAuODcgODAgMzI1LjU2IDgwWk0xODcuNyAyMzYuNTFDMTg3LjEyIDI0NC4xNSAxODUuMzcgMjUyLjkgMTgxLjA3IDI1OC42MUMxNzYuNTQgMjY0LjYxIDE2Ny45NSAyNjkuNzEgMTYwLjIzIDI3My4zMUMxNjYuMzMgMjYwLjIxIDE3NS41MSAyNDcuOTIgMTg3LjcgMjM2LjUxWk0zMjUuNTYgNTU3LjU0QzIxMC4wMiA1NTcuNTQgMTEzLjQ2IDQ3My45MSA5Ni4xNjAxIDM2MS4xMkM5Ny45MjAxIDM1OS44NCA5OS40MjAxIDM1OC4yIDEwMC40OCAzNTYuMjVDMTIyLjUzIDM0OC4zOCAxMzkuMTkgMzM3LjQ5IDE1NC4wOCAyOTAuMDhDMTYyLjA3IDI4Ny4yOCAxODIuMDEgMjc5LjM1IDE5MS42NCAyNjYuNTZDMjAxLjMyIDI1My42OSAyMDEuNDYgMjMzLjYxIDIwMS4wNSAyMjUuMjhDMjA3LjEgMjIwLjY3IDIxMy43IDIxNi4yMiAyMjAuODMgMjExLjk1QzI2MS41OSAxODcuNTUgMzA1LjU1IDE1NC4zNSAyOTQuNDEgMTMxLjMyQzI5MC43NCAxMjMuNzMgMjg0LjgzIDExOC45MiAyNzcuMzQgMTE3LjQzQzI2My4yMyAxMTQuNjMgMTkyLjcyIDE1NC40NCAxNDAuMTUgMTg1LjczQzE4My43IDEyOC4wOCAyNTIuNDEgOTMuMjQgMzI1LjU2IDkzLjI0QzQ1My41NyA5My4yNCA1NTcuNzEgMTk3LjM5IDU1Ny43MSAzMjUuMzlDNTU3LjcxIDQ1My4zOSA0NTMuNTcgNTU3LjU1IDMyNS41NiA1NTcuNTVWNTU3LjU0WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTI0Ny4zNCAzMjUuMzhDMjYxLjI5NiAzMjUuMzggMjcyLjYxIDMwNy44NDMgMjcyLjYxIDI4Ni4yMUMyNzIuNjEgMjY0LjU3NyAyNjEuMjk2IDI0Ny4wNCAyNDcuMzQgMjQ3LjA0QzIzMy4zODQgMjQ3LjA0IDIyMi4wNyAyNjQuNTc3IDIyMi4wNyAyODYuMjFDMjIyLjA3IDMwNy44NDMgMjMzLjM4NCAzMjUuMzggMjQ3LjM0IDMyNS4zOFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik00MDMuNzggMzI1LjM4QzQxNy43MzYgMzI1LjM4IDQyOS4wNSAzMDcuODQzIDQyOS4wNSAyODYuMjFDNDI5LjA1IDI2NC41NzcgNDE3LjczNiAyNDcuMDQgNDAzLjc4IDI0Ny4wNEMzODkuODI0IDI0Ny4wNCAzNzguNTEgMjY0LjU3NyAzNzguNTEgMjg2LjIxQzM3OC41MSAzMDcuODQzIDM4OS44MjQgMzI1LjM4IDQwMy43OCAzMjUuMzhaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMzg2LjE4IDQwNi45MUgyNjQuOTRDMjU4LjQ4IDQwNi45MSAyNTMuMjUgNDEyLjE0IDI1My4yNSA0MTguNkMyNTMuMjUgNDI1LjA2IDI1OC40OCA0MzAuMjkgMjY0Ljk0IDQzMC4yOUgzODYuMThDMzkyLjY0IDQzMC4yOSAzOTcuODcgNDI1LjA2IDM5Ny44NyA0MTguNkMzOTcuODcgNDEyLjE0IDM5Mi42NCA0MDYuOTEgMzg2LjE4IDQwNi45MVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik05My45NDAxIDMzMy42MUw5My45NzAxIDMzMy41OEw5My45NDAxIDMzMy42VjMzMy42MlYzMzMuNjFaIiBmaWxsPSJ3aGl0ZSIvPgo8L2c+CjxkZWZzPgo8ZmlsdGVyIGlkPSJmaWx0ZXIwX2RkXzExXzgiIHg9IjAiIHk9IjgwIiB3aWR0aD0iNTk0Ljk1IiBoZWlnaHQ9IjUxNC43NyIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiPgo8ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPgo8ZmVDb2xvck1hdHJpeCBpbj0iU291cmNlQWxwaGEiIHR5cGU9Im1hdHJpeCIgdmFsdWVzPSIwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAxMjcgMCIgcmVzdWx0PSJoYXJkQWxwaGEiLz4KPGZlT2Zmc2V0IGR4PSIyMCIgZHk9IjIwIi8+CjxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjIiLz4KPGZlQ29tcG9zaXRlIGluMj0iaGFyZEFscGhhIiBvcGVyYXRvcj0ib3V0Ii8+CjxmZUNvbG9yTWF0cml4IHR5cGU9Im1hdHJpeCIgdmFsdWVzPSIwIDAgMCAwIDEgMCAwIDAgMCAwLjM1Mjk0MSAwIDAgMCAwIDAuNDk0MTE4IDAgMCAwIDEgMCIvPgo8ZmVCbGVuZCBtb2RlPSJub3JtYWwiIGluMj0iQmFja2dyb3VuZEltYWdlRml4IiByZXN1bHQ9ImVmZmVjdDFfZHJvcFNoYWRvd18xMV84Ii8+CjxmZUNvbG9yTWF0cml4IGluPSJTb3VyY2VBbHBoYSIgdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDEyNyAwIiByZXN1bHQ9ImhhcmRBbHBoYSIvPgo8ZmVPZmZzZXQgZHg9IjEwIiBkeT0iMTAiLz4KPGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iMiIvPgo8ZmVDb21wb3NpdGUgaW4yPSJoYXJkQWxwaGEiIG9wZXJhdG9yPSJvdXQiLz4KPGZlQ29sb3JNYXRyaXggdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAgMCAwIDAgMC4wMzUyOTQxIDAgMCAwIDAgMC45OTYwNzggMCAwIDAgMCAwLjk3MjU0OSAwIDAgMCAxIDAiLz4KPGZlQmxlbmQgbW9kZT0ibm9ybWFsIiBpbjI9ImVmZmVjdDFfZHJvcFNoYWRvd18xMV84IiByZXN1bHQ9ImVmZmVjdDJfZHJvcFNoYWRvd18xMV84Ii8+CjxmZUJsZW5kIG1vZGU9Im5vcm1hbCIgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0Ml9kcm9wU2hhZG93XzExXzgiIHJlc3VsdD0ic2hhcGUiLz4KPC9maWx0ZXI+CjwvZGVmcz4KPC9zdmc+Cg==" diff --git a/dsh/plugins/dsh-mnemon-provider-honcho/src/index.ts b/dsh/plugins/dsh-mnemon-provider-honcho/src/index.ts new file mode 100644 index 000000000..ecf67c02f --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-honcho/src/index.ts @@ -0,0 +1,24 @@ +import { MEMORY_SPACE_PROVIDER_API_VERSION, defineMemorySpaceProvider, defineMemorySpaceProviderDefinition } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { HonchoProvider } from './driver.ts' +import { descriptor } from './descriptor.ts' + +export { HonchoProvider, descriptor } + +export const definition = defineMemorySpaceProviderDefinition({ + manifest: { + apiVersion: MEMORY_SPACE_PROVIDER_API_VERSION, kind: 'provider', + typeId: descriptor.id, packageName: 'dsh-mnemon-provider-honcho', version: '0.5.5', + label: descriptor.label, icon: descriptor.icon, summary: descriptor.summary, + ...(descriptor.summaryI18nKey === undefined ? {} : { summaryI18nKey: descriptor.summaryI18nKey }), + origin: descriptor.origin, locality: descriptor.kind, workspaceBinding: descriptor.workspaceBinding, + capabilities: descriptor.capabilities, fields: descriptor.fields, + secrets: descriptor.fields.filter(field => field.input === 'secret').map(field => field.key), + scoreSemantics: 'provider-native', + }, + create: context => new HonchoProvider(context.memorySpaces ?? context.memoryBodies, { requestTimeoutMs: context.config.timeoutMs }), +}) + +export default defineMemorySpaceProvider({ + id: descriptor.id, + apply(ctx, host) { host.install(ctx, definition) }, +}) diff --git a/dsh/plugins/dsh-mnemon-provider-honcho/tests/driver.spec.ts b/dsh/plugins/dsh-mnemon-provider-honcho/tests/driver.spec.ts new file mode 100644 index 000000000..a0c259bc4 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-honcho/tests/driver.spec.ts @@ -0,0 +1,92 @@ +import { mkdtempSync, rmSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { afterEach, describe, expect, it, vi } from 'vitest' +import { createMemorySpaceProviderFixture } from 'dsh-mnemon-source-memory-spaces/testing' +import type { MemoryProviderConnection, MemoryProviderId } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { HonchoProvider, descriptor } from '../src/index.ts' + +const temporaryDirectories: string[] = [] +afterEach(() => { for (const directory of temporaryDirectories.splice(0)) rmSync(directory, { recursive: true, force: true }) }) + +async function providerBody(providerId: MemoryProviderId, connection: MemoryProviderConnection) { + if (providerId !== descriptor.id) throw new Error('Wrong Provider fixture') + const dataDir = mkdtempSync(join(tmpdir(), 'mnemon-honcho-driver-')) + temporaryDirectories.push(dataDir) + const { authority: registry, body } = createMemorySpaceProviderFixture(descriptor, connection, { dataDir, instanceId: 'work-account' }) + return { registry, body } +} + +function response(payload: unknown, status = 200): Response { + return new Response(payload === undefined ? null : JSON.stringify(payload), { status, headers: { 'Content-Type': 'application/json' } }) +} + +describe('standalone honcho data plane', () => { + it('discovers its native namespaces independently', async () => { + const fetchMock = vi.fn(async () => response({ items: [{ id: 'workspace-1', metadata: { title: 'Product workspace', description: 'Honcho source metadata.' } }] })) + const honcho = await providerBody('honcho', { endpoint: 'https://honcho.example', workspace: 'old', userId: 'user', agentId: 'agent' }) + await expect(new HonchoProvider(honcho.registry, { fetch: fetchMock }).discover({ endpoint: 'https://honcho.example' })).resolves.toEqual([ + expect.objectContaining({ externalId: 'workspace-1', name: 'Product workspace', description: 'Honcho source metadata.', connection: { workspace: 'workspace-1', userId: '*', agentId: '*' } }), + ]) + }) + + it('does not start generic Provider HTTP requests after cancellation', async () => { + const fetchMock = vi.fn() + const controller = new AbortController() + controller.abort(new Error('navigation cancelled')) + const honcho = await providerBody('honcho', { endpoint: 'https://honcho.example', workspace: 'old', userId: 'user', agentId: 'agent' }) + + await expect(new HonchoProvider(honcho.registry, { fetch: fetchMock }).discover({ endpoint: 'https://honcho.example' }, controller.signal)).rejects.toThrow('navigation cancelled') + expect(fetchMock).not.toHaveBeenCalled() + }) + + it('uses Honcho v3 conclusion scope for recall, explicit writes, and deletion', async () => { + const requests: Array<{ url: string; init?: RequestInit }> = [] + const fetchMock = vi.fn(async (url, init) => { + requests.push({ url: String(url), ...(init === undefined ? {} : { init }) }) + const path = new URL(String(url)).pathname + if (path.endsWith('/conclusions/query')) return response([{ id: 'hon-1', content: 'Alice prefers short answers.', level: 'peer', observer_id: 'dsh', observed_id: 'alice' }]) + if (path.endsWith('/conclusions/list')) return response({ items: [{ id: 'hon-1', content: 'Alice prefers short answers.', created_at: '2026-08-16T00:00:00Z' }] }) + if (path.endsWith('/conclusions') && init?.method === 'POST') return response({ conclusions: [{ id: 'hon-2', content: 'Alice is testing memory.' }] }) + if (path.endsWith('/conclusions/hon-1') && init?.method === 'DELETE') return response(undefined, 204) + throw new Error(`unexpected ${init?.method ?? 'GET'} ${path}`) + }) + const { registry, body } = await providerBody('honcho', { + endpoint: 'https://api.honcho.dev', apiKey: 'honcho-secret', workspace: 'product team', userId: 'alice', agentId: 'dsh', + }) + const provider = new HonchoProvider(registry, { fetch: fetchMock }) + + await expect(provider.search(body, { query: 'answer style', limit: 7 })).resolves.toEqual({ + results: [expect.objectContaining({ id: 'hon-1', content: 'Alice prefers short answers.', entities: ['dsh', 'alice'] })], + }) + await expect(provider.list(body, { limit: 20 })).resolves.toEqual([expect.objectContaining({ id: 'hon-1' })]) + await expect(provider.remember(body, { content: 'Alice is testing memory.' })).resolves.toMatchObject({ action: 'stored', provider: 'honcho' }) + await expect(provider.forget(body, 'hon-1')).resolves.toMatchObject({ action: 'deleted', id: 'hon-1' }) + + expect(new URL(requests[0]!.url).pathname).toBe('/v3/workspaces/product%20team/conclusions/query') + expect(new Headers(requests[0]?.init?.headers).get('Authorization')).toBe('Bearer honcho-secret') + expect(JSON.parse(String(requests[0]?.init?.body))).toEqual({ + query: 'answer style', top_k: 7, filters: { observer_id: 'dsh', observed_id: 'alice' }, + }) + expect(JSON.parse(String(requests[2]?.init?.body))).toEqual({ + conclusions: [{ content: 'Alice is testing memory.', observer_id: 'dsh', observed_id: 'alice', session_id: null }], + }) + }) + + it('supplies stable Honcho peers when a discovered workspace uses wildcard scope', async () => { + const requests: Array<{ url: string; init?: RequestInit }> = [] + const fetchMock = vi.fn(async (url, init) => { + requests.push({ url: String(url), ...(init === undefined ? {} : { init }) }) + return response([]) + }) + const { registry, body } = await providerBody('honcho', { + endpoint: 'https://api.honcho.dev', workspace: 'dsh-lab', userId: '*', agentId: '*', + }) + + await new HonchoProvider(registry, { fetch: fetchMock }).search(body, { query: 'provider routing', limit: 5 }) + + expect(JSON.parse(String(requests[0]?.init?.body))).toEqual({ + query: 'provider routing', top_k: 5, filters: { observer_id: 'dsh', observed_id: 'dsh-user' }, + }) + }) +}) diff --git a/dsh/plugins/dsh-mnemon-provider-honcho/tests/provider.spec.ts b/dsh/plugins/dsh-mnemon-provider-honcho/tests/provider.spec.ts new file mode 100644 index 000000000..d8695af24 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-honcho/tests/provider.spec.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from 'vitest' +import type { MemorySpaceAuthority } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { mountMemorySpaceProvider } from 'dsh-mnemon-source-memory-spaces/testing' +import module, { definition, HonchoProvider, descriptor } from '../src/index.ts' + +describe('independent honcho Provider', () => { + it('installs complete immutable metadata in one parent-owned child', async () => { + const mounted = await mountMemorySpaceProvider(module, { instanceId: 'work', config: undefined }) + try { + expect(mounted.descriptor).toMatchObject({ id: 'work', typeId: 'honcho', label: descriptor.label }) + expect(Object.isFrozen(mounted.manifest)).toBe(true) + const authority: MemorySpaceAuthority = { runner: { effectiveDataDir: () => '/unused' }, list: () => [], providerConnection: () => ({}) } + expect(mounted.createAdapter({ memoryBodies: authority, config: { timeoutMs: 200 } }).id).toBe('work') + expect(mounted.registered).toBe(true) + expect(definition.manifest.secrets).toEqual(descriptor.fields.filter(field => field.input === 'secret').map(field => field.key).sort()) + } finally { await mounted.dispose() } + expect(mounted.registered).toBe(false) + }) + + it('constructs its own driver from a structural authority, without the main repository', () => { + const authority: MemorySpaceAuthority = { runner: { effectiveDataDir: () => '/unused' }, list: () => [], providerConnection: () => ({}) } + const adapter = definition.create({ memoryBodies: authority, config: { timeoutMs: 200 }, providerInstanceId: 'honcho', manifest: definition.manifest }) + expect(adapter).toBeInstanceOf(HonchoProvider) + expect(adapter.id).toBe('honcho') + }) +}) diff --git a/dsh/plugins/dsh-mnemon-provider-honcho/tsconfig.json b/dsh/plugins/dsh-mnemon-provider-honcho/tsconfig.json new file mode 100644 index 000000000..badeba7ca --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-honcho/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2024", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "verbatimModuleSyntax": true, + "allowImportingTsExtensions": true, + "skipLibCheck": true, + "types": [ + "node" + ] + }, + "include": [ + "src/**/*.ts", + "tests/**/*.ts", + "tsdown.config.ts", + "vitest.config.ts" + ] +} diff --git a/dsh/plugins/dsh-mnemon-provider-honcho/tsdown.config.ts b/dsh/plugins/dsh-mnemon-provider-honcho/tsdown.config.ts new file mode 100644 index 000000000..2d868cbd8 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-honcho/tsdown.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'tsdown' + +export default defineConfig({ + entry: ['src/index.ts', 'src/descriptor.ts'], outDir: 'lib', format: 'esm', platform: 'node', + target: 'es2024', dts: true, clean: true, fixedExtension: false, deps: { neverBundle: true }, +}) diff --git a/dsh/plugins/dsh-mnemon-provider-honcho/vitest.config.ts b/dsh/plugins/dsh-mnemon-provider-honcho/vitest.config.ts new file mode 100644 index 000000000..5a6724c22 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-honcho/vitest.config.ts @@ -0,0 +1,3 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ test: { environment: 'node' } }) diff --git a/dsh/plugins/dsh-mnemon-provider-mem0/LICENSE b/dsh/plugins/dsh-mnemon-provider-mem0/LICENSE new file mode 100644 index 000000000..e32e112dc --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-mem0/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 dsh-external contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dsh/plugins/dsh-mnemon-provider-mem0/README.md b/dsh/plugins/dsh-mnemon-provider-mem0/README.md new file mode 100644 index 000000000..7bbdb7ef1 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-mem0/README.md @@ -0,0 +1,27 @@ +# dsh-mnemon-provider-mem0 + +An adapter for Mem0 Platform or a self-hosted HTTP API. + +Configure the deployment mode and user/Agent scope. Extraction may complete asynchronously; Provider receipts retain that distinction. + +## Use + +The default `dsh-mnemon` Starter already installs this package. Enable and configure the service on the `dsh-mnemon` page under **Plugins**, then inspect its synchronized Memory Spaces. + +For a custom composition, install this package alongside `dsh-mnemon-source-memory-spaces` and include it in that Source's `config.providers`: + +```yaml +providers: + - use: dsh-mnemon-provider-mem0 + instanceId: mem0 +``` + +This is a **Memory Spaces child module**, not a top-level Source or a complete Strategy. It registers through `dsh-mnemon-source-memory-spaces/provider-sdk`; each parent Source owns its child Fibers, connection settings and lifetime. Credentials stay on the Host. + +[Provider setup and capability matrix](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/guides/memory-providers.md) · [中文指南](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/zh-CN/guides/memory-providers.md) + +## Develop independently + +From a source checkout with the declared dependencies installed, run `pnpm verify`. Tests, build and public exports belong to this package, without importing another package's controllers or repository configuration. Use the Source's public `/testing` fixtures for child registration and driver conformance; live service tests require a separately authorized environment. + +[Plugin author guide](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/development/extensions.md) diff --git a/dsh/plugins/dsh-mnemon-provider-mem0/package.json b/dsh/plugins/dsh-mnemon-provider-mem0/package.json new file mode 100644 index 000000000..a24c3bdb8 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-mem0/package.json @@ -0,0 +1,55 @@ +{ + "name": "dsh-mnemon-provider-mem0", + "version": "0.5.5", + "description": "Mem0 Provider child for the Memory Spaces Source.", + "license": "MIT", + "type": "module", + "engines": { + "node": ">=20" + }, + "exports": { + ".": { + "types": "./lib/index.d.ts", + "default": "./lib/index.js" + }, + "./descriptor": { + "types": "./lib/descriptor.d.ts", + "default": "./lib/descriptor.js" + }, + "./package.json": "./package.json" + }, + "files": [ + "lib", + "README.md" + ], + "scripts": { + "typecheck": "tsc --noEmit", + "build": "tsdown", + "test": "vitest run", + "verify": "pnpm typecheck && pnpm test && pnpm build" + }, + "peerDependencies": { + "@deepseek-ai/cordis": "^4.0.1", + "dsh-mnemon-source-memory-spaces": "^0.5.1" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "dsh-mnemon-source-memory-spaces": "^0.5.11", + "tsdown": "^0.22.0", + "typescript": "^5.9.0", + "vitest": "^3.2.0" + }, + "publishConfig": { + "access": "public", + "tag": "latest" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/mnemon-dev/mnemon.git", + "directory": "dsh/plugins/dsh-mnemon-provider-mem0" + }, + "homepage": "https://github.com/mnemon-dev/mnemon/tree/master/dsh/plugins/dsh-mnemon-provider-mem0#readme", + "bugs": { + "url": "https://github.com/mnemon-dev/mnemon/issues" + } +} diff --git a/dsh/plugins/dsh-mnemon-provider-mem0/src/descriptor.ts b/dsh/plugins/dsh-mnemon-provider-mem0/src/descriptor.ts new file mode 100644 index 000000000..13a9a86b9 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-mem0/src/descriptor.ts @@ -0,0 +1,96 @@ +import type { MemoryProviderDescriptor } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { ICON } from './icon.ts' + +/** Browser-safe metadata owned by this Provider. */ +export const descriptor: MemoryProviderDescriptor = { + "id": "mem0", + "label": "Mem0", + "icon": { + "kind": "data-url", + "value": ICON + }, + "kind": "remote", + "workspaceBinding": "provider-global", + "summary": "Automatic fact extraction, semantic retrieval, reranking, and deduplication.", + "summaryI18nKey": "overview.providerSummary.mem0", + "origin": "third-party", + "capabilities": { + "search": true, + "browse": true, + "graph": false, + "entities": false, + "related": false, + "remember": true, + "link": false, + "forget": true, + "writeMode": "async-extracting", + "deletionMode": "hard" + }, + "fields": [ + { + "key": "endpoint", + "label": "Endpoint", + "scope": "service", + "input": "url", + "required": true, + "defaultValue": "https://api.mem0.ai", + "i18nKey": "overview.providerEndpoint" + }, + { + "key": "apiKey", + "label": "API key", + "scope": "service", + "input": "secret", + "required": false, + "i18nKey": "overview.providerApiKey" + }, + { + "key": "mode", + "label": "Mode", + "scope": "service", + "input": "select", + "required": true, + "defaultValue": "platform", + "options": [ + { + "value": "platform", + "label": "Mem0 Platform", + "i18nKey": "overview.providerOption.platform" + }, + { + "value": "self-hosted", + "label": "Self-hosted server", + "i18nKey": "overview.providerOption.self-hosted" + } + ], + "i18nKey": "overview.providerField.mode" + }, + { + "key": "userId", + "label": "User ID", + "scope": "memory", + "input": "text", + "required": true, + "defaultValue": "dsh-user", + "i18nKey": "overview.providerField.userId" + }, + { + "key": "agentId", + "label": "Agent ID", + "scope": "memory", + "input": "text", + "required": true, + "defaultValue": "dsh", + "i18nKey": "overview.providerField.agentId" + }, + { + "key": "rerank", + "label": "Rerank search results", + "scope": "memory", + "input": "boolean", + "required": false, + "defaultValue": false, + "i18nKey": "overview.providerField.rerank" + } + ] +} diff --git a/dsh/plugins/dsh-mnemon-provider-mem0/src/driver.ts b/dsh/plugins/dsh-mnemon-provider-mem0/src/driver.ts new file mode 100644 index 000000000..730030b39 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-mem0/src/driver.ts @@ -0,0 +1,167 @@ +import { descriptor } from './descriptor.ts' +import type { JsonValue } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import type { MemorySpaceAuthority } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import type { Insight, MemoryBody as MemorySpace, MemoryListRequest, RememberRequest, SearchRequest } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { + HttpMemoryProvider, + firstArray, + jsonArray, + jsonNumber, + jsonObject, + jsonString, + type HttpProviderOptions, +} from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { NORMALIZED_RELEVANCE_SCORE, type MemoryProviderAdapter, type ProviderBodyStatus as ProviderSpaceStatus, type ProviderMemorySpace, type ProviderSearchResult } from 'dsh-mnemon-source-memory-spaces/provider-sdk' + +function category(item: Record): string { + const categories = jsonArray(item.categories).filter((value): value is string => typeof value === 'string') + return jsonString(item.category) ?? categories[0] ?? 'general' +} + +function insight(value: unknown): Insight | undefined { + const item = jsonObject(value) + const id = jsonString(item?.id) + const content = jsonString(item?.memory) ?? jsonString(item?.text) ?? jsonString(item?.content) + if (id === undefined || content === undefined) return undefined + const score = jsonNumber(item?.score) + const createdAt = jsonString(item?.created_at) ?? jsonString(item?.createdAt) ?? jsonString(item?.updated_at) + const tags = jsonArray(item?.categories).filter((entry): entry is string => typeof entry === 'string') + return { + id, + content, + category: category(item!), + source: 'external', + ...(score === undefined ? {} : { score }), + ...(createdAt === undefined ? {} : { createdAt }), + ...(tags.length === 0 ? {} : { tags }), + } +} + +export class Mem0Provider extends HttpMemoryProvider implements MemoryProviderAdapter { + readonly id = 'mem0' as const + readonly scoreSemantics = NORMALIZED_RELEVANCE_SCORE + + constructor(memorySpaces: MemorySpaceAuthority, options: HttpProviderOptions = {}) { + super(memorySpaces, { label: descriptor.label, ...options }) + } + + async discover(connection: Record, signal?: AbortSignal): Promise { + const mode = String(connection.mode ?? 'platform') + const payload = await this.requestConnection(connection, mode === 'self-hosted' ? '/entities' : '/v1/entities', { + headers: this.headers(connection, mode), + signal, + }) + return firstArray(payload, 'entities', 'results').flatMap(value => { + const item = jsonObject(value) + const id = jsonString(item?.id) + const type = jsonString(item?.type) + if (id === undefined || (type !== 'user' && type !== 'agent')) return [] + const metadata = jsonObject(item?.metadata) + const count = jsonNumber(item?.total_memories) + return [{ + externalId: `${type}:${id}`, + name: jsonString(item?.name) ?? jsonString(metadata?.name) ?? id, + description: jsonString(metadata?.description) ?? `${type === 'user' ? 'User' : 'Agent'} memory${count === undefined ? '' : ` · ${count} memories`}`, + connection: type === 'user' + ? { userId: id, agentId: '*', rerank: false } + : { userId: '*', agentId: id, rerank: false }, + }] + }) + } + + async status(body: MemorySpace, signal?: AbortSignal): Promise { + try { + await this.list(body, { limit: 1 }, signal) + return { healthy: true } + } catch (error) { + return { healthy: false, error: error instanceof Error ? error.message : String(error) } + } + } + + async search(body: MemorySpace, request: SearchRequest, signal?: AbortSignal): Promise { + const connection = this.connection(body) + const mode = String(connection.mode ?? 'platform') + const filters = this.filters(connection) + const payload = await this.request(body, mode === 'self-hosted' ? '/search' : '/v3/memories/search/', { + headers: this.headers(connection, mode), + json: { + query: request.query, + filters, + top_k: request.limit ?? 10, + ...(mode === 'platform' && connection.rerank === true ? { rerank: true } : {}), + }, + signal, + }) + return { results: firstArray(payload, 'results', 'memories').map(insight).filter((item): item is Insight => item !== undefined) } + } + + async list(body: MemorySpace, request: MemoryListRequest, signal?: AbortSignal): Promise { + const connection = this.connection(body) + const mode = String(connection.mode ?? 'platform') + const limit = Math.min(Math.max(request.limit ?? 200, 1), 200) + const payload = mode === 'self-hosted' + ? await this.request(body, `/memories?${new URLSearchParams({ ...this.filters(connection) as Record, limit: String(limit) })}`, { + headers: this.headers(connection, mode), + signal, + }) + : await this.request(body, `/v3/memories/?page=1&page_size=${limit}`, { + headers: this.headers(connection, mode), + json: { filters: this.filters(connection), ...(request.category === undefined ? {} : { categories: [request.category] }) }, + signal, + }) + return firstArray(payload, 'results', 'memories').map(insight).filter((item): item is Insight => item !== undefined) + } + + async remember(body: MemorySpace, request: RememberRequest, signal?: AbortSignal): Promise { + const connection = this.connection(body) + const mode = String(connection.mode ?? 'platform') + const payload = await this.request(body, mode === 'self-hosted' ? '/memories' : '/v3/memories/add/', { + headers: this.headers(connection, mode), + json: { + messages: [{ role: 'user', content: request.content }], + user_id: String(connection.userId) === '*' ? 'dsh-user' : String(connection.userId), + agent_id: String(connection.agentId) === '*' ? 'dsh' : String(connection.agentId), + ...(mode === 'self-hosted' ? { infer: false } : {}), + metadata: { + source: 'dsh-mnemon', + ...(request.category === undefined ? {} : { category: request.category }), + ...(request.importance === undefined ? {} : { importance: request.importance }), + ...(request.tags === undefined ? {} : { tags: request.tags }), + ...(request.entities === undefined ? {} : { entities: request.entities }), + }, + }, + signal, + }) + const result = jsonObject(payload) ?? {} + return { + action: mode === 'platform' ? 'queued' : 'stored', + provider: this.id, + summary: mode === 'platform' ? 'Mem0 queued the memory for extraction.' : 'Mem0 stored the explicit memory.', + ...(jsonString(result.event_id) === undefined ? {} : { eventId: jsonString(result.event_id)! }), + ...(jsonString(result.status) === undefined ? {} : { status: jsonString(result.status)! }), + } + } + + async forget(body: MemorySpace, id: string, signal?: AbortSignal): Promise { + const connection = this.connection(body) + const mode = String(connection.mode ?? 'platform') + const path = mode === 'self-hosted' ? `/memories/${encodeURIComponent(id)}` : `/v1/memories/${encodeURIComponent(id)}` + await this.request(body, path, { method: 'DELETE', headers: this.headers(connection, mode), signal }) + return { action: 'deleted', provider: this.id, id } + } + + private filters(connection: Record): Record { + const userId = String(connection.userId) + const agentId = String(connection.agentId ?? '') + return { + ...(userId === '*' ? {} : { user_id: userId }), + ...(agentId === '' || agentId === '*' ? {} : { agent_id: agentId }), + } + } + + private headers(connection: Record, mode: string): HeadersInit { + const apiKey = String(connection.apiKey ?? '').replace(/^(?:Token|Bearer)\s+/iu, '') + if (apiKey === '') return {} + return mode === 'self-hosted' ? { 'X-API-Key': apiKey } : { Authorization: `Token ${apiKey}` } + } +} diff --git a/dsh/plugins/dsh-mnemon-provider-mem0/src/icon.ts b/dsh/plugins/dsh-mnemon-provider-mem0/src/icon.ts new file mode 100644 index 000000000..87cad40f0 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-mem0/src/icon.ts @@ -0,0 +1,2 @@ +/** Browser-safe brand asset shipped by this Provider, not by the Host. */ +export const ICON = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgZmlsbD0iI0NCQjJGRiIgcng9IjEyIi8+PGcgZmlsbD0iIzA5MDkwQiIgY2xpcC1wYXRoPSJ1cmwoI2EpIj48cGF0aCBkPSJNNTAuMDA0IDQxLjU0N2E4LjQ0NiA4LjQ0NiAwIDAgMC04LjQ1IDguNDQ5IDguNDQ2IDguNDQ2IDAgMCAwIDguNDUgOC40NDkgOC40NDcgOC40NDcgMCAwIDAgOC40NDktOC40NSA4LjQ0NiA4LjQ0NiAwIDAgMC04LjQ1LTguNDQ4bS4wNDItMTYuNTMxYTIuNTEzIDIuNTEzIDAgMCAwIDIuNDYzLTIuNTUzQTIuNDg2IDIuNDg2IDAgMCAwIDQ5Ljk1NSAyMGEyLjQ4NiAyLjQ4NiAwIDAgMC0yLjQ2MiAyLjU1MyAyLjQ4NiAyLjQ4NiAwIDAgMCAyLjU1MiAyLjQ2M20tLjA4OSA0OS45NjNhMi40ODYgMi40ODYgMCAwIDAtMi40NjMgMi41NTIgMi40ODYgMi40ODYgMCAwIDAgMi41NTIgMi40NjMgMi41MTMgMi41MTMgMCAwIDAgMi40NjMtMi41NTIgMi40ODYgMi40ODYgMCAwIDAtMi41NTMtMi40NjNNMzIuMzcyIDMyLjNhMi41MTMgMi41MTMgMCAwIDAtLjA2NS0zLjU0NyAyLjQ4NyAyLjQ4NyAwIDAgMC0zLjU0OC4wNjUgMi40ODcgMi40ODcgMCAwIDAgLjA2NSAzLjU0OCAyLjQ4NyAyLjQ4NyAwIDAgMCAzLjU0OC0uMDY1bTM1LjI2MiAzNS4zOTRhMi40ODcgMi40ODcgMCAwIDAgLjA2NSAzLjU0OCAyLjQ4NyAyLjQ4NyAwIDAgMCAzLjU0OC0uMDY1IDIuNTEzIDIuNTEzIDAgMCAwLS4wNjUtMy41NDggMi40ODcgMi40ODcgMCAwIDAtMy41NDguMDY1bS4wNjUtMzUuMzI5Yy45OC45NDQgMi41NzIuOTQ2IDMuNTQ4LS4wNjVhMi40ODcgMi40ODcgMCAwIDAtLjA2NS0zLjU0OCAyLjQ4NyAyLjQ4NyAwIDAgMC0zLjU0OC4wNjUgMi40ODcgMi40ODcgMCAwIDAgLjA2NSAzLjU0OE0zMi4zMDcgNjcuNjNhMi40ODcgMi40ODcgMCAwIDAtMy41NDguMDY1IDIuNDg3IDIuNDg3IDAgMCAwIC4wNjUgMy41NDhjLjk4Ljk0NCAyLjU3Mi45NDYgMy41NDgtLjA2NWEyLjQ4NyAyLjQ4NyAwIDAgMC0uMDY1LTMuNTQ4bTQ1LjA0Ni0yMC4xMzZhMi40ODcgMi40ODcgMCAwIDAtMi4zNjQgMi42NDcgMi41MTQgMi41MTQgMCAwIDAgMi42NDYgMi4zNjQgMi40ODcgMi40ODcgMCAwIDAgMi4zNjQtMi42NDYgMi40ODQgMi40ODQgMCAwIDAtMi42NDYtMi4zNjJ6TTIyLjY1IDUyLjUwMWEyLjQ4NyAyLjQ4NyAwIDAgMCAyLjM2NS0yLjY0NiAyLjQ4NSAyLjQ4NSAwIDAgMC0yLjY0Ni0yLjM2MiAyLjQ4NyAyLjQ4NyAwIDAgMC0yLjM2NSAyLjY0NiAyLjUxNSAyLjUxNSAwIDAgMCAyLjY0NiAyLjM2NHptMjMuNDYzLTE2LjkxNWE0LjM3IDQuMzcgMCAwIDAgNi4wNTMgMS4xNTJjMS4xMjUtLjc2NSAxLjgxOS0yLjAwNSAxLjkwNC0zLjQwMy4wNjItLjkwOC4yNjEtMi4xOTYgMS4yNjgtMi44OSAxLjAzLS42NTUgMi4yOTMtLjMzNSAzLjE1NC0uMDQgMS4zMi40NjcgMi43MzIuMzEzIDMuODc1LS40MjVhNC4zMiA0LjMyIDAgMCAwIDEuODk0LTIuNzQzIDQuMzIgNC4zMiAwIDAgMC0uNTk3LTMuMjhsLS4xNzQtLjI2YTQuMzcgNC4zNyAwIDAgMC02LjA1My0xLjE1MmMtMS4xMjYuNzY0LTEuODIgMi4wMDMtMS45MDQgMy40MDItLjA2My45MDgtLjI2MiAyLjE5Ni0xLjI2OSAyLjg5LTEuMDI5LjY1NS0yLjI5My4zMzUtMy4xNTQuMDQtMS4zMTctLjQ2Ny0yLjczMS0uMzEzLTMuODc0LjQyNWE0LjM2IDQuMzYgMCAwIDAtMS4zIDYuMDIybC4xNzUuMjYyem0tOS42MDIgNDAuNzA5YTQuMzcgNC4zNyAwIDAgMCA2LjA1MyAxLjE1MmMxLjEyNi0uNzY1IDEuODItMi4wMDUgMS45MDQtMy40MDMuMDYzLS45MDguMjYyLTIuMTk3IDEuMjY5LTIuODkgMS4wMjktLjY1NSAyLjI5Mi0uMzM1IDMuMTU0LS4wNCAxLjMyLjQ2NyAyLjczMS4zMTMgMy44NzQtLjQyNWE0LjMyIDQuMzIgMCAwIDAgMS44OTUtMi43NDMgNC4zMiA0LjMyIDAgMCAwLS41OTctMy4yOGwtLjE3NS0uMjZhNC4zNyA0LjM3IDAgMCAwLTYuMDUzLTEuMTUyYy0xLjEyNS43NjQtMS44MTkgMi4wMDMtMS45MDQgMy40MDEtLjA2Mi45MDktLjI2MSAyLjE5Ny0xLjI2OCAyLjg5LTEuMDMuNjU2LTIuMjkzLjMzNi0zLjE1NC4wNDEtMS4zMTgtLjQ2OC0yLjczMi0uMzEzLTMuODc1LjQyNWE0LjM2IDQuMzYgMCAwIDAtMS4zIDYuMDIybC4xNzUuMjYyek01Ny40MiAzNy4wNDhxLS4wOC40MzQtLjA3Ni44NzJhNC4zNyA0LjM3IDAgMCAwIDMuNTQ4IDQuMjE3YzEuMzM4LjI1MiAyLjcwNS0uMTM3IDMuNzUtMS4wNjcuNjg2LS42IDEuNzM1LTEuMzcyIDIuOTM5LTEuMTUgMS4xOS4yNjEgMS44NiAxLjM4IDIuMjYxIDIuMTk5LjYwNCAxLjI2MSAxLjcxNCAyLjE1IDMuMDQ1IDIuNDM0YTQuMzMgNC4zMyAwIDAgMCAzLjI4LS42MDQgNC4zMiA0LjMyIDAgMCAwIDEuODkyLTIuNzQ1bC4wNjItLjMwOXEuMDgyLS40MzUuMDc2LS44NzJhNC4zNyA0LjM3IDAgMCAwLTMuNTQ4LTQuMjE3Yy0xLjMzNy0uMjUzLTIuNzAyLjEzNy0zLjc0OSAxLjA2Ny0uNjg3LjYtMS43MzggMS4zNzEtMi45NCAxLjE1LTEuMTktLjI2Mi0xLjg2LTEuMzgtMi4yNi0yLjItLjYwNS0xLjI2LTEuNzE0LTIuMTQ5LTMuMDQzLTIuNDMzYTQuMzYgNC4zNiAwIDAgMC01LjE3MiAzLjM0OWwtLjA2Mi4zMDl6bS0zNS41MzkgMjIuMDVxLS4wOC40MzUtLjA3Ni44NzNhNC4zNyA0LjM3IDAgMCAwIDMuNTQ4IDQuMjE2YzEuMzM4LjI1MyAyLjcwNC0uMTM2IDMuNzUtMS4wNjcuNjg2LS42IDEuNzM1LTEuMzcgMi45MzgtMS4xNSAxLjE5LjI2MiAxLjg2MiAxLjM4IDIuMjYyIDIuMi42MDQgMS4yNjEgMS43MTQgMi4xNSAzLjA0NSAyLjQzM0E0LjMzIDQuMzMgMCAwIDAgNDAuNjI3IDY2YTQuMzIgNC4zMiAwIDAgMCAxLjg5Mi0yLjc0NGwuMDYzLS4zMXEuMDgxLS40MzMuMDc2LS44NzFhNC4zNyA0LjM3IDAgMCAwLTMuNTQ4LTQuMjE3Yy0xLjMzNy0uMjUzLTIuNzAyLjEzNi0zLjc0OSAxLjA2Ny0uNjg2LjYtMS43MzggMS4zNzEtMi45NCAxLjE1LTEuMTktLjI2Mi0xLjg2LTEuMzgtMi4yNi0yLjItLjYwNS0xLjI2MS0xLjcxNC0yLjE0OS0zLjA0My0yLjQzM2E0LjM2IDQuMzYgMCAwIDAtNS4xNzIgMy4zNDlsLS4wNjIuMzA4em00Mi41MjUtMTMuMDFhNC41IDQuNSAwIDAgMC0uNjcxLjU2MyA0LjM3IDQuMzcgMCAwIDAtLjQ3MiA1LjQ5Yy43NjcgMS4xMjUgMi4wMDkgMS44MTYgMy40MDQgMS44OTcuOTA5LjA2IDIuMTk3LjI2IDIuODkzIDEuMjY0LjY1NyAxLjAyNy4zNCAyLjI5LjA0NSAzLjE1NC0uNDY2IDEuMzItLjMxIDIuNzMxLjQzMSAzLjg3NGE0LjMzIDQuMzMgMCAwIDAgMi43NDUgMS44OSA0LjMzIDQuMzMgMCAwIDAgMy4yOC0uNjAxbC4yNjEtLjE3NXEuMzY1LS4yNS42NzEtLjU2M2E0LjM3IDQuMzcgMCAwIDAgLjQ3Mi01LjQ5Yy0uNzY3LTEuMTIzLTIuMDA2LTEuODE2LTMuNDA0LTEuODk3LS45MDktLjA2LTIuMi0uMjYtMi44OTMtMS4yNjQtLjY1Ny0xLjAyNi0uMzQtMi4yOS0uMDQ0LTMuMTU0LjQ2NS0xLjMyLjMwOC0yLjczMS0uNDMyLTMuODcyYTQuMzYgNC4zNiAwIDAgMC02LjAyNC0xLjI4OGwtLjI2Mi4xNzR6TTIzLjY4NyAzNi41NWE0LjUgNC41IDAgMCAwLS42NzEuNTY0IDQuMzcgNC4zNyAwIDAgMC0uNDcyIDUuNDljLjc2NyAxLjEyNSAyLjAwOSAxLjgxNiAzLjQwNSAxLjg5Ny45MDguMDYgMi4xOTYuMjYgMi44OTIgMS4yNjQuNjU4IDEuMDI2LjM0IDIuMjkuMDQ1IDMuMTU0LS40NjUgMS4zMi0uMzA5IDIuNzMxLjQzMiAzLjg3NGE0LjMyIDQuMzIgMCAwIDAgMi43NDQgMS44OSA0LjMzIDQuMzMgMCAwIDAgMy4yOC0uNjAxbC4yNjEtLjE3NXEuMzY1LS4yNS42NzItLjU2M2E0LjM3IDQuMzcgMCAwIDAgLjQ3Mi01LjQ5Yy0uNzY4LTEuMTIzLTIuMDA3LTEuODE2LTMuNDA1LTEuODk3LS45MDgtLjA2LTIuMi0uMjYtMi44OTItMS4yNjQtLjY1OC0xLjAyNy0uMzQtMi4yOS0uMDQ1LTMuMTU0LjQ2NS0xLjMyLjMwOC0yLjczMS0uNDMyLTMuODcyYTQuMzYgNC4zNiAwIDAgMC02LjAyNC0xLjI4OWwtLjI2Mi4xNzV6bTM5LjI2OCAyMC44NjhhNC40IDQuNCAwIDAgMC0uODczLS4wNzYgNC4zNyA0LjM3IDAgMCAwLTQuMjE3IDMuNTQ4Yy0uMjUyIDEuMzM4LjEzNyAyLjcwNSAxLjA2NyAzLjc1LjYuNjg2IDEuMzcyIDEuNzM1IDEuMTUgMi45MzktLjI2MSAxLjE5LTEuMzggMS44Ni0yLjE5OSAyLjI2MS0xLjI2MS42MDQtMi4xNSAxLjcxNC0yLjQzMyAzLjA0NWE0LjMzIDQuMzMgMCAwIDAgLjYwMyAzLjI4IDQuMzIgNC4zMiAwIDAgMCAyLjc0NSAxLjg5MmwuMzA5LjA2MnEuNDM1LjA4Mi44NzIuMDc2YTQuMzcgNC4zNyAwIDAgMCA0LjIxNy0zLjU0N2MuMjUzLTEuMzM4LS4xMzYtMi43MDMtMS4wNjctMy43NS0uNi0uNjg2LTEuMzcxLTEuNzM4LTEuMTUtMi45NC4yNjItMS4xOSAxLjM4LTEuODYgMi4yLTIuMjYgMS4yNi0uNjA1IDIuMTQ5LTEuNzE0IDIuNDMzLTMuMDQzYTQuMzYgNC4zNiAwIDAgMC0zLjM0OS01LjE3MmwtLjMwOC0uMDYyek00MC45IDIxLjg4YTQuNCA0LjQgMCAwIDAtLjg3Mi0uMDc3IDQuMzcgNC4zNyAwIDAgMC00LjIxNyAzLjU0OGMtLjI1MyAxLjMzOC4xMzYgMi43MDQgMS4wNjcgMy43NS42LjY4NiAxLjM3MSAxLjczNSAxLjE1IDIuOTM5LS4yNjIgMS4xOS0xLjM4IDEuODYtMi4yIDIuMjYxLTEuMjYuNjA0LTIuMTQ5IDEuNzE0LTIuNDMzIDMuMDQ1YTQuMzMgNC4zMyAwIDAgMCAuNjA0IDMuMjggNC4zMiA0LjMyIDAgMCAwIDIuNzQ1IDEuODkybC4zMDguMDYycS40MzUuMDgxLjg3My4wNzZhNC4zNyA0LjM3IDAgMCAwIDQuMjE2LTMuNTQ4Yy4yNTMtMS4zMzctLjEzNi0yLjcwMi0xLjA2Ny0zLjc0OS0uNi0uNjg3LTEuMzctMS43MzgtMS4xNS0yLjk0LjI2Mi0xLjE5IDEuMzgtMS44NiAyLjItMi4yNiAxLjI2MS0uNjA1IDIuMTUtMS43MTQgMi40MzMtMy4wNDNhNC4zNiA0LjM2IDAgMCAwLTMuMzQ4LTUuMTcybC0uMzEtLjA2MnoiLz48L2c+PGRlZnM+PGNsaXBQYXRoIGlkPSJhIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMjAgMjBoNjB2NTkuOTk4SDIweiIvPjwvY2xpcFBhdGg+PC9kZWZzPjwvc3ZnPgo=" diff --git a/dsh/plugins/dsh-mnemon-provider-mem0/src/index.ts b/dsh/plugins/dsh-mnemon-provider-mem0/src/index.ts new file mode 100644 index 000000000..493c58318 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-mem0/src/index.ts @@ -0,0 +1,24 @@ +import { MEMORY_SPACE_PROVIDER_API_VERSION, defineMemorySpaceProvider, defineMemorySpaceProviderDefinition } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { Mem0Provider } from './driver.ts' +import { descriptor } from './descriptor.ts' + +export { Mem0Provider, descriptor } + +export const definition = defineMemorySpaceProviderDefinition({ + manifest: { + apiVersion: MEMORY_SPACE_PROVIDER_API_VERSION, kind: 'provider', + typeId: descriptor.id, packageName: 'dsh-mnemon-provider-mem0', version: '0.5.5', + label: descriptor.label, icon: descriptor.icon, summary: descriptor.summary, + ...(descriptor.summaryI18nKey === undefined ? {} : { summaryI18nKey: descriptor.summaryI18nKey }), + origin: descriptor.origin, locality: descriptor.kind, workspaceBinding: descriptor.workspaceBinding, + capabilities: descriptor.capabilities, fields: descriptor.fields, + secrets: descriptor.fields.filter(field => field.input === 'secret').map(field => field.key), + scoreSemantics: 'normalized-relevance', + }, + create: context => new Mem0Provider(context.memorySpaces ?? context.memoryBodies, { requestTimeoutMs: context.config.timeoutMs }), +}) + +export default defineMemorySpaceProvider({ + id: descriptor.id, + apply(ctx, host) { host.install(ctx, definition) }, +}) diff --git a/dsh/plugins/dsh-mnemon-provider-mem0/tests/driver.spec.ts b/dsh/plugins/dsh-mnemon-provider-mem0/tests/driver.spec.ts new file mode 100644 index 000000000..70f81415f --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-mem0/tests/driver.spec.ts @@ -0,0 +1,66 @@ +import { mkdtempSync, rmSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { afterEach, describe, expect, it, vi } from 'vitest' +import { createMemorySpaceProviderFixture } from 'dsh-mnemon-source-memory-spaces/testing' +import type { MemoryProviderConnection, MemoryProviderId } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { Mem0Provider, descriptor } from '../src/index.ts' + +const temporaryDirectories: string[] = [] +afterEach(() => { for (const directory of temporaryDirectories.splice(0)) rmSync(directory, { recursive: true, force: true }) }) + +async function providerBody(providerId: MemoryProviderId, connection: MemoryProviderConnection) { + if (providerId !== descriptor.id) throw new Error('Wrong Provider fixture') + const dataDir = mkdtempSync(join(tmpdir(), 'mnemon-mem0-driver-')) + temporaryDirectories.push(dataDir) + const { authority: registry, body } = createMemorySpaceProviderFixture(descriptor, connection, { dataDir, instanceId: 'work-account' }) + return { registry, body } +} + +function response(payload: unknown, status = 200): Response { + return new Response(payload === undefined ? null : JSON.stringify(payload), { status, headers: { 'Content-Type': 'application/json' } }) +} + +describe('standalone mem0 data plane', () => { + it.each(['platform', 'self-hosted'])('distinguishes asynchronous ingestion from explicit storage in %s mode without an upstream status', async mode => { + const fetchMock = vi.fn(async () => response({ event_id: 'event-or-record-id' })) + const { registry, body } = await providerBody('mem0', { endpoint: 'https://mem0.example', apiKey: 'fixture', mode, userId: 'user', agentId: 'agent' }) + const result = await new Mem0Provider(registry, { fetch: fetchMock }).remember(body, { content: 'Synthetic memory.' }) + expect(result).toMatchObject({ action: mode === 'platform' ? 'queued' : 'stored' }) + if (mode === 'self-hosted') expect(JSON.parse(String(fetchMock.mock.calls[0]![1]?.body))).toMatchObject({ infer: false }) + }) + + it('discovers its native namespaces independently', async () => { + const fetchMock = vi.fn(async () => response([{ id: 'alice', name: 'Alice', type: 'user', total_memories: 4 }])) + const mem0 = await providerBody('mem0', { endpoint: 'https://mem0.example', mode: 'self-hosted', userId: 'user', agentId: 'agent' }) + await expect(new Mem0Provider(mem0.registry, { fetch: fetchMock }).discover({ endpoint: 'https://mem0.example', mode: 'self-hosted' })).resolves.toEqual([ + expect.objectContaining({ externalId: 'user:alice', name: 'Alice', connection: { userId: 'alice', agentId: '*', rerank: false } }), + ]) + }) + + it('uses Mem0 Platform v3 scoping and keeps the token out of result projections', async () => { + const requests: Array<{ url: string; init?: RequestInit }> = [] + const fetchMock = vi.fn(async (url, init) => { + requests.push({ url: String(url), ...(init === undefined ? {} : { init }) }) + const path = new URL(String(url)).pathname + if (path === '/v3/memories/search/') return response({ results: [{ id: 'mem-1', memory: 'Alice prefers concise replies.', score: 0.91, categories: ['preference'] }] }) + if (path === '/v3/memories/add/') return response({ status: 'PENDING', event_id: 'event-1' }) + if (path === '/v1/memories/mem-1') return response({ message: 'deleted' }) + throw new Error(`unexpected path ${path}`) + }) + const { registry, body } = await providerBody('mem0', { + endpoint: 'https://api.mem0.ai', apiKey: 'mem0-secret', mode: 'platform', userId: 'alice', agentId: 'dsh', rerank: true, + }) + const provider = new Mem0Provider(registry, { fetch: fetchMock }) + + await expect(provider.search(body, { query: 'reply style', limit: 5 })).resolves.toEqual({ + results: [expect.objectContaining({ id: 'mem-1', content: 'Alice prefers concise replies.', category: 'preference', score: 0.91 })], + }) + await expect(provider.remember(body, { content: 'Alice likes TypeScript.', category: 'preference' })).resolves.toMatchObject({ action: 'queued', eventId: 'event-1', status: 'PENDING' }) + await expect(provider.forget(body, 'mem-1')).resolves.toMatchObject({ action: 'deleted', id: 'mem-1' }) + + expect(new Headers(requests[0]?.init?.headers).get('Authorization')).toBe('Token mem0-secret') + expect(JSON.parse(String(requests[0]?.init?.body))).toMatchObject({ filters: { user_id: 'alice', agent_id: 'dsh' }, rerank: true }) + expect(JSON.stringify((await provider.search(body, { query: 'reply style' })).results)).not.toContain('mem0-secret') + }) +}) diff --git a/dsh/plugins/dsh-mnemon-provider-mem0/tests/provider.spec.ts b/dsh/plugins/dsh-mnemon-provider-mem0/tests/provider.spec.ts new file mode 100644 index 000000000..79c0b4df1 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-mem0/tests/provider.spec.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from 'vitest' +import type { MemorySpaceAuthority } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { mountMemorySpaceProvider } from 'dsh-mnemon-source-memory-spaces/testing' +import module, { definition, Mem0Provider, descriptor } from '../src/index.ts' + +describe('independent mem0 Provider', () => { + it('installs complete immutable metadata in one parent-owned child', async () => { + const mounted = await mountMemorySpaceProvider(module, { instanceId: 'work', config: undefined }) + try { + expect(mounted.descriptor).toMatchObject({ id: 'work', typeId: 'mem0', label: descriptor.label }) + expect(Object.isFrozen(mounted.manifest)).toBe(true) + const authority: MemorySpaceAuthority = { runner: { effectiveDataDir: () => '/unused' }, list: () => [], providerConnection: () => ({}) } + expect(mounted.createAdapter({ memoryBodies: authority, config: { timeoutMs: 200 } }).id).toBe('work') + expect(mounted.registered).toBe(true) + expect(definition.manifest.secrets).toEqual(descriptor.fields.filter(field => field.input === 'secret').map(field => field.key).sort()) + } finally { await mounted.dispose() } + expect(mounted.registered).toBe(false) + }) + + it('constructs its own driver from a structural authority, without the main repository', () => { + const authority: MemorySpaceAuthority = { runner: { effectiveDataDir: () => '/unused' }, list: () => [], providerConnection: () => ({}) } + const adapter = definition.create({ memoryBodies: authority, config: { timeoutMs: 200 }, providerInstanceId: 'mem0', manifest: definition.manifest }) + expect(adapter).toBeInstanceOf(Mem0Provider) + expect(adapter.id).toBe('mem0') + }) +}) diff --git a/dsh/plugins/dsh-mnemon-provider-mem0/tsconfig.json b/dsh/plugins/dsh-mnemon-provider-mem0/tsconfig.json new file mode 100644 index 000000000..badeba7ca --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-mem0/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2024", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "verbatimModuleSyntax": true, + "allowImportingTsExtensions": true, + "skipLibCheck": true, + "types": [ + "node" + ] + }, + "include": [ + "src/**/*.ts", + "tests/**/*.ts", + "tsdown.config.ts", + "vitest.config.ts" + ] +} diff --git a/dsh/plugins/dsh-mnemon-provider-mem0/tsdown.config.ts b/dsh/plugins/dsh-mnemon-provider-mem0/tsdown.config.ts new file mode 100644 index 000000000..2d868cbd8 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-mem0/tsdown.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'tsdown' + +export default defineConfig({ + entry: ['src/index.ts', 'src/descriptor.ts'], outDir: 'lib', format: 'esm', platform: 'node', + target: 'es2024', dts: true, clean: true, fixedExtension: false, deps: { neverBundle: true }, +}) diff --git a/dsh/plugins/dsh-mnemon-provider-mem0/vitest.config.ts b/dsh/plugins/dsh-mnemon-provider-mem0/vitest.config.ts new file mode 100644 index 000000000..5a6724c22 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-mem0/vitest.config.ts @@ -0,0 +1,3 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ test: { environment: 'node' } }) diff --git a/dsh/plugins/dsh-mnemon-provider-mnemon-native/LICENSE b/dsh/plugins/dsh-mnemon-provider-mnemon-native/LICENSE new file mode 100644 index 000000000..e32e112dc --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-mnemon-native/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 dsh-external contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dsh/plugins/dsh-mnemon-provider-mnemon-native/README.md b/dsh/plugins/dsh-mnemon-provider-mnemon-native/README.md new file mode 100644 index 000000000..ff08d21be --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-mnemon-native/README.md @@ -0,0 +1,29 @@ +# dsh-mnemon-provider-mnemon-native + +The default local Mnemon CLI backend: exact writes, typed relationships, recall and soft deletion. + +Install the `mnemon` CLI separately and ensure the DSH Host can resolve it. The npm package contains the driver, not the binary. + +## Use + +The default `dsh-mnemon` Starter already installs this package. Native is the default backend while its CLI is installed; without the CLI, Memory Spaces use another ready Provider. + +For a custom composition, install this package alongside `dsh-mnemon-source-memory-spaces` and include it in that Source's `config.providers`: + +```yaml +providers: + - use: dsh-mnemon-provider-mnemon-native + instanceId: mnemon-native +``` + +This is a **Memory Spaces child module**, not a top-level Source or a complete Strategy. It registers through `dsh-mnemon-source-memory-spaces/provider-sdk`; each parent Source owns its child Fibers, connection settings and lifetime. Credentials stay on the Host. + +Runtime bulk archival reuses only exact content found in a readonly namespace snapshot and groups identical entries within the pending batch. It imports the remaining originals with `mnemon import --no-diff`, so semantic similarity cannot skip a distinct fact or replace an existing one. Import counts, source indexes and exact content must match before receipts are returned. Ordinary `remember` continues to use the CLI's normal deduplication policy. No storage format or Source/Strategy contract changes are required. + +[Provider setup and capability matrix](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/guides/memory-providers.md) · [中文指南](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/zh-CN/guides/memory-providers.md) + +## Develop independently + +From a source checkout with the declared dependencies installed, run `pnpm verify`. Tests, build and public exports belong to this package, without importing another package's controllers or repository configuration. Use the Source's public `/testing` fixtures for child registration and driver conformance; live service tests require a separately authorized environment. + +[Plugin author guide](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/development/extensions.md) diff --git a/dsh/plugins/dsh-mnemon-provider-mnemon-native/package.json b/dsh/plugins/dsh-mnemon-provider-mnemon-native/package.json new file mode 100644 index 000000000..74c4d127e --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-mnemon-native/package.json @@ -0,0 +1,55 @@ +{ + "name": "dsh-mnemon-provider-mnemon-native", + "version": "0.5.8", + "description": "Native Mnemon CLI Provider child for Memory Spaces.", + "license": "MIT", + "type": "module", + "engines": { + "node": ">=20" + }, + "exports": { + ".": { + "types": "./lib/index.d.ts", + "default": "./lib/index.js" + }, + "./descriptor": { + "types": "./lib/descriptor.d.ts", + "default": "./lib/descriptor.js" + }, + "./package.json": "./package.json" + }, + "files": [ + "lib", + "README.md" + ], + "scripts": { + "typecheck": "tsc --noEmit", + "build": "tsdown", + "test": "vitest run", + "verify": "pnpm typecheck && pnpm test && pnpm build" + }, + "peerDependencies": { + "@deepseek-ai/cordis": "^4.0.1", + "dsh-mnemon-source-memory-spaces": "^0.5.1" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "dsh-mnemon-source-memory-spaces": "^0.5.14", + "tsdown": "^0.22.0", + "typescript": "^5.9.0", + "vitest": "^3.2.0" + }, + "publishConfig": { + "access": "public", + "tag": "latest" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/mnemon-dev/mnemon.git", + "directory": "dsh/plugins/dsh-mnemon-provider-mnemon-native" + }, + "homepage": "https://github.com/mnemon-dev/mnemon/tree/master/dsh/plugins/dsh-mnemon-provider-mnemon-native#readme", + "bugs": { + "url": "https://github.com/mnemon-dev/mnemon/issues" + } +} diff --git a/dsh/plugins/dsh-mnemon-provider-mnemon-native/src/descriptor.ts b/dsh/plugins/dsh-mnemon-provider-mnemon-native/src/descriptor.ts new file mode 100644 index 000000000..27ffc409e --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-mnemon-native/src/descriptor.ts @@ -0,0 +1,28 @@ +import type { MemoryProviderDescriptor } from 'dsh-mnemon-source-memory-spaces/provider-sdk' + +export const descriptor: MemoryProviderDescriptor = { + "id": "mnemon-native", + "label": "Mnemon Native", + "icon": { + "kind": "brand", + "value": "mnemon" + }, + "kind": "local", + "workspaceBinding": "automatic", + "summary": "Official local-first memory with exact writes, typed graph relations, and soft deletion.", + "summaryI18nKey": "overview.providerSummary.mnemon-native", + "origin": "native", + "capabilities": { + "search": true, + "browse": true, + "graph": true, + "entities": true, + "related": true, + "remember": true, + "link": true, + "forget": true, + "writeMode": "exact", + "deletionMode": "soft" + }, + "fields": [] +} diff --git a/dsh/plugins/dsh-mnemon-provider-mnemon-native/src/driver.ts b/dsh/plugins/dsh-mnemon-provider-mnemon-native/src/driver.ts new file mode 100644 index 000000000..0a5497376 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-mnemon-native/src/driver.ts @@ -0,0 +1,322 @@ +import { mkdtempSync, rmSync, writeFileSync } from 'node:fs' +import { join } from 'node:path' +import { tmpdir } from 'node:os' +import { NORMALIZED_RELEVANCE_SCORE, type MemorySpaceNativeRunner, type MemoryProviderAdapter, type JsonValue, type Insight, type MemoryBody as MemorySpace, type MemoryBodyStats as MemorySpaceStats, type MemoryGraphEdge, type MemoryGraphNode, type MemoryGraphSnapshot, type MemoryListRequest, type EdgeType, type RememberRequest, type SearchRequest, type ProviderBodyStatus as ProviderSpaceStatus, type ProviderSearchResult } from 'dsh-mnemon-source-memory-spaces/provider-sdk' + +/** + * Output cap for the two whole-Store reads: the full readonly recall and the + * HTML graph. Their size grows with the Store, not with a misbehaving command, + * and a Store of about 1,000 insights already produces 3–9 MB (issue #320). + * Every other command keeps the Source's 2 MiB default. + */ +export const STORE_DUMP_MAX_OUTPUT_BYTES = 128 * 1024 * 1024 + +function record(value: JsonValue | undefined): Record | undefined { + return typeof value === 'object' && value !== null && !Array.isArray(value) + ? value as Record + : undefined +} + +function text(value: JsonValue | undefined): string | undefined { + return typeof value === 'string' ? value : undefined +} + +function number(value: JsonValue | undefined): number | undefined { + return typeof value === 'number' && Number.isFinite(value) ? value : undefined +} + +function stringArray(value: JsonValue | undefined): string[] | undefined { + if (!Array.isArray(value)) return undefined + return value.filter((entry): entry is string => typeof entry === 'string') +} + +function normalizeInsight(value: JsonValue): Insight | undefined { + const item = record(value) + if (item === undefined) return undefined + // Mnemon <=0.1.2 returns full recall rows as + // `{ insight: { id, content, ... }, score, intent, via, signals }`; newer + // builds default to a compact flat row. Accept both without version gates. + const nested = record(item.insight) + const core = nested ?? item + const id = text(core.id) + const content = text(core.content) + if (id === undefined || content === undefined) return undefined + const insight: Insight = { id, content } + const optionalText = { + category: text(core.category), + source: text(core.source), + confidence: text(item.confidence), + intent: text(item.intent), + matchedVia: text(item.matched_via ?? item.via ?? item.via_edge_type), + createdAt: text(core.created_at), + edgeType: text(item.via_edge_type), + } + for (const [key, value] of Object.entries(optionalText)) if (value !== undefined) Object.assign(insight, { [key]: value }) + const optionalNumbers = { + importance: number(core.importance), + score: number(item.score), + depth: number(item.depth), + } + for (const [key, value] of Object.entries(optionalNumbers)) if (value !== undefined) Object.assign(insight, { [key]: value }) + const tags = stringArray(core.tags) + const entities = stringArray(core.entities) + if (tags !== undefined) insight.tags = tags + if (entities !== undefined) insight.entities = entities + return insight +} + +const JS_STRING = '"(?:\\\\.|[^"\\\\])*"' +const VIZ_NODE_PATTERN = new RegExp(`\\{id:(${JS_STRING}),label:(${JS_STRING}),title:(${JS_STRING}),color:(${JS_STRING}),font:\\{color:"white"\\}\\}`, 'g') +const VIZ_EDGE_PATTERN = new RegExp(`\\{from:(${JS_STRING}),to:(${JS_STRING}),label:(${JS_STRING}),color:\\{color:(${JS_STRING})\\},arrows:"to"`, 'g') +const EDGE_COLORS: Record = { + '#aaaaaa': 'temporal', + '#3498db': 'semantic', + '#e74c3c': 'causal', + '#2ecc71': 'entity', +} + +function decodeJsString(value: string): string { + const decoded = JSON.parse(value) as unknown + if (typeof decoded !== 'string') throw new Error('Mnemon viz contained an invalid string') + return decoded +} + +/** Parse the official Mnemon vis.js export without executing its HTML or loading its CDN script. */ +export function parseMemoryGraph(html: string, now = new Date()): MemoryGraphSnapshot { + const nodes: MemoryGraphNode[] = [] + const edges: MemoryGraphEdge[] = [] + for (const match of html.matchAll(VIZ_NODE_PATTERN)) { + const id = decodeJsString(match[1]!) + const label = decodeJsString(match[2]!) + const content = decodeJsString(match[3]!).replaceAll('\\n', '\n') + const color = decodeJsString(match[4]!) + const category = /\[([a-z_]+)\]/i.exec(label)?.[1] ?? 'general' + nodes.push({ id, content, category, color }) + } + for (const match of html.matchAll(VIZ_EDGE_PATTERN)) { + const color = decodeJsString(match[4]!) + const type = EDGE_COLORS[color.toLowerCase()] + edges.push({ + sourceId: decodeJsString(match[1]!), + targetId: decodeJsString(match[2]!), + label: decodeJsString(match[3]!), + color, + ...(type === undefined ? {} : { type }), + }) + } + if (!html.includes('var nodes = new vis.DataSet([')) throw new Error('Mnemon viz returned an unexpected HTML payload') + return { nodes, edges, generatedAt: now.toISOString() } +} + +function commaList(values: string[] | undefined, label: string, limit: number): string | undefined { + if (values === undefined) return undefined + const normalized = values.map(value => value.trim()).filter(value => value !== '') + if (normalized.length > limit) throw new Error(`${label} accepts at most ${limit} values`) + if (normalized.some(value => value.includes(','))) throw new Error(`${label} values cannot contain commas`) + return normalized.length === 0 ? undefined : normalized.join(',') +} + + +/** Native CLI data plane owned and independently testable by this Provider. */ +export class MnemonNativeProvider implements MemoryProviderAdapter { + readonly id = 'mnemon-native' + readonly scoreSemantics = NORMALIZED_RELEVANCE_SCORE + constructor(private readonly runner: MemorySpaceNativeRunner, private readonly config: { defaultRecallLimit: number } = { defaultRecallLimit: 10 }) {} + + list(body: MemorySpace, _request: MemoryListRequest, signal?: AbortSignal): Promise { + return this.allNativeInsights(body, signal, true) + } + + async status(body: MemorySpace, signal?: AbortSignal): Promise { + try { + const raw = await this.runner.runJson(['status'], { ...(signal === undefined ? {} : { signal }), store: body.id }) + const status = record(raw) + if (status === undefined) throw new Error('mnemon status returned an unexpected payload') + return { healthy: true, stats: this.parseStats(status) } + } catch (error) { + return { healthy: false, error: error instanceof Error ? error.message : String(error) } + } + } + + private parseStats(status: Record): MemorySpaceStats { + const byCategoryRecord = record(status.by_category) ?? {} + const byCategory: Record = {} + for (const [category, count] of Object.entries(byCategoryRecord)) if (typeof count === 'number') byCategory[category] = count + const topEntities = Array.isArray(status.top_entities) + ? status.top_entities.flatMap((entry) => { + const entity = record(entry) + const name = text(entity?.entity) + const count = number(entity?.count) + return name === undefined || count === undefined ? [] : [{ entity: name, count }] + }) + : [] + return { + totalInsights: number(status.total_insights) ?? 0, + deletedInsights: number(status.deleted_insights) ?? 0, + edgeCount: number(status.edge_count) ?? 0, + oplogCount: number(status.oplog_count) ?? 0, + dbSizeBytes: number(status.db_size_bytes) ?? 0, + byCategory, + topEntities, + } + } + + async graph(body: MemorySpace, signal?: AbortSignal): Promise { + const [html, insights] = await Promise.all([ + this.runner.runText(['viz', '--format', 'html', '--output', '-'], { + ...(signal === undefined ? {} : { signal }), store: body.id, maxOutputBytes: STORE_DUMP_MAX_OUTPUT_BYTES, + }), + // Mnemon's HTML visualization omits tags and entities. A readonly recall + // supplies that metadata without incrementing access counters. + this.allNativeInsights(body, signal, true), + ]) + const snapshot = parseMemoryGraph(html) + const metadata = new Map(insights.map(insight => [insight.id, insight])) + return { + ...snapshot, + nodes: snapshot.nodes.map(node => { + const insight = metadata.get(node.id) + return insight === undefined + ? node + : { ...node, ...insight, id: node.id, content: node.content, color: node.color } + }), + } + } + + private async allNativeInsights(body: MemorySpace, signal?: AbortSignal, readonly = false): Promise { + const payload = await this.runner.runJson([ + ...(readonly ? ['--readonly'] : []), + 'recall', '', '--basic', '--limit', '100000', + ], { ...(signal === undefined ? {} : { signal }), store: body.id, maxOutputBytes: STORE_DUMP_MAX_OUTPUT_BYTES }) + const values = Array.isArray(payload) ? payload : Array.isArray(record(payload)?.results) ? record(payload)!.results as JsonValue[] : [] + return values.map(normalizeInsight).filter((entry): entry is Insight => entry !== undefined) + } + + async metadataSample(body: MemorySpace, limit: number, signal?: AbortSignal): Promise { + const payload = await this.runner.runJson([ + '--readonly', + 'recall', '', '--basic', '--limit', String(limit), + ], { ...(signal === undefined ? {} : { signal }), store: body.id }) + const wrapper = record(payload) + const values = Array.isArray(payload) ? payload : Array.isArray(wrapper?.results) ? wrapper.results : [] + return values.map(normalizeInsight).filter((entry): entry is Insight => entry !== undefined) + } + + async search(body: MemorySpace, request: SearchRequest, signal?: AbortSignal): Promise { + const mode = request.mode ?? 'smart' + const args = mode === 'keyword' + ? ['search', request.query, '--limit', String(request.limit ?? this.config.defaultRecallLimit)] + : ['recall', request.query, '--limit', String(request.limit ?? this.config.defaultRecallLimit)] + if (mode === 'basic') args.push('--basic') + if (mode !== 'keyword') { + if (request.category !== undefined) args.push('--cat', request.category) + if (request.source !== undefined) args.push('--source', request.source) + if (request.intent !== undefined) args.push('--intent', request.intent) + } + const payload = await this.runner.runJson(args, { ...(signal === undefined ? {} : { signal }), store: body.id }) + const wrapper = record(payload) + const values = Array.isArray(payload) ? payload : Array.isArray(wrapper?.results) ? wrapper.results : [] + const hint = text(wrapper?.hint) + return { + results: values.map(normalizeInsight).filter((entry): entry is Insight => entry !== undefined), + ...(hint === undefined ? {} : { hint }), + } + } + + async remember(body: MemorySpace, request: RememberRequest, signal?: AbortSignal): Promise { + const args = ['remember', request.content, '--cat', request.category ?? 'general', '--imp', String(request.importance ?? 3), '--source', request.source ?? 'user'] + const tags = commaList(request.tags, 'tags', 20) + const entities = commaList(request.entities, 'entities', 50) + if (tags !== undefined) args.push('--tags', tags) + if (entities !== undefined) args.push('--entities', entities) + return this.runner.runJson(args, { ...(signal === undefined ? {} : { signal }), store: body.id }) + } + + async rememberMany(body: MemorySpace, requests: readonly RememberRequest[], signal?: AbortSignal): Promise { + if (requests.length === 0) return [] + // Bulk archival preserves exact committed facts. Native's normal semantic + // deduplication can skip a distinct configuration or correction that merely + // resembles an earlier entry. Reuse only byte-identical persisted content, + // and import the remaining originals without semantic replacement. + const existing = new Map((await this.allNativeInsights(body, signal, true)).map(entry => [entry.content, entry])) + const ordered = new Array(requests.length) + const pending = new Map() + for (const [index, request] of requests.entries()) { + const exact = existing.get(request.content) + if (exact !== undefined) ordered[index] = { action: 'skipped', id: exact.id, content: exact.content } + else { + const group = pending.get(request.content) + if (group === undefined) pending.set(request.content, { request, indexes: [index] }) + else group.indexes.push(index) + } + } + const batch = [...pending.values()] + if (batch.length === 0) return ordered + const temporary = mkdtempSync(join(tmpdir(), 'dsh-mnemon-runtime-archive-')) + const draftPath = join(temporary, 'memory-draft.json') + try { + writeFileSync(draftPath, JSON.stringify({ + schema_version: '1', + source: 'dsh-mnemon-runtime-archive', + insights: batch.map(({ request }) => ({ + content: request.content, + category: request.category, + importance: request.importance, + source: request.source, + ...(request.tags === undefined ? {} : { tags: request.tags }), + ...(request.entities === undefined ? {} : { entities: request.entities }), + })), + }), { encoding: 'utf8', mode: 0o600 }) + const payload = await this.runner.runJson(['import', draftPath, '--no-diff'], { ...(signal === undefined ? {} : { signal }), store: body.id }) + const summary = record(payload) + const rows = Array.isArray(summary?.results) ? summary.results : undefined + const errors = number(summary?.errors) + const imported = number(summary?.imported) + const updated = number(summary?.updated) + const skipped = number(summary?.skipped) + const invalid = () => new Error(`Mnemon runtime archive import returned an invalid or partial result for Memory Space ${body.id}`) + if (errors !== 0 || imported === undefined || updated === undefined || skipped === undefined || rows === undefined + || ![imported, updated, skipped].every(value => Number.isInteger(value) && value >= 0) + || imported !== batch.length || updated !== 0 || skipped !== 0 || rows.length !== batch.length) { + throw invalid() + } + const seen = new Set() + for (const candidate of rows) { + const row = record(candidate) + const index = number(row?.index) + const action = text(row?.action)?.trim().toLocaleLowerCase() + if (index === undefined || !Number.isInteger(index) || index < 0 || index >= batch.length || seen.has(index)) { + throw invalid() + } + if (row?.content !== batch[index]!.request.content || action !== 'added') { + throw invalid() + } + seen.add(index) + for (const [offset, originalIndex] of batch[index]!.indexes.entries()) { + ordered[originalIndex] = offset === 0 ? row : { ...row, action: 'skipped' } + } + } + return ordered + } finally { + rmSync(temporary, { recursive: true, force: true }) + } + } + + async related(body: MemorySpace, id: string, depth: number, edge?: EdgeType, signal?: AbortSignal): Promise { + const args = ['related', id, '--depth', String(depth)] + if (edge !== undefined) args.push('--edge', edge) + const payload = await this.runner.runJson(args, { ...(signal === undefined ? {} : { signal }), store: body.id }) + return Array.isArray(payload) ? payload.map(normalizeInsight).filter((entry): entry is Insight => entry !== undefined) : [] + } + + async link(body: MemorySpace, sourceId: string, targetId: string, type: EdgeType, weight: number, reason?: string, signal?: AbortSignal): Promise { + const args = ['link', sourceId, targetId, '--type', type, '--weight', String(weight)] + if (reason !== undefined) args.push('--meta', JSON.stringify({ reason })) + return this.runner.runJson(args, { ...(signal === undefined ? {} : { signal }), store: body.id }) + } + + forget(body: MemorySpace, id: string, signal?: AbortSignal): Promise { + return this.runner.runJson(['forget', id], { ...(signal === undefined ? {} : { signal }), store: body.id }) + } + +} diff --git a/dsh/plugins/dsh-mnemon-provider-mnemon-native/src/index.ts b/dsh/plugins/dsh-mnemon-provider-mnemon-native/src/index.ts new file mode 100644 index 000000000..26c424af9 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-mnemon-native/src/index.ts @@ -0,0 +1,29 @@ +import { MEMORY_SPACE_PROVIDER_API_VERSION, defineMemorySpaceProvider, defineMemorySpaceProviderDefinition } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { MnemonNativeProvider } from './driver.ts' +import { descriptor } from './descriptor.ts' + +export { MnemonNativeProvider, descriptor } + +export const definition = defineMemorySpaceProviderDefinition({ + manifest: { + apiVersion: MEMORY_SPACE_PROVIDER_API_VERSION, kind: 'provider', + typeId: descriptor.id, packageName: 'dsh-mnemon-provider-mnemon-native', version: '0.5.8', + label: descriptor.label, icon: descriptor.icon, summary: descriptor.summary, + ...(descriptor.summaryI18nKey === undefined ? {} : { summaryI18nKey: descriptor.summaryI18nKey }), + origin: descriptor.origin, locality: descriptor.kind, workspaceBinding: descriptor.workspaceBinding, + capabilities: descriptor.capabilities, fields: descriptor.fields, + secrets: descriptor.fields.filter(field => field.input === 'secret').map(field => field.key), + scoreSemantics: 'normalized-relevance', + }, + create(context) { + if (context.nativeRunner === undefined) throw new Error('Mnemon Native requires the Memory Spaces nativeRunner capability') + return new MnemonNativeProvider(context.nativeRunner, { defaultRecallLimit: context.config.defaultRecallLimit ?? 10 }) + }, +}) + +export default defineMemorySpaceProvider({ + id: descriptor.id, + apply(ctx, host) { host.install(ctx, definition) }, +}) + +export { parseMemoryGraph } from './driver.ts' diff --git a/dsh/plugins/dsh-mnemon-provider-mnemon-native/tests/provider.spec.ts b/dsh/plugins/dsh-mnemon-provider-mnemon-native/tests/provider.spec.ts new file mode 100644 index 000000000..bffc8c2e3 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-mnemon-native/tests/provider.spec.ts @@ -0,0 +1,126 @@ +import { existsSync, readFileSync } from 'node:fs' +import { describe, expect, it, vi } from 'vitest' +import type { MemorySpaceNativeRunner } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { createMemorySpaceProviderFixture, mountMemorySpaceProvider } from 'dsh-mnemon-source-memory-spaces/testing' +import module, { definition, descriptor, MnemonNativeProvider } from '../src/index.ts' +import { STORE_DUMP_MAX_OUTPUT_BYTES } from '../src/driver.ts' + +describe('independent Native Provider', () => { + it('mounts through the public module fixture and honors an aliased child identity', async () => { + const mounted = await mountMemorySpaceProvider(module, { instanceId: 'local', config: undefined }) + const { authority } = createMemorySpaceProviderFixture(descriptor, {}, { dataDir: '/unused', instanceId: 'local' }) + try { + expect(mounted.descriptor).toMatchObject({ id: 'local', typeId: 'mnemon-native' }) + expect(mounted.createAdapter({ memoryBodies: authority, config: { timeoutMs: 100 }, nativeRunner: { + runJson: vi.fn(), runText: vi.fn(), + } }).id).toBe('local') + } finally { await mounted.dispose() } + expect(mounted.registered).toBe(false) + }) + + it('requires a command capability and creates its own adapter', () => { + const { authority } = createMemorySpaceProviderFixture(descriptor, {}, { dataDir: '/unused' }) + expect(() => definition.create({ memoryBodies: authority, config: { timeoutMs: 100 }, providerInstanceId: 'native', manifest: definition.manifest })).toThrow('nativeRunner') + }) + + it('decodes both CLI response generations and keeps every command scoped', async () => { + const { body } = createMemorySpaceProviderFixture(descriptor, {}, { dataDir: '/unused', memoryBodyId: 'work' }) + const runJson = vi.fn() + .mockResolvedValueOnce([{ insight: { id: 'old', content: 'Nested recall', entities: ['A'] }, score: 0.9 }]) + .mockResolvedValueOnce({ results: [{ id: 'new', content: 'Flat recall', score: 0.8 }] }) + const provider = new MnemonNativeProvider({ runJson, runText: vi.fn() }) + await expect(provider.search(body, { query: 'recall' })).resolves.toMatchObject({ results: [{ id: 'old', entities: ['A'], score: 0.9 }] }) + await expect(provider.metadataSample(body, 6)).resolves.toMatchObject([{ id: 'new', score: 0.8 }]) + expect(runJson.mock.calls.map(call => call[1]?.store)).toEqual(['work', 'work']) + expect(runJson.mock.calls[1]?.[0]).toEqual(['--readonly', 'recall', '', '--basic', '--limit', '6']) + }) + + it('validates batch receipts and removes its private draft after failure', async () => { + const { body } = createMemorySpaceProviderFixture(descriptor, {}, { dataDir: '/unused' }) + let draftPath = '' + const runJson = vi.fn(async args => { + if (args[0] === '--readonly') return [] + draftPath = args[1]! + expect(args[2]).toBe('--no-diff') + expect(JSON.parse(readFileSync(draftPath, 'utf8')).insights[0].content).toBe('Keep exact content') + return { imported: 0, updated: 0, skipped: 0, errors: 1, results: [] } + }) + const provider = new MnemonNativeProvider({ runJson, runText: vi.fn() }) + await expect(provider.rememberMany(body, [{ content: 'Keep exact content' }])).rejects.toThrow('invalid or partial result') + expect(existsSync(draftPath)).toBe(false) + }) + + it('archives similar facts exactly while reusing identical persisted and in-batch content', async () => { + const { body } = createMemorySpaceProviderFixture(descriptor, {}, { dataDir: '/unused', memoryBodyId: 'work' }) + const old = 'Backend order retry limit is 3; preserve this independent decision.' + const next = 'Backend order retry limit is 4; preserve this independent decision.' + const another = 'Frontend order retry limit is 3; preserve this independent decision.' + let persisted = [{ id: 'old', content: old }] + const runJson = vi.fn(async (args, options) => { + expect(options?.store).toBe('work') + if (args[0] === '--readonly') return persisted + expect(args).toEqual(['import', expect.any(String), '--no-diff']) + const draft = JSON.parse(readFileSync(args[1]!, 'utf8')) as { insights: Array<{ content: string }> } + expect(draft.insights.map(entry => entry.content)).toEqual([next, another]) + const results = draft.insights.map((entry, index) => ({ index, id: 'new-' + index, action: 'added', content: entry.content })) + persisted = [...persisted, ...results] + return { imported: 2, updated: 0, skipped: 0, errors: 0, results: results.toReversed() } + }) + const provider = new MnemonNativeProvider({ runJson, runText: vi.fn() }) + const requests = [old, next, another, next].map(content => ({ content })) + await expect(provider.rememberMany(body, requests)).resolves.toMatchObject([ + { action: 'skipped', id: 'old', content: old }, { action: 'added', id: 'new-0', content: next }, + { action: 'added', id: 'new-1', content: another }, { action: 'skipped', id: 'new-0', content: next }, + ]) + await expect(provider.rememberMany(body, requests)).resolves.toMatchObject(requests.map(({ content }) => ({ action: 'skipped', content }))) + expect(runJson.mock.calls.filter(([args]) => args[0] === 'import')).toHaveLength(1) + expect(persisted.find(entry => entry.id === 'old')?.content).toBe(old) + }) + + it('does not import when the readonly exact-content snapshot fails', async () => { + const { body } = createMemorySpaceProviderFixture(descriptor, {}, { dataDir: '/unused', memoryBodyId: 'work' }) + const runJson = vi.fn().mockRejectedValue(new Error('snapshot unavailable')) + const provider = new MnemonNativeProvider({ runJson, runText: vi.fn() }) + await expect(provider.rememberMany(body, [{ content: 'Preserve the existing store' }])).rejects.toThrow('snapshot unavailable') + expect(runJson).toHaveBeenCalledOnce() + expect(runJson.mock.calls[0]?.[0][0]).toBe('--readonly') + }) + + it.each(['updated', 'skipped'])('rejects a %s receipt for a forced exact import', async action => { + const { body } = createMemorySpaceProviderFixture(descriptor, {}, { dataDir: '/unused' }) + const content = 'A distinct fact must be inserted exactly' + const runJson = vi.fn() + .mockResolvedValueOnce([]) + .mockResolvedValueOnce({ imported: 0, updated: action === 'updated' ? 1 : 0, skipped: action === 'skipped' ? 1 : 0, + errors: 0, results: [{ index: 0, id: 'other', action, content }] }) + const provider = new MnemonNativeProvider({ runJson, runText: vi.fn() }) + await expect(provider.rememberMany(body, [{ content }])).rejects.toThrow('invalid or partial result') + }) + + it('raises the output cap for whole-Store reads only', async () => { + const { body } = createMemorySpaceProviderFixture(descriptor, {}, { dataDir: '/unused', memoryBodyId: 'work' }) + const html = 'var nodes = new vis.DataSet([{id:"a",label:"a: [fact] One",title:"One",color:"#3498db",font:{color:"white"}}]);\nvar edges = new vis.DataSet([]);' + const runJson = vi.fn(async args => { + if (args[0] === 'import') { + const draft = JSON.parse(readFileSync(args[1]!, 'utf8')) as { insights: Array<{ content: string }> } + return { imported: 1, updated: 0, skipped: 0, errors: 0, results: [{ index: 0, id: 'b', action: 'added', content: draft.insights[0]!.content }] } + } + if (args[0] === 'status') return { total_insights: 1 } + return [{ id: 'a', content: 'One' }] + }) + const runText = vi.fn(async () => html) + const provider = new MnemonNativeProvider({ runJson, runText }) + await provider.list(body, {}) + await provider.graph(body) + await provider.rememberMany(body, [{ content: 'Two' }]) + await provider.search(body, { query: 'one' }) + await provider.status(body) + const cap = (args: readonly string[], options?: { maxOutputBytes?: number }) => [args.find(arg => !arg.startsWith('-')), options?.maxOutputBytes] + expect(runText.mock.calls.map(([args, options]) => cap(args, options))).toEqual([['viz', STORE_DUMP_MAX_OUTPUT_BYTES]]) + expect(runJson.mock.calls.map(([args, options]) => cap(args, options))).toEqual([ + ['recall', STORE_DUMP_MAX_OUTPUT_BYTES], ['recall', STORE_DUMP_MAX_OUTPUT_BYTES], ['recall', STORE_DUMP_MAX_OUTPUT_BYTES], + ['import', undefined], ['recall', undefined], ['status', undefined], + ]) + expect(STORE_DUMP_MAX_OUTPUT_BYTES).toBeGreaterThanOrEqual(64 * 1024 * 1024) + }) +}) diff --git a/dsh/plugins/dsh-mnemon-provider-mnemon-native/tsconfig.json b/dsh/plugins/dsh-mnemon-provider-mnemon-native/tsconfig.json new file mode 100644 index 000000000..badeba7ca --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-mnemon-native/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2024", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "verbatimModuleSyntax": true, + "allowImportingTsExtensions": true, + "skipLibCheck": true, + "types": [ + "node" + ] + }, + "include": [ + "src/**/*.ts", + "tests/**/*.ts", + "tsdown.config.ts", + "vitest.config.ts" + ] +} diff --git a/dsh/plugins/dsh-mnemon-provider-mnemon-native/tsdown.config.ts b/dsh/plugins/dsh-mnemon-provider-mnemon-native/tsdown.config.ts new file mode 100644 index 000000000..2d868cbd8 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-mnemon-native/tsdown.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'tsdown' + +export default defineConfig({ + entry: ['src/index.ts', 'src/descriptor.ts'], outDir: 'lib', format: 'esm', platform: 'node', + target: 'es2024', dts: true, clean: true, fixedExtension: false, deps: { neverBundle: true }, +}) diff --git a/dsh/plugins/dsh-mnemon-provider-mnemon-native/vitest.config.ts b/dsh/plugins/dsh-mnemon-provider-mnemon-native/vitest.config.ts new file mode 100644 index 000000000..5a6724c22 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-mnemon-native/vitest.config.ts @@ -0,0 +1,3 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ test: { environment: 'node' } }) diff --git a/dsh/plugins/dsh-mnemon-provider-openviking/LICENSE b/dsh/plugins/dsh-mnemon-provider-openviking/LICENSE new file mode 100644 index 000000000..e32e112dc --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-openviking/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 dsh-external contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dsh/plugins/dsh-mnemon-provider-openviking/README.md b/dsh/plugins/dsh-mnemon-provider-openviking/README.md new file mode 100644 index 000000000..40a141281 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-openviking/README.md @@ -0,0 +1,35 @@ +# dsh-mnemon-provider-openviking + +An adapter for an existing OpenViking HTTP service and its filesystem-shaped memory resources. + +Requires a reachable OpenViking endpoint supporting `POST /api/v1/content/write` with `mode: "create"`, `wait: true`, and tags. Agent-authored memories use unique Markdown files; the adapter verifies completed indexing and exact full-content readback before returning `stored` with a reversible `id`. It does not create extraction sessions. + +The supported scope is `viking://user//memories`. Existing `viking://user/memories` settings resolve through the configured user or authenticated `/api/v1/system/status` identity, without rewriting the local registry. A failed response, timeout, or mismatched readback produces an error instead of a committed receipt. The error includes the requested URI because remote content may remain; inspect it before retrying. Existing memories remain readable, and downgrading does not remove files written by this adapter. + +## Use + +The default `dsh-mnemon` Starter already installs this package. Enable and configure the service on the `dsh-mnemon` page under **Plugins**, then inspect its synchronized Memory Spaces. + +For a user API key without Admin API access, including managed OpenViking Service endpoints, fill `endpoint`, `apiKey`, `account`, and **User key owner (skip admin)** (`discoveryUser`) with the supplied account/user identifiers. The adapter validates `viking://user//memories` through the read-only `/api/v1/fs/ls` data API and synchronizes that one space. It sends no account/user identity headers in this mode: the key determines the authenticated tenant, and `account` identifies the local projection rather than overriding that identity. Use an existing accessible memory root; an absent root, rejected key, invalid response, or denied owner fails the save and preserves previous settings. Health-only success is insufficient. This path does not verify write permission or certify a particular cloud deployment; exact writes still require the content API above. + +Leave `discoveryUser` empty for the existing admin discovery flow. There is no automatic fallback after an admin error. Nonempty `discoveryUser` requires a safe user ID, an explicit account, and an API key; manually configured memory owners must match it. The new setting uses the existing service registry format. Before downgrading to an adapter without this field, clear it using the current version and use admin-capable configuration, or disable the service and restore an older compatible configuration. Reconnection and downgrade never delete remote memories. + +For a custom composition, install this package alongside `dsh-mnemon-source-memory-spaces` and include it in that Source's `config.providers`: + +```yaml +providers: + - use: dsh-mnemon-provider-openviking + instanceId: openviking +``` + +This is a **Memory Spaces child module**, not a top-level Source or a complete Strategy. It registers through `dsh-mnemon-source-memory-spaces/provider-sdk`; each parent Source owns its child Fibers, connection settings and lifetime. Credentials stay on the Host. + +[Provider setup and capability matrix](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/guides/memory-providers.md) · [中文指南](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/zh-CN/guides/memory-providers.md) + +## Develop independently + +From a source checkout with the declared dependencies installed, run `pnpm verify`. Tests, build and public exports belong to this package, without importing another package's controllers or repository configuration. Use the Source's public `/testing` fixtures for child registration and driver conformance; live service tests require a separately authorized environment. + +To test a disposable local OpenViking service (never a personal store), run `MNEMON_OPENVIKING_TEST_ENDPOINT=http://127.0.0.1:1933 pnpm exec vitest run tests/integration.spec.ts`. Supply `MNEMON_OPENVIKING_TEST_API_KEY` when the service requires authentication. It creates one synthetic canary in the default user's namespace, reads/searches/browses it, and deletes its exact URI. Ordinary verification skips this optional integration test. Admin discovery requires a key allowed to enumerate users; ROOT keys cannot access tenant data in `api_key` mode. The user-key regression uses deterministic responses, not a live cloud account. + +[Plugin author guide](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/development/extensions.md) diff --git a/dsh/plugins/dsh-mnemon-provider-openviking/package.json b/dsh/plugins/dsh-mnemon-provider-openviking/package.json new file mode 100644 index 000000000..ad2a77132 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-openviking/package.json @@ -0,0 +1,55 @@ +{ + "name": "dsh-mnemon-provider-openviking", + "version": "0.5.7", + "description": "OpenViking Provider child for the Memory Spaces Source.", + "license": "MIT", + "type": "module", + "engines": { + "node": ">=20" + }, + "exports": { + ".": { + "types": "./lib/index.d.ts", + "default": "./lib/index.js" + }, + "./descriptor": { + "types": "./lib/descriptor.d.ts", + "default": "./lib/descriptor.js" + }, + "./package.json": "./package.json" + }, + "files": [ + "lib", + "README.md" + ], + "scripts": { + "typecheck": "tsc --noEmit", + "build": "tsdown", + "test": "vitest run", + "verify": "pnpm typecheck && pnpm test && pnpm build" + }, + "peerDependencies": { + "@deepseek-ai/cordis": "^4.0.1", + "dsh-mnemon-source-memory-spaces": "^0.5.1" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "dsh-mnemon-source-memory-spaces": "^0.5.11", + "tsdown": "^0.22.0", + "typescript": "^5.9.0", + "vitest": "^3.2.0" + }, + "publishConfig": { + "access": "public", + "tag": "latest" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/mnemon-dev/mnemon.git", + "directory": "dsh/plugins/dsh-mnemon-provider-openviking" + }, + "homepage": "https://github.com/mnemon-dev/mnemon/tree/master/dsh/plugins/dsh-mnemon-provider-openviking#readme", + "bugs": { + "url": "https://github.com/mnemon-dev/mnemon/issues" + } +} diff --git a/dsh/plugins/dsh-mnemon-provider-openviking/src/descriptor.ts b/dsh/plugins/dsh-mnemon-provider-openviking/src/descriptor.ts new file mode 100644 index 000000000..5990b048d --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-openviking/src/descriptor.ts @@ -0,0 +1,98 @@ +import type { MemoryProviderDescriptor } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { ICON } from './icon.ts' + +/** Browser-safe metadata owned by this Provider. */ +export const descriptor: MemoryProviderDescriptor = { + "id": "openviking", + "label": "OpenViking", + "icon": { + "kind": "data-url", + "value": ICON + }, + "kind": "remote", + "workspaceBinding": "provider-global", + "summary": "Filesystem-shaped shared memory with verified exact writes and semantic retrieval.", + "summaryI18nKey": "overview.providerSummary.openviking", + "origin": "third-party", + "capabilities": { + "search": true, + "browse": true, + "graph": false, + "entities": false, + "related": false, + "remember": true, + "link": false, + "forget": true, + "writeMode": "exact", + "deletionMode": "hard" + }, + "fields": [ + { + "key": "endpoint", + "label": "Endpoint", + "scope": "service", + "input": "url", + "required": true, + "defaultValue": "http://127.0.0.1:1933", + "placeholder": "http://127.0.0.1:1933", + "i18nKey": "overview.providerEndpoint" + }, + { + "key": "targetUri", + "label": "Memory URI", + "scope": "memory", + "input": "text", + "required": true, + "defaultValue": "viking://user/memories", + "placeholder": "viking://user/memories", + "pattern": "^viking://user(?:/(?!\\.{1,2}/)[a-zA-Z0-9_.@-]+)?/memories$", + "normalize": "trim-trailing-slash", + "validationMessage": "OpenViking memory URI must be a viking://user/.../memories root", + "i18nKey": "overview.providerTargetUri" + }, + { + "key": "apiKey", + "label": "API key", + "scope": "service", + "input": "secret", + "required": false, + "i18nKey": "overview.providerApiKey" + }, + { + "key": "account", + "label": "Account", + "scope": "service", + "input": "text", + "required": false, + "i18nKey": "overview.providerAccount" + }, + { + "key": "discoveryUser", + "label": "User key owner (skip admin)", + "scope": "service", + "input": "text", + "required": false, + "maxLength": 200, + "pattern": "^(?!\\.{1,2}$)(?!.*@.*@)[a-zA-Z0-9_.@\\-]+$", + "validationMessage": "OpenViking discovery user must be a safe user ID", + "i18nKey": "overview.providerDiscoveryUser" + }, + { + "key": "user", + "label": "User", + "scope": "memory", + "input": "text", + "required": false, + "i18nKey": "overview.providerUser" + }, + { + "key": "actorPeerId", + "label": "Agent peer", + "scope": "memory", + "input": "text", + "required": false, + "defaultValue": "dsh", + "i18nKey": "overview.providerActorPeer" + } + ] +} diff --git a/dsh/plugins/dsh-mnemon-provider-openviking/src/driver.ts b/dsh/plugins/dsh-mnemon-provider-openviking/src/driver.ts new file mode 100644 index 000000000..9b63e68b3 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-openviking/src/driver.ts @@ -0,0 +1,372 @@ +import { randomUUID } from 'node:crypto' +import type { JsonValue } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import type { MemorySpaceAuthority } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import type { + Insight, + MemoryBody as MemorySpace, + MemoryGraphSnapshot, + MemoryListRequest, + MemoryProviderConnection, + OpenVikingBodyConnection as OpenVikingSpaceConnection, + RememberRequest, + SearchRequest, +} from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { NORMALIZED_RELEVANCE_SCORE, type MemoryProviderAdapter, type ProviderBodyStatus as ProviderSpaceStatus, type ProviderMemorySpace, type ProviderSearchResult } from 'dsh-mnemon-source-memory-spaces/provider-sdk' + +interface OpenVikingEnvelope { + status?: string + result?: unknown + error?: { code?: string; message?: string; trace_id?: string } + trace_id?: string +} + +interface OpenVikingRequestOptions { + timeoutMs?: number + signal?: AbortSignal | undefined +} + +interface OpenVikingProviderOptions { + fetch?: typeof fetch + requestTimeoutMs?: number +} + +type OpenVikingConnection = OpenVikingSpaceConnection & { discoveryUser?: string } + +function object(value: unknown): Record | undefined { + return typeof value === 'object' && value !== null && !Array.isArray(value) ? value as Record : undefined +} + +function string(value: unknown): string | undefined { + return typeof value === 'string' ? value : undefined +} + +function number(value: unknown): number | undefined { + return typeof value === 'number' && Number.isFinite(value) ? value : undefined +} + +// Agent-authored memories use the content API; extraction counters cannot +// prove that a one-turn session stored the caller's text. +const MEMORY_FOLDERS: Record = { + preference: 'preferences', + insight: 'experiences', + decision: 'experiences', + context: 'events', + fact: 'entities', + general: 'entities', +} + +function memoryUri(root: string, category: string, content: string): string { + const folder = Object.hasOwn(MEMORY_FOLDERS, category) ? MEMORY_FOLDERS[category] : 'entities' + const firstLine = content.split('\n').find(line => line.trim().length > 0) ?? 'memory' + const slug = Array.from(firstLine.replace(/^#+\s*/u, '').replace(/[^\p{L}\p{N}]+/gu, '-').replace(/^-+|-+$/gu, '')).slice(0, 48).join('').toLowerCase() || 'memory' + const stamp = new Date().toISOString().replace(/[-:T]/gu, '').slice(0, 14) + return `${root}/${folder}/${stamp}-${slug}-${randomUUID()}.md` +} + +function safeUser(user: string): boolean { + return /^[a-zA-Z0-9_.@-]+$/u.test(user) && user !== '.' && user !== '..' && (user.match(/@/gu)?.length ?? 0) <= 1 +} + +function discoveryUser(connection: MemoryProviderConnection | OpenVikingConnection): string | undefined { + const user = String(connection.discoveryUser ?? '').trim() + if (user === '') return undefined + if (!safeUser(user) || !safeUser(String(connection.account ?? '').trim())) { + throw new Error('OpenViking user-key discovery requires an explicit account and a safe discovery user') + } + if (String(connection.apiKey ?? '').trim() === '') throw new Error('OpenViking user-key discovery requires a user API key') + return user +} + +function isMemoryFile(uri: string, root: string): boolean { + if (!uri.startsWith(`${root}/`) || !uri.endsWith('.md') || /[%?#\\\u0000-\u001f\u007f]/u.test(uri)) return false + return uri.slice(root.length + 1).split('/').every(part => part !== '' && !part.startsWith('.') && part.trim() === part) +} + +function queueFailed(value: unknown): boolean { + return Object.values(object(value) ?? {}).some(entry => { + const queue = object(entry) + return (number(queue?.error_count) ?? 0) > 0 || (Array.isArray(queue?.errors) && queue.errors.length > 0) + }) +} + +function categoryFromUri(uri: string): string { + const marker = '/memories/' + const suffix = uri.includes(marker) ? uri.slice(uri.indexOf(marker) + marker.length) : '' + return suffix.split('/')[0]?.replace(/\.md$/u, '') || 'general' +} + +export class OpenVikingProvider implements MemoryProviderAdapter { + readonly id = 'openviking' as const + readonly scoreSemantics = NORMALIZED_RELEVANCE_SCORE + private readonly requestFetch: typeof fetch + private readonly requestTimeoutMs: number + + constructor(private readonly memorySpaces: MemorySpaceAuthority, options: OpenVikingProviderOptions = {}) { + this.requestFetch = options.fetch ?? globalThis.fetch + this.requestTimeoutMs = options.requestTimeoutMs ?? 15_000 + } + + async discover(connection: MemoryProviderConnection, signal?: AbortSignal): Promise { + signal?.throwIfAborted() + let account = String(connection.account ?? '').trim() + const selectedUser = discoveryUser(connection) + if (selectedUser !== undefined) { + const targetUri = await this.validateUserNamespace(connection, selectedUser, signal) + return [{ + externalId: `${account}:${selectedUser}`, + name: selectedUser, + description: `OpenViking memory namespace for ${selectedUser}`, + connection: { targetUri, user: selectedUser, actorPeerId: 'dsh' }, + }] + } + if (account === '') { + const accounts = await this.requestConnection(connection, '/api/v1/admin/accounts', {}, { signal }) + const items = Array.isArray(accounts) ? accounts : [] + const ids = items.flatMap(value => { + const id = string(object(value)?.account_id) ?? string(object(value)?.id) + return id === undefined ? [] : [id] + }) + if (ids.length > 1) throw new Error('OpenViking exposes multiple accounts; configure the account to select one discovery scope') + account = ids[0] ?? 'default' + } + const users = await this.requestConnection({ ...connection, account }, `/api/v1/admin/accounts/${encodeURIComponent(account)}/users?limit=100`, {}, { signal }) + const items = Array.isArray(users) ? users : [] + return items.flatMap(value => { + const item = object(value) + const user = string(item?.user_id) ?? string(item?.id) ?? string(item?.name) + if (user === undefined || !safeUser(user)) return [] + return [{ + externalId: `${account}:${user}`, + name: string(item?.display_name) ?? string(item?.name) ?? user, + description: string(item?.description) ?? string(item?.role) ?? `OpenViking memory namespace for ${user}`, + connection: { targetUri: `viking://user/${user}/memories`, user, actorPeerId: 'dsh' }, + }] + }) + } + + async status(body: MemorySpace, signal?: AbortSignal): Promise { + try { + const connection = this.connection(body) + const selectedUser = discoveryUser(connection) + if (selectedUser === undefined) await this.request(body, '/health', {}, { signal, timeoutMs: 5_000 }) + else { + await this.memoryRoot(body, signal) + await this.validateUserNamespace(connection, selectedUser, signal) + } + return { healthy: true } + } catch (error) { + return { healthy: false, error: error instanceof Error ? error.message : String(error) } + } + } + + async search(body: MemorySpace, request: SearchRequest, signal?: AbortSignal): Promise { + const memoryRoot = await this.memoryRoot(body, signal) + const result = await this.request(body, '/api/v1/search/find', { + method: 'POST', + body: JSON.stringify({ + query: request.query, + target_uri: memoryRoot, + context_type: ['memory'], + limit: request.limit, + }), + }, { signal }) + const root = object(result) + const entries = Array.isArray(root?.memories) ? root.memories : [] + const matches = entries.flatMap((value): Insight[] => { + const item = object(value) + const uri = string(item?.uri) + if (uri === undefined || !isMemoryFile(uri, memoryRoot)) return [] + const score = number(item?.score) + return [{ + id: uri, + externalUri: uri, + content: string(item?.overview) ?? string(item?.abstract) ?? uri, + category: string(item?.category) ?? categoryFromUri(uri), + source: 'external', + ...(score === undefined ? {} : { score }), + }] + }).slice(0, request.limit) + return { results: await Promise.all(matches.map(async item => ({ ...item, content: await this.readContent(body, item.id, signal) }))) } + } + + async graph(body: MemorySpace, signal?: AbortSignal): Promise { + // Remote providers are projected as a bounded, disconnected browse view; + // they do not pretend to expose Mnemon's typed graph relationships. + const items = await this.list(body, { limit: 200 }, signal) + return { + nodes: items.map(item => ({ ...item, color: '#5568d9' })), + edges: [], + generatedAt: new Date().toISOString(), + } + } + + async list(body: MemorySpace, request: MemoryListRequest, signal?: AbortSignal): Promise { + const memoryRoot = await this.memoryRoot(body, signal) + const query = new URLSearchParams({ uri: memoryRoot, recursive: 'true', output: 'original' }) + const result = await this.request(body, `/api/v1/fs/ls?${query}`, {}, { signal }) + const entries = Array.isArray(result) ? result : [] + const limit = Math.min(Math.max(request.limit ?? 200, 1), 1000) + const files = entries.flatMap((value): Array<{ item: Record; uri: string }> => { + const item = object(value) + const uri = string(item?.uri) + return item === undefined || uri === undefined || item.isDir === true || !isMemoryFile(uri, memoryRoot) ? [] : [{ item, uri }] + }).slice(0, limit) + return Promise.all(files.map(async ({ item, uri }): Promise => { + const content = await this.readContent(body, uri, signal) + const createdAt = string(item.modTime) + return { + id: uri, + externalUri: uri, + content, + category: categoryFromUri(uri), + source: 'external', + ...(createdAt === undefined ? {} : { createdAt }), + } + })) + } + + async remember(body: MemorySpace, request: RememberRequest, signal?: AbortSignal): Promise { + const root = await this.memoryRoot(body, signal) + const category = string(request.category) ?? 'general' + const uri = memoryUri(root, category, request.content) + const tags = ['source=mnemon', `category=${category}`] + const importance = number(request.importance) + if (importance !== undefined) tags.push(`importance=${importance}`) + const writtenBytes = Buffer.byteLength(request.content, 'utf8') + try { + const written = object(await this.request(body, '/api/v1/content/write', { + method: 'POST', + body: JSON.stringify({ uri, content: request.content, mode: 'create', wait: true, timeout: this.requestTimeoutMs / 1000, tags }), + }, { signal })) + if (written?.uri !== uri || written.content_updated !== true || written.written_bytes !== writtenBytes + || written.mode !== 'create' || written.context_type !== 'memory' + || written.vector_status !== 'complete' || !['complete', 'skipped'].includes(String(written.semantic_status)) + || queueFailed(written.queue_status)) { + throw new Error('content API did not confirm this exact memory and completed indexing') + } + // The public read projection removes OpenViking's reserved metadata. + // Reject normalization or a stale read rather than promising exactness. + if (await this.readContent(body, uri, signal) !== request.content) throw new Error('stored content does not match the requested text') + signal?.throwIfAborted() + } catch (error) { + // A server timeout can happen after the file was written. Do not retry, + // fall back to extraction, or delete a target whose creation is uncertain. + throw new Error(`OpenViking write was not verified at ${uri}; remote content may remain. Inspect this URI before retrying. ${error instanceof Error ? error.message : String(error)}`, { cause: error }) + } + return { + action: 'stored', + provider: 'openviking', + id: uri, + uri, + externalUri: uri, + summary: `OpenViking stored the memory at ${uri} (category ${category}).`, + writtenBytes, + vectorStatus: 'complete', + } + } + + async forget(body: MemorySpace, id: string, signal?: AbortSignal): Promise { + const root = await this.memoryRoot(body, signal) + const uri = id.trim() + if (!isMemoryFile(uri, root)) { + throw new Error('OpenViking forget requires an exact non-generated .md memory URI inside this Memory Space') + } + const query = new URLSearchParams({ uri, recursive: 'false' }) + const result = object(await this.request(body, `/api/v1/fs?${query}`, { method: 'DELETE' }, { signal })) ?? {} + if (result.uri !== uri) throw new Error('OpenViking did not confirm deletion of the requested memory URI') + return { + action: 'deleted', + provider: this.id, + id: uri, + uri, + ...(number(result.estimated_deleted_count) === undefined ? {} : { estimatedDeletedCount: number(result.estimated_deleted_count)! }), + } + } + + private connection(body: MemorySpace): OpenVikingConnection { + if ((body.provider.typeId ?? body.provider.id) !== this.id) throw new Error(`OpenViking cannot serve provider ${body.provider.id}`) + const connection = this.memorySpaces.providerConnection(body.id, body.provider.id) + return { endpoint: String(connection.endpoint ?? ''), targetUri: String(connection.targetUri ?? ''), apiKey: String(connection.apiKey ?? ''), account: String(connection.account ?? ''), user: String(connection.user ?? ''), actorPeerId: String(connection.actorPeerId ?? ''), discoveryUser: String(connection.discoveryUser ?? '') } + } + + private async validateUserNamespace(connection: MemoryProviderConnection | OpenVikingConnection, user: string, signal?: AbortSignal): Promise { + const root = `viking://user/${user}/memories` + // Health is unauthenticated on some deployments. A read-only, scoped data + // request must succeed before the Host persists or replaces projections. + const query = new URLSearchParams({ uri: root, recursive: 'false', output: 'original' }) + const result = await this.requestConnection(connection, `/api/v1/fs/ls?${query}`, {}, { signal }) + if (!Array.isArray(result) || result.some(item => !string(object(item)?.uri)?.startsWith(`${root}/`))) { + throw new Error('OpenViking could not validate the selected memory namespace') + } + return root + } + + private async memoryRoot(body: MemorySpace, signal?: AbortSignal): Promise { + signal?.throwIfAborted() + const connection = this.connection(body) + const root = connection.targetUri.replace(/\/+$/u, '') + const match = /^viking:\/\/user(?:\/([^/]+))?\/memories$/u.exec(root) + if (match === null || (match[1] !== undefined && !safeUser(match[1]))) throw new Error('OpenViking memory URI must be a safe viking://user//memories root') + const selectedUser = discoveryUser(connection) + if (selectedUser !== undefined) { + if ((match[1] !== undefined && match[1] !== selectedUser) || (connection.user?.trim() && connection.user.trim() !== selectedUser)) { + throw new Error('OpenViking memory owner must match the configured discovery user') + } + return `viking://user/${selectedUser}/memories` + } + if (match[1] !== undefined) return root + // Retain old persisted shorthand without sending the ambiguous URI to + // newer servers. With no configured user, ask the authenticated backend. + const user = connection.user?.trim() || string(object(await this.request(body, '/api/v1/system/status', {}, { signal }))?.user) || '' + if (!safeUser(user)) throw new Error('OpenViking could not resolve the memory owner; configure an explicit user memory URI') + return `viking://user/${user}/memories` + } + + private async readContent(body: MemorySpace, uri: string, signal?: AbortSignal): Promise { + const result = await this.request(body, `/api/v1/content/read?${new URLSearchParams({ uri })}`, {}, { signal }) + if (typeof result !== 'string') throw new Error('OpenViking content read did not return text') + return result + } + + private async request(body: MemorySpace, path: string, init: RequestInit = {}, options: OpenVikingRequestOptions = {}): Promise { + const connection = this.connection(body) + return this.requestConnection(connection, path, init, options) + } + + private async requestConnection(connection: MemoryProviderConnection | OpenVikingConnection, path: string, init: RequestInit = {}, options: OpenVikingRequestOptions = {}): Promise { + options.signal?.throwIfAborted() + // In api_key mode the backend binds the account/user to the key and + // rejects trusted identity overrides. Preserve old discovery by default. + const keyBound = discoveryUser(connection) !== undefined + const controller = new AbortController() + const relay = () => controller.abort(options.signal?.reason) + options.signal?.addEventListener('abort', relay, { once: true }) + const timer = setTimeout(() => controller.abort(new Error('OpenViking request timed out')), options.timeoutMs ?? this.requestTimeoutMs) + try { + const response = await this.requestFetch(`${connection.endpoint}${path}`, { + ...init, + headers: { + 'Content-Type': 'application/json', + ...(connection.apiKey === undefined || connection.apiKey === '' ? {} : { Authorization: `Bearer ${connection.apiKey}` }), + ...(keyBound || connection.account === undefined || connection.account === '' ? {} : { 'X-OpenViking-Account': String(connection.account) }), + ...(keyBound || connection.user === undefined || connection.user === '' ? {} : { 'X-OpenViking-User': String(connection.user) }), + ...(connection.actorPeerId === undefined || connection.actorPeerId === '' ? {} : { 'X-OpenViking-Actor-Peer': String(connection.actorPeerId) }), + ...init.headers, + }, + signal: controller.signal, + }) + const envelope = await response.json().catch(() => ({})) as OpenVikingEnvelope + if (!response.ok || envelope.status === 'error') { + const trace = envelope.error?.trace_id ?? envelope.trace_id + throw new Error(`${envelope.error?.message ?? `OpenViking HTTP ${response.status}`}${trace === undefined ? '' : ` (trace ${trace})`}`) + } + return envelope.result ?? envelope + } catch (error) { + if (controller.signal.aborted && options.signal?.aborted !== true) throw new Error(`OpenViking request timed out after ${options.timeoutMs ?? this.requestTimeoutMs}ms`) + throw error + } finally { + clearTimeout(timer) + options.signal?.removeEventListener('abort', relay) + } + } +} diff --git a/dsh/plugins/dsh-mnemon-provider-openviking/src/icon.ts b/dsh/plugins/dsh-mnemon-provider-openviking/src/icon.ts new file mode 100644 index 000000000..9b2dba6ee --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-openviking/src/icon.ts @@ -0,0 +1,2 @@ +/** Browser-safe brand asset shipped by this Provider, not by the Host. */ +export const ICON = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAASKADAAQAAAABAAAASAAAAACQMUbvAAAVNUlEQVR4Ae2bCZRUxbmA/7pLb7d7erpnumdlmAWQYUAMsiggkBhjjCImUZK8qE89CDEeTWKi5GXxzXkxmrgmGlBUIiQRI8YsgLhBQEVwYRWGgRlmX3q6p3t6X+5a76/ByRtx2GSgOS8Up0/drrm3btXHX3/9SzXAuXKOwDkC5wj8GxJYuHC7+G847ROf8tptkVt/+Xz0xhN/Ijt3ctl5LQBHoHVEkXXlzQ/1LZ27cLstW+M4a997/o9el/a0yPXPbab0nt+nX1vweKj0rB1stgb2br3ycChF6VNvUFr7vLxv0eP+idkay9Hem7UlxgaEErQWK336WEq9eaYaj9PxylWLm88qSFkFtK8lvaM7ZLRUFhFSWWBQr8daAkT/be0zjaXLlp0du1xWAS293ZvoS2hbNRSjz1US8DgM6pQsl+xuydxw8RXjfrX6rZ4JTNKyWbIKiE08kjT+GUsB5OUQqB5BoLgoh2vvVmb3RuiGiaNd/1i/JXDFvzWg3ri2IxIzZFkFMroYoLrMRHNzrJfeeG+dry9GfzNhnOtv698NXJstSFmXoM7WYKeigc8gAAJPYEaNCC5XrjCq1HLLxWMtjzd2GZsnT3Cv+tsW3/xsQMo6oNqby6NpWe8CBKTqQMsLeaguNwMYcNUND/VIr26N3L6/A6LTJ3ie+8eWnqvPNKSsAwIgFIuPTVwzAHiOks9fYKVmq1SeCodnPrSooGlvc+LnHX28bfK4vJVr3ur8wpmEdBYA6gcT0ynWOiWyRuiUsRYYPdJJZMW4lMFYsfbQ8r3NmY29CSH3wvEFq17eHJxypiCdFYBUWU1oOgDqIoQEIFkIuexCK2iUzKB0Nb/j6clqIJz48YE2LRVOCQVTxuW8uOSvrdVnAtJZAYiCoVGUoH5AuMwU1EWXTDBDscc89pZHq3FvA1h8rWd7KJpe1tYLIBtixRUXFa6qXVJXeLohnRWArGYTDwgIlXT/B7d8cDt4mDLO6YrHhX9JSmtb6EF/UOnsCKCuMpsvuP5rVX+67YE21+mEdFYAIoRKDA6TItRDKEF4gWV8lYM4beL5AwAeur2iJxRPPZKUAToDQCWb+dI7b/D8Yc536+wD9wx3fVYAstt4O1teBnLRcYlpOsFlBFDs5sFlF8YOnnT9If9yf5+8D+8lrT1AC/OsVz16V8XKL/2oRxp833BdnxWAqKF70sr/AWKg2HeHleCHLx882d8vHhsPhdOPRpK0X+JafUDz3dav/edV4sLB9w3XdfYBXbhMNIB3ZxgglBoD6bB/GRWozUwApavoB6s7rIMnvK+jfXUwqu5HjgT1Fe0KGODOMX3njsffyxl833BcZx3Qj++62K4b1N0Pp1/1oEmNZjXuZMDzAHYz8Qq9Mefgyf7x7onJaFJekUZljvqLRBIaFUVpzPQJlV8bfN9wXGcdUGWu5MmkwaMjEAaJIB+mrCmTJsqWGLFrBuc5crIdvYnVvWGtj+MISaQppNIAJrOEUrQe/ZThK1kH5LRaKwwiWClTPFgYIHbJNjL2sdsEs9VkzT9yyo8sKG5LpvVNzD1RVQKZtE4NwzQ1P3f8nCPvPZXvWQdkkywTDcxxEA7XCsLBbMdhXYRwmMHolEzE0PQhDcJoSlnXFzv8jKIaaIELxGF33nYqQI58NuuAEM5FzL1gYPiPR8MkiC23lAykuMAGhYW2fmv6yMGHE/K2QEjB/YwQFfWRphq0KM96+R2Pd0w98t7P+j2rgJau+8ilG+bJTP9wOBIGiOkd9O779RCTIFTAaBtx6GB8uuz44MPWWEpv1HQBMjLufApu+S7RMqrUceun7/5sLVkFlGsvmaxoYqnwMRwGiBmKDBKTIhQqUt8STTS397031PRefeIraFPTPaioQVEoyAxSisKIQtPVV9+zc0ipG6qfY7VlFVA0ZlyhUw5dBtyBRCSCRUfNzJYXg5XKUOgNyQ2qPdlytElomtbEdFcqpUMiqSMkg+bnWr2XTy+65mjPnEx71gA9s8FfgD5XmUU0tBwb7ddBbEdimBgclCriC2kQT6vvPb1oMmqYoQvhSScDmkbfJBTWcEfDPnDJji2XWBz7lOd3yh0MPezjtxIFLuZ4c7owjxctJqZ2UAyQDlPWuKFBbxgzHtE0Lh393WP1hkG1LmYiiBjP7osqEMdMrYzLzZUjVn/1R+9+yjw4Vl9D/S0rgB5dvdWqqLTabhWtXjfgDs90DnMwmPQQaPPhNYIKR5IpqpHdQw18oE3VDLzbkK0WkWQyer8UMdIWkXfPmuo5ZT2UFUA2c/nsvjgJjygQRlvxlBBTyIyIgK5Fd9CAWJIAShWToBZfn9A8AGOoOpNWA/h42GIR0DXhUA8pkEgRynGiqdhjLx/qmZNpO+OAVq+mfEbjv4jh1FaPixutfhzmMAmoaNFl6OihIKFrqmF7NGHsWlFbkTnWhO6/dUckI8thEzoYKEUQickQjVPKfLk8p1h5rGdP5G84rDNbwqLv0r4YH5823lboyuGtCm7NTH9gmgdautBtQDBMkmQZdyXZGHJ7ZyNe/15oXL7bfJk3l7/GbRcrdjaotL2LQLtPJb0hhcYSFuB48ROxpM8y0zMKqLa2lkvq4g0tLdFfz7sk957+3YonVFYpHOqkwJxOHXURWtZEkVN6SjHtGDypJ9ZEKkrzTVeWeriv5zvFqQVuzmZFmC0odSnVRCVHAlNHBm3rTsDoSgvkO3kM19biKqlF/J8ubGfAwhb4UcsZBZQ3fuFlfSkqB99vaBRuLpth4DJg23MH2sk8ShHTOwZKFKZ+gGhGx7OLV+684aE9UnVx2eU5dvP1LrtptjePdzswwGoWEKRC6fttlO5vM0gGIXvzrVDgTUGPPwm+QC54csUR339sds5vfgCRIwn8eVPP+JfeaEtie8uRfxv8/YwB2rRpk7Czl1/UHUj/7Ie/+vxUySqUhyIGDSUAnHYOt3NkgqFW0cShgWhAKslt/uHT37/FJZnvsFrFcSaM61tRz6DAUElEUwelbPNuCm1+HXNpTAgMUA0Oxo114XbfAw2tURg1wuEdPbKkCP/4CUBzaqkAQuzmcCL6wGAYQ12fMUC7gmOujSbh0OwLLCFMYizvjRIuw+wVB9efUe2L6RDsUw+GI8oGfyi1PqWbv5Pncj6JBgA6oZhRtFDIsXFQ4iYQQ6gfHDAgnMAAP67HgSikisvTyvFkQo2bfrQvQn0hu8WVZ2OA6gdP/qZZmf9wWISR35x7XnBw+1DXZwTQs691uINh4ZulheIbXo/jHcoJo9kuk84Y1BdQ9gejysaW9sTaP764/f3gu/PiCx4J3G63WuYqmSQIFpGarDwt8RAyAsNmDR0UGpi+QpsHbSAEZFCN6qhKULmj4ayjHjPjWq2skOihTpUrdhnlgyf+X7/3eco84v29sfSDg9uPdn1GAJkF671jRtpmSjbLvHga/aWYvCccybza7U+ue+71tp2dL03HeODh8o3axklKxrg/ZaRBsJuoIBCowBNoXieBvS24pHpw+aGSymCeCEO1/c6ExURw+XEgiBxGBTi23qC02AHhsAE9Ua7q4677q1kTXf9tEknxh3simwe3H+369ALCXeKFtyM3u3NsC2NJo66lK/5klz+27hf3790Nh5gn/slSftMmi6byj6Ew5Ii8QW0WDiZWCWBD3bO70YBARIMUi4GgZ8LxQCwWHmz4sVpQdwkcZVEBBvSwuwJQ6BbhUJOIO9nh8vSbmcuLC0yLdtSndz28ZNuBgfZj1acV0MKnd1gTFSOUpA9mLPjKwo8AXsKFdfRS7vTe7nQ4ZqVSGfC4Jbioxtrvehxo19A3U0HFQLUJAQhoKFkw48FgmHEGIk+BJwY4bQSNQx73foSEsDKyntSKTWhdAfx0ZbCkpkxYEs8Q4RAuZ6ibj3mU4xf8zzo7ypX3tFYrqrKlpsLtZm4Z2jgELWTaHZAhEldBZplFjBCxVaXhFqZi4IhFIjEthLuVjVx0vhtqKu0IVEuG4+quQCi9vq7Zv+bem2r2w4XbhTefm/CSx2Wa9/auTHrb3raLXvjpWPwPO345rRJ0/NcfvqMWTYCOD8wPRxImd4HbTA+0RmDr7iDNyBgiRKlBG4lFVfFmDkSRB6/bAheMc3GTa9wwqtQKZlFNoML/sKkzvHZnfei1+24dw5ZPvy5ib1j7ZPXP852meS0+gKbO+EaEs5e1n0g5KwCdb7no25Yy+pUUWnvBmALBiAKuXBtKiwV9MqN/SXnzzeS8CjuZOMYFI7x41kHXYygpO/Y0BtfvqI+sf+LO0fuHmvC6D6LXe/MsP4lhMG1vY8Y42BT4Hd73L3hDPTO4bVgAjfp2Y87dNxWNXHSZ/YT/ZwYGsXZ3sMTBi/c5JL3/+EsSA8t5edZ+nwx3a1JVZofxoxzEg2c4MrIWCsfk9z/YJ6/Ze7Bv49K7Rh0a6Geo+k/vRr7kyrUvxR2O33FQhfqmwDuvPfL4P4e692htnxnQtx7uzi9x22eUecUrL6jk56DhN+K3a9LPd4b0nz10s73naC8c3I66hvxjm+n+t1uUUgyMUZtVAE+elVjMOVCYL0KBU9HNJmgKhuNb325IbPhwf8+Wv/5yUtvgPo52vXJjcGpZnv2P7hzO0dgu020f9Rn+gP/XAE8fNTo5VF8noaSv4xc9taSyINc2M8/BfTnPzk2vKjKXjirBoyjoS618Iw5jRthBMuOEoqnaH37V/jy+8JiivGpD8PpGn/UPGYUjpUXofeNOJHKZPqvZ2G4V1X82dcXefnDpB3t7N89H2/nEywtbw5+rKJT+5rKLIxMJgz6zNky6e4J/WfPAWHZS9phjOvItxwVUu6zbVlzp+C+Dmi6z27hxFQWCw5uLziJuoyaBUtxJ6IqNMmnuStNciZIvTHITzGdh0Et5BU+E1S6+Nnf7kS9l3x97uXcSJ0hvJzOClGNVuniibdOpvCYYSbxVe2NV+1DPnEjbX7ZHp1V4pdWSmS9Du5GuWBcguw4l+iRLauaLP5/wCZfjRPo77hKr27BF1ubNylRXmKdNqkDDzARGArMN6CVArp2QTXUYLEcFaEK7xB/VIJrMGAW5Fs5pN12ZIwlzlr+RWt7SE/vNfTcWtgwMqHZVdz6q3p9oqrJK4qN/jwV9H/7klklD5r4GnjmR+p3G5Fy7xbTcJPAeNBpxWYVhf6cGI0usv1jy3aqThsPeeVwJGhjYNf/T+JjDIn7/q7Pz6IwaOzjQut3fBfT1PSqE4wqeSNVpOqORQidP58/KYZ45RVOFwygEOqF6b29UfaahKbzs14uK22ufa7FEwynbY3fV9A30fyr16q2pkopC/rs5kngXqjULWtK0oydJV7yZ4ty55LVxE/OvXjSZnJTuGRjPCQPavn27uPhFbqkB1gVV+QatGeOhI0ocxOW00I5ejTT5FBqKKoQdIrh8sgRjSy2EBcSsGOPBmmDqHPxB3d8W0P7c6tdW/HS+/ZjB+IEBDlVfiL8EmptXXuW050yRBHr5qGL+i1UlfAGzIvG0Pu30peiT6yNccaGtu8Slzr79y95j7nZDvWOg7YQBsQdW19WZ/vyatKRhf+8CLRU3CC9CeYkTxo/JJZVlueggmYg/jJk/tHQvGW9DKeJQTwFblmBHf8lhQ3MPAzptPiPT2qNv740aa9q6Uq//dndzPeBR34FBDa7xp3bcis3fy0krpEqWhQmE56eZRG6KyHPnOSXBXonBjAJ2eghzPzpwNBiW4YXNUa6s2C5LJvnrC77gfmVwfyd7fVKAWOfLtlNx6x7/w50toTvbmgMQjcoGL6JDiSTKimxkbFU+LSx0wZRqB8m38ySaoGyp9R+GYs4kixqaBeSE4NgxO5/fkANRoy4Q0TeomrYXMxNuycaV4umygmTSyE+m1FxM6xThcyUOu9nkcop4ZgjPL+YTKHSh547/MK5EMAzUn5bdUpfiPC6BJqOJ7103y/vEyQI58v6TBsQ6YPbLHav77u4NJe9r2Nkq9gQSVMyxUxE9STT6qQVPskyodMH18yrBky9BHA8gqBj1Y4EtFpRnwNiLsRtcfhxRVA78mCgMoQXNMhMOyYTuA/O5KHrrBKXQgFwHBZOFNwSBEslMqMNKCXodzHmlKJlcIq4aazf7uLpDMVzKsZ+9/OCMXx452c/y/TMBGnjR3a/Gr8Oc+O98zb3epoYeg3flEjvmkeV4mvR29mFwWSWzphXBzGllGIwXEYAGaQTFols8fvBQEE6QY8fI8aOyFoqR1X4vPQedUFeOQNFLJ8VoRUtoV7DIKkofhjaYbiMsPURUzMWv29QOT73YQJo78XcxqrI4svX6U5acgTmeEiDWya/eSkyIKNzSdEqfqcYTIJjNRgLjy8mESkOBKPG1hsAl8XDZnDKYNM6DsWeOsuxFRjHYAUyUJhwCTpyFJ9BuAYcFwIM/rmMHOP1BGaq8+BuykSZ2IqZf4rAiCJFgPp6+ucUHS1btRzsnjkvXaEbJu23fX+a+MTC54ahPGRAbxPItvQ4/SPcSg78j3yaa7aJOw2lKu8M6wXAqdPfEIOCLQ1mBGebO8ML4cjsqcuDSMlAZlxyuPEDB6A+MYYQV/BEKvj4dplUQKCkQMD9GaQoD+gwOhoLIwaa48ewLB8iWXQE8u6BRp1NYZZPkH7//7Nc7hwPK4D6GBdBAh09+GJ8DxHxfoVOcUY7WNgYEwR8zaCOmk+u7FejqTeFZQgVGF1np9PNsZFShCSSREBtmKVgKqAvBHPKjgkLdM/M8HmM9BFAtYbYCAKUSWjqTsHZDO3lvZxBUJY27ItnqkrgHNj31xVcOy+HASIavHlZAbFh3rG80Tyoq+pbbbr5zpFv4XJUbG3GCIcxAtWOa54Bfh/Y+DQIYIXRJBGpKzFiLkMQEcyqN553xrNAFZRzwJkLCSUo78QhMfWuSfNQQh1ZfCjQ5qVnN/Dsmoj9107cn/X1+DZ4TOY1l2AENjPU6PPx9zVj3l9yS8A2vxE93S1yZU2JnNQ//qieMwNrQjm5F6epP+uGPV4rRnqnIQ5DY3tSjwUGUuu5AEuKxVAZ3tQOSTdiIeurl393y7PuEDJ0tHXj/cNWnDdDgAd72fJtrWrV3AiqbqZiHr8EsRAkKSD6mnN2qTsxJ2RBYjszMU6UvrqTaA3pfJKH4MLZ8kNOVvdTQ9pjlgw1P3PnpQP/g95yO6zMCaOiB1wqVi2+QRpVLZrugmdD2g/betNxQ35yaNTeaeWn+/GMG+Ifu81zrOQLnCJwj8P+LwP8CaTGCYxH2NZgAAAAASUVORK5CYII=" diff --git a/dsh/plugins/dsh-mnemon-provider-openviking/src/index.ts b/dsh/plugins/dsh-mnemon-provider-openviking/src/index.ts new file mode 100644 index 000000000..41ad52f93 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-openviking/src/index.ts @@ -0,0 +1,24 @@ +import { MEMORY_SPACE_PROVIDER_API_VERSION, defineMemorySpaceProvider, defineMemorySpaceProviderDefinition } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { OpenVikingProvider } from './driver.ts' +import { descriptor } from './descriptor.ts' + +export { OpenVikingProvider, descriptor } + +export const definition = defineMemorySpaceProviderDefinition({ + manifest: { + apiVersion: MEMORY_SPACE_PROVIDER_API_VERSION, kind: 'provider', + typeId: descriptor.id, packageName: 'dsh-mnemon-provider-openviking', version: '0.5.7', + label: descriptor.label, icon: descriptor.icon, summary: descriptor.summary, + ...(descriptor.summaryI18nKey === undefined ? {} : { summaryI18nKey: descriptor.summaryI18nKey }), + origin: descriptor.origin, locality: descriptor.kind, workspaceBinding: descriptor.workspaceBinding, + capabilities: descriptor.capabilities, fields: descriptor.fields, + secrets: descriptor.fields.filter(field => field.input === 'secret').map(field => field.key), + scoreSemantics: 'normalized-relevance', + }, + create: context => new OpenVikingProvider(context.memorySpaces ?? context.memoryBodies, { requestTimeoutMs: context.config.timeoutMs }), +}) + +export default defineMemorySpaceProvider({ + id: descriptor.id, + apply(ctx, host) { host.install(ctx, definition) }, +}) diff --git a/dsh/plugins/dsh-mnemon-provider-openviking/tests/driver.spec.ts b/dsh/plugins/dsh-mnemon-provider-openviking/tests/driver.spec.ts new file mode 100644 index 000000000..95ac3a761 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-openviking/tests/driver.spec.ts @@ -0,0 +1,184 @@ +import { mkdtempSync, rmSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { afterEach, describe, expect, it, vi } from 'vitest' +import { createMemorySpaceProviderFixture } from 'dsh-mnemon-source-memory-spaces/testing' +import { OpenVikingProvider, descriptor } from '../src/index.ts' + +const temporaryDirectories: string[] = [] +afterEach(() => { for (const directory of temporaryDirectories.splice(0)) rmSync(directory, { recursive: true, force: true }) }) + +async function bodyAndRegistry(fetchMock: typeof fetch) { + const dataDir = mkdtempSync(join(tmpdir(), 'mnemon-openviking-driver-')) + temporaryDirectories.push(dataDir) + const { authority, body } = createMemorySpaceProviderFixture(descriptor, { + endpoint: 'https://memory.example.com', targetUri: 'viking://user/team/memories', apiKey: 'private-key', + account: 'acme', user: 'grivn', actorPeerId: 'dsh-workbench', + }, { dataDir, instanceId: 'work-account' }) + return { body, provider: new OpenVikingProvider(authority, { fetch: fetchMock, requestTimeoutMs: 1_000 }) } +} + +function ok(result: unknown): Response { return new Response(JSON.stringify({ status: 'ok', result }), { status: 200, headers: { 'Content-Type': 'application/json' } }) } + +describe('standalone OpenViking data plane', () => { + it('does not start discovery after cancellation', async () => { + const fetchMock = vi.fn() + const controller = new AbortController() + controller.abort(new Error('workspace changed')) + const { provider } = await bodyAndRegistry(fetchMock) + + await expect(provider.discover({ endpoint: 'https://memory.example.com', apiKey: 'private-key', account: 'acme' }, controller.signal)).rejects.toThrow('workspace changed') + expect(fetchMock).not.toHaveBeenCalled() + }) + + it('discovers every registered user namespace in the configured account', async () => { + const fetchMock = vi.fn(async (url) => { + expect(new URL(String(url)).pathname).toBe('/api/v1/admin/accounts/acme/users') + return ok([ + { user_id: 'alice', display_name: 'Alice', description: 'Product lead memory.' }, + { user_id: 'bob', name: 'Bob', role: 'Engineer' }, + { user_id: '../foreign', display_name: 'Unsafe namespace' }, + { user_id: '%2e%2e', display_name: 'Encoded unsafe namespace' }, + { user_id: 'a@b@c', display_name: 'Invalid identity' }, + ]) + }) + const { provider } = await bodyAndRegistry(fetchMock) + + await expect(provider.discover({ endpoint: 'https://memory.example.com', apiKey: 'private-key', account: 'acme' })).resolves.toEqual([ + { externalId: 'acme:alice', name: 'Alice', description: 'Product lead memory.', connection: { targetUri: 'viking://user/alice/memories', user: 'alice', actorPeerId: 'dsh' } }, + { externalId: 'acme:bob', name: 'Bob', description: 'Engineer', connection: { targetUri: 'viking://user/bob/memories', user: 'bob', actorPeerId: 'dsh' } }, + ]) + }) + + it('scopes semantic retrieval to the configured memory URI and keeps credentials in host headers', async () => { + const requests: Array<{ url: string; init?: RequestInit }> = [] + const fetchMock = vi.fn(async (url, init) => { + requests.push({ url: String(url), ...(init === undefined ? {} : { init }) }) + if (new URL(String(url)).pathname === '/api/v1/content/read') return ok('用户偏好简洁中文回答。') + return ok({ + memories: [{ + uri: 'viking://user/team/memories/preferences/style.md', + abstract: '用户偏好简洁中文回答。', + score: 0.86, + }], + }) + }) + const { body, provider } = await bodyAndRegistry(fetchMock) + + const result = await provider.search(body, { query: '回答风格', limit: 8 }) + + expect(result.results).toEqual([expect.objectContaining({ + id: 'viking://user/team/memories/preferences/style.md', + externalUri: 'viking://user/team/memories/preferences/style.md', + content: '用户偏好简洁中文回答。', + category: 'preferences', + source: 'external', + score: 0.86, + })]) + expect(requests[0]?.url).toBe('https://memory.example.com/api/v1/search/find') + expect(JSON.parse(String(requests[0]?.init?.body))).toMatchObject({ + query: '回答风格', + target_uri: 'viking://user/team/memories', + context_type: ['memory'], + limit: 8, + }) + expect(new Headers(requests[0]?.init?.headers).get('Authorization')).toBe('Bearer private-key') + expect(new Headers(requests[0]?.init?.headers).get('X-OpenViking-Account')).toBe('acme') + expect(new Headers(requests[0]?.init?.headers).get('X-OpenViking-Actor-Peer')).toBe('dsh-workbench') + }) + + it('browses remote memory markdown without exposing OpenViking system files', async () => { + const fetchMock = vi.fn(async url => new URL(String(url)).pathname === '/api/v1/content/read' ? ok('偏好简洁回答。') : ok([ + { uri: 'viking://user/team/memories/preferences/style.md', isDir: false, abstract: '偏好简洁回答。', modTime: '2026-08-16T00:00:00Z' }, + { uri: 'viking://user/team/memories/preferences', isDir: true }, + { uri: 'viking://user/team/memories/preferences/.abstract.md', isDir: false, abstract: 'system summary' }, + { uri: 'viking://user/team/memories/.meta.json', isDir: false }, + ])) + const { body, provider } = await bodyAndRegistry(fetchMock) + + await expect(provider.list(body, { limit: 10 })).resolves.toEqual([ + expect.objectContaining({ content: '偏好简洁回答。', category: 'preferences' }), + ]) + expect(String(fetchMock.mock.calls[0]?.[0])).toContain('recursive=true') + }) + + it('writes the memory through the content API and returns an exact write receipt', async () => { + const requests: Array<{ path: string; body: Record }> = [] + const fetchMock = vi.fn(async (url, init) => { + const path = new URL(String(url)).pathname + if (path === '/api/v1/content/read') return ok('发布必须先通过灰度验证。') + const input = JSON.parse(String(init?.body)) as Record + requests.push({ path, body: input }) + if (path === '/api/v1/content/write') { + return ok({ + uri: input.uri, + root_uri: 'viking://user/team/memories/experiences', + context_type: 'memory', + mode: 'create', + written_bytes: Buffer.byteLength(String(input.content)), + content_updated: true, + semantic_status: 'skipped', + vector_status: 'complete', + }) + } + throw new Error(`unexpected path ${path}`) + }) + const { body, provider } = await bodyAndRegistry(fetchMock) + + await expect(provider.remember(body, { content: '发布必须先通过灰度验证。', category: 'decision', importance: 4 })).resolves.toMatchObject({ + action: 'stored', + provider: 'openviking', + vectorStatus: 'complete', + writtenBytes: Buffer.byteLength('发布必须先通过灰度验证。'), + }) + + expect(requests).toHaveLength(1) + expect(requests[0]?.path).toBe('/api/v1/content/write') + expect(requests[0]?.body).toMatchObject({ + mode: 'create', + wait: true, + content: '发布必须先通过灰度验证。', + tags: ['source=mnemon', 'category=decision', 'importance=4'], + }) + expect(String(requests[0]?.body.uri)).toMatch(/^viking:\/\/user\/team\/memories\/experiences\/.*\.md$/u) + }) + + it('routes each memory category into its OpenViking memory folder', async () => { + const uris: string[] = [] + const files = new Map() + const fetchMock = vi.fn(async (url, init) => { + const parsed = new URL(String(url)) + if (parsed.pathname === '/api/v1/content/read') return ok(files.get(parsed.searchParams.get('uri')!)) + const request = JSON.parse(String(init?.body)) as { uri: string; content: string } + uris.push(request.uri) + files.set(request.uri, request.content) + return ok({ uri: request.uri, mode: 'create', context_type: 'memory', content_updated: true, + written_bytes: Buffer.byteLength(request.content), semantic_status: 'skipped', vector_status: 'complete' }) + }) + const { body, provider } = await bodyAndRegistry(fetchMock) + + await provider.remember(body, { content: '偏好简洁回答。', category: 'preference' }) + await provider.remember(body, { content: '发布必须先通过灰度验证。', category: 'decision' }) + await provider.remember(body, { content: 'ServerKit 使用 Go 后端。', category: 'fact' }) + await provider.remember(body, { content: 'Context', category: 'context' }) + await provider.remember(body, { content: 'Insight', category: 'insight' }) + await provider.remember(body, { content: 'General', category: 'general' }) + + expect(uris.map(uri => uri.split('/').at(-2))).toEqual(['preferences', 'experiences', 'entities', 'events', 'experiences', 'entities']) + }) + + it('forgets only an exact non-generated memory file inside the configured root', async () => { + const fetchMock = vi.fn(async () => ok({ uri: 'viking://user/team/memories/preferences/style.md', estimated_deleted_count: 1 })) + const { body, provider } = await bodyAndRegistry(fetchMock) + + await expect(provider.forget(body, 'viking://user/team/memories/preferences/style.md')).resolves.toMatchObject({ + action: 'deleted', + provider: 'openviking', + estimatedDeletedCount: 1, + }) + expect(String(fetchMock.mock.calls[0]?.[0])).toContain('/api/v1/fs?') + expect(fetchMock.mock.calls[0]?.[1]?.method).toBe('DELETE') + await expect(provider.forget(body, 'viking://user/team/memories/.overview.md')).rejects.toThrow(/exact non-generated/u) + await expect(provider.forget(body, 'viking://user/other/memories/fact.md')).rejects.toThrow(/inside this Memory Space/u) + }) +}) diff --git a/dsh/plugins/dsh-mnemon-provider-openviking/tests/integration.spec.ts b/dsh/plugins/dsh-mnemon-provider-openviking/tests/integration.spec.ts new file mode 100644 index 000000000..729613b44 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-openviking/tests/integration.spec.ts @@ -0,0 +1,45 @@ +import { randomUUID } from 'node:crypto' +import { writeFileSync } from 'node:fs' +import { describe, expect, it } from 'vitest' +import { createMemorySpaceProviderFixture } from 'dsh-mnemon-source-memory-spaces/testing' +import { OpenVikingProvider, descriptor } from '../src/index.ts' + +const endpoint = process.env.MNEMON_OPENVIKING_TEST_ENDPOINT +const apiKey = process.env.MNEMON_OPENVIKING_TEST_API_KEY ?? '' + +describe.skipIf(endpoint === undefined)('published OpenViking loopback integration', () => { + it('creates, reads, searches, browses, and deletes an exact synthetic memory', async () => { + if (!['127.0.0.1', '[::1]', 'localhost'].includes(new URL(endpoint!).hostname)) throw new Error('Use a disposable loopback OpenViking backend') + const canary = `OpenViking integration ${randomUUID()}: release approval requires a completed canary.\n保留完整中文和换行。` + const requests: Array<{ method: string; path: string }> = [] + const requestFetch: typeof fetch = async (url, init) => { + requests.push({ method: init?.method ?? 'GET', path: new URL(String(url)).pathname }) + return fetch(url, init) + } + const { authority, body } = createMemorySpaceProviderFixture(descriptor, { + endpoint: endpoint!, apiKey, targetUri: 'viking://user/default/memories', user: 'default', account: 'default', actorPeerId: 'dsh', + }, { dataDir: '/unused', instanceId: 'isolated-openviking' }) + const provider = new OpenVikingProvider(authority, { fetch: requestFetch, requestTimeoutMs: 30_000 }) + let receipt: { id: string; action: string; vectorStatus: string } | undefined + let removed: unknown + try { + receipt = await provider.remember(body, { content: canary, category: 'decision', importance: 4 }) as typeof receipt + expect(receipt).toMatchObject({ action: 'stored', vectorStatus: 'complete' }) + const readback = await fetch(`${endpoint}/api/v1/content/read?${new URLSearchParams({ uri: receipt!.id })}`, { + headers: apiKey ? { Authorization: `Bearer ${apiKey}` } : {}, + }).then(response => response.json()) as { result: string } + expect(readback.result).toBe(canary) + const recalled = await provider.search(body, { query: canary, limit: 20 }) + expect(recalled.results).toContainEqual(expect.objectContaining({ id: receipt!.id, content: canary })) + const listed = await provider.list(body, { limit: 200 }) + expect(listed).toContainEqual(expect.objectContaining({ id: receipt!.id, content: canary })) + expect(requests.some(request => request.path.includes('/sessions'))).toBe(false) + } finally { + if (receipt !== undefined) removed = await provider.forget(body, receipt.id) + } + expect(removed).toMatchObject({ action: 'deleted', uri: receipt!.id }) + expect((await provider.list(body, { limit: 200 })).some(item => item.id === receipt!.id)).toBe(false) + const reportPath = process.env.MNEMON_OPENVIKING_TEST_REPORT + if (reportPath) writeFileSync(reportPath, JSON.stringify({ endpoint, canary, receipt, removed, exactReadback: true, recalled: true, browsed: true, deleted: true, requests }, null, 2)) + }, 60_000) +}) diff --git a/dsh/plugins/dsh-mnemon-provider-openviking/tests/provider.spec.ts b/dsh/plugins/dsh-mnemon-provider-openviking/tests/provider.spec.ts new file mode 100644 index 000000000..510a5fc68 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-openviking/tests/provider.spec.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from 'vitest' +import type { MemorySpaceAuthority } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { mountMemorySpaceProvider } from 'dsh-mnemon-source-memory-spaces/testing' +import module, { definition, OpenVikingProvider, descriptor } from '../src/index.ts' + +describe('independent openviking Provider', () => { + it('installs complete immutable metadata in one parent-owned child', async () => { + const mounted = await mountMemorySpaceProvider(module, { instanceId: 'work', config: undefined }) + try { + expect(mounted.descriptor).toMatchObject({ id: 'work', typeId: 'openviking', label: descriptor.label }) + expect(Object.isFrozen(mounted.manifest)).toBe(true) + const authority: MemorySpaceAuthority = { runner: { effectiveDataDir: () => '/unused' }, list: () => [], providerConnection: () => ({}) } + expect(mounted.createAdapter({ memoryBodies: authority, config: { timeoutMs: 200 } }).id).toBe('work') + expect(mounted.registered).toBe(true) + expect(definition.manifest.secrets).toEqual(descriptor.fields.filter(field => field.input === 'secret').map(field => field.key).sort()) + } finally { await mounted.dispose() } + expect(mounted.registered).toBe(false) + }) + + it('constructs its own driver from a structural authority, without the main repository', () => { + const authority: MemorySpaceAuthority = { runner: { effectiveDataDir: () => '/unused' }, list: () => [], providerConnection: () => ({}) } + const adapter = definition.create({ memoryBodies: authority, config: { timeoutMs: 200 }, providerInstanceId: 'openviking', manifest: definition.manifest }) + expect(adapter).toBeInstanceOf(OpenVikingProvider) + expect(adapter.id).toBe('openviking') + }) +}) diff --git a/dsh/plugins/dsh-mnemon-provider-openviking/tests/user-discovery.spec.ts b/dsh/plugins/dsh-mnemon-provider-openviking/tests/user-discovery.spec.ts new file mode 100644 index 000000000..aed4f1e92 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-openviking/tests/user-discovery.spec.ts @@ -0,0 +1,125 @@ +import { randomUUID } from 'node:crypto' +import { describe, expect, it, vi } from 'vitest' +import { createMemorySpaceProviderFixture } from 'dsh-mnemon-source-memory-spaces/testing' +import { OpenVikingProvider, descriptor } from '../src/index.ts' + +const service = { endpoint: 'https://memory.example/openviking', account: 'work', discoveryUser: 'alice', apiKey: randomUUID() } +const root = 'viking://user/alice/memories' +const ok = (result: unknown) => new Response(JSON.stringify({ status: 'ok', result }), { status: 200 }) +const denied = () => new Response(JSON.stringify({ status: 'error', error: { message: 'API is currently unavailable due to access restrictions' } }), { status: 403 }) +function provider(fetchMock: typeof fetch) { + return new OpenVikingProvider({ runner: { effectiveDataDir: () => '/unused' }, list: () => [], providerConnection: () => ({}) }, { fetch: fetchMock, requestTimeoutMs: 30 }) +} + +describe('OpenViking explicit user-key discovery', () => { + it('uses the same safe owner pattern in Host and modern HTML form validation', () => { + const field = descriptor.fields.find(item => item.key === 'discoveryUser')! + for (const flags of ['u', 'v']) { + const pattern = new RegExp(field.pattern!, flags) + expect(pattern.test('alice-team@example.com')).toBe(true) + for (const user of ['..', '../bob', 'a@b@c']) expect(pattern.test(user)).toBe(false) + } + }) + + it('discovers the explicit owner through the data plane when every admin route is denied', async () => { + const fetchMock = vi.fn(async (input, init) => { + const url = new URL(String(input)) + if (url.pathname.includes('/admin/')) return denied() + expect(url.pathname).toBe('/openviking/api/v1/fs/ls') + expect(url.searchParams.get('uri')).toBe(root) + expect(url.searchParams.get('recursive')).toBe('false') + expect(url.searchParams.get('output')).toBe('original') + const headers = new Headers(init?.headers) + expect(headers.get('Authorization')).toBe(`Bearer ${service.apiKey}`) + expect(headers.has('X-OpenViking-Account')).toBe(false) + expect(headers.has('X-OpenViking-User')).toBe(false) + return ok([]) + }) + await expect(provider(fetchMock).discover(service)).resolves.toEqual([{ + externalId: 'work:alice', name: 'alice', description: 'OpenViking memory namespace for alice', + connection: { targetUri: root, user: 'alice', actorPeerId: 'dsh' }, + }]) + expect(fetchMock).toHaveBeenCalledTimes(1) + }) + + it.each([ + { account: '' }, { account: 'work:foreign' }, { discoveryUser: '../foreign' }, + { discoveryUser: 'a@b@c' }, { discoveryUser: '.' }, { discoveryUser: '%2e%2e' }, { apiKey: '' }, + ])('rejects incomplete or unsafe explicit configuration before I/O: %j', async change => { + const fetchMock = vi.fn() + await expect(provider(fetchMock).discover({ ...service, ...change })).rejects.toThrow(/OpenViking/) + expect(fetchMock).not.toHaveBeenCalled() + }) + + it.each([{}, null, 'healthy', [{ uri: 'viking://user/other/memories/private.md' }], [{ name: 'missing-uri' }]])('rejects an invalid data-plane response: %j', async result => { + const fetchMock = vi.fn(async () => ok(result)) + await expect(provider(fetchMock).discover(service)).rejects.toThrow('OpenViking could not validate the selected memory namespace') + expect(fetchMock).toHaveBeenCalledTimes(1) + }) + + it('keeps a denied namespace denied without an admin or health fallback', async () => { + const fetchMock = vi.fn(async () => denied()) + await expect(provider(fetchMock).discover(service)).rejects.toThrow('access restrictions') + expect(fetchMock).toHaveBeenCalledTimes(1) + expect(String(fetchMock.mock.calls[0]?.[0])).toContain('/fs/ls?') + }) + + it('honors cancellation and timeout on the explicit probe', async () => { + const fetchMock = vi.fn(async (_url, init) => new Promise((_resolve, reject) => { + init?.signal?.addEventListener('abort', () => reject(init.signal?.reason), { once: true }) + })) + const controller = new AbortController() + controller.abort(new Error('scope changed')) + await expect(provider(fetchMock).discover(service, controller.signal)).rejects.toThrow('scope changed') + expect(fetchMock).not.toHaveBeenCalled() + await expect(provider(fetchMock).discover(service)).rejects.toThrow('timed out') + }) + + it('retains key-bound headers for browse, search, status, write, readback and exact deletion', async () => { + let written = '' + const content = 'Synthetic user-key canary.' + const fetchMock = vi.fn(async (input, init) => { + const url = new URL(String(input)) + const headers = new Headers(init?.headers) + expect(headers.get('Authorization')).toBe(`Bearer ${service.apiKey}`) + expect(headers.has('X-OpenViking-Account')).toBe(false) + expect(headers.has('X-OpenViking-User')).toBe(false) + expect(url.pathname).not.toMatch(/admin|system|health/) + if (url.pathname.endsWith('/content/write')) { + const request = JSON.parse(String(init?.body)) + written = request.uri + return ok({ uri: written, content_updated: true, written_bytes: Buffer.byteLength(content), mode: 'create', context_type: 'memory', vector_status: 'complete', semantic_status: 'skipped' }) + } + if (url.pathname.endsWith('/content/read')) return ok(content) + if (url.pathname.endsWith('/search/find')) return ok({ memories: [{ uri: written, score: 1 }] }) + if (init?.method === 'DELETE') return ok({ uri: written }) + return ok(written ? [{ uri: written, isDir: false }] : []) + }) + const { authority, body } = createMemorySpaceProviderFixture(descriptor, { ...service, user: 'alice', targetUri: root }, { dataDir: '/unused', instanceId: 'cloud' }) + const adapter = new OpenVikingProvider(authority, { fetch: fetchMock }) + expect(await adapter.status(body)).toEqual({ healthy: true }) + expect(await adapter.remember(body, { content })).toMatchObject({ action: 'stored', id: written }) + expect((await adapter.list(body, {}))[0]?.content).toBe(content) + expect((await adapter.search(body, { query: 'canary', limit: 5 })).results[0]?.content).toBe(content) + expect(await adapter.forget(body, written)).toMatchObject({ action: 'deleted', id: written }) + }) + + it.each([ + { targetUri: 'viking://user/bob/memories', user: 'alice' }, + { targetUri: root, user: 'bob' }, + ])('rejects a memory owner different from the explicit service scope: %j', async memory => { + const fetchMock = vi.fn() + const { authority, body } = createMemorySpaceProviderFixture(descriptor, { ...service, ...memory }, { dataDir: '/unused', instanceId: 'cloud' }) + const adapter = new OpenVikingProvider(authority, { fetch: fetchMock }) + await expect(adapter.search(body, { query: 'canary', limit: 5 })).rejects.toThrow('configured discovery user') + expect(fetchMock).not.toHaveBeenCalled() + }) + + it('retains account enumeration and user enumeration when the explicit user is empty', async () => { + const fetchMock = vi.fn(async input => String(input).endsWith('/admin/accounts') ? ok([{ account_id: 'work' }]) : ok([{ user_id: 'alice' }, { user_id: 'bob' }])) + expect(await provider(fetchMock).discover({ endpoint: service.endpoint, discoveryUser: '' })).toHaveLength(2) + expect(fetchMock.mock.calls.map(([input]) => new URL(String(input)).pathname)).toEqual([ + '/openviking/api/v1/admin/accounts', '/openviking/api/v1/admin/accounts/work/users', + ]) + }) +}) diff --git a/dsh/plugins/dsh-mnemon-provider-openviking/tests/write-regression.spec.ts b/dsh/plugins/dsh-mnemon-provider-openviking/tests/write-regression.spec.ts new file mode 100644 index 000000000..6b86e7da8 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-openviking/tests/write-regression.spec.ts @@ -0,0 +1,160 @@ +import { describe, expect, it, vi } from 'vitest' +import { createMemorySpaceProviderFixture } from 'dsh-mnemon-source-memory-spaces/testing' +import { OpenVikingProvider, descriptor } from '../src/index.ts' + +function ok(result: unknown): Response { + return new Response(JSON.stringify({ status: 'ok', result }), { status: 200 }) +} + +function writeFixture(options: { + response?: Record; readback?: string; targetUri?: string; user?: string; + requestTimeoutMs?: number; fetch?: typeof fetch; +} = {}) { + const files = new Map() + const fetchMock = vi.fn(options.fetch ?? (async (url, init) => { + const path = new URL(String(url)) + if (path.pathname === '/api/v1/system/status') return ok({ initialized: true, user: 'resolved-owner' }) + if (path.pathname === '/api/v1/content/read') return ok(options.readback ?? files.get(path.searchParams.get('uri')!)) + const input = JSON.parse(String(init?.body)) as { uri: string; content: string } + files.set(input.uri, input.content) + return ok({ uri: input.uri, mode: 'create', context_type: 'memory', content_updated: true, + written_bytes: Buffer.byteLength(input.content), semantic_status: 'skipped', vector_status: 'complete', + ...options.response }) + })) + const { authority, body } = createMemorySpaceProviderFixture(descriptor, { + endpoint: 'https://memory.example', targetUri: options.targetUri ?? 'viking://user/team/memories', + user: options.user ?? '', account: 'work', apiKey: 'synthetic-only-key', actorPeerId: 'synthetic-actor', + }, { dataDir: '/unused', instanceId: 'work-account' }) + return { body, authority, files, fetchMock, provider: new OpenVikingProvider(authority, { fetch: fetchMock, requestTimeoutMs: options.requestTimeoutMs ?? 1_000 }) } +} + +describe('OpenViking issue 233 durable writes', () => { + it('stores the requested text even when one-turn extraction makes no durable change', async () => { + const files = new Map() + const fetchMock = vi.fn(async (url, init) => { + const parsed = new URL(String(url)) + const request = JSON.parse(String(init?.body ?? '{}')) as Record + if (parsed.pathname === '/api/v1/content/write') { + files.set(String(request.uri), String(request.content)) + return ok({ uri: request.uri, mode: 'create', context_type: 'memory', content_updated: true, written_bytes: Buffer.byteLength(String(request.content)), semantic_status: 'skipped', vector_status: 'complete' }) + } + if (parsed.pathname === '/api/v1/content/read') return ok(files.get(parsed.searchParams.get('uri') ?? '')) + // Issue 233: successful extraction can report a cosmetic update without + // ever persisting the caller's candidate. Counters are not write proof. + if (parsed.pathname === '/api/v1/sessions') return ok({ session_id: request.session_id }) + if (parsed.pathname.endsWith('/messages')) return ok({ message_id: 'message-1' }) + if (parsed.pathname.endsWith('/commit')) return ok({ task_id: 'task-1' }) + if (parsed.pathname === '/api/v1/tasks/task-1') return ok({ status: 'completed', result: { memories_extracted: { memory_update: 1 } } }) + throw new Error(`Unexpected request: ${parsed.pathname}`) + }) + const { authority, body } = createMemorySpaceProviderFixture(descriptor, { + endpoint: 'https://memory.example', targetUri: 'viking://user/team/memories', user: 'team', + }, { dataDir: '/unused', instanceId: 'work-account' }) + const provider = new OpenVikingProvider(authority, { fetch: fetchMock }) + const content = 'Release approvals require a completed canary.\n保留原文和换行。' + const receipt = await provider.remember(body, { content, category: 'decision' }) + + expect(receipt).toMatchObject({ action: 'stored' }) + expect([...files.values()]).toEqual([content]) + expect(receipt).toMatchObject({ id: [...files.keys()][0], uri: [...files.keys()][0] }) + expect(fetchMock.mock.calls.some(([url]) => String(url).includes('/sessions'))).toBe(false) + }) + + it.each([ + ['pending vectors', { vector_status: 'queued' }], + ['failed vectors', { vector_status: 'failed' }], + ['missing vector evidence', { vector_status: undefined }], + ['failed semantics', { semantic_status: 'failed' }], + ['missing semantic evidence', { semantic_status: undefined }], + ['no content update', { content_updated: false }], + ['wrong byte count', { written_bytes: 0 }], + ['foreign URI', { uri: 'viking://user/other/memories/entities/foreign.md' }], + ['another file in the same root', { uri: 'viking://user/team/memories/entities/other.md' }], + ['replacement instead of creation', { mode: 'replace' }], + ['wrong content type', { context_type: 'resource' }], + ['queue errors despite complete flags', { queue_status: { Embedding: { error_count: 1, errors: [] } } }], + ['reported queue error', { queue_status: { Semantic: { error_count: 0, errors: ['synthetic failure'] } } }], + ])('does not report a committed write for %s', async (_name, response) => { + const f = writeFixture({ response }) + await expect(f.provider.remember(f.body, { content: 'Exact canary', category: 'fact' })).rejects.toThrow(/not verified.*remote content may remain/u) + expect(f.fetchMock).toHaveBeenCalledTimes(1) + // An ambiguous response must never delete an unrelated target or retry. + expect(f.fetchMock.mock.calls[0]?.[1]?.method).toBe('POST') + }) + + it('requires exact readback even after completed indexing', async () => { + const f = writeFixture({ readback: 'A summary is not the exact original.' }) + await expect(f.provider.remember(f.body, { content: 'Exact original', category: 'fact' })).rejects.toThrow('stored content does not match') + expect(f.fetchMock).toHaveBeenCalledTimes(2) + }) + + it.each(['constructor', '__proto__'])('maps inherited object names to the safe default category folder: %s', async category => { + const f = writeFixture() + const request = { content: 'Unknown category', category } as unknown as Parameters[1] + expect(await f.provider.remember(f.body, request)).toMatchObject({ id: expect.stringContaining('/memories/entities/') }) + }) + + it.each([404, 409, 500])('does not retry, extract, or delete after HTTP %s', async status => { + const f = writeFixture({ fetch: async () => new Response(JSON.stringify({ status: 'error', error: { message: `Synthetic HTTP ${status}` } }), { status }) }) + await expect(f.provider.remember(f.body, { content: 'Canary', category: 'fact' })).rejects.toThrow(`Synthetic HTTP ${status}`) + expect(f.fetchMock).toHaveBeenCalledTimes(1) + }) + + it('rejects an empty successful HTTP response', async () => { + const f = writeFixture({ fetch: async () => new Response('', { status: 200 }) }) + await expect(f.provider.remember(f.body, { content: 'Canary', category: 'fact' })).rejects.toThrow('did not confirm') + }) + + it('honors the configured timeout without extending it to a minute', async () => { + const f = writeFixture({ requestTimeoutMs: 10, fetch: async (_url, init) => new Promise((_resolve, reject) => { + init?.signal?.addEventListener('abort', () => reject(init.signal?.reason), { once: true }) + }) }) + await expect(f.provider.remember(f.body, { content: 'Canary', category: 'fact' })).rejects.toThrow('timed out after 10ms') + expect(f.fetchMock).toHaveBeenCalledTimes(1) + expect(JSON.parse(String(f.fetchMock.mock.calls[0]?.[1]?.body))).toMatchObject({ timeout: 0.01, mode: 'create', wait: true }) + }) + + it('does not begin a write after cancellation', async () => { + const f = writeFixture() + const signal = AbortSignal.abort(new Error('Turn was cancelled')) + await expect(f.provider.remember(f.body, { content: 'Canary', category: 'fact' }, signal)).rejects.toThrow('Turn was cancelled') + expect(f.fetchMock).not.toHaveBeenCalled() + }) + + it.each(['configured-owner', ''])('resolves legacy shorthand using configured or authenticated user %j', async user => { + const f = writeFixture({ targetUri: 'viking://user/memories', user }) + const owner = user || 'resolved-owner' + const receipt = await f.provider.remember(f.body, { content: 'Canary', category: 'fact' }) + expect(receipt).toMatchObject({ id: expect.stringContaining(`viking://user/${owner}/memories/entities/`) }) + const requests = f.fetchMock.mock.calls + expect(requests.some(([url]) => String(url).includes('/system/status'))).toBe(user === '') + for (const [, init] of requests) { + expect(new Headers(init?.headers).get('Authorization')).toBe('Bearer synthetic-only-key') + expect(new Headers(init?.headers).get('X-OpenViking-Account')).toBe('work') + expect(new Headers(init?.headers).get('X-OpenViking-Actor-Peer')).toBe('synthetic-actor') + } + }) + + it.each(['viking://user/../memories', 'viking://user/%2e%2e/memories', 'viking://user/team?x/memories', 'viking://resources/memories'])('refuses unsafe persisted root %s before network access', async targetUri => { + const f = writeFixture() + const connection = f.authority.providerConnection(f.body.id, f.body.provider.id) + const provider = new OpenVikingProvider({ ...f.authority, providerConnection: () => ({ ...connection, targetUri }) }, { fetch: f.fetchMock }) + await expect(provider.remember(f.body, { content: 'Canary', category: 'fact' })).rejects.toThrow('safe viking://user') + expect(f.fetchMock).not.toHaveBeenCalled() + }) + + it.each([ + '../foreign.md', 'entities/../../other/memories/foreign.md', 'entities/%2e%2e/foreign.md', + 'entities/%252e%252e/foreign.md', 'entities/..\\foreign.md', 'entities//foreign.md', + '.private/secret.md', 'entities/.abstract.md', 'entities/foreign.md?query.md', 'entities/foreign.md#hash.md', + ])('refuses unsafe forget suffix %s before network access', async suffix => { + const f = writeFixture() + await expect(f.provider.forget(f.body, `viking://user/team/memories/${suffix}`)).rejects.toThrow('exact non-generated') + expect(f.fetchMock).not.toHaveBeenCalled() + }) + + it('never accepts another namespace deletion receipt', async () => { + const f = writeFixture({ fetch: async () => ok({ uri: 'viking://user/other/memories/entities/other.md', estimated_deleted_count: 1 }) }) + await expect(f.provider.forget(f.body, 'viking://user/team/memories/entities/canary.md')).rejects.toThrow('did not confirm deletion') + }) +}) diff --git a/dsh/plugins/dsh-mnemon-provider-openviking/tsconfig.json b/dsh/plugins/dsh-mnemon-provider-openviking/tsconfig.json new file mode 100644 index 000000000..badeba7ca --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-openviking/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2024", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "verbatimModuleSyntax": true, + "allowImportingTsExtensions": true, + "skipLibCheck": true, + "types": [ + "node" + ] + }, + "include": [ + "src/**/*.ts", + "tests/**/*.ts", + "tsdown.config.ts", + "vitest.config.ts" + ] +} diff --git a/dsh/plugins/dsh-mnemon-provider-openviking/tsdown.config.ts b/dsh/plugins/dsh-mnemon-provider-openviking/tsdown.config.ts new file mode 100644 index 000000000..2d868cbd8 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-openviking/tsdown.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'tsdown' + +export default defineConfig({ + entry: ['src/index.ts', 'src/descriptor.ts'], outDir: 'lib', format: 'esm', platform: 'node', + target: 'es2024', dts: true, clean: true, fixedExtension: false, deps: { neverBundle: true }, +}) diff --git a/dsh/plugins/dsh-mnemon-provider-openviking/vitest.config.ts b/dsh/plugins/dsh-mnemon-provider-openviking/vitest.config.ts new file mode 100644 index 000000000..5a6724c22 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-openviking/vitest.config.ts @@ -0,0 +1,3 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ test: { environment: 'node' } }) diff --git a/dsh/plugins/dsh-mnemon-provider-retaindb/LICENSE b/dsh/plugins/dsh-mnemon-provider-retaindb/LICENSE new file mode 100644 index 000000000..e32e112dc --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-retaindb/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 dsh-external contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dsh/plugins/dsh-mnemon-provider-retaindb/README.md b/dsh/plugins/dsh-mnemon-provider-retaindb/README.md new file mode 100644 index 000000000..3c8af0476 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-retaindb/README.md @@ -0,0 +1,27 @@ +# dsh-mnemon-provider-retaindb + +An adapter for RetainDB project/user-scoped memory. + +Requires a reachable endpoint, credentials and explicit project/user scope. + +## Use + +The default `dsh-mnemon` Starter already installs this package. Enable and configure the service on the `dsh-mnemon` page under **Plugins**, then inspect its synchronized Memory Spaces. + +For a custom composition, install this package alongside `dsh-mnemon-source-memory-spaces` and include it in that Source's `config.providers`: + +```yaml +providers: + - use: dsh-mnemon-provider-retaindb + instanceId: retaindb +``` + +This is a **Memory Spaces child module**, not a top-level Source or a complete Strategy. It registers through `dsh-mnemon-source-memory-spaces/provider-sdk`; each parent Source owns its child Fibers, connection settings and lifetime. Credentials stay on the Host. + +[Provider setup and capability matrix](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/guides/memory-providers.md) · [中文指南](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/zh-CN/guides/memory-providers.md) + +## Develop independently + +From a source checkout with the declared dependencies installed, run `pnpm verify`. Tests, build and public exports belong to this package, without importing another package's controllers or repository configuration. Use the Source's public `/testing` fixtures for child registration and driver conformance; live service tests require a separately authorized environment. + +[Plugin author guide](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/development/extensions.md) diff --git a/dsh/plugins/dsh-mnemon-provider-retaindb/package.json b/dsh/plugins/dsh-mnemon-provider-retaindb/package.json new file mode 100644 index 000000000..727a03a9a --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-retaindb/package.json @@ -0,0 +1,55 @@ +{ + "name": "dsh-mnemon-provider-retaindb", + "version": "0.5.5", + "description": "RetainDB Provider child for the Memory Spaces Source.", + "license": "MIT", + "type": "module", + "engines": { + "node": ">=20" + }, + "exports": { + ".": { + "types": "./lib/index.d.ts", + "default": "./lib/index.js" + }, + "./descriptor": { + "types": "./lib/descriptor.d.ts", + "default": "./lib/descriptor.js" + }, + "./package.json": "./package.json" + }, + "files": [ + "lib", + "README.md" + ], + "scripts": { + "typecheck": "tsc --noEmit", + "build": "tsdown", + "test": "vitest run", + "verify": "pnpm typecheck && pnpm test && pnpm build" + }, + "peerDependencies": { + "@deepseek-ai/cordis": "^4.0.1", + "dsh-mnemon-source-memory-spaces": "^0.5.1" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "dsh-mnemon-source-memory-spaces": "^0.5.11", + "tsdown": "^0.22.0", + "typescript": "^5.9.0", + "vitest": "^3.2.0" + }, + "publishConfig": { + "access": "public", + "tag": "latest" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/mnemon-dev/mnemon.git", + "directory": "dsh/plugins/dsh-mnemon-provider-retaindb" + }, + "homepage": "https://github.com/mnemon-dev/mnemon/tree/master/dsh/plugins/dsh-mnemon-provider-retaindb#readme", + "bugs": { + "url": "https://github.com/mnemon-dev/mnemon/issues" + } +} diff --git a/dsh/plugins/dsh-mnemon-provider-retaindb/src/descriptor.ts b/dsh/plugins/dsh-mnemon-provider-retaindb/src/descriptor.ts new file mode 100644 index 000000000..643f00dfd --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-retaindb/src/descriptor.ts @@ -0,0 +1,66 @@ +import type { MemoryProviderDescriptor } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { ICON } from './icon.ts' + +/** Browser-safe metadata owned by this Provider. */ +export const descriptor: MemoryProviderDescriptor = { + "id": "retaindb", + "label": "RetainDB", + "icon": { + "kind": "data-url", + "value": ICON + }, + "kind": "remote", + "workspaceBinding": "provider-global", + "summary": "Cloud memory with hybrid vector/BM25 retrieval, profiles, and typed durable facts.", + "summaryI18nKey": "overview.providerSummary.retaindb", + "origin": "third-party", + "capabilities": { + "search": true, + "browse": true, + "graph": false, + "entities": false, + "related": false, + "remember": true, + "link": false, + "forget": true, + "writeMode": "exact", + "deletionMode": "hard" + }, + "fields": [ + { + "key": "endpoint", + "label": "Endpoint", + "scope": "service", + "input": "url", + "required": true, + "defaultValue": "https://api.retaindb.com", + "i18nKey": "overview.providerEndpoint" + }, + { + "key": "apiKey", + "label": "API key", + "scope": "service", + "input": "secret", + "required": true, + "i18nKey": "overview.providerApiKey" + }, + { + "key": "project", + "label": "Project", + "scope": "memory", + "input": "text", + "required": true, + "defaultValue": "dsh", + "i18nKey": "overview.providerField.project" + }, + { + "key": "userId", + "label": "User ID", + "scope": "memory", + "input": "text", + "required": true, + "defaultValue": "dsh-user", + "i18nKey": "overview.providerField.userId" + } + ] +} diff --git a/dsh/plugins/dsh-mnemon-provider-retaindb/src/driver.ts b/dsh/plugins/dsh-mnemon-provider-retaindb/src/driver.ts new file mode 100644 index 000000000..117ec24fe --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-retaindb/src/driver.ts @@ -0,0 +1,139 @@ +import { descriptor } from './descriptor.ts' +import type { JsonValue } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import type { MemorySpaceAuthority } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import type { Insight, MemoryBody as MemorySpace, MemoryListRequest, RememberRequest, SearchRequest } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { HttpMemoryProvider, firstArray, jsonNumber, jsonObject, jsonString, type HttpProviderOptions } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { NORMALIZED_RELEVANCE_SCORE, type MemoryProviderAdapter, type ProviderBodyStatus as ProviderSpaceStatus, type ProviderMemorySpace, type ProviderSearchResult } from 'dsh-mnemon-source-memory-spaces/provider-sdk' + +function insight(value: unknown): Insight | undefined { + const item = jsonObject(value) + const id = jsonString(item?.id) ?? jsonString(item?.memory_id) + const content = jsonString(item?.content) ?? jsonString(item?.memory) ?? jsonString(item?.text) + if (id === undefined || content === undefined) return undefined + const score = jsonNumber(item?.score) ?? jsonNumber(item?.similarity) + const createdAt = jsonString(item?.created_at) ?? jsonString(item?.createdAt) ?? jsonString(item?.updated_at) + return { + id, + content, + category: jsonString(item?.memory_type) ?? jsonString(item?.category) ?? 'general', + source: 'external', + ...(score === undefined ? {} : { score }), + ...(createdAt === undefined ? {} : { createdAt }), + } +} + +export class RetainDbProvider extends HttpMemoryProvider implements MemoryProviderAdapter { + readonly id = 'retaindb' as const + readonly scoreSemantics = NORMALIZED_RELEVANCE_SCORE + + constructor(memorySpaces: MemorySpaceAuthority, options: HttpProviderOptions = {}) { + super(memorySpaces, { label: descriptor.label, ...options }) + } + + async discover(connection: Record, signal?: AbortSignal): Promise { + const payload = await this.requestConnection(connection, '/v1/projects', { headers: this.headers(connection, '/v1/projects'), signal }) + return firstArray(payload, 'projects', 'items').flatMap(value => { + const item = jsonObject(value) + const project = jsonString(item?.slug) ?? jsonString(item?.name) ?? jsonString(item?.id) + if (project === undefined) return [] + return [{ + externalId: jsonString(item?.id) ?? project, + name: jsonString(item?.name) ?? project, + description: jsonString(item?.description) ?? `RetainDB project ${project}`, + connection: { project, userId: '*' }, + }] + }) + } + + async status(body: MemorySpace, signal?: AbortSignal): Promise { + try { + await this.list(body, { limit: 1 }, signal) + return { healthy: true } + } catch (error) { + return { healthy: false, error: error instanceof Error ? error.message : String(error) } + } + } + + async search(body: MemorySpace, request: SearchRequest, signal?: AbortSignal): Promise { + const connection = this.connection(body) + const payload = await this.request(body, '/v1/memory/search', { + headers: this.headers(connection, '/v1/memory/search'), + json: { + project: String(connection.project), + query: request.query, + ...(String(connection.userId) === '*' ? {} : { user_id: String(connection.userId) }), + session_id: `dsh-${body.id}`, + top_k: request.limit ?? 10, + include_pending: true, + }, + signal, + }) + return { results: firstArray(payload, 'results', 'memories').map(insight).filter((item): item is Insight => item !== undefined) } + } + + async list(body: MemorySpace, request: MemoryListRequest, signal?: AbortSignal): Promise { + const connection = this.connection(body) + const params = new URLSearchParams({ project: String(connection.project), include_pending: 'true' }) + let payload: unknown + try { + if (String(connection.userId) === '*') throw new Error('project-wide scope uses the collection endpoint') + payload = await this.request(body, `/v1/memory/profile/${encodeURIComponent(String(connection.userId))}?${params}`, { + headers: this.headers(connection, '/v1/memory/profile'), + signal, + }) + } catch { + if (String(connection.userId) !== '*') params.set('user_id', String(connection.userId)) + params.set('limit', String(Math.min(Math.max(request.limit ?? 200, 1), 200))) + payload = await this.request(body, `/v1/memories?${params}`, { headers: this.headers(connection, '/v1/memories'), signal }) + } + return firstArray(payload, 'memories', 'results').map(insight).filter((item): item is Insight => item !== undefined) + .filter(item => request.category === undefined || item.category === request.category) + .slice(0, Math.min(Math.max(request.limit ?? 200, 1), 200)) + } + + async remember(body: MemorySpace, request: RememberRequest, signal?: AbortSignal): Promise { + const connection = this.connection(body) + const json = { + project: String(connection.project), + content: request.content, + memory_type: request.category ?? 'factual', + user_id: String(connection.userId) === '*' ? 'dsh-user' : String(connection.userId), + session_id: `dsh-${body.id}`, + importance: request.importance ?? 0.7, + write_mode: 'sync', + } as const + let payload: unknown + try { + payload = await this.request(body, '/v1/memory', { headers: this.headers(connection, '/v1/memory'), json, signal }) + } catch { + const { write_mode: _writeMode, ...legacy } = json + payload = await this.request(body, '/v1/memories', { headers: this.headers(connection, '/v1/memories'), json: legacy, signal }) + } + const result = jsonObject(payload) ?? {} + return { + action: 'stored', + provider: this.id, + summary: 'RetainDB stored the memory synchronously.', + ...(jsonString(result.id) === undefined ? {} : { id: jsonString(result.id)! }), + } + } + + async forget(body: MemorySpace, id: string, signal?: AbortSignal): Promise { + const connection = this.connection(body) + try { + await this.request(body, `/v1/memory/${encodeURIComponent(id)}`, { method: 'DELETE', headers: this.headers(connection, '/v1/memory'), signal }) + } catch { + await this.request(body, `/v1/memories/${encodeURIComponent(id)}`, { method: 'DELETE', headers: this.headers(connection, '/v1/memories'), signal }) + } + return { action: 'deleted', provider: this.id, id } + } + + private headers(connection: Record, path: string): HeadersInit { + const token = String(connection.apiKey ?? '').replace(/^Bearer\s+/iu, '') + return { + Authorization: `Bearer ${token}`, + 'x-sdk-runtime': 'dsh-mnemon', + ...(path.startsWith('/v1/memory') || path.startsWith('/v1/context') ? { 'X-API-Key': token } : {}), + } + } +} diff --git a/dsh/plugins/dsh-mnemon-provider-retaindb/src/icon.ts b/dsh/plugins/dsh-mnemon-provider-retaindb/src/icon.ts new file mode 100644 index 000000000..87ed36eaa --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-retaindb/src/icon.ts @@ -0,0 +1,2 @@ +/** Browser-safe brand asset shipped by this Provider, not by the Host. */ +export const ICON = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDgiIGhlaWdodD0iNDgiIHZpZXdCb3g9IjAgMCA0OCA0OCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8cmVjdCB3aWR0aD0iNDgiIGhlaWdodD0iNDgiIHJ4PSIxMSIgZmlsbD0iI0Y4RjVFRiIvPgogIDxyZWN0IHg9IjExIiB5PSI3IiAgd2lkdGg9IjI2IiBoZWlnaHQ9IjUuNSIgcng9IjIuNzUiIGZpbGw9IiMwRDBCMDkiLz4KICA8cmVjdCB4PSIxMSIgeT0iMTYiIHdpZHRoPSIyNiIgaGVpZ2h0PSI1LjUiIHJ4PSIyLjc1IiBmaWxsPSIjMEQwQjA5Ii8+CiAgPHJlY3QgeD0iMTEiIHk9IjI1IiB3aWR0aD0iMTYiIGhlaWdodD0iNS41IiByeD0iMi43NSIgZmlsbD0iIzBEMEIwOSIvPgogIDxyZWN0IHg9IjExIiB5PSIzNCIgd2lkdGg9IjI2IiBoZWlnaHQ9IjUuNSIgcng9IjIuNzUiIGZpbGw9IiMwRDBCMDkiLz4KPC9zdmc+Cg==" diff --git a/dsh/plugins/dsh-mnemon-provider-retaindb/src/index.ts b/dsh/plugins/dsh-mnemon-provider-retaindb/src/index.ts new file mode 100644 index 000000000..a7e99ace4 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-retaindb/src/index.ts @@ -0,0 +1,24 @@ +import { MEMORY_SPACE_PROVIDER_API_VERSION, defineMemorySpaceProvider, defineMemorySpaceProviderDefinition } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { RetainDbProvider } from './driver.ts' +import { descriptor } from './descriptor.ts' + +export { RetainDbProvider, descriptor } + +export const definition = defineMemorySpaceProviderDefinition({ + manifest: { + apiVersion: MEMORY_SPACE_PROVIDER_API_VERSION, kind: 'provider', + typeId: descriptor.id, packageName: 'dsh-mnemon-provider-retaindb', version: '0.5.5', + label: descriptor.label, icon: descriptor.icon, summary: descriptor.summary, + ...(descriptor.summaryI18nKey === undefined ? {} : { summaryI18nKey: descriptor.summaryI18nKey }), + origin: descriptor.origin, locality: descriptor.kind, workspaceBinding: descriptor.workspaceBinding, + capabilities: descriptor.capabilities, fields: descriptor.fields, + secrets: descriptor.fields.filter(field => field.input === 'secret').map(field => field.key), + scoreSemantics: 'normalized-relevance', + }, + create: context => new RetainDbProvider(context.memorySpaces ?? context.memoryBodies, { requestTimeoutMs: context.config.timeoutMs }), +}) + +export default defineMemorySpaceProvider({ + id: descriptor.id, + apply(ctx, host) { host.install(ctx, definition) }, +}) diff --git a/dsh/plugins/dsh-mnemon-provider-retaindb/tests/driver.spec.ts b/dsh/plugins/dsh-mnemon-provider-retaindb/tests/driver.spec.ts new file mode 100644 index 000000000..b90a06f18 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-retaindb/tests/driver.spec.ts @@ -0,0 +1,61 @@ +import { mkdtempSync, rmSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { afterEach, describe, expect, it, vi } from 'vitest' +import { createMemorySpaceProviderFixture } from 'dsh-mnemon-source-memory-spaces/testing' +import type { MemoryProviderConnection, MemoryProviderId } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { RetainDbProvider, descriptor } from '../src/index.ts' + +const temporaryDirectories: string[] = [] +afterEach(() => { for (const directory of temporaryDirectories.splice(0)) rmSync(directory, { recursive: true, force: true }) }) + +async function providerBody(providerId: MemoryProviderId, connection: MemoryProviderConnection) { + if (providerId !== descriptor.id) throw new Error('Wrong Provider fixture') + const dataDir = mkdtempSync(join(tmpdir(), 'mnemon-retaindb-driver-')) + temporaryDirectories.push(dataDir) + const { authority: registry, body } = createMemorySpaceProviderFixture(descriptor, connection, { dataDir, instanceId: 'work-account' }) + return { registry, body } +} + +function response(payload: unknown, status = 200): Response { + return new Response(payload === undefined ? null : JSON.stringify(payload), { status, headers: { 'Content-Type': 'application/json' } }) +} + +describe('standalone retaindb data plane', () => { + it('discovers its native namespaces independently', async () => { + const fetchMock = vi.fn(async () => response({ projects: [{ id: 'project-1', name: 'Launch', slug: 'launch', description: 'RetainDB source metadata.' }] })) + const retain = await providerBody('retaindb', { endpoint: 'https://retain.example', apiKey: 'key', project: 'old', userId: 'user' }) + await expect(new RetainDbProvider(retain.registry, { fetch: fetchMock }).discover({ endpoint: 'https://retain.example', apiKey: 'key' })).resolves.toEqual([ + expect.objectContaining({ externalId: 'project-1', name: 'Launch', description: 'RetainDB source metadata.', connection: { project: 'launch', userId: '*' } }), + ]) + }) + + it('preserves RetainDB project/user/session scope and current-to-legacy fallbacks', async () => { + const requests: Array<{ url: string; init?: RequestInit }> = [] + const fetchMock = vi.fn(async (url, init) => { + requests.push({ url: String(url), ...(init === undefined ? {} : { init }) }) + const path = new URL(String(url)).pathname + if (path === '/v1/memory/search') return response({ memories: [{ id: 'ret-1', content: 'Use staged rollout.', score: 0.8, memory_type: 'decision' }] }) + if (path === '/v1/memory') return response({ message: 'missing' }, 404) + if (path === '/v1/memories' && init?.method === 'POST') return response({ id: 'ret-2' }) + if (path === '/v1/memory/ret-1') return response({ message: 'missing' }, 404) + if (path === '/v1/memories/ret-1') return response({ deleted: true }) + throw new Error(`unexpected ${init?.method ?? 'GET'} ${path}`) + }) + const { registry, body } = await providerBody('retaindb', { + endpoint: 'https://api.retaindb.com', apiKey: 'retain-secret', project: 'launch', userId: 'alice', + }) + const provider = new RetainDbProvider(registry, { fetch: fetchMock }) + + await expect(provider.search(body, { query: 'rollout', limit: 4 })).resolves.toEqual({ + results: [expect.objectContaining({ id: 'ret-1', category: 'decision', score: 0.8 })], + }) + await expect(provider.remember(body, { content: 'Canary before production.', category: 'decision' })).resolves.toMatchObject({ id: 'ret-2' }) + await expect(provider.forget(body, 'ret-1')).resolves.toMatchObject({ action: 'deleted' }) + + expect(JSON.parse(String(requests[0]?.init?.body))).toMatchObject({ project: 'launch', user_id: 'alice', top_k: 4 }) + expect(new Headers(requests[0]?.init?.headers).get('Authorization')).toBe('Bearer retain-secret') + expect(new Headers(requests[0]?.init?.headers).get('X-API-Key')).toBe('retain-secret') + expect(requests.map(request => new URL(request.url).pathname)).toContain('/v1/memories/ret-1') + }) +}) diff --git a/dsh/plugins/dsh-mnemon-provider-retaindb/tests/provider.spec.ts b/dsh/plugins/dsh-mnemon-provider-retaindb/tests/provider.spec.ts new file mode 100644 index 000000000..a977da7f3 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-retaindb/tests/provider.spec.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from 'vitest' +import type { MemorySpaceAuthority } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { mountMemorySpaceProvider } from 'dsh-mnemon-source-memory-spaces/testing' +import module, { definition, RetainDbProvider, descriptor } from '../src/index.ts' + +describe('independent retaindb Provider', () => { + it('installs complete immutable metadata in one parent-owned child', async () => { + const mounted = await mountMemorySpaceProvider(module, { instanceId: 'work', config: undefined }) + try { + expect(mounted.descriptor).toMatchObject({ id: 'work', typeId: 'retaindb', label: descriptor.label }) + expect(Object.isFrozen(mounted.manifest)).toBe(true) + const authority: MemorySpaceAuthority = { runner: { effectiveDataDir: () => '/unused' }, list: () => [], providerConnection: () => ({}) } + expect(mounted.createAdapter({ memoryBodies: authority, config: { timeoutMs: 200 } }).id).toBe('work') + expect(mounted.registered).toBe(true) + expect(definition.manifest.secrets).toEqual(descriptor.fields.filter(field => field.input === 'secret').map(field => field.key).sort()) + } finally { await mounted.dispose() } + expect(mounted.registered).toBe(false) + }) + + it('constructs its own driver from a structural authority, without the main repository', () => { + const authority: MemorySpaceAuthority = { runner: { effectiveDataDir: () => '/unused' }, list: () => [], providerConnection: () => ({}) } + const adapter = definition.create({ memoryBodies: authority, config: { timeoutMs: 200 }, providerInstanceId: 'retaindb', manifest: definition.manifest }) + expect(adapter).toBeInstanceOf(RetainDbProvider) + expect(adapter.id).toBe('retaindb') + }) +}) diff --git a/dsh/plugins/dsh-mnemon-provider-retaindb/tsconfig.json b/dsh/plugins/dsh-mnemon-provider-retaindb/tsconfig.json new file mode 100644 index 000000000..badeba7ca --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-retaindb/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2024", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "verbatimModuleSyntax": true, + "allowImportingTsExtensions": true, + "skipLibCheck": true, + "types": [ + "node" + ] + }, + "include": [ + "src/**/*.ts", + "tests/**/*.ts", + "tsdown.config.ts", + "vitest.config.ts" + ] +} diff --git a/dsh/plugins/dsh-mnemon-provider-retaindb/tsdown.config.ts b/dsh/plugins/dsh-mnemon-provider-retaindb/tsdown.config.ts new file mode 100644 index 000000000..2d868cbd8 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-retaindb/tsdown.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'tsdown' + +export default defineConfig({ + entry: ['src/index.ts', 'src/descriptor.ts'], outDir: 'lib', format: 'esm', platform: 'node', + target: 'es2024', dts: true, clean: true, fixedExtension: false, deps: { neverBundle: true }, +}) diff --git a/dsh/plugins/dsh-mnemon-provider-retaindb/vitest.config.ts b/dsh/plugins/dsh-mnemon-provider-retaindb/vitest.config.ts new file mode 100644 index 000000000..5a6724c22 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-retaindb/vitest.config.ts @@ -0,0 +1,3 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ test: { environment: 'node' } }) diff --git a/dsh/plugins/dsh-mnemon-provider-supermemory/LICENSE b/dsh/plugins/dsh-mnemon-provider-supermemory/LICENSE new file mode 100644 index 000000000..e32e112dc --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-supermemory/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 dsh-external contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dsh/plugins/dsh-mnemon-provider-supermemory/README.md b/dsh/plugins/dsh-mnemon-provider-supermemory/README.md new file mode 100644 index 000000000..b68acd3b3 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-supermemory/README.md @@ -0,0 +1,27 @@ +# dsh-mnemon-provider-supermemory + +An adapter for Supermemory ingestion, extracted memories and container-scoped retrieval. + +Requires an endpoint, credentials and container scope. Ingestion acceptance does not mean extraction is complete. + +## Use + +The default `dsh-mnemon` Starter already installs this package. Enable and configure the service on the `dsh-mnemon` page under **Plugins**, then inspect its synchronized Memory Spaces. + +For a custom composition, install this package alongside `dsh-mnemon-source-memory-spaces` and include it in that Source's `config.providers`: + +```yaml +providers: + - use: dsh-mnemon-provider-supermemory + instanceId: supermemory +``` + +This is a **Memory Spaces child module**, not a top-level Source or a complete Strategy. It registers through `dsh-mnemon-source-memory-spaces/provider-sdk`; each parent Source owns its child Fibers, connection settings and lifetime. Credentials stay on the Host. + +[Provider setup and capability matrix](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/guides/memory-providers.md) · [中文指南](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/zh-CN/guides/memory-providers.md) + +## Develop independently + +From a source checkout with the declared dependencies installed, run `pnpm verify`. Tests, build and public exports belong to this package, without importing another package's controllers or repository configuration. Use the Source's public `/testing` fixtures for child registration and driver conformance; live service tests require a separately authorized environment. + +[Plugin author guide](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/development/extensions.md) diff --git a/dsh/plugins/dsh-mnemon-provider-supermemory/package.json b/dsh/plugins/dsh-mnemon-provider-supermemory/package.json new file mode 100644 index 000000000..52efd09e7 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-supermemory/package.json @@ -0,0 +1,55 @@ +{ + "name": "dsh-mnemon-provider-supermemory", + "version": "0.5.5", + "description": "Supermemory Provider child for the Memory Spaces Source.", + "license": "MIT", + "type": "module", + "engines": { + "node": ">=20" + }, + "exports": { + ".": { + "types": "./lib/index.d.ts", + "default": "./lib/index.js" + }, + "./descriptor": { + "types": "./lib/descriptor.d.ts", + "default": "./lib/descriptor.js" + }, + "./package.json": "./package.json" + }, + "files": [ + "lib", + "README.md" + ], + "scripts": { + "typecheck": "tsc --noEmit", + "build": "tsdown", + "test": "vitest run", + "verify": "pnpm typecheck && pnpm test && pnpm build" + }, + "peerDependencies": { + "@deepseek-ai/cordis": "^4.0.1", + "dsh-mnemon-source-memory-spaces": "^0.5.1" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "dsh-mnemon-source-memory-spaces": "^0.5.11", + "tsdown": "^0.22.0", + "typescript": "^5.9.0", + "vitest": "^3.2.0" + }, + "publishConfig": { + "access": "public", + "tag": "latest" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/mnemon-dev/mnemon.git", + "directory": "dsh/plugins/dsh-mnemon-provider-supermemory" + }, + "homepage": "https://github.com/mnemon-dev/mnemon/tree/master/dsh/plugins/dsh-mnemon-provider-supermemory#readme", + "bugs": { + "url": "https://github.com/mnemon-dev/mnemon/issues" + } +} diff --git a/dsh/plugins/dsh-mnemon-provider-supermemory/src/descriptor.ts b/dsh/plugins/dsh-mnemon-provider-supermemory/src/descriptor.ts new file mode 100644 index 000000000..85f598d42 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-supermemory/src/descriptor.ts @@ -0,0 +1,85 @@ +import type { MemoryProviderDescriptor } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { ICON } from './icon.ts' + +/** Browser-safe metadata owned by this Provider. */ +export const descriptor: MemoryProviderDescriptor = { + "id": "supermemory", + "label": "Supermemory", + "icon": { + "kind": "data-url", + "value": ICON + }, + "kind": "remote", + "workspaceBinding": "provider-global", + "summary": "Semantic memory, persistent profiles, conversation ingest, and multi-container recall.", + "summaryI18nKey": "overview.providerSummary.supermemory", + "origin": "third-party", + "capabilities": { + "search": true, + "browse": true, + "graph": false, + "entities": false, + "related": false, + "remember": true, + "link": false, + "forget": true, + "writeMode": "async-extracting", + "deletionMode": "soft" + }, + "fields": [ + { + "key": "endpoint", + "label": "Endpoint", + "scope": "service", + "input": "url", + "required": true, + "defaultValue": "https://api.supermemory.ai", + "i18nKey": "overview.providerEndpoint" + }, + { + "key": "apiKey", + "label": "API key", + "scope": "service", + "input": "secret", + "required": true, + "i18nKey": "overview.providerApiKey" + }, + { + "key": "containerTag", + "label": "Container tag", + "scope": "memory", + "input": "text", + "required": true, + "defaultValue": "dsh", + "maxLength": 100, + "pattern": "^[a-zA-Z0-9_:-]+$", + "i18nKey": "overview.providerField.containerTag" + }, + { + "key": "searchMode", + "label": "Search mode", + "scope": "memory", + "input": "select", + "required": true, + "defaultValue": "hybrid", + "options": [ + { + "value": "hybrid", + "label": "Hybrid", + "i18nKey": "overview.providerOption.hybrid" + }, + { + "value": "memories", + "label": "Memories", + "i18nKey": "overview.providerOption.memories" + }, + { + "value": "documents", + "label": "Documents", + "i18nKey": "overview.providerOption.documents" + } + ], + "i18nKey": "overview.providerField.searchMode" + } + ] +} diff --git a/dsh/plugins/dsh-mnemon-provider-supermemory/src/driver.ts b/dsh/plugins/dsh-mnemon-provider-supermemory/src/driver.ts new file mode 100644 index 000000000..cedf9ecfc --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-supermemory/src/driver.ts @@ -0,0 +1,165 @@ +import { descriptor } from './descriptor.ts' +import type { JsonValue } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import type { MemorySpaceAuthority } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import type { Insight, MemoryBody as MemorySpace, MemoryListRequest, RememberRequest, SearchRequest } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { HttpMemoryProvider, firstArray, jsonNumber, jsonObject, jsonString, type HttpProviderOptions } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { NORMALIZED_RELEVANCE_SCORE, type MemoryProviderAdapter, type ProviderBodyStatus as ProviderSpaceStatus, type ProviderMemorySpace, type ProviderSearchResult } from 'dsh-mnemon-source-memory-spaces/provider-sdk' + +function insight(value: unknown): Insight | undefined { + const item = jsonObject(value) + const id = jsonString(item?.id) + const content = jsonString(item?.memory) ?? jsonString(item?.chunk) ?? jsonString(item?.content) + if (id === undefined || content === undefined) return undefined + const metadata = jsonObject(item?.metadata) + const score = jsonNumber(item?.similarity) ?? jsonNumber(item?.score) + const createdAt = jsonString(item?.updatedAt) ?? jsonString(item?.createdAt) + return { + id, + content, + category: jsonString(metadata?.category) ?? 'general', + source: 'external', + ...(score === undefined ? {} : { score }), + ...(createdAt === undefined ? {} : { createdAt }), + } +} + +export class SupermemoryProvider extends HttpMemoryProvider implements MemoryProviderAdapter { + readonly id = 'supermemory' as const + readonly scoreSemantics = NORMALIZED_RELEVANCE_SCORE + + constructor(memorySpaces: MemorySpaceAuthority, options: HttpProviderOptions = {}) { + super(memorySpaces, { label: descriptor.label, ...options }) + } + + async discover(connection: Record, signal?: AbortSignal): Promise { + const payload = await this.requestConnection(connection, '/v3/container-tags/list', { headers: this.headers(connection), signal }) + return firstArray(payload, 'containerTags', 'items').flatMap(value => { + const item = jsonObject(value) + const tag = jsonString(item?.containerTag) ?? jsonString(item?.container_tag) + if (tag === undefined) return [] + return [{ + externalId: jsonString(item?.id) ?? tag, + name: jsonString(item?.name) ?? tag, + description: jsonString(item?.description) ?? `Supermemory space ${tag}`, + connection: { containerTag: tag, searchMode: 'hybrid' }, + }] + }) + } + + async status(body: MemorySpace, signal?: AbortSignal): Promise { + try { + await this.list(body, { limit: 1 }, signal) + return { healthy: true } + } catch (error) { + return { healthy: false, error: error instanceof Error ? error.message : String(error) } + } + } + + async search(body: MemorySpace, request: SearchRequest, signal?: AbortSignal): Promise { + const connection = this.connection(body) + const payload = await this.request(body, '/v4/search', { + headers: this.headers(connection), + json: { + q: request.query, + containerTag: String(connection.containerTag), + searchMode: String(connection.searchMode ?? 'hybrid'), + limit: request.limit ?? 10, + }, + signal, + }) + return { results: firstArray(payload, 'results').map(insight).filter((item): item is Insight => item !== undefined) } + } + + async list(body: MemorySpace, request: MemoryListRequest, signal?: AbortSignal): Promise { + const connection = this.connection(body) + const limit = Math.min(Math.max(request.limit ?? 200, 1), 200) + const payload = await this.request(body, '/v4/memories/list', { + headers: this.headers(connection), + json: { + containerTags: [String(connection.containerTag)], + limit, + page: 1, + sort: 'createdAt', + order: 'desc', + }, + signal, + }) + const memories = firstArray(payload, 'memoryEntries', 'results').map(insight).filter((item): item is Insight => item !== undefined) + + // Supermemory exposes extracted memories and their source documents as + // separate read surfaces. Lite may extract only some documents, so merge + // both surfaces instead of hiding documents as soon as one memory exists. + const documents = await this.request(body, '/v3/documents/documents', { + headers: this.headers(connection), + json: { + containerTags: [String(connection.containerTag)], + limit, + page: 1, + sort: 'createdAt', + order: 'desc', + }, + signal, + }) + const projectedDocuments = firstArray(documents, 'documents', 'memories', 'results').map(insight).filter((item): item is Insight => item !== undefined) + return [...new Map([...memories, ...projectedDocuments].map(item => [item.id, item])).values()] + .filter(item => request.category === undefined || item.category === request.category) + .slice(0, limit) + } + + async remember(body: MemorySpace, request: RememberRequest, signal?: AbortSignal): Promise { + const connection = this.connection(body) + const payload = await this.request(body, '/v3/documents', { + headers: this.headers(connection), + json: { + content: request.content, + containerTag: String(connection.containerTag), + taskType: 'memory', + metadata: { + sm_source: 'dsh-mnemon', + ...(request.category === undefined ? {} : { category: request.category }), + ...(request.importance === undefined ? {} : { importance: request.importance }), + }, + }, + signal, + }) + const result = jsonObject(payload) ?? {} + return { + action: 'queued', + provider: this.id, + summary: 'Supermemory accepted the memory document for extraction.', + ...(jsonString(result.id) === undefined ? {} : { id: jsonString(result.id)! }), + ...(jsonString(result.status) === undefined ? {} : { status: jsonString(result.status)! }), + } + } + + async forget(body: MemorySpace, id: string, signal?: AbortSignal): Promise { + const connection = this.connection(body) + try { + const payload = await this.request(body, '/v4/memories', { + method: 'DELETE', + headers: this.headers(connection), + json: { id, containerTag: String(connection.containerTag), reason: 'Deleted from dsh-mnemon' }, + signal, + }) + return { + action: 'deleted', + provider: this.id, + id, + ...(jsonObject(payload)?.forgotten === undefined ? {} : { forgotten: jsonObject(payload)!.forgotten as JsonValue }), + } + } catch (error) { + if (!(error instanceof Error) || !/HTTP 404\b/u.test(error.message)) throw error + await this.request(body, `/v3/documents/${encodeURIComponent(id)}`, { + method: 'DELETE', + headers: this.headers(connection), + signal, + }) + return { action: 'deleted', provider: this.id, id, document: true } + } + } + + private headers(connection: Record): HeadersInit { + const token = String(connection.apiKey ?? '').replace(/^Bearer\s+/iu, '') + return { Authorization: `Bearer ${token}`, 'x-sm-source': 'dsh-mnemon' } + } +} diff --git a/dsh/plugins/dsh-mnemon-provider-supermemory/src/icon.ts b/dsh/plugins/dsh-mnemon-provider-supermemory/src/icon.ts new file mode 100644 index 000000000..8453eab3e --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-supermemory/src/icon.ts @@ -0,0 +1,2 @@ +/** Browser-safe brand asset shipped by this Provider, not by the Host. */ +export const ICON = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMCAyNCIgZmlsbD0ibm9uZSI+CiAgPHBhdGggZD0iTTI5LjMzODggOS40Njc2N0gxOC40NDhWMC4wMDE0NjQ4NEgxNC45MjkzVjEwLjI3MjVDMTQuOTI5MyAxMS4zNjM0IDE1LjM2IDEyLjQxMSAxNi4xMjU0IDEzLjE4M0wyNS4wMTggMjIuMTUxTDI3LjUwNiAxOS42NDE5TDIwLjkzOCAxMy4wMTgzSDI5LjM0MDhWOS40Njk3NUwyOS4zMzg4IDkuNDY3NjdaIiBmaWxsPSIjMEIxMDE1Ii8+CiAgPHBhdGggZD0iTTEuODI4MzkgNC4zNjA1Nkw4LjM5NjMzIDEwLjk4NDJILTAuMDA2NDY5NzNWMTQuNTMyOEgxMC44ODQzVjIzLjk5OUgxNC40MDNWMTMuNzI4QzE0LjQwMyAxMi42MzcgMTMuOTcyMyAxMS41ODk0IDEzLjIwNjkgMTAuODE3NUw0LjMxNjM1IDEuODUxNDdMMS44MjgzOSA0LjM2MDU2WiIgZmlsbD0iIzBCMTAxNSIvPgo8L3N2Zz4K" diff --git a/dsh/plugins/dsh-mnemon-provider-supermemory/src/index.ts b/dsh/plugins/dsh-mnemon-provider-supermemory/src/index.ts new file mode 100644 index 000000000..4ba959b1c --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-supermemory/src/index.ts @@ -0,0 +1,24 @@ +import { MEMORY_SPACE_PROVIDER_API_VERSION, defineMemorySpaceProvider, defineMemorySpaceProviderDefinition } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { SupermemoryProvider } from './driver.ts' +import { descriptor } from './descriptor.ts' + +export { SupermemoryProvider, descriptor } + +export const definition = defineMemorySpaceProviderDefinition({ + manifest: { + apiVersion: MEMORY_SPACE_PROVIDER_API_VERSION, kind: 'provider', + typeId: descriptor.id, packageName: 'dsh-mnemon-provider-supermemory', version: '0.5.5', + label: descriptor.label, icon: descriptor.icon, summary: descriptor.summary, + ...(descriptor.summaryI18nKey === undefined ? {} : { summaryI18nKey: descriptor.summaryI18nKey }), + origin: descriptor.origin, locality: descriptor.kind, workspaceBinding: descriptor.workspaceBinding, + capabilities: descriptor.capabilities, fields: descriptor.fields, + secrets: descriptor.fields.filter(field => field.input === 'secret').map(field => field.key), + scoreSemantics: 'normalized-relevance', + }, + create: context => new SupermemoryProvider(context.memorySpaces ?? context.memoryBodies, { requestTimeoutMs: context.config.timeoutMs }), +}) + +export default defineMemorySpaceProvider({ + id: descriptor.id, + apply(ctx, host) { host.install(ctx, definition) }, +}) diff --git a/dsh/plugins/dsh-mnemon-provider-supermemory/tests/driver.spec.ts b/dsh/plugins/dsh-mnemon-provider-supermemory/tests/driver.spec.ts new file mode 100644 index 000000000..67469d415 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-supermemory/tests/driver.spec.ts @@ -0,0 +1,105 @@ +import { mkdtempSync, rmSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { afterEach, describe, expect, it, vi } from 'vitest' +import { createMemorySpaceProviderFixture } from 'dsh-mnemon-source-memory-spaces/testing' +import type { MemoryProviderConnection, MemoryProviderId } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { SupermemoryProvider, descriptor } from '../src/index.ts' + +const temporaryDirectories: string[] = [] +afterEach(() => { for (const directory of temporaryDirectories.splice(0)) rmSync(directory, { recursive: true, force: true }) }) + +async function providerBody(providerId: MemoryProviderId, connection: MemoryProviderConnection) { + if (providerId !== descriptor.id) throw new Error('Wrong Provider fixture') + const dataDir = mkdtempSync(join(tmpdir(), 'mnemon-supermemory-driver-')) + temporaryDirectories.push(dataDir) + const { authority: registry, body } = createMemorySpaceProviderFixture(descriptor, connection, { dataDir, instanceId: 'work-account' }) + return { registry, body } +} + +function response(payload: unknown, status = 200): Response { + return new Response(payload === undefined ? null : JSON.stringify(payload), { status, headers: { 'Content-Type': 'application/json' } }) +} + +describe('standalone supermemory data plane', () => { + it('does not treat a returned document id as completed memory extraction', async () => { + const fetchMock = vi.fn(async () => response({ id: 'accepted-document' })) + const { registry, body } = await providerBody('supermemory', { endpoint: 'https://supermemory.example', apiKey: 'fixture', containerTag: 'notes' }) + await expect(new SupermemoryProvider(registry, { fetch: fetchMock }).remember(body, { content: 'Synthetic input.' })) + .resolves.toMatchObject({ action: 'queued', id: 'accepted-document' }) + }) + + it('discovers its native namespaces independently', async () => { + const fetchMock = vi.fn(async () => response([{ id: 'space-1', name: 'Team space', containerTag: 'team', description: 'Supermemory source metadata.' }])) + const supermemory = await providerBody('supermemory', { endpoint: 'https://supermemory.example', apiKey: 'key', containerTag: 'old', searchMode: 'hybrid' }) + await expect(new SupermemoryProvider(supermemory.registry, { fetch: fetchMock }).discover({ endpoint: 'https://supermemory.example', apiKey: 'key' })).resolves.toEqual([ + expect.objectContaining({ externalId: 'space-1', name: 'Team space', description: 'Supermemory source metadata.' }), + ]) + }) + + it('maps Supermemory v4 recall/list/forget and v3 document ingestion', async () => { + const requests: Array<{ url: string; init?: RequestInit }> = [] + const fetchMock = vi.fn(async (url, init) => { + requests.push({ url: String(url), ...(init === undefined ? {} : { init }) }) + const path = new URL(String(url)).pathname + if (path === '/v4/search') return response({ results: [{ id: 'sm-1', memory: 'Alice uses dark mode.', similarity: 0.88, metadata: { category: 'preference' } }] }) + if (path === '/v4/memories/list') return response({ memoryEntries: [{ id: 'sm-1', memory: 'Alice uses dark mode.', createdAt: '2026-08-16T00:00:00Z' }] }) + if (path === '/v3/documents/documents') return response({ documents: [] }) + if (path === '/v3/documents') return response({ id: 'doc-1', status: 'queued' }) + if (path === '/v4/memories' && init?.method === 'DELETE') return response({ id: 'sm-1', forgotten: true }) + throw new Error(`unexpected ${init?.method ?? 'GET'} ${path}`) + }) + const { registry, body } = await providerBody('supermemory', { + endpoint: 'https://api.supermemory.ai', apiKey: 'sm-secret', containerTag: 'alice', searchMode: 'hybrid', + }) + const provider = new SupermemoryProvider(registry, { fetch: fetchMock }) + + await expect(provider.search(body, { query: 'theme', limit: 6 })).resolves.toEqual({ + results: [expect.objectContaining({ id: 'sm-1', category: 'preference', score: 0.88 })], + }) + await expect(provider.list(body, { limit: 10 })).resolves.toEqual([expect.objectContaining({ id: 'sm-1' })]) + await expect(provider.remember(body, { content: 'Alice prefers dark mode.', category: 'preference' })).resolves.toMatchObject({ id: 'doc-1', status: 'queued' }) + await expect(provider.forget(body, 'sm-1')).resolves.toMatchObject({ forgotten: true }) + + expect(JSON.parse(String(requests[0]?.init?.body))).toMatchObject({ q: 'theme', containerTag: 'alice', searchMode: 'hybrid', limit: 6 }) + expect(new Headers(requests[0]?.init?.headers).get('Authorization')).toBe('Bearer sm-secret') + expect(new Headers(requests[0]?.init?.headers).get('x-sm-source')).toBe('dsh-mnemon') + expect(JSON.parse(String(requests.at(-1)?.init?.body))).toEqual({ id: 'sm-1', containerTag: 'alice', reason: 'Deleted from dsh-mnemon' }) + }) + + it('merges Supermemory documents with extracted entries and falls back for document deletion', async () => { + const requests: string[] = [] + const fetchMock = vi.fn(async (url, init) => { + const path = new URL(String(url)).pathname + requests.push(`${init?.method ?? 'GET'} ${path}`) + if (path === '/v4/memories/list') return response({ memoryEntries: [{ id: 'memory-1', memory: 'An extracted memory is browseable.' }] }) + if (path === '/v3/documents/documents') { + return response({ documents: [{ + id: 'doc-1', + content: 'A retained document is still browseable.', + createdAt: '2026-08-16T00:00:00Z', + metadata: { category: 'context' }, + }] }) + } + if (path === '/v4/memories' && init?.method === 'DELETE') return response({ message: 'memory not found' }, 404) + if (path === '/v3/documents/doc-1' && init?.method === 'DELETE') return response({ deleted: true }) + throw new Error(`unexpected ${init?.method ?? 'GET'} ${path}`) + }) + const { registry, body } = await providerBody('supermemory', { + endpoint: 'https://api.supermemory.ai', apiKey: 'sm-secret', containerTag: 'alice', searchMode: 'hybrid', + }) + const provider = new SupermemoryProvider(registry, { fetch: fetchMock }) + + await expect(provider.list(body, { limit: 10 })).resolves.toEqual([ + expect.objectContaining({ id: 'memory-1', content: 'An extracted memory is browseable.' }), + expect.objectContaining({ id: 'doc-1', content: 'A retained document is still browseable.', category: 'context' }), + ]) + await expect(provider.forget(body, 'doc-1')).resolves.toMatchObject({ action: 'deleted', document: true }) + expect(requests).toEqual([ + 'POST /v4/memories/list', + 'POST /v3/documents/documents', + 'DELETE /v4/memories', + 'DELETE /v3/documents/doc-1', + ]) + }) +}) diff --git a/dsh/plugins/dsh-mnemon-provider-supermemory/tests/provider.spec.ts b/dsh/plugins/dsh-mnemon-provider-supermemory/tests/provider.spec.ts new file mode 100644 index 000000000..a8b7f84ac --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-supermemory/tests/provider.spec.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from 'vitest' +import type { MemorySpaceAuthority } from 'dsh-mnemon-source-memory-spaces/provider-sdk' +import { mountMemorySpaceProvider } from 'dsh-mnemon-source-memory-spaces/testing' +import module, { definition, SupermemoryProvider, descriptor } from '../src/index.ts' + +describe('independent supermemory Provider', () => { + it('installs complete immutable metadata in one parent-owned child', async () => { + const mounted = await mountMemorySpaceProvider(module, { instanceId: 'work', config: undefined }) + try { + expect(mounted.descriptor).toMatchObject({ id: 'work', typeId: 'supermemory', label: descriptor.label }) + expect(Object.isFrozen(mounted.manifest)).toBe(true) + const authority: MemorySpaceAuthority = { runner: { effectiveDataDir: () => '/unused' }, list: () => [], providerConnection: () => ({}) } + expect(mounted.createAdapter({ memoryBodies: authority, config: { timeoutMs: 200 } }).id).toBe('work') + expect(mounted.registered).toBe(true) + expect(definition.manifest.secrets).toEqual(descriptor.fields.filter(field => field.input === 'secret').map(field => field.key).sort()) + } finally { await mounted.dispose() } + expect(mounted.registered).toBe(false) + }) + + it('constructs its own driver from a structural authority, without the main repository', () => { + const authority: MemorySpaceAuthority = { runner: { effectiveDataDir: () => '/unused' }, list: () => [], providerConnection: () => ({}) } + const adapter = definition.create({ memoryBodies: authority, config: { timeoutMs: 200 }, providerInstanceId: 'supermemory', manifest: definition.manifest }) + expect(adapter).toBeInstanceOf(SupermemoryProvider) + expect(adapter.id).toBe('supermemory') + }) +}) diff --git a/dsh/plugins/dsh-mnemon-provider-supermemory/tsconfig.json b/dsh/plugins/dsh-mnemon-provider-supermemory/tsconfig.json new file mode 100644 index 000000000..badeba7ca --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-supermemory/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2024", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "verbatimModuleSyntax": true, + "allowImportingTsExtensions": true, + "skipLibCheck": true, + "types": [ + "node" + ] + }, + "include": [ + "src/**/*.ts", + "tests/**/*.ts", + "tsdown.config.ts", + "vitest.config.ts" + ] +} diff --git a/dsh/plugins/dsh-mnemon-provider-supermemory/tsdown.config.ts b/dsh/plugins/dsh-mnemon-provider-supermemory/tsdown.config.ts new file mode 100644 index 000000000..2d868cbd8 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-supermemory/tsdown.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'tsdown' + +export default defineConfig({ + entry: ['src/index.ts', 'src/descriptor.ts'], outDir: 'lib', format: 'esm', platform: 'node', + target: 'es2024', dts: true, clean: true, fixedExtension: false, deps: { neverBundle: true }, +}) diff --git a/dsh/plugins/dsh-mnemon-provider-supermemory/vitest.config.ts b/dsh/plugins/dsh-mnemon-provider-supermemory/vitest.config.ts new file mode 100644 index 000000000..5a6724c22 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-provider-supermemory/vitest.config.ts @@ -0,0 +1,3 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ test: { environment: 'node' } }) diff --git a/dsh/plugins/dsh-mnemon-source-documents/LICENSE b/dsh/plugins/dsh-mnemon-source-documents/LICENSE new file mode 100644 index 000000000..e32e112dc --- /dev/null +++ b/dsh/plugins/dsh-mnemon-source-documents/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 dsh-external contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dsh/plugins/dsh-mnemon-source-documents/README.md b/dsh/plugins/dsh-mnemon-source-documents/README.md new file mode 100644 index 000000000..d21b049de --- /dev/null +++ b/dsh/plugins/dsh-mnemon-source-documents/README.md @@ -0,0 +1,34 @@ +# dsh-mnemon-source-documents + +Owns managed Markdown, search, revisions and local archiving. It requires a workspace identity even when storage is shared globally. + +## Use + +The `dsh-mnemon` Starter installs and mounts this Source by default. The package alone does not activate an instance or select a Strategy. + +For a custom Profile, mount an explicit Entry after the Host's `ctx.mnemonMemory` service is available: + +```yaml +- id: personal-documents + name: dsh-mnemon-source-documents + config: + dataDir: /absolute/path/to/personal-memory +``` + +Choose an existing data authority deliberately. Without `dataDir`, each stable Source instance uses an isolated directory under `~/.mnemon/sources/`; the default Starter supplies the existing product paths. Replacing the default Source or adding a second instance requires an explicit selection Strategy, not a second implicit default role. + +Manual management can create a document without a model, but it still needs a workspace in the request scope. Standalone archiving retains content locally; cross-Source LLM distillation is an optional Host workflow. + +## Create-only Action / 仅创建操作 + +The `create` Action accepts `title`, `content`, and optional `description`, `sourcePaths`, and `sessionIds`. It always creates a separate document, rejects mutation selectors such as `action` or `id`, and fails at capacity without updating or archiving existing documents. The existing `manage` Action remains available for create/update workflows. Mnemon idle review receives only the create-only document tool and skips already-covered candidates. No document format migration is required. + +`create` Action 接收 `title`、`content`,以及可选的 `description`、`sourcePaths`、`sessionIds`。它始终新建独立档案,拒绝 `action`、`id` 等 mutation 选择字段;容量不足时失败,不更新或归档已有档案。原有 `manage` Action 继续支持创建和更新。Mnemon 后台审查只获得仅创建档案工具,已有内容覆盖候选时跳过。本变更无需迁移档案格式。 + +## Source-owned UI and tests + +The optional `./client` entry is an ordinary DSH Client plugin. This package owns its pages and `presentation/` resources and uses `dsh-mnemon/client` for the shared frame and scoped management client. It never receives a Host Context, credentials or another Source's controller. + +From a source checkout, install the declared dependencies and run `pnpm verify` to check Host behavior, Source-backed page interactions and Host/browser artifacts. Unreleased SDK work consumes a packed `dsh-mnemon` peer, not repository aliases or copied root tests. + +[Plugin development](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/en/development/extensions.md) · [中文指南](https://github.com/mnemon-dev/mnemon/blob/master/dsh/docs/zh-CN/development/extensions.md) diff --git a/dsh/plugins/dsh-mnemon-source-documents/locale/en.json b/dsh/plugins/dsh-mnemon-source-documents/locale/en.json new file mode 100644 index 000000000..3c9847629 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-source-documents/locale/en.json @@ -0,0 +1,6 @@ +{ + "meta": { + "title": "Project Documents", + "description": "Versioned narrative documents searched first and read in full on demand." + } +} diff --git a/dsh/plugins/dsh-mnemon-source-documents/locale/zh.json b/dsh/plugins/dsh-mnemon-source-documents/locale/zh.json new file mode 100644 index 000000000..b6d060b65 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-source-documents/locale/zh.json @@ -0,0 +1,6 @@ +{ + "meta": { + "title": "项目档案", + "description": "可版本化的叙事文档,先检索,再按需阅读全文。" + } +} diff --git a/dsh/plugins/dsh-mnemon-source-documents/package.json b/dsh/plugins/dsh-mnemon-source-documents/package.json new file mode 100644 index 000000000..b145f23c1 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-source-documents/package.json @@ -0,0 +1,114 @@ +{ + "name": "dsh-mnemon-source-documents", + "version": "0.5.9", + "description": "Self-contained documents Source for Composable View Memory.", + "license": "MIT", + "type": "module", + "engines": { + "node": ">=20" + }, + "exports": { + ".": { + "types": "./lib/index.d.ts", + "default": "./lib/index.js" + }, + "./package.json": "./package.json", + "./locale/*.json": "./locale/*.json", + "./client": { + "types": "./lib/types/client.d.ts", + "default": "./lib/client.js" + }, + "./contracts": { + "types": "./lib/contracts.d.ts", + "default": "./lib/contracts.js" + }, + "./presentation/locales.json": "./presentation/locales.json", + "./presentation/page.module.css": "./presentation/page.module.css", + "./presentation/sidebar.module.css": "./presentation/sidebar.module.css" + }, + "files": [ + "lib", + "locale", + "README.md", + "presentation" + ], + "dsh": { + "client": { + "platform": "web", + "external": [ + "dsh-mnemon/client" + ] + } + }, + "scripts": { + "typecheck": "tsc --noEmit", + "build": "tsdown && tsc -p tsconfig.client-types.json", + "test": "vitest run", + "verify": "pnpm typecheck && pnpm test && pnpm build" + }, + "dependencies": { + "@deepseek-ai/schemastery": "^3.18.4", + "markdown-to-jsx": "^7.7.0" + }, + "peerDependencies": { + "@deepseek-ai/cordis": "^4.0.1", + "dsh-mnemon": "^0.5.19", + "react": "^18.2.0" + }, + "devDependencies": { + "@deepseek-ai/dsh-client-ui-primitives": "0.1.7-rc.2", + "@testing-library/react": "^16.3.0", + "@types/node": "^22.0.0", + "@types/react": "^18.3.0", + "@types/react-dom": "^18.3.0", + "dsh-mnemon": "^0.5.23", + "jsdom": "^26.0.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "tsdown": "^0.22.0", + "typescript": "^5.9.0", + "vitest": "^3.2.0", + "lightningcss": "^1.33.0", + "clsx": "^2.1.1", + "@shikijs/langs": "^4.3.1", + "anser": "^2.3.5", + "katex": "^0.16.47", + "mdast-util-from-markdown": "^2.0.3", + "mdast-util-gfm": "^3.1.0", + "mdast-util-math": "^3.0.0", + "micromark-core-commonmark": "^2.0.3", + "micromark-extension-gfm": "^3.0.0", + "micromark-extension-math": "^3.1.0", + "micromark-factory-space": "^2.0.1", + "micromark-util-character": "^2.1.1", + "micromark-util-classify-character": "^2.0.1", + "micromark-util-sanitize-uri": "^2.0.1", + "micromark-util-symbol": "^2.0.1", + "shiki": "^4.3.1", + "@deepseek-ai/dsh-client-store": "0.1.7-rc.2", + "@deepseek-ai/dsh-util-code-language": "0.1.7-rc.2", + "@deepseek-ai/dsh-util-workspace-path": "0.1.7-rc.2", + "diff": "^9.0.0", + "immer": "^10.1.1", + "simple-icons": "16.31.0", + "zustand": "~4.4.7" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + }, + "publishConfig": { + "access": "public", + "tag": "latest" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/mnemon-dev/mnemon.git", + "directory": "dsh/plugins/dsh-mnemon-source-documents" + }, + "homepage": "https://github.com/mnemon-dev/mnemon/tree/master/dsh/plugins/dsh-mnemon-source-documents#readme", + "bugs": { + "url": "https://github.com/mnemon-dev/mnemon/issues" + } +} diff --git a/dsh/plugins/dsh-mnemon-source-documents/presentation/locales.json b/dsh/plugins/dsh-mnemon-source-documents/presentation/locales.json new file mode 100644 index 000000000..0ea45b89a --- /dev/null +++ b/dsh/plugins/dsh-mnemon-source-documents/presentation/locales.json @@ -0,0 +1,124 @@ +{ + "zh": { + "nav.documents": "档案", + "documents.title": "项目档案", + "documents.description": "当前工作区的项目文档:先检索,再按需阅读全文;接近容量上限时,最久未用的档案会归档。", + "documents.capacity": "{used} / {limit}", + "documents.summary": "档案存储摘要", + "documents.active": "活跃档案", + "documents.activeHint": "近场检索范围", + "documents.archivedCount": "归档", + "documents.archivedHint": "不占活跃容量", + "documents.activeCapacity": "活跃容量", + "documents.capacityHint": "按实际 UTF-8 文件大小计算", + "documents.searchAria": "检索项目档案", + "documents.searchPlaceholder": "搜索设计、调查、流程或交接记录…", + "documents.search": "检索", + "documents.scope": "档案范围", + "documents.new": "新建档案", + "documents.newTitle": "创建托管档案", + "documents.editTitle": "编辑活跃档案", + "documents.editorHint": "控制层会生成 frontmatter、哈希与修订号;原项目文件始终只读。", + "documents.managedCopy": "托管副本", + "documents.name": "标题", + "documents.routing": "检索说明", + "documents.sources": "来源路径", + "documents.sourcesPlaceholder": "src/index.ts, docs/architecture.md", + "documents.markdown": "Markdown 内容", + "documents.saving": "保存中…", + "documents.create": "创建档案", + "documents.save": "保存修订", + "documents.created": "已创建活跃档案。", + "documents.createdAfterArchive": "已先迁移 {count} 份旧档案,再创建活跃档案。", + "documents.updated": "已保存新的档案修订。", + "documents.updatedAfterArchive": "已先迁移 {count} 份旧档案,再保存修订。", + "documents.archived": "已建立 Mnemon 冷索引并归档;关联记忆空间:{spaces}", + "documents.list": "项目档案列表", + "documents.activeList": "活跃目录", + "documents.archiveList": "归档目录", + "documents.noDescription": "暂无检索说明。", + "documents.missing": "文件缺失", + "documents.emptyActive": "还没有活跃档案", + "documents.emptyActiveText": "复杂对话达到活动评分门槛后会自动审阅并整理档案,也可以在上方手动创建。", + "documents.emptyArchived": "还没有归档", + "documents.emptyArchivedText": "只有完成 Mnemon 索引的档案才会迁移到这里。", + "documents.reader": "档案阅读器", + "documents.selectTitle": "选择一份档案", + "documents.selectText": "在左侧查看活跃项目知识或沿 Mnemon 引用打开归档原文。", + "documents.coldArchive": "归档原文", + "documents.edit": "编辑", + "documents.path": "托管路径", + "documents.revision": "修订", + "documents.hash": "内容哈希", + "documents.size": "文件大小", + "documents.archiveReceipt": "Mnemon 冷索引回执", + "documents.archiveTitle": "迁入归档", + "documents.archiveDescription": "独立任务 Agent 先拟定摘要和目标空间;Host 校验后写入冷索引,再移动原文。需要已启用且支持精确写入与删除的记忆空间。", + "documents.archive": "归档", + "documents.archiveConfirm": "确认建立 Mnemon 索引并迁移这份档案?", + "documents.archiving": "索引并迁移中…", + "documents.archiveNow": "确认归档", + "documents.footnote": "`.mnemon/documents/index.json` 是控制面事实源;active 总量固定不超过 10 MB,archived 不计入上限,项目源文件不会被修改。" + }, + "en": { + "nav.documents": "Documents", + "documents.title": "Project Documents", + "documents.description": "Project documents for this workspace: searched first, read in full when needed; near the limit the least-used ones are archived.", + "documents.capacity": "{used} / {limit}", + "documents.summary": "Document storage summary", + "documents.active": "Active", + "documents.activeHint": "Near-field search scope", + "documents.archivedCount": "Archive", + "documents.archivedHint": "Excluded from active capacity", + "documents.activeCapacity": "Active capacity", + "documents.capacityHint": "Measured from actual UTF-8 files", + "documents.searchAria": "Search project documents", + "documents.searchPlaceholder": "Search designs, investigations, procedures, or handoffs…", + "documents.search": "Search", + "documents.scope": "Document scope", + "documents.new": "New document", + "documents.newTitle": "Create managed document", + "documents.editTitle": "Edit active document", + "documents.editorHint": "The control plane generates frontmatter, hashes, and revisions. Project source files stay read only.", + "documents.managedCopy": "Managed copy", + "documents.name": "Title", + "documents.routing": "Retrieval description", + "documents.sources": "Source paths", + "documents.sourcesPlaceholder": "src/index.ts, docs/architecture.md", + "documents.markdown": "Markdown content", + "documents.saving": "Saving…", + "documents.create": "Create document", + "documents.save": "Save revision", + "documents.created": "Active document created.", + "documents.createdAfterArchive": "Archived {count} older document(s), then created the active document.", + "documents.updated": "New document revision saved.", + "documents.updatedAfterArchive": "Archived {count} older document(s), then saved the revision.", + "documents.archived": "Mnemon cold index created and document archived; Memory Spaces: {spaces}", + "documents.list": "Project document list", + "documents.activeList": "Active directory", + "documents.archiveList": "Archive directory", + "documents.noDescription": "No retrieval description.", + "documents.missing": "File missing", + "documents.emptyActive": "No active documents yet", + "documents.emptyActiveText": "Complex work is reviewed after it reaches the activity-score gate, or you can create a Document above.", + "documents.emptyArchived": "No archives yet", + "documents.emptyArchivedText": "Only documents with a completed Mnemon index are moved here.", + "documents.reader": "Document reader", + "documents.selectTitle": "Select a document", + "documents.selectText": "Read active project knowledge or follow a Mnemon reference to an archived original.", + "documents.coldArchive": "Archived original", + "documents.edit": "Edit", + "documents.path": "Managed path", + "documents.revision": "Revision", + "documents.hash": "Content hash", + "documents.size": "File size", + "documents.archiveReceipt": "Mnemon cold-index receipt", + "documents.archiveTitle": "Move to archive", + "documents.archiveDescription": "An independent task Agent proposes a summary and destination. The Host validates and writes the cold index before moving the original. An active Memory Space with exact writes and safe deletion is required.", + "documents.archive": "Archive", + "documents.archiveConfirm": "Create the Mnemon index and archive this document?", + "documents.archiving": "Indexing and moving…", + "documents.archiveNow": "Confirm archive", + "documents.footnote": "`.mnemon/documents/index.json` is the control-plane source of truth. active stays at or below 10 MB, archived is excluded, and project source files are never modified." + } +} diff --git a/dsh/plugins/dsh-mnemon-source-documents/presentation/page.module.css b/dsh/plugins/dsh-mnemon-source-documents/presentation/page.module.css new file mode 100644 index 000000000..e049ab281 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-source-documents/presentation/page.module.css @@ -0,0 +1,144 @@ +.documentSummary { display: grid; grid-template-columns: .7fr .7fr 1.6fr; gap: 12px; margin-bottom: 12px; } +.documentSummary article { min-width: 0; min-height: 92px; padding: 14px 16px; border: .5px solid var(--mn-card-line); border-radius: var(--dsw-radius-lg); } +.documentSummary article > span { display: block; color: var(--mn-faint); font-size: 12px; line-height: 18px; } +.documentSummary article > strong { display: block; margin: 4px 0 2px; font-size: 20px; font-weight: 500; line-height: 28px; font-variant-numeric: tabular-nums; } +.documentSummary article > small { color: var(--mn-faint); font-size: 12px; line-height: 18px; } +.documentCapacity > div { height: 4px; margin: 8px 0 6px; overflow: hidden; border-radius: 999px; background: var(--mn-fill); } +.documentCapacity > div i { display: block; height: 100%; border-radius: inherit; background: var(--dsw-alias-brand-primary); transition: width .3s ease; } + +.documentToolbar { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; padding: 8px; border: .5px solid var(--mn-card-line); border-radius: var(--dsw-radius-lg); } +.documentToolbar form { display: flex; min-width: 260px; flex: 1; align-items: center; gap: 8px; } +.documentSearch { flex: 1; } +.documentEditor input, +.documentEditor textarea { width: 100%; border: .5px solid var(--mn-field-line); border-radius: var(--dsw-radius-md); outline: 0; padding: 6px 10px; color: var(--mn-text); background: var(--mn-input); font-size: 13px; line-height: 20px; } +.documentEditor input:focus, +.documentEditor textarea:focus { border-color: var(--mn-accent); } +/* Filter, as the DSH SegmentedControl. */ +.documentToolbar > div { display: flex; align-items: center; gap: 2px; padding: 3px; border-radius: var(--dsw-radius-md); background: var(--mn-hover); } +.documentToolbar > div button { min-height: 28px; border: 0; border-radius: var(--dsw-radius-sm); padding: 0 12px; color: var(--mn-muted); background: transparent; font-size: 13px; font-weight: 500; line-height: 20px; cursor: pointer; } +.documentToolbar > div button:hover { color: var(--mn-text); } +.documentToolbar > div button[data-active] { color: var(--mn-text); background: var(--mn-layer-1); box-shadow: var(--dsw-elevation-soft); } +.documentToolbar > div b { margin-left: 6px; color: var(--mn-faint); font-size: 12px; font-weight: 400; font-variant-numeric: tabular-nums; } + +.documentWorkspace { display: grid; min-height: 590px; grid-template-columns: minmax(250px, 310px) minmax(0, 1fr); gap: 12px; } +.documentList, +.documentReader { min-width: 0; overflow: hidden; border: .5px solid var(--mn-card-line); border-radius: var(--dsw-radius-lg); } +.documentList { align-self: stretch; } +.documentList > header { display: flex; min-height: 44px; align-items: center; justify-content: space-between; padding: 0 14px; border-bottom: .5px solid var(--mn-line); color: var(--mn-faint); font-size: 12px; line-height: 18px; } +.documentList > header code { color: var(--mn-muted); } +.documentList > button { --mn-provider-color: var(--mn-faint); display: block; width: calc(100% - 12px); margin: 6px 6px 0; border: 0; border-radius: var(--dsw-radius-md); padding: 10px 12px; color: var(--mn-text); background: transparent; text-align: left; cursor: pointer; transition: background-color .15s; } +.documentList > button:hover { background: var(--mn-hover); } +.documentList > button[data-selected] { background: var(--mn-fill); } +.documentList > button > div { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; } +.documentList > button strong { overflow: hidden; font-size: 13px; font-weight: 500; line-height: 20px; text-overflow: ellipsis; white-space: nowrap; } +.documentList > button time { flex: 0 0 auto; color: var(--mn-faint); font-size: 11px; font-variant-numeric: tabular-nums; } +.documentList > button p { display: -webkit-box; margin: 4px 0 6px; overflow: hidden; color: var(--mn-faint); font-size: 12px; line-height: 18px; -webkit-box-orient: vertical; -webkit-line-clamp: 2; } +.documentList > button footer { display: flex; align-items: center; gap: 8px; color: var(--mn-faint); font-size: 11px; line-height: 16px; } +.documentList > button footer code { margin-left: auto; } +.documentList > button footer em { color: var(--mn-danger); font-style: normal; } +.documentListEmpty { display: grid; min-height: 230px; place-items: center; align-content: center; gap: 4px; padding: 24px; color: var(--mn-muted); text-align: center; } +.documentListEmpty > span { margin-bottom: 6px; color: var(--mn-faint); font-size: 24px; line-height: 1; } +.documentListEmpty p { margin: 0; color: var(--mn-faint); font-size: 12px; line-height: 18px; } + +.documentReader { padding: clamp(16px, 2vw, 24px); } +.documentReader > .emptyState { height: 100%; border: 0; background: transparent; } +.documentDetail > header { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; padding-bottom: 14px; border-bottom: .5px solid var(--mn-line); } +.documentDetail > header span { color: var(--mn-faint); font-size: 12px; line-height: 18px; } +.documentDetail > header h3 { margin: 2px 0; font-size: 16px; font-weight: 500; line-height: 24px; } +.documentDetail > header p { margin: 0; color: var(--mn-faint); font-size: 12px; line-height: 18px; } +.documentDetail > dl { display: grid; grid-template-columns: 2fr .45fr .8fr .55fr; margin: 12px 0; border: .5px solid var(--mn-line); border-radius: var(--dsw-radius-md); overflow: hidden; } +.documentDetail > dl > div { min-width: 0; padding: 8px 10px; } +.documentDetail > dl > div + div { border-left: .5px solid var(--mn-line); } +.documentDetail dt { margin-bottom: 2px; color: var(--mn-faint); font-size: 11px; line-height: 16px; } +.documentDetail dd { margin: 0; overflow: hidden; font-size: 12px; line-height: 18px; text-overflow: ellipsis; white-space: nowrap; } +.documentSources { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 12px 0; } +.documentSources > span { margin-right: 4px; color: var(--mn-faint); font-size: 12px; } +.documentSources code, +.documentArchiveReceipt code { padding: 1px 8px; border-radius: 999px; color: var(--mn-muted); background: var(--mn-fill); font-size: 11px; line-height: 17px; } +.markdownBody { min-height: 310px; margin: 16px 0 0; overflow-wrap: anywhere; padding: clamp(16px, 2.5vw, 28px); border: .5px solid var(--mn-line); border-radius: var(--dsw-radius-md); color: var(--mn-text); font-size: 14px; line-height: 1.75; } +.markdownBody > :first-child { margin-top: 0; } +.markdownBody > :last-child { margin-bottom: 0; } +.markdownBody h1, +.markdownBody h2, +.markdownBody h3, +.markdownBody h4 { margin: 1.5em 0 .6em; color: var(--mn-text); font-weight: 500; line-height: 1.35; } +.markdownBody h1 { padding-bottom: .35em; border-bottom: .5px solid var(--mn-line); font-size: 1.6em; } +.markdownBody h2 { padding-bottom: .3em; border-bottom: .5px solid var(--mn-line); font-size: 1.35em; } +.markdownBody h3 { font-size: 1.15em; } +.markdownBody p, +.markdownBody ul, +.markdownBody ol, +.markdownBody blockquote, +.markdownBody table, +.markdownBody pre { margin: .85em 0; } +.markdownBody ul, +.markdownBody ol { padding-left: 1.6em; } +.markdownBody li + li { margin-top: .3em; } +.markdownBody blockquote { margin-inline: 0; padding: .1em 1em; border-left: 2px solid var(--mn-line-strong); color: var(--mn-muted); } +.markdownBody code { padding: .15em .4em; border-radius: var(--dsw-radius-xs); color: var(--mn-text); background: var(--dsw-alias-markdown-inline-code, var(--mn-fill)); font: .88em/1.55 var(--mn-code); } +.markdownBody pre { max-width: 100%; overflow: auto; padding: 12px 16px; border-radius: var(--dsw-radius-md); background: var(--dsw-alias-markdown-code-block, var(--mn-fill)); } +.markdownBody pre code { padding: 0; background: transparent; font-size: 12px; } +.markdownBody a { color: var(--dsw-alias-link, var(--mn-accent)); text-decoration-thickness: 1px; text-underline-offset: 3px; } +.markdownBody hr { margin: 1.8em 0; border: 0; border-top: .5px solid var(--mn-line); } +.markdownBody table { display: block; max-width: 100%; overflow-x: auto; border-collapse: collapse; } +.markdownBody th, +.markdownBody td { padding: 8px 10px; border: .5px solid var(--mn-line); text-align: left; vertical-align: top; } +.markdownBody th { background: var(--mn-fill); font-weight: 500; } +.markdownBody img { max-width: 100%; height: auto; border-radius: var(--dsw-radius-sm); } +.documentArchiveReceipt { margin: 12px 0; padding: 10px 12px; border-radius: var(--dsw-radius-md); background: color-mix(in srgb, var(--mn-success) 8%, transparent); } +.documentArchiveReceipt p { margin: 2px 0 8px; color: var(--mn-muted); font-size: 12px; line-height: 18px; } +.documentArchiveReceipt div { display: flex; flex-wrap: wrap; gap: 6px; } +.documentDanger { display: flex; min-height: 56px; align-items: center; justify-content: flex-end; gap: 8px; margin-top: 12px; padding-top: 12px; border-top: .5px solid var(--mn-line); } +.documentDanger > div { margin-right: auto; } +.documentDanger strong { display: block; font-size: 13px; font-weight: 500; line-height: 20px; } +.documentDanger p { margin: 2px 0 0; color: var(--mn-faint); font-size: 12px; line-height: 18px; } +.documentDanger > span { margin-right: auto; color: var(--mn-danger); font-size: 12px; } + +.documentEditor { margin-bottom: 12px; padding: 16px; border: .5px solid var(--mn-card-line); border-radius: var(--dsw-radius-lg); } +.documentReader > .documentEditor { margin: 0; border: 0; padding: 0; background: transparent; } +.documentEditor > header { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 12px; } +.documentEditor h3 { margin: 0; font-size: 14px; font-weight: 500; line-height: 22px; } +.documentEditor header p { margin: 2px 0 0; color: var(--mn-faint); font-size: 12px; line-height: 18px; } +.documentEditor header > span, +.documentEditor header > code { padding: 1px 8px; border-radius: 999px; color: var(--mn-muted); background: var(--mn-fill); font-size: 11px; font-weight: 500; line-height: 17px; } +.documentEditor label { display: grid; gap: 6px; margin-top: 12px; color: var(--mn-muted); font-size: 12px; font-weight: 500; line-height: 18px; } +.documentEditor textarea { resize: vertical; line-height: 1.65; } +.documentEditorMeta { display: grid; grid-template-columns: .8fr 1.2fr; gap: 12px; } +.documentEditorMeta label { margin: 0; } +.documentEditor footer { display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; } + +@media (max-width: 1000px) { + .documentWorkspace { grid-template-columns: minmax(220px, 270px) minmax(0, 1fr); } + .documentDetail > dl { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .documentDetail > dl > div + div { border-left: 0; } + .documentDetail > dl > div:nth-child(even) { border-left: .5px solid var(--mn-line); } + .documentDetail > dl > div:nth-child(n + 3) { border-top: .5px solid var(--mn-line); } +} + +@media (max-width: 760px) { + .documentSummary { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .documentCapacity { grid-column: 1 / -1; } + .documentToolbar { align-items: stretch; flex-direction: column; } + .documentToolbar form { min-width: 0; } + .documentToolbar > div, +.documentToolbar > button { width: 100%; } + .documentToolbar > div button { flex: 1; } + .documentWorkspace { min-height: 0; grid-template-columns: 1fr; } + .documentList { max-height: 330px; overflow: auto; -webkit-overflow-scrolling: touch; } + .documentReader { min-height: 430px; } + .documentEditorMeta { grid-template-columns: 1fr; } +} + +@media (max-width: 520px) { + .documentDetail > header, +.documentDanger { align-items: flex-start; flex-direction: column; } + .documentDetail > header > div:last-child, +.documentDetail > header button { width: 100%; } + .documentDanger > div, +.documentDanger > span { margin-right: 0; } + .documentDanger > button { width: 100%; } + .documentDetail > dl { grid-template-columns: 1fr; } + .documentDetail > dl > div:nth-child(even) { border-left: 0; } + .documentDetail > dl > div + div { border-top: .5px solid var(--mn-line); } + .markdownBody { padding: 16px; font-size: 13px; } +} diff --git a/dsh/plugins/dsh-mnemon-source-documents/presentation/sidebar.module.css b/dsh/plugins/dsh-mnemon-source-documents/presentation/sidebar.module.css new file mode 100644 index 000000000..9f8cf5d36 --- /dev/null +++ b/dsh/plugins/dsh-mnemon-source-documents/presentation/sidebar.module.css @@ -0,0 +1,27 @@ +.shell .modal form[class*='documentEditor'] { + margin: 0; + padding: 0; + border: 0; + border-radius: 0; + background: transparent; +} +.shell .modal form[class*='documentEditor'] > header { display: none; } + +.shell [class*='documentWorkspace'] { + height: clamp(520px, calc(100dvh - 220px), 760px); + min-height: 520px; + align-items: stretch; +} +.shell [class*='documentList'], +.shell [class*='documentReader'] { + min-height: 0; + overflow-y: auto; + overscroll-behavior: contain; + scrollbar-gutter: stable; +} + +@media (max-width: 760px) { + .shell [class*='documentWorkspace'] { height: auto; min-height: 0; } + .shell [class*='documentList'] { overflow-y: auto; scrollbar-gutter: auto; } + .shell [class*='documentReader'] { overflow: visible; scrollbar-gutter: auto; } +} diff --git a/dsh/plugins/dsh-mnemon-source-documents/src/client.ts b/dsh/plugins/dsh-mnemon-source-documents/src/client.ts new file mode 100644 index 000000000..16b0fc72e --- /dev/null +++ b/dsh/plugins/dsh-mnemon-source-documents/src/client.ts @@ -0,0 +1,3 @@ +export * from './client/pages.tsx' +export type * from './client/api.ts' +export * from './client/ui.tsx' diff --git a/dsh/plugins/dsh-mnemon-source-documents/src/client/api.ts b/dsh/plugins/dsh-mnemon-source-documents/src/client/api.ts new file mode 100644 index 000000000..9ac07baee --- /dev/null +++ b/dsh/plugins/dsh-mnemon-source-documents/src/client/api.ts @@ -0,0 +1,10 @@ +import type { DocumentMutation, DocumentMutationResult, DocumentSearchResult, DocumentSnapshot, DocumentView } from '../contracts.ts' + +/** Source-owned structural page API; the default bundle may supply agent-assisted callbacks. */ +export interface DocumentsPageClient { + documents(): Promise + document(id: string): Promise + searchDocuments(query: string, includeArchived?: boolean, limit?: number): Promise + mutateDocument(request: DocumentMutation): Promise + archiveDocument(id: string): Promise +} diff --git a/dsh/plugins/dsh-mnemon-source-documents/src/client/pages.tsx b/dsh/plugins/dsh-mnemon-source-documents/src/client/pages.tsx new file mode 100644 index 000000000..e441f7c0e --- /dev/null +++ b/dsh/plugins/dsh-mnemon-source-documents/src/client/pages.tsx @@ -0,0 +1,217 @@ +import { css, sidebarCss, useT } from './presentation.ts' +import type { JSX } from 'react' +import { useCallback, useEffect, useId, useLayoutEffect, useRef, useState, type FormEvent } from 'react' +import Markdown from 'markdown-to-jsx' +import { type DocumentRecord, type DocumentSnapshot, type DocumentView } from '../contracts.ts' +import type { DocumentsPageClient } from './api.ts' +import { useRequestVersion, appearanceClass, useLocale, humanBytes, message, PageHeader, ProgressiveFooter, SidebarModal, EmptyState, SearchField } from 'dsh-mnemon/client' + +const SAFE_LINK_PATTERN = /^(?:https?:|mailto:|#|\/)/iu + +function safeLink(href: string | null | undefined): string | undefined { + if (href == null) return undefined + const value = href.trim() + return SAFE_LINK_PATTERN.test(value) ? value : undefined +} + +/** Render managed Markdown without raw HTML and with a deliberately small link surface. */ +function DocumentMarkdown(props: { content: string }): JSX.Element { + return ( +
+ { + const target = safeLink(href) + return target === undefined + ? {children} + : {children} + }, + }, + }, + }}>{props.content} +
+ ) +} + +type DocumentListItem = DocumentRecord & { healthy?: boolean; excerpt: string } + +export function DocumentsPage(props: { client: DocumentsPageClient; revision: number; writeEnabled: boolean; sessionId?: string; canCreate?: boolean; documentId?: string; onMutate: () => void }): JSX.Element { + const t = useT() + const locale = useLocale() + const documentCreateFormId = useId() + const documentEditFormId = useId() + const pageSize = 8 + const readerRef = useRef(null) + const [snapshot, setSnapshot] = useState(null) + const [items, setItems] = useState([]) + const [visibleLimit, setVisibleLimit] = useState(pageSize) + const [selectedId, setSelectedId] = useState(props.documentId ?? null) + const [selected, setSelected] = useState(null) + const [status, setStatus] = useState<'active' | 'archived'>('active') + const [query, setQuery] = useState('') + const [loading, setLoading] = useState(true) + const [saving, setSaving] = useState(false) + const [error, setError] = useState(null) + const [notice, setNotice] = useState(null) + const [composing, setComposing] = useState(false) + const [editing, setEditing] = useState(false) + const [confirmArchive, setConfirmArchive] = useState(false) + const [title, setTitle] = useState('') + const [description, setDescription] = useState('') + const [content, setContent] = useState('') + const [sources, setSources] = useState('') + const displayRequests = useRequestVersion() + + const display = useCallback(async (nextQuery: string, nextStatus: 'active' | 'archived') => { + const request = displayRequests.begin() + setLoading(true); setError(null); setVisibleLimit(pageSize) + try { + const current = await props.client.documents() + const records = nextQuery.trim() === '' + ? current.documents + : (await props.client.searchDocuments(nextQuery, nextStatus === 'archived')).results + const filtered = records.filter(record => record.status === nextStatus) + .sort((left, right) => Date.parse(right.createdAt) - Date.parse(left.createdAt)) + if (!displayRequests.isCurrent(request)) return + setSnapshot(current) + setItems(filtered) + setSelectedId(previous => previous !== null && filtered.some(record => record.id === previous) ? previous : filtered[0]?.id ?? null) + } catch (reason) { + if (!displayRequests.isCurrent(request)) return + setError(message(reason)); setSnapshot(null); setItems([]); setSelectedId(null) + } finally { + if (displayRequests.isCurrent(request)) setLoading(false) + } + }, [displayRequests, pageSize, props.client]) + + useEffect(() => { void display(query, status) }, [display, props.revision, status]) + useEffect(() => { + setSelected(null) + if (selectedId === null) return + let active = true + void props.client.document(selectedId).then(value => { if (active) setSelected(value) }).catch(reason => { if (active) setError(message(reason)) }) + return () => { active = false } + }, [props.client, selectedId, props.revision]) + useLayoutEffect(() => { + if (readerRef.current !== null) readerRef.current.scrollTop = 0 + }, [selectedId]) + useEffect(() => { + if (selectedId === null) return + const index = items.findIndex(item => item.id === selectedId) + if (index >= visibleLimit) setVisibleLimit(Math.ceil((index + 1) / pageSize) * pageSize) + }, [items, pageSize, selectedId, visibleLimit]) + + const resetComposer = () => { setTitle(''); setDescription(''); setContent(''); setSources(''); setComposing(false) } + const startComposer = () => { setTitle(''); setDescription(''); setContent(''); setSources(''); setEditing(false); setComposing(true) } + const sourcePaths = (value: string) => value.split(/\r?\n|,/gu).map(path => path.trim()).filter(Boolean) + + const create = async (event: FormEvent) => { + event.preventDefault(); setSaving(true); setError(null); setNotice(null) + try { + const result = await props.client.mutateDocument({ action: 'create', title, description, content, sourcePaths: sourcePaths(sources) }) + setNotice(result.maintenance === undefined ? t('documents.created') : t('documents.createdAfterArchive', { count: result.maintenance.archivedDocumentIds.length })) + setStatus('active'); setQuery(''); resetComposer(); props.onMutate(); await display('', 'active'); setSelectedId(result.document.id) + } catch (reason) { setError(message(reason)) } finally { setSaving(false) } + } + + const beginEdit = () => { + if (selected === null) return + setTitle(selected.title); setDescription(selected.description); setContent(selected.content); setSources(selected.sourcePaths.join('\n')); setEditing(true); setComposing(false); setConfirmArchive(false) + } + + const update = async (event: FormEvent) => { + event.preventDefault() + if (selected === null) return + setSaving(true); setError(null); setNotice(null) + try { + const result = await props.client.mutateDocument({ action: 'update', id: selected.id, title, description, content, sourcePaths: sourcePaths(sources) }) + setNotice(result.maintenance === undefined ? t('documents.updated') : t('documents.updatedAfterArchive', { count: result.maintenance.archivedDocumentIds.length })) + setEditing(false); props.onMutate(); await display(query, status); setSelectedId(result.document.id) + } catch (reason) { setError(message(reason)) } finally { setSaving(false) } + } + + const archive = async () => { + if (selected === null) return + setSaving(true); setError(null); setNotice(null) + try { + const result = await props.client.archiveDocument(selected.id) + setNotice(t('documents.archived', { spaces: result.maintenance?.memoryBodyIds.join(', ') || '—' })) + setConfirmArchive(false); setStatus('archived'); setQuery(''); props.onMutate(); await display('', 'archived'); setSelectedId(result.document.id) + } catch (reason) { setError(message(reason)) } finally { setSaving(false) } + } + + const usage = snapshot === null ? 0 : Math.min(100, snapshot.activeBytes / snapshot.limitBytes * 100) + const activeCount = snapshot?.activeCount ?? 0 + const archivedCount = snapshot?.archivedCount ?? 0 + const composer =
void create(event)}> +

{t('documents.newTitle')}

{t('documents.editorHint')}

{t('documents.managedCopy')}
+
+ +