Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
7e30add
feat(kap-server): add global fs:mkdir endpoint (#2281)
sailist Jul 28, 2026
425cfdf
chore(web): upgrade markstream-vue to 1.0.7 (#2294)
wbxl2000 Jul 28, 2026
e556088
feat(tree-sitter-bash): add a pure-TypeScript bash parser and an agen…
sailist Jul 28, 2026
cdbd33c
fix(kosong): fail fast on quota-exhausted 429 instead of retrying (#1…
vinlee19 Jul 28, 2026
b0f43ae
feat(oauth): return structured managed usage rows (#2300)
liruifengv Jul 28, 2026
d03a488
feat(server): remove the 50 MiB upload size cap and stream uploads to…
sailist Jul 28, 2026
de0ba9d
fix(agent-core): count validation-rejected tool calls toward the repe…
starquakee Jul 28, 2026
d88b377
fix(agent-core-v2): count validation-rejected tool calls toward the r…
7Sageer Jul 28, 2026
f79fde2
feat(node-sdk): migrate the SDK method surface to agent-core-v2 (#2262)
sailist Jul 28, 2026
ceaa969
feat(kap-server): add global message search with literal and live-ses…
sailist Jul 28, 2026
67dd031
feat(tui): customizable footer status line via status_line config (#2…
he-yufeng Jul 28, 2026
973e2a0
chore: prune non-user-facing changesets before release (#2335)
liruifengv Jul 29, 2026
16c7189
ci: release packages (#2244)
github-actions[bot] Jul 29, 2026
efac96c
feat(agent-core): custom agent files and secondary model on the v1 en…
7Sageer Jul 29, 2026
37d9bdc
docs(changelog): sync 0.30.0 from apps/kimi-code/CHANGELOG.md (#2343)
liruifengv Jul 29, 2026
b850c5f
fix(node-sdk): wire applyPersistedSecondaryModel to agent-core-v2 (#2…
7Sageer Jul 29, 2026
f8ec3d1
docs: fix dead anchor links in en/zh docs (#2348)
wenhua020201-arch Jul 29, 2026
6f7d5de
merge: upstream main (f8ec3d16) into fork — adopt upstream secondary-…
zicochaos Jul 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 0 additions & 5 deletions .changeset/host-identity-system-prompt.md

This file was deleted.

5 changes: 0 additions & 5 deletions .changeset/plugin-quota-note.md

This file was deleted.

5 changes: 0 additions & 5 deletions .changeset/plugin-update-notice.md

This file was deleted.

5 changes: 5 additions & 0 deletions .changeset/v1-custom-agent-files.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": minor
---

Support Markdown-defined custom agents on agent-core.
5 changes: 5 additions & 0 deletions .changeset/v1-secondary-model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": minor
---

Add the /secondary_model slash command to configure the secondary model used by subagents.
6 changes: 4 additions & 2 deletions AGENTS.md

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions FORK.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,21 @@ rg -n "disabled_skills|persist_default_model|agents_md_expand_includes|formatTer

| Flag | Default | Purpose |
| --- | --- | --- |
| `subagent-model-selection` | `false` | Optional exact configured/materializable model aliases on `Agent` and `AgentSwarm`, composed with upstream `primary`/`secondary` when secondary-model is enabled. Env: `KIMI_CODE_EXPERIMENTAL_SUBAGENT_MODEL_SELECTION` |
| `subagent-model-selection` | `false` | **v2 engine only.** Optional exact configured/materializable model aliases on `Agent` and `AgentSwarm`, composed with upstream `primary`/`secondary` when secondary-model is enabled. Env: `KIMI_CODE_EXPERIMENTAL_SUBAGENT_MODEL_SELECTION`. The v1-side implementation was retired: upstream `#2232` shipped secondary-model binding + custom agent files on v1 (TUI included), so the default v1 engine now offers upstream's `primary`/`secondary` choices only |

### Upstream foundation (already on main@origin)

| Feature | Notes |
| --- | --- |
| `#2064` secondary model | Configurable `[secondary_model]`, `primary`/`secondary` tool choices, secondary overlay, startup warning. Keep intact; fork exact-alias selection sits on top. |
| `#2064` secondary model | Configurable `[secondary_model]`, `primary`/`secondary` tool choices, secondary overlay, startup warning. Keep intact; fork exact-alias selection (v2-only) sits on top. |
| `#2232` v1 secondary model + agent files | Custom agent files (user/project/extra/explicit dirs), `--agent`/`--agent-file` in TUI and `kimi -p`, `[secondary_model]` + `KIMI_SECONDARY_MODEL` binding for spawned subagents behind `KIMI_CODE_EXPERIMENTAL_SECONDARY_MODEL`, `/secondary_model` TUI command, `disallowedTools` deny semantics. Supersedes the fork's retired v1 exact-alias selection. |

### TUI / protocol

| Feature | Purpose |
| --- | --- |
| Stable terminal title | `formatTerminalTitle(workDir)` renders `[host] - ~/path` instead of changing with the session title |
| Subagent model on headers | `subagent.spawned.model` supplies the effective model to Agent cards and AgentSwarm rows |
| v1 spawn event parity | The default v1 engine includes the effective model in `subagent.spawned` |
| Subagent model on cards | The bound model reaches Agent cards and AgentSwarm panels via the child's `agent.status.updated` (upstream's channel). v1's `subagent.spawned` no longer carries `model`; the roster tracker, kimi-web projector, and TUI all learn it from status updates |
| Persistent managed quota | The TUI footer shows rolling plan windows and refreshes them after model/session/provider changes; stale responses are ignored |

**Managed quota note:** quota is shown only when the active model provider is `managed:kimi-code`. Custom providers never display these account limits. `/usage` and `/status` refresh the TUI footer values. There is no `/usages` command.
Expand Down Expand Up @@ -103,8 +103,8 @@ Then `kimi -c` can continue the previous healthy session for that workdir.

### Secondary model vs exact-alias selection

- Upstream `secondary-model` remains the foundation for `primary`/`secondary` and `[secondary_model]`.
- Fork `subagent-model-selection` adds exact aliases under a separate experimental flag (default off).
- Upstream `secondary-model` (now including `#2232`'s v1 port) is the foundation for `primary`/`secondary` and `[secondary_model]` on both engines.
- Fork `subagent-model-selection` adds exact aliases under a separate experimental flag (default off) — **v2 engine only**; the v1 implementation was dropped in favor of upstream's.
- Permission matching uses the semantic profile name; display labels may include the model for clarity.
- Resume model semantics differ by engine: v1 realigns the child to the parent model alias (tool-call `model` is ignored); v2 keeps the journal-bound model and does not rebind from parent tool args.
- Known limitations left open: legacy model directory still reserves the tokens `primary` / `secondary` (M-3), and the subagent model directory does not live-reload after config changes without a new session or equivalent restart (M-4).
Expand Down
20 changes: 20 additions & 0 deletions apps/kimi-code/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
# @moonshot-ai/kimi-code

## 0.30.0

### Minor Changes

- [#2255](https://github.com/MoonshotAI/kimi-code/pull/2255) [`67dd031`](https://github.com/MoonshotAI/kimi-code/commit/67dd03149f36be91a0c081e70d8a2d721b0f1c64) Thanks [@he-yufeng](https://github.com/he-yufeng)! - Add a customizable footer status line, configured via `[status_line]` in `tui.toml`.

### Patch Changes

- [#2313](https://github.com/MoonshotAI/kimi-code/pull/2313) [`de0ba9d`](https://github.com/MoonshotAI/kimi-code/commit/de0ba9d0654273ff6b028a7a561983ebee4e723e) Thanks [@starquakee](https://github.com/starquakee)! - Stop the turn after repeated invalid tool calls instead of retrying indefinitely.

- [#2147](https://github.com/MoonshotAI/kimi-code/pull/2147) [`29783e4`](https://github.com/MoonshotAI/kimi-code/commit/29783e471afcf7975852e496907646458264d2e6) Thanks [@wbxl2000](https://github.com/wbxl2000)! - Show a quota note after installing official plugins that bill against plan quota (such as Kimi Datasource).

- [#2147](https://github.com/MoonshotAI/kimi-code/pull/2147) [`29783e4`](https://github.com/MoonshotAI/kimi-code/commit/29783e471afcf7975852e496907646458264d2e6) Thanks [@wbxl2000](https://github.com/wbxl2000)! - Show a notice when an official plugin used in the session has an update available. Run /plugins to install it.

- [#1857](https://github.com/MoonshotAI/kimi-code/pull/1857) [`cdbd33c`](https://github.com/MoonshotAI/kimi-code/commit/cdbd33c13c7f5cd4c49ec112ee4313b3938a7752) Thanks [@vinlee19](https://github.com/vinlee19)! - Fail fast when account quota or balance is exhausted instead of silently retrying for ~3 minutes.

- [#2294](https://github.com/MoonshotAI/kimi-code/pull/2294) [`425cfdf`](https://github.com/MoonshotAI/kimi-code/commit/425cfdf53f0fd3b01527f5fba87acff68f49f368) Thanks [@wbxl2000](https://github.com/wbxl2000)! - web: Fix garbled line numbers in code blocks.

- [#2312](https://github.com/MoonshotAI/kimi-code/pull/2312) [`d03a488`](https://github.com/MoonshotAI/kimi-code/commit/d03a4886fdf7c35014c10079a3d417aeb0447d9a) Thanks [@sailist](https://github.com/sailist)! - Remove the 50 MB size limit on file uploads to the built-in server.

## 0.29.2

### Patch Changes
Expand Down
8 changes: 4 additions & 4 deletions apps/kimi-code/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@moonshot-ai/kimi-code",
"version": "0.29.2",
"version": "0.30.0",
"description": "The Starting Point for Next-Gen Agents",
"license": "MIT",
"author": "Moonshot AI",
Expand Down Expand Up @@ -63,9 +63,9 @@
"test:native:smoke": "node scripts/native/smoke.mjs",
"dev": "node scripts/dev.mjs",
"dev:cli-only": "tsx --import ../../build/register-raw-text-loader.mjs ./src/main.ts",
"dev:server": "tsx --tsconfig ./tsconfig.dev.json --import ../../build/register-raw-text-loader.mjs ./src/main.ts web --no-open --debug-endpoints",
"dev:kap-server": "tsx --tsconfig ./tsconfig.dev.json --import ../../build/register-raw-text-loader.mjs ./src/main.ts web --no-open --debug-endpoints",
"dev:kap-server:multi": "tsx --tsconfig ./tsconfig.dev.json --import ../../build/register-raw-text-loader.mjs ./src/main.ts web --no-open --debug-endpoints",
"dev:server": "KIMI_CODE_DEV_SERVER=1 tsx --tsconfig ./tsconfig.dev.json --import ../../build/register-raw-text-loader.mjs ./src/main.ts web --no-open --debug-endpoints",
"dev:kap-server": "KIMI_CODE_DEV_SERVER=1 tsx --tsconfig ./tsconfig.dev.json --import ../../build/register-raw-text-loader.mjs ./src/main.ts web --no-open --debug-endpoints",
"dev:kap-server:multi": "KIMI_CODE_DEV_SERVER=1 tsx --tsconfig ./tsconfig.dev.json --import ../../build/register-raw-text-loader.mjs ./src/main.ts web --no-open --debug-endpoints",
"dev:server:restart": "node scripts/dev-server-restart.mjs",
"dev:plugin-marketplace": "node scripts/dev-plugin-marketplace-server.mjs",
"build:plugin-marketplace": "node scripts/build-plugin-marketplace-cdn.mjs",
Expand Down
2 changes: 1 addition & 1 deletion apps/kimi-code/scripts/dev-server-restart.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function start() {
console.error('[dev:server:restart] starting server…');
child = spawn(tsxBin, tsxArgs, {
cwd: APP_ROOT,
env: process.env,
env: { ...process.env, KIMI_CODE_DEV_SERVER: '1' },
// Server does not read stdin; keep ours free for the Enter trigger.
stdio: ['ignore', 'inherit', 'inherit'],
});
Expand Down
42 changes: 42 additions & 0 deletions apps/kimi-code/src/cli/agent-selection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { readFile } from 'node:fs/promises';
import { homedir } from 'node:os';

import { parseAgentFileText, resolveAgentPath } from '@moonshot-ai/kimi-code-sdk';

import type { CLIOptions } from './options';

/**
* Resolve which agent profile the launch flags select.
*
* `--agent` carries the profile name directly; `--agent-file` implicitly
* selects the profile the file defines, so the file is parsed here (fatal on
* error) so a bad file fails before any session work. Returns undefined when
* neither flag is present.
*/
export async function resolveAgentProfileSelection(
opts: Pick<CLIOptions, 'agent' | 'agentFiles'>,
workDir: string,
): Promise<string | undefined> {
if (opts.agent !== undefined) return opts.agent;
const agentFile = opts.agentFiles?.[0];
if (agentFile === undefined) return undefined;

const path = resolveAgentPath(agentFile, workDir, homedir());
let text: string;
try {
text = await readFile(path, 'utf8');
} catch (error) {
throw new Error(
`Failed to read agent file "${path}": ${error instanceof Error ? error.message : String(error)}`,
{ cause: error },
);
}
try {
return parseAgentFileText({ path, source: 'explicit', text }).name;
} catch (error) {
throw new Error(
`Invalid agent file "${path}": ${error instanceof Error ? error.message : String(error)}`,
{ cause: error },
);
}
}
4 changes: 2 additions & 2 deletions apps/kimi-code/src/cli/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export function createProgram(
.addOption(
new Option(
'--agent <name>',
'Agent profile to use for this invocation (v2 engine only). Custom profiles are discovered from agent directories or loaded via --agent-file.',
'Agent profile to start the new session with. Custom profiles are discovered from agent directories or loaded via --agent-file. Cannot be combined with --session/--continue.',
)
.argParser((value: string, previous: string | undefined) => {
if (previous !== undefined) {
Expand All @@ -90,7 +90,7 @@ export function createProgram(
.addOption(
new Option(
'--agent-file <path>',
'Load an agent definition from a Markdown file and select it (v2 engine only).',
'Load an agent definition from a Markdown file and select it for the new session. Cannot be combined with --session/--continue.',
)
.argParser((value: string, previous: string[] | undefined) => {
if ((previous?.length ?? 0) > 0) {
Expand Down
15 changes: 9 additions & 6 deletions apps/kimi-code/src/cli/experimental-v2.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
/**
* Experimental agent-core-v2 engine gate for `kimi -p` (print mode).
* Experimental agent-core-v2 engine gate for the CLI surfaces.
*
* When the master switch `KIMI_CODE_EXPERIMENTAL_FLAG` is truthy, print mode
* routes to the native agent-core-v2 runner instead of the default v1
* harness (see `run-prompt.ts`). Read directly from the env (matching
* When the master switch `KIMI_CODE_EXPERIMENTAL_FLAG` is truthy, `kimi -p`
* (print mode) routes to the native agent-core-v2 runner (see
* `run-prompt.ts`) and the interactive TUI builds its harness through the
* SDK's v2-backed client (see `run-shell.ts`), both instead of the default
* v1 engine. The master switch also enables every experimental feature flag
* in the engine. Read directly from the env (matching
* `cli/update/rollout.ts`) because the CLI must not depend on the core flag
* registry. Unset / any non-truthy value keeps the v1 harness.
* registry. Unset / any non-truthy value keeps the v1 path.
*
* Note: `kimi web` always boots kap-server (the agent-core-v2 engine
* server) — it no longer consults this switch.
* server) — it does not consult this switch.
*/

export const KIMI_V2_ENV = 'KIMI_CODE_EXPERIMENTAL_FLAG';
Expand Down
6 changes: 2 additions & 4 deletions apps/kimi-code/src/cli/options.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { isKimiV2Enabled } from './experimental-v2';

export type UIMode = 'shell' | 'print';
export type PromptOutputFormat = 'text' | 'stream-json';

Expand Down Expand Up @@ -101,10 +99,10 @@ export function validateOptions(
}
if (
(opts.agent !== undefined || opts.agentFiles.length > 0) &&
(!promptMode || !isKimiV2Enabled(env))
(opts.session !== undefined || opts.continue)
) {
throw new OptionConflictError(
'--agent/--agent-file are only available with the v2 engine (kimi -p with KIMI_CODE_EXPERIMENTAL_FLAG=1).',
'Cannot combine --agent/--agent-file with --session/--continue: the agent is bound at session creation and the bound agent is restored automatically on resume.',
);
}
if (promptMode && opts.session === '') {
Expand Down
7 changes: 7 additions & 0 deletions apps/kimi-code/src/cli/run-prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { resolve } from 'pathe';

import { CLI_SHUTDOWN_TIMEOUT_MS, PROMPT_CLEANUP_TIMEOUT_MS } from '#/constant/app';

import { resolveAgentProfileSelection } from './agent-selection';
import { isKimiV2Enabled } from './experimental-v2';
import { resolveOutputFormat } from './options';
import type { CLIOptions, PromptOutputFormat } from './options';
Expand Down Expand Up @@ -296,6 +297,9 @@ async function resolvePromptSession(
stderr: PromptOutput,
setRestorePermission: (restorePermission: () => Promise<void>) => void,
): Promise<ResolvedPromptSession> {
// `--agent`/`--agent-file` are creation-only: validateOptions rejects them
// together with --session/--continue, so resume paths never forward a
// profile — the bound agent is restored from the session itself.
if (opts.session !== undefined) {
const sessions = await harness.listSessions({ sessionId: opts.session, workDir });
const target = sessions[0];
Expand Down Expand Up @@ -365,12 +369,15 @@ async function resolvePromptSession(
stderr.write(`No sessions to continue under "${workDir}"; starting a fresh session.\n`);
}

const agentProfile = await resolveAgentProfileSelection(opts, workDir);
const model = requireConfiguredModel(opts.model, defaultModel);
const session = await harness.createSession({
workDir,
model,
permission: 'auto',
additionalDirs: opts.addDirs?.length ? opts.addDirs : undefined,
agentProfile,
agentFiles: opts.agentFiles?.length ? opts.agentFiles : undefined,
drainAgentTasksOnStop: true,
});
installHeadlessHandlers(session);
Expand Down
18 changes: 16 additions & 2 deletions apps/kimi-code/src/cli/run-shell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import { join } from 'node:path';

import {
createKimiHarness,
createKimiHarnessV2,
flushDiagnosticLogsSync,
log,
type KimiHarness,
type KimiHarnessOptions,
type TelemetryClient,
} from '@moonshot-ai/kimi-code-sdk';
import {
Expand All @@ -29,6 +31,8 @@ import { toTerminalHyperlink } from '#/utils/terminal-hyperlink';
import { restoreTerminalModes } from '#/utils/terminal-restore';

import type { CLIOptions } from './options';
import { resolveAgentProfileSelection } from './agent-selection';
import { isKimiV2Enabled } from './experimental-v2';
import { createCliTelemetryBootstrap, initializeCliTelemetry } from './telemetry';
import { createKimiCodeHostIdentity } from './version';

Expand Down Expand Up @@ -60,7 +64,7 @@ export async function runShell(
withContext: withTelemetryContext,
setContext: setTelemetryContext,
};
const harness = createKimiHarness({
const harnessOptions: KimiHarnessOptions = {
homeDir: telemetryBootstrap.homeDir,
identity: createKimiCodeHostIdentity(version),
skillDirs: opts.skillsDirs,
Expand All @@ -76,7 +80,13 @@ export async function runShell(
});
},
sessionStartedProperties: { yolo: opts.yolo, auto: opts.auto, plan: opts.plan, afk: false },
});
};
// Experimental agent-core-v2 route (same master switch as `kimi -p`): the
// harness is the SDK's v2-backed client, so the whole TUI runs on the
// agent-core-v2 engine.
const harness = isKimiV2Enabled()
? createKimiHarnessV2(harnessOptions)
: createKimiHarness(harnessOptions);
log.info('kimi-code starting', {
version,
uiMode: CLI_UI_MODE,
Expand All @@ -101,8 +111,12 @@ export async function runShell(
configWarning = combineStartupNotice(configWarning, warning);
}
const configMs = Date.now() - configStartedAt;
// Resolve --agent/--agent-file once for the startup session; validateOptions
// has already rejected them alongside --session/--continue.
const agentProfile = await resolveAgentProfileSelection(opts, workDir);
const tui = new KimiTUI(harness, {
cliOptions: opts,
agentProfile,
additionalDirs: opts.addDirs?.length ? opts.addDirs : undefined,
tuiConfig,
version,
Expand Down
28 changes: 25 additions & 3 deletions apps/kimi-code/src/cli/sub/web/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* `startServer`).
*/

import { existsSync } from 'node:fs';
import { join } from 'node:path';

import { hostRequestHeadersSeed } from '@moonshot-ai/agent-core-v2';
Expand Down Expand Up @@ -266,6 +267,12 @@ async function runServerInProcess(
// logger, close }`, so adapt it to the `RoutedServer` surface the rest of
// this runner consumes.
const logger = createServerLogger({ level: options.logLevel });
const webAssetsDir = serverWebAssetsDir();
if (webAssetsDir === undefined) {
logger.info(
'dev mode: web assets not built; starting the API server without the web UI',
);
}
const v2 = await startServer({
host: options.host,
port: options.port,
Expand All @@ -288,7 +295,7 @@ async function runServerInProcess(
// requests (model, WebSearch, FetchURL) carry the same User-Agent +
// X-Msh-* identity as direct CLI runs.
seeds: hostRequestHeadersSeed(buildKimiDefaultHeaders(version)),
webAssetsDir: serverWebAssetsDir(),
webAssetsDir,
});
logger.info('serving the REST/WS API and the bundled web UI');
running = {
Expand All @@ -315,8 +322,23 @@ async function runServerInProcess(
});
}

function serverWebAssetsDir(): string {
return resolveServerWebAssetsDir();
/**
* Resolve the web assets directory passed to kap-server. In dev mode
* (`KIMI_CODE_DEV_SERVER=1`, set by the repo's `dev:server` / `dev:kap-server*`
* scripts) a missing `dist-web` build is tolerated: the server starts API-only
* and the web UI is expected to come from the kimi-web Vite dev server.
* Outside dev mode the directory is always returned and kap-server keeps
* failing fast when the assets are missing.
*/
export function serverWebAssetsDir(
env: NodeJS.ProcessEnv = process.env,
nativeWebAssetsDir: string | null = getNativeWebAssetsDir(),
): string | undefined {
const dir = resolveServerWebAssetsDir(nativeWebAssetsDir);
if (env['KIMI_CODE_DEV_SERVER'] === '1' && !existsSync(join(dir, 'index.html'))) {
return undefined;
}
return dir;
}

export function resolveServerWebAssetsDir(
Expand Down
Loading
Loading