Skip to content

Plugin market / plugin updates never appear after restart — generation peer validation false-positives freeze the projection #293

Description

@juanyezz

[Bug] Plugin market / plugin updates never appear after restart — generation peer validation false-positives freeze the projection

DSH Desktop version: 0.7.2
Platform: Windows 10/11
Related component: dsh-desktop-market-installergenerations/installer.mjsverifyGenerationPeers
Plugin market version: dshmarket 1.41.0
Severity: High (blocks installing/updating any generation plugin via the market)


中文摘要 / Summary (中文)

安装插件市场(dsh-market)后,界面提示“重启一次 Harness,即可加载完整的插件市场界面”,但重启后一直不出现。用市场安装/更新其他插件时也一样:装好后界面不加载。

根因已定位: verifyGenerationPeers() 对 DSH Desktop 的运行环境产生大量误报,导致 migrateProfileToGenerations() 一直失败(进入 deferred-failure),进而让 prepareGenerationsForLaunch()(即把插件投影进 dsh.profile.bundles 那一步)被整体跳过。结果新装/更新的 generation 插件永远进不了 bundles,Harness 启动时不会组合加载其界面。


Actual behavior (描述)

设置 → 插件市场 一直显示:

插件市场已安装 / 重启一次 Harness,即可加载完整的插件市场界面

重启后界面仍不出现。市场内安装/更新任何社区插件后,插件同样“已安装但界面不加载”。

Expected behavior (期望)

重启后插件市场完整界面(浏览/搜索/安装/更新)出现;市场内安装/更新的插件应进入 dsh.profile.bundles 并在重启后被组合加载。


Root cause (根因,已严格验证)

dsh-desktop-market-installer/generations/installer.mjsverifyGenerationPeers() 在以下两种情形误报:

1. Type-only / 纯工具包被误判为“依赖缺失”

require.resolve() 对以下包抛错,但包其实已安装(生成树/closure 里 package.json 都存在),只是没有运行时 exports 入口:

  • @types/node, @types/*, undici-types, math-intrinsics, dunder-proto

verifyGenerationPeersrequire.resolve 抛错统一当成 resolved === undefined,再报 "does not resolve from the generation or installation closure"。

2. 解析到应用树被误判为“逃逸”

DSH Desktop 的安装 closure($DSH_HOME/profiles/node_modules)里的宿主包(reactreact-dom@deepseek-ai/*,以及 typescript@lexical/internal 等普通依赖)是 junction,指向 Desktop 应用自带的 resources/app/node_modules。Windows 上 require.resolve跟随 junction 直接落到应用树,于是:

@deepseek-ai/dsh-agent resolves outside the installation closure: D:\...\DSH Desktop\resources\app\node_modules\@deepseek-ai\dsh-agent\lib\index.js
@deepseek-ai/dsh-llm resolves outside the installation closure: ...
@deepseek-ai/dsh-skill-filesystem resolves outside the installation closure: ...

verifyGenerationPeersrealpath() 比较,realpath 一解析就把路径拉出 closure 的名义目录,误判为“逃逸”。这正是 aegis 插件报错的直接原因(它依赖 @deepseek-ai/dsh-agent@deepseek-ai/dsh-llm@deepseek-ai/dsh-skill-filesystem)。

连锁后果

任何一个 generation 插件校验失败 → migrateProfileToGenerations() 返回 deferred-failureprepareGenerationsForLaunch()projectGenerations,把插件写进 dsh.profile.bundles)被跳过。因此本次安装/更新的插件永远进不了 bundles

实际环境里,除 aegis 外,dsh-better-sidebardsh-builtin-browser@openviking/dsh-memory-plugin 的 generation 也因上述两类误报而校验失败,迁移整体冻结。


Reproduction steps (复现步骤)

  1. 安装 DSH Desktop 0.7.2
  2. 设置 → 插件市场,安装 dsh-market(提示重启生效)。
  3. 重启,进入插件市场,安装任一社区插件(例如 dsh-better-sidebar)。
  4. 安装完成提示“重启后生效”,再次重启。
  5. 观察:该插件界面不出现;profiles/web/package.jsondsh.profile.bundles 里没有该插件;.generations-deferred.json 出现,reasonaegis failed peer validation: ... resolves outside the installation closure

Suggested fix (建议修复)

放宽 verifyGenerationPeers() 对 Desktop 布局的判定,同时保留“依赖真缺失”的报错:

  1. type-only 容忍:require.resolve 失败时,若该依赖作为真实目录(含 package.json)存在于 generation 或 installation closure,则不报“缺失”(type-only / 纯工具包运行时不会被 require,无需报错)。真正缺失的包仍应报错。
  2. junction 宿主容忍: 依赖解析到应用树时,若它在 closure 里存在指向该位置的 junction(链接目标),则视为宿主提供的正常布局,不判为“逃逸”。该判定对 host 单例(react@deepseek-ai/*)与普通依赖统一适用。

验证:18 个 generation 目录全部通过;阴性对照(依赖真缺失)仍正确报错,安全边界未被削弱。

(如需具体 diff,可见本 issue 附带的补丁说明。)


Impact & why this matters (影响)

不修复此问题,DSH Desktop 的插件市场只能安装、无法真正生效——所有 generation 插件的安装/更新都会被冻结在“已安装但不加载”状态。这会影响任何通过 dsh-market 安装社区插件的用户。感谢确认与修复!


Evidence / 复现证据 (reproduction evidence)

Below are the actual artifacts observed on a 0.7.2 installation. These reflect the pre-fix failing state (presented as evidence of the bug).

1. .generations-deferred.json (freeze marker written on every boot)

{
  "protocol": 4,
  "fingerprint": "1beb0ec7eeaadecd0bffa9aaa4c13298d359f4585ee55480f31ae76994a404ae",
  "reason": "aegis failed peer validation: @deepseek-ai/dsh-agent resolves outside the installation closure: D:\\software\\deepseek harness\\DSH Desktop\\resources\\app\\node_modules\\@deepseek-ai\\dsh-agent\\lib\\index.js; @deepseek-ai/dsh-llm resolves outside the installation closure: D:\\software\\deepseek harness\\DSH Desktop\\resources\\app\\node_modules\\@deepseek-ai\\dsh-llm\\lib\\index.js; @deepseek-ai/dsh-skill-filesystem resolves outside the installation closure: D:\\software\\deepseek harness\\DSH Desktop\\resources\\app\\node_modules\\@deepseek-ai\\dsh-skill-filesystem\\lib\\index.js",
  "failedAt": "2026-09-03T15:22:31.843Z",
  "retryAfter": "2026-09-03T21:22:31.843Z"
}

2. profiles/web/package.jsondsh.profile.bundles (the installed plugin is missing)

The plugin market (dshmarket) and the community plugins are installed and in desired.json, but dshmarket is absent from dsh.profile.bundles, so the harness never composes its UI:

"dsh": {
  "profile": {
    "bundles": [
      "@deepseek-ai/dsh-base",
      "@deepseek-ai/dsh-web-app",
      "aegis",
      "@linxin666/dsh-client-ui-git-graph",
      "@linxin666/dsh-client-ui-skill-explorer",
      "@openviking/dsh-memory-plugin",
      "dsh-better-sidebar",
      "dsh-builtin-browser",
      "dsh-cost-meter",
      "@linxin666/dsh-client-ui-task-board"
      // note: dshmarket is missing here
    ]
  }
}

3. The junction host-package layout (Root cause of the "escaped" misreport)

$DSH_HOME/profiles/node_modules/@deepseek-ai/dsh-agent
  -> LinkType: Junction
  -> Target:   D:\software\deepseek harness\DSH Desktop\resources\app\node_modules\@deepseek-ai\dsh-agent

$DSH_HOME/profiles/node_modules/@deepseek-ai/dsh-llm
$DSH_HOME/profiles/node_modules/@deepseek-ai/dsh-skill-filesystem
$DSH_HOME/profiles/node_modules/react
  -> all junctions pointing into the bundled app tree

4. Type-only / no-exports packages that failed require.resolve but are installed

@types/node, @types/*, undici-types, math-intrinsics, dunder-proto
  -> present as real directories with a package.json in the generation & closure,
     but require.resolve throws (MODULE_NOT_FOUND / ERR_PACKAGE_PATH_NOT_EXPORTED)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions