refactor(kap-server): rename server-v2 package to kap-server

- rename packages/server-v2 to packages/kap-server and update every
  @moonshot-ai/server-v2 reference across apps, packages, flake.nix,
  and build scripts
- remove the experimental `kimi v2` CLI prefix and its tests from the
  apps/kimi-code main entry
- add legacy BackgroundTask protocol types and the `blocked` turn
  interrupt reason for cross-engine compatibility
This commit is contained in:
haozhe.yang 2026-07-07 16:56:19 +08:00
parent 957c493136
commit 304883ab81
151 changed files with 144 additions and 211 deletions

View file

@ -88,7 +88,7 @@
"@moonshot-ai/migration-legacy": "workspace:^",
"@moonshot-ai/pi-tui": "workspace:^",
"@moonshot-ai/server": "workspace:^",
"@moonshot-ai/server-v2": "workspace:^",
"@moonshot-ai/kap-server": "workspace:^",
"@moonshot-ai/vis-server": "workspace:^",
"@moonshot-ai/vis-web": "workspace:*",
"@types/semver": "^7.7.0",

View file

@ -1,9 +1,8 @@
/**
* Experimental agent-core-v2 engine gate.
*
* Both the `kimi v2 <subcommand>` prefix (see `main.ts`) and the
* `kimi server run` server-v2 routing (see `sub/server/run.ts`) key off this
* single master switch. Read directly from the env (matching
* The `kimi server run` server-v2 routing (see `sub/server/run.ts`) keys off
* this single master switch. 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 engine.
*/

View file

@ -51,9 +51,9 @@ const WEB_ASSETS_DIR = 'dist-web';
/**
* `kimi server run` server-v2 routing is gated by the master experimental
* switch shared with the `kimi v2` prefix (`#/cli/experimental-v2`). When it is
* truthy, the in-process runner boots the DI × Scope engine server
* (`@moonshot-ai/server-v2`) instead of the default `@moonshot-ai/server`.
* switch (`#/cli/experimental-v2`). When it is truthy, the in-process runner
* boots the DI × Scope engine server (`@moonshot-ai/kap-server`) instead of
* the default `@moonshot-ai/server`.
*
* Re-exported under the historical name so existing callers/tests keep working.
*/
@ -405,7 +405,7 @@ async function runServerInProcess(
// the default (flag-off) path keeps the exact v1-only module graph — v2 and
// its agent-core-v2 engine are only resolved when the flag is on.
const { createServerLogger: createServerV2Logger, startServer: startServerV2 } =
await import('@moonshot-ai/server-v2');
await import('@moonshot-ai/kap-server');
const logger = createServerV2Logger({ level: options.logLevel });
const v2 = await startServerV2({
host: options.host,

View file

@ -23,7 +23,6 @@ import {
} from '@moonshot-ai/kimi-telemetry';
import { createProgram } from './cli/commands';
import { isKimiV2Enabled, KIMI_V2_ENV } from './cli/experimental-v2';
import { finalizeHeadlessRun } from './cli/headless-exit';
import type { CLIOptions } from './cli/options';
import { OptionConflictError, validateOptions } from './cli/options';
@ -133,43 +132,6 @@ const MIGRATE_CLI_OPTIONS: CLIOptions = {
skillsDirs: [],
};
const V2_PREFIX_TOKEN = 'v2';
export type ApplyV2PrefixOutcome =
| { readonly argv: readonly string[] }
| { readonly error: string };
/**
* Rewrite argv for the experimental `kimi v2` prefix.
*
* `v2` is not a Commander subcommand it is a prefix handled here so that
* `kimi v2 <subcommand> <args>` runs through the *identical* code paths as
* `kimi <subcommand> <args>`. Engine selection stays keyed on
* `KIMI_CODE_EXPERIMENTAL_FLAG` (e.g. `kimi v2 server run` boots server-v2 /
* agent-core-v2, which `sub/server/run.ts` already honors).
*
* Only effective when the flag is set: with the flag off, the prefix is
* rejected with a hint rather than silently falling back to the v1 engine.
*/
export function applyV2Prefix(
argv: readonly string[],
env: Readonly<Record<string, string | undefined>> = process.env,
): ApplyV2PrefixOutcome {
// The prefix must be the first positional token (right after node + script).
if (argv[2] !== V2_PREFIX_TOKEN) {
return { argv };
}
if (!isKimiV2Enabled(env)) {
return {
error:
"'kimi v2' is experimental and requires the agent-core-v2 engine. " +
`Set ${KIMI_V2_ENV}=1 to enable it.`,
};
}
// Strip the `v2` token; the remaining argv is parsed as a normal `kimi` invocation.
return { argv: [...argv.slice(0, 2), ...argv.slice(3)] };
}
export function main(): void {
process.title = PROCESS_NAME;
installCrashHandlers();
@ -191,12 +153,6 @@ export function main(): void {
const version = getVersion();
const v2 = applyV2Prefix(process.argv);
if ('error' in v2) {
process.stderr.write(`${v2.error}\n`);
process.exit(1);
}
const program = createProgram(
version,
(opts) => {
@ -252,7 +208,7 @@ export function main(): void {
},
);
program.parse(v2.argv);
program.parse(process.argv);
}
main();

View file

@ -8,7 +8,7 @@ import { runPrompt } from '#/cli/run-prompt';
import { runShell } from '#/cli/run-shell';
import { formatStartupError } from '#/cli/startup-error';
import { runUpdatePreflight } from '#/cli/update/preflight';
import { applyV2Prefix, handleMainCommand, handleUpgradeCommand, main } from '#/main';
import { handleMainCommand, handleUpgradeCommand, main } from '#/main';
const mocks = vi.hoisted(() => {
const parse = vi.fn();
@ -421,87 +421,3 @@ describe('main entry command handling', () => {
).toBe('error: failed to run prompt: Provider not set\n');
});
});
describe('kimi beta prefix', () => {
const ON = { KIMI_CODE_EXPERIMENTAL_FLAG: '1' };
const OFF: Readonly<Record<string, string | undefined>> = {};
describe('applyV2Prefix', () => {
it('passes argv through unchanged when there is no beta token', () => {
const argv = ['node', 'kimi', 'server', 'run'];
expect(applyV2Prefix(argv, ON)).toEqual({ argv });
expect(applyV2Prefix(argv, OFF)).toEqual({ argv });
});
it('strips the beta token when the experimental flag is on', () => {
expect(applyV2Prefix(['node', 'kimi', 'beta', 'server', 'run'], ON)).toEqual({
argv: ['node', 'kimi', 'server', 'run'],
});
});
it('strips a lone beta token (default chat) when the flag is on', () => {
expect(applyV2Prefix(['node', 'kimi', 'beta'], ON)).toEqual({
argv: ['node', 'kimi'],
});
});
it('rejects the beta prefix with a hint when the flag is off', () => {
const outcome = applyV2Prefix(['node', 'kimi', 'beta', 'server', 'run'], OFF);
expect('error' in outcome && outcome.error).toMatch(/KIMI_CODE_EXPERIMENTAL_FLAG/);
});
it('does not treat a later beta token as the prefix', () => {
const argv = ['node', 'kimi', 'server', 'beta'];
expect(applyV2Prefix(argv, ON)).toEqual({ argv });
});
});
describe('main() wiring', () => {
let originalArgv: string[];
beforeEach(() => {
vi.clearAllMocks();
originalArgv = process.argv;
});
afterEach(() => {
process.argv = originalArgv;
vi.unstubAllEnvs();
vi.restoreAllMocks();
});
it('parses the stripped argv when the flag is on', () => {
vi.stubEnv('KIMI_CODE_EXPERIMENTAL_FLAG', '1');
process.argv = ['node', 'kimi', 'beta', 'server', 'run'];
main();
expect(mocks.parse).toHaveBeenCalledWith(['node', 'kimi', 'server', 'run']);
});
it('exits with a hint and never parses when the flag is off', () => {
vi.stubEnv('KIMI_CODE_EXPERIMENTAL_FLAG', '');
process.argv = ['node', 'kimi', 'beta', 'server', 'run'];
const writes: string[] = [];
vi.spyOn(process.stderr, 'write').mockImplementation((chunk) => {
writes.push(String(chunk));
return true;
});
vi.spyOn(process, 'exit').mockImplementation((code?: string | number | null) => {
throw new ExitCalled(Number(code ?? 0));
});
let exitCode: number | undefined;
try {
main();
} catch (error) {
if (error instanceof ExitCalled) exitCode = error.code;
else throw error;
}
expect(exitCode).toBe(1);
expect(mocks.parse).not.toHaveBeenCalled();
expect(writes.join('')).toContain('KIMI_CODE_EXPERIMENTAL_FLAG');
});
});
});

View file

@ -693,11 +693,10 @@ describe('server-v2 routing (KIMI_CODE_EXPERIMENTAL_FLAG)', () => {
}
});
it('is the canonical experimental-v2 gate shared with the `kimi beta` prefix', async () => {
it('is the canonical experimental-v2 gate', async () => {
const { isServerV2Enabled } = await import('#/cli/sub/server/run');
const { isKimiV2Enabled, KIMI_V2_ENV } = await import('#/cli/experimental-v2');
expect(KIMI_V2_ENV).toBe('KIMI_CODE_EXPERIMENTAL_FLAG');
// Server routing and the `kimi beta` prefix must agree on the exact same gate.
expect(isServerV2Enabled).toBe(isKimiV2Enabled);
});
});

View file

@ -6,7 +6,7 @@ import { dirname, join } from 'node:path';
* package.json `imports` field does scoped to the IMPORTER's owning package,
* honoring array fallbacks such as `"#/*": ["./src/*.ts", "./src/<x>/index.ts"]`.
*
* Why this is needed: when the CLI bundles `@moonshot-ai/server-v2`, rolldown
* Why this is needed: when the CLI bundles `@moonshot-ai/kap-server`, rolldown
* inlines `@moonshot-ai/agent-core-v2` source, whose internal `#/foo` imports
* must resolve against each package's own `src/`. Rolldown (like tsx) only
* honors the first array element of an `imports` target and therefore breaks

View file

@ -66,7 +66,7 @@
./packages/agent-core
./packages/agent-core-v2
./packages/server
./packages/server-v2
./packages/kap-server
./packages/server-e2e
./packages/kaos
./packages/kosong
@ -91,7 +91,7 @@
"@moonshot-ai/agent-core"
"@moonshot-ai/agent-core-v2"
"@moonshot-ai/server"
"@moonshot-ai/server-v2"
"@moonshot-ai/kap-server"
"@moonshot-ai/server-e2e"
"@moonshot-ai/kaos"
"@moonshot-ai/kosong"

View file

@ -6,7 +6,7 @@
* logs through `log`. Bound at App scope.
*
* WS event fan-out (sequencing, journaling, replay, per-connection dispatch)
* is a transport concern and lives in the edge package (`packages/server-v2`)
* is a transport concern and lives in the edge package (`packages/kap-server`)
* on top of `IEventService` + `IAgentRecordService` not here.
*/

View file

@ -1283,7 +1283,8 @@ type TelemetryInterruptReason =
| 'aborted'
| 'max_steps'
| 'error'
| 'filtered';
| 'filtered'
| 'blocked';
function telemetryInterruptReason(
reason: LoopTurnInterruptedEvent['reason'] | Exclude<TurnEndedEvent['reason'], 'completed'>,
@ -1294,6 +1295,7 @@ function telemetryInterruptReason(
}
if (reason === 'aborted' || reason === 'cancelled') return 'aborted';
if (reason === 'failed') return 'error';
if (reason === 'blocked') return 'blocked';
// Remaining values are `max_steps` | `error` | `filtered`, which match the
// telemetry enum.
return reason;

View file

@ -1,5 +1,5 @@
{
"name": "@moonshot-ai/server-v2",
"name": "@moonshot-ai/kap-server",
"version": "0.0.0",
"private": true,
"description": "Kimi Code server backed by the DI × Scope agent engine (agent-core-v2)",

View file

@ -1,5 +1,5 @@
/**
* `@moonshot-ai/server-v2/contract` the public `/api/v2` wire contract.
* `@moonshot-ai/kap-server/contract` the public `/api/v2` wire contract.
*
* Re-exports the server-side allowlists that bind public `resource:action`
* segments and event names to internal Services. Client SDKs import this

View file

@ -1,5 +1,5 @@
/**
* `@moonshot-ai/server-v2` public surface the Kimi Code server backed by the
* `@moonshot-ai/kap-server` public surface the Kimi Code server backed by the
* DI × Scope agent engine (`@moonshot-ai/agent-core-v2`).
*/

Some files were not shown because too many files have changed in this diff Show more