Handle OpenCode session events that carry the session ID under properties.info.id or properties.info.sessionID so background tasks and continuation hooks do not miss idle/error/delete events.
Add regression coverage for nested session.idle events completing background tasks and waking continuation hooks.
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 #3450), totalInputTokens > actualLimit and the rendered numbers go nonsensical (issue #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 #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.
- New context-limit-resolver.ts with resolveActualContextLimit() shared helper
- Anthropic provider detection now uses .includes('anthropic') instead of hard-coded IDs
- Both context-window-monitor and dynamic-truncator use the shared resolver
- Added missing test cases: Anthropic+1M disabled+cached limit, non-Anthropic without cache
- Add shared/system-directive.ts with SYSTEM_DIRECTIVE_PREFIX constant
- Unify all system message prefixes to [SYSTEM DIRECTIVE: OH-MY-OPENCODE - ...]
- Add isSystemDirective() filter to keyword-detector to skip system messages
- Update prometheus-md-only tests to use new prefix constants
- Update preemptive-compaction hook to use 1M limit when env vars set
- Update dynamic-truncator to use 1M limit for output truncation
- Update context-window-monitor to use 1M limit for usage tracking
Previously hardcoded 200k limits caused compaction at 140k tokens even
with 1M context enabled. Now respects env vars consistently with base
opencode implementation.
Fixes compaction triggering too early with Claude Sonnet 4.5 1M context.
Related to anomalyco/opencode#6660
Previously, token calculation accumulated ALL assistant messages' tokens,
causing incorrect usage display (e.g., 524.9%) after compaction.
Now uses only the last message's input tokens, which reflects the actual
current context window usage.
🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)