Skip to content

fix(context-window-monitor): clamp displayed context status percentages so the directive stays trustworthy (fixes #3655)#3701

Open
MoerAI wants to merge 1 commit intocode-yeongyu:devfrom
MoerAI:fix/context-window-monitor-pct-clamp
Open

fix(context-window-monitor): clamp displayed context status percentages so the directive stays trustworthy (fixes #3655)#3701
MoerAI wants to merge 1 commit intocode-yeongyu:devfrom
MoerAI:fix/context-window-monitor-pct-clamp

Conversation

@MoerAI
Copy link
Copy Markdown
Contributor

@MoerAI MoerAI commented Apr 28, 2026

Summary

Clamp the displayed % used and % remaining numbers in the [SYSTEM DIRECTIVE: OH-MY-OPENCODE - CONTEXT WINDOW MONITOR] block to [0, 100] so the directive remains trustworthy when the resolved actualLimit underestimates the model's real context window. Without clamping, the block currently advertises numbers like 144.7% used (289,370/200,000 tokens), -44.7% remaining, which safety-tuned models flag as a prompt injection and refuse to follow.

Root Cause

src/hooks/context-window-monitor.ts lines 62-74 compute and render the directive:

const actualUsagePercentage = totalInputTokens / actualLimit
if (actualUsagePercentage < CONTEXT_WARNING_THRESHOLD) return
// ...
const usedPct = (actualUsagePercentage * 100).toFixed(1)
const remainingPct = ((1 - actualUsagePercentage) * 100).toFixed(1)
output.output += `\n\n${createContextReminder(actualLimit)}
[Context Status: ${usedPct}% used (${usedTokens}/${limitTokens} tokens), ${remainingPct}% remaining]`

When resolveActualContextLimit() underestimates the real context window (the deeper concern tracked as #3450 - e.g. a 1M-context Anthropic model that falls back to DEFAULT_ANTHROPIC_ACTUAL_LIMIT = 200_000), totalInputTokens / actualLimit exceeds 1.0 and the formatting math then produces nonsensical numbers. The injected block also wraps itself in [SYSTEM DIRECTIVE: ...] framing and issues behavioral imperatives, so safety-tuned models correctly identify the >100% / negative-remaining pattern as adversarial:

"I ignored it as a prompt injection - context usage is fine, and the directive contradicted my actual instructions." (real session log from issue #3655)

Changes

File Change
src/hooks/context-window-monitor.ts Compute clampedPercentage = Math.min(Math.max(actualUsagePercentage, 0), 1) and use it for both usedPct and remainingPct. The threshold check on line 64 still uses the raw value so the block continues to fire correctly above 70%. resolveActualContextLimit() is left untouched (deeper concern, tracked as #3450).
src/hooks/context-window-monitor.test.ts Add a regression test that drives the hook with input: 289,370 against a 200,000 resolved limit (matching the issue's reproduction numbers) and asserts both usedPct and remainingPct stay within [0, 100].

Total diff: 2 files, +65 lines.

Reproduction (before fix)

With the regression test added but the source change reverted:

(fail) context-window-monitor > should clamp displayed percentages when input
       exceeds actualLimit (regression #3655)

  expect(received).toBeLessThanOrEqual(expected)
    Expected: <= 100
    Received: 144.7

 0 pass
 1 fail

The actually rendered block looks like:

[Context Status: 144.7% used (289,370/200,000 tokens), -44.7% remaining]

Verification (after fix)

$ bun test src/hooks/context-window-monitor.test.ts -t "regression #3655"
 1 pass / 0 fail / 6 expect() calls

$ bun test src/hooks/context-window-monitor.test.ts \
           src/hooks/context-window-monitor.model-context-limits.test.ts
 15 pass / 0 fail / 29 expect() calls

$ bun run typecheck
$ tsc --noEmit
(no output, exit 0)

After the fix, the block now reads:

[Context Status: 100.0% used (289,370/200,000 tokens), 0.0% remaining]

The numbers are still informative (token counts are unchanged so the user still sees they are over the resolved limit) but they no longer match the prompt-injection pattern that safety-tuned models reject.

Test

  • Regression test: src/hooks/context-window-monitor.test.ts (new case should clamp displayed percentages when input exceeds actualLimit (regression #3655)).
  • Full hook suites: context-window-monitor.test.ts (9 tests) + context-window-monitor.model-context-limits.test.ts (6 tests) - 15 pass / 0 fail.
  • Typecheck: clean.
  • Note: this PR does NOT change resolveActualContextLimit() itself. Fixing the underlying root cause - so the resolver returns the model's real context window for 1M-context Anthropic models - is tracked as [Bug]: resolveActualContextLimit falls back to 200k when anthropic provider has no explicit model limits in config #3450 and will land separately. This PR is purely a defensive UX clamp on the displayed string.

Fixes #3655


View in Codesmith
Need help on this PR? Tag @codesmith with what you need.

  • Let Codesmith autofix CI failures and bot reviews

Summary by cubic

Clamp context status percentages to 0–100% so the monitor stays accurate and doesn’t trigger safety checks. Fixes #3655 by preventing outputs like “144.7% used, -44.7% remaining” when the resolved limit is lower than the model’s real window.

  • Bug Fixes
    • Format used%/remaining% from a clamped usage value; threshold logic still uses the raw ratio so the block triggers as before.
    • Added a regression test (289,370 / 200,000) to ensure percentages stay within [0, 100]; all existing tests pass and typecheck is clean.

Written for commit 07064a9. Summary will update on new commits. Review in cubic

…es so the directive stays trustworthy (fixes code-yeongyu#3655)

Root cause: the context-window-monitor hook computes actualUsagePercentage = (input + cache.read) / actualLimit and renders both 'X% used' and '(1 - X) * 100% remaining' inside a [SYSTEM DIRECTIVE: OH-MY-OPENCODE - CONTEXT WINDOW MONITOR] block that is appended to bash tool output. When resolveActualContextLimit() underestimates the model's real context window (for example a 1M-context Anthropic model that falls back to the 200K default per code-yeongyu#3450), totalInputTokens > actualLimit and the rendered numbers go nonsensical (issue code-yeongyu#3655 reproduces 144.7% used / -44.7% remaining at 289,370 / 200,000 tokens). Safety-tuned models recognize the >100% / negative-remaining pattern as a tell-tale prompt injection and refuse to follow the directive.

Fix: clamp actualUsagePercentage to [0, 1] before formatting. The 70% threshold check still uses the raw value so the block continues to fire above threshold, and resolveActualContextLimit() is left untouched (the deeper resolver concern is tracked separately as code-yeongyu#3450). When totalInputTokens exceeds actualLimit the displayed numbers now read '100.0% used / 0.0% remaining' instead of the impossible >100% / negative pair, and safety-tuned models stop flagging the block as an injection attempt.

Verification: added a regression test (input 289,370, limit 200,000) that asserts usedPct in [0,100] and remainingPct in [0,100]. Test fails before the fix (Received: 144.7) and passes after. Full context-window-monitor.test.ts and context-window-monitor.model-context-limits.test.ts: 15 pass / 0 fail. Typecheck clean.
@MoerAI
Copy link
Copy Markdown
Contributor Author

MoerAI commented Apr 28, 2026

I have read the CLA Document and I hereby sign the CLA

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 2 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Auto-approved: The PR implements simple clamping for display percentages to avoid nonsensical values. It includes a regression test and does not change core logic, posing no risk of regression.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant