DeepSeek Harness plugin

sent-msg-locator

DSH 静态 bundle 插件:定位当前会话中每次通过输入框发送的消息。会话标题栏「消息定位」按钮 → 右侧浮动面板列出全部已发送消息(序号/时间/摘要),支持关键词搜索、点击展开全文、一键回填输入框重新使用。A static-bundle DSH plugin that locates every message you sent from the composer.

Jump to install

Source facts

Repository
ShanHaiFish/sent-msg-locator
Latest update
Aug 21, 2026
Category
Memory
GitHub stars
2
Format
plugin
Catalog evidence
Upstream dsh.bundle evidence
Evidence path
package.json#dsh.bundle
Checked against
0.1.0-rc.8
Upstream check date
2026-08-20

This evidence comes from the upstream catalog. This site has not installed, run, or security-reviewed the plugin.

Install

Start with a prompt that asks an agent to review the GitHub repository and source. Switch to the command if you want to install it yourself.

Copy this prompt into DSH, Codex, or another agent and ask it to review the GitHub repository and source first.

Do not install or run any commands yet. Read this plugin's GitHub repository, README, and relevant source code. Then answer the questions below clearly and directly so I can decide whether it fits my needs:

1. What is this plugin, and what problem does it solve?
2. Who is it for, and what are its typical use cases?
3. How is it used after installation? Include one minimal example.
4. What known limitations or privacy, security, compatibility, or maintenance risks does it have?
5. Give a clear recommendation: recommend, conditionally recommend, or do not recommend, with reasons.

Distinguish statements documented by the repository, inferences from source code, and unknowns. If evidence is insufficient, say so explicitly. Do not guess or simply repeat the README.

GitHub: https://github.com/ShanHaiFish/sent-msg-locator
Plugin: sent-msg-locator
Author: ShanHaiFish

Check the source files

Read the README and other files from this plugin directory before installing.

File explorer4 files
README.en.mdSource · read only
README language

Sent Message Locator (sent-msg-locator)

English · 中文

!GitHub Release !License !GitHub Stars ![npm](https://www.npmjs.com/package/sent-msg-locator)

A DSH (DeepSeek Harness) plugin that locates every turn of the current session (a turn = your message sent → assistant's complete reply).

A slim floating rail sits at the left edge of the conversation area: one bubble-shaped numbered badge per turn. Click a badge to smooth-scroll the chat viewport to the first user-typed message of that turn; the rail highlights the turn you are currently viewing and follows along as you scroll, updating in real time.

Since v2.2.0 this is a static bundle plugin loaded automatically from the profile layer stack — install once, and it activates on every DSH startup with no manual define/run needed.

Features

  • Left-edge icon rail (fixed position, ~40px wide, always visible):

- One bubble-shaped numbered badge per turn (#N), latest turns at the bottom - Click a badge → smooth-scroll to the exact user-typed message of that turn (matched via the built-in chat anchor mechanism data-chat-anchor-key, element-level API, never touches document/window) - Current-turn highlight follows scrolling; the rail auto-scrolls to keep the highlighted badge visible - In-progress turns show a dashed translucent badge until the reply completes - Hover tooltip: a rounded custom card — "Turn N · time (· in progress)" plus the first user-typed message of that turn (clamped to 280px × 6 lines with an ellipsis; image-only messages show "[image]"); also shown on keyboard focus - Internal scrollbar for long conversations; rail stays at the left edge of the conversation area (right of the sidebar) and follows sidebar collapse/drag via timer-calibrated coordinates

  • Turn unit: one official engine Turn (user send → assistant complete reply), including open/in-progress status
  • Real-time updates: the rail refreshes as the session snapshot changes; switching sessions clears stale data automatically
  • Theme aware: all colors use theme CSS variables (--dsw-alias-*), readable on light/dark/any theme

Data comes from the official chat.timeline (turnOrder + turns) merged with the chat.nodes snapshot (so turns never disappear when session history is paged via loadOlder). No backend dependency, no document/window manipulation.

> Pure Client plugin: no Host capabilities (no RPC / fs / network / spawn). Scrolling uses only element-level APIs (closest / getBoundingClientRect / scrollTo / scrollIntoView).

Demo

![sent-msg-locator demo animation](assets/demo.gif)

Click a badge to smooth-scroll to that turn's user-typed message; the current-turn highlight follows scrolling and updates in real time.

Quick Start

# Install (static bundle, recommended)
dsh plugin --profile web add sent-msg-locator

For a local, unpublished copy use file: pointing at this repository (the path must NOT contain spaces):

dsh plugin --profile web add file:/path/to/sent-msg-locator

Restart dsh web and the plugin activates automatically: the numbered rail appears at the left edge of the conversation area. No manual define/run required.

Usage

1. See the rail: open any session with history — a floating numbered rail appears at the left edge of the conversation area, one bubble badge per completed turn; 2. Jump to a turn: click any badge — the chat viewport smooth-scrolls to the first user-typed message of that turn; 3. Follow your position: scroll the conversation — the current-turn badge highlights (brand color) and the rail auto-scrolls to keep it visible; 4. Long conversations: scroll the rail's own scrollbar to browse all turns; older turns appear as you load more history (Load earlier in the chat view — the DSH engine pages history at 50 events per request, so earlier turns show up incrementally); 5. In-progress turns: while the assistant is replying, the newest badge is dashed/translucent and becomes clickable once the reply completes.

Repository Contents

PathDescription
package.json + cordis.patch.yml + lib/ + client/Static bundle (recommended): auto-loaded on DSH startup after dsh plugin add
manifest.json + client-source.jsDynamic-plugin fallback form: for profiles without bundle support
assets/demo.gifDemo animation shown on the README intro page (~11MB, compressed from the 94MB raw recording)
LICENSEMIT License
AGENTS.mdAgent collaboration conventions (rebuild flow / change workflow / coding conventions / versioning)
README.md / README.en.md中文 / English docs

Two Forms

Static bundle (v2.2.0, recommended)Dynamic plugin (fallback)
Loadingdsh plugin add installs into the profile layer stack; auto-loaded on DSH startupMust be re-registered with cordis_define + cordis_run after every DSH restart
Codelib/index.js (Host) + client/client.js (Client)client-source.js (Client only; code.host is null)
When to useNormal profiles (web, etc.)Profiles without bundle support

> Do not activate the static bundle and the dynamic form at the same time — the same slots would be registered twice and the rail would render duplicated.

Dynamic fallback steps:

1. Have an agent read client-source.js; 2. cordis_define: plugin: { kind: "new", idPrefix: "smsg" }, name/purpose from manifest.json, code.host: null, code.client as the full content of client-source.js; 3. cordis_run to activate; success when the rail appears.

> The dynamic form does not survive DSH restarts and must be reloaded; the static bundle form has no such limitation.

Technical Notes

  • Data source: useSession snapshot → official chat.timeline (turnOrder + turns) as primary, chat.nodes snapshot as fallback — merged so turns never go missing when session history is paged (loadOlder limits each page to 50 events). Per-turn fields: turn number / start.time / end.time / status (open / closed / unknown).
  • Data bridge: a hidden 0-size element inside the session-scoped conversation.input.dock slot captures useSession/sessionId and measures the conversation-area left edge (getBoundingClientRect, element-level) as the fixed-position baseline; a 300ms timer poll keeps the rail attached to the sidebar's right edge when the sidebar collapses or is dragged.
  • Turn-tail anchors: the additive conversation.chat.assistant-actions slot renders a 0-size anchor element at each completed assistant message, resolving its turn from the snapshot node's data.turn; the first anchor attaches a scroll listener on the chat scroll container (closest('[data-conversation-scroll]')) for current-turn detection.
  • Precise jump: click → match the snapshot node key against the built-in data-chat-anchor-key rows (element-level querySelectorAll), compute the row position, smooth-scroll; falls back to the tail-anchor position when the row is missing.
  • Current-turn detection: on scroll, the turn of the first anchor below the viewport top wins; the rail keeps the highlighted badge visible with scrollIntoView({ block: 'nearest' }).
  • Hover tooltip card (v2.3.0): data comes from the first kind === 'user' / 'steering' node of the turn — data.content (ContentBlock[], only { type: 'text' } blocks are joined; image-only messages show "[image]" placeholder). Steering nodes are also user input: the session's first message (and mid-turn steering messages) are claimed via agent/inbox/spliced (target: next-step) and rendered as kind === 'steering' instead of 'user' (same renderer, same anchor mechanism — no visible difference; fixed in v2.3.2 so the first badge shows its text too). A custom fixed-position rounded card replaces the native title (which cannot be sized or rounded; aria-label keeps the summary): ≤280px wide, text clamped to 6 lines (-webkit-line-clamp + max-height fallback, plus a 400-code-point JS safety cap); clamped against the chat scroll container's getBoundingClientRect to avoid right/top/bottom overflow; a 200ms timer poll re-measures while shown (follows sidebar drag / rail internal scroll); rendered as a sibling of the rail (the rail has overflow-y: auto and would clip children); triggered by mouse hover and keyboard focus; pointer-events: none so it never blocks the badges.
  • Paged window & placeholder (v2.3.3): history loads in pages of 50 messages (the page cut never splits a message but can split a turn), so the oldest visible turn (the rail's first badge — its number is not necessarily 1) usually has only its tail loaded — its user message lies outside the loaded window and no user/steering node exists in the snapshot, so the text cannot be obtained client-side. In that case the tooltip shows an italic placeholder "该轮用户消息尚未加载,点击聊天区「加载更早」后显示" (label-tertiary theme color); clicking "Load earlier" refreshes the snapshot and the text appears automatically.
  • Compaction marker (v2.1.12): after DSH auto-compaction the old turns disappear from the rail; a small compaction icon + dashed separator appears at the top (detected via kind === 'compaction' checkpoint nodes), tooltip "N history records compressed · ~M tokens".
  • All slots are additive (conversation.input.dock / conversation.chat.assistant-actions / shell.overlay); no built-in UI is replaced.

Security & Boundaries

  • Capability declaration: none (pure UI) — no network requests, no spawn/process, no filesystem, no document/window globals; client uses only ctx / React / styles / console built-ins.
  • Security review: ALLOW (0/300), reviewed by plugin_security_review (see dsh-plugin-security-review).
  • Known limitation: the DSH engine pages session history (50 events per Load earlier request), so the rail can only show turns within the currently loaded window; earlier turns appear incrementally as history is loaded. This is engine behavior, not a plugin defect.

Version History

VersionNotes
v2.3.3Fixed the oldest visible turn (the rail's first badge — its number is not necessarily 1) showing no user text on hover in paged sessions: history loads in pages of 50 messages (the cut never splits a message but can split a turn), so the oldest visible turn usually has only its tail loaded — its user message lies outside the loaded window and no user/steering node exists in the snapshot, so the text cannot be obtained client-side. Fix: the tooltip now shows an italic placeholder "该轮用户消息尚未加载,点击聊天区「加载更早」后显示" (label-tertiary color); the text appears automatically after loading earlier history; security review ALLOW (0/300)
v2.3.2Fixed the first badge showing no user text on hover — the session's first message (and mid-turn steering messages) is claimed via agent/inbox/spliced (target: next-step) and rendered as a kind === 'steering' node instead of 'user' (same renderer, same anchor mechanism, no visible difference), so the old node.kind !== 'user' filter missed turn 1's text and precise-jump key. Fix: the filter now accepts both 'user' and 'steering' (injected context messages are still excluded); security review ALLOW (0/300)
v2.3.1Fixed the v2.3.0 outage — the data bridge crashed on blank/loading sessions and the whole rail disappeared. Root cause: userTextByTurn was declared inside the if (order.length) block but referenced outside it in setState; with an empty order the ReferenceError was permanently abdicated by the slot system (one render error retires the entry forever), so the bridge stopped rendering, left stayed 0 and the rail never appeared. Fix: declaration moved to the outer scope + the whole derivation wrapped in try/catch (skip the pass and console.warn on error, so the bridge can never be abdicated again); security review ALLOW (0/300)
v2.3.0Added hover tooltip cards: a custom fixed-position rounded card (replacing the native title, which cannot be sized or rounded) shows "Turn N · time (· in progress)" plus the first user-typed message text of that turn (from the first kind === 'user' node's data.content text blocks; image-only messages show "[image]"); long text clamps to 280px × 6 lines with an ellipsis (-webkit-line-clamp + max-height fallback, 400-code-point JS safety cap); clamped against the chat scroll container rect to avoid overflow; 200ms timer poll keeps it attached while shown; rendered as a sibling of the rail; keyboard focus also shows it; security review ALLOW (0/300)
v2.2.0Switched to static bundle form: installed into the web profile via the ~/.dsh/plugins-dev/sent-msg-locator copy (file: install, added to the dsh.profile.bundles layer stack), auto-loads across DSH restarts; dynamic form (manifest.json + client-source.js) kept as fallback; functionality identical to v2.1.12; security review ALLOW (0/300)
v2.1.12Added compaction visual separator: after DSH auto-compaction old turns disappear from the rail and a compaction icon + dashed divider appears at the top (detected via kind === 'compaction' checkpoint nodes), tooltip "N history records compressed · ~M tokens"
v2.1.11Shrunk badges to 80% (22→17.6px, font 10.5→8.4px); current-turn digit now uses contrast-color() for black/white by brand luminance (falls back to #fff), fixing invisible white digits on light brand themes
v2.1.10Nudged the rail 5px→2px from the conversation-area left edge
v2.1.9Nudged the rail 7px→5px from the conversation-area left edge
v2.1.8Nudged the rail 14px→7px from the conversation-area left edge
v2.1.7Made the rail body fully transparent (removed background/border/shadow/radius), keeping only the bubble badges — no more occlusion when the rail overlaps conversation text at narrow widths
v2.1.6Rebased the left-edge measurement to the chat scroll container [data-conversation-scroll] — the hero phase (blank session) uses a narrow centered composer (~812px), so measuring the dock row's left edge pushed the rail away from the sidebar and over the input box; the scroll container spans the whole conversation column and its left edge equals the conversation-area left edge in hero/active/settling phases
v2.1.5Restored a visible rail scrollbar and widened the rail (28→40px) so all turns are browsable when there are many
v2.1.4Fixed missing turns: the official timeline can omit early turns when history is paged; turns are now derived from timeline + chat nodes merged, so every rendered turn shows up
v2.1.3Left a gap between the rail and the sidebar right edge (left +14)
v2.1.2Moved the rail down below the session header so it no longer covers the "Chat" view tab
v2.1.1Anchored the rail to the sidebar right edge (conversation-area left edge); timer-polled coordinate calibration follows sidebar collapse/drag
v2.1.0Click now jumps precisely to the first user-typed message of the turn (snapshot node key matched against the built-in data-chat-anchor-key rows); current-turn detection switched to "first anchor below viewport top" for accurate highlight
v2.0.0Rebuilt as the left-edge icon rail: one bubble numbered badge per turn, click to smooth-scroll to the turn start, current-turn highlight + auto-follow; removed the header entry, right panel, search, expand, refill; one turn = official engine Turn; data from chat.timeline; pure Client, security review ALLOW (0/300)
v1.0.0Initial release: header "Locate Messages" button + right floating panel, search / expand full text / refill composer; pure Client, security review ALLOW (0/300)