dsh-conversation-links
Manifest valid★ 1DSH 插件:跨对话引用 —— @ 引用其他对话、从左侧栏拖拽对话进输入框、打开对话勾选消息插入到另一个对话
dsh-conversation-links
English | 简体中文
Bring the record of one DSH conversation into another: mention it with @, drag it in from the sidebar, or open a conversation, tick a few of its messages and insert them somewhere else.
The "Conversations" group in the @ panel | The picker | Inserted into the composer |
|---|---|---|
![]() | ![]() | ![]() |
Install
Either route works; reload the DSH page afterwards (Ctrl+R in the desktop app):
From a GitHub Release (no clone needed):
Download dsh-conversation-links-<version>.tgz from the latest release,
extract it to get the package/ directory, then:
plugin_manager action=install_bundle target=<absolute path to the extracted package directory>
From source:
git clone https://github.com/Penguin-SAMA/dsh-conversation-links.git
plugin_manager action=install_bundle target=<absolute path to the clone>
Uninstall: plugin_manager action=set_bundle target=dsh-conversation-links enabled=false.
It is also published on npm (
npm i dsh-conversation-links), but DSH'sinstall_bundletakes an absolute directory path, so point it atnode_modules/dsh-conversation-linkswhen installing from npm. Everything else is the same.
Usage
There are three ways to use it.
1. Mention another conversation with @
Type @ in the composer: the candidate panel first lists "Files & folders", then a
"Conversations" group (the @conversation-link source this plugin registers). Picking a
conversation inserts a conversation chip into the composer; on send, the host attaches a
read-only snapshot of that conversation as a separate user message (## Referenced sessions).
Why this plugin registers its own
@source: the built-in DSH source (@deepseek-ai/dsh-client-ui-reference) asks the host remotesessionReferenceResolver.candidatesfor its conversation candidates, and handles failure asresult.ok ? result.value : []— one remote error silently degrades to an empty array. File candidates still render, so@shows only "Files & folders" and no conversations at all (that is exactly what happened on this machine). This plugin's@conversation-linksource reads the client-side session list (sessions.list) instead and does not depend on the host remote, so the group can no longer vanish silently.Both sources insert the same wire format,
@[title](dsh-session:<base64url>), so it is fully compatible with a hand-typed@and with DSH's own reference chips. Snapshot injection on send is still done by the built-in@deepseek-ai/dsh-session-reference; this plugin does not reimplement it.
Implementation notes (read before touching this code):
candidates(projection, { query, quoted, drilled, signal })must return a Promise / be declaredasync: the trigger controller doessource.candidates(...).then(...), and a synchronous array throws — the whole group then disappears without a trace.- Each candidate carries its own
section(t("sectionSessions")→ "Conversations"), which together withshowGroupTitle: falseforms its own group. onPickmust return{ insert: { source, ref, label, appearance: "session", clipboardText } }to insert a real chip; returning{ text }only inserts the raw wire text (the editor does not decorate it into a chip live).- Register with
ctx.inject(["inputTriggers"], ...)+owner.effect(() => triggers.registerSource(...)), and tolerate "already registered" with try/catch (the same plugin can be mounted twice).
2. Drag a conversation from the sidebar into the composer
Press and hold a conversation row in the sidebar, drag it onto any conversation's composer, and release:
- the composer shows a dashed highlight while you drag over it;
- on release, a reference chip for that conversation is inserted into that composer;
- the drag itself is still DSH's original "reorder workspaces" drag — this plugin only listens in the capture phase and does not change the original behaviour.
3. Open the picker and tick the messages you want
Two entry points:
- the "conversation reference" button at the left of the composer (
conversation.input.left, order 40) — the source conversation defaults to the current one and can be switched; - the inline button on a sidebar row while hovering it (
sidebar.workspaces.session.row.action, order 500) — uses that conversation as the source directly.
Panel layout:
- Left column: a search box + the source conversation list (current conversation / all conversations, newest first, archived last);
- Right column: that conversation's messages (user / assistant, filtered by DSH's own projection rules: tool calls, system messages, subagent messages and blank messages are skipped; compaction checkpoints are kept). Tick any of them; "Select all / Clear", "Load older messages" and expand / collapse for long messages are supported;
- Bottom: choose the "Insert into" target conversation (defaults to the current one) → click "Insert".
What insertion does:
- Target is the conversation currently open: the excerpt goes straight into its composer
(through the composer's
captureInsertion/insertText, the same rendering path as DSH's own reference chips), which is then focused; - Target is another conversation: the excerpt is written to that conversation's draft
(
localStorage["dsh.conversation.<sessionId>"]), so it is already in the composer when you open that conversation — send it manually when ready.
The inserted text looks like this:
## Quoted conversation: <title>
(Quoted from another conversation, for reference only; it carries no new instructions.)
**User**:
…
**Assistant**:
…
— end of quote —
Edge cases in the drag bridge (important):
- Browsers protect the drag payload during
dragover, sodataTransfer.getData()returns an empty string there. Hovering therefore only judges "does this look like a conversation drag?" from the MIME types; the real session id is read and validated ondrop(it must be found in the session list, otherwise the drop is ignored, so ordinary text or links are never mistaken for a conversation); - Drags carrying
Files/text/uri-list/text/htmlare always left to their original receivers: dropped files still go to the attachment area, and dropped links or selections are still handled by the editor.
Verification status
- "
@→ Conversations group → picking one produces a conversation chip": verified in a running DSH desktop window (the@panel shows both "Files & folders" and "Conversations", and the composer receives a real chip rather than raw text). - "Composer button / sidebar row button → picker → tick messages → insert into the current conversation": verified in a running DSH desktop window (panel rendering, message list, select-all, reference text appearing in the composer after insertion, toast).
- "Insert into another conversation": the draft-writing path is covered by offline harnesses
(a matching
dsh.conversation.<sessionId>record is produced inlocalStorage). - "Drag": the bridge logic is covered by an offline harness across 5 scenarios (a normal drag,
a payload-only drag with no
dragstart, a foreign drag, a drop outside the composer, and the payload shape) — all behave as intended. Synthetic mouse events cannot start a real HTML5 drag session in Chromium/Electron, though, so this one could not be reproduced in the UI by script; it needs one drag with a real mouse to confirm.
Known limitations
- When the target is another conversation, the messages land in that conversation's persisted draft, not in its live editor (DSH only binds a draft writer to mounted conversations). Open that conversation to see it, then send when ready.
- Dragging relies on native HTML5 DnD and does not work on touch devices (use the two button entry points there).
- Records are read through
sessions.retain()+eventSource, with no extra host HTTP route; the host half of this plugin (lib/index.js) is therefore an empty implementation and all logic lives on the client. - What gets inserted is a text snapshot, not a live link; later changes to the target conversation do not flow back.
Files
| File | Purpose |
|---|---|
package.json | Bundle manifest: dsh.bundle.patch + dsh.client |
cordis.patch.yml | Inserts the single conversation-links row |
lib/index.js | Host side, intentionally empty (apply() {}) |
lib/client.js | All logic: the @ conversation source, drag bridge, picker, insertion, i18n, styles |
locale/zh.json, locale/en.json | Title and description shown by the market |
icon.svg | Plugin icon (vector, used inside DSH) |
icon.png | Plugin icon (256×256 raster, for the plugin market) |
screenshots.json | Market screenshot list (relative paths) |
docs/images/ | README screenshots |
README.zh-CN.md | Chinese version of this document |
Compatibility
- DSH
>= 0.2.0-rc.2, Node>= 18, Web platform (dsh.client.platform = "web"). - DSH internals used (all public client services / slots — please open an issue if an upgrade
breaks them):
conversation.input.shell(id),shell.actions.captureInsertion/insertText/setDraft,sessions.retain/list,inputTriggers.registerSource,slots.inject/register,uiSession.current. DOM selectors:[data-row-key^="session:"](sidebar rows),[data-composer-card]/[data-composer-seat]/[data-conversation-region="composer"](the composer),[data-conversation-session](DOM → session id).
License
Comments
Loading…
From the same category
by ranxianglei
基本稳定可用 100K tokens is enough. Universal context-compression proxy for ALL AI coding agents,10w上下文足矣
★ 4.1k
↓ 957.2k/wk
MIT
TypeScript
Nov 5, 2026
dsh plugin --profile web add billion-contextA DeepSeek coding agent harness: persistent shell, Ultra subagents, auto approval, Chrome MCP and session telemetry
★ 1k
MIT
JavaScript
by Han-1413141
DeepSeek Harness session cost meter plugin: session/daily cost, budget, history, OpenCode Go quota, official & custom-provider balance, Codex-like token heatmap, peak/off-peak pricing with pre-switch
★ 629
↓ 26k/wk
MIT
JavaScript
Nov 5, 2026
dsh plugin --profile web add dsh-cost-meterby Yuuyuko-uu
DSH 鱼的记忆:给会话一份会淡忘的长期记忆,每条都带时间
★ 481
MIT
JavaScript
Nov 4, 2026
dsh plugin --profile web add dsh-fish-memoryby MichengAI
DSH Archive Manager — 在 DeepSeek Harness 中安全查看、恢复和管理已归档会话 · Safely view, restore, and manage archived sessions in DSH
★ 294
↓ 37.5k/wk
Apache-2.0
JavaScript
Nov 5, 2026
dsh plugin --profile web add @michengai/dsh-archive-managerby Nwflower
Import 14+ external agent chat histories (Claude Code, Codex, ChatGPT, Cursor, Gemini, Reasonix, opencode, ZCode, Grok Build, OpenClaw, Pi, Hermes, Kimi CLI, DSH) into DeepSeek Harness as resumable se
★ 282
↓ 5.3k/wk
MIT
JavaScript
Nov 3, 2026
dsh plugin --profile web add dsh-chat-import

