dsh-plugin-verified-search
f0909172434
Verified current-source search workflow for DeepSeek Harness
PROJECT TOPICS
PROJECT README
English | 中文
A DeepSeek Harness web plugin that makes the composer accept many more attachment formats, Codex-style. Zero core-package changes: a pure plugin that reuses the harness-native image draft rail, upload limits, history rendering and model request pipeline.
| File | Handling | Destination |
|---|---|---|
| PNG / JPEG / WebP / GIF | native pipeline (plugin not involved) | image draft rail (native) |
| PDF (with text layer) | text-layer extraction (≤40 pages via the pymupdf4llm high-fidelity engine; larger/unavailable falls back to pdfjs) | full text on a document card (merged on send); over-limit → workspace spill + index card |
| PDF (scanned / no text layer) | tesseract.js OCR (accepted only at confidence ≥45), falls back to page images | OCR success → text channel; failure → image draft rail (vision models only) |
| Word (.docx) / Excel (.xlsx) / PPT (.pptx) | text extraction — docx via mammoth HTML → turndown, tables kept as Markdown pipe tables | document card (merged on send); over-limit → spill + index card |
| Legacy .doc / .xls / .ppt | LibreOffice headless → docx/xlsx/pptx → standard Office pipeline (needs soffice; clear error when absent) |
document card (merged on send) |
| epub / odt / rtf | pandoc → Markdown (probe on PATH); epub/odt fall back to jszip+turndown without pandoc; rtf requires pandoc | document card (merged on send) |
| TIFF (.tiff/.tif) | sharp (libvips) → PNG pages (multi-page, ≤20) | native image draft rail |
| txt / md / json / code | read in the browser (UTF-8, GB18030 fallback) | document card (merged on send); over-limit → spill + index card |
| BMP / ICO / AVIF / SVG etc. | browser decode → canvas → PNG | native image draft rail |
| iWork / audio-video / archives | — (not yet supported; explicit notice, skipped) | — |
Text-like attachments that are dragged in or picked are not stuffed into the input
box: their content mounts as a document card above the composer (file name +
character count + full-text/index label, individually removable), while images keep
flowing into the native image draft rail. You type normally, and at the moment of
sending the plugin merges the card content into the message (with
[attachment: <file name>] provenance markers) before the native submit — your prompt
always stays on top and no content is lost:
Text beyond 80k characters and long multi-page PDFs are not stuffed into the message. Instead:
.dsh-attachments/<sha-16>/
(content-addressed, reused on re-drop, auto-cleaned after ~7 days of no access):doc.md — PDF text layer assembled per page (leading <!-- pN --> markers),
Office-extracted text, long text as-is (long JSON is prettified to doc.json);pages/pNN.png — rendered page images (≤100 pages, for vision models via
read_image; rendered lazily, only when the index-card path needs them);manifest.json — source, page/line/char counts, engine, full source SHA-256
and the converter-policy fingerprint (engine/OCR/doc-server switches invalidate
the cache automatically);INDEX.md (cache root) — the aggregated list of every spilled document in this
workspace.read tool (offset/limit, line numbers
as coordinates) — full summaries read through (no dropped tails), targeted lookups
jump by outline; missing content is an explicit tool failure, never silent loss.Design rationale and evidence: docs/design-longdoc.md; comparison with similar work:
docs/alternatives.md. Upgrades for current limitations (researched GitHub solutions
and v0.6 roadmap): docs/upgrade-v6.md.
auto (default) → the venv's pymupdf4llm for ≤40 pages
(high-fidelity tables/headings); pdfjs (seconds) for larger documents or when the
venv is missing. Env: DSH_ATTACH_ENGINE=auto|python|builtin.vendor/tessdata/).
Confidence below 45 falls back to page images with a clear reason. Env:
DSH_ATTACH_OCR=auto|baidu|tesseract-js|off (see below).soffice, probed on PATH plus
the usual Windows install locations) converts to the modern OOXML format first, then
the standard Office pipeline runs. Each run uses an isolated UserInstallation
profile to avoid lock conflicts.get_toc / pdfjs getOutline) now feed the index
card's outline first; the font-size heuristic is only the fallback. Empty-bookmark
PDFs are unaffected.BAIDU_OCR_API_KEY / BAIDU_OCR_SECRET (console → 文字识别 → create app);DSH_ATTACH_OCR=auto|baidu|tesseract-js|off (auto = Baidu when credentials
exist, else local tesseract.js);DSH_ATTACH_OCR_ACCURATE=1 for the high-accuracy endpoint (separate free
quota).
Quota exhausted / API failure → automatic fallback to local tesseract.js with a
note; forced baidu mode reports the reason instead.DSH_ATTACH_VLM_BASE /
DSH_ATTACH_VLM_MODEL (+ optional DSH_ATTACH_VLM_KEY) point at any
OpenAI-compatible vision endpoint (olmOCR-2, GLM-4V, Qwen-VL…). Pages are
transcribed one by one via chat/completions. OCR chain: Baidu → VLM →
tesseract.js (or force with DSH_ATTACH_OCR=vlm).get_drawings) — text-heavy manuals
skip the slow high-fidelity pass and go straight to the fast pdfjs engine, while
table/graphic-heavy documents still get pymupdf4llm. ≤40 pages are unchanged.DSH_ATTACH_DOC_SERVER=<base URL>
points at a parser service (PP-StructureV3 paddleocr serve, MinerU, or any
shim). Contract: POST {base}/convert with multipart field file →
{ "ok": true, "markdown": "..." }. When configured, PDFs go to the server
first; any failure falls through to the local engine chain.GET /api/attach-formats/cache + POST .../cache/delete + POST .../cache/clear.GET /api/attach-formats/resolve asks the host to confirm a
same-source file by name + size + full SHA-256 (bounded ~2.5s walk skipping
dependency dirs). A hit mounts a 📎 reference card — the content is not
uploaded (only the name, size and hash are sent); the model reads the path with
its read tool. A miss falls back to the normal upload pipeline. Files over 16MB
are rejected outright (no zero-copy attempt).contextPressure projection
(model context window × current usage); the full-text merge limit becomes
min(80k chars, headroom × 1.5) — when headroom is short, the card automatically turns
into an index card with a status-bar note, so merged content can never blow up the
context and get silently truncated by the API. A missing projection falls back to the
fixed 80k threshold./attach command (composer slash menu, host-registered):/attach list — list the spilled documents in this workspace (id/name/size/engine);/attach full <id|name> — merge the full text into model context as a next-step
message (takes effect on the next message, current turn untouched); 300k-char cap
with an explicit truncation notice — never silent loss. read still works afterwards
for line-precise lookup.conversation.input.left), opens a
multi-select file picker whose accept list covers every format in the table above.Native image drag/paste stays on the harness built-in pipeline; when a single drop mixes other formats in, the plugin takes over the whole batch (converts first, then hands the produced images back to the built-in draft rail as a "synthetic drop").
dsh-attachment-formats/
├── lib/
│ ├── index.js # host half: POST /api/attach-formats/convert + engine routing
│ ├── client.js # browser half: button/drop interception/synthetic drop/text injection/status bar
│ ├── cache.js # workspace .dsh-attachments spill/manifest/INDEX.md/cleanup
│ ├── py/pymupdf4llm_convert.py # venv high-fidelity engine (subprocess call)
│ └── convert/
│ ├── util.js # magic-byte sniffing (pdf/tiff/OLE/rtf/zip), base64, truncation
│ ├── provider.js # engine/binary detection (venv python, pandoc, LibreOffice) + subprocess bridges
│ ├── pdftext.js # pdfjs text-layer extraction: line assembly/header-footer dedup/bookmark TOC
│ ├── outline.js # md heading outline, JSON first-level key tree
│ ├── ocr.js # tesseract.js OCR (traineddata download cache/confidence)
│ ├── pdf.js # pdfjs-dist + @napi-rs/canvas → PNG/JPEG pages
│ ├── docx.js # mammoth HTML → turndown+GFM → Markdown (tables preserved)
│ ├── xlsx.js # exceljs → tab-separated text
│ ├── pptx.js # jszip + a:t text runs → per-slide text
│ ├── tiff.js # sharp (libvips) → PNG pages
│ ├── pandoc.js # pandoc → Markdown + epub/odt zip fallback
│ └── libreoffice.js # legacy .doc/.xls/.ppt → modern OOXML
├── .venv/ # (optional) pymupdf4llm engine (generated by setup, not committed)
├── vendor/tessdata/ # OCR language-data cache (downloaded on first use, not committed)
├── docs/ # design-longdoc.md / alternatives.md / upgrade-v6.md
├── scripts/smoke-*.mjs # five offline smoke suites (converters/router/client/OCR/P0)
└── cordis.patch.yml
cwd is read by the client from session state and
sent with the request (it decides where the spill lands).conversation.input.dock); success auto-hides after 6s, errors can be dismissed.From GitHub (recommended):
dsh plugin --profile web add github:linkingoscar/dsh-attachment-formats
Local development:
cd path\to\dsh-attachment-formats
npm install # host dependencies (first time)
# optional: high-fidelity PDF engine (pymupdf4llm, self-contained venv)
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install pymupdf4llm
npm run smoke # offline smoke tests (optional)
dsh plugin --profile web add link:path\to\dsh-attachment-formats
Restart dsh web (close the page → the desktop shortcut auto-restarts, or re-run
dsh web) and refresh the browser. OCR language data downloads automatically on the
first scanned-PDF recognition (≈24MB, cached in vendor/tessdata/, offline-ready
afterwards).
docs/upgrade-v6.md)..doc/.xls/.ppt require LibreOffice (soffice); rtf requires pandoc;
epub/odt work out of the box but pandoc (if installed) gives better fidelity.
Missing binaries produce clear, actionable errors — nothing is silently dropped.libreoffice+builtin
engine label.doc.* size), page images materialize lazily
when a cache hit downgrades to index mode, INDEX.md is fully rebuilt from live
manifests (no ghost rows, populated timestamps), legacy .doc/.xls/.ppt cache
keys use the original OLE bytes so hits skip LibreOffice, atomic manifest/INDEX
writes.read access via file atime, page images rendered
lazily (clean small PDFs skip rasterization), 2–16 MB text files reach the host
spill instead of being rejected, React key warnings eliminated, Node >=20, CI
actions upgraded to v7.useCallback
reference), converters no longer pre-truncate (never-silent-truncation restored
end-to-end), session-derived workspace authority for all routes, XLSX empty-column
coordinate fix, true conversion cache keyed by source hash, cache TTL based on last
access, verified merge into the composer draft; added ESLint, CI (Node 20/22) and
component-level smoke tests./attach list|full, adaptive merge limit,
pymupdf4llm/pdfjs engines, tesseract.js OCR.Apache-2.0 © 2026 linkingoscar
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。