From 5788066510187e70e35518a1bd6945b4decd80fc Mon Sep 17 00:00:00 2001 From: Solveit Date: Fri, 20 Mar 2026 22:11:27 +0000 Subject: [PATCH 1/2] fix: clamp reserveTokensFloor to prevent negative memory flush threshold When reserveTokensFloor equals contextWindowTokens, the flush threshold becomes zero and memory flush never triggers. Clamp reserveTokens so the threshold always remains positive. Fixes #50611 --- src/auto-reply/reply/memory-flush.ts | 7 +++++-- src/auto-reply/reply/reply-state.test.ts | 13 ++++++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/auto-reply/reply/memory-flush.ts b/src/auto-reply/reply/memory-flush.ts index 95f6dbaa053..967f1007a0a 100644 --- a/src/auto-reply/reply/memory-flush.ts +++ b/src/auto-reply/reply/memory-flush.ts @@ -197,9 +197,12 @@ export function shouldRunMemoryFlush(params: { return false; } const contextWindow = Math.max(1, Math.floor(params.contextWindowTokens)); - const reserveTokens = Math.max(0, Math.floor(params.reserveTokensFloor)); const softThreshold = Math.max(0, Math.floor(params.softThresholdTokens)); - const threshold = Math.max(0, contextWindow - reserveTokens - softThreshold); + const reserveTokens = Math.min( + Math.max(0, Math.floor(params.reserveTokensFloor)), + Math.max(0, contextWindow - softThreshold - 1), + ); + const threshold = contextWindow - reserveTokens - softThreshold; if (threshold <= 0) { return false; } diff --git a/src/auto-reply/reply/reply-state.test.ts b/src/auto-reply/reply/reply-state.test.ts index f83d313e2d3..4db8c4c39d3 100644 --- a/src/auto-reply/reply/reply-state.test.ts +++ b/src/auto-reply/reply/reply-state.test.ts @@ -357,6 +357,17 @@ describe("shouldRunMemoryFlush", () => { }), ).toBe(false); }); + + it("triggers when reserveTokensFloor equals contextWindowTokens", () => { + expect( + shouldRunMemoryFlush({ + entry: { totalTokens: 199_000, compactionCount: 0 }, + contextWindowTokens: 200_000, + reserveTokensFloor: 200_000, + softThresholdTokens: 4_000, + }), + ).toBe(true); + }); }); describe("hasAlreadyFlushedForCurrentCompaction", () => { @@ -483,4 +494,4 @@ describe("incrementCompactionCount", () => { // totalTokens unchanged expect(stored[sessionKey].totalTokens).toBe(180_000); }); -}); +}); \ No newline at end of file From 1857ff82a1cb9098cfd731909a058800d5d44404 Mon Sep 17 00:00:00 2001 From: Solveit Date: Fri, 20 Mar 2026 22:49:48 +0000 Subject: [PATCH 2/2] fix: add console.warn when reserveTokensFloor is clamped Log a warning when reserveTokensFloor exceeds contextWindow - softThreshold - 1, so users are alerted to misconfigured values instead of silent clamping. --- pnpm-lock.yaml | 10 +++++----- src/auto-reply/reply/memory-flush.ts | 16 ++++++++++++---- src/auto-reply/reply/reply-state.test.ts | 2 +- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f821a4aa3c4..32c24d11f64 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -356,7 +356,7 @@ importers: version: 10.6.2 openclaw: specifier: '>=2026.3.11' - version: 2026.3.13(@napi-rs/canvas@0.1.95)(@types/express@5.0.6)(audio-decode@2.2.3)(jimp@1.6.0)(node-llama-cpp@3.16.2(typescript@5.9.3)) + version: 2026.3.13(@discordjs/opus@0.10.0)(@napi-rs/canvas@0.1.95)(@types/express@5.0.6)(audio-decode@2.2.3)(jimp@1.6.0)(node-llama-cpp@3.16.2(typescript@5.9.3)) extensions/huggingface: {} @@ -427,7 +427,7 @@ importers: dependencies: openclaw: specifier: '>=2026.3.11' - version: 2026.3.13(@napi-rs/canvas@0.1.95)(@types/express@5.0.6)(audio-decode@2.2.3)(jimp@1.6.0)(node-llama-cpp@3.16.2(typescript@5.9.3)) + version: 2026.3.13(@discordjs/opus@0.10.0)(@napi-rs/canvas@0.1.95)(@types/express@5.0.6)(audio-decode@2.2.3)(jimp@1.6.0)(node-llama-cpp@3.16.2(typescript@5.9.3)) extensions/memory-lancedb: dependencies: @@ -7499,7 +7499,7 @@ snapshots: dependencies: css-tree: 3.2.1 - '@buape/carbon@0.0.0-beta-20260216184201(hono@4.12.8)(opusscript@0.1.1)': + '@buape/carbon@0.0.0-beta-20260216184201(@discordjs/opus@0.10.0)(hono@4.12.8)(opusscript@0.1.1)': dependencies: '@types/node': 25.5.0 discord-api-types: 0.38.37 @@ -12436,11 +12436,11 @@ snapshots: ws: 8.19.0 zod: 4.3.6 - openclaw@2026.3.13(@napi-rs/canvas@0.1.95)(@types/express@5.0.6)(audio-decode@2.2.3)(jimp@1.6.0)(node-llama-cpp@3.16.2(typescript@5.9.3)): + openclaw@2026.3.13(@discordjs/opus@0.10.0)(@napi-rs/canvas@0.1.95)(@types/express@5.0.6)(audio-decode@2.2.3)(jimp@1.6.0)(node-llama-cpp@3.16.2(typescript@5.9.3)): dependencies: '@agentclientprotocol/sdk': 0.16.1(zod@4.3.6) '@aws-sdk/client-bedrock': 3.1009.0 - '@buape/carbon': 0.0.0-beta-20260216184201(hono@4.12.8)(opusscript@0.1.1) + '@buape/carbon': 0.0.0-beta-20260216184201(@discordjs/opus@0.10.0)(hono@4.12.8)(opusscript@0.1.1) '@clack/prompts': 1.1.0 '@discordjs/voice': 0.19.2(@discordjs/opus@0.10.0)(opusscript@0.1.1) '@grammyjs/runner': 2.0.3(grammy@1.41.1) diff --git a/src/auto-reply/reply/memory-flush.ts b/src/auto-reply/reply/memory-flush.ts index 967f1007a0a..5d68d55d45a 100644 --- a/src/auto-reply/reply/memory-flush.ts +++ b/src/auto-reply/reply/memory-flush.ts @@ -167,6 +167,8 @@ export function resolveMemoryFlushContextWindowTokens(params: { ); } +const _warnedReserve = new Set(); + export function shouldRunMemoryFlush(params: { entry?: Pick< SessionEntry, @@ -198,10 +200,16 @@ export function shouldRunMemoryFlush(params: { } const contextWindow = Math.max(1, Math.floor(params.contextWindowTokens)); const softThreshold = Math.max(0, Math.floor(params.softThresholdTokens)); - const reserveTokens = Math.min( - Math.max(0, Math.floor(params.reserveTokensFloor)), - Math.max(0, contextWindow - softThreshold - 1), - ); + const rawReserve = Math.max(0, Math.floor(params.reserveTokensFloor)); + const maxReserve = Math.max(0, contextWindow - softThreshold - 1); + const warnKey = `${rawReserve}:${maxReserve}`; + if (rawReserve > maxReserve && !_warnedReserve.has(warnKey)) { + _warnedReserve.add(warnKey); + console.warn( + `reserveTokensFloor (${rawReserve}) exceeds contextWindow - softThreshold - 1 (${maxReserve}), clamping to ${maxReserve}`, + ); + } + const reserveTokens = Math.min(rawReserve, maxReserve); const threshold = contextWindow - reserveTokens - softThreshold; if (threshold <= 0) { return false; diff --git a/src/auto-reply/reply/reply-state.test.ts b/src/auto-reply/reply/reply-state.test.ts index 4db8c4c39d3..0ba30d1f83b 100644 --- a/src/auto-reply/reply/reply-state.test.ts +++ b/src/auto-reply/reply/reply-state.test.ts @@ -494,4 +494,4 @@ describe("incrementCompactionCount", () => { // totalTokens unchanged expect(stored[sessionKey].totalTokens).toBe(180_000); }); -}); \ No newline at end of file +});