dsh-recall-unread
Manifest valid★ 51DeepSeek Harness (DSH) Plugin: Recall sent-but-unread text messages before the model reads them, supporting recall of individual or all messages. A DeepSeek Harness plugin that recalls sent-but-unread text messages before the model reads them — one-by-one or all at once.
dsh-recall-unread
DeepSeek Harness (DSH) plugin: recall text messages that have been sent but not yet read by the model.
When you send text while the model is running (interjection send), the message appears as a "pending" bubble at the end of the conversation — before the model claims (reads) it, you can recall it with one click.
Screenshot placeholder: you can save an actual screenshot of the effect as
docs/screenshot.pngand uncomment the![screenshot]above.
✨ Features
- Effective on enable: after enabling this plugin in the plugin market (dsh-market) or the plugin management page, the "unread messages" strip takes effect immediately; disabling it unloads it entirely. No launcher menu dependency.
- Unread messages strip: lists all sent-but-not-yet-read interjection messages (
placement: 'steering') above the input box (in theconversation.input.dockslot). - Recall one: each message shows preview text plus a "Recall" button; clicking it removes the message from the conversation and displays the notice "Recalled one message".
- Recall all: when multiple unread messages exist, a "Recall all" one-click action is provided.
- Read-only semantics: once a message is claimed by the model (reading has started), it automatically leaves the strip and can no longer be recalled — strictly enforcing "only unread messages are recallable".
- Graceful degradation: when a recall fails (the message has already started being sent), a clear notice is shown and the session is not corrupted.
🔍 How It Works
In DSH, a "sent but unread" message = a message still sitting in the Agent inbox (pending queue), i.e. a pending item in the ConversationSnapshot.queue snapshot. It has two placements:
| placement | Meaning | Current official UI |
|---|---|---|
queued | Queued while running, waiting for the next round | The official queue dock already has a "delete" button |
steering | Interjection sent while running, shown as a pending bubble at the end of the conversation | No recall entry point at all ← this plugin adds one |
Recall in two versions:
- Static version (recommended): the Client directly calls the official session RPC
session.updateQueue(itemId, { kind: 'remove' })(same implementation as the official queue dock's "delete"). - Dynamic version: uses a private in-package RPC:
Client (撤回按钮)
│ host.call('recall', { sessionId, itemId })
▼
Host harness.handle('recall')
│ agents.get(sessionId).inbox.remove(itemId)
▼
效果 消息从 inbox 移除 → session/queue 快照更新 → 气泡与条带同步消失
The Host-side logic is consistent with the internal implementation of kind: 'remove' in the official session.updateQueue (agent.inbox.remove).
📁 Directory Structure
dsh-recall-unread/
├── README.md # 本文件
├── LICENSE # MIT 许可证
├── .gitignore # Git 忽略规则
├── package.json # 项目元数据(GitHub 展示用)
├── plugin.json # 插件清单(名称 / ID 前缀 / 代码来源)
├── src/
│ ├── host.js # Host 半端源码(动态版,可直接作为 code.host)
│ └── client.js # Client 半端源码(动态版,可直接作为 code.client)
├── plugin/ # 静态 web profile 插件包(安装后随 DSH 加载,推荐)
│ ├── package.json # 安装包元数据(含 dsh.client 声明)
│ ├── host.js # Host 半端(空实现占位,撤回走客户端官方 RPC)
│ └── client.js # Client bundle(__ModuleLoader__ 注册)
└── docs/ # 截图等附加资源(可选)
🚀 Installation & Activation
Option 1: Static Installation (Recommended — loads with DSH, persists across restarts)
Install the plugin/ directory as a static plugin for the DSH web profile:
- Copy the entire
plugin/directory into the web profile's node_modules:~/.dsh/profiles/web/node_modules/dsh-recall-unread/ - Append a load entry at the end of
~/.dsh/profiles/web/cordis.patch.yml:- insert: - id: recall-unread name: dsh-recall-unread - Restart DSH. Once the plugin is enabled, the "unread messages" strip is active. You can disable/enable it at any time from the plugin marketplace (dsh-market) or the plugin management page.
Note: The static version directly calls the official session RPC
session.updateQueue(itemId, { kind: 'remove' })on the Client side (the same implementation as the "remove" action in the official queue dock). No dynamic RPC is needed, and behavior matches the official UI exactly. The plugin does not depend on Amadeus.
Option 2: Dynamic Plugin (Temporary — does not survive restarts)
This project can also be tried temporarily as a DSH dynamic Cordis plugin (process-level definition):
- Open DSH and enter the Cordis plugin development flow (
cordis_define) in a conversation. - Create a new plugin:
idPrefix:recallcode.host: paste the full contents ofsrc/host.jscode.client: paste the full contents ofsrc/client.jsname:Recall Sent Unread Messagespurpose: a one-line description of the use case
- Use the returned
pluginId/packageIdto callcordis_runand activate it, then approve the run in the UI. - After activation, send an interjection message while the model is running to see the "unread messages" strip and the "Recall" button above the input box.
Note: dynamic plugins are process-level and temporary — redefine after DSH restart. For long-term use, prefer the static install in Option 1.
⚙️ Configuration
This plugin is zero-config and ready to use out of the box — no setup required. If you need to fine-tune it, modify src/client.js and re-define it (cordis_define). Common tunables:
| Item | Location | Description |
|---|---|---|
| Strip ordering | slots.register(…, order: 30) | A larger number places it further back; adjust to control how close it sits to the input box |
| Toast dwell time | ctx.timeout(() => setStatus(null), 2500) | How long the recall result toast is shown before auto-dismissing (milliseconds) |
📖 How to Use
- Ensure plugin is enabled (default on after install; toggle in marketplace/management page).
- While the model is running, use interjection send to send a text.
- The message appears at conversation end (marked "pending"), and the "unread messages" strip shows above the input box.
- Click "Recall" next to the message — bubble and strip vanish; toast: "one message recalled".
- For multiple unread, click "Recall all" to clear at once.
⚠️ Limitations & Known Issues
- Only messages not yet read by the model (still in inbox) can be recalled; once claimed, it's final.
- Queued messages (
queued) already have delete in the official queue dock; not duplicated here. - No official "bubble-level" slot exists, so the recall control is in the strip above the input box.
- Dynamic plugins don't persist; static install (Option 1) auto-loads with DSH and survives restarts.
🔒 Anti-Lockout Mechanisms (Important)
DSH web startup is fail-loud: if any plugin fails to load, the whole screen shows
Failed to load plugins, preventing DSH from opening. This plugin previously triggered a startup lockout with ReferenceError: module is not defined because the bundle referenced
module.exports. This is now hardened at three levels:
1. Bundle Structural Fix (In Effect)
The factory in DSH's window.__ModuleLoader__.load only injects require, not
module / exports. plugin/client.js no longer references module / exports
at all — the factory simply return { inject, apply } (the return value IS the module
export). No matter how it is modified or re-wrapped, module is not defined cannot
recur. A detailed warning comment is included at the top of the file.
⚠️ Do NOT remove
'timer'frominject: Cordis'sctxis a Proxy; accessing a service property that was not injected throwscannot get property "timer" without injectdirectly, causing the apply phase to fail. The client-sidetimerservice is provided by the core plugin@deepseek-ai/dsh-cordis-client-runner(ctx.interval/ctx.timeoutare mixed into the Context prototype and cleaned up automatically with the fiber); both polling and auto-dismissing toasts depend on it. Similarly, the Host half keepsinject: ['timer'](the Host-side timer is guaranteed by@deepseek-ai/cordis-plugin-timer, which is auto-loaded at profile startup).
2. Git Hook: No Committing a Broken Bundle (Install Once)
pwsh -NoProfile -ExecutionPolicy Bypass -File tools/install-hooks.ps1
After installation, every git commit automatically runs tools/check-client-bundle.ps1:
node --checkvalidates the syntax ofplugin/client.js/plugin/host.js;- blocks any non-comment reference to
module/exports(i.e., the pattern that would lock out DSH).
A failed check rejects the commit outright, preventing regressions at the source. You can also run it manually:
pwsh -NoProfile -ExecutionPolicy Bypass -File tools/check-client-bundle.ps1.
3. Emergency Switch: If It Breaks Again, Get Into DSH With One Click
If the plugin breaks startup again, no config change needed — just disable it:
# 禁用撤回插件(自动备份 cordis.patch.yml),然后重启 DSH
pwsh -NoProfile -ExecutionPolicy Bypass -File tools/recall-killswitch.ps1
# 恢复撤回插件,然后重启 DSH
pwsh -NoProfile -ExecutionPolicy Bypass -File tools/recall-killswitch.ps1 -Action enable
How it works: add/remove
disabled: true on the recall-unread entry in ~/.dsh/profiles/web/cordis.patch.yml
(the loader skips disabled entries and does not import that plugin).
After modifying
plugin/client.js, remember to redeploy it to~/.dsh/profiles/web/node_modules/dsh-recall-unread/and restart DSH.
📦 Changelog
- v1.1.0 (2026-08) Removed the Amadeus launcher dependency: deleted the 🧩 menu entry and the client-side Amadeus polling; the plugin is now active as soon as it is enabled (the enable/disable toggle in the plugin marketplace/management page controls loading).
- v1.0.1 (2026-08) Anti-lockout hardening: the bundle completely removed
module/exportsreferences (the factory now returns the export directly); added a?.loadregistration guard; keptinjectas['slots','timer']with a Proxy access warning; added a git hook validation and a one-click emergency switch script. - v1.0.0 (2026-08) First runnable version: Host RPC recall + Client unread message strip.
📄 License
Compatibility
Versions
| Latest version | Published | Size |
|---|---|---|
| 1.1.0 | — | — |
| 1.2.0 | — | — |
Comments
Loading…
Similar plugins
by kyle123740
每条消息可撤回 / 删除 / 删除此处及之后 —— DSH 插件(append-only 墓碑,模型上下文与界面同步)。Per-message recall & delete for DeepSeek Harness. Requires dsh >= 0.1.7.
★ 3
↓ 37/wk
MIT
JavaScript
Oct 23, 2026
dsh plugin --profile web add dsh-message-recallby viplocco
DeepSeek Harness 消息级删除插件,用于避免用户误发或错误的助手消息污染会话上下文。
★ 3
↓ 58/wk
MIT
JavaScript
Sep 9, 2026
dsh plugin --profile web add dsh-delete-messageby mbj733
DeepSeek Harness (DSH) plugin: edit sent messages and resend (stop an in-flight reply, edit, resend), plus branch-based edit / reroll / retry and a version timeline.
★ 4
↓ 35/wk
MIT
TypeScript
Sep 9, 2026
dsh plugin --profile web add dsh-edit-resendby EvannZhongg
A DeepSeek Harness plugin that keeps the exact text of user-authored messages that compaction has shadowed, and re-injects them as a budgeted runtime-context snapshot.
★ 0
MIT
TypeScript
Oct 18, 2026
dsh plugin --profile web add dsh-lossless-anchorsby tianyaojiudi-prog
DSH(DeepSeek Harness) 插件:把一段可在设置页随时改写的文字,作为全局系统提示词段注入到所有会话,含子代理与工作流内部子代理;文本与开关即时生效,无需重启。
★ 9
MIT
JavaScript
Oct 4, 2026
dsh plugin --profile web add dsh-djy-xttscby XSJUSTC
DSH (DeepSeek Harness) conversation rewind plugin - rewind to any user message, interrupt a running turn, hide the tail from chat view and model context, cancel freely before sending.
★ 11
MIT
JavaScript
Sep 22, 2026
dsh plugin --profile web add @xsj/dsh-rewind