<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Plugin System on Trevor's Blog</title><link>http://mings.in/en/tags/plugin-system/</link><description>Recent content in Plugin System on Trevor's Blog</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Thu, 29 Oct 2026 07:52:35 +0800</lastBuildDate><atom:link href="http://mings.in/en/tags/plugin-system/index.xml" rel="self" type="application/rss+xml"/><item><title>After Upgrading DeepSeek Harness, All My Plugins Got Disabled</title><link>http://mings.in/en/posts/dsh-022/</link><pubDate>Thu, 29 Oct 2026 07:52:35 +0800</pubDate><guid>http://mings.in/en/posts/dsh-022/</guid><description>The version on npm is not the newest one. You have to pin the version number to get it. Once installed, three plugins were rejected on the spot. Not a bug. An exact-version lock doing its job.</description><content:encoded><![CDATA[<p>Last night I upgraded DeepSeek Harness on my machine from 0.2.1-rc.2 to 0.2.2-alpha.2. Sixteen seconds.</p>
<p>One thing has to be said up front. This version is not the latest one on npm yet. A plain install still gives you 0.2.1-rc.2, so you have to pin the version number by hand.</p>
<pre tabindex="0"><code>npm install -g @deepseek-ai/dsh@0.2.2-alpha.2
</code></pre><p>Skip that version number and nothing I describe below will be visible to you.</p>
<p>Then I started it, and there was the problem. Three consecutive <code>disabling profile plugin row</code> lines in the log. Three of the things I had installed were taken away.</p>
<p>npm had also flagged something during the install: install scripts for five packages were blocked, including two native modules, node-pty and koffi. I stared at it for a while and started the app anyway. The web UI opened, the model list loaded, nothing seemed broken. I filed that away to look at later if something misbehaved.</p>
<h2 id="three-lines-hit-me-the-moment-it-booted">Three lines hit me the moment it booted</h2>
<p>Here is the log, verbatim.</p>
<pre tabindex="0"><code>dsh: disabling profile plugin row &#34;plugin-market&#34;:
  Plugin @deepseek-ai/dsh-plugin-market@0.2.1-rc.2
  is incompatible with dsh 0.2.2-alpha.2:
  requirements {&#34;@deepseek-ai/dsh-plugin-manager&#34;:&#34;0.2.1-rc.2&#34;,
                &#34;@deepseek-ai/dsh-settings&#34;:&#34;0.2.1-rc.2&#34;,
                &#34;@deepseek-ai/dsh-tools&#34;:&#34;0.2.1-rc.2&#34;,
                &#34;@deepseek-ai/dsh-typert-protocol&#34;:&#34;0.2.1-rc.2&#34;}
</code></pre><p>The three that got disabled were <code>ui-agent-team</code>, <code>plugin-market</code>, and <code>tool-plugin-market</code>. The last two are actually the same package, referenced by two different config rows.</p>
<p>The <code>requirements</code> block is the whole story. It reads <code>&quot;0.2.1-rc.2&quot;</code>. No caret, no tilde, four exact version numbers. And the four internal packages at runtime had already moved to 0.2.2-alpha.2.</p>
<p>Exact match fails, plugin gets rejected. DeepSeek Harness handles it cleanly: it does not load the plugin, it does not throw, it prints one warning line and keeps going.</p>
<p>That design is actually right. Letting a plugin that declared &ldquo;I was only tested against these versions&rdquo; run on a newer runtime makes the eventual crash far harder to diagnose. Rejection beats loading something broken.</p>
<p>The cost lands on the user though. You bump a version, things you installed disappear, and because it is a warning rather than an error, it is very easy to miss.</p>
<h2 id="the-fallout-spreads-to-the-plugin-market">The fallout spreads to the plugin market</h2>
<p>I had a custom plugin index configured. It lives at line 12 of <code>~/.dsh/profiles/web/cordis.patch.yml</code> and points at a local static server.</p>
<p>After the upgrade I opened Plugins → Discover and got one line in red:</p>
<blockquote>
<p>Could not read the index sources.</p>
</blockquote>
<p>There was a Retry button next to it. Clicking it returned the same line.</p>
<p><img loading="lazy" src="/images/dsh-022/05-discover-failed.png"></p>
<p>My first instinct was the static server. <code>lsof -iTCP:3489</code> came back empty, so the service had died at some point. I assumed that was the whole story, brought it back up, and <code>curl</code> returned 200 with a 3,764-byte index.json sitting there.</p>
<p>Back to the browser, clicked Retry. Same red line.</p>
<p>Same red line on the second try, and only then did it click: reading the index is itself an action performed by plugin-market. The index source was fine. The thing that reads it was gone. An empty page either way.</p>
<p>So there are two layers stacked here. On the surface the index source was down. Underneath, the plugin that reads it had been disabled.</p>
<p>I also tripped over a hole of my own making, worth noting. The <code>serve.py</code> I use for reproduction hardcodes its access log at <code>/tmp/market/access.log</code>, and I had cleaned that directory out. Every incoming request threw before it could respond, so <code>curl</code> returned exit 52 with an empty body. Creating the directory fixed the requests.</p>
<h2 id="there-is-an-official-switch-buried-in-the-warning-text">There is an official switch, buried in the warning text</h2>
<p>The tail of those three warnings does give you a way out. It mentions <code>dsh plugin allow-version</code>, or the plugin manager, to grant an exemption for an exact version.</p>
<p>I read the help first. The plugin subcommand documentation for DeepSeek Harness does not mention this at all. It only says the remaining arguments are forwarded to pnpm.</p>
<p>So I just ran it, and the usage fell out on its own:</p>
<pre tabindex="0"><code>dsh: Error: usage: dsh plugin allow-version &lt;package@version&gt;
                   --dsh-version &lt;exact&gt; --accept-risk
</code></pre><p>Filling that in, one exemption per plugin:</p>
<pre tabindex="0"><code>$ dsh plugin --profile web allow-version \
    &#34;@deepseek-ai/dsh-plugin-market@0.2.1-rc.2&#34; \
    --dsh-version 0.2.2-alpha.2 --accept-risk
dsh: allowed @deepseek-ai/dsh-plugin-market@0.2.1-rc.2 for DSH 0.2.2-alpha.2
</code></pre><p>The exemption is scoped to one package at one version. A different version and you have to grant it again. After restarting DeepSeek Harness, the three warning lines were gone from the log, Discover read the index immediately, both plugins listed properly, and category and tag filters were back.</p>
<p><img loading="lazy" src="/images/dsh-022/06-discover-fixed.png"></p>
<p>One note: <code>--accept-risk</code> means what it says. I got lucky here and the plugin ran without incident. That is not a guarantee.</p>
<h2 id="what-the-new-interface-looks-like">What the new interface looks like</h2>
<p><img loading="lazy" src="/images/dsh-022/02-plugins-page.png"></p>
<p>The plugins page now has two tabs, Installed and Discover. Installed splits into two sections.</p>
<p>The top section is Built-in plugin settings, four entries. Subagent governs recursion depth, count, and model. Web search configures the DeepSeek search source. Agent loop controls how the agent dispatches tool calls. Shell limits how long a single command may run and how much output it may produce.</p>
<p>The bottom section is Experimental plugins, five entries, each with a toggle:</p>
<p>Agent teams lets the agent split work across named teammates that share a task board and report results back.</p>
<p>Voice input transcribes locally with SenseVoice; the first use requires installing dependencies.</p>
<p>Custom plugin indexes is what adds the Discover tab to Plugins, letting you browse and install from index sources you add.</p>
<p>Auto authorization review is a new review mode: before each tool call the model checks it against your original request and batch-approves the ones that comply.</p>
<p>Change requests &amp; worktrees tracks each session&rsquo;s change requests along with their check and review results, and can start new sessions directly inside a Git worktree.</p>
<p>Of the five, the first two are on by default and the last three are off.</p>
<h2 id="two-things-that-actually-changed-on-the-plugins-page">Two things that actually changed on the plugins page</h2>
<p>After all that, it is worth looking back at what this upgrade actually delivered.</p>
<p>The plugin detail page now renders the package&rsquo;s own README. Click into an experimental plugin and you get the full documentation, with an English and Chinese toggle, a summary, a table of contents, and a section called Use this package further down.</p>
<p>More interesting is that the same page breaks the bundle into its 5 components, listed one per row, each with an icon, a one-line description, a short name, the full package name, and its own toggle on the right.</p>
<p>Take the Change requests &amp; worktrees package. Expanded, it is five parts.</p>
<p>Change requests, GitHub change requests, Session change requests, Worktree service, Change request panel. The top of the page reads <code>Components 5 total · 5 off</code>.</p>
<p>The bottom also shows provenance: Code source is Built in, current version 0.2.2-alpha.2.</p>
<p><img loading="lazy" src="/images/dsh-022/04-plugin-components.png"></p>
<p>This is far clearer than the old &ldquo;one plugin, one switch&rdquo; model. You can inspect each part of a bundle and flip them independently, and you no longer have to open a config file to find out what is installed.</p>
<h2 id="where-i-land-on-this">Where I land on this</h2>
<p>The interesting part of this whole episode is not the trap I fell into. It is the design tradeoff the trap exposes.</p>
<p>An exact-version lock keeps the plugin ecosystem safe, and the price is that any runtime upgrade can invalidate a batch of plugins at once. It is especially visible between alpha and rc: bump a few internal packages and every plugin declaring a dependency on them drops offline.</p>
<p>DeepSeek Harness provides an exemption switch, but the default path is rejection. The bet it is making is &ldquo;better to load less than to run something broken.&rdquo;</p>
<p>For people building plugins, this means reissuing a version declaration every time you follow a runtime release. For people using plugins, it means knowing what you installed before you upgrade, because afterwards you may not even be able to tell what went missing.</p>
<p>DeepSeek&rsquo;s official release notes for 0.2.2-alpha.2 also mention that a failed hot plugin update will attempt to restore the previous version. That mechanism protects updates already in flight. It cannot save the version check that runs before startup. The two pieces of logic do not touch each other.</p>
<p>This release also lists a few things I did not verify: subagent_oar can hand standalone tasks to a local Codex or Claude CLI, and the developer tools stats plugin can show context, cache, model, and tool timing by turn. The first one sounds the most interesting, but I could not find an entry point in the UI, so it likely needs a separate install and I left it alone. Same for the hot-update rollback: official wording, and I did not construct a failure to test it.</p>
<p>Do you check which plugins you have installed before upgrading your toolchain? Or are you like me, finding out only after things are gone.</p>
<h2 id="references">References</h2>
<ul>
<li>DeepSeek Harness v0.2.2-alpha.2 release notes: <a href="https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.2.2-alpha.2">https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.2.2-alpha.2</a></li>
<li>Test environment: macOS arm64, DeepSeek Harness upgraded directly from 0.2.1-rc.2 to 0.2.2-alpha.2, npm global install</li>
</ul>
]]></content:encoded></item></channel></rss>