xzyonline
dsh-chat-files
Session-bound file attachments for DeepSeek Harness: drag, paste, or select files into the chat; the agent reads them via bounded, session-authorized tools. GB18030/GBK decoding, secret redaction, zip-bomb defense. One-click install. | DeepSeek Harness 会话文件附件插件:文件拖入/粘贴/选择进对话,模型经会话授权工具受限读取。GB18030/GBK 中文编码、敏感信息脱敏、压缩炸弹防护。一键安装。
- Stars
- 2
- Language
- TypeScript
- Created
- Aug 14, 2026
- Updated
- Aug 17, 2026
Introduction
dsh-chat-files
Session-bound file attachments for DeepSeek Harness. Drag, paste, or select a file into the chat; the agent reads it on its next step through bounded, session-authorized tools. Supports PDF, Office documents, archives, and plain text — with GB18030/GBK decoding, automatic secret redaction, and zip-bomb defense. One-click install on macOS and Windows.
DeepSeek Harness 会话文件附件插件。文件拖入、粘贴或选择进对话后,模型在下一步经会话授权的受限工具读取。支持 PDF、Office 文档、压缩包与纯文本;含 GB18030/GBK 中文编码、敏感信息自动脱敏与压缩炸弹防护。macOS 与 Windows 一键安装。
内部包名保持
@dsh-external/dsh-file-attachments(安装链契约)。
Features
| Capability | Summary |
|---|---|
| Intake | Drop anywhere in the window, paste, or use the attach button |
| Auto-announcement | Announces each new file to the model before the next step |
| Typed cards | Composer cards show an icon and colour per detected family |
| Delivery receipt | Agent received → Agent reading… → Agent read under the user bubble |
| Readers | PDF, DOCX, XLSX, PPTX, legacy .doc/.xls, RTF, ODF, EPUB, ZIP/7z/RAR, HTML/XML/SVG, Markdown, plain text |
| Model tools | attachment_info / read_attachment / list_archive with bounded pagination |
| Redaction | Credential keys, PEM blocks, and YAML scalars are redacted before reaching the model |
Intake
Drop a file anywhere in the window, paste it, or use the attach button. Raster images continue on DSH's native image path; everything else enters this plugin. A "release to attach" overlay replaces the native images-only blocker.
Auto-announcement
The message wire carries no visible file marker, so a text-only model can silently ignore uploads. Before each step the plugin appends one plugin-source line announcing every unannounced file with its detected type; the model then calls attachment_info → read_attachment / list_archive and combines the file content with the user's words. Injected through the official agent/pre-step waterfall (the serial chain before request derivation) and rendered by the official UI as an inject line.
Typed cards
Composer cards show a distinct icon and colour per detected family (text / document / spreadsheet / presentation / archive / …).
Delivery receipt
A line under the user's bubble reports real events: Agent received → Agent reading… → Agent read. The final stage appears only after the model calls the read tools.
Readers
Text/config/source files, Markdown, HTML/XML/SVG markup (detected and read as text), PDF, DOCX, XLSX (sheet + A1-range reads), PPTX, legacy .doc/.xls, RTF, ODF, EPUB, and ZIP/7z/RAR/EPUB listing with safe single-entry extraction (CJK entry names supported).
Model tools
attachment_info, read_attachment, list_archive — bounded page/range/cursor/paragraph pagination, archives listed before extraction.
Redaction
Credential keys, private-key PEM blocks, and YAML block scalars are redacted line-wise before any byte reaches the model (aws_secret_access_key, apiKey, set-cookie included; public_key, monkey untouched).
Architecture
composer drop / paste / button (client)
│ full-window takeover, generic files only — images stay native
▼
POST /api/dsh-file-attachments/v1/files (Origin + session-existence verified, fail-closed)
▼
content-addressed store (sha256 blob, atomic rename publish, immutable)
├─ detection (magic bytes + ZIP metadata, worker-isolated)
├─ refs + batch indexes (durable metadata, path-injection-validated)
└─ read path: session ownership check → bounded worker parse → redaction
▼
model view:
├─ agent/pre-step announcement line (plugin source, type-tagged)
└─ attachment_info / read_attachment / list_archive tools
Install
One-command install
# macOS / Linux (from the repository root)
node scripts/install.mjs
# or double-click: install.command (Windows: install.bat)
The installer builds host + client bundles, symlinks the package into the shared profile directory ($DSH_HOME/profiles/node_modules, so every profile — web, CLI, headless — resolves it), and appends one row to $DSH_HOME/cordis.patch.yml (a backup is written first). It is idempotent: running it again is safe. Uninstall: node scripts/install.mjs --uninstall or uninstall.bat / uninstall.command.
Prebuilt bundle: download dsh-file-attachments-<version>.zip from Releases, verify it against SHA256SUMS.txt, extract, and double-click the installer. No git, build step, or package manager required.
Windows users: see docs/WINDOWS-INSTALL.zh.md for the full walkthrough (prerequisites, install.ps1, manual steps, macOS comparison table, and FAQ). Requires Node.js ≥ 20, PowerShell 5.1+, and for archives either the built-in tar.exe (Windows 10 1803+, bsdtar) or Git for Windows; dependencies via npm or pnpm.
Per-end deployment
| End | Steps | What you get |
|---|---|---|
Web (dsh web) | run the installer → restart dsh web → hard-refresh (Cmd+Shift+R / Ctrl+Shift+R) | attach button, full-window drop, typed cards, delivery receipt, auto-announcement |
| CLI / headless | nothing further — the host half mounts through the shared profile | attachment_info / read_attachment / list_archive tools; attachments uploaded through the API (or by a Web session) are readable, and announcements fire on the same official events |
Configuration: the storage root defaults to $DSH_HOME/file-attachments; override with config.root on the plugin row in cordis.patch.yml.
Safety model
- Session ownership — every read verifies
ownerSessionIdand that the session still exists (sessionQuery.readSession, fail-closed, 10 s TTL cache). - Origin — uploads require a trusted
Origin(exact match or loopback-equivalent127.0.0.1/localhost/[::1]on the same port); cross-site reads and deletes are rejected while origin-less local clients stay allowed. - Parsing isolation — every parse runs in a worker thread under
maxOldGenerationSizeMb: 512and a hard timeout that terminates hung parsers (word-extractor/xlsxcannot be aborted otherwise). - Archive safety — paths are normalized and whitelisted; extraction writes to stdout only (no zip-slip surface); hostile entries are skipped during listing and rejected during extraction; decompressed output is capped at 256 MB.
- Redaction — line-wise credential/private-key redaction before the model (see Features).
- Limits — 25 MB per file (100 MB archives), 10 files / 50 MB per message, 256 KB / 2 000 lines per read, 15 s parser timeout, 10 s archive timeout.
- Announcement degradation — the pre-step listener never throws into the loop; any failure falls through unchanged, and a strengthened
systemPromptsection (order: 70) remains as the fallback trigger.
Official API conformance
Every seam this plugin uses is a public DeepSeek Harness contract — no monkey-patching, no core changes, harness upgrades unaffected:
| Used | Purpose | Official reference |
|---|---|---|
agent/pre-step waterfall | append the announcement to the authoritative enter batch | docs/subsystems/core.md ("the only serial listener chain before request derivation") |
MessageSourceMap.plugin | announcement source (kind: 'plugin'), rendered by the official UI as an inject line | @deepseek-ai/dsh-llm type declarations |
systemPrompt.section | fallback trigger instructions | @deepseek-ai/dsh-system-prompt |
tools.register / defineTool | the three model tools | @deepseek-ai/dsh-tools |
webServer.register | HTTP upload/metadata/delete routes | @deepseek-ai/dsh-host-webserver |
sessionQuery.readSession | session existence verification | @deepseek-ai/dsh-session-query |
| Content-addressed attachment design | storage philosophy (immutable sha256 objects) | @deepseek-ai/dsh-attachment |
Compatibility
| macOS | Windows | Linux | |
|---|---|---|---|
| Symlink / junction | symlink | symlink, junction fallback | symlink |
| Archive reader | system bsdtar | built-in tar.exe (Windows 10+) | system tar |
| CI | ubuntu / macos / windows × Node 22 / 24 — build + typecheck + 168 tests + installer smoke |
Development
npm install
npm run build # host + client bundles
npm run typecheck
npm test # 168 tests (vitest)
node scripts/smoke.mjs http://127.0.0.1:3080 <file> # live smoke against a running web
Attribution and license
- MIT — see LICENSE.
- Dependency attributions — THIRD_PARTY_NOTICES.md (
@keep-lts/xlsx,cfb,fflate,file-type,pdfjs-dist,word-extractor,react, build/test tooling). - Derived code — the user-message renderer used by the delivery receipt is ported from DeepSeek Harness's own
dsh-client-ui-conversation(MIT); the installer's junction-fallback strategy follows@linxin666/dsh-client-ui-skin-center(Apache-2.0). Both are credited in the respective source files and notices. - Design reference — the content-addressed storage model follows the official
@deepseek-ai/dsh-attachmentpackage (see conformance table above).