thanhdz235123-commits/nyx-dsh-plugins--packages-nyx-message-edit ↗★ 0

nyx-message-edit

原位修改用户消息并清空后续内容后重算新回复 适合希望像重写消息点位继续推理的用户;编辑会截断后文并重新生成,不保留被截断内容。

套件
nyx-message-edit
相容性
待驗證
Cordis 依賴範圍
^4.0.1
版本
0.1.3
授權
MIT
最近更新
2026年9月13日

安裝

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:thanhdz235123-commits/nyx-dsh-plugins#412cd8578b269ba268e2c6abf052c75e59ea9f00&path:packages/nyx-message-edit

nyx-message-edit

Real edit message for DeepSeek Harness.

Editing a user message replaces it in place, cuts everything after it, and regenerates the answer from that exact point — the way ChatGPT and Claude do it, and the way harness conversations are supposed to work.

Before                              After editing "hello" → "1"
──────────────────────────────      ──────────────────────────────
User: hello                         User: 1
AI:   hey, what's up?               AI:   [fresh answer to "1"]

User: Python là gì?
AI:   Python là một ngôn ngữ…

User: giải thích thêm
AI:   …

Everything from the edited message onward — including the old answer to that message — leaves the active conversation and never enters the next request. Nothing is appended at the end.


Install

npx --yes github:thanhdz235123-commits/nyx-dsh-plugins install

Then restart DSH Desktop once (the host half registers an Agent hook and its routes at harness start). Later client-only updates just need a window reload.

Other shapes:

node bin/nyx-message-edit.mjs install --home "/path/to/harness" --profile web
node bin/nyx-message-edit.mjs install --dep        # package.json dependency + bundle
node bin/nyx-message-edit.mjs status
node bin/nyx-message-edit.mjs uninstall
node bin/nyx-message-edit.mjs doctor

Works on macOS / Windows / Linux: the harness home is resolved the same way DSH resolves it, and no harness package is imported by the host half.


Use

  1. Hover any user message in the Chat transcript → a pencil appears at its top-right.
  2. Click it. The message turns into an inline editor holding its current text.
  3. Hủy bỏ / Esc cancels — the conversation is untouched.
  4. Gửi / ⌘/Ctrl+Enter applies the edit: the message is replaced where it stood, everything after it disappears, and the model answers the edited message again in the same session.

Editing while a response is still streaming is allowed: the running turn is cancelled with the harness's own stop semantics, then the edit lands.


How it works (harness-native, no parallel conversation store)

DeepSeek Harness keeps an append-only event log and derives the model history from its surface — an ordered view of the log's message-producing events, where each event declares how it joins the surface (surfaceOp).

Edit uses exactly that mechanism:

StepMechanism
Truncate + replaceone session.append('user/message', edited, { surfaceOp: { op: 'replace', start, end }, sourceEventSeqs }) where start is the edited message's surface node and end is the current surface tail
Regenerateagent.wakeDriver() opens a normal turn; the plugin's agent/pre-step hook returns the step with no appended user message, so the request is built from the rewritten surface
Request builduntouched — the loop calls session.deriveMessages() and hands it to the provider exactly as for any other turn
Streaming, model selector, system prompt, tools, thinking, markdown, attachmentsuntouched
Persistenceuntouched — the replacement is a normal durable event; the log stays append-only and replayable
UIthe replacement event renders through the harness's own user Chat renderer, so the edited bubble looks native

Because the surface fold is what deriveMessages() reads, the next request contains only the messages before the edit point plus the edited message. Verified against a recording provider stub: editing the first message of hello / Python là gì? / giải thích thêm produced a request of exactly [system, 1].

The transcript rows the edit removed are addressed by their own data-chat-flow-key / data-chat-turn (both derived from the durable log, not from client state), so a reload never resurrects them.

Why a plugin and not a patch

Nothing in the app bundle is modified. The host half imports zero harness packages; it uses the documented plugin surfaces only:

  • connection.fetch.register for /api/nyx-message-edit.*
  • ctx.on('agent/pre-step', …) — the Agent step waterfall the harness itself uses for prompt assembly
  • session.surface.nodes, session.eventAt, session.append, session.ownEvents
  • agent.wakeDriver(), agent.cancel(), agent.whenIdle()

API (host routes, same-origin, browser-session fenced)

RouteMethodPurpose
/api/nyx-message-edit.state?sessionId=GETuser messages currently on the surface + the transcript rows earlier edits removed
/api/nyx-message-edit.editPOST{ sessionId, messageId, text } → truncate + replace + regenerate
/api/nyx-message-edit.healthGETbuild, pending edits, whether the Agent service is reachable
/api/nyx-message-edit.diagPOSTclient diagnostic sink

Error codes: not-found (message no longer on the surface), bad-request (empty text), busy (agent could not be quiesced), timeout, unsupported.


Verified

Run on a real harness instance (same app build as the desktop app) with a recording provider stub for exact-request evidence, and with the real DeepSeek provider for the end-to-end experience:

CaseResult
Edit the first message of a 3-turn chatsurface + request = only the edited message
A → B → C, edit Brequest = A, respA, X; C gone
Edit the last messageprefix kept, last turn replaced
Edit the 4th of 6 turnsrequest = m1…m3 + edit; m4…m6 gone
Edit an already-edited message (nested)previous edit's answer also gone
Cancelconversation byte-identical
Edit while a response streamsrunning turn cancelled, edit applied, regenerated
System prompt / model selector / streaming / markdownunchanged in every request
Message with an image attachmentnon-text blocks preserved, text replaced
Reload (page and harness restart)truncated turns stay gone; state rebuilt from the log
Identical textno-op, no request
Empty textrefused
Unknown / stale message idrefused

Privacy

Local software: no network calls beyond its own harness host, no telemetry, and no trace of your conversation on disk unless you ask for one. Diagnostics require NYX_MESSAGE_EDIT_DIAG=1 and record lengths and ids — never the text of a message.

Layout

index.js              host half: edit engine, Agent step hook, routes
client.js             client half: pencil, inline editor, edited-message node, transcript hiding
cordis.patch.yml      bundle patch (insert row)
bin/                  installer CLI
tools/mock-llm.mjs    recording OpenAI-completions stub used by the verification runs

Package layout (DeepSeek Harness plugin format)

This package is laid out the way DeepSeek Harness's own plugins are, so it installs, resolves and publishes the same way:

lib/index.js              host half   — export const name / inject, export function apply(ctx)
lib/client.js             client half — window.__ModuleLoader__.load({ id, factory })
lib/types/**/*.d.ts       type declarations for both halves
cordis.patch.yml          the profile patch that inserts the plugin
bin/                      the installer CLI
tools/, docs/             development tools and documentation (not published)

package.json carries the contract the loader reads:

{
  "main": "lib/index.js",
  "exports": { ".": {...}, "./client": { "default": "./lib/client.js" } },
  "dsh": {
    "bundle": { "patch": "./cordis.patch.yml" },
    "client": { "inject": [], "immediately": true, "platform": "web" }
  }
}

Two different lists, easy to confuse: dsh.client.inject names packages whose client bundles must load first, while the client module's own inject names the cordis services it needs at apply time.

Publishing

npm pack --dry-run     # check what ships
npm publish --access public

Published, it installs into a profile either by adding its name to dsh.profile.bundles in the profile's package.json, or with the bundled installer:

npx nyx-message-edit install

License

MIT