feat: log enabled experimental flags (#357)

This commit is contained in:
_Kerman 2026-06-03 11:35:29 +08:00 committed by GitHub
parent 9143fdadf6
commit 179aecf423
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 72 additions and 11 deletions

View file

@ -0,0 +1,6 @@
---
"@moonshot-ai/agent-core": minor
"@moonshot-ai/kimi-code": minor
---
Log enabled experimental flags at startup.

View file

@ -17,14 +17,12 @@ export const MASTER_ENV = 'KIMI_CODE_EXPERIMENTAL_FLAG';
* L3 registry default
*/
export class FlagResolver {
private readonly env: Readonly<Record<string, string | undefined>>;
private readonly byId: ReadonlyMap<string, FlagDefinitionInput>;
constructor(
env: Readonly<Record<string, string | undefined>> = process.env,
definitions: readonly FlagDefinitionInput[] = FLAG_DEFINITIONS,
private readonly env: Readonly<Record<string, string | undefined>> = process.env,
private readonly definitions: readonly FlagDefinitionInput[] = FLAG_DEFINITIONS,
) {
this.env = env;
this.byId = new Map(definitions.map((def) => [def.id, def]));
}
@ -36,6 +34,18 @@ export class FlagResolver {
if (override !== undefined) return override;
return def.default; // L3 default
}
snapshot(): Record<string, boolean> {
return Object.fromEntries(
this.definitions.map((def) => [def.id, this.enabled(def.id as FlagId)]),
);
}
enabledIds(): readonly FlagId[] {
return this.definitions
.filter((def) => this.enabled(def.id as FlagId))
.map((def) => def.id as FlagId);
}
}
/**

View file

@ -22,11 +22,8 @@ import {
type MoonshotServiceConfig,
} from '../config';
import {
FLAG_DEFINITIONS,
flags,
type ExperimentalFlagMap,
type FlagDefinitionInput,
type FlagId,
} from '../flags';
import type { Logger } from '../logging/types';
import { resolveSessionMcpConfig, type SessionMcpConfig } from '../mcp';
@ -169,6 +166,7 @@ export class KimiCore implements PromisableMethods<CoreAPI> {
this.pluginsReady = this.plugins.load().catch((error: unknown) => {
this.pluginsLoadError = error instanceof Error ? error : new Error(String(error));
});
log.info('experimental flags enabled', { flags: flags.enabledIds() });
this.sdk = rpcClient(this);
}
@ -259,8 +257,7 @@ export class KimiCore implements PromisableMethods<CoreAPI> {
}
getExperimentalFlags(): ExperimentalFlagMap {
const defs: readonly FlagDefinitionInput[] = FLAG_DEFINITIONS;
return Object.fromEntries(defs.map((def) => [def.id, flags.enabled(def.id as FlagId)]));
return flags.snapshot();
}
async closeSession({ sessionId }: CloseSessionPayload): Promise<void> {

View file

@ -63,7 +63,7 @@ describe('FlagResolver', () => {
expect(enabled('b-off-default')).toBe(true);
});
it('L1 master switch beats an L2 per-feature off (D2)', () => {
it('L1 master switch beats an L2 per-feature off', () => {
const enabled = make({ [MASTER_ENV]: '1', KIMI_CODE_EXPERIMENTAL_A: '0' });
expect(enabled('a-on-default')).toBe(true);
});
@ -73,6 +73,15 @@ describe('FlagResolver', () => {
expect(enabled('b-off-default')).toBe(false);
});
it('returns a full snapshot and enabled ids in registry order', () => {
const resolver = new FlagResolver({ KIMI_CODE_EXPERIMENTAL_B: '1' }, DEFS);
expect(resolver.snapshot()).toEqual({
'a-on-default': true,
'b-off-default': true,
});
expect(resolver.enabledIds()).toEqual(['a-on-default', 'b-off-default']);
});
it('reads the env name declared in the registry (the declared name works, others do not)', () => {
expect(make({ KIMI_CODE_EXPERIMENTAL_B: '1' })('b-off-default')).toBe(true);
// The name mechanically derived from the id must not take effect (env is explicitly ..._B).

View file

@ -1,18 +1,32 @@
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'pathe';
import { afterEach, describe, expect, it, vi } from 'vitest';
import {
FLAG_DEFINITIONS,
MASTER_ENV,
createRPC,
KimiCore,
type ApprovalResponse,
type CoreAPI,
type SDKAPI,
} from '../../src';
import {
__resetRootLoggerForTest,
getRootLogger,
resolveGlobalLogPath,
} from '../../src/logging/logger';
import { resolveLoggingConfig } from '../../src/logging/resolve-config';
import type { OAuthTokenProviderResolver } from '../../src/session/provider-manager';
function requiredFlagEnv(id: string): string {
const def = FLAG_DEFINITIONS.find((item) => item.id === id);
if (def === undefined) throw new Error(`Missing flag definition: ${id}`);
return def.env;
}
describe('KimiCore runtime config', () => {
let tmp: string;
@ -20,9 +34,34 @@ describe('KimiCore runtime config', () => {
if (tmp !== undefined) {
await rm(tmp, { recursive: true, force: true });
}
await __resetRootLoggerForTest();
vi.unstubAllEnvs();
vi.unstubAllGlobals();
});
it('logs all enabled experimental flags once on core startup', async () => {
tmp = await mkdtemp(join(tmpdir(), 'kimi-core-runtime-'));
const homeDir = join(tmp, 'home');
await mkdir(homeDir, { recursive: true });
await getRootLogger().configure(resolveLoggingConfig({ homeDir }));
vi.stubEnv(MASTER_ENV, '0');
for (const def of FLAG_DEFINITIONS) {
vi.stubEnv(def.env, '0');
}
vi.stubEnv(requiredFlagEnv('goal-command'), '1');
vi.stubEnv(requiredFlagEnv('background-ask'), '1');
void new KimiCore(async () => ({}) as never, { homeDir });
await getRootLogger().flushGlobal();
const text = await readFile(resolveGlobalLogPath(homeDir), 'utf-8');
expect(text).toContain('experimental flags enabled');
expect(text).toContain('goal-command');
expect(text).toContain('background-ask');
expect(text.match(/experimental flags enabled/g)).toHaveLength(1);
});
it('uses the shared OAuth resolver for Moonshot service tokens', async () => {
tmp = await mkdtemp(join(tmpdir(), 'kimi-core-runtime-'));
const homeDir = join(tmp, 'home');