Skip to content

dsh-token-guard

Verified

dsh-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/schemastery junction 当 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 <包名>, 解析的是 latest dist-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,不影响主流程):

  1. 宿主 stdout:console.warn 打印 [dsh-token-guard] <kind>: session=<id> <消息>。
  2. 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-guard section),热生效。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。