Skip to content
Merged
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/remove-micro-compaction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---

Remove the experimental micro compaction feature and its toggle from the experiments panel.
3 changes: 1 addition & 2 deletions apps/kimi-code/src/tui/commands/config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {
effectiveModelAlias,
type ExperimentalFeatureState,
type FlagId,
type ModelAlias,
type PermissionMode,
type Session,
Expand Down Expand Up @@ -590,7 +589,7 @@ export async function applyExperimentalFeatureChanges(
return;
}

const experimental: Partial<Record<FlagId, boolean>> = {};
const experimental: Record<string, boolean> = {};
for (const change of changes) {
experimental[change.id] = change.enabled;
}
Expand Down
6 changes: 4 additions & 2 deletions docs/en/configuration/config-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ reserved_context_size = 50000
max_running_tasks = 4
keep_alive_on_exit = false

[experimental]
micro_compaction = false
# [experimental]
# micro_compaction = false # disabled: micro compaction has been removed

[[permission.rules]]
decision = "allow"
Expand Down Expand Up @@ -200,13 +200,15 @@ You can also switch models temporarily without touching the config file — by s

`keep_alive_on_exit` can be overridden by the `KIMI_CODE_BACKGROUND_KEEP_ALIVE_ON_EXIT` environment variable, which takes higher priority than `config.toml`.

<!--
## `experimental`
Comment on lines +203 to 204

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 Badge Keep the experimental anchor or remove its link

Commenting out the whole ## experimental section removes the #experimental anchor, but the top-level fields table above still links experimental to that anchor. Users clicking the config-field reference now land nowhere in both locale pages; either remove/update that link or render a short section that says no experimental flags are currently registered.

Useful? React with 👍 / 👎.

Comment on lines +203 to 204

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 Badge Keep the experimental anchor reachable

With this section hidden in an HTML comment, the rendered page no longer has an #experimental heading, but the top-level options table still links experimental to #experimental (and the zh mirror does the same). Users clicking the config table get a dead in-page link; either remove/update the table row or leave a short visible section explaining that no experimental flags are currently available.

Useful? React with 👍 / 👎.


`experimental` stores persistent overrides for experimental-feature flags. Currently, `micro_compaction` is the only user-facing entry and defaults to `false`; set it to `true` to enable automatic trimming of older large tool results.

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `micro_compaction` | `boolean` | `false` | Trim older large tool results from context while preserving recent conversation |
-->

## `services`

Expand Down
1 change: 0 additions & 1 deletion docs/en/configuration/env-vars.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ Switches that control the behavior of subsystems such as telemetry, background t
| `KIMI_CODE_PLUGIN_MARKETPLACE_URL` | Override the plugin marketplace JSON loaded by `/plugins`; useful for dev loopback servers, staging CDN files, or alternate marketplace directories | `https://code.kimi.com/kimi-code/plugins/marketplace.json`; also accepts `http://`, `file://` URLs, and local paths |
| `KIMI_CODE_AGENT_SWARM_MAX_CONCURRENCY` | Cap how many AgentSwarm subagents run concurrently during the initial ramp; leave unset for no cap | Positive integer; invalid values fail fast |
| `KIMI_CODE_EXPERIMENTAL_FLAG` | Enable all registered experimental features for this process | `1`, `true`, `yes`, `on` |
| `KIMI_CODE_EXPERIMENTAL_MICRO_COMPACTION` | Override [`[experimental].micro_compaction`](./config-files.md#experimental) for this process | Truthy or falsy |
| `KIMI_SHELL_PATH` | Override the Git Bash path on Windows (used when auto-detection fails) | Absolute path |
| `KIMI_MODEL_MAX_COMPLETION_TOKENS` | Hard cap on `max_completion_tokens` per LLM step; applies to the `kimi` provider only | Positive integer; `0` or negative disables clamping |
| `KIMI_MODEL_TEMPERATURE` | Sampling temperature for every request; applies to the `kimi` provider only (global — independent of `KIMI_MODEL_NAME`) | Number, e.g. `0.3` |
Expand Down
6 changes: 4 additions & 2 deletions docs/zh/configuration/config-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ reserved_context_size = 50000
max_running_tasks = 4
keep_alive_on_exit = false

[experimental]
micro_compaction = false
# [experimental]
# micro_compaction = false # disabled: micro compaction has been removed

[[permission.rules]]
decision = "allow"
Expand Down Expand Up @@ -200,13 +200,15 @@ default_effort = "high"

`keep_alive_on_exit` 可被环境变量 `KIMI_CODE_BACKGROUND_KEEP_ALIVE_ON_EXIT` 覆盖,优先级高于配置文件。

<!--
## `experimental`

`experimental` 存放实验功能 flag 的持久化覆盖。目前 `micro_compaction` 是唯一用户可见的字段,默认值为 `false`;如需自动清理较旧的大型工具结果,把它设为 `true`。

| 字段 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `micro_compaction` | `boolean` | `false` | 清理较旧的大型工具结果内容,同时保留最近对话 |
-->

## `services`

Expand Down
1 change: 0 additions & 1 deletion docs/zh/configuration/env-vars.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ kimi
| `KIMI_CODE_PLUGIN_MARKETPLACE_URL` | 覆盖 `/plugins` 加载的 plugin marketplace JSON,适合 dev loopback server、测试 CDN 文件或替换 marketplace 目录 | `https://code.kimi.com/kimi-code/plugins/marketplace.json`;也接受 `http://`、`file://` URL 和本地路径 |
| `KIMI_CODE_AGENT_SWARM_MAX_CONCURRENCY` | 限制 AgentSwarm 初始提升并发阶段可同时运行的子 Agent 数量;不设置表示不限制 | 正整数;非法值会立即失败 |
| `KIMI_CODE_EXPERIMENTAL_FLAG` | 在当前进程启用所有已注册的实验功能 | `1`、`true`、`yes`、`on` |
| `KIMI_CODE_EXPERIMENTAL_MICRO_COMPACTION` | 覆盖当前进程的 [`[experimental].micro_compaction`](./config-files.md#experimental) | 真值或假值 |
| `KIMI_SHELL_PATH` | Windows 上覆盖 Git Bash 路径(自动探测失败时使用) | 绝对路径 |
| `KIMI_MODEL_MAX_COMPLETION_TOKENS` | 单步 LLM 请求的 `max_completion_tokens` 硬上限,仅对 `kimi` 供应商生效 | 正整数;`0` 或负数禁用 clamp |
| `KIMI_MODEL_TEMPERATURE` | 每次请求的采样温度,仅对 `kimi` 供应商生效(全局生效,不依赖 `KIMI_MODEL_NAME`) | 数字,如 `0.3` |
Expand Down
163 changes: 87 additions & 76 deletions packages/agent-core/src/agent/compaction/micro.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import type { ContentPart } from '@moonshot-ai/kosong';
// Micro compaction is disabled; ContentPart is no longer referenced here.
// import type { ContentPart } from '@moonshot-ai/kosong';

import type { Agent } from '..';
import type { ContextMessage } from '../context';
import {
estimateTokensForContentParts,
estimateTokensForMessages,
// estimateTokensForMessages, // disabled with micro compaction
} from '../../utils/tokens';

export interface MicroCompactionConfig {
Expand Down Expand Up @@ -47,83 +48,93 @@ export class MicroCompaction {
}

detect(): void {
if (!this.agent.experimentalFlags.enabled('micro_compaction')) return;

const config = this.config;
const { history, lastAssistantAt } = this.agent.context;
const cacheAgeMs = lastAssistantAt === null ? null : Date.now() - lastAssistantAt;
const cacheMissed = cacheAgeMs !== null && cacheAgeMs >= config.cacheMissedThresholdMs;
if (!cacheMissed) return;

const maxContextTokens = this.agent.config.modelCapabilities.max_context_tokens;
const contextTokens = this.agent.context.tokenCountWithPending;
const contextUsageRatio =
maxContextTokens !== undefined && maxContextTokens > 0
? contextTokens / maxContextTokens
: 1;
if (contextUsageRatio < config.minContextUsageRatio) return;

const previousCutoff = this.cutoff;
const nextCutoff = Math.max(0, history.length - config.keepRecentMessages);
this.apply(nextCutoff);
if (previousCutoff !== nextCutoff) {
const effect = this.measureEffect(history, nextCutoff);
const previousEffect = this.measureEffect(history, previousCutoff);
const rawContextTokens = estimateTokensForMessages(history);
// Whole-context length before/after this cutoff change, mirroring the
// `tokens_before`/`tokens_after` fields on `compaction_finished` so the
// two compaction paths can be compared on the same axis.
const tokensBefore =
rawContextTokens -
previousEffect.truncatedToolResultTokensBefore +
previousEffect.truncatedToolResultTokensAfter;
const tokensAfter =
rawContextTokens -
effect.truncatedToolResultTokensBefore +
effect.truncatedToolResultTokensAfter;
this.agent.telemetry.track('micro_compaction_finished', {
keep_recent_messages: config.keepRecentMessages,
min_content_tokens: config.minContentTokens,
cache_missed_threshold_ms: config.cacheMissedThresholdMs,
truncated_marker: config.truncatedMarker,
min_context_usage_ratio: config.minContextUsageRatio,
truncated_tool_result_count: effect.truncatedToolResultCount,
truncated_tool_result_tokens_before: effect.truncatedToolResultTokensBefore,
truncated_tool_result_tokens_after: effect.truncatedToolResultTokensAfter,
tokens_before: tokensBefore,
tokens_after: tokensAfter,
previous_cutoff: previousCutoff,
cutoff: nextCutoff,
message_count: history.length,
cache_age_ms: cacheAgeMs,
thinking_effort: this.agent.config.thinkingEffort,
});
}
// Micro compaction is disabled: the `micro_compaction` experimental flag has
// been removed from the registry, so detection is intentionally a no-op.
return;

// Original implementation (disabled):
// if (!this.agent.experimentalFlags.enabled('micro_compaction')) return;
//
// const config = this.config;
// const { history, lastAssistantAt } = this.agent.context;
// const cacheAgeMs = lastAssistantAt === null ? null : Date.now() - lastAssistantAt;
// const cacheMissed = cacheAgeMs !== null && cacheAgeMs >= config.cacheMissedThresholdMs;
// if (!cacheMissed) return;
//
// const maxContextTokens = this.agent.config.modelCapabilities.max_context_tokens;
// const contextTokens = this.agent.context.tokenCountWithPending;
// const contextUsageRatio =
// maxContextTokens !== undefined && maxContextTokens > 0
// ? contextTokens / maxContextTokens
// : 1;
// if (contextUsageRatio < config.minContextUsageRatio) return;
//
// const previousCutoff = this.cutoff;
// const nextCutoff = Math.max(0, history.length - config.keepRecentMessages);
// this.apply(nextCutoff);
// if (previousCutoff !== nextCutoff) {
// const effect = this.measureEffect(history, nextCutoff);
// const previousEffect = this.measureEffect(history, previousCutoff);
// const rawContextTokens = estimateTokensForMessages(history);
// // Whole-context length before/after this cutoff change, mirroring the
// // `tokens_before`/`tokens_after` fields on `compaction_finished` so the
// // two compaction paths can be compared on the same axis.
// const tokensBefore =
// rawContextTokens -
// previousEffect.truncatedToolResultTokensBefore +
// previousEffect.truncatedToolResultTokensAfter;
// const tokensAfter =
// rawContextTokens -
// effect.truncatedToolResultTokensBefore +
// effect.truncatedToolResultTokensAfter;
// this.agent.telemetry.track('micro_compaction_finished', {
// keep_recent_messages: config.keepRecentMessages,
// min_content_tokens: config.minContentTokens,
// cache_missed_threshold_ms: config.cacheMissedThresholdMs,
// truncated_marker: config.truncatedMarker,
// min_context_usage_ratio: config.minContextUsageRatio,
// truncated_tool_result_count: effect.truncatedToolResultCount,
// truncated_tool_result_tokens_before: effect.truncatedToolResultTokensBefore,
// truncated_tool_result_tokens_after: effect.truncatedToolResultTokensAfter,
// tokens_before: tokensBefore,
// tokens_after: tokensAfter,
// previous_cutoff: previousCutoff,
// cutoff: nextCutoff,
// message_count: history.length,
// cache_age_ms: cacheAgeMs,
// thinking_effort: this.agent.config.thinkingEffort,
// });
// }
}

compact(messages: readonly ContextMessage[]): readonly ContextMessage[] {
if (!this.agent.experimentalFlags.enabled('micro_compaction')) return messages;

const config = this.config;
const result: ContextMessage[] = [];
let i = 0;
for (const msg of messages) {
if (
i < this.cutoff &&
msg.role === 'tool' &&
msg.toolCallId !== undefined &&
estimateTokensForContentParts(msg.content) >= config.minContentTokens
) {
result.push({
...msg,
content: [{ type: 'text', text: config.truncatedMarker } satisfies ContentPart],
});
} else {
result.push(msg);
}
i++;
}
return result;
// Micro compaction is disabled: the `micro_compaction` experimental flag has
// been removed from the registry, so messages are always returned unchanged.
return messages;

// Original implementation (disabled):
// if (!this.agent.experimentalFlags.enabled('micro_compaction')) return messages;
//
// const config = this.config;
// const result: ContextMessage[] = [];
// let i = 0;
// for (const msg of messages) {
// if (
// i < this.cutoff &&
// msg.role === 'tool' &&
// msg.toolCallId !== undefined &&
// estimateTokensForContentParts(msg.content) >= config.minContentTokens
// ) {
// result.push({
// ...msg,
// content: [{ type: 'text', text: config.truncatedMarker } satisfies ContentPart],
// });
// } else {
// result.push(msg);
// }
// i++;
// }
// return result;
}

private measureEffect(
Expand Down
19 changes: 11 additions & 8 deletions packages/agent-core/src/flags/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@ import type { FlagDefinitionInput } from './types';
* not equal the master switch 'KIMI_CODE_EXPERIMENTAL_FLAG'; `id` must not be 'flag'.
*/
export const FLAG_DEFINITIONS = [
{
id: 'micro_compaction',
title: 'Micro compaction',
description: 'Trim older large tool results from context while keeping recent conversation intact.',
env: 'KIMI_CODE_EXPERIMENTAL_MICRO_COMPACTION',
default: false,
surface: 'core',
},
// Micro compaction has been disabled and removed: the capability cannot be
// enabled via env, config, or the master experimental switch. The entry is
// kept here commented out so it can be restored if the feature is revived.
// {
// id: 'micro_compaction',
// title: 'Micro compaction',
// description: 'Trim older large tool results from context while keeping recent conversation intact.',
// env: 'KIMI_CODE_EXPERIMENTAL_MICRO_COMPACTION',
// default: false,
// surface: 'core',
// },
] as const satisfies readonly FlagDefinitionInput[];

/** Literal union of registered flag ids. */
Expand Down
5 changes: 4 additions & 1 deletion packages/agent-core/src/flags/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ export type ExperimentalFlagConfig = Partial<Record<FlagId, boolean>>;
export type ExperimentalFlagSource = 'master-env' | 'env' | 'config' | 'default';

export interface ExperimentalFeatureState {
readonly id: FlagId;
/** Feature id. Typed as `string` because this is a runtime snapshot that
* crosses the SDK/RPC boundary and must remain usable even when no flags are
* registered (in which case the internal `FlagId` union collapses to `never`). */
readonly id: string;
readonly title: string;
readonly description: string;
readonly surface: FlagSurface;
Expand Down
4 changes: 3 additions & 1 deletion packages/agent-core/test/agent/basic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ it('creates an independent agent with a scoped experimental flag resolver', () =
experimentalFlags: new FlagResolver({}, FLAG_DEFINITIONS),
});

expect(ctx.agent.experimentalFlags.enabled('micro_compaction')).toBe(false);
// No experimental flags are currently registered, so the scoped resolver
// reports none enabled.
expect(ctx.agent.experimentalFlags.enabledIds()).toEqual([]);
});

it('runs a text-only agent turn from prompt to completion', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,9 @@ describe('compaction — probe tests (high-risk scenarios)', () => {
// to a recent suffix but still projected through MicroCompaction.compact()
// with the cutoff computed for the FULL history. The absolute cutoff applied
// to the shifted suffix can clear recent tool results the summary needs.
it.fails('does not clear recent tool results when projecting a shrunk suffix under an active micro-compaction cutoff', () => {
// SKIPPED: micro-compaction has been disabled and its flag removed, so this
// defect no longer exists.
it.skip('does not clear recent tool results when projecting a shrunk suffix under an active micro-compaction cutoff', () => {
// This defect only exists when micro-compaction is active, so enable the
// flag explicitly rather than inheriting the ambient KIMI_CODE_EXPERIMENTAL
// master switch — otherwise the probe's pass/fail flips with the runner's
Expand Down
12 changes: 6 additions & 6 deletions packages/agent-core/test/agent/compaction/full.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,9 @@ describe('FullCompaction', () => {
).toBe(false);
});

it('micro-compacts old tool results before sending the summary request', async () => {
// Micro compaction is disabled; this scenario is skipped because the feature
// can no longer be enabled.
it.skip('micro-compacts old tool results before sending the summary request', async () => {
vi.useFakeTimers();
enableMicroCompactionFlag();
const ctx = testAgent({
Expand Down Expand Up @@ -2227,11 +2229,9 @@ function enableMicroCompactionFlag(): void {
}

function getMicroCompactionFlagEnv(): string {
const flag = FLAG_DEFINITIONS.find((definition) => definition.id === 'micro_compaction');
if (flag === undefined) {
throw new Error('Missing micro_compaction flag definition.');
}
return flag.env;
// Micro compaction is disabled and its flag has been removed from the registry;
// the env var name is kept so the (skipped) test still type-checks.
return 'KIMI_CODE_EXPERIMENTAL_MICRO_COMPACTION';
}

function deferred<T>() {
Expand Down
19 changes: 10 additions & 9 deletions packages/agent-core/test/agent/compaction/micro.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,18 @@ const MINUTE = 60 * 1000;
const DEFAULT_MARKER = '[Old tool result content cleared]';
const MICRO_COMPACTION_FLAG_ENV = getMicroCompactionFlagEnv();

describe('MicroCompaction', () => {
// Micro compaction is disabled and its flag has been removed; the suite is
// skipped because the feature can no longer be enabled.
describe.skip('MicroCompaction', () => {
beforeEach(() => {
vi.stubEnv(MASTER_ENV, '0');
vi.stubEnv(MICRO_COMPACTION_FLAG_ENV, '1');
});

it('defaults the micro_compaction flag off', () => {
expect(new FlagResolver({}, FLAG_DEFINITIONS).enabled('micro_compaction')).toBe(false);
});
// The micro_compaction flag no longer exists, so there is no default to assert.
// it('defaults the micro_compaction flag off', () => {
// expect(new FlagResolver({}, FLAG_DEFINITIONS).enabled('micro_compaction')).toBe(false);
// });

it('truncates old tool results after cache miss', () => {
vi.useFakeTimers();
Expand Down Expand Up @@ -966,11 +969,9 @@ function hasMarker(messages: readonly Message[]): boolean {
}

function getMicroCompactionFlagEnv(): string {
const flag = FLAG_DEFINITIONS.find((definition) => definition.id === 'micro_compaction');
if (flag === undefined) {
throw new Error('Missing micro_compaction flag definition.');
}
return flag.env;
// Micro compaction is disabled and its flag has been removed from the registry;
// the env var name is kept so the (skipped) suite still type-checks.
return 'KIMI_CODE_EXPERIMENTAL_MICRO_COMPACTION';
}

function singleTelemetryEvent(
Expand Down
Loading
Loading