Skip to content

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.

Terminal running DeepSeek Harness headless with a --json event stream

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 reused with --session-id after a DeepSeek Harness headless run

--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.

Continue on the portal

Install DSH, walk the tutorial track, or browse curated plugins after this article.