Run DeepSeek Harness Headless with --json
Headless · --json · --session-id · CI
dsh --profile headless opens no port. Keep stdout for --json, treat unknown session ids as failures, and split multi-turn work into separate runs.
DeepSeek Harness headless is not the Web UI
DeepSeek Harness headless answers a different question from npx @deepseek-ai/dsh web. The Web path starts a local UI — usually on http://127.0.0.1:3080/ — and stays up until you stop it. Headless runs one task, prints the answer, and exits. It opens no listening port and leaves nothing running behind. That is the surface for scripts and CI, not for clicking through a first evaluation.
Upstream documents the profile as dsh --profile headless. From the published package the same shape is npx @deepseek-ai/dsh --profile headless "run the tests". Pin the version you verified — on 28 September 2026, npm latest was still a Pre-release RC — and keep the install checklist nearby for Node and credentials. Product identity stays in What is DeepSeek Harness. The first-hour browser path stays in the npx quickstart.
If the headless package README disagrees with this page, the README wins.
One task, then exit
sh
npx @deepseek-ai/dsh --profile headless "List the top-level files and quote the first README heading"
Default mode prints the final answer on stdout. Non-empty provider reasoning goes to stderr under a dsh: reasoning: heading. Exit 0 means a completed final turn; exit 1 means abort, error, or no usable turn. A failure outside a turn also prints a dsh: line on stderr.
You can feed the task from stdin when you omit the positional argument, or pass a lone -:
sh
{ echo "Summarize these changes:"; git diff --stat; } | npx @deepseek-ai/dsh --profile headless
A blank positional argument is a usage error even if a pipe is waiting — it will not silently consume stdin. Mixing - with other task words is also a usage error. dsh --profile headless --help prints help and runs nothing.
This is still one shot. There is no interactive follow-up in the same process. Split multi-step work into separate runs, or continue with --session-id below.
--json is a machine channel, not Trajectory
--json replaces the final-text stdout line with a newline-delimited JSON event stream. Stderr keeps only dsh: diagnostics. The stream opens with session (the identity this run used) and closes with final. In between you may see status, text, thinking, tool_call, and tool_result.
Treat that stream as a projection for supervisors, not as a lossless Session log. Strings other than the terminal final are capped (upstream documents 8 KiB per string, with a truncated flag). The durable log you open after a failed agent run is still the Trajectory path — how to read Trajectory logs and the observability intro.
A well-formed stream can still describe a failed run: an in-turn failure may end with final (often empty) and no error event. Trust exit code 1 and the turn-end reason. A failure the runner raises outside a turn writes error and ends without final.
--session-id refuses silent empty history
Every run defaults to a fresh session-… identity. With --json, that id appears on the opening session event — store it with the cwd you launched from. Pass --session-id on a later invocation to continue that conversation.
An unknown id fails before the task runs. It does not invent an empty history. Adoption also refuses a session recorded under a different working directory, a subagent or fork, a missing cwd, a live agent already holding that id in this process, or a preset this headless profile does not compose. Those refusals are the point: a CI job should not silently drive someone else's conversation.
Resume from the same disposable folder you used for the first run. If Node or npx itself will not start, that is still npx and port 3080 — headless does not use 3080, but a broken Node install is the same floor.
Ten-minute operator pass
1. Confirm credentials and the default model the same way you would for Web. Headless does not invent a second auth story.
2. Run one boring task without --json. Expect text on stdout and exit 0.
3. Re-run with --json. Keep the opening session id and the launch cwd.
4. Continue once with --session-id from the same directory. Expect refusal if you cd elsewhere first.
5. Pipe a short task through stdin. Confirm a blank positional argument does not steal that pipe.
6. In CI, accept a run only when exit is 0 and — if you used --json — the stream closed with final and no out-of-turn error.
7. Keep stderr out of shared logs when reasoning may contain sensitive paths. Route it yourself.
What this article is not
- Not a second Web quickstart — that remains npx quickstart.
- Not the 0.1.7 RC feature dump — What’s New in 0.1.7.
- Not a Computer Use or SSH remote workspace guide.
- Not a claim that headless is production-certified. Upstream still warns that preview compatibility breaks.
Where to go next
1. Contract and flags: headless README on the tag you pin.
2. Browser first hour: install → npx quickstart.
3. After a failed run: read Trajectory logs.
Related articles
- 2026-10-09 DeepSeek Harness on macOS: Homebrew, PATH, Full Disk Access Same npx command as every other OS — on Mac the surprises are which node is on PATH, TCC prompts, and not granting Full Disk Access on day one.
- 2026-09-28 What's New in DeepSeek Harness 0.1.7 RC v0.1.7-rc.2 is Pre-release. Scheduled tasks stay off, session logs move to V4, and PTC names become ptc-runtime.
- 2026-09-23 Read DeepSeek Harness Trajectory Logs Start at cwd and agentPreset, then find the tool/call whose callId never received a tool/result.
Continue on the portal
Install DSH, walk the tutorial track, or browse curated plugins after this article.