@yeastcloud/dsh-anchor

sessioninstall failsnpmCC-BY-NC-SA-4.0

Session instruction anchor for DeepSeek Harness: the first step of a new session injects a freely composed instruction block as a plugin-sourced message, and it is re-anchored after a summarizing compaction, every N turns, or when metered context pressure crosses a threshold. An /anchor command re-a

下载量 / Downloads
1,611
测试时间 / Tested
2026-11-06
DSH 版本 / Version
0.2.2-rc.1

Install

dsh plugin --profile web add @yeastcloud/dsh-anchor

Sandbox test results

StageResult
1 install✗
2 list—
3 mount—
4 boot—
First error
dsh: initialized profile web at /home/node/.dsh/profiles/web

Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).

README

<div align="center">

定锚 · Anchor

为 DeepSeek Harness 会话注入一段可自由组合的指令,并在压缩或长会话后自动重锚同一段原文。

人设、口吻、工作纪律、项目规范——开场说一次,之后永远算数。

![npm version](https://www.npmjs.com/package/@yeastcloud/dsh-anchor)

![npm downloads](https://www.npmjs.com/package/@yeastcloud/dsh-anchor)

![license](LICENSE)

![node](#安装)

[English](README.en.md) · [更新日志](CHANGELOG.md) · 问题反馈

</div>


它解决什么问题

你在会话开头写下一段"人设 / 规则"——用中文回答、先给结论、危险操作要先问、代码只给最小可运行版本。前几轮它很好用。然后:

  • 第 40 轮:那段话已经沉在上下文最深处,模型开始忘;
  • 一次 /compact 之后:它被摘要吞掉,彻底消失——而摘要不会替你复述"你要求先给结论";
  • 长工具链里:几十条工具结果把它越推越远,风格和纪律一起漂走。

dsh-anchor 就是给这段指令打一口锚:开场注入一次,之后每当会话被压缩、跨过你设定的轮数、或上下文占用升过你设定的 token 数,就把同一段原文重新锚回去。

名字取自航海:锚不是缆绳,不能拖着船走;它只做一件事——让船别漂。这条插件也只做一件事。

特性

特性 · 说明

🧩 多预设自由组合 · 预设库随便建(最多 100 条),勾选任意几条,按你排的顺序拼成一段指令注入。

⚓ 压缩后自动重锚 · 检测到 compaction/summary(自动压力压缩或 /compact)后,在下一个 step 边界(可能是回合中途)立刻重锚,下一次模型请求就带着它。

🔁 按轮数自动重锚 · 距上次注入满 N 轮(默认 20,可调,0 关闭)后,在下一轮开始时重锚一次。

🔻 对端折叠后自动重锚 · 代理层(billion-context)压缩时 DSH 侧一个事件都没有,唯一痕迹是下一轮真实上报的 prompt 塌下去。上一轮峰值 ≥ 128000、本轮掉到它的 70% 以下、绝对降幅 ≥ 4000,就在下一轮开头重锚一次(reinjectOnShrink,默认开)。只认提供方真实上报的 usage,估算值不参与

📈 按 token 压力自动重锚 · 读官方 token 计量:上下文占用(就是输入框旁边显示的那个数)升到你设定的 token 数(reinjectTokenThreshold,默认 0 关闭)后,在下一轮开始时重锚一次。同一次跨越只触发一次——占用一直很高也不会一轮一轮地重锚;被压缩打回阈值以下则重新计数,下次再升过阈值时再触发一次。

🎯 重锚来源可选 · 三档:「定锚原文」= 永远重复会话开头那段;「最近一条」= 重复本插件最近一次注入(例如你随后用 /anchor 定的那条);「按最新组合刷新」= 重锚时用设置页里当前的组合——改了预设或勾选,正在进行的会话下次重锚就换成新文本。

🧠 触发判定可回放 · 压缩与轮数重锚只读会话日志,是纯函数;重启、续聊、日志回放都得到同样的结论,同一次触发天然不会重复。token 压力触发读的是官方 token 计量(它本身也是对会话日志的回放),并按会话记一个进程内的「已触发位」——见下方「触发时机」。

🚦 上限护卫 · 单条上限(编辑限长)与合并上限(注入门禁)都在设置页可改,默认 8000 字;超上限拒绝注入并写日志,绝不静默截断你的字。

🐋 子代理默认不锚 · 被委派出去的子代理会话默认不定锚、也不重锚——人设与纪律是给「你自己在开的会话」的,子代理复述一遍只是白花 token;想在子代理里也生效就在设置页打开。

⚓ /anchor 命令 · 输入 /anchor 立即把当前组合定锚进本会话:命令在本地执行,不花 token、不开新回合、不延长正在跑的回合(锚握在插件手里,下一条消息的第一个 step 注入——不进输入框、不会自己开回合)。/anchor status 只看状态不注入。

🎛 自带设置页 · 预设库(分页、多选、固定「不注入」行)、注入顺序(拖拽 / ↑↓ 排序、合并预览)、重锚策略 —— 全是可视化操作,不用改配置文件。

💾 预设可导入 / 导出 · 导出成一份自带格式标记与版本号的 JSON,含库顺序、注入顺序与全部设置(9 项,一个不落),换机器、备份、分享都行。导入是可合并非破坏式的:不冲突的预设默认勾选引入,同一个 id 内容不同的列为冲突项(默认保留现有,面板给出「现有 vs 导入」两侧摘要);设置按 注入行为 / 重锚策略 / 字数上限 三组列出,默认按项勾选、可整组取消;selectedIds 里引用不到的 id 不再整份拒绝,而是作为「无法解析项」列出(默认不选)。「注入顺序(组合)」在面板里是单独一项(默认勾选、可取消),文件只改组合也能导入。只有文件本身坏了(非 JSON / 不是本插件的文件 / 版本不认识 / 结构不对)才整份拒绝并给一条明确理由。v1 老文档仍可导入,且不会用它的默认值覆盖你的设置。

🔒 绝不外传 · 纯本地插件:不联网、不埋点、不写任何远端;设置只落在你自己的机器上(0.1.6 线是 ~/.dsh/settings.yaml 的 dsh-anchor 段;0.1.7 线是当前 profile 的配置文档,entry id 同样叫 dsh-anchor)。

DSH 版本对应

DSH 版本 · 定锚插件版本

0.2.x(0.2.0–0.2.1,含 rc;本机实测 0.2.1-rc.4 可跑) · 0.15.5(当前)

0.1.7(含 0.1.7-rc.1 / 0.1.7-rc.2) · 0.15.5(当前)

0.1.6(本机实测可跑) · 0.11.0

0.1.5(peer 声明覆盖,未实测) · 0.11.0

- 0.2.x 线本机实测可跑 ✓(2026-10-28 在 0.2.1-rc.4 上复测):0.15.x 同时覆盖 0.1.7 线与 0.2.x 线,两条线都指最新版。
- 0.13.0–0.14.x 的 peer 要求 = >=0.1.7-rc.1,0.15.0 起抬到 >=0.1.7 ⇒ 不再兼容 0.1.6 线 ✗(0.1.7 换了设置服务、客户端设置面与会话格式 v4,详见 CHANGELOG.md)。
- ⚠️ >=0.1.7 在裸 semver 规则下不匹配 0.2.1-rc.4 这类 prerelease(范围不含 prerelease 时,带 prerelease 的版本落在范围外),而 DSH 装载器实测能加载 ⇒ 这条下限不是笔误,别改回 >=0.1.7-rc.1。
- 0.11.0 的 peer 是 ^0.1.5-rc.2(git show 6358c90:package.json 实测):按 node-semver 规则只放行 0.1.5 同 tuple 的范围;
0.1.6-alpha.2 不在该声明覆盖内,但本机长期实测可跑 ✓ —— 故 0.1.6 一行写「实测可跑」、0.1.5 一行写「peer 覆盖、未实测」✓。

安装

# 需要 DeepSeek Harness(Web profile)
dsh plugin --profile web add @yeastcloud/dsh-anchor

# 重启 profile 后生效(Host 半只在启动时加载)
dsh web

装好后打开 设置 → 定锚:

1. 「+ 新增预设」写几段你要长期生效的指令(人设、纪律、项目规范各一条都行);

2. 勾选它们,在下方注入顺序里拖成你想要的拼接顺序;

3. 新建一个会话——第一条指令就是这样进来的。

环境要求:Node ^22.19 || >=24;DeepSeek Harness 0.1.7 线起(peer 写 >=0.1.7)至 0.2.x 均可用,其中 0.2.x 线本机实测可跑(当前 0.2.1-rc.4)(见上表)。插件含 Host 半(注入逻辑)与 Client 半(设置页)。

设置放在哪:0.1.6 线由 ctx.settings.installSection 把设置节落在 ~/.dsh/settings.yaml;0.1.7 线起该 API 被删除,设置表单改由本插件自己的 Config schema 生成,值随当前 profile 的配置文档走——entry id 与插件命名空间同值(dsh-anchor),平台的退休导入机制会把老的 dsh-anchor: 段落到同一个 entry 上。

界面语言:设置页与左侧导航跟随 DSH 的语言设置(中文 / English);/anchor 的输出跟随宿主进程的 LC_ALL / LANG(语言标签首段是 en 时走英文,例如 en、en_US.UTF-8;其余一律走中文)。

命令

在任意会话里输入 /anchor:

命令 · 做什么

/anchor · 把当前组合定锚进本会话:注入一条插件来源的消息,在下一条消息的第一个 step 生效(不进输入框、不会自己开回合,也不会延长正在跑的回合),此后该会话的压缩/轮数重锚都复用它。命令处理器在接收它的 agent 上本地执行,所以不花 token、不产生模型回复。

/anchor status · 只读报告:总开关、组合与字数、重锚来源 / 轮数间隔 / token 阈值 / 压缩后开关、本会话的注入历史(首条 / 最近一条各自的 seq 与字数,以及排队中还没落地的那条)与距上次注入过了几轮。不注入任何内容。

被拒绝的情形都会明确报错,而不是"差不多地注入":总开关关闭、组合为空(「不注入」)、合并字数超上限。

为什么把它做成命令:命令契约明确写着 handler 在 agent 上本地执行、不把命令发给模型——所以它能做到"零副作用地改上下文",这正是"给会话打锚"该有的样子。也正因为如此,早先那个挂在输入框旁的「发组合」按钮连同它的"内容摘要认领"机制一起删掉了:命令把同样的文本送进会话,却不用花 token、不用开回合,也不用靠比对内容来认出自己。

工作原理

graph TD
  A[新建会话 · turn 1] -->|定锚:注入组合文本| B{会话进行}
  B -->|每个 step 边界判定触发条件| C{触发条件?}
  C -->|日志出现 compaction/summary| D[重锚:下一个 step 边界立即注入]
  C -->|距上次注入满 N 轮| E[重锚:下一轮 step 1 注入]
  C -->|上下文占用升过 token 阈值| G[重锚:下一轮 step 1 注入一次]
  C -->|都没有| B
  D --> B
  E --> B
  G --> B
  B -->|你输 /anchor| F[锚握在插件手里 · 下一条消息的第一个 step 注入]
  F --> B

注入形态:一条 source.kind = "plugin" 的用户消息(模型可见、进日志、与真人发言可区分);当 loop 没有可挂靠的输入时不会凭空开一轮。

触发时机

触发 · 时机 · 注入的文本

定锚(会话首轮) · 本会话自己的第一轮第一个有效 step · 设置页当前组合

压缩后重锚 · compaction/summary 之后的第一个 step 边界(可落在回合中途) · 按「重锚来源」取定锚原文或最近一条

按轮数重锚 · 距上次注入满 N 轮后的下一轮 step 1 · 同上

按 token 压力重锚 · 官方 token 计量的上下文占用升过阈值后的下一轮 step 1;同一次跨越只触发一次 · 同上

对端折叠后重锚(shrink) · 真实

…(truncated)

Rendered from the README published on npm. Formatted only, not edited.

Versions

Published on npm::22,最后更新 2026-10-28

Links

GitHub · Homepage · npm

Same category