DeepSeek Harness Plugin Hub

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

探索

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

社区

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

相关链接

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

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

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

dsh-escape-hatch

Escape Hatch

当某个插件启动失败时,让您的 DSH UI 继续运行——将致命启动屏降级为可关闭的警告。

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

npx -y @deepseek-ai/dsh plugin --profile web add github:MYCF711/dsh-escape-hatch#61ad704ee336869f942118eda849334fd0865c45
README兼容性版本

兼容性与来源证明

Escape Hatch 以 dsh-escape-hatch 发布,当前版本为 1.0.0。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

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

版本

1.0.0stable
2026/9/27

相关插件

正在加载相关插件…

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

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

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

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

认领这个 Plugin →
报告问题

相关插件

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

Web App@deepseek-ai/dsh-web-appdsh 浏览器界面捆绑包:位于 dsh-base 之上的 Web 补丁层,加上运行时粘合插件(提供前端 dist、Web 界面提示符、bash 运行时变量和 URL 行)Experimental Agent Team Web Profile@deepseek-ai/dsh-experimental-agent-team-web-profile用于 Agent Teams Remote 和 UI 插件的实验性 Web 配置层Ab Chartdsh-ab-chart带有绘制其调用的浏览器卡片的图表渲染工具,封装在一个可发布的插件包中Whale Widgetdsh-whale-widgetDSH Web 界面右下角的 DeepSeek 余额小鲸鱼挂件:余额/今日已用/峰谷定价、自定义泡泡点击序列(文本/余额/今日/峰谷/图片/随机语句与并列加权选择)、逐行样式与字体、悬浮快捷编辑、音效与每轮消耗、自定义角色/动图/音效、吸附与翻转自定义

README

dsh-escape-hatch

Keep your DSH UI alive when one plugin fails to boot.

简体中文 | English

dsh-escape-hatch is a DSH (DeepSeek Harness) plugin that turns a fatal "Failed to load plugins" boot screen into a dismissible corner warning — the rest of the UI keeps working.

One broken plugin should not take down the whole application.


The problem

When the DSH Web shell finishes booting, it runs a startup audit over every Loader entry. If any single entry failed to reach ACTIVE — an import error, a pending service, a failed fiber — it throws:

function VS(e, n) {
  const o = [];
  for (const s of e.loader.entries()) {
    if (s.fiber === void 0) { o.push(`${s.options.name}: import failed...`); continue; }
    // ...
  }
  if (o.length > 0)
    throw new Error(`web boot: ${o.length} entries did not activate\n${o.join("\n")}`);
}

That throw bubbles up to the boot runner's catch, which falls back to the boot overlay's page.fail():

catch (n) { console.error(n), e !== void 0 ? e(n) : this.page.fail(n instanceof Error ? n.message : String(n)) }

page.fail() → render() → card.replaceChildren(wordmark, failureBlock) replaces the entire startup card with "Failed to load plugins".

Worse: because the exception aborts the sequence, the application mount that follows —

await e.inject(["uiRenderer"], (o) => {
  o.effect(() => o.uiRenderer.mount(n), "web boot: application mount");
});

— never runs. One plugin's failure means no UI at all.

The asymmetry

The host side already knows better. In @deepseek-ai/dsh-app-boot there is an explicit required/optional distinction:

function activationDiagnostic(binName, failures) {
  // "Render optional-only warnings without changing startup policy."
}
function startupDiagnostic(binName, failures, required) { /* ... */ }

Optional plugin failures degrade to a warning and do not change the startup policy.

The browser shell has no such distinction. It treats every inactive entry as fatal. That gap is what this plugin closes.


How it works

dsh-escape-hatch takes over both ends of the failure path at runtime. It modifies no shipped artifact.

StepMechanism
CaptureWraps window.__ModuleLoader__.create to retain the shell's module system → Loader → each entry's fiber state
AssessAt the moment of the audit failure, reads live state: some entries ACTIVE and some entries inactive ⇒ degradable
InterceptWraps Element.prototype.replaceChildren, holding back the render at the instant the fatal screen would be painted
RecoverRe-mounts the application by replaying what the shell skipped: ctx.inject(['uiRenderer'], ui => ui.effect(() => ui.uiRenderer.mount(root)))
InformRenders a dismissible corner banner listing the inactive plugins plus the original error

The Recover step is essential and non-obvious. Suppressing the fatal render does not restore the mount that the exception skipped — without it, the page would sit on an empty card, which is worse than an honest failure screen.

Safety boundary

If every entry is inactive, the failure is platform-level: mounting anything yields an empty shell. In that case the plugin declares the failure non-degradable and leaves the original fatal screen untouched. It never masks a genuine platform failure.


Verified behavior

Everything below is reproducible with the bundled scripts. There is no "trust me" here.

npm run verify — 21 checks, all passing

scripts/verify-bundle.mjs (13/13) — validates the client bundle contract, run under a minimal DOM/browser stub:

  • invokes window.__ModuleLoader__.load({ id, factory }) exactly once
  • registration id equals the package name
  • the factory materializes and exports apply
  • apply(ctx) installs all interception layers
  • ctx.effect cleanup fully restores create and replaceChildren

scripts/verify-rescue.mjs (8/8) — end-to-end behavior against the real bundle plus a faithful replica of the shell's boot sequence:

ScenarioExpectedResult
A — no plugin, 1 plugin failsfatal screen shown, UI not mounted✅ failure reproduced
B — plugin active, 1 plugin failsfatal screen suppressed, banner shown, UI mounts✅ rescued
C — plugin active, all plugins failfatal screen preserved, no banner, UI not mounted✅ platform failure not masked

Scenario A exists to prove the test harness actually reproduces the real-world bug. A test that only passes is not evidence.


Context and token cost: zero

This is a pure client-side fault-tolerance layer. It has no interaction with the LLM context window:

  • no tools registered (tool definitions would consume system prompt)
  • no conversation event subscriptions, no history reads
  • no LLM calls, no background polling
  • dsh.client.inject is empty — not even plugin service dependencies
  • the client half runs in the browser; it cannot reach the prompt assembly pipeline

The host half is a single ctx.logger.info line. It registers no agent/pre-step hook, injects no system prompt, and writes no memory.

Runtime footprint: ~14 KB of JavaScript, plus two wrapped function references. The warning banner element is created only when a plugin actually fails; on a healthy boot, nothing is added to the DOM.


Installation

# 1. Place the package inside your profile's node_modules
$profile = "$env:APPDATA\in.dsh-plug.dsh-launcher\homes\<version>\profiles\web"
Copy-Item . "$profile\node_modules\dsh-escape-hatch" -Recurse
# 2. Register it in the profile's cordis.patch.yml
- insert:
    - id: escape-hatch
      name: 'dsh-escape-hatch'

Restart the DSH web server and refresh the page.

Activation requirement

The client bundle is discovered by the host scanning Loader entries for dsh.client declarations, and is composed into window.__DSH_BOOT__. It therefore requires a DSH web server restart plus a page refresh to load. immediately: true guarantees the plugin's apply runs before the boot sequence, which is what makes the interception possible.


Building

Self-authored plugins must ship a pre-bundled client. The runtime consumes exactly one form:

window.__ModuleLoader__.load({ id: "<package-name>", factory: (require) => exports });
npm run build
# or
node scripts/bundle-client.mjs --entry src/client.js --out lib/client.js

scripts/bundle-client.mjs is a zero-dependency, single-entry ESM bundler that emits this form. Key contract details:

  • The executed script only registers a factory. All side effects stay inside the closure, running at materialization time (lazy CJS model).
  • require is a synchronous table lookup: platform seed → materialized module → registered factory → otherwise throw. Dependencies must be inlined or declared in dsh.client.external.
  • import statements for local modules are rewritten to synchronous require calls; ESM exports are lowered onto module.exports.

The registration id is read from package.json, so the bundle stays in sync with the package name automatically.


Repository layout

.
├── cordis.patch.yml          # loader patch: inserts this plugin
├── package.json              # declares dsh.client (platform/immediately/inject)
├── src/
│   ├── client.js             # browser half — the actual interception
│   └── index.js              # host half — log line only
├── lib/                      # build output, committed (consumed at runtime)
│   ├── client.js             # bundled, __ModuleLoader__.load wrapper
│   └── index.js
└── scripts/
    ├── bundle-client.mjs     # zero-dependency client bundler
    ├── verify-bundle.mjs     # 13 contract checks
    └── verify-rescue.mjs     # 8 end-to-end behavior checks

lib/ is committed on purpose: the runtime consumes built artifacts directly, and DSH has no install-time build step for plugins.


Upstream note

Long term, this throw arguably belongs as a warning in DSH itself — optional plugin failures should not take down the UI, exactly as the host side already behaves. If a future DSH version relaxes the client-side audit, this plugin becomes unnecessary and can simply be removed.


Uninstall

  1. Remove the insert block from cordis.patch.yml
  2. Delete the node_modules/dsh-escape-hatch directory

No other state is modified.


License

MIT © 2026 MYCF711