DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

Selection Quote — DeepSeek Harness 插件(DSH Plugin)
DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录
← Plugins
S

dsh-selection-quote

Selection Quote

在 DSH 对话中选择文本,并将其作为可移除的引用附加到编辑器。

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add github:cfyofjackie/dsh-selection-quote#499e365ce65f32b7bff5cd2d451e3a5f2171d7d7
README兼容性版本
上:选中一段文字,旁边浮出「添加到对话框」按钮。下:点一下之后,输入框里出现一枚可以整体删除的引用芯片

兼容性与来源证明

Selection Quote 以 dsh-selection-quote 发布,当前版本为 0.1.0。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
web
发布来源
github
Registry 更新时间
2026/10/4

版本

0.1.0stable
2026/10/4

相关插件

正在加载相关插件…

最新版
0.1.0
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
未提供
文件数
未提供
Surface
web
许可证
MIT
发布源
github
GitHub
★ 0
周下载
0
最近提交
2026/10/4
查看源码 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题

相关插件

继续浏览 memory-context 分类下经过校验的插件。

Hindsight Coding Agents@vectorize-io/hindsight-coding-agents面向编程代理的仅反思式 Hindsight 长期记忆(可插拔支持多种运行框架:opencode、……),自动在后台导入(无需设置 CLI)。Memsearch Dsh@zilliz/memsearch-dsh适用于 DeepSeek Harness 的 MemSearch 插件:在多个代理之间共享 Markdown 记忆,支持捕获、步骤前上下文注入、记忆召回技能和技能候选审核面板。Memory@furongjun1999/dsh-memory灵枢(Lingshu·líng shū)DeepSeek Harness 插件:完整大脑——长期记忆/知识飞轮/自我认知/递归反思接入 DSH,对话自动沉淀进 md_cg 认知图(md 文档)Archive Manager@michengai/dsh-archive-manager归档、恢复、收藏和永久删除会话,并诊断损坏的会话日志。

README

dsh-selection-quote

Select text in a DSH conversation and attach it to the composer as a quote reference — an atomic chip, not pasted text. Nothing is sent.

English | 中文

Selecting a passage in the transcript; a floating "add to chat" pill appears next to the selection

What it does

  1. Select a non-empty passage in the transcript (an assistant reply, your own message, or a steering note).

  2. A small pill appears next to the selection: 「添加到对话框」 / "Add to chat".

  3. Clicking it places a quote chip in the composer, labelled with a short taste of the passage:

    what you had already typed [引用:the passage…] 
    
  4. The chip is a node in the editor, not a string:

    • one Backspace removes the whole thing;
    • it undoes in the same step as the typing around it;
    • copying the draft expands it to the quote text (> …), so it stays readable wherever it is pasted;
    • at submit time this plugin's reference codec turns it into the model text, rather than splicing text into the draft when you click.

    Which makes it the same kind of thing as @file / @session — DSH's own answer to "an object in the composer".

  5. Focus returns to the composer so you can type your question and send it yourself.

What it does not do: it never sends, never writes storage, and never touches the session log.

How a quote becomes model text

This is the plugin's actual mechanism, and the difference from "paste the text in":

ProjectionOwnerValue
Chip label on screenlabel at insertion引用:the passage…
Clipboard / persisted draftcodec.clipboardText(ref)> the passage
What the model receivescodec.serialize(ref, signal)> the passage
  • The source is registered with ctx.inputTriggers.registerSource({ trigger: '@', name: 'quote', codec }). ReferenceInsert.source on every inserted chip must equal that name, or the submit path cannot find the codec.

  • DSH defines a missing codec as a refused submission, not a silent downgrade to the clipboard text. So ref is self-contained — the passage is base64url-encoded inside it — and resolution never depends on plugin memory. A restored draft, a hot reload, or a chip from another session can always be serialized.

  • The insertion point is expressed in detect coordinates. The composer walks its document into two projections: detectText, where each chip is exactly one object-replacement character, and the clipboard projection the shell publishes as InputState.draft, where each chip is its full clipboard form. So "the end of the document" has to be folded back:

    end = state.draft.length - Σ(occurrence.length - 1)
    
  • The entry point is the one the service documents in its own error message: ctx.sessions.scope(sessionId).conversation.input.for(actx).insertReference(ref, span).

  • If the insertion is refused (a stale revision, a span the fold could not place) the plugin falls back to plain-text quoting rather than dropping the passage. Set DIAGNOSTIC = true in src/client.tsx to see which path was taken.

Install

The plugin has a Host half and a browser half, and it is mounted by naming it in a profile. There are three ways to do that. Pick one — listing the package as a bundle and writing an explicit insert leaves two entries sharing an id, and only one of them stays active.

needs a package managereditsverified
1. As a packageyesnoneyes
2. From a clone, by absolute pathnofour lines of YAMLyes
3. As a package, inserted by nameyesfour lines of YAMLyes

1. As a package

dsh plugin --profile desktop add github:cfyofjackie/dsh-selection-quote

That is the whole install. The command forwards to pnpm inside the profile directory, and because the package declares dsh.bundle.patch it also appends the package name to dsh.profile.bundles — which is what actually mounts the plugin. It does not say so in its output, so check the bundle list rather than adding the name yourself: a duplicate entry mounts the same layer twice and leaves two entries sharing one id, of which only one stays active.

A package listed as a bundle without declaring dsh.bundle fails the boot loudly rather than silently doing nothing. Installing without being listed leaves the package present but inert.

2. From a clone, by absolute path

No package manager involved. On macOS and Linux the desktop profile lives at ~/.dsh/profiles/desktop/cordis.patch.yml:

# ~/.dsh/profiles/desktop/cordis.patch.yml
- insert:
    - id: dsh-selection-quote
      name: "/absolute/path/to/dsh-selection-quote/lib/index.js"

A freshly created profile's patch file contains just []. Replace that line with the block above — do not append after it, because [] followed by a list item is a YAML parse error and the profile will fail to boot. If the file already holds entries, append normally.

On Windows the profile is %USERPROFILE%\.dsh\profiles\desktop\cordis.patch.yml and the path is a Windows path. Forward slashes work and avoid YAML escaping:

- insert:
    - id: dsh-selection-quote
      name: "C:/path/to/dsh-selection-quote/lib/index.js"

3. As a package, inserted by name

Same dsh plugin … add as above, but mount it through the profile's patch file instead of its bundle list. The Loader resolves a bare name from the profile's own node_modules, which is where the package manager put it:

# ~/.dsh/profiles/desktop/cordis.patch.yml
- insert:
    - id: dsh-selection-quote
      name: "dsh-selection-quote"

After mounting

Profile patches reload live, but an already-loaded page keeps the boot graph it was served — reload once (Cmd+R on macOS, Ctrl+R elsewhere, or restart DSH).

To remove it, undo whichever mount you chose and reload.

lib/ is committed on purpose: the profile loads lib/index.js and lib/client.js directly, so a checkout works without a build step.

Trying it without touching your main profile

A profile is a self-contained plugin tree, so a throwaway one is the safe way to evaluate this — and pointing a separate DSH_HOME at it keeps sessions and storage out of the way too:

CLI="/Applications/DeepSeek Harness.app/Contents/Resources/runtime/cli/bin/dsh"
export DSH_HOME=/tmp/dsh-sandbox
"$CLI" --profile sandbox --from-default-profile web --dump-config   # create
"$CLI" plugin --profile sandbox add github:cfyofjackie/dsh-selection-quote
"$CLI" --profile sandbox --port 3917                                # boot

(Without a package manager, skip the second line and put install route 2's YAML in $DSH_HOME/profiles/sandbox/cordis.patch.yml instead.)

To check the mount actually took before booting, --dump-config prints the composed tree, and the dsh web page's boot graph carries one row per client plugin with the content hash of its bundle.

Two things that surprise people about a fresh DSH_HOME:

  • it has no model credentials. The UI loads and the plugin registers, but a conversation has nothing to answer with. Copy ~/.dsh/.credentials.yaml into the sandbox home to lend it your key — or, if you would rather not, create the second profile inside your real DSH_HOME instead (--profile sandbox --from-default-profile web without the export); that shares credentials and sessions and differs only in its plugin tree.
  • the web auth token is not a setting. It is minted per boot and carried in the URL dsh web prints. Opening the bare 127.0.0.1:<port> answers 401; open the printed URL instead.

UI language

The pill follows DSH's active locale. The plugin registers both zh and en dictionaries (ctx.locale.register), matching the shipped convention that a language's fallback chain terminates at English — so switching DSH's language to English shows "Add to chat" with no plugin change.

Development

npm install --no-save esbuild   # the only build tool; DSH's own runtime also works
npm run build                   # writes lib/index.js and lib/client.js
npm run watch                   # rebuild on change
npm test                        # build, then the headless suite (no browser needed)

The underlying commands are plain Node, so npm is a convenience rather than a requirement: node build.mjs, node build.mjs --watch, node --test. Nothing in the repository depends on a package being installed — npm install above is only there to supply esbuild when the machine has no DSH runtime to borrow it from. build.mjs looks for esbuild in the plugin folder, $DSH_HOME/profiles/node_modules, the npm npx cache (POSIX and Windows), and the DSH app runtime; DSH_ESBUILD=/path/to/esbuild overrides the search.

Build output

FileRole
lib/index.jsHost half. It must exist: the client module system only composes a browser bundle for packages that appear as Loader entries and declare dsh.client. The body is deliberately empty.
lib/client.jsBrowser half: a CJS bundle wrapped in the window.__ModuleLoader__.load({ id, factory }) registration envelope.
cordis.patch.ymlThis package's own profile layer, named by dsh.bundle.patch. It is what makes "add the name to dsh.profile.bundles" a complete install.

Tests

npm test runs the built bundle, not the TypeScript source: it stubs the browser globals and the platform-seed modules, renders components with a small hook runtime that also implements React's error-boundary semantics, and replays real event orderings (pointerdown capture before click). Regression tests cover each failure this plugin has actually had.

Notes from building this

Four failures shaped the current design; every one of them was invisible without instrumentation, and each is generalizable to any DSH client plugin:

  1. A slot entry that throws once is silently abdicated. The slot renderer wraps every entry in an error boundary and marks a crashed entry abdicated, after which it is skipped forever — no error UI, no log the user sees, and the plugin still reports active in the loader.
  2. Platform-seed module exports drift between shell builds. The desktop app serves the dsh-web-frontend dist bundled inside app.asar, which is not the copy published in the profile's node_modules. In that build the icon set is named Icon*OutlineRegular / Icon*OutlineMedium, so IconPlusOutline16 resolved to undefined and React raised error #130.
  3. A document-level capture scroll listener that measures the selection forces a layout per event. During a drag-select with autoscroll that is dozens of forced layouts, and it made the pill take a second or two to appear.
  4. A transcript selection does not clear by itself. Chromium only collapses an existing selection when the press lands on a selectable target, and the transcript's own chrome opts out of selection — so pressing next to a message left the blue highlight up with no way to remove it. The plugin now dismisses it on a primary press, while deliberately leaving shift-press (extend) and secondary press (context menu → Copy) alone.

The full writeup, with the code each conclusion came from, is in docs/gotchas.md.

Known limitations

  • The quote lands at the end of the document, not at the caret. The caret is not part of the public standard props (ComposerKeyboard.caretSpan() is package-internal), whereas the document end can be computed exactly.
  • If insertion is refused the quote falls back to plain text: the passage is not lost, it just stops being a chip.
  • One quote is capped at 20 000 characters; beyond that it is truncated with an explicit marker in the quote, never silently shortened.
  • A quote is not editable — it is an atomic node; delete it and select again.
  • The pill is disabled while the composer is adjudicating / claimed / submitting.
  • Chat view only. The selection must sit inside [data-chat-flow]; the trajectory and waterfall views do not carry those anchors.
  • A selection spanning several transcript nodes is judged by the node it starts in.
  • A quote carries no provenance (which message it came from). The INCLUDE_PROVENANCE constant is the hook for adding it.

Compatibility

Built and verified against DSH 0.1.5-rc.3 client packages on the macOS desktop profile. It deliberately depends on nothing but React from the platform seed, so it does not break when a shell build renames its own exports.

Platform support. Nothing in the plugin is OS-specific: the browser half is React plus DOM APIs, and the build script uses only node:fs/node:path. The parts that could differ are covered explicitly:

  • the esbuild search walks POSIX and Windows locations (%LOCALAPPDATA%\npm-cache\_npx, the packaged runtime under %PROGRAMFILES%), and DSH_ESBUILD overrides it outright;
  • lib/ is committed, so a user never needs the build step at all — the only thing a Windows user does is point a patch at the path;
  • line endings are pinned by .gitattributes, esbuild normalizes output to LF, and a test asserts the committed bundle contains no CRLF;
  • the test that imports the Host half goes through pathToFileURL, because a dynamic import() of C:\… is rejected as an unsupported URL scheme;
  • CI runs the build and the full test suite on ubuntu, windows and macos, and fails if the committed lib/ no longer matches the source.

What is not verified is DSH's own Windows build: the desktop app's Electron shell, and therefore which dist it serves, was only inspected on macOS. If something misbehaves there, the plugin's DIAGNOSTIC switch will say which gate refused the selection.

Not affiliated with DeepSeek.

License

MIT — see LICENSE.