**English** · [简体中文](README.md)
# dsh-file-beam
**Drag files onto the DSH Web composer — resolve their real absolute paths, register them as chips, and let the agent analyze the source files directly. No copy, no path text in your input.**
[](https://github.com/topics/dsh-plugin)
[](LICENSE)
---
## Install
```sh
dsh plugin --profile web add github:bleamayaka/dsh-file-beam
```
Restart `dsh web` and refresh the browser to take effect.
---
## Features
- **Dock chip interaction** — drop a file **or folder** anywhere on the page; a chip appears above the composer
- **Instant path when exposed** — when the browser hands over the real path (`text/uri-list`), it is used directly with zero search; otherwise the host searches the session workspace + `~/Downloads`, `~/Desktop`, `~/Documents` by name and returns candidate absolute paths like `C:\Users\...\Desktop\report.docx`; you pick the right one
- **mtime-proximity ranking** — with multiple same-name candidates, the one whose disk mtime is closest to the dropped file's modification time surfaces first
- **No copy, no transfer** — only the path string is registered; file contents are never read or uploaded; the agent reads the source file directly with its own fs tools
- **Hidden path injection** — the chosen path is NOT written into the composer text (your input stays clean); on submit the host injects the file list into the agent request as a hidden plugin-notice message — the model sees the full absolute paths, the UI shows only a one-line "📎 N files" summary
- **Send without typing** — once every chip is registered a Send button appears in the chip bar; one click hands the files to the agent (an invisible U+2063 draft marker enables file-only submit; the host strips it before the model sees it)
- **History attachment cards** — sent files stay as mini chips in the conversation history (metadata lives in the message's durable `source` field — UI-side data, never part of the model context)
- **Works from any workspace** — search is based on the session workspace + configured dirs, so the agent can always locate the files
- **Unique match auto-registers** — no click needed when there's exactly one candidate; candidate buttons only appear for same-name files in multiple directories
- **Auto-clear after send** — chips disappear once your message is actually sent (the host has consumed the registrations), and on session switch; nothing lingers
- **Images keep the native flow** — pure image drops use DSH's built-in multimodal upload; anything containing a non-image file becomes a path reference
- **Robustness** — duplicate-drop dedup, same-name dedup, connection-reset cleanup, cancellable directory search
---
## Usage
1. Drop a file (e.g. `report.docx`, `data.csv`) anywhere on the DSH Web page
2. A chip appears above the composer: "resolving…" then candidate path buttons (a directly exposed path registers instantly, no waiting)
3. Pick the right path → the chip becomes "registered " (dismissible with ×)
4. Type your instruction and send, or just click the **Send** button in the chip bar — no path text appears in the composer, but the agent receives the hidden file list and reads the source files directly; the chips clear after send and mini attachment cards stay in the history
---
## Configuration (optional)
```yaml
# Override in the profile's cordis.patch.yml:
- id: dsh-file-beam
config:
searchDirs: ["~/Downloads", "~/Desktop", "~/Documents"] # extra search dirs
workspaceDepth: 10 # workspace search depth
searchDirsDepth: 3 # extra-dir search depth
maxResults: 20 # candidate cap
```
---
## Structure
- `lib/index.js` — Host half: path search, session registration (attach/detach/pending), `agent/pre-step` hidden injection
- `lib/client.js` — Browser half: drag capture, dock chip (`conversation.input.dock` slot), candidate picking
- `cordis.patch.yml` — bundle layer declaration (inserts itself)
---
## Official community
This plugin is part of the DeepSeek Harness ecosystem. Join the official community to discuss, report issues, or share your experience:
- [DeepSeek Harness GitHub Discussions](https://github.com/deepseek-ai/deepseek-harness/discussions) — official forum: feedback, questions, plugin sharing
- [DeepSeek Harness Discord](https://discord.gg/Ycq5dCaS4) — official Discord community
- This repo carries the [`dsh-plugin`](https://github.com/topics/dsh-plugin) topic for GitHub discoverability
---
## Credits
This plugin references the following open-source projects and official materials:
**Interaction & path resolution**
- **Interaction design** (dock chip, name-based real-path search, candidate picking, unique-match auto-register): inspired by [gezg/dsh-drag-file](https://github.com/gezg/dsh-drag-file) (MIT) — this plugin changes its model to "no path text in the composer; the host injects paths hidden."
- **Chip-bar Send button & path optimizations** ([AKIRACOD/dsh-drag-and-drop](https://github.com/akiracod/dsh-drag-and-drop), BSD-3-Clause, fork of [omdsh-dev/dsh-drag-and-drop](https://github.com/omdsh-dev/dsh-drag-and-drop)):
- **Send without typing**: a Send button in the chip bar that submits via `conversation.input.for(scope).submit()` — no typing required
- **Instant path extraction**: real paths are parsed straight from the drop's `text/uri-list` / `text/plain` `file://` URLs (including UNC network paths), skipping the host search entirely
- **mtime-proximity ranking**: with multiple same-name candidates, the one whose disk mtime is closest to the dropped file's `lastModified` surfaces first
**Hidden injection & persistence**
- **Hidden injection mechanism** (`agent/pre-step` inserting a plugin-notice message — model sees the full list, UI shows a one-line summary): inspired by the attachment-bridge design of [re-ITRT/dsh-file-fix](https://github.com/re-itrt/dsh-file-fix).
- **File-only-submit trick & history cards** ([WJZ-P/dsh-attachments](https://github.com/WJZ-P/dsh-attachments), MIT):
- **U+2063 invisible draft marker**: when files are ready and the draft is empty, an invisible non-whitespace character enables the native submit; the host strips it before the model sees it — no synthetic copy is ever shown
- **Durable attachment metadata**: file metadata is written into the message's `source` field (UI-side, never model context) and lifted into chat history via a `conversationEvents` definition + the keyed `conversation.chat.node` slot rendering attachment cards
**Spec & docs**
- **DSH plugin contract** (`dsh.bundle` / `dsh.client` / cordis entry / client bundle wrapper): per the official [cordis-tutorial](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/cordis-tutorial/01-first-plugin.md) and the community [make-dsh-plugin](https://github.com/vlln/plugin-registry/blob/main/skills/make-dsh-plugin/SKILL.md) spec.
---
## License
MIT