diff --git a/src/hooks/context-window-monitor.model-context-limits.test.ts b/src/hooks/context-window-monitor.model-context-limits.test.ts
new file mode 100644
index 000000000..2b2c9cc61
--- /dev/null
+++ b/src/hooks/context-window-monitor.model-context-limits.test.ts
@@ -0,0 +1,90 @@
+///
+
+import { describe, expect, it } from "bun:test"
+import { createContextWindowMonitorHook } from "./context-window-monitor"
+
+function createOutput() {
+ return { title: "", output: "original", metadata: null }
+}
+
+describe("context-window-monitor modelContextLimitsCache", () => {
+ it("does not append reminder below cached non-anthropic threshold", async () => {
+ // given
+ const modelContextLimitsCache = new Map()
+ modelContextLimitsCache.set("opencode/kimi-k2.5-free", 262144)
+
+ const hook = createContextWindowMonitorHook({} as never, {
+ anthropicContext1MEnabled: false,
+ modelContextLimitsCache,
+ })
+ const sessionID = "ses_non_anthropic_below_threshold"
+
+ await hook.event({
+ event: {
+ type: "message.updated",
+ properties: {
+ info: {
+ role: "assistant",
+ sessionID,
+ providerID: "opencode",
+ modelID: "kimi-k2.5-free",
+ finish: true,
+ tokens: {
+ input: 150000,
+ output: 0,
+ reasoning: 0,
+ cache: { read: 10000, write: 0 },
+ },
+ },
+ },
+ },
+ })
+
+ // when
+ const output = createOutput()
+ await hook["tool.execute.after"]({ tool: "bash", sessionID, callID: "call_1" }, output)
+
+ // then
+ expect(output.output).toBe("original")
+ })
+
+ it("appends reminder above cached non-anthropic threshold", async () => {
+ // given
+ const modelContextLimitsCache = new Map()
+ modelContextLimitsCache.set("opencode/kimi-k2.5-free", 262144)
+
+ const hook = createContextWindowMonitorHook({} as never, {
+ anthropicContext1MEnabled: false,
+ modelContextLimitsCache,
+ })
+ const sessionID = "ses_non_anthropic_above_threshold"
+
+ await hook.event({
+ event: {
+ type: "message.updated",
+ properties: {
+ info: {
+ role: "assistant",
+ sessionID,
+ providerID: "opencode",
+ modelID: "kimi-k2.5-free",
+ finish: true,
+ tokens: {
+ input: 180000,
+ output: 0,
+ reasoning: 0,
+ cache: { read: 10000, write: 0 },
+ },
+ },
+ },
+ },
+ })
+
+ // when
+ const output = createOutput()
+ await hook["tool.execute.after"]({ tool: "bash", sessionID, callID: "call_1" }, output)
+
+ // then
+ expect(output.output).toContain("context remaining")
+ })
+})
diff --git a/src/hooks/context-window-monitor.ts b/src/hooks/context-window-monitor.ts
index 399c0810c..f749bd4e5 100644
--- a/src/hooks/context-window-monitor.ts
+++ b/src/hooks/context-window-monitor.ts
@@ -7,6 +7,7 @@ const CONTEXT_WARNING_THRESHOLD = 0.70
type ModelCacheStateLike = {
anthropicContext1MEnabled: boolean
+ modelContextLimitsCache?: Map
}
function getAnthropicActualLimit(modelCacheState?: ModelCacheStateLike): number {
@@ -32,6 +33,7 @@ interface TokenInfo {
interface CachedTokenState {
providerID: string
+ modelID: string
tokens: TokenInfo
}
@@ -57,13 +59,19 @@ export function createContextWindowMonitorHook(
const cached = tokenCache.get(sessionID)
if (!cached) return
- if (!isAnthropicProvider(cached.providerID)) return
+ const cachedLimit = modelCacheState?.modelContextLimitsCache?.get(
+ `${cached.providerID}/${cached.modelID}`
+ )
+ const actualLimit =
+ cachedLimit ??
+ (isAnthropicProvider(cached.providerID) ? getAnthropicActualLimit(modelCacheState) : null)
+
+ if (!actualLimit) return
const lastTokens = cached.tokens
const totalInputTokens = (lastTokens?.input ?? 0) + (lastTokens?.cache?.read ?? 0)
- const actualUsagePercentage =
- totalInputTokens / getAnthropicActualLimit(modelCacheState)
+ const actualUsagePercentage = totalInputTokens / actualLimit
if (actualUsagePercentage < CONTEXT_WARNING_THRESHOLD) return
@@ -95,6 +103,7 @@ export function createContextWindowMonitorHook(
role?: string
sessionID?: string
providerID?: string
+ modelID?: string
finish?: boolean
tokens?: TokenInfo
} | undefined
@@ -104,6 +113,7 @@ export function createContextWindowMonitorHook(
tokenCache.set(info.sessionID, {
providerID: info.providerID,
+ modelID: info.modelID ?? "",
tokens: info.tokens,
})
}