From 36eabde760cc5c001e424c418aadbccef00262a2 Mon Sep 17 00:00:00 2001 From: "haozhe.yang" Date: Fri, 4 Sep 2026 20:26:58 +0800 Subject: [PATCH] feat: graduate remote-control, minidb read model and search worker from experimental flags - remote-control is always available: kimi rc, kimi web --remote-control and /remote-control no longer require KIMI_CODE_EXPERIMENTAL_REMOTE_CONTROL or the master flag - add the [database] config section with base (session-index read model) and search (global search worker) kill switches, both defaulting to true - rename the kill-switch env vars to KIMI_CODE_PERSISTENCE_MINIDB_READMODEL and KIMI_CODE_SEARCH_WORKER (precedence: env > config > default) --- .changeset/database-config-section.md | 5 ++ .changeset/remote-control-always-available.md | 5 ++ AGENTS.md | 2 +- apps/kimi-code/src/cli/sub/web/index.ts | 5 +- .../src/cli/sub/web/remote-control.ts | 16 +--- apps/kimi-code/src/cli/sub/web/run.ts | 13 +-- apps/kimi-code/src/main.ts | 2 +- apps/kimi-code/src/native/search-worker.ts | 4 +- apps/kimi-code/src/tui/commands/registry.ts | 3 +- apps/kimi-code/test/cli/options.test.ts | 6 +- .../test/cli/web/remote-control.test.ts | 16 ---- apps/kimi-code/test/cli/web/web.test.ts | 47 ++--------- .../test/tui/commands/registry.test.ts | 4 +- .../test/tui/commands/resolve.test.ts | 4 +- docs/en/configuration/config-files.md | 11 +++ docs/en/configuration/env-vars.md | 2 + docs/en/guides/remote-control.md | 15 ---- docs/zh/configuration/config-files.md | 11 +++ docs/zh/configuration/env-vars.md | 2 + docs/zh/guides/remote-control.md | 15 ---- .../agent-core-v2/docs/config-manifest.toml | 17 +++- .../src/app/config/configService.ts | 2 +- .../src/app/remoteControl/flag.ts | 16 ---- .../sessionIndex/sessionIndexMirrorService.ts | 9 +- .../app/sessionIndex/sessionIndexService.ts | 8 +- packages/agent-core-v2/src/index.ts | 4 +- .../src/persistence/backends/minidb/flag.ts | 13 --- .../src/persistence/configSection.ts | 45 ++++++++++ .../agent-core-v2/test/app/config/stubs.ts | 8 ++ .../app/sessionIndex/sessionIndex.test.ts | 29 +++---- .../sessionIndex/sessionIndexMirror.test.ts | 13 +-- packages/agent-core-v2/test/harness/agent.ts | 17 ++-- packages/agent-core-v2/test/setup.ts | 2 +- .../kap-server/src/search/searchService.ts | 82 ++++++++++--------- packages/kap-server/test/globalSetup.ts | 4 +- .../test/search/searchRoute.test.ts | 2 +- .../test/search/searchService.bench.ts | 13 +-- .../test/search/searchService.test.ts | 30 ++++--- packages/kap-server/test/sessions.test.ts | 2 +- packages/kap-server/test/setup.ts | 4 +- packages/migration-legacy/src/steps/config.ts | 3 +- packages/node-sdk/test/list-sessions.test.ts | 10 +-- 42 files changed, 257 insertions(+), 264 deletions(-) create mode 100644 .changeset/database-config-section.md create mode 100644 .changeset/remote-control-always-available.md delete mode 100644 packages/agent-core-v2/src/app/remoteControl/flag.ts delete mode 100644 packages/agent-core-v2/src/persistence/backends/minidb/flag.ts create mode 100644 packages/agent-core-v2/src/persistence/configSection.ts diff --git a/.changeset/database-config-section.md b/.changeset/database-config-section.md new file mode 100644 index 00000000000..8126b88be07 --- /dev/null +++ b/.changeset/database-config-section.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": minor +--- + +Add a `[database]` config section with `base`/`search` kill switches (both default `true`) and rename their environment variables to `KIMI_CODE_PERSISTENCE_MINIDB_READMODEL` and `KIMI_CODE_SEARCH_WORKER`. diff --git a/.changeset/remote-control-always-available.md b/.changeset/remote-control-always-available.md new file mode 100644 index 00000000000..3a30fbab2ab --- /dev/null +++ b/.changeset/remote-control-always-available.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": minor +--- + +Kimi Remote Control is no longer experimental: `kimi rc`, `kimi web --remote-control`, and `/remote-control` work out of the box, without setting `KIMI_CODE_EXPERIMENTAL_REMOTE_CONTROL` or `KIMI_CODE_EXPERIMENTAL_FLAG`. diff --git a/AGENTS.md b/AGENTS.md index b7481516411..aa139e87b29 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -65,7 +65,7 @@ This is a TypeScript monorepo built for agent-assisted development. Keep the roo - Gate a not-yet-public feature behind an experimental flag. Flags are env-driven and default off: `KIMI_CODE_EXPERIMENTAL_` toggles one, `KIMI_CODE_EXPERIMENTAL_FLAG` enables all. Precedence is per-flag env > `[experimental]` config > master env > the flag's `default`. Release by flipping the entry's `default` to `true`. - `packages/agent-core` (v1): add the flag to the central registry at `packages/agent-core/src/flags/registry.ts`, then check it with `flags.enabled('my-feature')`. - - `packages/agent-core-v2` and kap-server modules: there is no central catalog — declare the flag in the owning domain via `registerFlagDefinition` at import time, then check it with `IFlagService.enabled(id)`. Current search-index-separation flags: `persistence_minidb_readmodel` (session read model, default on) and `search_worker` (global search worker host, default on). + - `packages/agent-core-v2` and kap-server modules: there is no central catalog — declare the flag in the owning domain via `registerFlagDefinition` at import time, then check it with `IFlagService.enabled(id)`. ## Where to Update Instructions diff --git a/apps/kimi-code/src/cli/sub/web/index.ts b/apps/kimi-code/src/cli/sub/web/index.ts index c7bb5d836e0..30240210b23 100644 --- a/apps/kimi-code/src/cli/sub/web/index.ts +++ b/apps/kimi-code/src/cli/sub/web/index.ts @@ -13,7 +13,6 @@ import type { Command } from 'commander'; import { registerDeprecatedServerCommand } from './deprecated-server'; -import { isRemoteControlEnabled } from './remote-control'; import { registerRotateTokenCommand } from './rotate-token'; import { buildWebCommand } from './run'; @@ -27,9 +26,9 @@ export function registerWebCommand(program: Command): void { registerDeprecatedServerCommand(program); buildWebCommand( program - .command('rc', { hidden: !isRemoteControlEnabled() }) + .command('rc') .alias('remote') - .description('Run the local Kimi server and open the web UI through Remote Control (experimental).'), + .description('Run the local Kimi server and open the web UI through Remote Control.'), { forceRemoteControl: true }, ); } diff --git a/apps/kimi-code/src/cli/sub/web/remote-control.ts b/apps/kimi-code/src/cli/sub/web/remote-control.ts index 42cd221f980..3c9e9db0a86 100644 --- a/apps/kimi-code/src/cli/sub/web/remote-control.ts +++ b/apps/kimi-code/src/cli/sub/web/remote-control.ts @@ -21,18 +21,6 @@ export const REMOTE_CONTROL_RELAY_ORIGIN = 'https://code-rc.kimi.com'; export const REMOTE_CONTROL_RELAY_URL_ENV = 'KIMI_CODE_REMOTE_CONTROL_RELAY_URL'; -export const REMOTE_CONTROL_FLAG_ENV = 'KIMI_CODE_EXPERIMENTAL_REMOTE_CONTROL'; - -const TRUTHY_ENV_VALUES = new Set(['1', 'true', 'yes', 'on']); - -export function isRemoteControlEnabled( - env: Readonly> = process.env, -): boolean { - const truthy = (key: string): boolean => - TRUTHY_ENV_VALUES.has((env[key] ?? '').trim().toLowerCase()); - return truthy('KIMI_CODE_EXPERIMENTAL_FLAG') || truthy(REMOTE_CONTROL_FLAG_ENV); -} - export function resolveRemoteControlRelayOrigin( env: Readonly> = process.env, ): string { @@ -144,7 +132,7 @@ export function formatRemoteControlOutput(options: RemoteControlOutputOptions): const feedback = toTerminalHyperlink('feedback', 'https://kimi.com/code/feedback'); return [ '', - ` ${title('Kimi Remote Control ready')} ${muted(`${getVersion()} (experimental)`)}`, + ` ${title('Kimi Remote Control ready')} ${muted(getVersion())}`, ` ${muted('Use Kimi Code on this machine from your phone or another computer.')}`, '', ` ${label('1.')} Scan the QR code, or open ${link(options.url)}`, @@ -159,7 +147,7 @@ export function formatRemoteControlOutput(options: RemoteControlOutputOptions): ` ${label('QR code PNG: ')}${options.pngPath} ${muted('(open this if the QR above does not scan)')}`, ` ${label('Local UI: ')}${muted(options.localOrigin)} ${muted('(LAN: --host)')}`, '', - ` ${muted('Experimental —')} ${docs} ${muted('·')} ${feedback}`, + ` ${docs} ${muted('·')} ${feedback}`, ` ${label('Logs: ')}${muted('off (--log-level info)')} ${muted('·')} ${label('Stop: ')}${muted('Ctrl+C')}`, '', ].join('\n'); diff --git a/apps/kimi-code/src/cli/sub/web/run.ts b/apps/kimi-code/src/cli/sub/web/run.ts index 261623444fa..7c8d02ed386 100644 --- a/apps/kimi-code/src/cli/sub/web/run.ts +++ b/apps/kimi-code/src/cli/sub/web/run.ts @@ -39,8 +39,6 @@ import { type NetworkAddress } from './networks'; import { formatRemoteControlOutput, formatRemoteControlStatus, - isRemoteControlEnabled, - REMOTE_CONTROL_FLAG_ENV, startRemoteControl, type RemoteControlHandle, type RemoteControlOptions, @@ -170,10 +168,8 @@ export function buildWebCommand( withServerOptions.addOption( new Option( '--rc, --remote-control', - 'Expose the web UI through Kimi Remote Control (experimental).', - ) - .default(false) - .hideHelp(!isRemoteControlEnabled()), + 'Expose the web UI through Kimi Remote Control.', + ).default(false), ); } return withServerOptions @@ -195,11 +191,6 @@ export async function handleWebCommand( deps: WebCommandDeps = DEFAULT_WEB_COMMAND_DEPS, ): Promise { const parsed = parseServerOptions(opts); - if (opts.remoteControl === true && !isRemoteControlEnabled()) { - throw new Error( - `--remote-control is experimental: set ${REMOTE_CONTROL_FLAG_ENV}=1 (or KIMI_CODE_EXPERIMENTAL_FLAG=1) to enable it.`, - ); - } if (opts.remoteControl === true && parsed.dangerousBypassAuth) { throw new Error('--remote-control cannot be combined with --dangerous-bypass-auth.'); } diff --git a/apps/kimi-code/src/main.ts b/apps/kimi-code/src/main.ts index a956d46f597..9f78c1322ec 100644 --- a/apps/kimi-code/src/main.ts +++ b/apps/kimi-code/src/main.ts @@ -198,7 +198,7 @@ function bootstrap(): void { ); // Same pattern for the global-search worker: extracted from the SEA blob so // the search index runs off the main thread; a failure leaves the search - // surface degraded (the `search_worker` flag restores the inline host). + // surface degraded ([database] search = false restores the inline host). const searchWorkerInstall = installKapSearchWorker(); startupTrace( searchWorkerInstall.status === 'installed' diff --git a/apps/kimi-code/src/native/search-worker.ts b/apps/kimi-code/src/native/search-worker.ts index 158825ce98e..709aec84fcd 100644 --- a/apps/kimi-code/src/native/search-worker.ts +++ b/apps/kimi-code/src/native/search-worker.ts @@ -36,8 +36,8 @@ function errorCode(error: unknown): string { /** * Install the SEA-bundled global-search worker without making optional * extraction fatal. Without it the search service resolves no worker entry - * inside the single-file binary and reports the index as degraded; the - * `search_worker` experimental flag restores the in-process host. + * inside the single-file binary and reports the index as degraded; + * `[database] search = false` restores the in-process host. */ export function installKapSearchWorker( options: NativeAssetOptions = {}, diff --git a/apps/kimi-code/src/tui/commands/registry.ts b/apps/kimi-code/src/tui/commands/registry.ts index 3448c6aefa5..594552bdaf9 100644 --- a/apps/kimi-code/src/tui/commands/registry.ts +++ b/apps/kimi-code/src/tui/commands/registry.ts @@ -434,10 +434,9 @@ export const BUILTIN_SLASH_COMMANDS = [ { name: 'remote-control', aliases: ['rc'], - description: 'Open the current session through Kimi Remote Control (experimental)', + description: 'Open the current session through Kimi Remote Control', priority: 40, availability: 'always', - experimentalFlag: 'remote-control', }, { name: 'exit', diff --git a/apps/kimi-code/test/cli/options.test.ts b/apps/kimi-code/test/cli/options.test.ts index f4f298efea7..cfb6bef2b61 100644 --- a/apps/kimi-code/test/cli/options.test.ts +++ b/apps/kimi-code/test/cli/options.test.ts @@ -5,7 +5,7 @@ * Run: pnpm -C apps/kimi-code exec vitest run test/cli/options.test.ts */ -import { describe, expect, it, onTestFinished, vi } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import { createProgram } from '#/cli/commands'; import type { CLIOptions } from '#/cli/options'; @@ -572,9 +572,6 @@ describe('CLI options parsing', () => { }); it('registers the visible sub-commands', () => { - vi.stubEnv('KIMI_CODE_EXPERIMENTAL_FLAG', '0'); - vi.stubEnv('KIMI_CODE_EXPERIMENTAL_REMOTE_CONTROL', '0'); - onTestFinished(() => { vi.unstubAllEnvs(); }); const program = createProgram( '0.0.0', () => {}, @@ -591,6 +588,7 @@ describe('CLI options parsing', () => { 'acp', 'web', 'server', + 'rc', 'login', 'doctor', 'vis', diff --git a/apps/kimi-code/test/cli/web/remote-control.test.ts b/apps/kimi-code/test/cli/web/remote-control.test.ts index 56cf347a377..d7eb9c3c78f 100644 --- a/apps/kimi-code/test/cli/web/remote-control.test.ts +++ b/apps/kimi-code/test/cli/web/remote-control.test.ts @@ -19,7 +19,6 @@ import { filterForwardRequestHeaders, formatRemoteControlOutput, formatRemoteControlStatus, - isRemoteControlEnabled, parseRawHttpRequest, resolveRemoteControlRelayOrigin, rewriteRemoteControlResponse, @@ -44,21 +43,6 @@ afterEach(async () => { while (cleanups.length > 0) await cleanups.pop()!(); }); -describe('Remote Control experimental flag', () => { - it('is off unless the per-feature env or the master switch is truthy', () => { - expect(isRemoteControlEnabled({})).toBe(false); - expect(isRemoteControlEnabled({ KIMI_CODE_EXPERIMENTAL_REMOTE_CONTROL: '0' })).toBe(false); - expect(isRemoteControlEnabled({ KIMI_CODE_EXPERIMENTAL_REMOTE_CONTROL: '1' })).toBe(true); - expect(isRemoteControlEnabled({ KIMI_CODE_EXPERIMENTAL_FLAG: 'true' })).toBe(true); - expect( - isRemoteControlEnabled({ - KIMI_CODE_EXPERIMENTAL_FLAG: '0', - KIMI_CODE_EXPERIMENTAL_REMOTE_CONTROL: 'yes', - }), - ).toBe(true); - }); -}); - describe('Remote Control URLs', () => { it('builds the public device entry without a local token', () => { const url = buildRemoteControlUrl('device/one'); diff --git a/apps/kimi-code/test/cli/web/web.test.ts b/apps/kimi-code/test/cli/web/web.test.ts index 5f7f2a6f34c..a417b3c641e 100644 --- a/apps/kimi-code/test/cli/web/web.test.ts +++ b/apps/kimi-code/test/cli/web/web.test.ts @@ -412,7 +412,6 @@ describe('`kimi web` opens the browser', () => { }); it('rejects Remote Control on a non-loopback host', async () => { - vi.stubEnv('KIMI_CODE_EXPERIMENTAL_REMOTE_CONTROL', '1'); const { handleWebCommand } = await import('#/cli/sub/web/run'); const { runner } = makeRunner(); const { stdout, stderr } = makeIo(); @@ -425,32 +424,11 @@ describe('`kimi web` opens the browser', () => { ).rejects.toThrow('--remote-control requires a loopback host.'); }); - it('rejects --remote-control while the experimental flag is off', async () => { - vi.stubEnv('KIMI_CODE_EXPERIMENTAL_FLAG', '0'); - vi.stubEnv('KIMI_CODE_EXPERIMENTAL_REMOTE_CONTROL', '0'); - const { handleWebCommand } = await import('#/cli/sub/web/run'); - const { runner } = makeRunner(); - const { stdout, stderr } = makeIo(); - - await expect( - handleWebCommand( - { remoteControl: true, open: false }, - { startServerForeground: runner, openUrl: vi.fn(), stdout, stderr }, - ), - ).rejects.toThrow('--remote-control is experimental:'); - }); - - it('hides --remote-control from help unless the experimental flag is on', () => { - const remoteControlOption = () => - makeProgram() - .commands.find((command) => command.name() === 'web')! - .options.find((option) => option.long === '--remote-control'); - - vi.stubEnv('KIMI_CODE_EXPERIMENTAL_FLAG', '0'); - vi.stubEnv('KIMI_CODE_EXPERIMENTAL_REMOTE_CONTROL', '0'); - expect(remoteControlOption()?.hidden).toBe(true); - vi.stubEnv('KIMI_CODE_EXPERIMENTAL_REMOTE_CONTROL', '1'); - expect(remoteControlOption()?.hidden).toBe(false); + it('shows --remote-control in help', () => { + const remoteControlOption = makeProgram() + .commands.find((command) => command.name() === 'web')! + .options.find((option) => option.long === '--remote-control'); + expect(remoteControlOption?.hidden).toBeFalsy(); }); }); @@ -471,17 +449,11 @@ describe('kimi rc', () => { expect(longs).not.toContain('--remote-control'); }); - it('hides `rc` from help unless the experimental flag is on', () => { - vi.stubEnv('KIMI_CODE_EXPERIMENTAL_FLAG', '0'); - vi.stubEnv('KIMI_CODE_EXPERIMENTAL_REMOTE_CONTROL', '0'); - expect(makeProgram().helpInformation()).not.toContain('rc|remote'); - vi.stubEnv('KIMI_CODE_EXPERIMENTAL_REMOTE_CONTROL', '1'); + it('shows `rc` in help', () => { expect(makeProgram().helpInformation()).toContain('rc|remote'); }); it('forces Remote Control for both `rc` and `remote`', async () => { - vi.stubEnv('KIMI_CODE_EXPERIMENTAL_FLAG', '0'); - vi.stubEnv('KIMI_CODE_EXPERIMENTAL_REMOTE_CONTROL', '0'); for (const name of ['rc', 'remote']) { const program = makeProgram(); let stderr = ''; @@ -493,14 +465,13 @@ describe('kimi rc', () => { .spyOn(process, 'exit') .mockImplementation(() => undefined as never); try { - await program.parseAsync(['node', 'kimi', name]); + await program.parseAsync(['node', 'kimi', name, '--host', '0.0.0.0']); } finally { errSpy.mockRestore(); exitSpy.mockRestore(); } - // The flag-off experimental error proves remoteControl was forced before - // the runner could start. - expect(stderr).toContain('--remote-control is experimental:'); + // The loopback check only runs when remoteControl was forced on. + expect(stderr).toContain('--remote-control requires a loopback host.'); } }); }); diff --git a/apps/kimi-code/test/tui/commands/registry.test.ts b/apps/kimi-code/test/tui/commands/registry.test.ts index 163e0acaf15..8b411b8b0b4 100644 --- a/apps/kimi-code/test/tui/commands/registry.test.ts +++ b/apps/kimi-code/test/tui/commands/registry.test.ts @@ -235,10 +235,10 @@ describe('built-in slash command registry', () => { expect(resolveSlashCommandAvailability(command!, 'Ship feature X')).toBe('always'); }); - it('gates remote-control behind the remote-control experiment, always available', () => { + it('registers remote-control as always available', () => { const command = findBuiltInSlashCommand('remote-control'); expect(command).toBeDefined(); - expect((command as KimiSlashCommand).experimentalFlag).toBe('remote-control'); + expect((command as KimiSlashCommand).experimentalFlag).toBeUndefined(); expect(resolveSlashCommandAvailability(command!, '')).toBe('always'); }); diff --git a/apps/kimi-code/test/tui/commands/resolve.test.ts b/apps/kimi-code/test/tui/commands/resolve.test.ts index d11ce73f966..e85c0f3ff71 100644 --- a/apps/kimi-code/test/tui/commands/resolve.test.ts +++ b/apps/kimi-code/test/tui/commands/resolve.test.ts @@ -64,9 +64,7 @@ describe('resolveSlashCommandInput', () => { }); }); - it('gates /remote-control behind the remote-control experimental flag', () => { - expect(resolve('/rc')).toEqual({ kind: 'message', input: '/rc' }); - setExperimentalFeatures([{ id: 'remote-control', enabled: true }]); + it('resolves /remote-control and /rc as built-ins', () => { expect(resolve('/rc')).toMatchObject({ kind: 'builtin', name: 'remote-control' }); expect(resolve('/remote-control')).toMatchObject({ kind: 'builtin', name: 'remote-control' }); }); diff --git a/docs/en/configuration/config-files.md b/docs/en/configuration/config-files.md index 4d1ab7a0e58..e356c1c7750 100644 --- a/docs/en/configuration/config-files.md +++ b/docs/en/configuration/config-files.md @@ -442,6 +442,17 @@ Like the `tools` / `disallowedTools` fields of an agent file, this section shape `max_edge_px` can be overridden by the `KIMI_IMAGE_MAX_EDGE_PX` environment variable and `read_byte_budget` by `KIMI_IMAGE_READ_BYTE_BUDGET`; both take higher priority than `config.toml`. +## `database` + +`database` controls the embedded storage engines behind session indexing and global search. Both keys default to `true` and act as kill switches that fall back to the legacy behavior when set to `false`. + +| Field | Type | Default | Description | +| --- | --- | --- | --- | +| `base` | `boolean` | `true` | Use the minidb-backed read model for session indexing; `false` falls back to reading session metadata directly | +| `search` | `boolean` | `true` | Run the global search index in a dedicated worker thread; `false` runs it in the server process | + +`base` can be overridden by the `KIMI_CODE_PERSISTENCE_MINIDB_READMODEL` environment variable and `search` by `KIMI_CODE_SEARCH_WORKER`; both take higher priority than `config.toml`. +