dsh-token-guard
Verifieddsh-token-guard · v0.1.0-beta.13 · MIT · Web UI
[EXPERIMENTAL / PRE-RELEASE] Session token budget guard for DeepSeek Harness: per-session token accounting with an automatic task interrupt at the configured cap.
Install
dsh plugin add dsh-token-guard Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Published to npm without a public repository. Inspect the package contents before installing.
Creators
Readme
说明
本插件代码均由 DeepSeek 完成。
dsh-token-guard
DeepSeek Harness 插件:统计每个会话的累计 token 用量,并在到达可配置上限时自动中断当前任务(而不是像原生那样只做上下文压缩)。可在官方桌面应用里安装运行。
⚠️ 开发中 / 实验性(pre-release) 本插件尚处于开发阶段,功能不完善:v1 服务端功能(
tokenBudget、contextRatio、includeSubagents)均已实现, 但客户端 UI(设置卡片 / 侧栏入口)尚未做,配置项与行为可能随时变更。请勿用于生产环境,当前版本仅供试用与反馈,下载前请自行评估风险。
- v1 = 纯服务端(零 UI),核心是
llm/stream瀑布短路 +session/event用量累计。 - 默认
overLimit: 'alert'(只告警不中断);用户显式设block才中断。
目录结构
src/
├── index.ts # 插件入口(export name / inject / Config / apply)
├── config.ts # 配置 schema(schemastery)+ 默认值归一化
├── ledger.ts # 累计用量账本(纯内存,无依赖)
├── policy.ts # 上限判定(纯函数,无依赖)
├── guard.ts # llm/stream 拦截 + session/event 累计 + 告警(无运行时依赖)
├── notify.ts # 告警可见性:console.warn + 可选 webhook POST(fail-soft)
├── persistence.ts # storage-domain 持久化(可选依赖,缺失降级内存)
└── command.ts # /token-guard(status / unblock / config / set)
tests/
├── policy.spec.ts
├── ledger.spec.ts
├── guard.spec.ts
├── notify.spec.ts
├── command.spec.ts
└── persistence.spec.ts
开发
npm install --legacy-peer-deps # 只装 devDeps;@deepseek-ai/* 不在 npm,跳过 peer
npm run test # vitest(纯逻辑单测)
pwsh ./scripts/link-types.ps1 # ① 链 .typecheck/@deepseek-ai(typecheck 用)② 链 node_modules/@deepseek-ai/schemastery(运行时 peer)
npm run typecheck # tsc -p tsconfig.typecheck.json(对齐真实 DSH 类型)
npm run build # tsdown → lib/(ESM + .d.ts,@deepseek-ai/* 走 external)
@deepseek-ai/*不在公开 npm registry(本机已核实),运行时由桌面宿主解析、不打进包。 typecheck 靠tsconfig.typecheck.json的paths指向.typecheck/@deepseek-ai/*——那是scripts/link-types.ps1生成的目录 junction,指向D:\code\Git\deepseek-harness的vendor/*+packages/*/*(277 个均已 built 的.d.ts)。⚠️ 每次
npm install都会把node_modules/@deepseek-ai/schemasteryjunction 当 extraneous 清掉, 之后--patch挂载会报Cannot find package '@deepseek-ai/schemastery'。重跑一次pwsh ./scripts/link-types.ps1即可恢复(幂等)。DSH 检出路径变了也用它-Root <新路径>。
本地挂载验证(不改 3080 宿主 / 不碰 ~/.dsh)
用 DSH 检出的 bundled CLI(无需 tsx/esbuild)+ 隔离 DSH_HOME 挂载,看启动日志确认加载:
cd D:\code\Git\deepseek-harness
$env:DSH_HOME = 'D:\code\local\.dsh-test-home'
node apps\cli\lib\bin.js --profile token-guard-test --from-default-profile web --patch D:\code\local\dsh-token-guard\dev-patch.yml --host 127.0.0.1 --port 3090 --no-open
# 首次跑用 --from-default-profile web 建隔离 profile,之后可去掉;看到下面这行即加载成功:
# [dsh-token-guard] loaded: tokenBudget=unlimited, contextRatio=unset, warnRatio=0.8, overLimit=alert
启动日志用
console.log(不是ctx.logger):DSH 没有任何 profile 挂 cordis logger exporter,ctx.logger.*只进 ring buffer、不会打印/落盘(见worker-host.ts的installLogSink注释)。dev-patch.yml是本地专用 overlay(name: ./lib/index.js会被锚成 file URL);发布用的cordis.patch.yml仍是包名dsh-token-guard,随包走。
发布(npm)
$env:NPM_TOKEN = '<token>'
npm publish # 稳定版:不指定 --tag,默认落到 latest
npm publish --tag beta # 预发布:只动 beta 标签,不动 latest
⚠️ 稳定版必须落到
latest。 桌面端「安装插件(不填版本)」走pnpm add <包名>, 解析的是latestdist-tag;若一直用--tag beta发版、latest停在老版本,用户 卸载重装 / 不带版本安装会拿到旧的latest(再叠加桌面端 pnpm 元数据缓存~/.dsh/desktop/pnpm/cache,TTL 内不回源),而不是最新版。 因此:预发布用--tag beta;稳定版发布时不要加--tag beta(默认即 latest), 或事后用npm dist-tag add dsh-token-guard@<version> latest补齐。
配置(~/.dsh/settings.yaml,本插件命名空间下)
token-guard:
tokenBudget: 200000 # 绝对上限;未设置=不限
# contextRatio: 0.8 # 或:当前上下文压力占模型窗口比例上限;默认关闭(不设/注释即关,暂定),与 tokenBudget 独立(任一先触发)
warnRatio: 0.8 # 达上限该比例时告警
overLimit: block # alert=仅告警(默认);block=到限短路后续请求
# includeSubagents: false # 是否把子代理 token 归入父会话累计(默认 false;父含子、子仍单独记一行)
# webhookUrl: 'https://…' # 告警 webhook(可选,需以 http(s):// 开头)
告警可见性
warnRatio 命中 / overLimit=alert 超限 / overLimit=block 阻断 / 解除阻断,都会通过两个通道送出(都 fail-soft,不影响主流程):
- 宿主 stdout:
console.warn打印[dsh-token-guard] <kind>: session=<id> <消息>。 - webhook(可选):配置
webhookUrl后,POST 通用 JSON 到该地址:
{
"text": "[dsh-token-guard] blocked: session=xxx …",
"kind": "warn | over | blocked | unblocked",
"sessionId": "…",
"cumulativeTokens": 1234,
"budgetLimit": 2000,
"pressureLimit": null,
"timestamp": "2025-…"
}
text字段兼容 Slack incoming webhook;钉钉/飞书/企业微信自定义机器人的信封({msgtype,…})需用户侧网关适配。webhookUrl必须以http://或https://开头,否则构造期校验报错。
⚠️ 默认
overLimit: 'alert'只告警、不中断:超限时不会拦下任务,只会走上面的告警通道;要「到限自动中断任务」,必须显式设overLimit: 'block'。
命令
/token-guard—— 查看当前会话累计用量、上下文压力、阻断状态/token-guard unblock—— 解除当前会话阻断,并清零累计用量(彻底重置本会话额度)。注意:若阻断是contextRatio(上下文压力)触发的,历史消息仍在上下文中,压力不会因 unblock 下降,会立刻再次触发——此时应压缩上下文 / 清历史 / 开新会话/token-guard config—— 查看生效配置/token-guard set <key> <value>—— 在线修改配置,写同一份 settings 命名空间(即 settings.yaml 的token-guardsection),热生效。key 支持tokenBudget/contextRatio/warnRatio/overLimit/includeSubagents/webhookUrl;set <key> unset清除该字段回落默认值(如set tokenBudget unset= 不限)。contextRatio本身就是开关:set contextRatio <0..1>开启、set contextRatio unset关闭(默认关闭,暂定)
关键事实(来自计划文档,均已核实)
- 携带
usage的事件是assistant/message(event.data.usage);监听ctx.on('session/event', (session, event) => …)。 - 拦截点
llm/stream:ctx.on('llm/stream', (options, next) => …, { prepend: true }),options.sessionId标识会话(辅助调用可能为 undefined)。 TokenUsage = { inputTokens, outputTokens, totalTokens?, cacheReadTokens?, cacheWriteTokens?, reasoningTokens? }。- 修正性错误流形状见
src/guard.ts的correctiveStream。