From 926ed923071a44a55a6d5bd8c9eae5736299370d Mon Sep 17 00:00:00 2001 From: xDarkicex <0509479@my.scccd.edu> Date: Tue, 28 Apr 2026 20:14:42 -0700 Subject: [PATCH] fix: stop leaking prePromptMessageCount to daemon in afterTurn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `...args` spread forwarded every framework-provided field to the daemon — prePromptMessageCount, tokenBudget, runtimeContext. The daemon uses prePromptMessageCount to skip messages by array position, and a stale value from the framework silently drops messages before persistence. The daemon already has content-hash dedup (afterTurnIngestedKeys), so the positional prePromptMessageCount hint is redundant for correctness. Strip it from both the kernel and RPC paths and use explicit params instead of the spread. Co-Authored-By: Claude Opus 4.7 --- src/context-engine.ts | 6 ++++-- test/integration/host-flow.test.ts | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/context-engine.ts b/src/context-engine.ts index 98bc9f2..e5a2b20 100644 --- a/src/context-engine.ts +++ b/src/context-engine.ts @@ -711,7 +711,6 @@ export function buildContextEngineFactory( sessionKey: args.sessionKey, userId: args.userId, messages, - prePromptMessageCount: args.prePromptMessageCount, isHeartbeat: args.isHeartbeat, }); await performAfterTurnPredictiveCompaction({ @@ -723,8 +722,11 @@ export function buildContextEngineFactory( } const rpc = await runtime.getRpc(); const result = await rpc.call("after_turn_kernel", { - ...args, + sessionId: args.sessionId, + sessionKey: args.sessionKey, + userId: args.userId, messages, + isHeartbeat: args.isHeartbeat, }); await performAfterTurnPredictiveCompaction({ sessionId: args.sessionId, diff --git a/test/integration/host-flow.test.ts b/test/integration/host-flow.test.ts index c13df1f..c9a3c93 100644 --- a/test/integration/host-flow.test.ts +++ b/test/integration/host-flow.test.ts @@ -445,7 +445,7 @@ test("compact omits invalid currentTokenCount values from the wire request", asy assert.equal("currentTokenCount" in params, false); }); -test("afterTurn forwards message arrays and pre-prompt counts correctly", async () => { +test("afterTurn forwards only daemon-relevant fields, strips prePromptMessageCount", async () => { const rpc = new StaticContractRpc(); const recallCache = createRecallCache(); const cfg: PluginConfig = { rpcTimeoutMs: 1000 }; @@ -469,7 +469,7 @@ test("afterTurn forwards message arrays and pre-prompt counts correctly", async assert.ok(params, "Expected after_turn_kernel to be called"); assert.equal(params.sessionId, "test-session"); assert.equal(params.userId, "test-user"); - assert.equal(params.prePromptMessageCount, 2); + assert.equal("prePromptMessageCount" in params, false, "prePromptMessageCount must not leak to daemon — daemon defaults to 0 and uses content-hash dedup"); assert.equal(params.isHeartbeat, false); assert.deepEqual(params.messages, mockMessages); });