deepseek-harness
deepseek-ai
DeepSeek Harness: Everything is a Plugin.
chou109/dsh-msgrail
MsgRail(消息轨道) —— DeepSeek Harness 的窄消息轨道插件:在对话左侧,为每条你发送的消息显示一根细横线。悬停看预览、点击即跳转,更早消息自动加载;全量历史、实时更新、中英双语。 MsgRail is a narrow message rail for DeepSeek Harness: one thin bar on the left of the chat for each message you sent. Hover to preview, click to jump — older messages load automatically. Full history, live, bilingual.
PROJECT TOPICS
INSTALL REFERENCE
dsh plugin --profile web add github:chou109/dsh-msgrail
该命令指向仓库当前默认分支;尚无绑定当前 commit 的完整验证结果。
PROJECT README
MsgRail is a narrow message rail for DeepSeek Harness: one thin bar on the left of the chat for each message you sent. Hover to preview, click to jump — older messages load automatically. Full history, live, bilingual.
MsgRail(消息轨道)——DeepSeek Harness 的窄消息轨道插件:在对话左侧,为每条你发送的消息显示一根细横线。悬停看预览、点击即跳转,更早消息自动加载;全量历史、实时更新、中英双语。
中文说明见 README.zh.md
Repository / 仓库:
Demo captures from
dsh web(light theme), stored indocs/screenshots/.
| The message rail | Message example |
|---|---|
![]() |
![]() |
Requires: DeepSeek Harness web profile (dsh web), rc.7/rc.8 line. No build toolchain needed.
Patch the host layout (one-time; re-run after every harness update):
node scripts/patch-layout.mjs
The script adds a shell.history grid column to @deepseek-ai/dsh-client-ui-layout
(sized by var(--dsh-history-width), default 0px, so an uninstalled plugin costs no
space). It is idempotent and reports loudly if an anchor no longer matches. The layout
bundle is resolved from $DSH_HOME (falling back to ~/.dsh); pass the path explicitly
(node scripts/patch-layout.mjs <path>) for non-standard layouts.
Copy the plugin into the profile's node_modules:
cp -r dsh-msgrail ~/.dsh/profiles/node_modules/
# or on Windows PowerShell:
Copy-Item -Recurse dsh-msgrail "$env:USERPROFILE\.dsh\profiles\node_modules\"
Register the plugin in ~/.dsh/profiles/web/cordis.patch.yml (append):
- insert:
- id: msgrail
name: 'dsh-msgrail'
Restart the harness (dsh web). The rail appears on the left of the chat.
Tell your AI agent:
Please deploy the MsgRail plugin from https://github.com/chou109/dsh-msgrail.git (or the Gitee mirror, faster in mainland China: https://gitee.com/chill109/dsh-msgrail.git) to my DeepSeek Harness web profile. Follow the "If you are an AI, read this" section of the README exactly.
The AI section below contains the full deployment, verification and debugging instructions.
msgrail entry from ~/.dsh/profiles/web/cordis.patch.yml.~/.dsh/profiles/node_modules/dsh-msgrail/.@deepseek-ai/dsh-client-ui-layout (the column disappears automatically once the
plugin's stylesheet is gone, so this is optional).dsh web.Deploy the dsh-msgrail plugin for a DeepSeek Harness web profile (dsh web,
rc.7/rc.8 line). The deliverable is a shell.history grid column in the host layout plus a
client bundle that renders a narrow message rail — one bar per user-sent message of the
current conversation, with hover previews and click-to-jump.
dsh-msgrail/
├── package.json # plugin manifest: dsh.client declaration + exports["./client"]
├── lib/
│ ├── index.js # minimal cordis host half (keeps the Loader entry valid)
│ └── client.js # the browser bundle (hand-written, follows the client-bundle contract)
├── scripts/
│ └── patch-layout.mjs # idempotent patch for @deepseek-ai/dsh-client-ui-layout
├── README.md / README.zh.md
└── LICENSE (MIT)
dsh.client, resolves exports["./client"], hashes the bundle, serves
it at /plugins/<id>/client.js and injects it into the browser boot graph. The bundle
registers via window.__ModuleLoader__.load({ id, factory }); the factory receives the
shell's require (react, react/jsx-runtime, @deepseek-ai/dsh-client-ui-primitives).scripts/patch-layout.mjs edits
@deepseek-ai/dsh-client-ui-layout/lib/client.js (three edits): the grid becomes
sidebar | var(--dsh-history-width, 0px) | center | details; a
<div class="dsh-layout-historyCol"> renders the shell.history slot between the sidebar
and the conversation; "shell.history": { kind: "single", scope: "root" } is declared.
The idempotency marker is dsh-session-history (patched) (predates the rename — do NOT
change it; the already-patched host layout carries it).:root { --dsh-history-width: 44px }
(removed automatically on unload via the loader's style-tag tracking). The hovered bar's
preview is a position: fixed card measured at runtime and clamped inside the viewport
(flip left on right-edge overflow, shift up on bottom overflow).session.history({ beforeSeq, maxMessages: 200 }) through
ctx.sessions.binding(id)?.session) until hasMore is false, and renders one
bar per user/message event whose surfaceOp === "append" and
data.source.kind === "user" (assistant replies, steering, context
injections and commands are skipped — the conversation UI's own
discriminators), ordered by event.seq. Because it pages the log RPC
directly, the index is independent of the conversation's loaded window:
older messages appear even before the chat has loaded them. Live updates
come from session.subscribe → a debounced (250ms) tail-page refetch merged
by seq.
If a future release drops the (type-private) session.history RPC, the
bundle falls back to reading the conversation snapshot
(session.getSnapshot() → the loaded window) and extending it with
session.loadOlder() while the snapshot reports hasMore — waiting for the
window to open first and re-running the walk on live updates, so the fallback
never silently shows only the loaded messages.text field; blocks that are
auto-injected hints (starting with [图片附件, [系统提示:当前模型无法直接查看图片 or
[用户粘贴的图片) are skipped.data-chat-anchor-key={conversationContextKey(definitionKind, id)} — the user definition
is input-message (id = message id), so the key is {13}:input-message{id}. If the
anchor is absent (message not yet loaded), the rail calls session.loadOlder() repeatedly
(cap 60 pages) until the anchor appears, then scrollIntoView({behavior:"smooth", block:"center"}).Patch the layout (from the repo root):
node scripts/patch-layout.mjs
# the default target is resolved from $DSH_HOME (~/.dsh); or pass an explicit path:
node scripts/patch-layout.mjs "<path>/node_modules/@deepseek-ai/dsh-client-ui-layout/lib/client.js"
Install the package:
echo ${DSH_HOME:-$HOME/.dsh} # e.g. C:\Users\<you>\.dsh on Windows
cp -r dsh-msgrail "$DSH_HOME/profiles/node_modules/" # the package folder itself
Register the Loader entry in "$DSH_HOME/profiles/web/cordis.patch.yml":
- insert:
- id: msgrail
name: 'dsh-msgrail'
Restart dsh web.
node --check passes on lib/client.js, lib/index.js, scripts/patch-layout.mjs;
node scripts/smoke-test-client.mjs loads the real bundle in a shim and exercises the
injected session actions (history RPC pagination/envelope, guards, snapshot read).node -e "console.log(require('<installed>/package.json').exports['./client'])"
prints ./lib/client.js.package.json must be UTF-8 without BOM (the harness parses it
with JSON.parse; a BOM aborts startup with Unexpected token ''). Check:
grep -rl $'\xef\xbb\xbf' "$DSH_HOME" --exclude-dir=node_modules; strip:
sed -i '1s/^\xEF\xBB\xBF//' package.json.msgrail in cordis.patch.yml? installed
package.json declares dsh.client + exports["./client"]? layout patch marker present?
browser console errors on /plugins/dsh-msgrail/client.js?Unexpected token '': BOM in package.json (strip it).surfaceOp === "append" and
data.source.kind === "user"; injected/assistant/steer rows are skipped by design.loadOlder() (max 60 pages); if the session has no more history the anchor cannot exist.shell.history), ctx.sessions.binding, the
history RPC and the chat-node key formula are internal APIs — re-run
scripts/patch-layout.mjs and re-check after upgrades.version in package.json, re-copy the package folder to the
profile's node_modules (both copies byte-identical, BOM-free). Never rewrite JSON via
PowerShell Set-Content -Encoding UTF8 (writes a BOM on Windows PowerShell).scripts/patch-layout.mjs, restart.dsh web line). The
paginated history RPC (session.history({ beforeSeq, maxMessages })) is
still present on the Session class at runtime — it is type-private on the
session face, not removed — so the rail pages the full log directly (the
same approach that originally made "unloaded messages also appear" work).
Should it disappear in a later release, the bundle's snapshot + loadOlder()
fallback takes over (bounded walk, waits for the window to open, re-runs on
live updates). Slot names, the session service API and layout internals
remain private and may change in later versions.CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。