前言¶
在 DSH 的 Web UI 里做智能体对话时,经常会遇到这样的需求:某轮用户消息发送出去后,智能体已经继续回复,甚至触发了子智能体指令、压缩、重试、工作流等后续节点;此时希望把这一整段从对话中移除,让后续模型看不到这些内容,但不想因此回滚已经发生的代码或文件改动。
dsh-recall 为 DSH Web UI 提供这个能力:撤回一条消息及其之后的全部内容,并通过 append-only 日志中的持久墓碑事件保证重启后仍然生效。DSH 的插件生态强调「一切皆插件」;下面按资料介绍这个客户端插件的功能、使用方式和实现边界。社区目录是独立站点,不应理解为官方应用商店。
这是什么¶
dsh-recall 是 DSH 的客户端插件,许可证为 MIT。仓库路径为 Mongfayi/dsh-recall,资料中的维护者信息记为 Mongfayi。
它解决的问题是:把一条用户消息及其之后的所有内容从对话中移除——界面不再显示、模型不再可见——但绝不回退任何代码/文件改动。撤回不是简单删除界面元素,而是通过追加日志(append-only)中的一条持久墓碑事件实现,重启后依然生效。
核心功能¶
- 整轮撤回:点击用户消息旁的撤销按钮,撤回该轮对话及其后的全部内容。撤回范围包括用户提问、智能体回复,以及长对话中的子智能体指令、压缩、重试、工作流等。
- 恢复编辑:撤回后,被撤回用户消息的文本与图片整体恢复到输入框,可修改后重新发送。
- 不破坏界面:撤回按钮与框架的复制按钮同一行、互不遮挡;消息气泡、复制键、引用标签、图片画廊保持框架原样;含图片的消息始终正常显示图片。
- 持久且可追溯:撤回记录是一条持久墓碑事件,随会话日志
flush持久化,重启后依然生效;日志记录一个不删。 - 实时更新:墓碑到达实时会话后触发会话装配重建,被撤回行立即消失,无需刷新页面。
- 宿主端路由:提供
POST /recall路由,支持{sessionId, messageId}或{sessionId, boundary},基于既有会话协议完成撤回。
典型用法¶
1、会话空闲时,点击用户消息旁的撤销按钮(复制键左侧)。
2、确认弹窗后,该轮对话及其后的全部内容被撤回。
3、撤回位置显示「已撤回的消息」提示。
4、被撤回消息的文本与图片恢复到输入框,可修改后重新发送。
注意:智能体运行中撤销按钮会禁用,需先停止当前回合再撤回。
安装与启用¶
资料未提供官方安装命令,因此这里不补充拼接出的安装命令。
插件依赖 @deepseek-ai/dsh-session:经本插件 node_modules/@deepseek-ai/dsh-session 符号链接指向 dsh 源码部署的 packages/core/session,与宿主共享同一模块实例、同一版本。README 给出的重建符号链接命令如下,用于让插件指向同一份 session 模块:
ln -sfn ../../../../deepseek-harness/packages/core/session node_modules/@deepseek-ai/dsh-session
因为插件会注册宿主路由并覆盖客户端渲染器,安装前建议先阅读源码,确认 MIT 许可证和行为符合预期。插件以当前 dsh 进程权限运行,部署环境应按生产组件对待。
工作原理¶
宿主端¶
宿主端提供 POST /recall 路由,请求体可以是:
{ "sessionId": "...", "messageId": "..." }
或:
{ "sessionId": "...", "boundary": "..." }
撤回处理分为三步:
1、校验边界:边界必须为当前 surface 上的活跃消息节点;如果该边界已撤回或被遮蔽,会返回 recall-rejected。
2、追加墓碑:追加一条持久墓碑,形式为空内容的 assistant/message,其中 surfaceOp 为 {op:'replace', start: boundary, end},且 sourceEventSeqs 覆盖全部被遮蔽节点。空内容 assistant 消息不派生任何消息,模型可见历史收缩到 boundary 之前。
3、标记与持久化:在 data.recall = {boundary, end} 中记录墓碑标记,用于客户端提示节点;随后随常规 flush 持久化。
相关错误码包括:
session-not-found
subagent-owned
agent-busy
message-not-found
recall-rejected
浏览器端¶
浏览器端通过客户端脚本完成按钮、隐藏、提示与输入框恢复。
撤回按钮以 priority: -1 覆盖 conversation.chat.node 的 user keyed 渲染器,并委托给框架自己的用户渲染器。这样消息气泡、复制按键、引用标签与图片画廊都保持框架原样;撤销按钮定位在框架操作行同一行、复制按键左侧,避免遮挡。
图片渲染始终经过框架的图片槽 renderMessageImages,会话授权、标签与灯箱保持框架原样,不依赖插件自带的画廊加载器。
整轮隐藏发生在装配期。实现上统一包装每个框架对话定义的 buildViewNode:调用原构建器后,如果该行的锚点 seq 落在已撤回区间内就返回 null。涉及的锚点字段包括 anchorSeq、data.seq、data.finalNode.seq 和 data.closing.finalNode.seq。资料列出的覆盖节点种类包括 steering、context、assistant-step、command、manual-compaction、compaction、model-retry、turn-error、turn-max-tokens、turn-tail、unknown、command-input、tool-call、workflow-run。
「已撤回的消息」提示通过 recall 定义匹配墓碑事件(assistant/message + data.recall)注册。
实时更新方面,墓碑到达实时会话后,会重新注册本插件的 recall 定义,触发一次会话装配重建,被撤回行立即消失,无需刷新页面。
输入框恢复时,文本经 conversation.input.for(scope).setDraft() 写回;图片经会话远程 sessions.binding(sessionId).session.readAttachment() 取回字节,再经 conversation.createDraftImages() 注册为草稿图片,并用 addImages() 挂到图片轨道。被撤回附件仍保留在 append-only 日志中,因此恢复有真实数据源;单张图片恢复失败不会影响其余恢复,也不会回滚已完成的撤回。
兼容性与适用场景¶
dsh-recall 不需要 session/recall 事件类型、Session.recall 或客户端窗口过滤;它基于框架既有的 surface 替换协议与 keyed Chat Node 席位。
已适配并验证于 DSH 0.1.1-rc.1、0.1.1-rc.2 与 0.1.2-alpha.1。资料列出的 0.1.2 适配点包括:
- 定义注册服务由
conversationEvents改为uiConversation.events; - 子代理归属检查(原
@deepseek-ai/dsh-api-remotes的hasApiRemoteSubagentOwner)已内联进插件; - 图片恢复改走会话远程
readAttachment(原conversation.resolveImage已移除); - 客户端 bundle 改经
/plugins/??<id>/client.jscombo URL 服务。
适用场景是 DSH Web UI 中需要撤回某轮对话及后续节点,同时保留代码、文件或其他副作用的场景。如果对话中包含子智能体指令、压缩、重试或工作流节点,并且希望它们一起从后续模型可见历史中消失,这类插件会直接对应这个需求。
注意事项:
- 撤回边界必须为当前
surface上的活跃消息节点,否则可能返回recall-rejected; - 智能体运行中撤销按钮会禁用,需先停止当前回合再撤回;
- 撤回不会回退代码或文件改动,但它会改变后续对话中模型可见的历史范围;
- 资料未提供官方安装命令,部署前应按项目自身插件安装流程处理,并检查源码与许可证。
链接¶
资料提供的目录页线索是:https://www.skillhub.cn/plugins/Mongfayi/dsh-recall
GitHub:https://github.com/Mongfayi/dsh-recall