dsh-auto-handoff
Discovered★ 5DSH 插件:上下文到阈值时生成交接包并切到新会话。(已弃用 2026-10-17:换会话整条退役——续航交给 DSH 官方上下文压缩,跨重启续跑交给 dsh-memory-board)
⚠ 2026-09-28 合并记录:本插件现在也管"会话那一摊"
起因是一次「合插件」的需求 ——
dsh-session-switch(建新会话、发交接包、写 pending、供界面切过去) 整个搬进了本包,作为switch-session.js与switch-store.js两个模块:
- 内部握手改成直调(
runSwitchDirect()),不再是POST http://127.0.0.1:3080/api/session/switch那条网络往返;- 那四条 HTTP 路由照旧注册(
/api/session/switch|pending|ack|health),自检、手工 curl、老调用方都还认;- 状态目录不变:
~/.dsh/session-switch/(DSH_SESSION_SWITCH_DIR可覆盖);- 会话那一摊要的七个宿主服务改成运行时注入:缺谁只让这一摊缺席,档案那一摊照常(合并前是"整包卡 pending");
- 自检多了一个口:
DSH_SESSION_SWITCH_DIR=/tmp/ss node index.js --switch-selftest(原样 19 项)。撤下前的完整副本在
plugin-src/归档-合并前-20260927-232845/。
dsh-auto-handoff
DSH(DeepSeek Harness)插件:上下文占用到阈值时自动交接。
到阈值那一刻,它做这两件事:
- 拼一份限长的交接包(默认 1200 字,只有它进新会话),请会话那一摊
(2026-09-28 已并入本包的
switch-session.js)开新会话、把交接包当第一条消息送进去; - 记一笔幂等指纹到自己状态目录下的
handoff-fingerprints.json(跨重启有效,封顶 50 条)。
⚠️ 2026-10-16 起:默认 不写档案
以前它每交接一次就往日记里塞一条"上下文到阈值,插件自动做了一次交接"的流水账(外加状态一节、 生长记录一行),一天能塞好几条、删了又长;接手那边还会被提示"再补一条"。现在默认
archiveOnHandoff: false:
- 交接只交接 —— 一个档案字节都不碰;
- 幂等指纹不再寄生在日记条目上,改落
~/.dsh/session-switch/handoff-fingerprints.json;- 日记/状态/生长记录只在人点「日记」或跑「收尾」时才更新;
- 想回到旧行为(写日记 + 状态一节 + 生长记录一行)就把
archiveOnHandoff设成true。到阈值时的默认动作也变了:
autoTrigger: true时它不自己切会话,而是给 AI 塞一条【自动收尾提示】(先做收尾,做完写fire-now举手换会话)—— 免得在人正看着屏幕的时候把对话切走。
本插件不自己建会话。 建会话、切界面、归档原会话都是会话那一摊(已并入本包的 switch-session.js)的活;
两者只靠一个 HTTP 调用握手,叫不动就走降级(把完整待接写到约定位置,由浏览器那半边自己切)。
它管什么 / 不管什么
| 本插件 | dsh-session-switch | |
|---|---|---|
| 管什么 | 交接包 + 待接记录(archiveOnHandoff: true 时才写日记 / 状态 / 生长记录) | 会话:建、切、归档 |
| 触发 | 上下文到阈值自动;或手动打演练关键词 | 收到 /api/session/switch 才动 |
记忆的看 / 编 / 导入 / 导出是另一个插件(记忆面板)的事,本插件不碰。
安装
- 把这个目录放进你的 DSH 插件源目录(本机示例:
~/.dsh/plugin-src/dsh-auto-handoff)。 - 把
cordis.patch.example.yml里那一条按你的情况改好,插进 profile 的cordis.patch.yml;或者用 Plugin Manager 的install_bundle把它当 bundle 装进 profile。 - 重启宿主。实测:装 bundle、改 bundle 内代码、改 config 都认重启(HMR 不可靠)。
唯一的必填项是档案根:留空则依次尝试
config.root → 环境变量 DSH_MEMORY_ROOT → ~/.dsh/memory → 从当前目录逐级向上找
认根靠特征文件:身份.md + 日记.md + 生长\状态.md 三者齐备才算一份档案;
认不到就不动作(绝不拿一个猜出来的根去写真档案)。
配置
| 键 | 默认 | 说明 |
|---|---|---|
root | '' | 档案根;留空按上面的顺序找 |
triggerRatio | 0.65 | 占用到这个比例触发交接 |
minTurns | 3 | 会话太短不触发 |
cooldownMs | 180000 | 冷却 3 分钟;过了冷却还能再交一次(不是"一会话一次") |
handoffMaxChars | 1200 | 交接包上限 |
dryRun | true | 首启只算不写:把"本来会写什么"记日志,一个字节都不落盘 |
archiveOnHandoff | false | 交接要不要写档案:false = 只出交接包(默认、推荐);true = 还写日记一条 + 状态一节 + 生长记录一行 |
autoTrigger | false | true = 到阈值不自己切会话,而是给 AI 塞一条「该收尾了」的提示(见上文 2026-10-16 说明) |
device | 【PC】 | 日记标题与状态节里的设备标签(archiveOnHandoff: true 时才用得上) |
drillKeyword | #交接演练 | 手动演练关键词(只在用户消息里整句匹配) |
gatewayBase | http://127.0.0.1:3080 | dsh-session-switch 的地址 |
legacyDevice / legacyBoundary | '' | 旧条目设备归属判据;没配就完全不推断 |
占用率的取数口径:优先会话投影 contextPressure(跟页面上那个百分比同源),
拿不到再退回 ctx.tokenMeter.measure() ÷ request/context().contextWindow;
两个都拿不到就返回"不动作" —— 不许瞎猜分母。
手动演练
在对话里整句打 #交接演练:
- 只写演练副本根(
sandboxRoot),真档案一个字节不碰; - 没配
sandboxRoot就只做一次干跑,日志里能看到"本来会写什么"; sandboxRoot落在真档案根里面时硬拒(那样"演练"就是真写)。
Host 侧路由
前缀 /api/handoff/,五条各自注册一次(requestBody 只能是 buffered,写别的会静默失败)。
| 路由 | 方法 | 用途 |
|---|---|---|
pending | GET | 给客户端轮询的待接(完整交接包 + sourceSession + newSessionId) |
take | GET/POST | 与 pending 同义(取一份待接) |
ack | POST | 客户端切完回报,标记"已处理" |
health | GET | 一眼看配置与管道:阈值 / 冷却 / 上限 / 档案根 / gateway |
status | GET | 最近 20 行诊断日志 + 上次判定 |
draft-report | POST | 客户端上报输入框草稿状态("别打断正在打字的人"用) |
fire | POST | AI 举手请求换会话(只认「收尾」口令 + 每小时 ≤2/每天 ≤8) |
fire-ack | POST | 客户端替用户打完「换会话」后回来清标志 |
客户端半边(client.js)只做"轮询 + 调导航/归档":不注册 slot、不写 DOM,
出错只 console,绝不抛 —— 客户端入口抛错的代价是"整个页面打不开"。
「收尾后自动换会话」怎么举手(文件信令)
POST /api/handoff/fire 那条路要过宿主鉴权(实测在本机回 401,本机 curl 也一样),
所以 AI 举手改用写一个文件:
: > ~/.dsh/session-switch/fire-now # 路径 = `<状态目录>/fire-now`,内容随便
- 插件每 1 秒看一次这个文件 → 删掉 → 设
fireRequested; - 客户端本来就在轮询
/pending,看到fire就替用户打一条「换会话」,于是走既有那条触发路径 (建新会话、送交接包、切界面),打完回fire-ack清标志; - 闸没松:仍然受 每小时 ≤2/每天 ≤8 限制;
fire那条 HTTP 路由还额外要求"最近一条用户消息就是「收尾」"。 - 典型用法:AI 跑完收尾(日记 → 备份 → 体检)写一下这个文件,人就自动被带到新会话。
写档案的护栏(只在 archiveOnHandoff: true 时用得上)
默认不写档案(见上文),所以这一节只在把 archiveOnHandoff 打开时生效。写盘一律四条:
- 改前备份(整份复制到
<根>\归档-旧版本与记录\写入备份\,.bak-YYYYMMDD-HHMMSS); - 只改定位处(序号 +1、
total_count+1、last_updated、unfinished,别的一字不动); - 写完复核(读回来逐字比对 + "旧内容只能是新内容的前缀/只差一处"的形状复核);
- 外部改动拒写(指纹比对:我们读过之后被改过 → 拒写)。
外加:
- 幂等:同一份交接重复跑不追加第二条 ——
archiveOnHandoff: false时指纹写在<状态目录>/handoff-fingerprints.json,true时写在日记条目里的注释标记; - 写临时文件再 rename,不赌覆盖式写入;
- 行尾/编码:全文件 CRLF、UTF-8 无 BOM。
自检
node index.js --selftest # 在临时目录造假档案,跑全套回归(每项打实际输出)
node test-handoff-sandbox.mjs # 同一套,只打结论
node test-handoff-sandbox.mjs --verbose
自检口在模块层(不是 apply 里)—— 独立跑 node index.js 时 cordis 不会调
apply,写在里面会一声不吭。
已知边界
- 它不保证"交接的内容是这一轮的全文":交接包是压缩后的接手起点,不是复制品 —— 新会话一开就吃满上下文等于把坑搬过去。
- 日记正文里的
event_description由"最近说过的话 + 动过的工具"拼成; 没给userMood时那一栏会照实留白,不替你编心情。 - 跨设备没做:手机侧没有同一套 Web 客户端,切会话要另想办法。
legacyDevice/legacyBoundary只是判据:本插件不会去改写任何旧条目。
License
MIT
Comments
Loading…
Similar plugins
by MountBatten718
Hand your long DSH session over to a fresh one: /handoff summarizes the recent conversation with an LLM, creates a clean new session in the same workspace, injects the summary as its first message, and auto-opens it. The origin session is unchanged.
★ 3
MIT
JavaScript
Oct 20, 2026
dsh plugin --profile web add dsh-session-handoffby IKEASven69
DSH 会话接力插件:拉取六家 agent 会话(/resume-*)、寄存当前会话(/handoff)、开局取件(/inbox),落在 handoff: 1 开放协议上
★ 0
MIT
TypeScript
Oct 26, 2026
dsh plugin --profile web add dsh-takeoverby tianyaojiudi-prog
DSH(DeepSeek Harness) 插件:把一段可在设置页随时改写的文字,作为全局系统提示词段注入到所有会话,含子代理与工作流内部子代理;文本与开关即时生效,无需重启。
★ 9
MIT
JavaScript
Oct 4, 2026
dsh plugin --profile web add dsh-djy-xttscby liuyun847
DSH 宿主插件:给模型一个 restart_dsh 工具,重启 dsh 后自动恢复原会话续跑 (DeepSeek Harness)
★ 0
MIT
JavaScript
Oct 19, 2026
dsh plugin --profile web add dsh-host-restartby qigai74
DSH 插件:按「会话 × 供应商 × 模型」拆解调用次数与 token 用量,数据只读自 dsh-cost-meter 账本。
★ 0
MIT
JavaScript
Oct 18, 2026
dsh plugin --profile web add dsh-usage-breakdownby instpe
DSH 插件:会话智能体自重启宿主进程,并在重启后向原会话投递一条消息
★ 0
JavaScript
Oct 18, 2026
dsh plugin --profile web add @instpe/dsh-self-restart