Where to put spend guardrails in DeepSeek Harness

Published 16 August 2026

An agent that discovers its budget from the monthly invoice does not have a budget. It has a postmortem. The useful question about any harness is where you can stand in the loop and say no, and DeepSeek Harness answers that with two named waterfall extension points.

First, the shape of the loop

The harness uses a turn and step model. A step is one model request plus the tool calls it produces. A turn contains zero or more steps. The flow for a turn runs roughly: input claim, prompt assembly, agent/pre-step, LLM stream, tool execution, step completion, then turn closure.

Two event streams run alongside each other. session/event carries durable replay facts — chunks, messages, tool calls, results. agent/* carries live coordination signals — status, inbox, request interception. Guardrails belong in the second; accounting belongs in the first.

Interception point 1: agent/pre-step

This is the checkpoint before the model request goes out. The docs state that the returned agent/pre-step decision is authoritative, and that listeners wrapping next() preserve downstream messages unless replacement is intentional. That is the property that makes it usable as a budget gate: a listener can reject a proposed step, or modify it, and the decision stands.

What belongs here:

Interception point 2: tools/pre-execute

Tool execution is a three-phase pipeline. The tools/pre-execute waterfall runs first, monotonic guards run next, and only then does the tool body execute — sandboxed, with filesystem gatekeeping. Afterwards a post-execute waterfall can accept, block, replace, or add context to the result, and ToolDefinition.finalizeContent enforces content invariants synchronously.

The permission layer is deliberately asymmetric. Registered monotonic guards deny or abstain, with identity protected — they never grant. If a guard denies, or if ctx.approval's one-shot prompt is absent or unanswerable, the result is denial and the tool body is skipped. Failure closed, not open.

Filesystem mutations are separately gated: only fs/write-intent or fs/edit-intent tool-fs mutations pass. Writes are not an incidental side effect of a tool running.

Two properties make this pipeline trustworthy as a control point: guards can only deny, and an unanswerable approval prompt denies. Most homegrown agent policy layers fail open when the approval channel is missing, which is exactly when you needed them.

What the pipeline records

Three session events give you the audit trail: tool/call before execution, tool/code-dispatch for sub-calls, and tool/result as the final authoritative outcome. Sub-calls being separately recorded matters — a single model-visible tool call that fans out internally does not hide its fan-out.

A practical guardrail set

ControlHookStops
Max steps per turnagent/pre-stepNon-converging loops
Cumulative token ceilingagent/pre-stepSlow-burn overspend
Route downshift past thresholdagent/pre-stepFrontier pricing on cheap work
Tool allow-list by cost classMonotonic guardExpensive tools in cheap contexts
Subprocess and network denialctx.sandbox policyUnmetered egress
Write-intent gatingctx.fs seamUnreviewed mutations

The caveat

DeepSeek Harness is in developer preview and its own README warns of compatibility-breaking changes. These hook names and semantics are the ones documented today; treat guardrail plugins built against them as code you will revisit, and pin the version you built against.

Related


Want this applied to your own LLM spend? FinOps LLM runs a free audit of your AI costs and shows where the savings are. Book free audit →

Back to research

FAQ

Where can you enforce spend policy in DeepSeek Harness?

Two waterfall extension points. agent/pre-step runs before a model request is issued and its returned decision is authoritative. tools/pre-execute runs before any tool body executes, ahead of the monotonic permission guards.

What is a waterfall hook in DeepSeek Harness?

A waterfall is an ordered chain of listeners where each must call next() to delegate downstream. Listeners that wrap next() preserve downstream messages unless replacement is intentional, so a guard can inspect, modify, or short-circuit a step.

What is a monotonic guard?

A monotonic guard evaluates tool permissions without modifying them: it can deny or abstain but never grant. Guards run after the tools/pre-execute waterfall, and if a guard denies or approval is unanswerable the tool body is skipped.