Summary
The per-request token calibration records a value that can exceed the model's entire context
window by hundreds of times. In the worst observed snapshot the field holds 58,043,860
while the very same snapshot reports an actual usage of 86,650 tokens and a context limit
of 95,232 — i.e. roughly 670× the real figure.
The value is not merely cosmetic: it is emitted as part of a live budget snapshot with
"source": "calibrated" and drives the compaction decision ("state": "blocking"), and the
validator that guards it only checks that the number is finite and positive.
Environment
| Item |
Value |
| Build |
v2026.923.0, commit 5a0efbc5106e7c80c3df8e20184c439ed3d0603b |
| Provider / model |
deepseek (protocol: openai), deepseek-flash |
| Context config |
maxContextTokens: 95232, warningRatio: 0.8, blockingRatio: 0.9 |
Observed evidence
runtime.log:771, an auto-compaction trigger — note the internal inconsistency: the snapshot
reports 86,650 tokens of real usage and, in the same object, a 58-million-token "actual input":
2026-09-23T19:43:39.258Z [gateway] [context:auto-compact] policy_trigger {
"sessionId": "web-s_b412fb22-467c-4de7-ba2b-5408173ccdbc",
"reason": "blocking_threshold",
"snapshot": {
"tokens": 86650,
"estimateSource": "usage",
"usageTokens": 86650,
"localEstimateTokens": 57767,
"calibrationActualInputTokens": 58043860,
"calibrationEstimatedInputTokens": 56472,
"totalContextTokens": 128000,
"maxContextTokens": 95232,
"state": "blocking",
"ratio": 0.9098832325268817,
"source": "calibrated"
}
}
Distribution across 3,000 logged values:
| Statistic |
Value |
| Minimum |
3,726 |
| Median |
61,078 |
| Maximum |
58,043,860 |
| Values above 1,000,000 |
38 |
Other observed outliers include 55,012,552 (repeated). Typical healthy rows look correct,
e.g. "tokens":80153,"usageTokens":80153,"localEstimateTokens":74574, "calibrationActualInputTokens":76465 — so the field is right most of the time and wrong
catastrophically in a minority of cases.
The same field also appears in the persisted session transcript
(.../always-on/worktrees/Users-Administrator-.pilotdeck/chats/web-s_fd5c62fa-....jsonl), so
the bad value is written to disk as well as logged.
Root cause
The value is a sum of three usage fields
// dist/src/context/budget/TokenAccountingRuntime.js:185
export function actualInputTokensFromUsage(usage) {
if (!usage) return undefined;
let total = 0;
for (const tokens of [usage.inputTokens, usage.cacheReadTokens, usage.cacheWriteTokens]) {
if (typeof tokens === "number" && Number.isFinite(tokens) && tokens > 0) {
total += tokens;
}
}
return total > 0 ? Math.ceil(total) : undefined;
}
That summation is correct for a single response's usage. The problem is the usage object it
is handed.
The usage object accumulates instead of being replaced
The assembler that produces the usage object merges every usage event it sees:
// dist/src/model/streaming/assembleModelMessage.js
// :8
usage: {},
// :58
case "usage":
state.usage = mergeUsage(state.usage, event.usage);
// :81
usage: hasUsage(state.usage) ? state.usage : undefined,
and mergeUsage adds every numeric field rather than taking the latest value:
// dist/src/agent/loop/AgentLoop.js:2736
function mergeUsage(first, second) {
if (!second) {
return first;
}
return {
inputTokens: add(first.inputTokens, second.inputTokens),
outputTokens: add(first.outputTokens, second.outputTokens),
cacheReadTokens: add(first.cacheReadTokens, second.cacheReadTokens),
cacheWriteTokens: add(first.cacheWriteTokens, second.cacheWriteTokens),
totalTokens: add(first.totalTokens, second.totalTokens),
};
}
// :2748
function add(first, second) {
if (first === undefined && second === undefined) {
return undefined;
}
return (first ?? 0) + (second ?? 0);
}
The OpenAI-compatible streaming path emits a usage event for every chunk that carries a
usage object rather than only once per response:
// dist/src/model/providers/openai/stream.js:41
const usage = normalizeOpenAIUsage(chunk.usage);
if (usage) {
events.push({ type: "usage", usage, raw });
}
So for a response whose usage is reported on more than one chunk (or replayed across a
retry/continuation that reuses the same assembler state), the individual counts are
summed, and the total grows without relation to the real single-request input size. The
result is then read back as the "actual input tokens" for that request:
// dist/src/agent/loop/AgentLoop.js:621
this.recordTokenCalibration(calibrationRequest, assembled.usage, requestInputEstimate);
// :1813
recordTokenCalibration(request, usage, estimatedInputTokens) {
const actualInputTokens = actualInputTokensFromUsage(usage);
...
// :1817-1822 — stored per route; overwrite, not accumulated
this.tokenCalibrationByRoute.set(route, { provider, model, actualInputTokens,
estimatedInputTokens });
Because the entry is stored per route and overwritten, a single inflated value is then
reused for that provider/model route on subsequent turns, which is consistent with the
repeated occurrences of the identical value 55,012,552 and 58,043,860 in the logs.
Nothing rejects an implausible value
The only validation applied to a stored calibration is finiteness and positivity:
// dist/src/context/budget/TokenAccountingRuntime.js:198 (matchingCalibration)
if (!Number.isFinite(calibration.actualInputTokens) || calibration.actualInputTokens <= 0) {
return undefined;
}
if (!Number.isFinite(calibration.estimatedInputTokens) || calibration.estimatedInputTokens <= 0) {
return undefined;
}
return calibration;
A value of 58,043,860 passes this check. Paired with the accompanying
calibrationEstimatedInputTokens: 56,472, the implied actual/estimate ratio is about
1028×, and the snapshot is still published with "source": "calibrated".
What I could and could not establish
I verified: the field's computation, the accumulating merge, the emission of multiple usage
events per response, the per-route overwrite, the absence of any plausibility check, and the
real observed values.
I could not conclusively determine how much of the budget calculation is corrupted by the
bad value in the captured case. In the log line above, tokens, usageTokens and the
ratio are all internally consistent from the real usage (86,650 / 95,232 = 0.9099), which
means that particular decision was driven by the true usage while the inflated calibration was
carried alongside it. A distorted value could still poison estimates on turns where the
provider reports no usage and the calibration ratio is the only available scaling factor. The
report therefore treats the corrupted field as a confirmed defect whose downstream blast radius
is only partially characterised.
Impact
- A corrupted per-request "ground truth" that is stored per route and reused, so the error
persists for the lifetime of the route.
- A budget snapshot published with
source: "calibrated" that carries a value two to three
orders of magnitude wrong.
- The bad value is persisted into session transcripts on disk.
- Any calibration-driven estimate that does use the ratio is off by a factor of roughly 10³,
which would trip the blocking threshold immediately and force unnecessary compaction.
Severity: moderate (no crash observed from this path, but it corrupts the input to context
budgeting and is silently persisted).
Suggested fix
- Do not merge usage events by addition for a single response. Either treat the last
usage event as authoritative (providers that split usage across chunks generally send the
cumulative totals in the final chunk), or accumulate per stream and reset the assembler's
usage when a response completes. assembleModelMessage.js:59 and the mergeUsage call
sites in AgentLoop.js are the places to change; mergeUsage is also used at
AgentLoop.js:614 to combine per-turn usage, so the fix must distinguish
"merge across turns" (addition is correct) from "merge within one response" (addition is
wrong).
- Sanity-check the calibration before storing it. Reject or discard a calibration whose
actualInputTokens exceeds the model's context window, or whose implied ratio against
estimatedInputTokens is implausible, and fall back to undefined (which
matchingCalibration already handles as "no calibration").
- Consider not persisting the raw calibration values into session transcripts, or persisting
them only after the sanity check, so a fluke is not written to disk indefinitely.
Summary
The per-request token calibration records a value that can exceed the model's entire context
window by hundreds of times. In the worst observed snapshot the field holds 58,043,860
while the very same snapshot reports an actual usage of 86,650 tokens and a context limit
of 95,232 — i.e. roughly 670× the real figure.
The value is not merely cosmetic: it is emitted as part of a live budget snapshot with
"source": "calibrated"and drives the compaction decision ("state": "blocking"), and thevalidator that guards it only checks that the number is finite and positive.
Environment
v2026.923.0, commit5a0efbc5106e7c80c3df8e20184c439ed3d0603bdeepseek(protocol: openai),deepseek-flashmaxContextTokens: 95232,warningRatio: 0.8,blockingRatio: 0.9Observed evidence
runtime.log:771, an auto-compaction trigger — note the internal inconsistency: the snapshotreports 86,650 tokens of real usage and, in the same object, a 58-million-token "actual input":
Distribution across 3,000 logged values:
Other observed outliers include
55,012,552(repeated). Typical healthy rows look correct,e.g.
"tokens":80153,"usageTokens":80153,"localEstimateTokens":74574, "calibrationActualInputTokens":76465— so the field is right most of the time and wrongcatastrophically in a minority of cases.
The same field also appears in the persisted session transcript
(
.../always-on/worktrees/Users-Administrator-.pilotdeck/chats/web-s_fd5c62fa-....jsonl), sothe bad value is written to disk as well as logged.
Root cause
The value is a sum of three usage fields
That summation is correct for a single response's usage. The problem is the usage object it
is handed.
The usage object accumulates instead of being replaced
The assembler that produces the usage object merges every usage event it sees:
and
mergeUsageadds every numeric field rather than taking the latest value:The OpenAI-compatible streaming path emits a usage event for every chunk that carries a
usageobject rather than only once per response:So for a response whose usage is reported on more than one chunk (or replayed across a
retry/continuation that reuses the same assembler state), the individual counts are
summed, and the total grows without relation to the real single-request input size. The
result is then read back as the "actual input tokens" for that request:
Because the entry is stored per route and overwritten, a single inflated value is then
reused for that provider/model route on subsequent turns, which is consistent with the
repeated occurrences of the identical value
55,012,552and58,043,860in the logs.Nothing rejects an implausible value
The only validation applied to a stored calibration is finiteness and positivity:
A value of 58,043,860 passes this check. Paired with the accompanying
calibrationEstimatedInputTokens: 56,472, the implied actual/estimate ratio is about1028×, and the snapshot is still published with
"source": "calibrated".What I could and could not establish
I verified: the field's computation, the accumulating merge, the emission of multiple usage
events per response, the per-route overwrite, the absence of any plausibility check, and the
real observed values.
I could not conclusively determine how much of the budget calculation is corrupted by the
bad value in the captured case. In the log line above,
tokens,usageTokensand theratioare all internally consistent from the real usage (86,650 / 95,232 = 0.9099), whichmeans that particular decision was driven by the true usage while the inflated calibration was
carried alongside it. A distorted value could still poison estimates on turns where the
provider reports no usage and the calibration ratio is the only available scaling factor. The
report therefore treats the corrupted field as a confirmed defect whose downstream blast radius
is only partially characterised.
Impact
persists for the lifetime of the route.
source: "calibrated"that carries a value two to threeorders of magnitude wrong.
which would trip the blocking threshold immediately and force unnecessary compaction.
Severity: moderate (no crash observed from this path, but it corrupts the input to context
budgeting and is silently persisted).
Suggested fix
usage event as authoritative (providers that split usage across chunks generally send the
cumulative totals in the final chunk), or accumulate per stream and reset the assembler's
usage when a response completes.
assembleModelMessage.js:59and themergeUsagecallsites in
AgentLoop.jsare the places to change;mergeUsageis also used atAgentLoop.js:614to combine per-turn usage, so the fix must distinguish"merge across turns" (addition is correct) from "merge within one response" (addition is
wrong).
actualInputTokensexceeds the model's context window, or whose implied ratio againstestimatedInputTokensis implausible, and fall back toundefined(whichmatchingCalibrationalready handles as "no calibration").them only after the sanity check, so a fluke is not written to disk indefinitely.