From 49a9c15988190c16fb07d84cf5912433251df0bc Mon Sep 17 00:00:00 2001 From: luvs01 <27862058+luvs01@users.noreply.github.com> Date: Tue, 22 Sep 2026 04:37:23 +0900 Subject: [PATCH 1/2] fix(cursor): bound external replay construction and drop quadratic indexOf Track the collapsed replay entry's index instead of rescanning entries per duplicate, and cap the synchronous replay window at CURSOR_EXTERNAL_REPLAY_MESSAGE_LIMIT (4096) before the smaller wire-size limits apply. Restoration of luvs01/opencodex#348, which was closed without a rationale comment while the fix was still absent. bun test tests/providers/cursor/cursor-repetition-breaker.test.ts + 8 related cursor files: 335 pass --- src/adapters/cursor/protobuf-request.ts | 16 ++++++++---- .../cursor/cursor-repetition-breaker.test.ts | 25 ++++++++++++++++++- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/adapters/cursor/protobuf-request.ts b/src/adapters/cursor/protobuf-request.ts index 83e076dcced..8a667654617 100644 --- a/src/adapters/cursor/protobuf-request.ts +++ b/src/adapters/cursor/protobuf-request.ts @@ -76,6 +76,8 @@ export const CURSOR_ROUTING_LEVEL_PARAMETER_ID = "optimization"; export const CURSOR_EXTERNAL_ROOT_BLOB_LIMIT = 192; /** Approximate prompt-size guard; tool schemas and protocol framing consume context separately. */ export const CURSOR_EXTERNAL_ROOT_BYTE_LIMIT = 512 * 1024; +/** Bound synchronous replay construction before the smaller wire-size limits are applied. */ +export const CURSOR_EXTERNAL_REPLAY_MESSAGE_LIMIT = 4096; /** Honest placeholder when native Composer history has a toolCall with no matching toolResult. */ export const CURSOR_MISSING_TOOL_RESULT = "[missing tool_result for this tool_use in history]"; /** @@ -311,6 +313,7 @@ function rootPromptMessages( const replayRuns = new Map(); const toolCallCounts = new Map(); @@ -337,17 +340,20 @@ function rootPromptMessages( // half, so losing it re-primes the self-reinforcing loop the breaker exists to end. { ...opts, text: marked, messageIndex: previous.entry.messageIndex ?? opts.messageIndex }, ); - entries[entries.indexOf(previous.entry)] = replacement; - replayRuns.set(role, { text: normalized, entry: replacement, length: runLength }); + entries[previous.entryIndex] = replacement; + replayRuns.set(role, { text: normalized, entry: replacement, entryIndex: previous.entryIndex, length: runLength }); return; } const entry = rootBlobCandidate(payload, role, opts); entries.push(entry); - replayRuns.set(role, { text: normalized, entry, length: 1 }); + replayRuns.set(role, { text: normalized, entry, entryIndex: entries.length - 1, length: 1 }); }; - for (let i = 0; i < messages.length; i++) { - if (i === activeUserIndex) break; + const replayEnd = activeUserIndex < 0 ? messages.length : activeUserIndex; + const replayStart = externalModel + ? Math.max(0, replayEnd - CURSOR_EXTERNAL_REPLAY_MESSAGE_LIMIT) + : 0; + for (let i = replayStart; i < replayEnd; i++) { const message = messages[i]; if (!message) continue; if (message.role === "user" || message.role === "developer") { diff --git a/tests/providers/cursor/cursor-repetition-breaker.test.ts b/tests/providers/cursor/cursor-repetition-breaker.test.ts index 48b9ca0ac2a..8987eb9f013 100644 --- a/tests/providers/cursor/cursor-repetition-breaker.test.ts +++ b/tests/providers/cursor/cursor-repetition-breaker.test.ts @@ -1,6 +1,9 @@ import { describe, expect, test } from "bun:test"; import { fromBinary } from "@bufbuild/protobuf"; -import { encodeCursorRunRequest } from "../../../src/adapters/cursor/protobuf-request"; +import { + CURSOR_EXTERNAL_REPLAY_MESSAGE_LIMIT, + encodeCursorRunRequest, +} from "../../../src/adapters/cursor/protobuf-request"; import { handleCursorNativeKv } from "../../../src/adapters/cursor/native-exec"; import { create } from "@bufbuild/protobuf"; import { @@ -169,4 +172,24 @@ describe("cursor external-replay repetition breaker (devlog 260826 gap-9)", () = expect(texts.filter(text => text.startsWith("[Tool Result]"))).toHaveLength(3); expect(texts.filter(text => text.includes("same tool call repeated 3 times"))).toHaveLength(1); }); + + test("bounds replay construction before processing an oversized history", () => { + const messages: OcxMessage[] = [ + { role: "user", content: "old turn", timestamp: 1 }, + ...Array.from({ length: 5 }, (_, index) => ({ + role: "assistant" as const, + content: REPEAT, + timestamp: index + 2, + })), + ...Array.from({ length: CURSOR_EXTERNAL_REPLAY_MESSAGE_LIMIT }, (_, index) => ({ + role: "assistant" as const, + content: index % 2 === 0 ? "recent A" : "recent B", + timestamp: index + 7, + })), + { role: "user", content: "continue", timestamp: CURSOR_EXTERNAL_REPLAY_MESSAGE_LIMIT + 7 }, + ] as OcxMessage[]; + + const texts = rootTexts(encode(messages)); + expect(texts.some(text => text.includes("Take a DIFFERENT action now"))).toBe(false); + }); }); From 68f74eb844b7650da21f82e7619cced3848aa571 Mon Sep 17 00:00:00 2001 From: luvs01 <27862058+luvs01@users.noreply.github.com> Date: Tue, 22 Sep 2026 07:30:10 +0900 Subject: [PATCH 2/2] test(cursor): assert oversized replay sheds all history roots Pin that an over-limit external replay emits only the system prompt root: no weakened breaker note, no stale REPEAT, and no recent tail fragment. --- tests/providers/cursor/cursor-repetition-breaker.test.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/providers/cursor/cursor-repetition-breaker.test.ts b/tests/providers/cursor/cursor-repetition-breaker.test.ts index 8987eb9f013..3a91bd6e156 100644 --- a/tests/providers/cursor/cursor-repetition-breaker.test.ts +++ b/tests/providers/cursor/cursor-repetition-breaker.test.ts @@ -191,5 +191,12 @@ describe("cursor external-replay repetition breaker (devlog 260826 gap-9)", () = const texts = rootTexts(encode(messages)); expect(texts.some(text => text.includes("Take a DIFFERENT action now"))).toBe(false); + // The oversized history does not ship at all: the envelope sheds every replayed + // entry rather than emitting a weakened note or a partial fragment, so only the + // system prompt root survives. The old REPEAT is provably gone — not just unnoted — + // and no recent tail leaks past the bound either. + expect(texts).toHaveLength(1); + expect(texts.some(text => text === REPEAT)).toBe(false); + expect(texts.some(text => text === "recent A" || text === "recent B")).toBe(false); }); });