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/restore-swarm-preference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---

Restore the preference for parallel swarm work over single sequential subagent tasks, and add a `default_swarm_mode` config option so new sessions can start in Swarm mode.
1 change: 1 addition & 0 deletions docs/en/configuration/config-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ Fields in the config file fall into two categories: **top-level scalars** that d
| `planning_model` | `string` | — | Model alias used while Plan mode is active; must be defined in `models` and have the same context window as `default_model` |
| `default_permission_mode` | `string` | `manual` | Default permission mode for new sessions; one of `manual` (prompt each time), `yolo` (auto-approve tool actions, but the agent may still ask questions), or `auto` (fully autonomous — the agent decides everything without asking) |
| `default_plan_mode` | `boolean` | `false` | Whether new sessions start in Plan mode (produce a plan before executing) by default |
| `default_swarm_mode` | `boolean` | `false` | Whether new sessions start in Swarm mode (prefer parallel `AgentSwarm` subagents) by default |
| `merge_all_available_skills` | `boolean` | `true` | Whether to merge Agent Skills from all available directories |
| `extra_skill_dirs` | `array<string>` | — | Extra skill search directories, layered on top of the default directories |
| `extra_agent_dirs` | `array<string>` | — | Extra custom agent search directories, layered on top of the default directories |
Expand Down
1 change: 1 addition & 0 deletions docs/zh/configuration/config-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ timeout = 5
| `planning_model` | `string` | — | Plan 模式激活时使用的模型别名,必须在 `models` 中定义,且上下文窗口需与 `default_model` 相同 |
| `default_permission_mode` | `string` | `manual` | 新会话的默认权限模式,可选 `manual`(逐次询问)、`yolo`(自动批准工具操作,Agent 仍可能提问)、`auto`(完全自主,Agent 自己做决定,不再提问) |
| `default_plan_mode` | `boolean` | `false` | 新会话是否默认以 Plan 模式(先出计划再执行)启动 |
| `default_swarm_mode` | `boolean` | `false` | 新会话是否默认以 Swarm 模式(优先使用并行的 `AgentSwarm` 子代理)启动 |
| `merge_all_available_skills` | `boolean` | `true` | 是否合并所有目录中的 Agent Skills |
| `extra_skill_dirs` | `array<string>` | — | 额外 Skill 搜索目录,叠加到默认目录之上 |
| `extra_agent_dirs` | `array<string>` | — | 额外自定义 Agent 搜索目录,叠加到默认目录之上 |
Expand Down
1 change: 1 addition & 0 deletions fork/PATCHES.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ mark the row `merged-upstream` (keep the row as history).
| planning-model switching | `feat/planning-model-switching` | `planning_model` config + v1/v2 plan-mode model flip, `modelquery`/`modellist`/`modelset` tools with a context-window switch guard, `/planning-model` TUI command; web "Planning model" setting in `kimi-code-web` | — | — | local |
| disable kimi OAuth by default | `feat/disable-kimi-oauth-by-default` | Kimi OAuth login (kimi.com/kimi.ai device-code) disabled by default; opt-in via `[experimental] kimi_oauth` / `KIMI_CODE_EXPERIMENTAL_KIMI_OAUTH` / `kimi web --enable-kimi-oauth`; token/apiKey auth stays the default; web + TUI hide the login unless enabled | — | — | not-submitting |
| Windows native installer | `feat/windows-installer` | Fork-style `install.ps1` (win32-x64/arm64, GitHub Releases + gh-pages channel); publish `install.ps1` to the update channel; point `README.md` + `README.zh-CN.md` + `fork/README.md` at the fork install commands | — | — | not-submitting |
| swarm-task preference + default_swarm_mode | `fix/swarm-task-preference` | Restore the #3028-removed proactiveness/action directives and add explicit swarm-preference + subagent-rules guidance to both engines' system prompts; add a `default_swarm_mode` config (v1 + v2 + protocol) so new sessions can start in swarm mode | — | [#3433](https://github.com/MoonshotAI/kimi-code/issues/3433) | local |

Changesets under `.changeset/` deliberately name the upstream package
`@moonshot-ai/kimi-code`, not the fork's `@mbuckaway/kimi-code`, so a change can
Expand Down
11 changes: 10 additions & 1 deletion packages/agent-core-v2/docs/config-manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@
# commented "# field: type" lines describe the remaining schema fields.
# Values resolve as: default -> config.toml -> env overlay -> memory.

# Index (28 sections · 2 overlay(s))
# Index (29 sections · 2 overlay(s))
# background src/agent/task/configSection.ts
# builtinProductSkills src/features/skill/catalog/configSection.ts
# cron src/features/cron/configSection.ts
# defaultPermissionMode src/agent/permissionMode/configSection.ts
# defaultPlanMode src/features/plan/configSection.ts
# defaultSwarmMode src/features/swarm/configSection.ts
# experimental src/app/flag/flag.ts
# extraAgentDirs src/workspace/workspaceAgentProfileLoader/configSection.ts
# extraSkillDirs src/features/skill/catalog/configSection.ts
Expand Down Expand Up @@ -110,6 +111,14 @@ manual_tick = false

default_plan_mode = false

# ##########################################################################
# defaultSwarmMode (config.toml: default_swarm_mode)
# owner: src/features/swarm/configSection.ts
# scope: core
# ##########################################################################

default_swarm_mode = false

# ##########################################################################
# experimental
# owner: src/app/flag/flag.ts
Expand Down
11 changes: 9 additions & 2 deletions packages/agent-core-v2/src/app/agentProfileCatalog/system.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
You are ${product_name}, an interactive general AI agent running on a user's computer.

Your primary goal is to help users with software engineering tasks. You should also answer questions when asked. Always adhere strictly to the following system instructions and the user's requirements.
Your primary goal is to help users with software engineering tasks by taking action — use the tools available to you to make real changes on the user's system. You should also answer questions when asked. Always adhere strictly to the following system instructions and the user's requirements.

${role_additional}

Expand All @@ -13,7 +13,9 @@ Keep code, commands, identifiers, file paths, and technical terms in their origi

# Prompt and Tool Use

When calling tools, do not provide detailed explanations or chain-of-thought. For simple requests, call tools directly. For non-trivial or multi-step tasks, first emit one short user-visible sentence describing what you will do next, then call the tool(s). Keep that sentence to roughly 8–10 words, plain and concrete — for example, "Next, I'll patch the config and update the related tests." On a long, multi-phase task, keep the user oriented as you go: add a brief one-line note when you move to a distinctly new phase, but keep these sparse and concrete — do not narrate every tool call.
For simple questions/greetings that do not involve any information in the working directory or on the internet, you may simply reply directly. For anything else, default to taking action with tools. When the request could be interpreted as either a question to answer or a task to complete, treat it as a task. For instance, "change `methodName` to snake_case" is a task, not a question — locate the method in the code and edit it; do not just reply with `method_name`.

When handling the user's request, if it involves creating, modifying, or running code or files, you MUST use the appropriate tools available to you to make actual changes — do not just describe the solution in text. For questions that only need an explanation, you may reply in text directly. When calling tools, do not provide detailed explanations or chain-of-thought. For simple requests, call tools directly. For non-trivial or multi-step tasks, first emit one short user-visible sentence describing what you will do next, then call the tool(s). Keep that sentence to roughly 8–10 words, plain and concrete — for example, "Next, I'll patch the config and update the related tests." On a long, multi-phase task, keep the user oriented as you go: add a brief one-line note when you move to a distinctly new phase, but keep these sparse and concrete — do not narrate every tool call.

When a dedicated tool fits the job, reach for it before raw shell: `Read` a known path, `Glob` to find files by name, and `Grep` to search file contents. These resolve paths through the workspace access policy and cap their output, so they keep large raw dumps out of the conversation.

Expand Down Expand Up @@ -118,10 +120,15 @@ At any time, you should be HELPFUL, CONCISE, ACCURATE, and CANDID. Be thorough i
- Try your best to avoid any hallucination. Do fact checking before providing any factual information.
- Think about the best approach, then take action decisively.
- Do not give up too early.
- Default to making progress, not to asking: once the goal is clear and you have the user's go-ahead to act on it, carry it through and work blockers yourself; ask only when the user's answer would actually change your next step. This never overrides the rule to stop and discuss when the goal is unclear, or to wait for explicit instruction before writing code.
- ALWAYS, keep it stupidly simple. Do not overcomplicate things.
- Talk like a seasoned engineer, not a cheerleader. Skip flattery, motivational filler, and hollow reassurance — the user wants the work done, not to be impressed. A correct, plainly-stated answer respects them more than praise does.
- Think and reply in the user's language; artifacts that go into the repository follow the project's conventions instead.
- When you have evidence the user is wrong, say so and show the evidence — agreeing to be agreeable wastes their time and can break their code. Defer once they've decided; until then, an honest objection is the helpful answer.
- When the task requires creating or modifying files, always use tools to do so. Never treat displaying code in your response as a substitute for actually writing it to the file system.
- Deliver the complete change. Never stub out code with placeholders like `// ... rest unchanged` or leave the user to fill in the gaps; write out every line you mean to change.
- Prefer the `AgentSwarm` tool (parallel swarm of subagents) over a single sequential `Agent` call whenever the work decomposes into independent parts: partition it, run the subagents in parallel, and cross-check their results.
- When delegating to any subagent, include in its prompt the requirement to read and follow the project's coding standards and `AGENTS.md` before writing code.
- After a change, sweep for comments and docstrings that now describe the old behavior, and bring them in line with what the code actually does.
- Before calling a task done, verify it: run the checks that cover your change and look at the result instead of assuming. Don't mark work complete while tests are red or the implementation is still partial — this holds whether or not you are tracking the work in a todo list.
- When the context fills up it is compacted automatically, so you may suddenly see a summary of the work so far in place of the full thread. Assume compaction happened while you were working: continue naturally from the summary instead of restarting, and make reasonable assumptions about anything it omits rather than redoing settled work. Treat any "done" it reports as unverified until you re-check.
Expand Down
10 changes: 10 additions & 0 deletions packages/agent-core-v2/src/features/swarm/configSection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,13 @@ export function resolveSwarmTimeoutMs(config: IConfigService): number {
config.get<SwarmConfig | undefined>(SWARM_SECTION)?.timeoutMs ?? DEFAULT_SWARM_TIMEOUT_MS
);
}

export const DEFAULT_SWARM_MODE_SECTION = 'defaultSwarmMode';

export const DefaultSwarmModeSchema = z.boolean().optional();

export type DefaultSwarmMode = z.infer<typeof DefaultSwarmModeSchema>;

registerConfigSection(DEFAULT_SWARM_MODE_SECTION, DefaultSwarmModeSchema, {
defaultValue: false,
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { ILogService } from '#/_base/log/log';
import { drainLogCloses } from '#/_base/log/logService';
import { DEFAULT_PLAN_MODE_SECTION } from '#/features/plan/configSection';
import { IAgentPlanService } from '#/features/plan/plan';
import { DEFAULT_SWARM_MODE_SECTION } from '#/features/swarm/configSection';
import { IAgentSwarmService } from '#/features/swarm/agent/swarm';
import { LifecycleScope } from '#/app/scopes';
import { IBootstrapService } from '#/app/bootstrap/bootstrap';
import { IConfigService } from '#/app/config/config';
Expand Down Expand Up @@ -211,6 +213,14 @@ export class SessionLifecycleService extends Disposable implements ISessionLifec
}
await planHandle.accessor.get(IAgentPlanService).enter();
}
if (this.config.get<boolean>(DEFAULT_SWARM_MODE_SECTION) === true) {
const swarmAgent = main ?? (await ensureMainAgent(handle));
const swarmHandle = agents.handleOf(swarmAgent.agentId);
if (swarmHandle === undefined) {
throw new Error2(ErrorCodes.AGENT_NOT_FOUND, 'Main agent was not found');
}
swarmHandle.accessor.get(IAgentSwarmService).enter('manual');
}
await this.appendSessionIndexEntry(sessionId, opts.workDir);
} catch (error) {
const sessionDir = handle.accessor.get(ISessionContext).sessionDir;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import { WorkspaceDirsService } from '#/workspace/workspaceDirs/workspaceDirsSer
import { IWorkspaceInstructionsService } from '#/workspace/workspaceInstructions/workspaceInstructions';
import { IWorkspaceMcpService } from '#/workspace/workspaceMcp/workspaceMcp';
import { IAgentPlanService } from '#/features/plan/plan';
import { IAgentSwarmService } from '#/features/swarm/agent/swarm';
import { SECONDARY_MODEL_FLAG_ID } from '#/session/subagent/flag';
import { IModelCatalog } from '#/kosong/model/catalog';
import { IModelService } from '#/kosong/model/model';
Expand Down Expand Up @@ -2111,4 +2112,96 @@ describe('SessionLifecycleService', () => {
expect(enter).not.toHaveBeenCalled();
});
});

describe('defaultSwarmMode bootstrap', () => {
it('enters swarm mode on a fresh session when config.defaultSwarmMode is true', async () => {
const { lifecycle, enter, create } = agentLifecycleCapturingSwarmSpy();
const svc = await build([
stubPair(IConfigService, configStub({ defaultSwarmMode: true })),
stubPair(IAgentLifecycleService, lifecycle),
]);

await svc.create({ sessionId: 's1', workDir: '/tmp/proj' });

expect(create).toHaveBeenCalledTimes(1);
expect(enter).toHaveBeenCalledWith('manual');
});

it('leaves swarm mode inactive when config.defaultSwarmMode is absent', async () => {
const { lifecycle, enter, create } = agentLifecycleCapturingSwarmSpy();
const svc = await build([
stubPair(IConfigService, configStub({})),
stubPair(IAgentLifecycleService, lifecycle),
]);

await svc.create({ sessionId: 's1', workDir: '/tmp/proj' });

expect(create).not.toHaveBeenCalled();
expect(enter).not.toHaveBeenCalled();
});

it('does not apply config.defaultSwarmMode when resuming a session', async () => {
const workDir = '/tmp/proj';
const summary = { id: 's1', workspaceId: 'wd_stub', cwd: workDir } as SessionSummary;
const { lifecycle, enter, create } = agentLifecycleCapturingSwarmSpy({
mainPreexists: true,
});
const svc = await build([
stubPair(IConfigService, configStub({ defaultSwarmMode: true })),
stubPair(IAgentLifecycleService, lifecycle),
stubPair(ISessionIndex, {
...sessionIndexStub(),
get: () => Promise.resolve(summary),
}),
]);

await svc.resume('s1');

expect(create).not.toHaveBeenCalled();
expect(enter).not.toHaveBeenCalled();
});
});
});

function makeSwarmAgentHandle(agentId: string, swarmService: unknown): IAgentScopeHandle {
return {
id: agentId,
kind: LifecycleScope.Agent,
accessor: {
get: (token: unknown) => (token === IAgentSwarmService ? swarmService : {}),
},
dispose: () => {},
} as unknown as IAgentScopeHandle;
}

function agentLifecycleCapturingSwarmSpy(opts: { mainPreexists?: boolean } = {}): {
lifecycle: IAgentLifecycleService;
enter: ReturnType<typeof vi.fn>;
create: ReturnType<typeof vi.fn>;
} {
const enter = vi.fn();
const swarmService = {
enter,
exit: vi.fn(),
isActive: false,
};
let mainContext: AgentContext | undefined = opts.mainPreexists
? agentContextStub(MAIN_AGENT_ID)
: undefined;
let mainHandle: IAgentScopeHandle | undefined = opts.mainPreexists
? makeSwarmAgentHandle(MAIN_AGENT_ID, swarmService)
: undefined;
const create = vi.fn((args: CreateAgentOptions | undefined) => {
const agentId = args?.agentId ?? MAIN_AGENT_ID;
mainContext = agentContextStub(agentId);
mainHandle = makeSwarmAgentHandle(agentId, swarmService);
return Promise.resolve(mainContext);
});
const lifecycle: IAgentLifecycleService = {
...agentLifecycleStub(),
get: (id: string) => (id === MAIN_AGENT_ID ? mainContext : undefined),
create,
handleOf: (id: string) => (id === MAIN_AGENT_ID ? mainHandle : undefined),
};
return { lifecycle, enter, create };
}
2 changes: 2 additions & 0 deletions packages/agent-core/src/config/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ export const KimiConfigSchema = z.object({
yolo: z.boolean().optional(),
defaultPermissionMode: PermissionModeSchema.optional(),
defaultPlanMode: z.boolean().optional(),
defaultSwarmMode: z.boolean().optional(),
permission: PermissionConfigSchema.optional(),
hooks: z.array(HookDefSchema).optional(),
services: ServicesConfigSchema.optional(),
Expand Down Expand Up @@ -414,6 +415,7 @@ export const KimiConfigPatchSchema = z
yolo: z.boolean().optional(),
defaultPermissionMode: PermissionModeSchema.optional(),
defaultPlanMode: z.boolean().optional(),
defaultSwarmMode: z.boolean().optional(),
permission: PermissionConfigPatchSchema.optional(),
hooks: z.array(HookDefSchema).optional(),
services: ServicesConfigPatchSchema.optional(),
Expand Down
1 change: 1 addition & 0 deletions packages/agent-core/src/config/toml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,7 @@ export function configToTomlData(config: KimiConfig): Record<string, unknown> {
'yolo',
'defaultPermissionMode',
'defaultPlanMode',
'defaultSwarmMode',
'mergeAllAvailableSkills',
'extraSkillDirs',
'extraAgentDirs',
Expand Down
Loading
Loading