WASD258-jpg
dsh-context-surgery
Surgical rewriting of the model-visible conversation history of a DSH session (surface replacement). Edit reply vs reasoning chain separately.
- Stars
- 0
- Language
- JavaScript
- Created
- Aug 15, 2026
- Updated
- Aug 15, 2026
Introduction
dsh-context-surgery
中文 | English
Surgical rewriting of the model-visible conversation history of a DSH session (surface replacement): fix an out-of-character reply, retcon a scene, or correct wording — the model's next request reads the edited history as if it had always been that way. Reply text and the reasoning chain are edited separately.
This is a community project. It is not an official DeepSeek plugin and does not imply any endorsement by DeepSeek.
Features
- Per-message surgery buttons — every AI message's action row (slot
conversation.chat.assistant-actions, order 20): ✏️ edit reply, 💡 edit thinking (shown only when the message has a reasoning block), 🗑️ delete. - Separate reply / thinking editing —
part=replyrewrites only text blocks (keeps reasoning and tool calls);part=thinkingonly reasoning blocks (empty text removes them all);part=allrewrites the whole message as plain text. /contextcommand family — list / edit / think / clear-think / rewrite / delete / replace from the chat input.- Model-callable tools —
context_list,context_edit,context_delete,context_replace(for self-correction loops). - HTTP API —
GET /list,GET /message,POST /edit|/delete|/replaceunder/api/dsh-context-surgery. - Immediate effect — the next model request reads the edited history; the GUI conversation view refolds.
- Append-only log — the original text is never erased; every rewrite is a new log event shadowing old nodes.
- Top-level sessions only — subagents are rejected (
requireRootAgent).
Install
Requires the DSH web profile (@deepseek-ai/dsh-web-app) and Node >= 22.19.0.
-
Clone or download this repository:
git clone https://github.com/WASD258-jpg/dsh-context-surgery.git -
Install into the DSH profile dependency directory (choose one):
Option A: npm install (recommended)
cd "$env:USERPROFILE\.dsh\profiles" npm install @wasd258/dsh-context-surgery --no-saveOption B: copy from source
$src = '.\dsh-context-surgery' $dst = "$env:USERPROFILE\.dsh\profiles\node_modules\@wasd258\dsh-context-surgery" New-Item -ItemType Directory -Force -Path "$dst\lib" | Out-Null Copy-Item "$src\package.json" $dst -Force Copy-Item "$src\lib\index.js","$src\lib\client.js" "$dst\lib" -Force Copy-Item "$src\LICENSE","$src\NOTICE" $dst -ForceOn Linux/macOS use
~/.dsh(or$DSH_HOMEif set) instead of$env:USERPROFILE\.dsh. -
Register the package as a bundle: append the name to
dsh.profile.bundlesin the profile'spackage.json:{ "name": "dsh-profile-web", "private": true, "dependencies": {}, "dsh": { "profile": { "bundles": [ "@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "@wasd258/dsh-context-surgery" ] } } } -
Restart DSH web once (or hot-assemble the bundle at runtime), then refresh the browser page so the client module (
lib/client.js) loads.
Usage
UI: per-message actions
- Open a running top-level session; hover an AI message's action row: ✏️ / 💡 / 🗑️.
- ✏️ opens a textarea prefilled with the reply; edit and click 保存 (Save) → "回复已改写 ✓" (reasoning and tool calls kept).
- 💡 edits the reasoning chain; leave empty and save to remove it → "思考链已改写 ✓".
- 🗑️ asks for confirmation, then deletes → "已删除 ✓".
/context commands
/context (or list / ls / ?) prints the current model-visible history — the last 10 rows with seq:
模型可见上下文共 10 条(显示最后 10 条):
[1] YOU 你好,来扮演图书馆管理员吧。
[2] AI 好的,欢迎光临。今天想找什么书?
[3] YOU 讲讲那本被禁的书。
[4] AI [reasoning] 主角不该知道这本书……那本书只是传说。
[5] TOOL [tool-call toolu_01ABC…]
[6] TOOL [tool-result] {"found": false}
[7] AI 抱歉,馆里没有这本书的记载。
[8] YOU 你刚才不是这么说的。
[9] AI 我记错了,请您原谅。
[10] YOU 继续吧。
Row format
[seq] AI|TOOL|YOU <preview>(120-char truncation); the plugin's status strings are in Chinese.
| Command | Effect |
|---|---|
/context edit <seq> <text> | Rewrite only the reply (keeps reasoning) → 已改写 [7] 的回复正文(思考链保留):下一轮模型请求将看到新文本。 |
/context think <seq> <text> | Rewrite only the thinking chain → 已改写 [7] 的思考链(回复正文保留)。 |
/context clear-think <seq> | Remove the thinking chain → 已移除 [7] 的思考链。 |
/context rewrite <seq> <user|assistant> <text> | Rewrite the whole message (part=all) → 已整条重写 [7] 为 user 消息:下一轮模型请求将看到新文本。 |
/context delete <seq> | Delete the node (empty assistant shadow) → 已删除 [7](assistant/message):从模型视角消失。 |
/context replace <start> <end> <user|assistant> <text> | Replace a contiguous range → 已整段替换 [3,4] 为一条 assistant 消息。 |
Errors: 操作失败:<原因> or 未知子命令:<op>; seq must be an integer.
part semantics
| part | text blocks | reasoning blocks | other blocks (tool-call …) | applies to |
|---|---|---|---|---|
reply (default) | replaced | kept | kept | user, assistant |
thinking | kept | replaced; empty text removes them all | kept | assistant only (user messages have no reasoning chain) |
all | one text block | dropped | dropped | user, assistant |
Tools
| Tool | Parameters | Notes |
|---|---|---|
context_list | limit? | surface rows in order (or last N): seq, role, text |
context_edit | seq, text, role?, part? | part defaults to reply |
context_delete | seq | shadowed by an empty assistant node |
context_replace | start, end, role, text | inclusive; both must be current surface nodes |
context_edit { "seq": 7, "part": "reply", "text": "…" } →
{ "ok": true, "replacementSeq": 11, "shadowedSeq": 7, "role": "assistant", "part": "reply", "text": "…" };
failures return { "ok": false, "error": "…" }.
HTTP API
All routes are under /api/dsh-context-surgery:
| Method | Path | Description |
|---|---|---|
| GET | /list | surface rows; ?sessionId=&limit= (limit = last N; omit/0 = all) |
| GET | /message | one message split into reply/reasoning; ?sessionId=&messageId= |
| POST | /edit | rewrite one node; body { sessionId, seq|messageId, part?, role?, text } |
| POST | /delete | delete one node; body { sessionId, seq|messageId } |
| POST | /replace | replace a range; body { sessionId, start, end, role, text } |
GET /list?sessionId=abc&limit=3 → { "ok": true, "sessionId": "abc", "total": 12, "rows": [{ "seq": 10, "type": "user/message", "role": "user", "text": "…" }, { "seq": 11, "type": "assistant/message", "role": "assistant", "text": "…", "messageId": "msg_…" }] }
GET /message?sessionId=abc&messageId=msg_… → { "ok": true, "sessionId": "abc", "seq": 11, "messageId": "msg_…", "type": "assistant/message", "role": "assistant", "reply": "reply text", "reasoning": "thinking chain", "hasReasoning": true }
POST /edit with { "sessionId": "abc", "seq": 11, "part": "thinking", "text": "…" } →
{ "ok": true, "replacementSeq": 15, "shadowedSeq": 11, "role": "assistant", "part": "thinking", "text": "…" }
Errors:
| Status | Condition |
|---|---|
| 403 | non-loopback source (context-surgery API is loopback-only) |
| 403 | Host header not loopback/current port (invalid host header) |
| 403 | POST from a cross-origin page (cross-origin request rejected) |
| 404 | session not running / messageId not in surface / unknown route |
| 415 | POST without Content-Type: application/json |
| 400 | invalid JSON body, or body larger than 256 KiB |
200 + ok:false | operation-level failure (seq shadowed, tool/result node, thinking on a user message, …) |
| 500 | unexpected server error |
How it works
lib/index.js(host) registers the/contextcommand, the fourcontext_*tools and the HTTP routes;lib/client.js(web) injects the per-message buttons via theconversation.chat.assistant-actionsslot.- The model's message list is derived from the session log's surface (
Session.deriveMessages()→ request assembly). The log is append-only, but the surface supports positional replacement: appending an event withsurfaceOp: { op: "replace", start, end }+sourceEventSeqs(covering every shadowed node) replaces that surface range — exactly the seam compaction summaries use. - After a rewrite the next request reads the edited history, the GUI refolds, and the original text stays in the log.
- Only the running top-level session may be edited; subagents are rejected. Shadowing ranges that contain tool
calls can leave later tool references inconsistent — prefer single-message edits;
tool/resultnodes cannot be rewritten.
Privacy & security
- The HTTP API is loopback-only and unauthenticated by design: any local process can read session content (including reasoning chains) and drive rewrites. Run DSH in a trusted local environment only.
- Host-header + same-origin checks block DNS-rebinding and cross-site requests; writes need
application/json. - The
context_*tools give the model power to rewrite its own history — enable only where you trust the model and its inputs not to be prompt-injected (a compromised model could be steered to hide or alter history). - No telemetry, no external network requests, no credential access.
Compatibility
- Requires a DSH web profile; verified against DeepSeek Harness commit
47f9438. - Node >= 22.19.0; version 0.1.1; MIT.
- Rewrites ride the session surface-replacement seam (same mechanism as compaction); the log itself is never modified.
Official ecosystem requirements
DeepSeek suggests community authors publish plugins in their own GitHub repositories and add the
dsh-plugin topic for discoverability; the official repository does not
accept external PRs. See CONTRIBUTING.zh.md.
License
MIT — see LICENSE. Portions reference DeepSeek Harness; provenance and the non-affiliation statement are in NOTICE.