diff --git a/.changeset/compaction-threshold-boundary.md b/.changeset/compaction-threshold-boundary.md new file mode 100644 index 00000000000..2e610dbefb0 --- /dev/null +++ b/.changeset/compaction-threshold-boundary.md @@ -0,0 +1,5 @@ +--- +"@kilocode/cli": patch +--- + +Prevent configured compaction thresholds from interrupting active tool sequences. diff --git a/packages/opencode/src/session/overflow.ts b/packages/opencode/src/session/overflow.ts index 64b2b795531..bd462f11b99 100644 --- a/packages/opencode/src/session/overflow.ts +++ b/packages/opencode/src/session/overflow.ts @@ -30,8 +30,7 @@ export function isOverflow(input: { if (input.model.limit.context === 0) return false const count = KiloSessionOverflow.count(input.tokens) // kilocode_change - // kilocode_change start - const cap = KiloSessionOverflow.limit({ cfg: input.cfg, model: input.model, usable: usable(input) }) - return count >= cap + // kilocode_change start - post-step checks are safety-only; economic thresholds run in preflight + return count >= usable(input) // kilocode_change end } diff --git a/packages/opencode/test/kilocode/session-overflow.test.ts b/packages/opencode/test/kilocode/session-overflow.test.ts index 4cca5369492..7f5c67afa16 100644 --- a/packages/opencode/test/kilocode/session-overflow.test.ts +++ b/packages/opencode/test/kilocode/session-overflow.test.ts @@ -47,16 +47,17 @@ function tokens(count: number): MessageV2.Assistant["tokens"] { return { input: count, output: 0, reasoning: 0, cache: { read: 0, write: 0 } } } -describe("Kilo auto-compaction threshold", () => { - test("triggers at the configured context percentage", () => { +describe("Kilo post-step compaction safety", () => { + test("ignores the configured threshold after a provider step", () => { const conf = cfg({ threshold_percent: 75 }) const mdl = model({ context: 200_000, output: 32_000 }) expect(isOverflow({ cfg: conf, model: mdl, tokens: tokens(149_999) })).toBe(false) - expect(isOverflow({ cfg: conf, model: mdl, tokens: tokens(150_000) })).toBe(true) + expect(isOverflow({ cfg: conf, model: mdl, tokens: tokens(167_999) })).toBe(false) + expect(isOverflow({ cfg: conf, model: mdl, tokens: tokens(168_000) })).toBe(true) }) - test("keeps the reserved safety trigger when it is lower", () => { + test("uses the usable context limit when the threshold is high", () => { const conf = cfg({ threshold_percent: 95 }) const mdl = model({ context: 200_000, output: 32_000 }) @@ -68,8 +69,8 @@ describe("Kilo auto-compaction threshold", () => { const conf = cfg({ threshold_percent: 75 }) const mdl = model({ context: 400_000, input: 200_000, output: 32_000 }) - expect(isOverflow({ cfg: conf, model: mdl, tokens: tokens(149_999) })).toBe(false) - expect(isOverflow({ cfg: conf, model: mdl, tokens: tokens(150_000) })).toBe(true) + expect(isOverflow({ cfg: conf, model: mdl, tokens: tokens(179_999) })).toBe(false) + expect(isOverflow({ cfg: conf, model: mdl, tokens: tokens(180_000) })).toBe(true) }) test("ignores a cleared threshold", () => { @@ -114,14 +115,14 @@ describe("Kilo auto-compaction threshold", () => { const conf = cfg({ threshold_percent: 75 }) const mdl = model({ context: 200_000, output: 32_000 }) - expect(isOverflow({ cfg: conf, model: mdl, tokens: { ...tokens(149_999), reasoning: 1 } })).toBe(true) + expect(isOverflow({ cfg: conf, model: mdl, tokens: { ...tokens(167_999), reasoning: 1 } })).toBe(true) }) test("falls back to provider total when normalized usage is unavailable", () => { const conf = cfg({ threshold_percent: 75 }) const mdl = model({ context: 200_000, output: 32_000 }) - expect(isOverflow({ cfg: conf, model: mdl, tokens: { ...tokens(0), total: 150_000 } })).toBe(true) + expect(isOverflow({ cfg: conf, model: mdl, tokens: { ...tokens(0), total: 168_000 } })).toBe(true) }) test("uses the output cap as the reserve for single-window gateway models", () => {