
Security News
PHP Foundation Says Chainguard Broke Security Embargo Ahead of Emergency Patches
The PHP Foundation says Chainguard broke a security embargo ahead of critical RCE patches, as leaks disrupted the coordinated release.
dsh-message-editor
Advanced tools
Recall (撤回), edit-and-resend (编辑重发) and regenerate (重新生成) for DeepSeek Harness conversation messages — Web and Desktop
Recall · Edit-and-resend · Regenerate for DeepSeek Harness conversations — works on the Web GUI and the Desktop app (both share the same Web frontend).
English · 简体中文
DeepSeek Harness stores every conversation as an append-only event log, so there is
no built-in "undo". dsh-message-editor brings back the three moves every chat deserves —
撤回 (recall), 编辑重发 (edit-and-resend) and 重新生成 (regenerate).
Recall / edit remove the target messages from the conversation view and the model
context — that is exactly the effect you see. What stays untouched is the underlying
durable transcript: it remains append-only, old events are never rewritten or deleted,
and the plugin merely appends one valid replacement event (the same replace primitive
the built-in compaction uses) to rewind the surface — so the log keeps a full audit trail
of every rewind.
| Action | Where | What happens |
|---|---|---|
| ↩ 撤回 (recall) | hover any assistant reply, or the row under any user message | Removes the whole exchange round (the input and the agent's output, tool rows included) from both the model context and the conversation view; the input text is echoed into the composer so you can re-ask or re-edit immediately. A small transient notice marks the rewind and disappears once you keep typing. |
| ✎ 编辑重发 (edit & re-send) | row under any user message | The old message and its reply are rewound and hidden. By default the conversation starts fresh (earlier messages are hidden too and excluded from context); the edited text is sent and the agent answers. A collapsed "original input" reference sits right under the new message — click to expand, configurable off. |
| ↻ 重新生成 (regenerate) | hover any assistant reply | The reply (and everything after it) is rewound and hidden, then the original prompt is re-sent so the agent answers again. |
Why it's different
replace primitive the built-in compaction uses), so the log keeps a full audit trail.Requires DeepSeek Harness with the
dshCLI. Installs the plugin as a profile bundle and automatically rebuilds the Web client:
# DSH Desktop (desktop profile)
dsh plugin --profile desktop add dsh-message-editor
# standalone Web (`dsh web` / web profile)
dsh plugin --profile web add dsh-message-editor
⚠️ Restart after install. A running app keeps the previously loaded bundle in memory, so quit and reopen DSH Desktop (or restart the
dshprocess for a standalone Web deployment) before the plugin activates.
That's it — after the restart, hover any assistant reply, or any user message, and use ↩ / ✎ / ↻.
The package declares a dsh.bundle manifest, so it installs through the official
plugin path into any profile:
dsh plugin --profile <name> add dsh-message-editor
⚠️ Restart required. The install writes the new files and re-renders the profile composition, but a running app does not hot-reload bundles — quit and reopen DSH Desktop (or restart the
dshprocess for a standalone Web deployment) to load the plugin. To uninstall:dsh plugin --profile <name> remove dsh-message-editor(then restart again).
It also shows up in dsh-market for one-click install from inside Settings (same restart applies).
dsh CLI)The same result with plain file edits and pnpm — exactly the steps
dsh plugin add performs for you:
Open the profile manifest (defaults: ~/.dsh/profiles/desktop on DSH
Desktop, ~/.dsh/profiles/web for standalone Web) and add both the
dependency and the bundle-layer entry:
{
"dependencies": {
"dsh-message-editor": "^0.2.0"
},
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"dsh-message-editor"
]
}
}
}
(Keep whatever entries your profile already has; only add the two
dsh-message-editor lines.)
Install inside the profile directory:
cd ~/.dsh/profiles/<name> && pnpm install
Restart DSH Desktop / the dsh process (see above).
For local development, point the dependency at a checkout instead of the
registry: "dsh-message-editor": "file:/path/to/dsh-message-editor" — or let
dsh do it: dsh plugin --profile <name> add /path/to/dsh-message-editor.
npm i dsh-message-editor
Add the package to the harness composition (cordis.yml of the app/deployment you use):
- name: 'dsh-message-editor'
The client half is picked up automatically from the package's dsh.client metadata and
bundled into the Web client (a client-module rebuild happens automatically when the
composition changes). The Host half registers the same-origin HTTP route
/api/plugins/message-editor/* for the browser UI.
Use the dynamic entries shipped in the package. In the session where you want the feature:
lib/dynamic-host.js
(Host half) and lib/dynamic-client.js (Client half).The dynamic host registers the same operations behind the package-private
harness.handle RPC (messageEditor.recall / messageEditor.editAndResend /
messageEditor.regenerate).
| Setting | Default | Description |
|---|---|---|
| 编辑后显示原提问对照 | on | A collapsed "original input" reference under the re-sent message showing the most recent replaced text (reference only — never sent to the model). |
| 编辑后从新对话开始 | on | After editing, hide earlier messages too so the conversation looks like a fresh start (the whole surface is rewound before re-sending). |
durable transcript (append-only) model context & view
┌────────────────────────────────┐ ┌──────────────────┐
│ ... target message │ │ … target message │
│ ↓ shadow span │ │ ↓ rewind │
│ [target … last surface node] │ ─────▶ │ (empty replace │
│ ↳ one replacement │ │ = context cut) │
│ assistant/message (empty)│ └──────────────────┘
│ ↳ optional original-input │ agent.followup(new prompt)
└────────────────────────────────┘ → next turn rebuilds request
lib/host-core.js, zero runtime imports) locates the target
message in the session's live surface, computes the shadow span
[message … last surface node], and appends one replacement
assistant/message with an empty body — a valid surface node that
derives to no model message, so the LLM context simply rewinds.agent.followup(...) with the
(new) prompt text; the agent's next turn builds its request from the
rewound session.deriveMessages().lib/client.js) registers:
user-actions conversation node under every user message
(编辑 / 撤回 row with an inline editor); recall echoes the text into the
composer,recall-marker node renderer: a notice row that injects CSS hiding
every shadowed message row from the flow (view and model context stay in
sync), plus the optional original-input comparison block,message-editor entry in the conversation.chat.assistant-actions
strip (撤回 / 重新生成),Two different layers are at play: the durable transcript (append-only; old events are never rewritten or deleted) and the model-visible surface (rewound by an appended replacement event). So the old events stay in the log as an audit trail — but they are synchronized out of both the model context and the visible conversation, and the view always reflects what the agent actually sees. Persistence, projections and the transcript remain consistent because the plugin only appends valid, typed session events.
agent-busy
otherwise.target-shadowed).# structure
lib/host-core.js # transport-neutral host logic (no imports)
lib/index.js # published Host: harness RPC + HTTP route
lib/client.js # client SOURCE (React via import, fetch transport)
lib/client.bundle.js # BUILT client bundle — the self-registering loader entry
# (`window.__ModuleLoader__.load`) served by client-modules
lib/dynamic-host.js # dynamic Host half (self-contained)
lib/dynamic-client.js # dynamic Client half (self-contained)
cordis.patch.yml # dsh.bundle profile patch layer
pnpm build # rebuild lib/client.bundle.js from lib/client.js (run before publishing)
npm pack --dry-run # verify the published file list
node --check lib/*.js # syntax check
PRs and issues are welcome — see CONTRIBUTING (coming soon) and the issue tracker.
Listed on the dsh-plugin topic and installable from dsh-market. For a curated overview of the DeepSeek Harness plugin ecosystem, see awesome-dsh-plugin.
Built by the OfferKuai team — an AI job application assistant on a mission that "users need results, not repeated conversations". Founder: Zhaofeng (Yaming). This plugin is released as open source for the DeepSeek Harness community.
MIT
FAQs
Recall (撤回), edit-and-resend (编辑重发) and regenerate (重新生成) for DeepSeek Harness conversation messages — Web and Desktop
The npm package dsh-message-editor receives a total of 4 weekly downloads. As such, dsh-message-editor popularity was classified as not popular.
We found that dsh-message-editor demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
The PHP Foundation says Chainguard broke a security embargo ahead of critical RCE patches, as leaks disrupted the coordinated release.

Security News
PHP releases emergency patches for two critical RCE vulnerabilities as agencies urge organizations to take unpatched sites offline.

Threat Research
/Security News
A supply chain attack compromised LiteLLM 1.107.0 on PyPI and Docker, spreading malware that steals cloud credentials, developer secrets, and AI provider keys.