<?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>DeepSeek on Trevor's Blog</title><link>http://mings.in/en/tags/deepseek/</link><description>Recent content in DeepSeek on Trevor's Blog</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Wed, 04 Nov 2026 22:40:00 +0800</lastBuildDate><atom:link href="http://mings.in/en/tags/deepseek/index.xml" rel="self" type="application/rss+xml"/><item><title>dsh 0.2.2-rc.1: This Release Changed 0 Lines of Code</title><link>http://mings.in/en/posts/dsh-022rc/</link><pubDate>Wed, 04 Nov 2026 22:40:00 +0800</pubDate><guid>http://mings.in/en/posts/dsh-022rc/</guid><description>Unpack both tarballs and compare md5 file by file: 8 packages, 290 files, 8 differences, all of them in package.json. The version number went from alpha to rc, not a line of code moved, but every dependency got bumped, and plugins have to follow.</description><content:encoded><![CDATA[<p>At 5:20 this afternoon, DeepSeek Harness shipped 0.2.2-rc.1.</p>
<p>Going from alpha to rc sounds like a milestone. I unpacked both versions and compared them. Conclusion: not a single byte changed.</p>
<h2 id="the-short-version-first">The short version first</h2>
<p><code>0.2.2-rc.1</code> was published at 17:20 Beijing time on November 4, 2026 (<code>2026-11-04T09:20:32Z</code>).</p>
<p>I ran a per-file md5 comparison against <code>0.2.2-alpha.3</code>, which shipped on November 3, 2026. The scope was the top-level package, the six core sub-packages, and the underlying framework <code>cordis</code>. 290 files, all lined up and hashed.</p>
<p>Result: in every package, the only file that differs is <code>package.json</code>.</p>
<p><code>dist</code>, <code>src</code>, <code>locale</code>, <code>README</code>, all identical. This is not &ldquo;a small change&rdquo;. This is zero change.</p>
<h2 id="how-i-compared-them">How I compared them</h2>
<p>Three steps on the command line, no special tooling.</p>
<p><code>npm pack @deepseek-ai/dsh@0.2.2-alpha.3</code> and <code>@0.2.2-rc.1</code> to pull one tarball each, unpack them into two directories, then hash every file by relative path.</p>
<p>The top-level package has 20 files, the six sub-packages have 30 to 60 each, <code>cordis</code> has 32. Every file with a matching name on both sides got hashed, and I only kept the ones whose hashes disagreed.</p>
<table>
	<thead>
			<tr>
					<th>Package</th>
					<th>Files</th>
					<th>Files with changed content</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>dsh (top level)</td>
					<td>20</td>
					<td>1, package.json</td>
			</tr>
			<tr>
					<td>dsh-agent-preset</td>
					<td>48</td>
					<td>1, package.json</td>
			</tr>
			<tr>
					<td>dsh-plugin-manager</td>
					<td>60</td>
					<td>1, package.json</td>
			</tr>
			<tr>
					<td>dsh-web-app</td>
					<td>32</td>
					<td>1, package.json</td>
			</tr>
			<tr>
					<td>dsh-client-ui-cordis</td>
					<td>30</td>
					<td>1, package.json</td>
			</tr>
			<tr>
					<td>dsh-tool-fs</td>
					<td>34</td>
					<td>1, package.json</td>
			</tr>
			<tr>
					<td>dsh-tool-cordis</td>
					<td>34</td>
					<td>1, package.json</td>
			</tr>
			<tr>
					<td>cordis</td>
					<td>32</td>
					<td>1, package.json</td>
			</tr>
	</tbody>
</table>
<p>No additions, no deletions. Eight packages, eight changes, all of them in the version number and dependency range lines.</p>
<p><img loading="lazy" src="/images/dsh-022rc/fig1.png"></p>
<h2 id="the-only-thing-that-actually-changed">The only thing that actually changed</h2>
<p>The code did not change. But every dependency version moved, and that is the only substantive act in this release:</p>
<table>
	<thead>
			<tr>
					<th>Package</th>
					<th>alpha.3</th>
					<th>rc.1</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>@deepseek-ai/cordis</code></td>
					<td>4.0.6-alpha.2</td>
					<td>4.1.0</td>
			</tr>
			<tr>
					<td><code>cordis-plugin-loader</code></td>
					<td>1.0.7-alpha.2</td>
					<td>1.1.0</td>
			</tr>
			<tr>
					<td><code>cordis-plugin-include</code></td>
					<td>1.0.11-alpha.2</td>
					<td>1.1.0</td>
			</tr>
			<tr>
					<td><code>cordis-plugin-timer</code></td>
					<td>1.1.8-alpha.2</td>
					<td>1.2.0</td>
			</tr>
			<tr>
					<td><code>@deepseek-ai/schemastery</code></td>
					<td>3.18.6-alpha.2</td>
					<td>3.19.0</td>
			</tr>
			<tr>
					<td><code>@deepseek-ai/cosmokit</code></td>
					<td>1.8.7-alpha.2</td>
					<td>1.9.0</td>
			</tr>
	</tbody>
</table>
<p>In one sentence: strip the alpha suffix off the entire cordis family and promote it to release version numbers.</p>
<p>But there is a detail here worth spelling out. I also unpacked <code>cordis</code> 4.0.6-alpha.2 and 4.1.0 and compared those two. Of its 32 files, again only <code>package.json</code> differs. Which means even the framework layer was carried over as-is, with nothing but the house number changed. That is more interesting than the table above.</p>
<p>The &ldquo;version number goes from alpha to stable&rdquo; event had a change count of zero.</p>
<p><img loading="lazy" src="/images/dsh-022rc/fig2.png"></p>
<h2 id="so-what-was-the-point-of-this-release">So what was the point of this release</h2>
<p>A tag.</p>
<p>Before this release, <code>latest</code> pointed at <code>0.2.1-rc.5</code> and <code>next</code> pointed at <code>0.2.2-alpha.3</code>. If you wanted the newest one, you had to type the full version number by hand.</p>
<p>After it, both <code>latest</code> and <code>next</code> point at <code>0.2.2-rc.1</code>. That means <code>npm i -g @deepseek-ai/dsh</code> with no version number installs this one by default.</p>
<p>For a project this size that is not a small thing. The alpha line is the &ldquo;we are willing to use it ourselves&rdquo; marker. rc is the &ldquo;you try it too, the interfaces are frozen&rdquo; marker. Changing features is development. Changing the marker is a promise. This release did the latter.</p>
<h2 id="the-upgrade-took-under-a-minute">The upgrade took under a minute</h2>
<p>One command for the global install, <code>npm i -g @deepseek-ai/dsh@0.2.2-rc.1</code>, and once it finishes <code>dsh --version</code> says <code>0.2.2-rc.1</code>.</p>
<p>The second command is not an upgrade, it is a profile fix. A lot of people skip this one. I skipped it last time:</p>
<p><code>dsh plugin --profile web add @deepseek-ai/dsh-cua-bundle@0.2.2-rc.1 @deepseek-ai/dsh-experimental-plugin-market-bundle@0.2.2-rc.1 @deepseek-ai/dsh-experimental-client-ui-agent-team@0.2.2-rc.1</code></p>
<p>pnpm came back with: resolved 62 packages, reused 41, downloaded 21, added 22, in 939 milliseconds.</p>
<p>After the install I ran a headless smoke test first, telling it to &ldquo;answer with two words only: received&rdquo;. It answered &ldquo;received&rdquo;, which means the profile boots, model routing works, and no plugin is stuck in a disabled state. That step is faster than looking at the UI.</p>
<p>Why run it again? Because a dsh profile is plugin layers stacked on plugin layers. Once you upgrade dsh globally, the plugins in the profile that are still pinned to alpha.3 no longer match the host program&rsquo;s version, and the version check disables them at startup.</p>
<p>Last time, when I went to alpha.3, I only upgraded globally. Six plugin rows got shut off, <code>storage-domain</code> among them. Once that one died, the workspace, the session list, and the scheduled tasks all froze with it, the web app was effectively dead, and this command is what brought it back.</p>
<p>So my order is fixed now: global first, then profile, then verify. Skip a step and you are making trouble for yourself.</p>
<p><img loading="lazy" src="/images/dsh-022rc/fig3.png"></p>
<h2 id="the-one-that-never-came-back-last-time-is-back">The one that never came back last time is back</h2>
<p>The only leftover damage from the previous upgrade was <code>ui-agent-team</code>. Its version line is unusual, there was no package on npm that lined up with alpha.3, so it stayed disabled the whole time.</p>
<p>This time it has a matching version. In the plugin list that <code>profile_ping</code> reports after the install, all three of these are present:</p>
<p><code>@deepseek-ai/dsh-experimental-agent-team</code></p>
<p><code>@deepseek-ai/dsh-experimental-client-ui-agent-team</code></p>
<p><code>@deepseek-ai/dsh-experimental-tool-agent-team</code></p>
<p><code>dsh-storage-domain</code>, the one that got disabled last round, is in the list too. On the plugins page the &ldquo;Agent teams&rdquo; toggle is on, and a new session&rsquo;s title bar now carries an extra Agent team badge.</p>
<p>The installer printed one line that I think is worth recording verbatim: <code>@deepseek-ai/dsh-experimental-client-ui-agent-team</code> does not declare <code>dsh.bundle</code>, so it was installed as an ordinary dependency, not as a profile layer.</p>
<p>In other words it works, but its position in the system is not the same as those bundles. Vendors do not put this in the release notes. It flashes by once, in the install output.</p>
<p><img loading="lazy" src="/images/dsh-022rc/fig4.png"></p>
<h2 id="not-everything-in-a-profile-is-an-official-package">Not everything in a profile is an official package</h2>
<p>I took a look at the profile&rsquo;s dependency list while I was there. Six entries.</p>
<p>Three bundles are on rc.1 now. <code>agent-team-web-profile</code> is still stuck on <code>0.1.6-alpha.2</code>. The last two I installed myself, one from a local tgz, one straight from a GitHub repo.</p>
<p>Which tells you that a dsh profile is not a closed official bundle, it is a combination you assemble yourself. The upside is you can add whatever you want. The cost is that on upgrade you have to watch it yourself: which ones are official, which are pinned to a specific version, which are not on the official version line at all.</p>
<p><code>agent-team-web-profile</code> is the example. It is the UI layer for agent-team, its version line is completely independent of the host program, and the host is on rc.1 while it sits on alpha.2. A package like that will not follow your upgrade. It goes its own way.</p>
<h2 id="a-quick-sanity-check">A quick sanity check</h2>
<p>After the upgrade I opened a new session in the web app and sent one line: call profile_ping and paste the returned JSON back as-is.</p>
<p>Ten seconds and it was done. The response started with <code>&quot;profile&quot;: &quot;web&quot;, &quot;dshVersion&quot;: &quot;0.2.2-rc.1&quot;, &quot;plugins&quot;: [</code> followed by 212 plugin names.</p>
<p>Those 212 lines are what my profile actually contains right now, from <code>cordis-plugin-timer</code> all the way to <code>dsh-plugin-profile-ping</code>, which I wrote myself. If you want to confirm the upgrade really took effect, that one number is more reliable than any UI.</p>
<p>Incidentally the query itself cost 56.3K tokens, with an 86% cache hit and 3% of context used. The cost is the long plugin list, not the reasoning.</p>
<p><img loading="lazy" src="/images/dsh-022rc/fig5.png"></p>
<h2 id="one-lesson-about-checking-versions">One lesson about checking versions</h2>
<p>If you want to track dsh versions too, there is a trap to avoid: the <code>latest</code> tag on sub-packages lags.</p>
<p>Take <code>dsh-experimental-client-ui-agent-team</code>. Its <code>latest</code> is stuck at <code>0.1.5-alpha.2</code>, <code>alpha</code> is stuck at <code>0.2.2-alpha.3</code>, and the actual new version lives under <code>next</code>.</p>
<p><code>npm view &lt;package&gt; dist-tags</code> and read all three tags together, not just <code>latest</code>. The main package&rsquo;s <code>latest</code> is correct right now. The sub-packages&rsquo; are not.</p>
<h2 id="my-call">My call</h2>
<p>Is a release like this worth following? My answer is yes, but there is no rush.</p>
<p>Zero code change means risk close to zero. Upgrading or not upgrading will not break your environment. The real payoff is not in dsh itself but in the plugins that got promoted along with it: a package like <code>ui-agent-team</code> that spans version lines only gets released when the whole line moves to rc together.</p>
<p>On the flip side, if you have an alpha.3 that runs fine and no plugin is disabled, you do not need to touch anything this time. Wait for the 0.2.2 stable release and catch up in one go. There are no new features coming your way in the meantime.</p>
<p>When you upgrade, do you read the release notes first, or just unpack the tarballs and diff them yourself?</p>
]]></content:encoded></item><item><title>I Had dsh Write Its Own Plugin. It Took Nine Minutes.</title><link>http://mings.in/en/posts/dsh-creator/</link><pubDate>Tue, 03 Nov 2026 19:40:00 +0800</pubDate><guid>http://mings.in/en/posts/dsh-creator/</guid><description>I asked dsh&amp;rsquo;s Creator mode for a plugin. It inspected the runtime, read source, wrote the package, requested install approval, and nine minutes later the tool was sitting in the plugin manager. A fresh session called it in ten seconds.</description><content:encoded><![CDATA[<p>The news item going around today is that DeepSeek shipped Creator Mode, described in English coverage as something mystical where you build plugins by chatting.</p>
<p>I run dsh daily, on 0.2.2-alpha.2 released October 28. My first instinct was not to paraphrase it. It was to try it.</p>
<p>So I opened a Creator mode session in my own web profile with a one-line request: write the smallest possible plugin, register a tool called <code>profile_ping</code>, and have it return the current profile name, the dsh version, and which plugins are installed in that profile.</p>
<p>Nine minutes later the tool was sitting in the plugin manager. I opened a new session, called it, and had the answer in ten seconds.</p>
<h2 id="dsh-has-four-modes">dsh has four modes</h2>
<p>Open the web UI and there is a mode selector above the input box. It expands to four:</p>
<ul>
<li>Standard: writes code, looks things up, runs commands, good for most tasks</li>
<li>PTC: everything Standard does, plus batches of tool calls you then filter, dedupe, count, or summarize</li>
<li>Minimal: terminal and image reading only, for testing baseline capability</li>
<li>Creator: in the official wording, customize dsh through conversation, letting the agent write plugins that add features or UI, or combine tools and prompts into a mode of your own</li>
</ul>
<p>I picked the fourth.</p>
<p><img alt="Four modes, and the fourth is the one that rewrites dsh itself" loading="lazy" src="/images/dsh-creator/fig1.png"></p>
<h2 id="it-did-not-start-by-writing-code">It did not start by writing code</h2>
<p>That was the most surprising part.</p>
<p>After receiving the request it loaded a skill package called <code>cordis-plugin-development</code>, then started inspecting the runtime.</p>
<p>The Trajectory panel on the right shows the sequence. It ran inspect providers to see what services exist, then query runtime to ask for the exact method signatures on <code>Service.listService</code>, and worked through the contracts for <code>pluginManager</code>, <code>profileContext</code>, and <code>defineTool</code>.</p>
<p>It even went and read the source of <code>@deepseek-ai/dsh-app-boot</code> to decide whether a plugin could import that package. It needed the dsh version, and the version only exists in the install directory&rsquo;s <code>package.json</code>.</p>
<p>That reasoning step is worth more than the result. It first established that a profile-installed plugin cannot resolve packages from the install directory, then switched to the <code>profileContext</code> the runtime already exposes, reading the version itself from the <code>installAnchor</code> path.</p>
<h2 id="what-it-produced">What it produced</h2>
<p>The output is a standard bundle package in the session workspace: manifest, patch, entry point, two locale files, an icon, and a README.</p>
<p>The line in <code>package.json</code>, <code>&quot;dsh&quot;: { &quot;bundle&quot;: { &quot;patch&quot;: &quot;./cordis.patch.yml&quot; } }</code>, is how it introduces itself to dsh. The patch file is three lines, inserting a row with the id <code>profile-ping</code>.</p>
<p><code>index.js</code> is 6.9 KB: it registers the tool, defines the parameter and output schemas, and writes a render function. The Chinese display name, &ldquo;Profile 探针&rdquo; (profile probe), it picked on its own.</p>
<p><img alt="It did not generate a file, it generated a package dsh can install" loading="lazy" src="/images/dsh-creator/fig3.png"></p>
<h2 id="i-rejected-it-once">I rejected it once</h2>
<p>The install step calls <code>plugin_manager</code> with <code>install_bundle</code>, which raises an approval dialog stating that this writes changes into the profile and that installed Host code runs outside the sandbox.</p>
<p>I clicked Allow once.</p>
<p>Later it wanted to copy the packed tgz out of <code>/tmp</code> into Documents for safekeeping, and asked for approval again. I clicked Reject, because this was a temporary artifact I planned to clean up.</p>
<p>Its reaction is worth recording: it stopped, and said explicitly that the user declined the escalation, so it should not work around it and would just explain.</p>
<p>That rule is written in its skill package, not improvised by the model. The text: do not hand-write the profile&rsquo;s <code>package.json</code>, do not run pnpm in the profile directory, <code>install_bundle</code> does those things.</p>
<p><img alt="The eighty-three steps it walked on its own" loading="lazy" src="/images/dsh-creator/fig2.png"></p>
<h2 id="it-works">It works</h2>
<p>In a new session I said: call <code>profile_ping</code> directly and paste the raw output.</p>
<p>Ten seconds later it came back with <code>Profile: web</code>, <code>dsh version: 0.2.2-alpha.2</code>, <code>Plugins (209):</code>, followed by 209 package names. The one at the end of that list is the tool it just wrote.</p>
<p>The plugin manager&rsquo;s installed list also gained a row: Profile ping, with icon, description, and its toggle switched on.</p>
<p><img alt="Top: the call result. Bottom: the same plugin in the plugin manager" loading="lazy" src="/images/dsh-creator/fig4.png"></p>
<h2 id="the-rules-are-written-into-the-skill-package">The rules are written into the skill package</h2>
<p>I only read the skill document it loaded after the run, and then I understood why it moved the way it did. The document writes that the workflow has five steps, and the first is &ldquo;deliver a working version first.&rdquo;</p>
<p>The opening of the doc says: ship something that runs, then talk about how it looks. If a visual request names no destination, the default is the current web UI. Do not build a preview page, do not make several design variants, do not write screenshot scripts.</p>
<p>Then three prohibitions: do not hand-write the profile&rsquo;s <code>package.json</code>, do not run pnpm in the profile directory, do not create packages under <code>$DSH_HOME</code>. Those all go through <code>install_bundle</code>, and a hand write costs an approval each time.</p>
<p>There is also a verification rule: after install, read <code>application</code> and <code>warnings</code> from the returned result, not logs, not the process list, not whether the page came up.</p>
<p>I did not interfere with a single technical decision. The only thing I did was two approval clicks, one yes and one no. It walked the whole thing inside that rulebook.</p>
<h2 id="three-rough-edges-i-have-to-mention">Three rough edges I have to mention</h2>
<p>First, the workspace directory it created has a literal backslash in its name. On macOS dsh built the path as <code>deepseek-harness\creator-lab</code>, and that backslash is a real character, not an escape. ESM cannot load such a path, so the plugin simply could not be imported. It found this itself and worked around it by packing a tgz and installing from that.</p>
<p>Second, in the same turn the tool could not be called. The tool list is fixed when a turn begins, so a freshly registered tool only appears next turn. It spun in the Trajectory several times, writing in its own reasoning that it could probably only emit tools from the static list. I had to open a new session to verify it.</p>
<p>Third, the install record says <code>file:/tmp/dsh-plugin-build/xxx.tgz</code>. Clear the temp directory and this plugin never installs again. That is the problem it wanted to fix, and that is the request I rejected. For anything long-lived, the package belongs in a workspace and should be installed by pointing <code>install_bundle</code> at the directory.</p>
<h2 id="what-it-can-build-and-what-it-cannot">What it can build, and what it cannot</h2>
<p>It can build tool plugins, which add a capability to the agent&rsquo;s toolbox, like my <code>profile_ping</code>; configuration plugins, which change the behavior of some host service; and UI plugins, which mount into a slot in the web UI. The skill package ships templates and reference docs for all three.</p>
<p>It cannot build plugins that need a new host service. Its own documentation says this plugin is not a kernel: no new services, no new session event types, no new sandbox backends. Cross-profile changes are also out, one profile at a time.</p>
<p>The more practical limit is tokens. Nine minutes of work burned 8.9 million tokens, and a 99% cache hit rate is what kept the cost bearable. If you want it doing serious work, decide first whether it is worth that.</p>
<h2 id="my-take">My take</h2>
<p>&ldquo;Build plugins by chatting&rdquo; makes it sound like a generator: you describe, it emits code.</p>
<p>It is not that. It behaves like an engineer who knows this framework: inspect what the runtime offers, read source to confirm the constraints, follow the skill package&rsquo;s procedure, request approval to install, and verify the result itself.</p>
<p>What is actually scarce here is the procedure. The skill package writes down &ldquo;deliver something runnable before anything pretty,&rdquo; &ldquo;an unspecified visual request lands in the current UI,&rdquo; and &ldquo;read <code>application</code>, not logs.&rdquo; Models change. This rulebook does not.</p>
<p>One boundary worth stating: 8.9 million tokens for nine minutes, 99% cache hit. This is not free magic, it is real construction work.</p>
<p>What tool of yours do you have to dig through config every time just to learn its state? That one is worth having a plugin written for it.</p>
]]></content:encoded></item></channel></rss>