Back to home@bHorse

dsh-session-sync

DeepSeek Harness(DSH)会话跨设备同步插件

Stars
0
Language
JavaScript
Created
Aug 20, 2026
Updated
Aug 20, 2026
GitHub repo

Introduction

dsh-session-sync

中文 | English

DeepSeek Harness(DSH)会话跨设备同步插件:把本机产生的每一个会话镜像到中心 PostgreSQL 数据库,并按照可配置的间隔把其他设备上的会话拉回本机。换一台 电脑也能继续看到、恢复之前的所有对话。

核心痛点:DSH 默认把会话存成 ~/.dsh/sessions/... 下的本地文件,换一台 电脑就"找不到"了。本插件在不动本地存储的前提下,加一层双向同步,让会话 跟着你走。


功能特性

  • 双向同步:本机会话定时推送到中心数据库;远端会话定时拉回本机。
  • 增量传输:只同步游标(seq)之后的新事件,不重复传输整段会话。
  • 最后写入胜出(LWW):同一事件在多台设备同时修改时,以 updated_at 较新者为准,不会静默丢失数据。
  • 显式工作区绑定:远端工作区在本地必须手动绑定目录才会被拉取, 绝不把别人的路径自动绑到你机器上的任意目录。
  • 崩溃安全:本地状态文件原子写入(写临时文件 → fsync → 改名);同步游标 只在事务提交后前进;进程中途被杀不会产生半成品数据。
  • 完全惰性:enabled: false 时不连接数据库、不调度定时器、不订阅任何事件。

交付形态:常规 npm 包(不是动态插件)

本插件以常规 npm 包方式加载到 profile 的 cordis.yml(与 @deepseek-ai/dsh-session@deepseek-ai/dsh-llm 等内置插件同级),不是 动态的 cordis_define / cordis_run 插件。

为什么:动态 Cordis 插件的 Host 沙盒只暴露 ctxharnessconsolebtoa/atobTextEncoder/TextDecoder(已通过 Builtin.listBuiltins 验证)。它无法 import pg 驱动、无法读取 process.env、无法使用 node:fs —— 而这三者正是本插件的硬依赖(Postgres 连接、DSH_HOME 解析、 state.json 原子写入)。常规包插件拥有完整的 Node 能力,所以本插件必须 以此形态交付。


安装

1. 打包

# 在本仓库目录
pnpm install
pnpm pack            # 生成 dsh-session-sync-0.1.0.tgz

2. 安装到 DSH profile

# 以 web profile 为例(其他 profile 同理)
cd ~/.dsh/profiles
pnpm add /path/to/dsh-session-sync-0.1.0.tgz   # 装到 profiles/node_modules

3. 注册插件行

~/.dsh/profiles/<profile>/cordis.patch.yml 中加入:

- insert:
    - id: session-sync
      name: 'dsh-session-sync'
      config:
        enabled: true
        # Postgres 账号密码方式访问(独立字段,不用连接 URL)
        pgHost: 'db.example.com'
        pgPort: 5432
        pgDatabase: 'dsh'
        pgUser: 'dsh_app'
        pgPassword: 's3cret'
        pgSchema: 'public'       # 数据库 schema(默认 public,可指定如 'dsh')
        intervalMs: 30000      # 同步间隔,毫秒(默认 30000)
        batchSize: 200         # 每批同步的事件数(默认 200)
        syncScope: 'mine'      # 'mine' = 只拉本设备;'all' = 拉取所有设备

重启 DSH 后生效。

enabled: false(默认值)时插件完全惰性:不连接数据库、不调度定时器、 不订阅事件。


配置项说明

配置项类型默认值说明
enabledbooleanfalse是否启用;false 时插件完全惰性
pgHoststringPostgres 主机,启用时必填
pgPortnumber5432Postgres 端口(范围 1–65535)
pgDatabasestring数据库名,启用时必填
pgUserstring用户名,启用时必填
pgPasswordstring密码,启用时必填
pgSchemastring'public'数据库 schema(必须是裸 SQL 标识符,如 publicdsh)
intervalMsnumber30000同步周期(范围 1000–3600000)
batchSizenumber200每批事件数(范围 1–2000)
syncScope'mine' | 'all''mine''mine' 只同步本设备的工作区;'all' 拉取所有设备(计划 M2)
deviceIdstring自动生成本机设备标识(UUIDv4),首次运行时写入 state.json

工作方式

┌───────────────────────── 本地 DSH 进程 ─────────────────────────┐
│                                                                  │
│  会话运行时 ─emit─> session/event ─listen─> 本地 JSONL.zstd   │
│                                │                                │
│                                └──listen──> SyncPlugin          │
│                                              │                  │
│  定时器 (intervalMs) ◄─────── 本地同步状态文件 ────┘            │
│       │                                                          │
│       ├── push: 本地未推送事件 ──> 中心 Postgres                 │
│       └── pull: 中心 Postgres 新事件 ──> 本地 JSONL              │
└──────────────────────────────────────────────────────────────────┘
                                  │
                                  ▼
                        ┌─────────────────────┐
                        │  PostgreSQL 数据库      │
                        │  - workspaces         │
                        │  - session_headers    │
                        │  - session_events     │
                        └─────────────────────┘

同步流程(每个周期)

  1. push:扫描本机事件监听器记录的新事件,把 header 与新事件批量写入 Postgres(幂等,LWW 冲突处理),成功后推进本地游标。
  2. pull:读取云端 workspace 列表,只对本地已绑定的 workspace 拉取 新事件,追加到本地 JSONL(通过 sessionPersistence.append),成功后推进 游标。

工作区绑定(重要)

  • 本地新工作区:首次出现在本机时自动注册为 bound
  • 远端工作区:在本机没有目录绑定时绝不会被拉取;它会出现在待绑定 列表里,等你手动指定本地目录后才开始同步。
  • 绑定失效:本地绑定目录被删除后,工作区状态切换为 orphaned,提示你 重新绑定。
  • 解绑:手动解绑后,本地映射保留并标记为 unbound(界面可显示"已解绑"), 重新绑定时复用同一个工作区 UUID。

同步预设(preset)

每个会话与其 agent 预设绑定。恢复会话时如果本地缺少该预设,DSH 会抛 UnknownPresetError 使恢复硬失败。因此:

  • MVP:引用本地缺失预设的会话照常拉取(可只读查看),并记录 WARN 标注 "可读不可续"。
  • M2(规划):预设定义随会话一起入云(preset_definitions 表),本地缺 预设时自动拉取并写回 ${DSH_HOME}/.agent-presets/<id>/,恢复不再失败。

客户端 UI(MVP 为占位)

./client 子路径导出(dsh-session-sync/client)是客户端插件。当前为 MVP 占位(no-op 桩),渲染辅助函数已就绪;真实的面板 Slot 注册需要在一个真实 运行的 DSH 会话中选定 Slot 键后完成(详见计划文档 Task 12)。


开发与测试

pnpm install
pnpm test          # 全量测试(Postgres 集成测试需要 Docker)

提示:本机 ~/.docker/config.json 可能带有 Windows 风格的 credsStore(会破坏 Linux 上的 testcontainers)。若遇到 Docker 报错, 可用干净的配置覆盖:

DOCKER_CONFIG=/tmp/dsh-docker-config pnpm test

测试结构:

  • 单元测试:状态文件、设置校验、事件监听器、渲染辅助。
  • 集成测试:PostgresBackend、Push/Pull 引擎、工作区绑定状态机、 LWW 冲突(用 testcontainers 起真实 Postgres)。
  • 端到端测试:设备 A 推送 → 设备 B 绑定 → 设备 B 拉取,验证完整闭环。

路线图

阶段内容
MVP(已完成)PostgresBackend + 迁移 + Push/Pull 引擎 + 显式绑定 + 设置 + 状态文件;syncScope: 'mine'
M2syncScope: 'all' + 面板完整目录选择交互 + 状态恢复加固 + 预设同步
M3testcontainers 集成套件全覆盖(引擎 + LWW + 绑定状态机)
M4客户端同步状态条("Last sync: xxx")
未来服务端全文搜索,跨会话检索

相关文档

  • 设计文档:docs/superpowers/specs/2026-08-20-dsh-session-sync-design.md
  • 实施计划:docs/superpowers/plans/2026-08-20-dsh-session-sync.md