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
6 changes: 6 additions & 0 deletions .changeset/enable-micro-compaction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@moonshot-ai/agent-core": minor
"@moonshot-ai/kimi-code": minor
---

Enable micro compaction by default while keeping its opt-out flag.
6 changes: 6 additions & 0 deletions .changeset/release-experimental-features.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@moonshot-ai/agent-core": minor
"@moonshot-ai/kimi-code": minor
---

Make goals, background questions, and sub-skill discovery available without experimental opt-ins.
2 changes: 1 addition & 1 deletion .changeset/template-agent-swarm.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
"@moonshot-ai/kimi-code": minor
---

Add swarm agent runs with SDK/TUI controls, live progress, and rate-limit-aware retries.
Add the `/swarm` command for running agent swarms with live progress and rate-limit-aware retries.
6 changes: 1 addition & 5 deletions apps/kimi-code/src/cli/goal-prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,7 @@ const GOAL_PREFIX = /^\/goal(\s|$)/;
* prompt). Non-create goal subcommands are not supported headless and fall
* through to normal prompt handling.
*/
export function parseHeadlessGoalCreate(
prompt: string,
flagEnabled: boolean,
): HeadlessGoalCreate | undefined {
if (!flagEnabled) return undefined;
export function parseHeadlessGoalCreate(prompt: string): HeadlessGoalCreate | undefined {
const trimmed = prompt.trim();
if (!GOAL_PREFIX.test(trimmed)) return undefined;
const args = trimmed.replace(/^\/goal/, '').trim();
Expand Down
4 changes: 1 addition & 3 deletions apps/kimi-code/src/cli/run-prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import {
import { resolve } from 'pathe';

import { CLI_SHUTDOWN_TIMEOUT_MS } from '#/constant/app';
import { experimentalFeatureMap } from '#/utils/experimental-features';

import type { CLIOptions, PromptOutputFormat } from './options';
import {
Expand Down Expand Up @@ -148,8 +147,7 @@ export async function runPrompt(
// the turn-run alive across continuation turns, so the normal prompt-turn
// waiter blocks until the goal is terminal; we then emit a summary and set a
// distinct exit code.
const flagMap = experimentalFeatureMap(await harness.getExperimentalFeatures());
const goalCreate = parseHeadlessGoalCreate(opts.prompt!, flagMap['goal_command'] === true);
const goalCreate = parseHeadlessGoalCreate(opts.prompt!);
if (goalCreate !== undefined) {
await runHeadlessGoal(session, goalCreate, goalModel, outputFormat, stdout, stderr);
} else {
Expand Down
2 changes: 0 additions & 2 deletions apps/kimi-code/src/tui/commands/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ export const BUILTIN_SLASH_COMMANDS = [
aliases: [],
description: 'Toggle swarm mode or run one task in swarm mode',
priority: 100,
experimentalFlag: 'agent_swarm',
completeArgs: swarmArgumentCompletions,
availability: 'idle-only',
},
Expand Down Expand Up @@ -179,7 +178,6 @@ export const BUILTIN_SLASH_COMMANDS = [
aliases: [],
description: 'Start or manage an autonomous goal',
priority: 80,
experimentalFlag: 'goal_command',
// No argumentHint: the menu description stays as short as every other
// command's. The subcommands (status/pause/resume/cancel/replace) surface in
// the argument autocomplete list once the user types `/goal ` (see
Expand Down
4 changes: 1 addition & 3 deletions apps/kimi-code/src/tui/kimi-tui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1045,9 +1045,7 @@ export class KimiTUI {
async syncRuntimeState(session: Session = this.requireSession()): Promise<void> {
const [status, goalResult] = await Promise.all([
session.getStatus(),
isExperimentalFlagEnabled('goal_command')
? session.getGoal()
: Promise.resolve({ goal: null }),
session.getGoal(),
]);
this.setAppState({
sessionId: session.id,
Expand Down
22 changes: 9 additions & 13 deletions apps/kimi-code/test/cli/goal-prompt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,15 @@ describe('goalExitCode', () => {
});

describe('parseHeadlessGoalCreate', () => {
it('returns undefined when the flag is disabled', () => {
expect(parseHeadlessGoalCreate('/goal Ship feature X', false)).toBeUndefined();
});

it('parses a create command into objective + replace', () => {
const result = parseHeadlessGoalCreate('/goal Ship feature X', true);
const result = parseHeadlessGoalCreate('/goal Ship feature X');
expect(result).toEqual({ objective: 'Ship feature X', replace: false });
});

it('returns undefined for non-goal prompts and non-create subcommands', () => {
expect(parseHeadlessGoalCreate('say hello', true)).toBeUndefined();
expect(parseHeadlessGoalCreate('/goal status', true)).toBeUndefined();
expect(parseHeadlessGoalCreate('/goal pause', true)).toBeUndefined();
expect(parseHeadlessGoalCreate('say hello')).toBeUndefined();
expect(parseHeadlessGoalCreate('/goal status')).toBeUndefined();
expect(parseHeadlessGoalCreate('/goal pause')).toBeUndefined();
});
});

Expand Down Expand Up @@ -106,7 +102,7 @@ const mocks = vi.hoisted(() => {
session,
eventHandlers,
mainEvent,
experimentalFeatures: [{ id: 'goal_command', enabled: true }],
experimentalFeatures: [{ id: 'micro_compaction', enabled: true }],
sessions: [] as Array<{ readonly id: string; readonly workDir: string }>,
};
});
Expand Down Expand Up @@ -164,7 +160,7 @@ describe('runPrompt headless goal mode', () => {

beforeEach(() => {
savedExitCode = process.exitCode;
mocks.experimentalFeatures = [{ id: 'goal_command', enabled: true }];
mocks.experimentalFeatures = [{ id: 'micro_compaction', enabled: true }];
mocks.sessions = [];
mocks.session.createGoal.mockClear();
mocks.session.getStatus.mockResolvedValue({ permission: 'auto', model: 'k2' } as never);
Expand Down Expand Up @@ -233,7 +229,7 @@ describe('runPrompt headless goal mode', () => {
expect(stdout.text()).not.toContain('"goalId":null');
});

it('treats /goal as a normal prompt when the flag is disabled', async () => {
it('creates a headless goal without reading experimental features', async () => {
mocks.experimentalFeatures = [];
const stdout = writer();
const stderr = writer();
Expand All @@ -242,8 +238,8 @@ describe('runPrompt headless goal mode', () => {
stderr,
process: { once: () => {}, off: () => {}, exit: () => undefined as never },
});
expect(mocks.session.createGoal).not.toHaveBeenCalled();
expect(mocks.session.prompt).toHaveBeenCalled();
expect(mocks.session.createGoal).toHaveBeenCalled();
expect(mocks.session.prompt).toHaveBeenCalledWith('Ship feature X');
});

it('validates the resumed session model before creating a headless goal', async () => {
Expand Down
22 changes: 11 additions & 11 deletions apps/kimi-code/test/tui/commands/experiments.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ function feature(
overrides: Partial<ExperimentalFeatureState> = {},
): ExperimentalFeatureState {
return {
id: 'goal_command',
title: 'Goal command',
description: 'Enable goal mode.',
surface: 'both',
env: 'KIMI_CODE_EXPERIMENTAL_GOAL_COMMAND',
defaultEnabled: false,
enabled: false,
id: 'micro_compaction',
title: 'Micro compaction',
description: 'Trim older tool results.',
surface: 'core',
env: 'KIMI_CODE_EXPERIMENTAL_MICRO_COMPACTION',
defaultEnabled: true,
enabled: true,
source: 'default',
...overrides,
};
Expand All @@ -40,7 +40,7 @@ function makeHost() {
harness: {
setConfig: vi.fn(async () => ({ providers: {} })),
getExperimentalFeatures: vi.fn(async () => [
feature({ enabled: true, source: 'config', configValue: true }),
feature({ enabled: false, source: 'config', configValue: false }),
]),
},
session,
Expand Down Expand Up @@ -77,14 +77,14 @@ describe('experimental feature command handlers', () => {
const host = makeHost();

await applyExperimentalFeatureChanges(host, [
{ id: 'goal_command', enabled: true },
{ id: 'micro_compaction', enabled: false },
]);

expect(host.harness.setConfig).toHaveBeenCalledWith({
experimental: { 'goal_command': true },
experimental: { 'micro_compaction': false },
});
expect(host.harness.getExperimentalFeatures).toHaveBeenCalledOnce();
expect(isExperimentalFlagEnabled('goal_command')).toBe(true);
expect(isExperimentalFlagEnabled('micro_compaction')).toBe(false);
expect(host.refreshSlashCommandAutocomplete).toHaveBeenCalled();
expect(host.restoreEditor).toHaveBeenCalled();
expect(host.session.reloadSession).toHaveBeenCalledOnce();
Expand Down
13 changes: 0 additions & 13 deletions apps/kimi-code/test/tui/commands/goal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -690,7 +690,6 @@ describe('dispatchInput /goal integration', () => {
});

it('routes /goal through the real resolver, creates the goal, and sends the objective', async () => {
setExperimentalFeatures([{ id: 'goal_command', enabled: true }]);
const { host, session } = makeHost();

dispatchInput(host, '/goal Ship feature X');
Expand All @@ -703,18 +702,6 @@ describe('dispatchInput /goal integration', () => {
expect(host.sendNormalUserInput).toHaveBeenCalledWith('Ship feature X');
expect(host.sendNormalUserInput).not.toHaveBeenCalledWith('/goal Ship feature X');
});

it('treats /goal as a normal message when the flag is disabled', async () => {
setExperimentalFeatures([]);
const { host, session } = makeHost();

dispatchInput(host, '/goal Ship feature X');

await vi.waitFor(() => {
expect(host.sendNormalUserInput).toHaveBeenCalledWith('/goal Ship feature X');
});
expect(session.createGoal).not.toHaveBeenCalled();
});
});

describe('goalArgumentCompletions', () => {
Expand Down
6 changes: 3 additions & 3 deletions apps/kimi-code/test/tui/commands/registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ describe('built-in slash command registry', () => {
it('keeps swarm mode changes and swarm tasks idle-only', () => {
const swarm = findBuiltInSlashCommand('swarm');
expect(swarm).toBeDefined();
expect((swarm as KimiSlashCommand).experimentalFlag).toBe('agent_swarm');
expect((swarm as KimiSlashCommand).experimentalFlag).toBeUndefined();
expect(resolveSlashCommandAvailability(swarm!, 'on')).toBe('idle-only');
expect(resolveSlashCommandAvailability(swarm!, 'off')).toBe('idle-only');
expect(resolveSlashCommandAvailability(swarm!, 'Ship feature X')).toBe('idle-only');
Expand Down Expand Up @@ -99,10 +99,10 @@ describe('built-in slash command registry', () => {
]);
});

it('registers goal behind the goal_command flag with subcommand-aware availability', () => {
it('registers goal with subcommand-aware availability', () => {
const goal = findBuiltInSlashCommand('goal');
expect(goal).toBeDefined();
expect((goal as KimiSlashCommand).experimentalFlag).toBe('goal_command');
expect((goal as KimiSlashCommand).experimentalFlag).toBeUndefined();
expect(resolveSlashCommandAvailability(goal!, '')).toBe('always');
expect(resolveSlashCommandAvailability(goal!, 'status')).toBe('always');
expect(resolveSlashCommandAvailability(goal!, 'pause')).toBe('always');
Expand Down
4 changes: 2 additions & 2 deletions apps/kimi-code/test/tui/commands/reload.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ auto_install = false
expect(host.harness.getConfig).toHaveBeenCalledWith({ reload: true });
expect(host.harness.getExperimentalFeatures).toHaveBeenCalledOnce();
expect(host.refreshSlashCommandAutocomplete).toHaveBeenCalledOnce();
expect(isExperimentalFlagEnabled('goal_command')).toBe(true);
expect(isExperimentalFlagEnabled('micro_compaction')).toBe(true);
expect(host.state.appState.theme).toBe('light');
expect(host.state.appState.availableModels).toEqual({
fresh: { provider: 'test', model: 'fresh-model', maxContextSize: 1000 },
Expand Down Expand Up @@ -128,7 +128,7 @@ function makeHost({
test: { type: 'kimi', apiKey: 'test-key' },
},
})),
getExperimentalFeatures: vi.fn(async () => [{ id: 'goal_command', enabled: true }]),
getExperimentalFeatures: vi.fn(async () => [{ id: 'micro_compaction', enabled: true }]),
},
setAppState: vi.fn((patch: Record<string, unknown>) => {
Object.assign(state.appState, patch);
Expand Down
25 changes: 2 additions & 23 deletions apps/kimi-code/test/tui/commands/resolve.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ describe('resolveSlashCommandInput', () => {
});

it('blocks idle-only built-ins while streaming', () => {
setExperimentalFeatures([{ id: 'agent_swarm', enabled: true }]);
expect(resolve('/new', { isStreaming: true })).toEqual({
kind: 'blocked',
commandName: 'new',
Expand Down Expand Up @@ -107,7 +106,6 @@ describe('resolveSlashCommandInput', () => {
});

it('blocks model and session pickers while compacting', () => {
setExperimentalFeatures([{ id: 'agent_swarm', enabled: true }]);
expect(resolve('/sessions', { isCompacting: true })).toEqual({
kind: 'blocked',
commandName: 'sessions',
Expand Down Expand Up @@ -220,15 +218,7 @@ describe('resolveSlashCommandInput', () => {
});
});

it('treats /swarm as a normal message when agent_swarm is disabled', () => {
expect(resolve('/swarm on')).toEqual({
kind: 'message',
input: '/swarm on',
});
});

it('resolves /swarm when agent_swarm is enabled', () => {
setExperimentalFeatures([{ id: 'agent_swarm', enabled: true }]);
it('resolves /swarm without an experimental flag', () => {
expect(resolve('/swarm Ship feature X')).toMatchObject({
kind: 'builtin',
name: 'swarm',
Expand All @@ -243,25 +233,15 @@ describe('goal command resolution', () => {
setExperimentalFeatures([]);
});

it('resolves /goal to the builtin command when goal_command is enabled', () => {
setExperimentalFeatures([{ id: 'goal_command', enabled: true }]);
it('resolves /goal to the builtin command without an experimental flag', () => {
expect(resolve('/goal Ship feature X')).toMatchObject({
kind: 'builtin',
name: 'goal',
args: 'Ship feature X',
});
});

it('treats /goal as a normal message when goal_command is disabled', () => {
setExperimentalFeatures([]);
expect(resolve('/goal Ship feature X')).toEqual({
kind: 'message',
input: '/goal Ship feature X',
});
});

it('blocks goal creation while streaming', () => {
setExperimentalFeatures([{ id: 'goal_command', enabled: true }]);
expect(resolve('/goal Ship feature X', { isStreaming: true })).toEqual({
kind: 'blocked',
commandName: 'goal',
Expand All @@ -270,7 +250,6 @@ describe('goal command resolution', () => {
});

it('does not block status/pause/cancel/bare goal while streaming', () => {
setExperimentalFeatures([{ id: 'goal_command', enabled: true }]);
for (const sub of ['status', 'pause', 'cancel']) {
expect(resolve(`/goal ${sub}`, { isStreaming: true })).toMatchObject({
kind: 'builtin',
Expand Down
Loading
Loading