DeepSeek Harness auto-review costs have no sessionId

TroubleshootingPublished 2026-10-20Author: DeepSeek Plugin Market
DeepSeek HarnessDSHauto-reviewsessionIdcost trackingllm.stream
DeepSeek Harness daily cost matches the gateway bill while session rows are about 75% short, because auto-review calls carry no sessionId.

The daily total matches the gateway invoice to the cent, yet the per-session rows add up to roughly 75% less — the cost accounting is not miscalculating; a batch of calls has no owner. Those calls are DeepSeek Harness (DSH) auto-review requests: one before each tool call, genuinely billed, but built without a sessionId. Cost tracking groups by options.sessionId, gets undefined, and can only add them to the daily total (source).

Reconcile the DeepSeek Harness gap in three columns

The test is not "the number looks low" but "the gap equals the number of tool calls inside auto windows, day by day".

  1. Export the day's total call count and cost. Expected: it matches the gateway or relay invoice.
  2. Export every session row's calls and cost and sum them. Expected: clearly below the daily total.
  3. Subtract to get the missing calls and cost. Expected: on a machine using auto, the gap is around 70%.
  4. Reconstruct each session's preset timeline and count only tool/call events inside auto windows. Expected: the count matches the gap exactly.
  5. Switch back to a fixed preset and sample again. Expected: the gap stops growing.
MetricDay oneDay two
Daily calls623860
Session-row calls359490
Missing calls264370
Gap share75.6%75.5%

The reverse check holds too: after switching back to a fixed preset, the daily and session totals each grew by 14 while the gap stayed flat, so unattributed usage stopped as soon as reviews stopped.

Why DeepSeek Harness review calls omit sessionId

GenerateOptions defines sessionId, but auto-review's review request supplies only provider, model, system, messages, temperature, and signal.

  1. The review entry point classifyRisk already has agent, so the session id is at hand. Expected: no new dependency is needed.
  2. It freezes the request options into an object whose fields omit sessionId. Expected: attribution is lost upstream.
  3. A comment at the same site notes that the review prompt only goes through ctx.llm.stream and never enters the Session log. Expected: the log cannot recover it either.
  4. Cost tracking reads options?.sessionId in the waterfall. Expected: it receives undefined.
  5. Every review therefore becomes a billed call with no owner. Expected: the gap grows with call count.

This is not an error in the cost DSH plugin: its daily figure matches the invoice and its session rows only include calls with attribution. What is missing is attribution from upstream.

Path 1: pass sessionId on the DeepSeek Harness review request

The smallest direct change is to write agent.session.id into the review request options so routing and billing both have an owner.

  1. Find the function that builds the review request and confirm sessionId is absent. Expected: the field list matches the discussion.
  2. Add sessionId: agent.session.id without introducing a new dependency. Expected: type checking passes.
  3. Enable auto in one session and run a few tool calls. Expected: that session's cost row starts to grow.
  4. Watch the daily total as well. Expected: the gap stops widening in step.
  5. If the session row does not change, billing does not use this field. Expected: move to Path 3 instead of retrying.
ts
const options = deepFreeze({
  provider: snapshot.provider,
  model: snapshot.model,
  sessionId: agent.session.id,
  system: REVIEW_POLICY,
  messages: [{ role: 'user', content: [{ type: 'text', text: reviewUserText(snapshot) }] }],
  temperature: 0,
  signal,
})

Decide the booking rule before adding the field: charge review spend to the reviewed session, or show it as a separate background cost. Both reconcile, but they mean different things in reports.

Path 2: extend purpose so DeepSeek Harness reviews are separate

If review spend should not count against the reviewed session, add a distinguishing purpose instead of only a sessionId.

  1. Add a review value to the purpose enum and update validation. Expected: both type and runtime validation accept it.
  2. Send sessionId and the new purpose together. Expected: attribution exists and the source is distinguishable.
  3. Add a "background review" bucket in cost reporting keyed by purpose. Expected: session rows plus that bucket reconcile to the daily total.
  4. Keep a compatibility path for old data: calls without purpose remain ordinary session calls. Expected: historical reports do not change.
  5. Document what the bucket counts. Expected: readers know it is not ordinary chat cost.

Today purpose allows only compaction and session titles, so extending the enum requires updating validation and docs, or older clients cannot send the new value.

Path 3: reconcile from the DeepSeek Harness statistics side

When the host cannot change, identify the review shape with no sessionId and book it explicitly instead of leaving an ownerless residual.

  1. In the llm/stream observer, check whether options.sessionId is empty. Expected: unattributed calls can be filtered out.
  2. Confirm the call is a review from its request shape, such as a fixed system policy and very short output. Expected: ordinary calls are not swept in.
  3. Record those calls in an explicit "auto-review" bucket. Expected: the daily total still equals the sum of buckets.
  4. Label the bucket as an estimate, not exact attribution. Expected: readers know the boundary.
  5. Keep the original fields when a plugin extends the report. Expected: other consumers are not broken.

This path does not change upstream behaviour; it only makes the loss visible. The bucket mixes estimates, so it cannot serve as precise attribution.

Verify and set bounds for DeepSeek Harness costs

Acceptance has one condition: the daily total equals the sum of attributed rows, and the gap no longer grows with auto windows.

  1. Run auto for ten minutes and record the daily and session totals before and after. Expected: both grow together.
  2. Switch back to a fixed preset and sample again. Expected: the gap stays flat.
  3. Check a conversation-only session and a tool-heavy session. Expected: the latter's review cost matches its call count.
  4. Watch for two days. Expected: no new ownerless residual.
  5. Compare the session-row growth under auto and a fixed preset. Expected: the difference is explained by review count.

Four limits apply: whether billing reads only sessionId is unverified, so compare one session after the change; the gap appears only under the auto preset and automatic approval; do not allocate total cost by call count, since review inputs are far longer than chat; and audit, rate limiting, and telemetry that depend on sessionId miss these calls too. For the related cost definition, see model window, cost, and configuration.

FAQ

Why does the DeepSeek Harness daily total match the relay invoice while session rows are more than 70% short?

Auto-review calls account for the gap in DeepSeek Harness. They are billed, so the daily total reconciles, but they carry no sessionId, so cost tracking cannot place them on a session row and leaves them in the daily residual.

Why does the DeepSeek Harness cost gap equal the number of tool calls inside auto windows?

Under the DeepSeek Harness auto preset, every tool call triggers one review and one model request. Reconstructing preset timelines and counting only tool/call events inside auto windows matches the gap day by day.

If I add sessionId to the review request, will DeepSeek Harness attribute the cost for sure?

DeepSeek Harness billing may not read GenerateOptions.sessionId, so adding the field is not guaranteed to attribute the cost. A provider-level marker would not change either. Compare one session before relying on it.

Without code changes, how do I make DeepSeek Harness costs reconcilable again?

Leave the DeepSeek Harness /permission auto preset and switch to a fixed preset with manual approval; the gap stops growing immediately. Past unattributed spend is not backfilled, so only the daily total can be checked.

Related Terms

sessionId
sessionId is the session identifier in DeepSeek Harness request options, written by the agent loop so routing, billing, and audit can attribute usage per session. A background review that omits it lands only in the global total.— https://github.com/deepseek-ai/deepseek-harness/discussions/10381
llm/stream waterfall
The llm/stream waterfall is DeepSeek Harness's onion-style interception chain for streaming model requests. Observers can read options and usage around the request, and the cost plugin obtains model, provider, and sessionId through it.— https://github.com/deepseek-ai/deepseek-harness/discussions/10381
purpose
purpose is the generation option that marks a request's use, currently allowing compaction and session titles. It determines whether a background call can be separated from ordinary chat in billing or reports.— https://github.com/deepseek-ai/deepseek-harness/discussions/10381

Sources