Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/bright-taxis-share.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"kilo-code": minor
---

Redesigned context condensing thresholds so global behavior stays percentage-based while each profile can optionally override with either percent-of-limit or fixed token triggers.
14 changes: 14 additions & 0 deletions packages/types/src/global-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,17 @@ export const DEFAULT_CHECKPOINT_TIMEOUT_SECONDS = 15
* GlobalSettings
*/

// kilocode_change start
export const profileCondenseOverrideModeSchema = z.enum(["percent", "tokens"])
export const profileCondenseOverrideSchema = z.object({
enabled: z.boolean(),
mode: profileCondenseOverrideModeSchema,
percent: z.number(),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SUGGESTION]: The Zod schema uses bare z.number() for percent and tokens without range constraints. While the application layer clamps values (e.g., clampNumber in the UI and resolveCondenseTrigger on the backend), adding .min()/.max() here would provide defense-in-depth validation at the schema level.

For example:

percent: z.number().min(0).max(100),
tokens: z.number().min(0),

tokens: z.number(),
})
export type ProfileCondenseOverride = z.infer<typeof profileCondenseOverrideSchema>
// kilocode_change end

export const globalSettingsSchema = z.object({
currentApiConfigName: z.string().optional(),
listApiConfigMeta: z.array(providerSettingsEntrySchema).optional(),
Expand Down Expand Up @@ -244,6 +255,9 @@ export const globalSettingsSchema = z.object({
*/
enterBehavior: z.enum(["send", "newline"]).optional(),
profileThresholds: z.record(z.string(), z.number()).optional(),
// kilocode_change start
profileCondenseOverrides: z.record(z.string(), profileCondenseOverrideSchema).optional(),
// kilocode_change end
hasOpenedModeSelector: z.boolean().optional(),
hasCompletedOnboarding: z.boolean().optional(), // kilocode_change: Track if user has completed onboarding flow
lastModeExportPath: z.string().optional(),
Expand Down
9 changes: 9 additions & 0 deletions packages/types/src/vscode-extension-host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,9 @@ export type ExtensionState = Pick<
| "codebaseIndexConfig"
| "codebaseIndexModels"
| "profileThresholds"
// kilocode_change start
| "profileCondenseOverrides"
// kilocode_change end
| "systemNotificationsEnabled" // kilocode_change
| "includeDiagnosticMessages"
| "maxDiagnosticMessages"
Expand Down Expand Up @@ -640,6 +643,12 @@ export type ExtensionState = Pick<
// eslint-disable-next-line @typescript-eslint/no-explicit-any
marketplaceInstalledMetadata?: { project: Record<string, any>; global: Record<string, any> }
profileThresholds: Record<string, number>
// kilocode_change start
profileCondenseOverrides?: Record<
string,
{ enabled: boolean; mode: "percent" | "tokens"; percent: number; tokens: number }
>
// kilocode_change end
hasOpenedModeSelector: boolean
hasCompletedOnboarding?: boolean // kilocode_change: Track if user has completed onboarding flow
openRouterImageApiKey?: string
Expand Down
124 changes: 124 additions & 0 deletions src/core/context-management/__tests__/context-management.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1062,6 +1062,130 @@ describe("Context Management", () => {
})
})

// kilocode_change start
describe("profile condense overrides", () => {
const contextWindow = 100000
const maxTokens = 30000
const baseOptions = {
contextWindow,
maxTokens,
autoCondenseContext: true,
autoCondenseContextPercent: 70,
profileThresholds: {},
currentProfileId: "profile-1",
}

it("keeps global-only trigger behavior when profile override is disabled", () => {
const withoutOverrides = willManageContext({
...baseOptions,
totalTokens: 69999,
lastMessageTokens: 1,
})

const withDisabledOverride = willManageContext({
...baseOptions,
totalTokens: 69999,
lastMessageTokens: 1,
profileCondenseOverrides: {
"profile-1": {
enabled: false,
mode: "tokens",
percent: 80,
tokens: 1000,
},
},
})

expect(withDisabledOverride).toBe(withoutOverrides)
expect(withoutOverrides).toBe(true)
})

it("triggers at exact token threshold in profile token mode", () => {
const shouldNotTrigger = willManageContext({
...baseOptions,
totalTokens: 44998,
lastMessageTokens: 1,
profileCondenseOverrides: {
"profile-1": {
enabled: true,
mode: "tokens",
percent: 80,
tokens: 45000,
},
},
})
const shouldTrigger = willManageContext({
...baseOptions,
totalTokens: 44999,
lastMessageTokens: 1,
profileCondenseOverrides: {
"profile-1": {
enabled: true,
mode: "tokens",
percent: 80,
tokens: 45000,
},
},
})

expect(shouldNotTrigger).toBe(false)
expect(shouldTrigger).toBe(true)
})

it("converts profile percent mode using effective budget", () => {
// effective budget = 100000 * 0.9 - 30000 = 60000
// 50% of effective budget = 30000
const shouldNotTrigger = willManageContext({
...baseOptions,
totalTokens: 29998,
lastMessageTokens: 1,
profileCondenseOverrides: {
"profile-1": {
enabled: true,
mode: "percent",
percent: 50,
tokens: 45000,
},
},
})
const shouldTrigger = willManageContext({
...baseOptions,
totalTokens: 29999,
lastMessageTokens: 1,
profileCondenseOverrides: {
"profile-1": {
enabled: true,
mode: "percent",
percent: 50,
tokens: 45000,
},
},
})

expect(shouldNotTrigger).toBe(false)
expect(shouldTrigger).toBe(true)
})

it("always triggers on hard overflow regardless of override mode/value", () => {
const shouldTrigger = willManageContext({
...baseOptions,
totalTokens: 60000,
lastMessageTokens: 1,
profileCondenseOverrides: {
"profile-1": {
enabled: true,
mode: "tokens",
percent: 5,
tokens: 1,
},
},
})

expect(shouldTrigger).toBe(true)
})
})
// kilocode_change end

/**
* Tests for the getMaxTokens function (private but tested through manageContext)
*/
Expand Down
Loading
Loading