DEV Community

Cover image for Pinning an agent harness is harder than pinning its package
Zack Chew
Zack Chew

Posted on • Originally published at openclawlaunch.com

Pinning an agent harness is harder than pinning its package

DeepSeek shipped its own agent harness this week. MIT, developer preview, and the architecture is the interesting part: everything in it is a plugin, including the pieces you would expect to be the program.

We had a reason to look closely, because we put it into a container image that other people run. That means pinning it, and pinning it turned out to be a different job from pinning the package.

The launcher is thin

dsh is a small commander-based launcher. It parses a few flags of its own, then hands the rest of the argv to a booted app that has its own parser. Almost nothing you care about is in the launcher.

That matters because dsh depends on its own family through caret ranges. Pin @deepseek-ai/dsh at an exact version and you still get @deepseek-ai/dsh-base and @deepseek-ai/dsh-headless at ^0.1.0-rc.6. The permission rows, which decide what the agent may touch, ship inside dsh-base as a patch layer. The parsing that decides whether your task text is a task or a flag lives in dsh-headless.

So the failure mode is quiet. Rebuild the image a week later, after either dependency publishes, and the behaviour of every run changes. Meanwhile dsh --version prints exactly the version you pinned, and every test that checks the version passes.

The fix is a build-time assert on the whole family rather than the launcher alone.

Two dashes, not one

The second thing cost an hour. Running a task by hand works the way the docs show:

dsh --profile headless "run the tests"
Enter fullscreen mode Exit fullscreen mode

Send the same task through a runner that builds argv programmatically and it arrives as flags instead of a prompt. The launcher consumes one -- separator and the booted app consumes another, so a task passed positionally needs both:

dsh --profile headless -- -- "run the tests"
Enter fullscreen mode Exit fullscreen mode

The reason this is worth writing down rather than shrugging at is the silent-success case. If the task text happens to begin with something that parses as a flag, you do not get an error. You get exit code 0, no output, and a run that did nothing. That is the worst shape a bug can have in an unattended pipeline, because nothing downstream notices.

What the sandbox actually does

The permission mode moves both the approval policy and the sandbox together, which is convenient right up until you assume the sandbox is doing something it is not. In our image it is not. The default confinement relies on bwrap, which is absent there, and on Landlock, which is unreadable in that context. On macOS the same setting confines through Seatbelt and genuinely does what it says.

So the honest description of a container run is that the confinement is advisory. That is fine when the container is the boundary you are relying on, which it is for us, but it is worth knowing which layer is actually holding the line rather than assuming it is the one named in the config.

Where this leaves it

None of this is a complaint about a package that has been public for a couple of days and says plainly that it is a developer preview. It is what a plugin architecture costs on the operations side: when everything is a plugin, the version number of the thing you installed stops describing the thing you are running.

If you want to try it without any of the above, it is npx and a key. If you want it to behave the same next month, assert the family.

Top comments (0)