DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

Projection Persist — DeepSeek Harness 插件(DSH Plugin)
DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录
← Plugins
P

dsh-projection-persist

Projection Persist

DSH Web 客户端的三项修复,无需修改 DSH 安装:(1) session 投影值(标题)在 Host generation 重置后仍能保留,(2) session 列表重建合并为每个动画帧一次,而不是每个 control frame 一次(完整重建次数减少约 6.3 倍),(3) 即使此 WebView2 host 无条件报告 prefers-reduced-motion: reduce,运行状态微调器仍会旋转。

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add github:iuuuuuuuu/dsh-projection-persist#6ef889d7a14dae59ec9de76fb664868ffb0773fa
README兼容性版本

兼容性与来源证明

Projection Persist 以 dsh-projection-persist 发布,当前版本为 1.0.0。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
web
发布来源
github
Registry 更新时间
2026/10/5

版本

1.0.0stable
2026/10/5

相关插件

正在加载相关插件…

最新版
1.0.0
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
未提供
文件数
未提供
Surface
web
许可证
MIT
发布源
github
GitHub
★ 0
周下载
0
最近提交
2026/10/15
查看源码 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题

相关插件

继续浏览 ui-customization 分类下经过校验的插件。

Client Ui Skill Explorer@linxin666/dsh-client-ui-skill-explorerDSH 技能中心:按来源(bundled / project / user / custom / runtime)浏览已加载的技能,在 Web GUI 面板中启用或禁用、创建和删除技能。Remote Web Ui@linxin666/dsh-remote-web-ui通过扫码配对访问 dsh Web GUI,共享一个官方界面:设置按钮旁的二维码可将手机和 PC 配对到同一个 Web GUI(手机采用竖屏触控适配层,PC 使用完整桌面界面),通过一次性令牌和 rClient Ui Task Board@linxin666/dsh-client-ui-task-board面向 DSH Web GUI 的主机权威任务面板,支持实际会话执行、主机 cron 调度以及可选的跨平台空闲睡眠保护;以挂载方式提供,无需修改 DSH 源代码。Web All@linxin666/dsh-web-allDSH Web UI 全家桶聚合插件:一键安装全部功能插件(task-board / git-graph / pet / remote-web-ui / web-ui-settings / skin-center / community-plugins / compat shim)。compat 桥接层已并入本包(src/client),无需独立 compat npm 包。

README

dsh-projection-persist

English | 简体中文

三个修复的集合(宿主半边 + 浏览器半边,不修改 DSH 安装目录里的任何文件):

  1. 会话标题在一次连接重建(Host generation reset)中不再掉成「未命名」;
  2. 会话列表不再被控制帧洪水逐帧全量重建(合帧到 rAF,实测约 6.3× 降幅);
  3. 运行中的转圈动画恢复旋转(本机 WebView2 恒报 prefers-reduced-motion: reduce)。

症状:会话列表里的标题会短暂变成「未命名」(英文界面是 Untitled),随后自行恢复。

这是一个 DeepSeek Harness(DSH)插件,包含宿主半边与浏览器半边。


症状与根因

会话列表闪一下、标题短暂变成「未命名」,本质是连接被重建时投影(projection)被清空:

  1. 宿主 dsh-api-gateway 对 /api/remote.mux 的 WebSocket 心跳默认 2000 ms,且 MAX_MISSED_HEARTBEATS 硬编码为 2。
  2. 客户端主线程只要卡顿 > 4 s,宿主即判定连接已死并 socket.terminate()。
  3. 浏览器侧收到 close 1006,ConnectionController 重建 connection generation。
  4. SessionManager.handleConnected() 对每一个常驻投影 store 调用 store.clear()。
  5. ProjectionValueStore#clear() 的实现是删除每一行,于是标题丢失,回落到 t("session.untitled") = 「未命名」,直到 refreshList() 返回才恢复。

关键代码锚点(版本 0.2.0-rc.2):

  • @deepseek-ai/dsh-api-gateway/lib/index.js — startHeartbeat()、MAX_MISSED_HEARTBEATS = 2
  • @deepseek-ai/dsh-api-session-controller/lib/types/client/sessions/manager.js — handleConnected() 里的 store.clear() 循环
  • @deepseek-ai/dsh-api-session-controller/lib/client.js — ProjectionValueStore#clear()

这个插件做了什么

替换 ProjectionValueStore.prototype.clear,保留每一行的值,只丢弃序号水位:

function retainValues() {
  for (const [key, row] of this.rows) {
    if (row.kind === "sequenced") this.rows.set(key, { kind: "cached", value: row.value });
    this.changed(key);
  }
}
  • 值保留 ⇒ 标题仍可读,buildListSnapshot() 不再回落到「未命名」。
  • 水位丢弃 ⇒ 紧随其后的 refreshList() 送来的 cached 块可以照常覆盖。
  • 每条 sequenced 行原地降级为 cached 行。

为什么不能简单地改成空操作(no-op):apply() 会拒绝 seq 不更新的帧 (if (row?.kind === "sequenced" && seq <= row.seq) return;), 保留旧水位会把新一代连接的低 seq 帧永久挡住 —— 所以必须保留「重置水位」的语义。

同时它也会覆盖懒创建的 store(包装 manager.projectionStore),并在卸载时完整回滚。

安装

从 GitHub 安装(推荐)

在 DSH profile 目录里用 pnpm 添加本仓库:

dsh plugin --profile <你的 profile> add github:iuuuuuuuu/dsh-projection-persist

或者直接进 profile 目录手工装:

cd "$DSH_HOME/profiles/<你的 profile>"
pnpm add github:iuuuuuuuu/dsh-projection-persist

安装后把包名加进 profile 的 package.json → dsh.profile.bundles:

{
  "dsh": {
    "profile": {
      "bundles": [
        "dsh-projection-persist"
      ]
    }
  }
}

改 dsh.profile.bundles 会触发热重载,通常不需要重启。

手工安装(不经过 pnpm)

把本仓库复制成 $DSH_HOME/profiles/<你的 profile>/node_modules/dsh-projection-persist/, 再把 "dsh-projection-persist" 追加进同一 profile package.json 的 dsh.profile.bundles。

验证

插件生效时,宿主半边会在 <DSH_HOME>/logs/projection-persist.log 追加一行安装记录:

{"kind":"install","ok":true,"revision":9,"installed":"instance","wrapped":true,"swept":42,"prototypePatched":true,"stores":42,"framePatch":"patched","spinner":"installed","watch":"watching"}

也可以直接查诊断端点(GET 返回当前 revision):

curl http://127.0.0.1:3080/dsh-projection-persist
# {"ok":true,"name":"projection-persist","revision":9}

回滚

两种方式都是热生效,任选其一:

  1. 删掉 node_modules/dsh-projection-persist/cordis.patch.yml 里的 - insert: 行;
  2. 从 profile 的 dsh.profile.bundles 里移除 "dsh-projection-persist",或直接删掉整个包目录。

插件卸载时会自动恢复原 clear() 与 manager.projectionStore。

兼容性

  • 只依赖 sessions 服务,不声明任何 peerDependencies,因此不会被兼容性检查判为不兼容。
  • 不修改 DSH 安装目录,DSH 升级不会覆盖它。
  • 已验证版本:@deepseek-ai/dsh-api-session-controller 0.2.0-rc.2。
  • 只影响管理器持有的投影 store;会话自身持有的 store 不在此列(该 store 的 seqOf 有其它消费者)。

另外两个修复(同一个客户端半边)

1. 会话列表整表重建合帧

SessionManager 的列表 Notifier 原本用 markDirty()(microtask 合帧)。控制帧是一条条独立的 socket task, microtask 根本合不掉它们,于是 8–15 个控制帧 ⇒ 8–15 次 projectList() 全量重建 + 无条件 list.set()。 插件把列表 notifier 的 markDirty 换成上游预留但从未启用的 markFrameDirty()(rAF 合帧,last-write-wins 不变, ensureFresh() 保证同步读仍然新鲜)。Session.notifier 不动 —— 它的同步契约由 beginSubmission/finishSubmission 依赖。

实测(交错 A/B × 3 轮,8 s/窗):

触发次数整表重建list.set每次触发重建
打补丁17614340.080
未打补丁2881451650.503

约 6.3×(按观测帧率折算约 18/s → 约 1.8/s)。

2. 运行中的转圈动画

在 WebView2 宿主里 matchMedia("(prefers-reduced-motion: reduce)") 恒为 true, 命中 StateDot.module.css 的 @media (prefers-reduced-motion: reduce) { .spinnerMotion, .spinnerArc { animation: none } }, 于是会话正在跑的时候不转圈。插件注入一段自有 <style>,在同一个 media 块内用 !important 重声明关键帧 (选择器限定 svg[data-state="ongoing"] > g 与 circle[class*="spinnerArc"],不误伤另外 9 个合法的 reduced-motion 块), 并在 <head> 上挂一个 childList MutationObserver,元素一旦被谁删掉就立刻重装(实测 healedAtMs: 83)。

单独关掉转圈修复:localStorage["dsh-projection-persist.spinner"] = "off"。

心跳阈值(另一条链路,本插件不负责)

把 websocketHeartbeatIntervalMs 调大只会降低重连发生频率,不能消除重连本身。两者叠加才是完整防线:

  • 调大心跳间隔 ⇒ 减少重连频率;
  • 本插件 ⇒ 重连真的发生时标题不再丢。

License

MIT