Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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/agent-core-auth-identity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/agent-core": patch
---

Thread the host identity through the managed auth facades so OAuth token refreshes from inside the core carry the `X-Msh-*` device headers.
5 changes: 5 additions & 0 deletions .changeset/cli-web-login-identity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---

Fix request headers not being passed correctly on some requests.
5 changes: 5 additions & 0 deletions .changeset/kap-server-host-identity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kap-server": minor
---

Require a `hostIdentity` when starting the server and derive the default outbound identity headers (User-Agent + `X-Msh-*`) from it, replacing the hardcoded CLI fallback. The `version` option is renamed to `serverVersion`, and session export manifests now record the host product version — plus an optional desktop version for desktop exports — instead of the engine version.
5 changes: 5 additions & 0 deletions .changeset/oauth-host-identity-platform.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code-oauth": minor
---

Rework the host identity type: rename `userAgentProduct` to `productName` and add a required `platform` field, so every host explicitly declares the `X-Msh-Platform` value it reports instead of silently inheriting the CLI's. OAuth requests now also send the product User-Agent (with the optional runtime suffix), so the OAuth host can tell client families and surfaces apart.
5 changes: 5 additions & 0 deletions .changeset/sdk-host-identity-rename.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code-sdk": minor
Comment thread
liruifengv marked this conversation as resolved.
---

Rename `userAgentProduct` to `productName` and require `platform` in the host identity options exposed by the SDK.
5 changes: 5 additions & 0 deletions .changeset/v2-bootstrap-client-identity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/agent-core-v2": minor
---

Replace the bootstrap `clientVersion` with a required `clientIdentity` host identity object: the OAuth device-flow endpoints now send the full `X-Msh-*` device headers on every host, telemetry reads the client version from the same source, and the session export manifest gains an optional desktop version field.
20 changes: 12 additions & 8 deletions apps/kimi-code/src/cli/sub/web/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,20 @@
import { existsSync } from 'node:fs';
import { join } from 'node:path';

import { hostRequestHeadersSeed } from '@moonshot-ai/agent-core-v2';
import { createServerLogger, startServer, type ServerLogger } from '@moonshot-ai/kap-server';
import { shutdownTelemetry, track } from '@moonshot-ai/kimi-telemetry';
import chalk from 'chalk';
import { type Command } from 'commander';

import { CLI_SHUTDOWN_TIMEOUT_MS } from '#/constant/app';
import { CLI_SHUTDOWN_TIMEOUT_MS, WEB_USER_AGENT_SUFFIX } from '#/constant/app';
import { getNativeWebAssetsDir } from '#/native/web-assets';
import { darkColors } from '#/tui/theme/colors';
import { openUrl as defaultOpenUrl } from '#/utils/open-url';
import { getDataDir } from '#/utils/paths';

import { initializeServerTelemetry } from '../../telemetry';
import {
buildKimiDefaultHeaders,
createKimiCodeHostIdentity,
getHostPackageRoot,
getVersion,
} from '../../version';
Expand Down Expand Up @@ -278,7 +277,16 @@ async function runServerInProcess(
port: options.port,
// Report the CLI's product version as `server_version` (/meta, web UI)
// rather than kap-server's private package version.
version,
serverVersion: version,
// The CLI's host identity: feeds the engine's bootstrap client identity
// and the derived outbound headers (User-Agent + X-Msh-*), so web-UI
// OAuth flows and model / WebSearch requests carry the CLI identity. The
// `web` User-Agent suffix distinguishes web-UI traffic from direct CLI
// runs upstream (same product token, same platform).
hostIdentity: {
...createKimiCodeHostIdentity(version),
userAgentSuffix: WEB_USER_AGENT_SUFFIX,
},
logLevel: options.logLevel,
logger,
debugEndpoints: options.debugEndpoints,
Expand All @@ -291,10 +299,6 @@ async function runServerInProcess(
// `telemetry` toggle). Complements the v1 client registered above, which
// only covers host-level events.
telemetry: true,
// Seed the CLI's Kimi identity headers so the engine's outbound
// requests (model, WebSearch, FetchURL) carry the same User-Agent +
// X-Msh-* identity as direct CLI runs.
seeds: hostRequestHeadersSeed(buildKimiDefaultHeaders(version)),
webAssetsDir,
});
logger.info('serving the REST/WS API and the bundled web UI');
Expand Down
2 changes: 1 addition & 1 deletion apps/kimi-code/src/cli/v2/run-v2-print.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ export async function runV2Print(
const identity = createKimiCodeHostIdentity(version);
const hostHeaders = createKimiDefaultHeaders({ homeDir, ...identity });

const { app } = bootstrap({ homeDir, clientVersion: version }, [
const { app } = bootstrap({ homeDir, clientIdentity: identity }, [
...logSeed(logging),
...hostRequestHeadersSeed(hostHeaders),
// `--skillsDir` (v1 print parity): explicit skill dirs replace default
Expand Down
13 changes: 3 additions & 10 deletions apps/kimi-code/src/cli/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@
import { existsSync, readFileSync } from 'node:fs';
import { dirname, resolve } from 'node:path';

import { createKimiDefaultHeaders, createKimiUserAgent, type KimiHostIdentity } from '@moonshot-ai/kimi-code-oauth';
import { createKimiUserAgent, KIMI_CODE_PLATFORM, type KimiHostIdentity } from '@moonshot-ai/kimi-code-oauth';

import { CLI_USER_AGENT_PRODUCT } from '#/constant/app';

import { getDataDir } from '../utils/paths';
import { KIMI_BUILD_INFO } from './build-info';

const MODULE_DIR = import.meta.dirname;
Expand Down Expand Up @@ -50,8 +49,9 @@ export function getVersion(): string {

export function createKimiCodeHostIdentity(version = getVersion()): KimiHostIdentity {
return {
userAgentProduct: CLI_USER_AGENT_PRODUCT,
productName: CLI_USER_AGENT_PRODUCT,
version,
platform: KIMI_CODE_PLATFORM,
};
}

Expand All @@ -62,10 +62,3 @@ export function createKimiCodeHostIdentity(version = getVersion()): KimiHostIden
export function createKimiCodeUserAgent(version = getVersion()): string {
return createKimiUserAgent(createKimiCodeHostIdentity(version));
}

export function buildKimiDefaultHeaders(version: string): Record<string, string> {
return createKimiDefaultHeaders({
homeDir: getDataDir(),
...createKimiCodeHostIdentity(version),
});
}
4 changes: 4 additions & 0 deletions apps/kimi-code/src/constant/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ export const CLI_UI_MODE = 'shell';
// Telemetry ui_mode for the `kimi web` host. Same product
// as the CLI (CLI_USER_AGENT_PRODUCT); the surface is distinguished by ui_mode.
export const WEB_UI_MODE = 'web';
// User-Agent suffix for the `kimi web` host: its requests go out as
// `kimi-code-cli/<version> (web)` so upstream can tell web-UI traffic
// apart from direct CLI runs without changing the product token or platform.
export const WEB_USER_AGENT_SUFFIX = 'web';

// Give telemetry a short flush window without making CLI exit feel stuck.
export const CLI_SHUTDOWN_TIMEOUT_MS = 3000;
Expand Down
2 changes: 1 addition & 1 deletion apps/kimi-code/test/cli/run-shell.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ describe('runShell', () => {
expect(mocks.kimiHarnessConstructor).toHaveBeenCalledWith(
expect.objectContaining({
identity: expect.objectContaining({
userAgentProduct: 'kimi-code-cli',
productName: 'kimi-code-cli',
version: '1.2.3-test',
}),
sessionStartedProperties: { yolo: true, auto: false, plan: true, afk: false },
Expand Down
6 changes: 5 additions & 1 deletion apps/kimi-code/test/cli/v2-run-print.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,11 @@ function makeFakeHarness() {
{
platform: 'linux',
arch: 'x64',
clientVersion: '1.2.3-test',
clientIdentity: {
productName: 'test-product',
version: '1.2.3-test',
platform: 'test_platform',
},
osHomeDir: '/home/test',
getEnv: () => undefined,
},
Expand Down
7 changes: 0 additions & 7 deletions apps/kimi-code/test/cli/version.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { dirname, join } from 'node:path';
import { describe, expect, it } from 'vitest';

import {
buildKimiDefaultHeaders,
createKimiCodeUserAgent,
getHostPackageJsonPath,
getHostPackageRoot,
Expand All @@ -21,12 +20,6 @@ describe('cli version helpers', () => {
expect(getVersion()).toBe(pkg.version);
});

it('builds default headers with the kimi-code-cli user-agent', () => {
const headers = buildKimiDefaultHeaders('1.2.3');

expect(headers['User-Agent']).toBe('kimi-code-cli/1.2.3');
});

it('builds the product user-agent for ad-hoc fetches', () => {
expect(createKimiCodeUserAgent('1.2.3')).toBe('kimi-code-cli/1.2.3');
});
Expand Down
2 changes: 1 addition & 1 deletion apps/vscode/docs/node-sdk-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ stay in the trusted Extension Host.

The runtime constructs the SDK client with:

- `userAgentProduct: "kimi-code-vscode"`
- `productName: "kimi-code-vscode"`
- `version` from `apps/vscode/package.json`
- `uiMode: "vscode"`

Expand Down
3 changes: 2 additions & 1 deletion apps/vscode/src/runtime/kimi-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@ export class KimiRuntime {
createKimiHarness({
...(options.homeDir === undefined ? {} : { homeDir: options.homeDir }),
identity: {
userAgentProduct: "kimi-code-vscode",
productName: "kimi-code-vscode",
version: options.version,
platform: "kimi_code_vscode",
},
uiMode: "vscode",
});
Expand Down
2 changes: 1 addition & 1 deletion apps/vscode/test/kimi-harness.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ async function createRuntimeRig(extraAliases: readonly string[] = []): Promise<R
async function createPlainHarness(homeDir: string): Promise<KimiHarness> {
const harness = createKimiHarness({
homeDir,
identity: { userAgentProduct: "kimi-code-cli", version: "test" },
identity: { productName: "kimi-code-cli", version: "test", platform: "kimi_code_cli" },
});
cleanups.push(() => harness.close());
return harness;
Expand Down
2 changes: 1 addition & 1 deletion apps/vscode/test/replay-resume.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ async function createReplayRig(): Promise<ReplayRig> {
const provider = await createFakeProviderHarness();
const harness = createKimiHarness({
homeDir,
identity: { userAgentProduct: "kimi-code-vscode", version: "test" },
identity: { productName: "kimi-code-vscode", version: "test", platform: "kimi_code_vscode" },
});
await harness.setConfig({
providers: {
Expand Down
2 changes: 1 addition & 1 deletion packages/agent-core-v2/src/app/auth/authService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -864,7 +864,7 @@ function managedModel(
class OAuthToolkitService extends KimiOAuthToolkit implements IOAuthToolkit {
declare readonly _serviceBrand: undefined;
constructor(@IBootstrapService bootstrap: IBootstrapService) {
super({ homeDir: bootstrap.homeDir });
super({ homeDir: bootstrap.homeDir, identity: bootstrap.clientIdentity });
}
}

Expand Down
20 changes: 12 additions & 8 deletions packages/agent-core-v2/src/app/bootstrap/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* Defines the `IBootstrapService`, the snapshot of the world the process runs
* in, resolved once at startup and frozen for the process: observed host facts
* (`platform`, `arch`, `cwd`, `osHomeDir`, `getEnv`, `clientVersion`) and the
* (`platform`, `arch`, `cwd`, `osHomeDir`, `getEnv`, `clientIdentity`) and the
* app path layout (`homeDir`, `configPath`, …). `resolveBootstrapOptions` is
* the single place that reads `process.env` / `os.homedir()` / invocation
* input to resolve the snapshot; everything downstream reads from
Expand All @@ -18,6 +18,8 @@ import { homedir } from 'node:os';

import { join } from 'pathe';

import type { KimiHostIdentity } from '@moonshot-ai/kimi-code-oauth';

import { SyncDescriptor } from '#/_base/di/descriptors';
import { createDecorator, type ServiceIdentifier } from '#/_base/di/instantiation';
import { createAppScope, type Scope, type ScopeSeed } from '#/_base/di/scope';
Expand All @@ -36,7 +38,7 @@ export interface IBootstrapOptions {
readonly arch: string;
readonly cwd: string;
readonly env: NodeJS.ProcessEnv;
readonly clientVersion: string;
readonly clientIdentity: KimiHostIdentity;
}

export const IBootstrapOptions: ServiceIdentifier<IBootstrapOptions> =
Expand All @@ -61,7 +63,7 @@ export interface IBootstrapService {
readonly osHomeDir: string;
readonly homeDir: string;
readonly configPath: string;
readonly clientVersion: string;
readonly clientIdentity: KimiHostIdentity;
readonly sessionsDir: string;
readonly blobsDir: string;
readonly storeDir: string;
Expand All @@ -87,10 +89,12 @@ export interface BootstrapInput {
readonly platform?: NodeJS.Platform;
readonly arch?: string;
readonly cwd?: string;
readonly clientVersion?: string;
/** Required: every process names its host. There is deliberately no default
— a fabricated identity would silently misreport the host upstream. */
readonly clientIdentity: KimiHostIdentity;
}

export function resolveBootstrapOptions(input: BootstrapInput = {}): IBootstrapOptions {
export function resolveBootstrapOptions(input: BootstrapInput): IBootstrapOptions {
const env = input.env ?? process.env;
const osHomeDir = input.osHomeDir ?? homedir();
const homeDir = resolveKimiHome(input.homeDir, env, osHomeDir);
Expand All @@ -103,19 +107,19 @@ export function resolveBootstrapOptions(input: BootstrapInput = {}): IBootstrapO
arch: input.arch ?? process.arch,
cwd: input.cwd ?? process.cwd(),
env,
clientVersion: input.clientVersion ?? 'unknown',
clientIdentity: input.clientIdentity,
};
}

export function bootstrapSeed(input: BootstrapInput = {}): ScopeSeed {
export function bootstrapSeed(input: BootstrapInput): ScopeSeed {
return [[IBootstrapOptions as ServiceIdentifier<unknown>, resolveBootstrapOptions(input)]];
}

export interface BootstrapResult {
readonly app: Scope;
}

export function bootstrap(input: BootstrapInput = {}, extraSeeds: ScopeSeed = []): BootstrapResult {
export function bootstrap(input: BootstrapInput, extraSeeds: ScopeSeed = []): BootstrapResult {
const options = resolveBootstrapOptions(input);
const app = createAppScope({
extra: [...bootstrapSeed(input), ...storageSeed(options), ...skillSeed(), ...extraSeeds],
Expand Down
6 changes: 4 additions & 2 deletions packages/agent-core-v2/src/app/bootstrap/bootstrapService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

import { basename, join, relative } from 'pathe';

import type { KimiHostIdentity } from '@moonshot-ai/kimi-code-oauth';

import { LifecycleScope, ScopeActivation, registerScopedService } from '#/_base/di/scope';

import {
Expand All @@ -32,7 +34,7 @@ export class BootstrapService implements IBootstrapService {
readonly osHomeDir: string;
readonly homeDir: string;
readonly configPath: string;
readonly clientVersion: string;
readonly clientIdentity: KimiHostIdentity;
readonly sessionsDir: string;
readonly blobsDir: string;
readonly storeDir: string;
Expand All @@ -51,7 +53,7 @@ export class BootstrapService implements IBootstrapService {
this.env = options.env;
this.homeDir = options.homeDir;
this.configPath = options.configPath;
this.clientVersion = options.clientVersion;
this.clientIdentity = options.clientIdentity;
this.sessionsDir = join(options.homeDir, 'sessions');
this.blobsDir = join(options.homeDir, 'blobs');
this.storeDir = join(options.homeDir, 'store');
Expand Down
12 changes: 6 additions & 6 deletions packages/agent-core-v2/src/app/hostIdentity/hostIdentity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* `hostIdentity` domain (L3) — runtime identity of the embedding host.
*
* Holds process-level overrides the host product (CLI, desktop, …) injects at
* the composition root: `productName` fills the `${product_name}` slot in the
* the composition root: `displayName` fills the `${product_name}` slot in the
* base system-prompt template, `replyStyleGuide` replaces the
* `${reply_style_guide}` block (the CLI default describes Markdown rendering
* in a terminal). Composition roots set them through {@link hostIdentitySeed};
Expand All @@ -13,8 +13,8 @@
import { createDecorator, type ServiceIdentifier } from '#/_base/di/instantiation';
import { LifecycleScope, registerScopedService, ScopeActivation, type ScopeSeed } from '#/_base/di/scope';

export interface HostIdentityOverrides {
readonly productName?: string;
export interface PromptIdentityOverrides {
readonly displayName?: string;
readonly replyStyleGuide?: string;
}

Expand All @@ -36,13 +36,13 @@ export class HostIdentity implements IHostIdentity {
) {}
}

export function hostIdentitySeed(overrides: HostIdentityOverrides | undefined): ScopeSeed {
export function hostIdentitySeed(overrides: PromptIdentityOverrides | undefined): ScopeSeed {
if (overrides === undefined) return [];
if (overrides.productName === undefined && overrides.replyStyleGuide === undefined) return [];
if (overrides.displayName === undefined && overrides.replyStyleGuide === undefined) return [];
return [
[
IHostIdentity as ServiceIdentifier<unknown>,
new HostIdentity(overrides.productName, overrides.replyStyleGuide),
new HostIdentity(overrides.displayName, overrides.replyStyleGuide),
],
];
}
Expand Down
2 changes: 2 additions & 0 deletions packages/agent-core-v2/src/app/sessionExport/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export function buildExportManifest(args: {
readonly sessionLogPath?: string | undefined;
readonly globalLogPath?: string | undefined;
readonly webLogPath?: string;
readonly desktopVersion?: string;
readonly installSource?: string | undefined;
readonly shellEnv?: ShellEnvironment | undefined;
}): ExportSessionManifest {
Expand All @@ -52,6 +53,7 @@ export function buildExportManifest(args: {
sessionLogPath: args.sessionLogPath,
globalLogPath: args.globalLogPath,
webLogPath: args.webLogPath,
desktopVersion: args.desktopVersion,
installSource: args.installSource,
shellEnv: args.shellEnv,
};
Expand Down
Loading
Loading