🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

会话交接 iuuuuuuuu/dsh-session-handoff

DeepSeek Harness
iuuuuuuuu27 天前有提交已提供中文文档扫描:未发现敏感能力

把快撑爆的对话记忆迁移到新会话继续用

29.8/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/10 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add iuuuuuuuu/dsh-session-handoff
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:待复验
是什么
dsh 原生插件 · chat
装得上吗
曾在 dsh 0.2.0-rc.2 下真实安装成功,但当前 dsh 已更新至 0.2.1-rc.5,本站尚未在新版下复验
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 27 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🟢 扫描:未发现敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描未发现敏感能力:最多只发网络请求、读取自身元数据或只读文件,未发现执行命令、文件写入或敏感信息读取。

扫描于 2026/11/3(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 迁移对话记忆至新会话
  • 救回不可用会话

DSH 插件:保留记忆的会话交接。在对话变得不可用之前把它带进新会话,也能救回已经不可用的会话。

README

由 DeepSeek 最新模型翻译生成
dsh-session-handoff English · 简体中文 在会话变得不可用之前,把它的记忆交接到一个新会话。 它解决的死锁 一个跑久了的 DSH 会话,最终会大到连它自己的压缩都失效。这个机制值得说清楚,因为正是它让失败变得永久: - 压缩会把整个被遮蔽区域当作一次总结请求的输入重放一遍。 - 上游服务商对单次请求的请求长度有硬上限。 - 会话一旦越过这条线,压缩请求自己就超限了。 - 于是压缩以「它本该治好的那个溢出」失败,每次都一样,永远如此。 这个会话从此既不能压缩、也不能继续;而那些朴素实现的交接插件用的是同样的构造方式,所以也交接不了。这就是那个死锁。/handoff 就是为了打破它。 安装 dsh plugin --profile <name> add /path/to/dsh-session-handoff 如果你的 profile 的 cordis.patch.yml 已经手工挂载了本插件,先删掉那一行,否则会跑两个实例。 必须重启 DSH。 这是硬要求:harness 把已加载的插件模块放在 loader cache 里,而 hmr.root 默认是 [],所以改插件源码不会在运行中的宿主里生效。
展开完整文档(README 剩余 1 节)
dsh-session-handoff

English · 简体中文

在会话变得不可用之前,把它的记忆交接到一个新会话。

它解决的死锁

一个跑久了的 DSH 会话,最终会大到连它自己的压缩都失效。这个机制值得说清楚,因为正是它让失败变得永久:

- 压缩会把整个被遮蔽区域当作一次总结请求的输入重放一遍。
- 上游服务商对单次请求的请求长度有硬上限。
- 会话一旦越过这条线,压缩请求自己就超限了。
- 于是压缩以「它本该治好的那个溢出」失败,每次都一样,永远如此。

这个会话从此既不能压缩、也不能继续;而那些朴素实现的交接插件用的是同样的构造方式,所以也交接不了。这就是那个死锁。/handoff 就是为了打破它。

安装

dsh plugin --profile <name> add /path/to/dsh-session-handoff

如果你的 profile 的 cordis.patch.yml 已经手工挂载了本插件,先删掉那一行,否则会跑两个实例。

必须重启 DSH。 这是硬要求:harness 把已加载的插件模块放在 loader cache 里,而 hmr.root 默认是 [],所以改插件源码不会在运行中的宿主里生效。

重启后日志会显示:

[session-handoff] installed: /handoff registered, pressure monitor active (upstream limit 1048576)

使用

/handoff 把这个对话交接给一个新会话 /handoff-status 显示测量值、绑定的上限、以及触发器状态

/handoff 会打印一份报告:

Handed off session-9ef22e43-… to session-e7d70ba3-….

- read: 32286 messages, 117541 events
- memory: 240 facts, 0 checkpoints
- summarize: 70 chunk(s), source=map-reduce, 18422 chars
- seed: 21544 chars (~5386 tokens)
- create: session-e7d70ba3-…
- archive: session-9ef22e43-…

The source session is archived; it stays readable in the sidebar.

源会话最后才归档,所以中途任何一步失败都留下一个完好可用的源会话。

它做什么

盯着

每一轮对话结束时,插件测量会话的 token 压力,并对照两条独立上限分类,绑定更紧的那条:

| 上限 | 从哪来 | 为什么重要 |
| 模型的上下文窗口 | 从会话正在使用的模型配置里读 | 模型自己接受多少 |
| 上游请求长度上限 | 配置项,因为没有地方声明它 | 服务商对单次请求接受多少 |

真正杀死会话的是第二条,而它不能从第一条推导 —— 一个路由可能宣称 1,000,000 的窗口,而它背后的账号对单次请求只给更少。

压力越过策略阶梯时,插件会往会话里注入一条提醒,写明实测数字、绑定的是哪条上限、以及出路。提醒是给下一步的模型可见上下文,所以它永远不会自己开一轮,并且在增长和时间两个维度都限流,停在 critical 的会话不会每一步都烦你。

交接

/handoff 跑一个事务:

1. read 读活会话(消息 + 事件)
2. memory 抽四层记忆
3. summarize 分块总结,每块一次请求,逐块限界
4. create 建后继,继承源的 route 和 cwd
5. deliver 把交接内容作为后继的开场消息投进去
6. archive 归档源,带 stopActivity —— 终止它的工作
7. release 丢掉源的 live 事件树

后继立刻开工。 seed 不是留给你读的便条;它被投递为后继的第一轮输入,所以新会话直接接着干。

归档不释放内存,而「释放」只释放了一半

归档是可见性变更:会话 id 加入归档集,把它从侧栏默认视图里藏起来。它的日志留在磁盘上、仍可阅读、取消归档能恢复原位。什么都没删,也什么都没释放。

内存只有一条规则:会话归档之后才会被释放。 交接时保留源会话可见(自动模式的默认)就不会释放它 —— 因为你可能还在看着它。你自己在侧栏归档的,由定期扫描归档集接手,因为手动归档不发任何插件能监听的事件。

释放调用 SessionStore.remove(id),跑 store 官方的 detach 生命周期。

它释放了什么、没释放什么。 一个会话被两个独立注册表持有:

| 注册表 | 持有什么 | 插件能不能摘 |
| ctx.sessions | 会话的事件树 | 能 —— sessions.remove(id) |
| ctx.agents | agent,而 agent 持有会话 | 不能 |

agent 注册表是一个普通 Map,没有任何公开的移除方法:agents.enter() 返回 detach disposer,但 enter() 是 agent loop 调的,不是插件,而且公开面上既没有 agents.remove 也没有 agent.dispose。所以释放会把会话从 session store 里摘掉,而它的 agent 仍然让那棵树可达。磁盘上的日志两种情况都不动。

用真实 SessionStore 和真实 AgentRegistry 实测:

释放前: sessions.get(id) -> true agents.get(id) -> true 释放后: sessions.get(id) -> false agents.get(id) -> true agent 仍然持有全部事件

agent 还在 running 的会话会被跳过,因为那时摘会跟它的收尾事件打架。把扫描关掉用 monitor.releaseArchived: false。

它带走的记忆

设计问题是带什么。四层,按成本排序:

| 层 | 来源 | 保真度 | 成本 |
| 逐字事实 | 对转录做正则抽取 | 精确 | 免费 |
| 已有检查点 | 会话自己写过的成功 compaction/end 摘要 | 精确 | 免费 |
| 最近若干轮原文 | 对话尾部,一字不改 | 精确 | 免费 |
| 叙述摘要 | 模型写的,分块归并总结 | 复述 | 一遍 |

三层精确的永不为叙述层让位:只有叙述层会被截断,且只从中间截。逐字尾部拿的是交接内容预算里保留的份额,而不是去和散文抢剩下的 —— 因为事实给你路径,而只有尾部给你「刚才那句话」。

凭据绝不跟着走

逐字层是拷贝,所以会话曾经回显过的东西 —— 调试时打印的 token、dotenv dump 里的 key —— 都会被拷进来。交接 seed 是一个新产物,可能被阅读或分享,所以按形状脱敏后才写入:bearer token、sk- / ghp_ / github_pat_ / xox / AKIA 密钥、JWT、PEM 私钥、password= 类赋值、连接串内联密码。事实层同样处理,因为路径里可能嵌密钥。

自动交接

自动交接默认开;自动归档默认关。

这个区分就是重点:交接创建一个继续干活的后继,而归档藏起那个坏掉的会话。会话在你不在的时候死掉时,那份记录是证据 —— 你想看它当时在干什么,而不是发现它没了。

三个独立触发器,因为会话可以以三种不同方式变得不可用:

| 触发器 | 信号 | 默认 |
| 压力 | 实测压力到 atLevel | critical |
| 连续失败 | 连续几轮对话都以失败收场 | 2 |
| 压缩失败 | 失败连续计数并且体量足够 | 3 次失败,窗口的 35% |

只有 error 和 max-tokens 两种结束算失败。completed 重置计数;aborted(你自己点的停止)和 interrupted(崩溃后自动补记的收尾)都不算,因为两者都不代表会话坏了。

压缩触发器刻意做成联合条件。 压缩失败从不单独判定:策略拒绝可以在一个正常工作的会话上重复几百次,所以光看次数会误杀健康会话。它要求失败连续计数和体量同时满足。终局码(请求在任何体量下都不再装得下)绕过体量下限,而平台级号池故障刻意不作为触发器 —— 交接修不了一个新会话照样继承的故障。

一个会话最多交接一次。成功的交接在本进程内抑制该会话;失败的交接三次之后停手,并让你手动跑 /handoff。

配置

在 profile patch 里挂配置,或者在 设置 → 会话交接 里实时改:

- id: session-handoff
name: 'dsh-session-handoff'
config:
monitor:
enabled: true
policy:
language: zh # 对话里那条提醒用什么语言 watchRatio: 0.45 warnRatio: 0.6 criticalRatio: 0.75 remindEveryTokens: 50000 remindCooldownMs: 600000 seedBudgetTokens: 24000 autoHandoff: enabled: true atLevel: critical onAnomaly: true anomalyThreshold: 2 archive: false

配置里的键名(upstreamPromptLimit 等)是程序读的标识符,保持英文。

两条上限都不需要配置。 模型窗口从会话正在使用的模型配置里读,所以换模型就换上限,不用动任何设置。上游请求长度上限是路由背后账号的属性、不出现在任何模型配置里,所以插件带一个本机实测出来的默认值;换账号的部署可以改 policy.upstreamPromptLimit。

保证

由测试套件断言(node --test tests/smoke.test.mjs):

- 组装出来的总结请求永远不会超预算 —— 这是让插件在已经超限的会话上仍然安全的不变量。
- 脱敏既不瞎也不过度:干净的尾部逐字节原样通过(包括看起来像密钥但正是该保留的证据的 SHA-256 摘要),而同一条尾部注入凭据后,每一个都被移除。
- 散文层装不下时,事实和检查点依然保留。
- 隐藏推理永不泄进 seed。
- 估算值可以提醒你,但永远不能触发自动交接。
- 设置页每一个标签都有双语,样式表里每一个颜色都来自设计 token。

它做不到什么(如实说明)

- 它不缩小活会话。 交接把工作移到新会话;旧的留在磁盘上(已归档、仍可阅读),直到你删掉它。
- 摘要是复述。 精确回忆来自事实层、检查点层,以及那个仍可打开的归档会话。
- 上游请求长度上限是实测默认值,不是探测出来的。 它是唯一一条无处可读的上限,所以这个值是本机路由上实测出来的。
- 分块总结每块花一次请求。 超大会话上是几十次;报告会告诉你几次。
- 归档不是删除。 它把会话从侧栏默认视图里移走。
- 自动触发器还没在自然发生的故障上触发过。 逻辑已用真实数据和隔离宿主验证,但那一刻仍未观测到。
- 提醒的语言是配置项,不是浏览器语言。 宿主半读不到浏览器的语言设置,所以由 policy.language 决定,默认中文。

目录

lib/
index.js 插件入口:命令、压力监控、触发器 client.js 设置界面(浏览器半) handoff.js 事务 memory.js 分层抽取 + 脱敏 + seed 组装 summarize.js 分块归并总结(每块独立限界) pressure.js 分类、提醒策略、压缩失败联合条件 token-budget.js token 估算与请求预算 tests/ smoke.test.mjs 每个模块的单元测试 auto-trigger-real.mjs 用真实失败序列重放自动触发器 client-render.mjs 忠实渲染设置界面 e2e-real-session.mjs 在真实转录上跑管线的只读探针 make-fixture.mjs 从真实会话日志重建本地 fixture docs/ NOTES.md 它是怎么被验证的、以及路上踩过什么坑

文档

| 文件 | 语言 |
| README.md | English |
| README.zh-CN.md | 简体中文 |
| docs/NOTES.md | English — 工程笔记与验证证据 |
| docs/NOTES.zh-CN.md | 简体中文 — 同上 |
| DELIVERY.md | 简体中文 |
| DELIVERY.en.md | English |

许可

MIT
预览分享卡片长什么样 ↓iuuuuuuuu/dsh-session-handoff
分享 · DeepSeek Harness Hub
iuuuuuuuu/dsh-session-handoff

把快撑爆的对话记忆迁移到新会话继续用

这是你的仓库?认领条目

回到顶部