🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI
广告☁️ 云服务器特惠阿里云首购 8 折 · 腾讯云合作特惠
DeepSeek Harness Hub
← 返回列表

XiaoYuOvO/dsh-external-session

DeepSeek Harnessspec-screened扫描:中风险在 GitHub 查看 ↗
未验证

DSH 项目外部会话持久化插件Host 平面:把属于指定项目的会话日志原生存放到项目仓库内默认建议…

尚未跑自动兼容性验证,可查看页面内的依赖与入口分析。 · 最近上游提交 2026/9/4 · 已提供中文文档

DeepSeek Harness 的可移植、由 Git 支持的外部会话持久化插件

综合分
28.3
GitHub 分
28.3
用户评分
—
★ Stars
0
周下载量
—
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add XiaoYuOvO/dsh-external-session
该插件未发布到 npm,走 GitHub 源安装(pnpm 若拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑)
信任档位:已验证本站已于 0 天前真实安装成功
是什么
dsh 原生插件 · tool
装得上吗
本站已真实安装成功(非静态推断)
安全吗
本站尚未对该插件做风险分级(暂未覆盖,不等同于无风险)
还在维护吗
活跃:最近一次提交在 21 天前

档位由下列信号合成:本站实装验证(真实安装,当前最高到 L4)· 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。下方各区块是它的证据明细。 验证判据与等级说明 →

🟢实装验证通过· 2026/9/26
由本站实装验证器在真实 dsh 环境安装成功,非静态推断。
数据截至 2026/9/21(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
依赖的 DSH / Cordis 模块
@deepseek-ai/schemastery@deepseek-ai/cordis@deepseek-ai/dsh-session@deepseek-ai/dsh-session-persistence@deepseek-ai/dsh-tools@deepseek-ai/dsh-client-runtime
用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动

README

由 DeepSeek 最新模型翻译生成
dsh-external-session

DSH 项目外部会话持久化插件(Host 平面):把属于指定项目的会话日志原生存放到项目仓库内(默认建议 /.dsh-sessions/),随 git clone/push 跨端同步;其它 workspace 的会话行为完全不变。

目录

- 它做什么
- Spike 结论:路线 A 可行
- 架构
- 安装
- 配置
- 命令与工具
- git 同步纪律(必读)
- 硬性约束与可逆卸载
- 与其它会话工具的互操作
- 自测记录(§9 验收清单)
- 已知取舍

它做什么

DSH 会话持久化默认只有一个全局根 $DSH_HOME/sessions,所有 workspace 的会话按 cwd 编码的子目录组织其下,日志是多 zstd frame 追加写的 JSONL。

本插件替换默认 @deepseek-ai/dsh-session-persistence-jsonl 后端,唯一新增的决策点是按会话 cwd 选存储根:

rootFor(cwd):
cwd 命中某 external 条目(位于其 cwdPrefix 下)→ 该条目 root
否则                                         → internalRoot(默认 $DSH_HOME/sessions)

于是:

1. 在属于"外部项目"的 cwd 下新建会话 → 日志落该项目的 .dsh-sessions/,内部 sessions 下不出现该项目目录。
2. 外部根中的会话(含 git 同步来的)重启后自动出现在会话列表(等价"导入",无需手工复制),可续聊、可 fork。
3. fork 子会话自动落同一外部根(SessionStore.fork() 继承父 cwd,路由按 cwd 生效,无需劫持 fork)。
4. 外部文件与内置 jsonl 后端保持相同日志协议,可互读(旧布局见自测 §5;portable 布局由本插件读取)。

Spike 结论:路线 A 可行

按 §5 的 spike 步骤核对本机部署(@deepseek-ai/dsh- 全部 0.1.1-rc.2):

1. session-persistence-jsonl 行位于 @deepseek-ai/dsh-base 的 cordis.patch.yml(bundle 层),行 id = session-persistence-jsonl,config 为 { root: !!js dshHomePath('sessions') }。
2. profile 的 cordis.patch.yml 在所有 bundle 层之后应用,且本 profile 已实测使用过 disabled: true(dsh-at-file)与 insert(多行)两种补丁能力。
3. JsonlSessionPersistence extends SessionPersistence extends Cordis Service,super(ctx, "sessionPersistence") —— 单 provider 语义。禁用默认行 + insert 本后端 = 进程内恰好一个 sessionPersistence 提供者。
4. session-query 的 SessionCorpus 通过 ctx.inject(["sessionPersistence"], …) 可选绑定,路由后端的 list() 扫外部根即可让外部会话自动进列表,前端零改动。
5. 所有 peer 依赖(@deepseek-ai/{cordis,schemastery,dsh-session,dsh-session-persistence,dsh-tools})均已提升到 profiles/node_modules,运行时可解析。

结论:走路线 A(路由持久化后端)。 默认 jsonl 行的 disabled 只发生在 profile patch(本包 cordis.patch.yml),可逆。

架构

lib/index.js 内的 RoutedJsonlPersistence 是 stock jsonl 后端的忠实复制,仅在两处插入路由:

- rootFor(cwd):cwd → root 的唯一决策点;locate/create/append/materialize/repair 全部以它取代原固定 this.root。

磁盘布局与跨 clone

外部项目使用与 clone 路径无关的相对目录 key:

/.dsh-sessions/--rel---//session.jsonl.zstd

例如项目根下的 src 会写入 --rel-src--,项目根本身写入 --rel-root--。header 中仍保留 DSH 原生绝对 cwd,读取时按当前 external 条目的 cwdPrefix 映射到当前 clone 的路径;事件内容和 session id 不变。

旧版本按绝对 cwd 生成的目录仍会读取,并在能识别项目后缀时映射到当前 clone。新写入统一使用 portable key。portable 布局依赖本插件的路由后端,不能由未加载本插件的 stock jsonl 后端直接扫描;旧绝对布局仍保持 stock 互读。

磁盘布局 / header 行 / 事件序列化 / 多帧 zstd 追加 / 截断尾帧恢复 / 崩溃截断修复,与 stock 后端保持相同日志协议;portable 目录只改变项目目录 key,由本插件在扫描时解码和重映射。

安装

本插件可直接从 GitHub 安装,也可以使用本地 link。GitHub 安装会把包加入 web profile,并自动识别包内的 dsh.bundle 声明:

dsh plugin --profile web add github:XiaoYuOvO/dsh-external-session

也可以在 profiles/web 目录执行本地安装:

pnpm add "link:../../plugins/dsh-external-session"

安装后需要在 profile patch 中配置要路由的项目:

2. bundles:把 "dsh-external-session" 追加进 profiles/web/package.json 的 dsh.profile.bundles 数组。

3. external 配置(profiles/web/cordis.patch.yml,追加):
- id: dsh-external-session
config:
external:
- id: rsprojects-dsh
cwdPrefix: 'H:\RSProjects\deepseek-harness-desktop'
root: 'H:\RSProjects\deepseek-harness-desktop\.dsh-sessions'

4. 重启宿主生效(Host half 改动不热更)。

link: 依赖在本机是 junction 指向插件源码;本包是 ESM、依赖 @deepseek-ai/ peer,解析链为 plugins/dsh-external-session/node_modules/@deepseek-ai → profiles/node_modules/@deepseek-ai(junction)。若 pnpm 因 github 源依赖被沙箱拦截无法 pnpm install,需手工建这两个 junction(见下),或在网络可用时完整 pnpm install 让 pnpm 处理 peer。
New-Item -ItemType Junction -Path 'profiles/web/node_modules/dsh-external-session' -Target 'G:\Deepseek Harness Desktop\data\dsh\plugins\dsh-external-session'
New-Item -ItemType Junction -Path 'G:\Deepseek Harness Desktop\data\dsh\plugins\dsh-external-session\node_modules\@deepseek-ai' -Target 'G:\Deepseek Harness Desktop\data\dsh\profiles\node_modules\@deepseek-ai'

本包自带的 cordis.patch.yml 只做两件事:disabled: true 关掉默认 session-persistence-jsonl 行,并 insert 本后端行(无 external 配置,默认纯内部根)。external 条目是用户特定配置,放 profile patch。

配置

- id: dsh-external-session
name: dsh-external-session
config:
enabled: true            # false = 不路由,纯内部根(仍提供持久化,等价 stock 行为)
internalRoot: ''         # 空 = $DSH_HOME/sessions
external:                # 外部项目条目(按序匹配,首个命中生效)
- id: rsprojects-dsh   # 展示名,[A-Za-z0-9._-]
cwdPrefix: 'H:\RSProjects\deepseek-harness-desktop'   # 项目根绝对路径
root: 'H:\RSProjects\deepseek-harness-desktop\.dsh-sessions'  # 外部目录绝对路径
packChunks: true
compression: zstd        # 'zstd' | 'none'
registerCommand: true    # 注册 /external-session
registerTools: true      # 注册 external_session_* 工具
registerWeb: true        # 注册 /external-session/* HTTP 路由(Client 导入/查询用)
listCacheTtlMs: 1000     # list() 目录扫描缓存 TTL(新会话落盘时立即失效)

安全校验(失败关闭):

- 路径必须绝对;cwdPrefix / root / internalRoot 已存在部分若经符号链接解析到词法路径之外 → PATH_UNSAFE 拒绝。
- external 条目 id 唯一;root 路径不得与其它条目或 internalRoot 撞车。

命令与工具

/external-session(命令)

| 子命令 | 作用 | 只读 |
| --- | --- | --- |
| status | 列出每个 root、路径、会话数 | ✅ |
| list [] | 列外部会话(或全部) | ✅ |
| move   | 内部↔外部迁移 | ❌ 确认门 |
| git-status  | 外部 root 仓库未提交/未跟踪统计 | ✅ |
| help | 帮助 | ✅ |

工具

- external_session_status(只读):报告各 root 与会话数。
- external_session_move(确认门):按 id 迁移非 live 会话。

确认门:move 写操作经 userQuestions(web UI)确认;无回答者时抛 NO_PROVIDER 失败关闭,绝不静默执行。只读操作不询问。

审计:每次 materialize(路由决策)/ move / 异常输出一行结构化日志(仅标量):

[dsh-external-session] {"event":"move-done","sessionId":"...","from":"...","to":"..."}

Client 端功能

浏览器侧(lib/client.js,经 dsh.client 声明加载)提供两个界面:

1. 导入外部项目:按钮位于设置面板标题栏的动作区,使用插件内部复制的 DSH browse 交互(目录数据仍由公开的 workspaces.listDirectory() / createDirectory() 提供),不调用 native-only 的 pickDirectory();选择后注册工作区,并优先打开该项目已有的外部会话。没有历史会话时,会创建并打开新的外部会话。
2. 外部标记:按会话的实际持久化根目录判断(而非仅按 cwd),显示在 Header 左侧 Agent 控件之后;绿色「外部」徽标加宽加高以提升辨识度。

配套 Host HTTP 路由(registerWeb):

| 方法/路径 | 作用 |
| --- | --- |
| GET /external-session/config | 列出 internalRoot + 全部外部条目(含 dynamic 标记) |
| POST /external-session/import {path} | 导入项目文件夹为外部条目(root 默认 /.dsh-sessions) |
| GET /external-session/sessions?entryId= | 列出一个外部项目条目中的已有会话,供导入后恢复最近会话 |
| GET /external-session/external?sessionId= | 按会话实际存储根判断是否外部(解决同一 cwd 下内部/外部会话误判);保留 cwd 查询供兼容。

侧边栏会话列表(sidebar.workspaces)由内置 WorkspaceBrowser 独占渲染,当前没有公开的列表项级 Slot,因此插件不改写宿主列表 DOM。Header 徽标按 sessionId 查询实际日志文件所在根目录;同一 cwd 下的内部会话不会因 cwd 相同而误标为外部。
git 同步纪律(必读)

外部根默认落在项目仓库内(.dsh-sessions/),.gitignore 不忽略它,git clone/push 即随之同步。

⚠️ 会话文件 = 内置同格式 .zstd 二进制追加日志,git 无法自动 merge。 必须遵守:

1. 同一外部会话不同时在两台机器开。 二进制追加日志无法三方合并,同时写 = 冲突损坏。
2. 提交前正常结束会话(turn 完整、帧完整),pull 前先 commit。
3. 跨 clone:新 portable 布局不要求两端 checkout 到相同绝对路径。请确保各端都把 .dsh-sessions/ 纳入 git,且项目条目指向当前 clone 根;读取时 header 的旧绝对 cwd 会映射为当前根下的相对目录。旧版本绝对路径布局也会尝试按项目目录名和路径后缀兼容映射,但无法识别项目对应关系的旧目录需要手动迁移。
4. 本插件不做 commit/push(把 git 留给用户);git-status 只读提示未提交/落后。

可选互操作(不实现)

若另装 dsh-session-sync(PerryLink),可将其 sessionRoot/仓库指向外部根,获得冲突"两边保留"合并。本插件自身不做自研 merge。

硬性约束与可逆卸载

- 不改 DSH 运行时源码、不改内置 preset;默认 jsonl 行的 disabled 只发生在 profile patch。
- 布局/写协议与 stock jsonl 一致(互读优先),无第二套私有格式。
- 命令/工具/审计随 fiber 释放,停止/卸载零残留。
- 可逆卸载:从 cordis.patch.yml 移除 session-persistence-jsonl: disabled 与 dsh-external-session insert,并从 bundles/依赖移除本包,重启即回到 stock 后端;内部会话与外部已有文件均完好。
- 不序列化 live 对象;审计只含标量。
- 性能:list() 目录扫描带 TTL 缓存,新会话落盘/迁移时立即失效,避免每帧刷新。

自测记录(§9 验收清单)
node test/run-tests.mjs(Node ≥22 直跑,无真实宿主)结果 48 passed, 0 failed:

| §9 项 | 覆盖 | 结果 |
| --- | --- | --- |
| 1 外部创建 | 外部 cwd 会话落外部根、内部根无该项目目录 | ✅ |
| 2 内部不受影响 | 其它 workspace 会话仍落内部根 | ✅ |
| 3 重启恢复 | 全新 backend 实例 list 到外部+内部会话 | ✅ |
| 4 导入/跨端 | 外部根中的日志自动进列表("外部即导入") | ✅ |
| 5 外部分支 | fork 继承 cwd → 路由落同一外部根(实测 test/fork-inherit.mjs:真实 SessionStore.fork 后子会话 cwd=父 cwd、rootFor 同根) | ✅ |
| 6 迁移 | move 内部↔外部双向、事件完整、源清理 | ✅ |
| 7 格式互读 | 旧绝对布局由 stock jsonl 后端 list/loadStored 读取;portable 布局由本插件跨 clone 读取 | ✅ |
| 8 并发/容错 | 截断尾帧被容忍、已确认事件不丢、返回 tornMarker | ✅ |
| 9 可逆卸载 | 移除补丁即回 stock 后端、文件完好(构造性) | ✅(见卸载说明) |

测试同时覆盖:路径编码(--H-RSProjects-deepseek-harness-desktop--)、header 往返、scanZstdFrames 帧扫描、非自然 move 后追加不产生分叉文件、live 会话 move 被拒、未知 rootId 抛错。

已知取舍

- move 与 cwd 路由的语义:路由由 cwd 派生,move 是"物理搬迁 + 位置缓存"——搬迁后追加跟随会话实际位置(不产生分叉文件),读取端只校验 id + 项目目录名(不要求文件必须在 rootFor(cwd) 处)。move 主要用于配置变更后把存量会话搬到新 root 对齐。
- materialize 用 rename 发布(未用 stock 后端的 Win32 MoveFileExW write-through + koffi 原生路径):首次落盘在极端断电下可能遗留孤儿 tmp 文件(随机名,无害);追加路径仍是 open(a)+write+fsync 的 durable 语义,崩溃截断修复逻辑逐字保留。
- 纯公开 zstd API:未复制 stock 的 Node 私有句柄解码器(性能优化),用 zstdDecompressSync 逐帧解码,正确性等价。

上游仓库有新提交时邮件通知你(每天最多一封,无更新不打扰),随时一键退订。

💬 加入社群

插件用法、部署报错、新插件第一时间同步——群里问,比一个人翻文档快。

DPharness QQ 群二维码,QQ 扫码进群
QQ 扫码进群
DPharness 飞书群二维码,飞书扫码进群
飞书扫码进群