npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

nyx-message-edit

v0.1.3

Published

True edit-message for DeepSeek Harness: replace a user message in place, truncate everything after it, and regenerate from that exact point.

Readme

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:

| Step | Mechanism | | --- | --- | | Truncate + replace | one 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 | | Regenerate | agent.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 build | untouched — 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, attachments | untouched | | Persistence | untouched — the replacement is a normal durable event; the log stays append-only and replayable | | UI | the 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)

| Route | Method | Purpose | | --- | --- | --- | | /api/nyx-message-edit.state?sessionId= | GET | user messages currently on the surface + the transcript rows earlier edits removed | | /api/nyx-message-edit.edit | POST | { sessionId, messageId, text } → truncate + replace + regenerate | | /api/nyx-message-edit.health | GET | build, pending edits, whether the Agent service is reachable | | /api/nyx-message-edit.diag | POST | client 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:

| Case | Result | | --- | --- | | Edit the first message of a 3-turn chat | surface + request = only the edited message | | A → B → C, edit B | request = A, respA, X; C gone | | Edit the last message | prefix kept, last turn replaced | | Edit the 4th of 6 turns | request = m1…m3 + edit; m4…m6 gone | | Edit an already-edited message (nested) | previous edit's answer also gone | | Cancel | conversation byte-identical | | Edit while a response streams | running turn cancelled, edit applied, regenerated | | System prompt / model selector / streaming / markdown | unchanged in every request | | Message with an image attachment | non-text blocks preserved, text replaced | | Reload (page and harness restart) | truncated turns stay gone; state rebuilt from the log | | Identical text | no-op, no request | | Empty text | refused | | Unknown / stale message id | refused |


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