Back to home

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 editingpart=reply rewrites only text blocks (keeps reasoning and tool calls); part=thinking only reasoning blocks (empty text removes them all); part=all rewrites the whole message as plain text.
  • /context command family — list / edit / think / clear-think / rewrite / delete / replace from the chat input.
  • Model-callable toolscontext_list, context_edit, context_delete, context_replace (for self-correction loops).
  • HTTP APIGET /list, GET /message, POST /edit|/delete|/replace under /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.

  1. Clone or download this repository:

    git clone https://github.com/WASD258-jpg/dsh-context-surgery.git
    
  2. 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-save
    

    Option 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 -Force
    

    On Linux/macOS use ~/.dsh (or $DSH_HOME if set) instead of $env:USERPROFILE\.dsh.

  3. Register the package as a bundle: append the name to dsh.profile.bundles in the profile's package.json:

    {
      "name": "dsh-profile-web",
      "private": true,
      "dependencies": {},
      "dsh": {
        "profile": {
          "bundles": [
            "@deepseek-ai/dsh-base",
            "@deepseek-ai/dsh-web-app",
            "@wasd258/dsh-context-surgery"
          ]
        }
      }
    }
    
  4. 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

  1. Open a running top-level session; hover an AI message's action row: ✏️ / 💡 / 🗑️.
  2. ✏️ opens a textarea prefilled with the reply; edit and click 保存 (Save) → "回复已改写 ✓" (reasoning and tool calls kept).
  3. 💡 edits the reasoning chain; leave empty and save to remove it → "思考链已改写 ✓".
  4. 🗑️ 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.

CommandEffect
/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

parttext blocksreasoning blocksother blocks (tool-call …)applies to
reply (default)replacedkeptkeptuser, assistant
thinkingkeptreplaced; empty text removes them allkeptassistant only (user messages have no reasoning chain)
allone text blockdroppeddroppeduser, assistant

Tools

ToolParametersNotes
context_listlimit?surface rows in order (or last N): seq, role, text
context_editseq, text, role?, part?part defaults to reply
context_deleteseqshadowed by an empty assistant node
context_replacestart, end, role, textinclusive; 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:

MethodPathDescription
GET/listsurface rows; ?sessionId=&limit= (limit = last N; omit/0 = all)
GET/messageone message split into reply/reasoning; ?sessionId=&messageId=
POST/editrewrite one node; body { sessionId, seq|messageId, part?, role?, text }
POST/deletedelete one node; body { sessionId, seq|messageId }
POST/replacereplace 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:

StatusCondition
403non-loopback source (context-surgery API is loopback-only)
403Host header not loopback/current port (invalid host header)
403POST from a cross-origin page (cross-origin request rejected)
404session not running / messageId not in surface / unknown route
415POST without Content-Type: application/json
400invalid JSON body, or body larger than 256 KiB
200 + ok:falseoperation-level failure (seq shadowed, tool/result node, thinking on a user message, …)
500unexpected server error

How it works

  • lib/index.js (host) registers the /context command, the four context_* tools and the HTTP routes; lib/client.js (web) injects the per-message buttons via the conversation.chat.assistant-actions slot.
  • 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 with surfaceOp: { 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/result nodes 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.