Special Topic · Inside DeepSeek Harness

Code Mode: One Program Beats Many Tool Rounds

Announcement calls it PTC, source calls it code mode: worker sandbox isolation, message protocol, and double bookkeeping

THE QUESTION THIS PAGE ANSWERS

ANSWER FIRST

What is the key idea behind “Code Mode: One Program Beats Many Tool Rounds”?

Announcement calls it PTC, source calls it code mode: worker sandbox isolation, message protocol, and double bookkeeping

DECISION RULE

Make the claim earn its place. Use this page as a decision aid, not a definition to memorize. Connect the idea to one real task, one observable result, and one failure that would change your mind.

TRY NEXT

Write one question you could answer with evidence after trying this idea.

WATCH FOR

A conclusion that sounds complete but leaves the key assumption untested.

Course goalAfter reading, you can explain two things: why having the model write a program is faster and cheaper than firing tool calls one by one; and when model-written code runs in a sandbox, how the host protects itself and why permissions don’t loosen an inch.
Try it first · One program vs multi-round calls
Same task: read 3 log files, aggregate stats, write 1 report
Traditional tool calls0 sampling rounds0k tokens
Model · sampling round 1Read the first file logs/a.log first.
read_file({ path: "logs/a.log" })
412 lines returned in full — the whole chunk enters context
Model · sampling round 2Now read logs/b.log.
read_file({ path: "logs/b.log" })
398 lines returned in full — the whole chunk enters context
Model · sampling round 3Still need logs/c.log.
read_file({ path: "logs/c.log" })
441 lines returned in full — the whole chunk enters context
Model · sampling round 4Compute stats from the three full texts in context, write the report.
write_file({ path: "report.md", ... })
Write succeeded
Model · sampling round 5Aggregation done — emit the final answer.
Task done · 5 sampling rounds total; every round resends a growing context
Code Mode(PTC)0 sampling rounds0k tokens
Model · sampling round 1Write a program and let the sandbox orchestrate these five steps.
Sample program (5 lines, course example — not a source quote)
let total = 0, errors = 0 // 统计值,全程留在沙箱里for (const p of ['a.log', 'b.log', 'c.log']) { // 三个文件,循环里读 total += 统计(await tools.read_file({ path: p })) }await tools.write_file({ path: 'report.md', … }) // 写报告,照走审批return { total, errors } // 只有这一行回到模型
worker sandbox · runs in isolation; intermediate results never return to the model
await tools.read_file("logs/a.log")Forwarded by the host; still goes through the approval pipeline
await tools.read_file("logs/b.log")Result stays in a sandbox variable
await tools.read_file("logs/c.log")Result stays in a sandbox variable
Accumulate stats inside the loopPure compute, zero round trips
await tools.write_file("report.md")Still the same permission pipeline
return { total, errors }Only this value plus logs leave the sandbox
Returns once { total: 1251, errors: 17 } · still sampling round 1
Hit Play to see how the same task runs in both modes.
Sampling rounds: 5 vs 1Left: each action costs one sampling round; right: one sampling round writes the program, and the sandbox runs every action for it.
Throughput sketch: ~18.3k vs ~2.6kLeft: every round resends a growing context; right: context holds only the program and a one-shot return value.
Where intermediates goLeft: all three full file texts land in context; right: they stay in sandbox variables — only what you print or return comes back.
Teaching sketch: round counts and token figures are course estimates to show structural differences between the two modes; the program on the right is a 5-line course example, not a source quote.
Idea 1 · Let the model write a program — don’t make it a remote control

First, clear up the names. The product announcement calls it PTC — programmatic tool calling — and the preset metadata even says name: PTC 模式 (Source: apps/cli/config/agent-presets/code/preset.yml line 1). Search the source for PTC and you’ll find nothing. Internally it’s code mode end to end: config mode: code, tool name run_code. Two names, one mechanism.

What problem it solves

Native tool calling is like handing the model a remote. Press once, read one file; result comes back, press again. Every press is a full sampling round — the model must re-read the growing context before deciding the next step. Reading 3 log files and writing a report is already 5 rounds; scale to 50 files and both budget and patience burn out. The cost bottleneck isn’t the tools — it’s the one-sample-per-step rhythm.

What the idea is

In one sampling round, the model writes a small TypeScript program and calls await tools.name(args) as many times as it wants — loops and branches fine. The program finishes the work in the sandbox; intermediates stay in sandbox variables; only print and return content go back to the model. The tool description says it plainly: “Only what you print or return comes back — curate it.” (Source: packages/core/tools/src/code-mode.ts line 52).

Remote-control mode (native tool calls) Model Tool 5 round trips — each needs a sampling round, and context keeps growing Program mode (Code Mode) Model Only 1 sampling round worker sandbox Calls 5 tools in a loop; intermediates stay in variables One program Only print and return come back
Teaching diagram: the same five-step task — 5 round trips above, 1 below.
Five round trips become one.

That line has a source: the design-intent comment at the top of the preset file says “five round trips becomes one” — this lesson’s title comes from there:

apps/cli/config/agent-presets/code/agent.cordis.ymllines 1–6 excerpt
# The `code` agent preset: the standard coding agent, presented as Code Mode.
#
# Everything in `standard` is here unchanged. What is added is the `tool-presentation`
# row: instead of one tool call per action, the model writes a TypeScript
# program against a generated SDK and `run_code` executes it, so a sequence
# that would be five round trips becomes one.
Source snapshot note: Based on the local deepseek-harness-master repo; verified against apps/cli/config/agent-presets/code/agent.cordis.yml, verified on 2026-08-13. Code blocks keep the original source text.

The second half of the comment plants a seed: this preset only changes how tools are presented — the registry itself stays with the host (Source: same file lines 8–11). Idea 2’s permission follow-through starts from that arrangement.

Why it lasts

This is an old lesson from decades of network programming: round trips are expensive, batching is cheap. Databases have bulk writes; RPC frameworks accumulate batches. One model sampling round costs far more than one network hop, so collapsing N trips into one pays even bigger. Rewrite DSH in another language someday — the same math still holds.

Idea 2 · When running someone else’s code, assume it’s hostile
What problem it solves

Idea 1 leaves a premise unsolved: the sandbox runs code the model just wrote — nobody reviewed a line. Run it in the host process for convenience, and failure modes are plentiful: scoop API keys from env vars; a while (true) eats all memory and takes the host down with it; the program can forge messages and impersonate tool results to fool upper layers. So from day one the host must assume the other side means harm — once that stance holds, the rest is engineering.

What the idea is

DSH’s approach is three lines of defense, plus one permission rule.

First: resources are welded shut. Every run spins up a fresh worker and throws it away. Launch flags slam the doors: env vars cleared so the program gets no host credentials; inherited loader flags cut off; heap limit welded — if the program blows the heap, the worker exits (Source: packages/code-runtime/code-runtime-worker-thread/src/index.ts lines 378–387).

Second: communication accepts only structured messages. Host and worker share one message port — no shared memory. Upstream messages are only call, log, output-limit, and done. Every inbound message is shape-checked first, then rebuilt field by field into a clean copy; garbage is silently dropped (Source: same file lines 142–165). The worker-side tools namespace is built with a null prototype, so forged names like __proto__ touch nothing (Source: bootstrap.ts lines 324–326).

Third: time and bytes are double-booked — numbers the worker self-reports don’t count. On time, two ledgers: computeMs polls the worker’s measured busy time so hot loops can’t hide, and idle waits on slow tools aren’t unfairly billed; maxWallMs is the backstop — either budget hits its limit and forces kill (Source: index.ts lines 534–545). On bytes, the worker pre-checks before send; the host re-books with OutputLedger on receive (Source: index.ts lines 169–229). Nobody gets away with reporting a pretty number alone.

Model Sees only logs and return values Host process (inspect, approve, bookkeep) Inspect: strip types, check the binding allowlist Reject bad programs outright — worker never starts Approve: same tool pipeline as native mode Each sub-call carries the parent token; pre-execute first Bookkeep: cross-check time and bytes on both ends computeMs、maxWallMs、OutputLedger worker sandbox Program (model-written) Fresh every run, discarded after tools namespace Only allowlisted tools Resource ceilings Empty env, heap ceiling, forced kill Program reply call Logs + return value Only structured messages between host and worker; every inbound message is shape-checked then rebuilt field by field
Teaching diagram: nodes and edges explain source relationships; content is course-adapted.

Then the permission rule: once the program is in the sandbox, approval doesn’t loosen an inch. Every await tools.xxx is wrapped by the host as a sub-dispatch, carrying the parent call’s token, through the same pre-execute approval cascade as native mode. Sub-call ids look like callId:code:n, traced end to end in events (Source: packages/core/tools/src/code-mode.ts lines 545, 477, 470). Tools the program can bind are exactly those declared in the System Prompt; restricted tools simply vanish from the list (Source: same file lines 601–608). Conversely, under mode: code, trying to bypass run_code and fire native calls is rejected as UNKNOWN_TOOL before the policy pipeline (Source: docs/subsystems/tools.zh.md). The entrance narrowed; the permission door didn’t change.

DSH is clear-eyed about this isolation. The README opens with:

This is isolation, not a security boundary: its trust stance is intentionally bash-equivalent… but it offers isolation bash doesn’t: a separate isolate, empty env, heap ceiling, and forced kill.”

Source: packages/code-runtime/code-runtime-worker-thread/README.zh.md; ellipsis marks an original citation omission
Why it lasts

A distrust boundary is a universal shape in security design — it has nothing to do with worker_threads as a specific tech. Swap in containers, a V8 isolate, or another language’s subprocess and you still do these four: spin a clean environment, cap resources, talk over a narrow interface with per-message validation, and keep ledgers on both ends. Browsers vs pages, OSes vs processes — same idea. Model code is just a new name on the list; the treatment stays the same.

Side-by-side · Who has an equivalent

Claude Code

No equivalent. The bash tool is a general escape hatch — the model can write a script and run it — but APIs like Read and Edit aren’t exposed as programmable bindings to scripts, and actions inside a script don’t go through each tool’s own pipeline. Anthropic’s official blog “Code execution with MCP” proposes the same idea; as of the verification date, Claude Code doesn’t ship a built-in run_code-class mechanism.

Based on local study materials search

Grok Build

No such mechanism, based on public evidence. A full-repo search of the local grok-build-main tree for run_code and code mode only hits a telemetry event name that collides literally. Its tool system combines native calls with toolset presets — no channel for the model to write a program that the sandbox orchestrates via tool APIs.

Based on local source search · 2026-08-13

Codex CLI & Cloudflare

The idea rhymes, but this lesson didn’t verify either codebase — just the facts: DSH’s design notes explicitly cite Cloudflare’s blog; the core observation is that models write code better than they fire tool calls in a chain (Source: .agents/notes/implemented/feature/2026-06-15-code-mode.zh.md). Codex CLI has similar public discussion; again, idea only.

Source not verified · public accounts only
Classroom Exercise
01

Two hostile programs — which budget does each hit?

Program A is a sync hot loop while (true) {}; program B is await new Promise(() => {}) that never resolves. Using Idea 2’s third defense, reason it out: are A and B killed by computeMs or maxWallMs? Why can’t A dodge billing by hanging a pending tool call?

Takeaway: Code Mode (announcement name PTC) trades one sampling round for N round trips: the model writes a program, the sandbox runs it, intermediates never enter context. The sandbox is isolation — not a security boundary — and protects itself with empty env, heap ceiling, dual budgets, and double bookkeeping. Permissions don’t loosen because you’re in a sandbox; every sub-call still walks the same approval cascade.

The handoffs inside “Try it first · One program vs multi-round calls”

“First, clear up the names.” shows that an Agent is not defined by the model alone. Each handoff between model, context, tools, state, permissions, and people affects both progress and recovery.

Write the state before adding capability

Starting from “Native tool calling is like handing the model a remote.”, split the workflow into starting state, next action, tool result, state update, and stop condition. Debugging then means finding the first lost piece of information or authority instead of saying vaguely that the model “got worse”.

A happy path is not reliability

Use “Program A is a sync hot loop while (true) {} ;” to replay one successful and one failed run. Record the context, tool result, and owner at each turn; the workflow is maintainable when a second person can follow it without the original builder.

From “Try it first · One program vs multi-round calls” to “Idea 1 · Let the model write a program — don’t make it a remote control”

“Try it first · One program vs multi-round calls” grounds the problem in “Same task: read 3 log files, aggregate stats, write 1 report Play Step Reset Traditional tool calls 0 sampling rounds 0 k tokens Model · sampling round 1 Read the first file logs/a.log first. read_file({ path…”. “Idea 1 · Let the model write a program — don’t make it a remote control” then moves it toward “First, clear up the names. The product announcement calls it PTC — programmatic tool calling — and the preset metadata even says name: PTC 模式 (Source: apps/cli/config/agent-presets/code/preset.yml line 1). Sear…”. Together, they show that the lesson is not just a conclusion to remember, but a claim with conditions.

Carry the judgment into the next situation

When analyzing an Agent, trace state, action, tool result, and next step in order. Each handoff should explain where information came from, who confirmed it, and where failure stops.

  • “Try it first · One program vs multi-round calls”: Same task: read 3 log files, aggregate stats, write 1 report Play Step Reset Traditional tool calls 0 sampling rounds 0 k tokens Model · sampling round 1 Read the first file logs/a.log first. read_file({ path…
  • “Idea 1 · Let the model write a program — don’t make it a remote control”: First, clear up the names. The product announcement calls it PTC — programmatic tool calling — and the preset metadata even says name: PTC 模式 (Source: apps/cli/config/agent-presets/code/preset.yml line 1). Sear…
  • “The closing point”: DSH’s approach is three lines of defense, plus one permission rule

The final “The closing point” brings the discussion to “DSH’s approach is three lines of defense, plus one permission rule”. The useful thing to carry forward is knowing which judgments must be revisited when input, scale, or risk changes.

Mark as learned Your reading progress updates automatically
← PreviousNext →

Keep reading

The next useful article in the thread.

ARTICLE DISCUSSION

Leave one useful thought here.

Keep the idea that clicked, the question that stayed open, or a small note for the next learner.

Discussing Code Mode: One Program Beats Many Tool Rounds Inside DeepSeek Harness
3discussionsArticle discussion · synced with the Circle
View in the learning circle
AM
Asha MorganContent editor
INSIGHTField note

I turned one judgment from this article into a small experiment I could run today. Knowing what to observe next is more useful than simply remembering the conclusion.

ARTICLE DISCUSSION7 helpful
LH
Lin HarperIndie developer
INSIGHTInsight

After reading this, I first looked for the conditions behind the idea instead of copying the method into a project. That order made the later trade-offs much clearer.

ARTICLE DISCUSSION5 helpful
KM
Kiki MooreProduct operations
QUESTIONQuestion

When this judgment reaches real work, which constraint should be added first? I am curious which step matters most between reading and the first practical attempt.

ARTICLE DISCUSSION4 helpful