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

会话交接与上下文管理 snow-The/dsh-session-handoff

DeepSeek Harness
snow-The2 天前有提交已提供中文文档扫描:含敏感能力

DeepSeek Harness 的会话交接与上下文管理:结构化交接文档(export/resume/status)+ 主动上下文裁剪(acp_* 走官方压缩 API)+ 可选的 OpenViking/archify 增强器

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

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

自动兼容检查有告警:未发布到 npm registry,仅可从源码安装。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add snow-The/dsh-session-handoff
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

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

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

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查⚠ 装前注意

未发布到 npm registry,仅可从源码安装

展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✗npm 包@snow-the/dsh-session-handoff(未发布到 npm,仅可源码安装)
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖要求 >=0.2.1-rc.5 · 最新 0.2.1-rc.5 兼容
✓入口文件main/exports/bin 已声明

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/11/2 20:49:35

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 66 处,展示前 60 条)
  • 读取本地文件
    lib/index.js:191
    if (existsSync(archifyPkg)) found.push('archify');
  • 写入本地文件
    lib/index.js:270
    await mkdir(dir, { recursive: true });
  • 写入本地文件
    lib/index.js:272
    await writeFile(file, doc, 'utf8');
  • 读取本地文件
    lib/index.js:299
    if (!existsSync(dir)) return 'No handoff documents found in .dsh-handoff/. Run handoff_export in the previous session first.';
  • 读取本地文件
    lib/index.js:300
    const files = (await readdir(dir)).filter((f) => f.endsWith('.md')).sort().reverse();
  • 读取本地文件
    lib/index.js:304
    if (!existsSync(file)) return `Handoff not found: ${target}. Available: ${files.join(', ')}`;
  • 读取本地文件
    lib/index.js:305
    const doc = await readFile(file, 'utf8');
  • 读取本地文件
    lib/index.js:1537
    const text = readFileSync(join(home, 'settings.yaml'), 'utf8');
  • 写入本地文件
    lib/index.js:1611
    await mkdir(dir, { recursive: true });
  • 写入本地文件
    lib/index.js:1613
    await writeFile(file, doc, 'utf8');
  • 写入本地文件
    lib/index.js:1936
    await mkdir(dir, { recursive: true });
  • 写入本地文件
    lib/index.js:1938
    await writeFile(file, doc, 'utf8');
  • 发起网络请求
    client/index.js:236
    var response = await fetch(ROUTE_PREFIX + path);
  • 发起网络请求
    client/index.js:240
    var response = await fetch(ROUTE_PREFIX + path, {
  • 读取本地文件
    lib/session-mgmt.js:38
    const raw = await readFile(entriesFile(), 'utf8');
  • 写入本地文件
    lib/session-mgmt.js:46
    await mkdir(trashRoot(), { recursive: true });
  • 写入本地文件
    lib/session-mgmt.js:47
    await writeFile(entriesFile(), JSON.stringify(entries, null, 2), 'utf8');
  • 删除本地文件递归
    lib/session-mgmt.js:56
    await rm(trashSessionDir(o.sessionId), { recursive: true, force: true }).catch(() => {});
  • 读取本地文件
    lib/session-mgmt.js:159
    if (originalPath !== undefined && existsSync(originalPath)) {
  • 写入本地文件
    lib/session-mgmt.js:160
    await mkdir(trashRoot(), { recursive: true });
  • 删除本地文件递归
    lib/session-mgmt.js:162
    await rm(trashPath, { recursive: true, force: true });
  • 写入本地文件
    lib/session-mgmt.js:163
    await rename(originalPath, trashPath);
  • 读取本地文件
    lib/session-mgmt.js:186
    if (entry.cwd && existsSync(trashPath)) {
  • 写入本地文件
    lib/session-mgmt.js:188
    await mkdir(dirname(target), { recursive: true });
  • 写入本地文件
    lib/session-mgmt.js:189
    await rename(trashPath, target);
  • 读取本地文件
    lib/session-mgmt.js:191
    } else if (existsSync(trashPath)) {
  • 删除本地文件递归
    lib/session-mgmt.js:211
    await rm(trashSessionDir(id), { recursive: true, force: true });
  • 读取本地文件
    lib/model-routes.js:90
    return readFileSync(path, 'utf8');
  • 读取本地文件
    lib/model-routes.js:172
    text = await readFile(path, 'utf8');
  • 写入本地文件
    lib/model-routes.js:192
    await writeFile(path, text, 'utf8');
  • 读取本地文件
    lib/acp-config.js:152
    try { text = await readFile(settingsPath(), 'utf8'); } catch { }
  • 读取本地文件
    lib/acp-config.js:160
    try { text = await readFile(path, 'utf8'); } catch { }
  • 写入本地文件
    lib/acp-config.js:211
    await writeFile(path, text, 'utf8');
  • 读取本地文件
    lib/zstd-reader.js:129
    const st = statSync(path);
  • 执行外部命令来自配置/变量
    lib/zstd-reader.js:176
    try { text = execSync(bin + ' -d -c ' + JSON.stringify(path), { encoding: 'utf8', maxBuffer: 1024 * 1024 * 1024, stdio: ['ignore', 'pipe', 'ignore'] }
  • 读取本地文件
    lib/zstd-reader.js:193
    const size = statSync(path).size;
  • 读取本地文件
    lib/zstd-reader.js:210
    collectEvents(readFileSync(path, 'utf8'), keep, (ev) => events.push(ev));
  • 写入本地文件
    lib/zstd-reader.js:252
    writeFileSync(tmp, buf);
  • 执行外部命令来自配置/变量
    lib/zstd-reader.js:255
    try { return execSync(bin + ' -d -c ' + JSON.stringify(tmp), { encoding: 'utf8', maxBuffer: 1024 * 1024 * 1024, stdio: ['ignore', 'pipe', 'ignore'] })
  • 删除本地文件
    lib/zstd-reader.js:257
    } catch { } finally { try { unlinkSync(tmp); } catch { } }
  • 读取本地文件
    lib/graph.js:72
    for (const entry of readdirSync(dir)) {
  • 读取本地文件
    lib/graph.js:85
    const hit = candidates.find((p) => existsSync(p));
  • 读取本地文件
    lib/graph.js:92
    for (const e of readdirSync(dir, { withFileTypes: true })) {
  • 执行外部命令来自配置/变量
    lib/graph.js:123
    text = execSync(bin + ' -d -c ' + JSON.stringify(path), { encoding: 'utf8', maxBuffer: 512 * 1024 * 1024, stdio: ['ignore', 'pipe', 'ignore'] });
  • 读取本地文件
    lib/graph.js:132
    text = readFileSync(path, 'utf8');
  • 写入本地文件
    lib/graph.js:174
    mkdirSync(graphDir(), { recursive: true });
  • 读取本地文件
    lib/graph.js:199
    for (const entry of readdirSync(dir)) {
  • 读取本地文件
    lib/graph.js:227
    if (!existsSync(root)) return;
  • 写入本地文件
    lib/graph.js:291
    function writeWatermarks(wm) { writeFileSync(watermarkPath(), JSON.stringify(wm, null, 2), 'utf8'); }
  • 读取本地文件
    lib/failover.js:101
    text = await readFile(settingsPath(), 'utf8');
  • 读取本地文件
    lib/failover.js:129
    text = await readFile(path, 'utf8');
  • 写入本地文件
    lib/failover.js:145
    await writeFile(path, text, 'utf8');
  • 读取本地文件
    lib/cache-stats.js:68
    if (!existsSync(file)) return null;
  • 读取本地文件
    lib/cache-stats.js:70
    const raw = JSON.parse(readFileSync(file, 'utf8'));
  • 写入本地文件
    lib/metrics.js:62
    mkdirSync(dirname(file), { recursive: true });
  • 写入本地文件
    lib/metrics.js:65
    if (statSync(file).size > maxBytes) renameSync(file, file + '.1');
  • 读取本地文件
    lib/metrics.js:65
    if (statSync(file).size > maxBytes) renameSync(file, file + '.1');
  • 写入本地文件
    lib/metrics.js:67
    appendFileSync(file, JSON.stringify({ v: 1, ...row }) + '\n');
  • 读取本地文件
    lib/metrics.js:82
    if (!existsSync(file)) return out;
  • 读取本地文件
    lib/metrics.js:85
    try { text = readFileSync(file, 'utf8'); } catch (err) { warnOnce('metrics journal unreadable', err); return out; }
扫描于 2026/11/4(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

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

依赖的 DSH / Cordis 模块
@deepseek-ai/dsh@deepseek-ai/dsh-home-paths@deepseek-ai/dsh-session-persistence-jsonl@deepseek-ai/dsh-tools
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 结构化交接文档
  • 主动上下文裁剪
  • 可选增强器

README

由 DeepSeek 最新模型翻译生成
@snow-the/dsh-session-handoff DeepSeek Harness 的会话交接与上下文管理 —— 做这个插件,是因为现有的会话管理和上下文裁剪插件都写于最新 DSH 更新之前,覆盖不了完整工作流。 为什么需要它 长会话(比如 170k 行的 r32 会话)会撞上上下文上限,反复触发自动压缩,然后卡住。换到新会话又会丢掉全部进度。这个插件把这两半都补上了 —— 还不止: 1. 交接 —— 导出结构化的可移植交接文档,让新会话能无缝接着干。 2. 主动上下文裁剪 —— 在上下文窗口填满之前压缩已用历史(走官方压缩 API,摘要由模型撰写)。 3. 会话管理 —— 回收站 / 恢复 / 彻底删除 / 列出会话。 4. 模型路由 —— 每条已注册的路由(官方 API、Ark、自定义、vision-toolkit 包装器)都能排序、能切换;每条路由可以固定自己的模型和推理强度(provider:model:effort),也可以跟随聊天当前的选择。没有任何模型 id 是写死的。 5. ACP 阈值 —— 调整压缩上限,不用碰 YAML。
展开完整文档(README 剩余 1 节)
@snow-the/dsh-session-handoff

DeepSeek Harness 的会话交接与上下文管理 —— 做这个插件,是因为现有的会话管理和上下文裁剪插件都写于最新 DSH 更新之前,覆盖不了完整工作流。

为什么需要它

长会话(比如 170k 行的 r32 会话)会撞上上下文上限,反复触发自动压缩,然后卡住。换到新会话又会丢掉全部进度。这个插件把这两半都补上了 —— 还不止:

1. 交接 —— 导出结构化的可移植交接文档,让新会话能无缝接着干。
2. 主动上下文裁剪 —— 在上下文窗口填满之前压缩已用历史(走官方压缩 API,摘要由模型撰写)。
3. 会话管理 —— 回收站 / 恢复 / 彻底删除 / 列出会话。
4. 模型路由 —— 每条已注册的路由(官方 API、Ark、自定义、vision-toolkit 包装器)都能排序、能切换;每条路由可以固定自己的模型和推理强度(provider:model:effort),也可以跟随聊天当前的选择。没有任何模型 id 是写死的。
5. ACP 阈值 —— 调整压缩上限,不用碰 YAML。

工具

模块 A —— 交接(零依赖)

| 工具 | 用途 |
| handoff_status | 会话概览,紧凑呈现:轮次、消息数、工具使用情况、检查点、上下文压力 |
| handoff_export | 把会话解析成结构化 Markdown 交接文档,放在 <workspace>/.dsh-handoff/ 下,外加一个开箱即用的交接包(OpenViking 归档命令、archify 图表命令 —— 只在装了这些增强器时才有) |
| handoff_resume | 在新会话里加载最新的交接文档并继续 |

还有 /handoff 命令。

模块 B —— 主动上下文裁剪(官方压缩 API)

| 工具 | 用途 |
| acp_status | 用量、surface seq 映射、上限(软/硬压力等级) |
| acp_compress | 用你的摘要替换一段闭区间的 surface seq(ctx.compaction.compactRegion) |
| acp_decompress | 读取被检查点藏起来的原文(只读) |
| acp_search | 搜索可见的和被隐藏的已压缩历史 |
| acp_config | 显示当前生效的阈值(软/硬上限、preserveRecent、minTokens、nudge) |
| acp_set_limit | 把新阈值持久化进 settings.yaml(新会话生效) |

另外还有一个系统提示词压力横幅,超过软/硬上限(默认 60% / 70%)就提醒模型去压缩;以及一个 compaction.summarize 拦截,让摘要由模型撰写。

模块 C —— 会话管理

| 工具 | 用途 |
| session_list | 列出会话(可选包含回收站) |
| session_trash | 归档会话产物并移入插件回收站(可恢复;正在运行的会话会拒绝) |
| session_restore | 移回原位并取消归档 |
| session_purge | 永久删除产物和回收站条目 |

回收站条目以 JSON 形式保存在 $DSH_HOME/dsh-session-handoff-trash/ 下(保留最新 10 条,超出的最旧条目自动清除)。

模块 D — 模型路由(同一个模型,多个服务商)

| 工具 | 用途 |
| model_routes | 列出所有服务 deepseek-v4.1-flash 的路由:服务商、baseURL、密钥环境变量 + 密钥家族(ark-/sk-)、默认标记(含 vision-toolkit- 变体)、vision wrapper 变体 |
| model_switch | 把 agent-default-model 指向某条路由(持久化到 settings.yaml;新会话会使用它)。可选 vision:true 选中 vision wrapper 变体;密钥家族缺失时给出警告 |

专为这类用户打造:同一个模型 id 同时用在官方 DeepSeek 和火山方舟套餐上。model_routes 显示当前配置了什么,model_switch deepseek 走方舟通道,model_switch deepseek-official --vision 走官方通道并启用 vision wrapper。

模块 E — Web 客户端(GUI)

插件自带手写的客户端 bundle(client/index.js,无需构建步骤),在 Web GUI 里注册一个 设置分区「模型路由 / Model Routes」,把宿主工具映射成可点击的控件:

- 模型路由面板 — 实时模型目录里的每条路由(聊天对话框提供的同一份列表,含所有 vision-toolkit wrapper),并显示当前聊天上下文的选中项(session.requestContext())。这里不配置 vision 模型 —— 聊天对话框自己的模型列表才是唯一来源;顺序由你决定。
- 自动故障转移(优先级列表) — 拖拽调整路由顺序,删除或新增条目,保存优先级。当前模型不可达或额度耗尽时(QUOTA / RATE_LIMIT / SERVER / TIMEOUT / TRANSPORT / EMPTY_RESPONSE / AUTH / 凭据 / 适配器错误),自动尝试下一条路由,工作继续 —— 用户主动中断永不切换。底层由 GET|POST /dsh-session-handoff/failover 以及 agent/request-error + agent/request 瀑布流支撑。
- 会话交接 — 一键把当前会话导出到 <workspace>/.dsh-handoff/handoff-<session>.md(POST /dsh-session-handoff/export)。
- 压缩阈值 — 软/硬上限滑块(17-90%),持久化到 settings.yaml 的 session-handoff: 段(GET/POST /dsh-session-handoff/acp)。「固定推荐 65/90 / Fixed 65/90」 按钮直接为 1M 窗口模型填入合理默认值(GUI 里不做逐会话计算;acp_recommend 工具仍为智能体提供成本模型估算)。

宿主路由与工具共用同一套逻辑(enumerateRoutes、switchProvider、readAcpSection/writeAcpConfig、exportHandoffForAgent),所以 GUI 和智能体工具永远不会走偏。

软增强(自动检测,绝不强制)

- OpenViking:检测到 viking_ 工具时,handoff_export 会在交接包里嵌入一条现成的 viking_remember 命令。
- archify:安装了 @tt-a1i/archify-dsh 时,交接包会附带一条 archify render 命令,用来生成进度图。

两者都不装,核心功能照常工作。

安装

dsh plugin --profile web add github:snow-The/dsh-session-handoff
重启 dsh web

用法

在旧会话里:

handoff_export → 写入 .dsh-handoff/handoff-<session>.md(+ handoff 包)

在新会话里:

handoff_resume → 加载 handoff;继续干活

长会话里干重活之前:

acp_status → 查看压力
acp_compress {start} {end} {summary} → 裁掉已用过的区间
acp_set_limit → 提前调好软/硬上限

给共享模型换服务商:

model_routes → 查看路由 model_switch deepseek → 走 Ark 通道 model_switch deepseek-official → 走官方通道 model_switch deepseek --vision → ……带上 vision 包装

开发

unit tests (run from a profile dir so @deepseek-ai/ resolves)
node --test test/

许可证

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

DeepSeek Harness 的会话交接与上下文管理:结构化交接文档(export/resume/status)+ 主动上下文裁剪(acp_* 走官方压缩 API)+ 可选的 OpenViking/archify 增强器

这是你的仓库?认领条目

回到顶部