跳到主要内容

dsh-essential-tools

已验证

dsh-essential-tools · v2.9.4 · MIT · Web 界面

DSH 永久插件:与原生 DSH 风格高度一体、界面简洁的 LVAL 工程工具(编译/运行/代码查看/程序版本快照回退)+ 对话树(会话内分支/编辑/重新生成)+ 消息小版本 + DET 管理器 + 全局插件控制(全局插件管理) + MDA 分层(跨对话记忆 CDM / 临时对话 TCT / 模型合作) + 网络权限(5 档)/ 混合模型(MMS)/ 安全审计 + 浏览器控制(扩展) + DLT 集成(自带文档/执行/环境引擎,DET 开启时 DLT 整体停摆,含草图导入按钮)。Permanent Dee

安装

dsh plugin add dsh-essential-tools

用 dsh --profile default --dump-config 确认 layer 已生效 —— 参见安装指南。

源码

标签

说明文档

DSHEssentialTools

DET · dsh-essential-tools — the plugin that turns DeepSeek Harness into a usable engineering cockpit.
Let the AI drive your own logged-in browser · Branch, edit and retry any conversation · Run / snapshot / roll back real projects · Manage every plugin and every token you spend

MIT DSH type version tools platform

Quick start · 用户指导手册 · Changelog · Security · 功能总览


😤 The problem

If you hack on real code with an AI agent in the loop, you already know these four:

Pain What everyone does today What DET does
🧠 One wrong turn kills the thread — you want to re-ask from message #12, not start over Copy-paste into a fresh chat and lose all context VTD: edit / retry any message → a real hidden branch, switch back with <N>, and the workspace code rolls back with you
🌐 The AI can't see or click what you see — your logged-in dashboards, internal pages, file:// pages Screenshot, paste, hand-hold, retry DET Browser Extension: the model drives your already-logged-in Edge/Chrome through a localhost bridge you gate yourself (off / read / write / on)
🛠 Build-run-look-check is a manual loop Alt-Tab to the IDE and terminal 40 times an hour Right toolbar: ▶ run · 🗎 file tree + preview/edit · 🕘 program snapshots & rollback
💸 You have no idea what a session costs Find out at the end of the month Live balance + per-model price + per-turn cost + peak/off-peak estimate in the corner

DET (the plugin dsh-essential-tools) is the permanent DeepSeek Harness plugin that fixes all four — and adds a full plugin manager, conversation memory layer and security audit on top. It lives in your web profile, survives restarts, and shows up in Settings → Plugin inventory.

🎁 Zero config: without project paths, every conversation/token/plugin feature works out of the box. Add paths only when you want ▶ run.


✨ Feature tour

🌐 Browser control — the headline act

A real MV3 extension (Edge / Chrome) connects to the DSH host over a localhost-only WebSocket (127.0.0.1:9123) so the model can operate the browser you are already logged into — internal tools, dashboards, webmail, local file:// pages, whatever you have open.

  • Read: read_text · read_dom · screenshot · get_url · get_title
  • Write: navigate · click · fill · run
  • Six model tools: det_browser plus web_human_search (search like a human), web_insite_search (find it in your open tabs), web_act, web_inspect, web_focus
  • You hold the permission dial — the four-position switch lives in the extension popup, and DSH can only read it:
    Mode Model can
    关闭 off nothing at all
    只读 read read pages (text / DOM / URL / screenshot)
    只写 write navigate, click, fill, run — but no page content comes back
    启用 on full read + write
  • Double gate: the host refuses to even start the bridge unless network permission = tier 4 (use your browser); below that, det_browser is blocked before it reaches your browser.
  • Approval aware: outside Full access mode, browser actions go through DSH's tools/pre-execute approval flow; Full access is exempt.
  • Hardened: origin-checked handshake, injection via fixed function + arguments (never new Function), only the tabId DSH names, and the bridge keeps only the newest extension connection so stale MV3 service workers can't eat your commands.

➡️ Setup: browser-extension/README.md · deep dive in the 指导手册

🌲 VTD — virtual conversation tree

Stop throwing conversations away.

  • Edit / retry any user message → creates a genuine branch child session (origin: vtd-fork, hidden from the sidebar). Your original thread is untouched.
  • <N> fork selector on messages: hop between branches, and DET snapshots the workspace and restores the target branch's code for you (message micro-versions).
  • Streaming branch view: no fixed 4 s polling — the host signals generating, so the tab refreshes at ≈700 ms while tokens flow and ≈2.5 s when idle, auto-scrolls, and shows “正在生成…”.
  • Product-native rendering: real user bubbles only, context injections folded away, tool calls/results as cards, reasoning folded, fine-grained Markdown.
  • Message micro-versions: baseline / edit / retry / auto-switch recorded automatically and restorable.

🖥 Right-hand toolbar — project work without leaving the chat

Tool What it does
▶ Run Finds your entry point (main/entry/run .py/.cpp, or .sln/.slnx), builds with MSBuild, launches the program
🗎 Files Workspace file tree (collapsible, counts, indent), click to preview or edit in a modal
🕘 Versions Program-level snapshots: manual snapshot / roll back (auto-backup first) / delete — code files only
🧩 Plugins Jump into the plugin manager / this conversation's plugin switches
🛡 Security Audit log and security switches

MSBuild missing or misconfigured? Auto-discovery: vswhere → common VS install dirs → PATH, cached for 60 s, and lvalInfo reports the path actually in use.

🧩 Global plugin manager — one library, five levels, two install paths

  • Library across the whole process (storage domain dsh_global_plugins, survives restarts). Every plugin carries a level: 全局启用 always · 对话AI可自行决定启用 ai-auto · 对话内AI需审批启用 ai-approve · 不再会有新启用 frozen · 全局禁用 disabled
  • Bring plugins in: ① promote a live dynamic Cordis plugin from any running conversation; ② search the store (GitHub / marketplace / leaderboard / radar) with a cached AI summary; ③ paste a manifest URL.
  • From GitHub two ways: ① direct download (det_global_plugin_github_direct); ② AI reads the source and rewrites an equivalent, safer version (det_global_plugin_github_rebuild → det_global_plugin_github_save) — the third-party code is never executed, and both paths return virus/vuln warnings.
  • Resident plugins (e.g. dbs) get a clean enable/disable switch that hot-unloads/reloads through the loader, persists across restarts, and offers a 刷新前端 button (det_global_plugin_set_enabled).
  • Intent and fact are stored in different fields (v2.9.4). This used to be one field doing two jobs — globallyEnabled was both "your intent" and the master switch's bookkeeping, and gpList additionally wrote the loader's actual state back into it. Three writers overwriting each other is why "disable a resident plugin → it comes back after a restart" kept happening. Now:
    • the intent lives in its own authoritative field enabledIntent (optional in the storage schema, so old records still read; they are inferred once and migrated on sight);
    • the level 全局禁用 disabled is a hard veto (by definition it "refuses any enable"), then enabledIntent decides, and only legacy records fall back to level (which outranks the bookkeeping-polluted globallyEnabled);
    • gpList never writes fact back into intent. The loader's truth is only reported (actualEnabled / fiberPhase / stateMismatch), never persisted over your choice;
    • the binary switch no longer destroys your level — it used to rewrite ai-approve / frozen into always / disabled, which lost your five-level setting for good. "Installed or not" and "who may enable it" are two dimensions and are now stored separately;
    • the boot-guard / master-switch pause writes a real intent (enabledIntent = false), so a safety disable is no longer silently undone by the next apply.
  • Intent vs. fact, told apart. Entries are matched by any of their names (id, nested id, options.id, options.name — the profile's insert row carries two different names, e.g. id: dbs vs name: 'dsh-bgm-service', which used to make "disable" silently impossible). When intent and fact disagree the card says so instead of rewriting your intent, and 立即应用禁用状态 (gpApplyPermanentStates) pushes the intent down to the loader without a restart. Persisted state is re-applied on boot by a delayed kick + the first session — deliberately not loader.await(), which deadlocks when called from a plugin's own apply.
  • Disabling a plugin never reloads your whole page (v2.9.4). It used to call window.location.reload() unconditionally — even when the host had just reported applyError, i.e. nothing had been applied at all. That is what "turning a plugin off messed up DET's own features and UI" looked like: it reset DET's interface, the settings page you were on, and any unsaved input. Now the panel reports what the loader actually did (loader 已实时禁用 / 实时未生效:<reason>) and leaves the refresh to a 刷新前端 button you choose to press.
  • Boot Guard: bootFailLimit consecutive boot failures (default 2) auto-disable all global plugins so a bad plugin can never brick your startup.
  • In-conversation tools: det_global_plugin_list/enable/disable/scan_installed/import_installed/set_enabled/github_direct/github_rebuild/github_save/store_search.

🧭 MDA · CDM · TCT — memory and cost control

  • MDA (Mixing Dialogue Agent) layering: native / workspace group / model group (collapsible tree from the sidebar 🔀 MDA 分组).
  • CDM (CrossDialogueMemory): cdm_list / cdm_search (workspace-scoped by default, cross=true to escalate) / cdm_read — retrieve what you already worked out in another conversation.
  • TCT (Temp Chat Tool): det_tct — one prompt, optional preset (review / summary / format / brainstorm), typed permissions, one feedback string, session destroyed, nothing persisted.
  • Model collaboration (model group): mda_card writes a model's profile with TCT; mda_activate dispatches work to another model (⚠ token-hungry, off by default); mda_create_no_workspace_agent spins up a workspace-less agent.

📊 DeepSeek balance · pricing · per-turn cost

  • Corner status card: balance from api.deepseek.com/user/balance, refreshed every 5 s, expandable to multi-currency detail and days-until-empty estimate.
  • Per-model price chip parsed from the official pricing page (CNY page first, USD fallback), switching between peak / off-peak (Beijing time Mon–Fri 09:00–12:00 & 14:00–18:00 = peak), cached 6 h with last-good fallback.
  • Per-turn cost for the conversation you're in.
  • Key handling: resolved by the host only (config → DSH credential seam → env var), lives only in a request header — never written to disk, never logged, never sent to the client. No MITM proxy, no key ledger, no telemetry. Network touches only api.deepseek.com and api-docs.deepseek.com.

🔧 DLT integration — DLT's features, hosted by DET

DET can take over DLT (dsh-light-tool) completely, so you can run one plugin instead of two. It ships its own copy of DLT's engine (lib/dlt-engine.js + py/), registers the same six model tools — dlt_env, dlt_run, dlt_build, dlt_doc_read, dlt_doc_write, dlt_doc_convert — injects the same environment/doc system prompt, and renders the same three UI pieces itself:

Piece Slot Endpoint
Per-turn cost chip (本轮 ¥x.xx, expandable token detail) conversation.chat.turnTail dltCost / dltPricingInfo
Right-sidebar Word / Excel / CSV preview documentPreviews + sidebar.right.tab.document dltOffice
Sketch button next to the composer's + (draw now → image attachment, or PNG path in a text model) conversation.input.left dltDraftSupport / dltDraftSave
  • While DET's master switch is on it declares the takeover (subordinateGrants().grants.dlt = true). DLT then steps aside entirely: it registers none of the six tools and no prompt section, and drops its cost chip, balance card, sketch button, previews and its own settings page. The single control surface becomes Settings → DET 管理器 → DLT 集成 (six per-piece switches: dltDocs / dltPreview / dltEnv / dltRun / dltCost / dltDraft).
  • Reversible, no restart: turn DET's master switch off (or set det.subordinate.suppress.dlt = false) and DLT takes its own features back within ~5 s — DLT re-checks the declaration on boot and every 5 s, DET re-checks on every feature change.
  • Not a loader pause. DLT is not in DET's global-plugin library and is never hot-unloaded the way library-bound plugins are; the takeover is a functional declaration over ctx.get('dshEssentialTools').subordinateGrants(). DLT's own master switch and switch.json keep working, so flipping DET off restores exactly the modules you had enabled there.
  • Balance and pricing stay DET's own (corner status card + price chip) — DLT's balance card was always the duplicate one; that's why there is no second balance card in the integration.
  • Sketch import in one line: the composer's ✏ button (next to +) opens a canvas; 导入 gets the drawing into the message as an image — if the current model doesn't declare image input (dltDraftSupport), DET asks the host which models do (dltVisionRoutes), switches the session to one of them, attaches the PNG as an image draft, and offers a one-click 切回 . No tool call, no "go read the file yourself". Only when no vision model exists at all does it fall back to saving <workspace>/.dsh-drafts/sketch-*.png and pasting the relative path — and it says so.
  • The judgement is genuinely live (v2.9.4). Two things used to be stale, and both are fixed: the model now comes from the session projection's pending ?? lastUsed (what you just picked in the model picker, not the last request that already ran), and the client re-asks the host on every 导入 instead of reusing the answer captured when the canvas opened. So switching models with the canvas open is seen immediately. "Will this model take an image?" is decided only by the model table's inputModalities; if it can't be read, the answer is conservatively "no" (save the PNG) rather than failing at send time.
  • Sketch is also its own package: dsh-sketch-draft (v2.9.4). Sketch needs nothing from DET — only the host's llm / sessions / sessionProjections / attachments — so it now ships standalone and DET's copy is optional. Exactly one pencil button is ever mounted: DET declares subordinateGrants().grants.sketchDraft = true while its dltDraft is on, and the standalone package reads that and steps aside (it registers no slot at all). Turn off dltDraft here (or DET's master switch) and the standalone package takes over automatically — no restart.

🛡 Security audit + network permission tiers

  • Optional pre-execution audit: every tool call gets one independent model review; high-risk calls are deny-ed and logged (secCmdAudit, secPromptDefense). ⚠ It costs latency and tokens per call.

  • Network permission — 5 tiers (inline dropdown in the input row, persisted as det.webperm):

    Tier key Meaning
    禁用网络 off no network at all
    官方API搜索 api DeepSeek official API only (balance/pricing need ≥1)
    搜索API搜索 search search APIs; generic fetch needs ≥2
    静默浏览器仿真 silent headless browser simulation (read pages)
    使用用户浏览器 browser drives your real browser (det_browser needs exactly this)

🎛 Master switch — stock DSH in one click

Settings → DET 管理器 opens with a master switch (det.features.master, on by default) above the per-feature switches.

  • Off = fully native. DET keeps exactly two things: this manager page and the switch itself. Everything else is unloaded — the ▶🗎🕘🧩🛡 toolbar, the corner balance/cost/MMS card, the network-permission control, the VTD tab and message actions, the MDA sidebar overlay, the Global plugins and MDA settings entries, all det_* / web_* model tools and system-prompt injections, the security-audit hook, the local browser bridge, and the sidebar-registry self-check. MDA grouping resets to native.
  • DET-managed plugins are stopped too — and only those. The scope is the Global plugins library: records bound to a loader entry (via scan installed plugins → import, which writes moduleName) that were actually on. Resident ones are hot-unloaded through the loader, dynamic ones have their per-session instances stopped. Plugins that are not in the library — e.g. dlt, which has its own master switch on its own settings page — belong to themselves and are never touched. (That is a loader-level statement: DLT's features can still be hosted by DET through the separate subordinateGrants() declaration — see DLT integration above — which suppresses DLT from the inside without pausing its fiber.) Container/builtin entries (cordis:include, which owns the whole cordis.yml subtree) are never disabled, and neither are framework packages (@deepseek-ai/*) or DET itself.
  • Stopping is verified, not assumed. Every target is re-read from the loader and retried once; anything that refused is reported on the manager page (⚠ 未能停用 + reason) instead of being claimed stopped. Library records that claim to be resident but are not bound to any loader entry are listed separately as not stopped (det.master.unmanaged) with a pointer to the import action. The stopped set is snapshotted in det.master.paused (with an applied flag) and restored when the switch goes back on; a resident plugin's browser half only disappears after a page refresh, so the panel offers a 刷新前端 button rather than reloading the page behind your back.
  • On restores all of it — including re-loading the plugins the master switch stopped (resident plugins re-mount, session enable-records are restored so gpSync brings them back). Per-feature switches keep their saved values. The switch applies instantly and is persisted — no restart, and turning it off is reversible because every registration is held as a disposer.
  • With the master switch on, the per-feature switches still work as before: turn plugin manager off and all global plugins are disabled; turn MDA off and grouping returns to native.

✅ Indicators follow reality

The Global plugins list never guesses:

  • Resident plugins are reported from the host loader (the only thing that decides whether a plugin is mounted), so 启用/禁用 is the real state. If DET's stored intent disagrees with the loader, DET corrects the record, marks the card, and also shows the fiber phase (failed / still loading).
  • Session state distinguishes a saved enable-record from actually running: “本会话运行中” only when the run is live, otherwise “已启用记录 · 未运行” or “本会话未打开 · 打开后恢复”. The model-facing det_global_plugin_list reports the same actual state.

🚀 Quick start (60 seconds)

Requirements: Windows, DeepSeek Harness 0.1.1-rc.2+, PowerShell, a web profile (auto-created on first dsh web).

# Option A — installer from the clone (recommended: installs + registers the plugin)
.\install.ps1 -Profile web

# Option B — manual, exactly equivalent
dsh plugin --profile web add dsh-essential-tools

# Option C — straight from GitHub (no npm needed)
dsh plugin --profile web add github:LLYlab/DSHEssentialTools

Then restart DSH. dsh-essential-tools appears under Settings → Plugin inventory, with its own DET 管理器 section.

The package declares dsh.bundle.patch (→ cordis.patch.yml), so any of the three commands above registers the plugin in the profile's bundle layer by itself — you do not need to hand-edit cordis.patch.yml. The manual insert below is only for mounting straight from a source checkout.

Registering by hand / adding project paths / config reference

Append to %USERPROFILE%\.dsh\profiles\web\cordis.patch.yml:

- insert:
    - id: dsh-essential-tools
      name: 'dsh-essential-tools'
      # config:                      # optional — only needed for the ▶ run / 🗎 file / 🕘 version tools
      #   lvalRoot: 'C:\path\to\project'
      #   srcDir: 'C:\path\to\project\src'
      #   solution: 'C:\path\to\project\App.slnx'
      #   msbuild: 'C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe'  # optional, auto-discovered
      #   configuration: 'Debug'
      #   platform: 'x64'
      #   rollbackTargetDefault: 'minor'
      #   bootFailLimit: 2           # consecutive boot failures before auto-disabling all global plugins

Leave config empty and everything except the project tools still works.

Optional: enable the browser extension
  1. edge://extensions (or chrome://extensions) → enable Developer mode → Load unpacked → pick browser-extension/.
  2. In DET, set network permission = 使用用户浏览器 (tier 4).
  3. Click the extension icon and pick a mode — start with 只读 and move up only when you need it.
  4. Ask the model to work on your open tab; watch the 浏览器控制 status block in DET.
v1 dynamic loading (development only)

cordis_define with plugin/host.js + plugin/client.js, then cordis_run. v2 (npm) is the supported path.


📚 Documentation

Doc What's inside
docs/GUIDE.md 🧑‍🏫 用户指导手册 — install, first-run walkthrough, every feature step by step, permission model, FAQ, troubleshooting, uninstall
docs/DET功能.md Feature inventory: 25 model tools, 75 endpoints, 8 UI slots, storage domains, permission tiers
docs/DET修改.md Change/dev log: what was changed and why
docs/DET运行思路.md Architecture and runtime flow
docs/SECURITY.md Security design, five-dimension review, known boundaries, mitigations
CHANGELOG.md Release history
ARCHITECTURE.md Implementation-level architecture

🔒 Security in one paragraph

Plugin code runs with the real permissions of the DSH process — that is not a sandbox, so only enable code you trust (DET says this loudly before every install). What DET does add: SSRF protection on every host-side fetch (http/https only, no credentials in URL, private/loopback/metadata addresses rejected, DNS re-checked after resolution to stop rebinding, manual redirects re-validated per hop, 5-hop cap), 15 s timeouts and Content-Length pre-checks, suspicious-code scanning with commit-SHA provenance on store installs, quoted cmd.exe argv to kill argument-splitting/injection, browser bridge bound to 127.0.0.1 with origin-checked handshake and an extension-side gate the host cannot override, approval routing for browser actions outside Full access, and a credential path that never persists your API key. The blacklist scanner is a hint, not a boundary; the real boundary is your approval.

🤝 Contributing

Issues and PRs welcome at LLYlab/DSHEssentialTools. The browser extension must be tested by loading it unpacked; the npm package never ships it (.npmignore).

License

MIT © 2026 L2959159224