dsh-compact-enhanced:让 /compact 可排队、可取消,并在菜单里显示实时状态

前言

在智能体对话里,历史随着轮次增长,迟早需要压缩。DeepSeek Harness 内置了 /compact,但这个命令在慢链路或上游不稳定时有两个实际问题:agent 忙时它直接拒绝,你这次想压缩的请求就丢了;它执行期间 composer 处于锁定状态,如果 summarizer 卡住不流式返回,你没有按钮可按、也没有地方可输入。

下面介绍的插件就是对着这两个问题做的:imrcpan/dsh-compact-enhanced,维护者 imrcpan,分类「工作流」。它把 /compact 请求改成立即返回、在 agent 忙时排队、后台执行的方式,并提供 /compact-cancel 取消,同时在斜杠菜单里用一行状态提示告诉你现在发生了什么。

这是什么

一个 DSH(DeepSeek Harness)插件,DSH 本身的理念是「一切皆插件」,能力通过插件拼装。这插件针对的是「手动触发上下文压缩」这一段工作流,不改动模型、不改动提示词,只改命令的调度与可观测性。

解决的问题:

  • 内置 /compact 在 agent 非空闲时拒绝请求,请求会丢失在正在运行的 turn 中间。
  • 内置 /compact 执行期间命令行 composer 不可用,卡住时无法中断。
  • 完成后没有方便的方式告诉用户「到底压了多少、压缩是否成功」。

命令与行为

插件提供两个命令,都不接受参数:

命令 行为
/compact agent 空闲时立即开始;否则排队,等到当前 turn 结束再开始
/compact-cancel 丢弃排队中的请求、中止正在运行的 summarizer 请求,或取消由其它命令模块启动的压缩

两个命令都不接受参数。当已有请求在排队或运行中时,/compact 会拒绝第二个请求,并在报错中指出用哪个命令取消。

取消这一行什么时候落定,取决于 abort 是否到达 summarizer:

  • Compaction cancelled. —— 运行已结束。
  • Cancellation requested; the summarizer has not stopped yet. —— 在 cancelTimeoutMs 内 summarizer 没有响应 abort,请求仍在。

会话面板里看到什么

请求自己的生命周期行会立刻出现:

Compaction started in the background.
Compaction queued; it starts when the current turn ends.

因为插件把请求命令的 id 作为 sourceCommandId 传给压缩引擎,压缩事件会挂到这一行上,摘要落地后这一行会升级成标准的压缩卡片。尝试结束后,插件会为同一个 id 再补一条 command/done,记录结果:

Compacted 42 history items (~12,345 tokens).
No compactable history yet.
Compaction cancelled.

以及 busy、changed、summary、commit、persistence 这几类已文档化的失败文案。

那条额外的 command/done 是刻意加的:命令服务在 handler 返回后就会关闭请求(这正是 composer 能解锁的原因),所以想在原来那一行上报后台结果,只能为同一个 id 再走一次生命周期事件,客户端会把它折叠到已有行里。

斜杠菜单里的实时状态

/compact 在斜杠菜单里不是一句固定文案,而是一行状态,会随状态变化,并在有时附加当前会话的 token 估算:

状态 菜单行文本
空闲 Ready · 12.3k token
在等当前 turn 结束 Queued · 12.3k token
summarizer 正在运行 Compacting · 12.3k token
拿不到 token 估算 Ready / Queued / Compacting,不带 token 部分

token 标签用一位小数加固定 k 单位(Claude Code 风格):3.9k token、12.0k token、0.9k token。估算为零或拿不到 token 计量时整段省略,所以不会看到 0.0k token。

/compact-cancel 那一行同步状态:Cancel the queued compaction、Cancel the running compaction,或 No queued or running compaction to cancel。

实现上,插件在命令目录这一层替换了 commands.list() 里的 compact 描述符,并去掉内置定义 id,避免客户端把描述重新本地化回通用文案;状态每次变化时重新发布 commands/change,下一次打开菜单会重新拉取。代价是菜单行显示的是原始命令名,丢掉了内置的本地化标签和图标——这是为换取实时状态做的取舍。

安装与启用

包本身是一个 DSH bundle:package.json 声明了 dsh.bundle.patch,所以标准安装会同时挂载命令。

dsh plugin --profile web add github:imrcpan/dsh-compact-enhanced

装完要重启 profile,因为组合关系是在启动时读取的。随包发布的 cordis.patch.yml 会禁用内置的 command-compact 行并插入插件行,这就腾出了 /compact 这个名字。

从本地 checkout 安装时,要作为 bundle 来装,好让它的 manifest 生效:

dsh plugin --profile web add /absolute/path/to/dsh-compact-enhanced

file: 依赖也可以被接受,但那样不会应用 bundle 层,需要手工把 example/cordis.patch.yml 里的那一行抄进 profile 的 cordis.patch.yml。

典型用法

在对话里直接使用即可:

# 触发一次压缩,立即返回
/compact

# 如果排队中或正在运行,想取消
/compact-cancel

运行环境要求:

  • DeepSeek Harness 0.2.1-rc.5(其它 release 可能不同;package.json 里 engines 写的是 dsh >=0.2.1-rc.2)
  • Node.js >=20.3,插件用到了 AbortSignal.any

配置项

配置是可选的,bundle 层不提供任何配置。

key 默认值 含义
retryDelayMs 250 一次启动与刚好新开的 turn 撞车后,等多久重试
maxQueueAttempts 40 重试多少次后把排队失败写进行里
cancelTimeoutMs 2000 取消命令等待被 abort 的运行落定的时长,超时后报告请求仍在

实现上的几点

  • 命令注册表不允许同一名重复全局注册,因此 bundle 层禁用内置 command-compact 行,插件用自己的 /compact 注册到这个名字。如果 profile 仍然挂载了内置行,插件会捕获重复注册并打 warning,保留 /compact-cancel,把内置命令留在原处。
  • 压缩入口没有办法从 profile 根访问到:agent preset 把它挂在隔离的 per-agent 组里,实际 agent.ctx 也不暴露它。插件因此用 commands.find 解析 agent 能用的那个 compact 定义(通常是 preset 挂载的那个,其 handler 闭包持有隔离服务),该 handler 以插件自己的 AbortSignal 在后台运行。找不到其它定义时就退回自己 context 可达的引擎;两者都不可达时,命令会报告压缩不可用,而不是静默地什么都不做。
  • agent preset 自己注册了 scoped /compact,会遮蔽全局替换。插件在命令服务这一层处理 /compact 这一行,所以排队逻辑作用在目录解析出的那个定义上;插件卸载时会还原原来的 execute 方法。
  • 取消就是 abort 插件交给被委托 handler(或直接交给引擎)的 signal,因此能作用到 summarizer 请求。
  • 一次启动如果与新开的 turn 抢跑,会在 retryDelayMs 后重试,最多 maxQueueAttempts 次,之后才写失败。排队状态按 agent 划分且只在内存里。
  • agent 忙时什么都不跑:队列等 agent 空闲,然后在 maintenance 里启动,位置正是内置命令本来会跑的地方。

适用场景与注意

适合:在慢链路或上游不稳定的环境下工作的 DSH 智能体开发者;需要手动压缩历史、并且希望压缩期间不阻塞对话输入的人;希望取消操作不必等 summarizer 自然结束的人。

需要注意:

  • 这个插件以当前 dsh 进程的权限运行,能力边界就是 profile 里已有的命令与服务,安装前应检查源码与许可证(MIT)。
  • 没有客户端按钮。取消只能靠 /compact-cancel,有效是因为 /compact 在 summarizer 启动前就返回了。
  • 队列在内存里。host 重启会结束 turn 并丢掉排队请求,之后要重新 /compact。
  • 自动压力压缩没有被改动,只包装了手动命令。排队的请求即使自动压缩已经做过一轮,仍会执行一次压缩,因为插件不知道下一次空闲检查会发现什么。
  • 取消报告是即时的,但最终这一行要等后台结果落定。

结尾

这个插件的价值很清楚:/compact 从「一次会被拒、无法中断、结果不可见的请求」变成「立即返回、可排队、可取消、状态可见的后台任务」。代码量不大,排队与取消状态按 agent 保存在内存中,配置只有三个数值项。如果你的使用场景是长会话加不稳定上游,值得一试。

目录页:https://www.skillhub.cn/plugins/imrcpan/dsh-compact-enhanced

仓库:https://github.com/imrcpan/dsh-compact-enhanced

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜