From d7b1ec4fff5072a820d2cf1de5468b1cc568bba6 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Tue, 2 Jun 2026 21:12:15 +0800 Subject: [PATCH 1/5] refactor: split SDK RPC client --- .changeset/split-sdk-rpc-client.md | 6 + apps/kimi-code/src/cli/run-prompt.ts | 5 +- apps/kimi-code/src/cli/run-shell.ts | 9 +- apps/kimi-code/src/cli/sub/export.ts | 5 +- apps/kimi-code/test/cli/export.test.ts | 33 +++-- apps/kimi-code/test/cli/run-prompt.test.ts | 35 +++--- apps/kimi-code/test/cli/run-shell.test.ts | 33 +++-- .../test/e2e/local-logging-export.e2e.test.ts | 4 +- .../test/e2e/real-llm-smoke.e2e.test.ts | 4 +- .../examples/kimi-harness-auth-smoke.ts | 4 +- .../examples/kimi-harness-cancel-smoke.ts | 4 +- .../examples/kimi-harness-config-smoke.ts | 4 +- .../examples/kimi-harness-export-smoke.ts | 4 +- .../examples/kimi-harness-list-smoke.ts | 4 +- .../examples/kimi-harness-log-marker.ts | 4 +- .../examples/kimi-harness-logging-smoke.ts | 4 +- .../examples/kimi-harness-prompt-demo.ts | 4 +- .../examples/kimi-harness-rename-smoke.ts | 4 +- .../examples/kimi-harness-set-model-smoke.ts | 4 +- .../kimi-harness-set-permission-smoke.ts | 4 +- .../kimi-harness-set-thinking-smoke.ts | 4 +- .../node-sdk/examples/kimi-harness-smoke.ts | 4 +- .../examples/kimi-harness-steer-smoke.ts | 4 +- .../examples/t8-race-create-single.ts | 4 +- packages/node-sdk/examples/t8-race-create.ts | 6 +- packages/node-sdk/src/index.ts | 7 ++ packages/node-sdk/src/kimi-harness.ts | 83 +++++-------- packages/node-sdk/src/local-rpc.ts | 113 ++++++++++++++++++ packages/node-sdk/src/rpc.ts | 60 +--------- packages/node-sdk/src/session.ts | 8 +- packages/node-sdk/test/auth-facade.test.ts | 18 +-- packages/node-sdk/test/config.test.ts | 10 +- .../test/create-session-transport.test.ts | 28 ++--- packages/node-sdk/test/export-session.test.ts | 6 +- packages/node-sdk/test/list-sessions.test.ts | 10 +- packages/node-sdk/test/local-logging.test.ts | 32 ++--- packages/node-sdk/test/rename-session.test.ts | 8 +- .../test/session-background-tasks.test.ts | 12 +- packages/node-sdk/test/session-cancel.test.ts | 10 +- .../session-plan-compact-usage-resume.test.ts | 20 ++-- .../test/session-prompt-events.test.ts | 16 ++- .../node-sdk/test/session-set-model.test.ts | 10 +- .../test/session-set-permission.test.ts | 8 +- .../test/session-set-thinking.test.ts | 8 +- packages/node-sdk/test/session-skills.test.ts | 16 ++- packages/node-sdk/test/session-steer.test.ts | 10 +- 46 files changed, 379 insertions(+), 314 deletions(-) create mode 100644 .changeset/split-sdk-rpc-client.md create mode 100644 packages/node-sdk/src/local-rpc.ts diff --git a/.changeset/split-sdk-rpc-client.md b/.changeset/split-sdk-rpc-client.md new file mode 100644 index 0000000000..4cf3d8356a --- /dev/null +++ b/.changeset/split-sdk-rpc-client.md @@ -0,0 +1,6 @@ +--- +"@moonshot-ai/kimi-code-sdk": minor +"@moonshot-ai/kimi-code": patch +--- + +Allow SDK runtime creation to use a separate RPC client while preserving local CLI startup. diff --git a/apps/kimi-code/src/cli/run-prompt.ts b/apps/kimi-code/src/cli/run-prompt.ts index a542fb2704..d5af0010e2 100644 --- a/apps/kimi-code/src/cli/run-prompt.ts +++ b/apps/kimi-code/src/cli/run-prompt.ts @@ -7,10 +7,11 @@ import { } from '@moonshot-ai/kimi-telemetry'; import chalk from 'chalk'; import { - KimiHarness, + createLocalKimiHarness, log, type Event, type HookResultEvent, + type KimiHarness, type Session, type SessionStatus, type TelemetryClient, @@ -60,7 +61,7 @@ export async function runPrompt( withContext: withTelemetryContext, setContext: setTelemetryContext, }; - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ homeDir: telemetryBootstrap.homeDir, identity: createKimiCodeHostIdentity(version), uiMode: PROMPT_UI_MODE, diff --git a/apps/kimi-code/src/cli/run-shell.ts b/apps/kimi-code/src/cli/run-shell.ts index a68d2f896d..179e2c2ae0 100644 --- a/apps/kimi-code/src/cli/run-shell.ts +++ b/apps/kimi-code/src/cli/run-shell.ts @@ -9,7 +9,12 @@ import { track, withTelemetryContext, } from '@moonshot-ai/kimi-telemetry'; -import { KimiHarness, log, type TelemetryClient } from '@moonshot-ai/kimi-code-sdk'; +import { + createLocalKimiHarness, + log, + type KimiHarness, + type TelemetryClient, +} from '@moonshot-ai/kimi-code-sdk'; import { CLI_SHUTDOWN_TIMEOUT_MS, CLI_UI_MODE } from '#/constant/app'; import { detectPendingMigration } from '#/migration/index'; @@ -51,7 +56,7 @@ export async function runShell( withContext: withTelemetryContext, setContext: setTelemetryContext, }; - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ homeDir: telemetryBootstrap.homeDir, identity: createKimiCodeHostIdentity(version), telemetry: telemetryClient, diff --git a/apps/kimi-code/src/cli/sub/export.ts b/apps/kimi-code/src/cli/sub/export.ts index f779cef583..267eed4770 100644 --- a/apps/kimi-code/src/cli/sub/export.ts +++ b/apps/kimi-code/src/cli/sub/export.ts @@ -14,9 +14,10 @@ import { withTelemetryContext, } from '@moonshot-ai/kimi-telemetry'; import { - KimiHarness, + createLocalKimiHarness, type ExportSessionInput, type ExportSessionResult, + type KimiHarness, type SessionSummary, type ShellEnvironment, type TelemetryClient, @@ -144,7 +145,7 @@ function createDefaultExportDeps(overrides: Partial = {}): ExportDep }; const getHarness = (): KimiHarness => { const currentTelemetryBootstrap = getTelemetryBootstrap(); - harness ??= new KimiHarness({ + harness ??= createLocalKimiHarness({ homeDir: currentTelemetryBootstrap.homeDir, identity, telemetry: telemetryClient, diff --git a/apps/kimi-code/test/cli/export.test.ts b/apps/kimi-code/test/cli/export.test.ts index 934c41ccd6..8407a5c881 100644 --- a/apps/kimi-code/test/cli/export.test.ts +++ b/apps/kimi-code/test/cli/export.test.ts @@ -52,24 +52,23 @@ vi.mock('@moonshot-ai/kimi-code-sdk', async (importOriginal) => { return { ...actual, resolveKimiHome: mocks.resolveKimiHome, - KimiHarness: class { - homeDir: string; - auth = { - getCachedAccessToken: mocks.harnessGetCachedAccessToken, - }; - ensureConfigFile = mocks.harnessEnsureConfigFile; - getConfig = mocks.harnessGetConfig; - track = mocks.harnessTrack; - constructor(...args: unknown[]) { - const options = args[0] as { readonly homeDir?: string } | undefined; - this.homeDir = options?.homeDir ?? '/tmp/kimi-export-home'; - if (mocks.harnessCreatesDeviceIdOnConstruction) { - mocks.createKimiDeviceId(this.homeDir); - } - mocks.kimiHarnessConstructor(...args); + createLocalKimiHarness: (...args: unknown[]) => { + const options = args[0] as { readonly homeDir?: string } | undefined; + const homeDir = options?.homeDir ?? '/tmp/kimi-export-home'; + if (mocks.harnessCreatesDeviceIdOnConstruction) { + mocks.createKimiDeviceId(homeDir); } - - exportSession = mocks.harnessExportSession; + mocks.kimiHarnessConstructor(...args); + return { + homeDir, + auth: { + getCachedAccessToken: mocks.harnessGetCachedAccessToken, + }, + ensureConfigFile: mocks.harnessEnsureConfigFile, + getConfig: mocks.harnessGetConfig, + track: mocks.harnessTrack, + exportSession: mocks.harnessExportSession, + }; }, }; }); diff --git a/apps/kimi-code/test/cli/run-prompt.test.ts b/apps/kimi-code/test/cli/run-prompt.test.ts index b62cf8e4cb..766380b0fb 100644 --- a/apps/kimi-code/test/cli/run-prompt.test.ts +++ b/apps/kimi-code/test/cli/run-prompt.test.ts @@ -78,25 +78,24 @@ vi.mock('@moonshot-ai/kimi-code-sdk', async (importOriginal) => { return { ...actual, resolveKimiHome: mocks.resolveKimiHome, - KimiHarness: class { - homeDir: string; - auth = { getCachedAccessToken: mocks.harnessGetCachedAccessToken }; - ensureConfigFile = mocks.harnessEnsureConfigFile; - getConfig = mocks.harnessGetConfig; - createSession = mocks.harnessCreateSession; - resumeSession = mocks.harnessResumeSession; - listSessions = mocks.harnessListSessions; - close = mocks.harnessClose; - track = mocks.harnessTrack; - - constructor(...args: unknown[]) { - const options = args[0] as { readonly homeDir?: string } | undefined; - this.homeDir = options?.homeDir ?? '/tmp/kimi-code-test-home'; - if (mocks.harnessCreatesDeviceIdOnConstruction) { - mocks.createKimiDeviceId(this.homeDir); - } - mocks.kimiHarnessConstructor(...args); + createLocalKimiHarness: (...args: unknown[]) => { + const options = args[0] as { readonly homeDir?: string } | undefined; + const homeDir = options?.homeDir ?? '/tmp/kimi-code-test-home'; + if (mocks.harnessCreatesDeviceIdOnConstruction) { + mocks.createKimiDeviceId(homeDir); } + mocks.kimiHarnessConstructor(...args); + return { + homeDir, + auth: { getCachedAccessToken: mocks.harnessGetCachedAccessToken }, + ensureConfigFile: mocks.harnessEnsureConfigFile, + getConfig: mocks.harnessGetConfig, + createSession: mocks.harnessCreateSession, + resumeSession: mocks.harnessResumeSession, + listSessions: mocks.harnessListSessions, + close: mocks.harnessClose, + track: mocks.harnessTrack, + }; }, }; }); diff --git a/apps/kimi-code/test/cli/run-shell.test.ts b/apps/kimi-code/test/cli/run-shell.test.ts index 8aa72d9455..fbd198e580 100644 --- a/apps/kimi-code/test/cli/run-shell.test.ts +++ b/apps/kimi-code/test/cli/run-shell.test.ts @@ -68,24 +68,23 @@ vi.mock('@moonshot-ai/kimi-code-sdk', async (importOriginal) => { return { ...actual, resolveKimiHome: mocks.resolveKimiHome, - KimiHarness: class { - homeDir: string; - auth = { - getCachedAccessToken: mocks.harnessGetCachedAccessToken, - }; - ensureConfigFile = mocks.harnessEnsureConfigFile; - getConfig = mocks.harnessGetConfig; - close = mocks.harnessClose; - track = mocks.harnessTrack; - - constructor(...args: unknown[]) { - const options = args[0] as { readonly homeDir?: string } | undefined; - this.homeDir = options?.homeDir ?? '/tmp/kimi-code-test-home'; - if (mocks.harnessCreatesDeviceIdOnConstruction) { - mocks.createKimiDeviceId(this.homeDir); - } - mocks.kimiHarnessConstructor(...args); + createLocalKimiHarness: (...args: unknown[]) => { + const options = args[0] as { readonly homeDir?: string } | undefined; + const homeDir = options?.homeDir ?? '/tmp/kimi-code-test-home'; + if (mocks.harnessCreatesDeviceIdOnConstruction) { + mocks.createKimiDeviceId(homeDir); } + mocks.kimiHarnessConstructor(...args); + return { + homeDir, + auth: { + getCachedAccessToken: mocks.harnessGetCachedAccessToken, + }, + ensureConfigFile: mocks.harnessEnsureConfigFile, + getConfig: mocks.harnessGetConfig, + close: mocks.harnessClose, + track: mocks.harnessTrack, + }; }, }; }); diff --git a/apps/kimi-code/test/e2e/local-logging-export.e2e.test.ts b/apps/kimi-code/test/e2e/local-logging-export.e2e.test.ts index 8421105f0b..dc402a982e 100644 --- a/apps/kimi-code/test/e2e/local-logging-export.e2e.test.ts +++ b/apps/kimi-code/test/e2e/local-logging-export.e2e.test.ts @@ -8,7 +8,7 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { registerExportCommand } from '#/cli/sub/export'; import { createKimiCodeHostIdentity } from '#/cli/version'; -import { KimiHarness, log } from '@moonshot-ai/kimi-code-sdk'; +import { createLocalKimiHarness, log } from '@moonshot-ai/kimi-code-sdk'; import { __resetRootLoggerForTest } from '../../../../packages/agent-core/src/logging/logger'; const SESSION_LOG = 'logs/kimi-code.log'; @@ -41,7 +41,7 @@ afterEach(async () => { describe.skipIf(!ENABLED)('local logging export e2e', () => { it('exports session log and global log by default, and allows skipping global log', async () => { - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ homeDir, identity: createKimiCodeHostIdentity('0.1.1'), }); diff --git a/apps/kimi-code/test/e2e/real-llm-smoke.e2e.test.ts b/apps/kimi-code/test/e2e/real-llm-smoke.e2e.test.ts index ffa93ba06c..91d5684767 100644 --- a/apps/kimi-code/test/e2e/real-llm-smoke.e2e.test.ts +++ b/apps/kimi-code/test/e2e/real-llm-smoke.e2e.test.ts @@ -14,7 +14,7 @@ import { mkdirSync } from 'node:fs'; import process from 'node:process'; -import { KimiHarness, type Event } from '@moonshot-ai/kimi-code-sdk'; +import { createLocalKimiHarness, type Event } from '@moonshot-ai/kimi-code-sdk'; import { describe, expect, test } from 'vitest'; import { createKimiCodeHostIdentity, getVersion } from '#/cli/version'; @@ -41,7 +41,7 @@ describe.skipIf(!ENABLED)('SDK e2e — real LLM smoke', () => { `[smoke] prompt=${JSON.stringify(prompt)}\n`, ); - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: createKimiCodeHostIdentity(version), }); diff --git a/packages/node-sdk/examples/kimi-harness-auth-smoke.ts b/packages/node-sdk/examples/kimi-harness-auth-smoke.ts index 98d3db8e65..2dfd9d39fc 100644 --- a/packages/node-sdk/examples/kimi-harness-auth-smoke.ts +++ b/packages/node-sdk/examples/kimi-harness-auth-smoke.ts @@ -2,7 +2,7 @@ import { mkdtemp, rm } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; -import { KimiHarness } from '@moonshot-ai/kimi-code-sdk'; +import { createLocalKimiHarness, type KimiHarness } from '@moonshot-ai/kimi-code-sdk'; import { smokeIdentityFromEnv, runPromptToEnd } from './runtime-smoke-helpers'; @@ -17,7 +17,7 @@ async function main(): Promise { const forceLogin = process.env['KIMI_SDK_AUTH_SMOKE_FORCE_LOGIN'] === '1'; const prompt = process.env['KIMI_SDK_AUTH_SMOKE_PROMPT'] ?? 'Reply with exactly: Kimi SDK auth smoke ok'; - const harness = new KimiHarness({ homeDir, identity: smokeIdentityFromEnv() }); + const harness = createLocalKimiHarness({ homeDir, identity: smokeIdentityFromEnv() }); process.stdout.write(`home: ${homeDir}\n`); process.stdout.write(`workDir: ${workDir}\n`); diff --git a/packages/node-sdk/examples/kimi-harness-cancel-smoke.ts b/packages/node-sdk/examples/kimi-harness-cancel-smoke.ts index 92bc11ad7d..398bd4b7d4 100644 --- a/packages/node-sdk/examples/kimi-harness-cancel-smoke.ts +++ b/packages/node-sdk/examples/kimi-harness-cancel-smoke.ts @@ -1,4 +1,4 @@ -import { KimiHarness } from '@moonshot-ai/kimi-code-sdk'; +import { createLocalKimiHarness } from '@moonshot-ai/kimi-code-sdk'; import { smokeIdentityFromEnv, @@ -10,7 +10,7 @@ const PROMPT = 'Write a detailed multi-paragraph explanation of how cancellation should work in an SDK streaming session.'; async function main(): Promise { - const harness = new KimiHarness({ identity: smokeIdentityFromEnv() }); + const harness = createLocalKimiHarness({ identity: smokeIdentityFromEnv() }); try { const session = await createConfiguredSession(harness); diff --git a/packages/node-sdk/examples/kimi-harness-config-smoke.ts b/packages/node-sdk/examples/kimi-harness-config-smoke.ts index 7c4f7d4d8a..5b81251bb4 100644 --- a/packages/node-sdk/examples/kimi-harness-config-smoke.ts +++ b/packages/node-sdk/examples/kimi-harness-config-smoke.ts @@ -2,13 +2,13 @@ import { mkdtemp, readFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; -import { KimiHarness } from '@moonshot-ai/kimi-code-sdk'; +import { createLocalKimiHarness } from '@moonshot-ai/kimi-code-sdk'; import { smokeIdentityFromEnv } from './runtime-smoke-helpers'; async function main(): Promise { const homeDir = await mkdtemp(join(tmpdir(), 'kimi-harness-config-home-')); - const harness = new KimiHarness({ homeDir, identity: smokeIdentityFromEnv() }); + const harness = createLocalKimiHarness({ homeDir, identity: smokeIdentityFromEnv() }); const initial = await harness.getConfig(); if (Object.keys(initial.providers).length > 0) { diff --git a/packages/node-sdk/examples/kimi-harness-export-smoke.ts b/packages/node-sdk/examples/kimi-harness-export-smoke.ts index 1244379183..7b1b3ffac3 100644 --- a/packages/node-sdk/examples/kimi-harness-export-smoke.ts +++ b/packages/node-sdk/examples/kimi-harness-export-smoke.ts @@ -2,14 +2,14 @@ import { mkdir, mkdtemp, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; -import { KimiHarness } from '@moonshot-ai/kimi-code-sdk'; +import { createLocalKimiHarness } from '@moonshot-ai/kimi-code-sdk'; import { smokeIdentityFromEnv } from './runtime-smoke-helpers'; async function main(): Promise { const homeDir = await mkdtemp(join(tmpdir(), 'kimi-harness-export-home-')); const workDir = await mkdtemp(join(tmpdir(), 'kimi-harness-export-work-')); - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: smokeIdentityFromEnv(), homeDir, }); diff --git a/packages/node-sdk/examples/kimi-harness-list-smoke.ts b/packages/node-sdk/examples/kimi-harness-list-smoke.ts index 01a601e1c7..f14728b5f6 100644 --- a/packages/node-sdk/examples/kimi-harness-list-smoke.ts +++ b/packages/node-sdk/examples/kimi-harness-list-smoke.ts @@ -3,7 +3,7 @@ import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { setTimeout as delay } from 'node:timers/promises'; -import { KimiHarness } from '@moonshot-ai/kimi-code-sdk'; +import { createLocalKimiHarness } from '@moonshot-ai/kimi-code-sdk'; import { smokeIdentityFromEnv } from './runtime-smoke-helpers'; @@ -11,7 +11,7 @@ async function main(): Promise { const homeDir = await mkdtemp(join(tmpdir(), 'kimi-harness-list-home-')); const workDirA = await mkdtemp(join(tmpdir(), 'kimi-harness-list-work-a-')); const workDirB = await mkdtemp(join(tmpdir(), 'kimi-harness-list-work-b-')); - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: smokeIdentityFromEnv(), homeDir, }); diff --git a/packages/node-sdk/examples/kimi-harness-log-marker.ts b/packages/node-sdk/examples/kimi-harness-log-marker.ts index 4ffa28ec83..89321631a7 100644 --- a/packages/node-sdk/examples/kimi-harness-log-marker.ts +++ b/packages/node-sdk/examples/kimi-harness-log-marker.ts @@ -3,8 +3,8 @@ import { join } from 'node:path'; import { parseArgs } from 'node:util'; import { + createLocalKimiHarness, flushDiagnosticLogs, - KimiHarness, log, resolveGlobalLogPath, resolveKimiHome, @@ -41,7 +41,7 @@ Options: async function main(): Promise { const options = parseCliArgs(); const resolvedHome = resolveKimiHome(options.homeDir); - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: { userAgentProduct: 'kimi-code-cli', version: 'log-marker' }, homeDir: options.homeDir, }); diff --git a/packages/node-sdk/examples/kimi-harness-logging-smoke.ts b/packages/node-sdk/examples/kimi-harness-logging-smoke.ts index 9dcde502b8..6688a36a01 100644 --- a/packages/node-sdk/examples/kimi-harness-logging-smoke.ts +++ b/packages/node-sdk/examples/kimi-harness-logging-smoke.ts @@ -2,7 +2,7 @@ import { mkdir, readFile, readdir, rm, stat, writeFile } from 'node:fs/promises' import { homedir } from 'node:os'; import { join } from 'node:path'; -import { KimiHarness, log } from '@moonshot-ai/kimi-code-sdk'; +import { createLocalKimiHarness, log } from '@moonshot-ai/kimi-code-sdk'; const SESSION_LOG = 'logs/kimi-code.log'; const GLOBAL_LOG = 'logs/global/kimi-code.log'; @@ -72,7 +72,7 @@ async function main(): Promise { const globalOnly = `SMOKE_GLOBAL_ONLY_${runId}`; const longEntry = `SMOKE_LONG_TRUNCATED_${runId}`; const finalEntry = `SMOKE_FINAL_AFTER_ROTATION_SHOULD_APPEAR_${runId}`; - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: { userAgentProduct: 'kimi-code-cli', version: '0.1.1' }, homeDir: TEST_HOME, }); diff --git a/packages/node-sdk/examples/kimi-harness-prompt-demo.ts b/packages/node-sdk/examples/kimi-harness-prompt-demo.ts index 34aee28c32..979d408043 100644 --- a/packages/node-sdk/examples/kimi-harness-prompt-demo.ts +++ b/packages/node-sdk/examples/kimi-harness-prompt-demo.ts @@ -1,4 +1,4 @@ -import { KimiHarness, type Session, type Event } from '@moonshot-ai/kimi-code-sdk'; +import { createLocalKimiHarness, type Event, type Session } from '@moonshot-ai/kimi-code-sdk'; import { smokeIdentityFromEnv } from './runtime-smoke-helpers'; @@ -8,7 +8,7 @@ const PROMPT = async function main(): Promise { const workDir = process.cwd(); - const harness = new KimiHarness({ identity: smokeIdentityFromEnv() }); + const harness = createLocalKimiHarness({ identity: smokeIdentityFromEnv() }); try { const config = await harness.getConfig(); diff --git a/packages/node-sdk/examples/kimi-harness-rename-smoke.ts b/packages/node-sdk/examples/kimi-harness-rename-smoke.ts index 8312d1ab30..4a95398cb8 100644 --- a/packages/node-sdk/examples/kimi-harness-rename-smoke.ts +++ b/packages/node-sdk/examples/kimi-harness-rename-smoke.ts @@ -2,7 +2,7 @@ import { mkdtemp, readFile, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; -import { KimiHarness } from '@moonshot-ai/kimi-code-sdk'; +import { createLocalKimiHarness } from '@moonshot-ai/kimi-code-sdk'; import type { Event } from '@moonshot-ai/kimi-code-sdk'; import { smokeIdentityFromEnv } from './runtime-smoke-helpers'; @@ -10,7 +10,7 @@ import { smokeIdentityFromEnv } from './runtime-smoke-helpers'; async function main(): Promise { const homeDir = await mkdtemp(join(tmpdir(), 'kimi-harness-rename-home-')); const workDir = await mkdtemp(join(tmpdir(), 'kimi-harness-rename-work-')); - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: smokeIdentityFromEnv(), homeDir, }); diff --git a/packages/node-sdk/examples/kimi-harness-set-model-smoke.ts b/packages/node-sdk/examples/kimi-harness-set-model-smoke.ts index 5af91a000d..22a7b0dd08 100644 --- a/packages/node-sdk/examples/kimi-harness-set-model-smoke.ts +++ b/packages/node-sdk/examples/kimi-harness-set-model-smoke.ts @@ -1,4 +1,4 @@ -import { KimiHarness } from '@moonshot-ai/kimi-code-sdk'; +import { createLocalKimiHarness } from '@moonshot-ai/kimi-code-sdk'; import { smokeIdentityFromEnv, @@ -7,7 +7,7 @@ import { } from './runtime-smoke-helpers'; async function main(): Promise { - const harness = new KimiHarness({ identity: smokeIdentityFromEnv() }); + const harness = createLocalKimiHarness({ identity: smokeIdentityFromEnv() }); try { const config = await harness.getConfig(); diff --git a/packages/node-sdk/examples/kimi-harness-set-permission-smoke.ts b/packages/node-sdk/examples/kimi-harness-set-permission-smoke.ts index e51ed3c277..7275d1bd2d 100644 --- a/packages/node-sdk/examples/kimi-harness-set-permission-smoke.ts +++ b/packages/node-sdk/examples/kimi-harness-set-permission-smoke.ts @@ -1,4 +1,4 @@ -import { KimiHarness, type PermissionMode } from '@moonshot-ai/kimi-code-sdk'; +import { createLocalKimiHarness, type PermissionMode } from '@moonshot-ai/kimi-code-sdk'; import { smokeIdentityFromEnv, @@ -7,7 +7,7 @@ import { } from './runtime-smoke-helpers'; async function main(): Promise { - const harness = new KimiHarness({ identity: smokeIdentityFromEnv() }); + const harness = createLocalKimiHarness({ identity: smokeIdentityFromEnv() }); const mode: PermissionMode = 'yolo'; try { diff --git a/packages/node-sdk/examples/kimi-harness-set-thinking-smoke.ts b/packages/node-sdk/examples/kimi-harness-set-thinking-smoke.ts index e5d440922a..056829c7ee 100644 --- a/packages/node-sdk/examples/kimi-harness-set-thinking-smoke.ts +++ b/packages/node-sdk/examples/kimi-harness-set-thinking-smoke.ts @@ -1,4 +1,4 @@ -import { KimiHarness } from '@moonshot-ai/kimi-code-sdk'; +import { createLocalKimiHarness } from '@moonshot-ai/kimi-code-sdk'; import { smokeIdentityFromEnv, @@ -7,7 +7,7 @@ import { } from './runtime-smoke-helpers'; async function main(): Promise { - const harness = new KimiHarness({ identity: smokeIdentityFromEnv() }); + const harness = createLocalKimiHarness({ identity: smokeIdentityFromEnv() }); try { const session = await createConfiguredSession(harness); diff --git a/packages/node-sdk/examples/kimi-harness-smoke.ts b/packages/node-sdk/examples/kimi-harness-smoke.ts index 85220da72b..fd9bf85fb1 100644 --- a/packages/node-sdk/examples/kimi-harness-smoke.ts +++ b/packages/node-sdk/examples/kimi-harness-smoke.ts @@ -2,7 +2,7 @@ import { mkdir, mkdtemp, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; -import { KimiHarness } from '@moonshot-ai/kimi-code-sdk'; +import { createLocalKimiHarness } from '@moonshot-ai/kimi-code-sdk'; import { smokeIdentityFromEnv } from './runtime-smoke-helpers'; @@ -10,7 +10,7 @@ async function main(): Promise { const workDir = await mkdtemp(join(tmpdir(), 'kimi-harness-smoke-')); const homeDir = await mkdtemp(join(tmpdir(), 'kimi-harness-home-')); - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: smokeIdentityFromEnv(), homeDir, }); diff --git a/packages/node-sdk/examples/kimi-harness-steer-smoke.ts b/packages/node-sdk/examples/kimi-harness-steer-smoke.ts index 704c2c0afd..6db6f6aa54 100644 --- a/packages/node-sdk/examples/kimi-harness-steer-smoke.ts +++ b/packages/node-sdk/examples/kimi-harness-steer-smoke.ts @@ -1,4 +1,4 @@ -import { KimiHarness } from '@moonshot-ai/kimi-code-sdk'; +import { createLocalKimiHarness } from '@moonshot-ai/kimi-code-sdk'; import { smokeIdentityFromEnv, @@ -10,7 +10,7 @@ const PROMPT = 'Draft a long checklist for validating a TypeScript SDK runtime.' const STEER = 'Also include cancellation and permission-mode checks.'; async function main(): Promise { - const harness = new KimiHarness({ identity: smokeIdentityFromEnv() }); + const harness = createLocalKimiHarness({ identity: smokeIdentityFromEnv() }); try { const session = await createConfiguredSession(harness); diff --git a/packages/node-sdk/examples/t8-race-create-single.ts b/packages/node-sdk/examples/t8-race-create-single.ts index d3b0c01600..05e5381c31 100644 --- a/packages/node-sdk/examples/t8-race-create-single.ts +++ b/packages/node-sdk/examples/t8-race-create-single.ts @@ -1,5 +1,5 @@ // One creator: meant to run twice as separate processes simultaneously. -import { KimiHarness } from '@moonshot-ai/kimi-code-sdk'; +import { createLocalKimiHarness } from '@moonshot-ai/kimi-code-sdk'; const workDir = process.argv[2]!; const homeDir = process.argv[3]!; @@ -7,7 +7,7 @@ const sessionId = process.argv[4]!; const label = process.argv[5] ?? 'P'; const identity: any = { userAgentProduct: 'kimi-code-cli', version: '0.0.1-test' }; -const h = new KimiHarness({ identity, homeDir }); +const h = createLocalKimiHarness({ identity, homeDir }); try { const s = await h.createSession({ workDir, id: sessionId, model: 'kimi-code/kimi-for-coding' }); diff --git a/packages/node-sdk/examples/t8-race-create.ts b/packages/node-sdk/examples/t8-race-create.ts index fb8a7f9fab..50bba44026 100644 --- a/packages/node-sdk/examples/t8-race-create.ts +++ b/packages/node-sdk/examples/t8-race-create.ts @@ -1,13 +1,13 @@ // T8.4 driver: create session with explicit id, twice concurrently in same process. -import { KimiHarness } from '@moonshot-ai/kimi-code-sdk'; +import { createLocalKimiHarness, type KimiHarness } from '@moonshot-ai/kimi-code-sdk'; const workDir = process.argv[2]!; const homeDir = process.argv[3]!; const sessionId = process.argv[4]!; const identity: any = { userAgentProduct: 'kimi-code-cli', version: '0.0.1-test' }; -const harnessA = new KimiHarness({ identity, homeDir }); -const harnessB = new KimiHarness({ identity, homeDir }); +const harnessA = createLocalKimiHarness({ identity, homeDir }); +const harnessB = createLocalKimiHarness({ identity, homeDir }); async function run(label: string, h: KimiHarness): Promise { try { diff --git a/packages/node-sdk/src/index.ts b/packages/node-sdk/src/index.ts index ae3d677d20..3c9fa88591 100644 --- a/packages/node-sdk/src/index.ts +++ b/packages/node-sdk/src/index.ts @@ -1,6 +1,13 @@ export { KimiHarness } from '#/kimi-harness'; +export type { KimiHarnessRuntimeOptions } from '#/kimi-harness'; export { Session } from '#/session'; export { KimiAuthFacade } from '#/auth'; +export { + createLocalKimiHarness, + LocalSDKRpcClient, + type LocalSDKRpcClientOptions, +} from '#/local-rpc'; +export { SDKRpcClient } from '#/rpc'; export { applyCatalogProvider, diff --git a/packages/node-sdk/src/kimi-harness.ts b/packages/node-sdk/src/kimi-harness.ts index 769cd3a3df..bececda403 100644 --- a/packages/node-sdk/src/kimi-harness.ts +++ b/packages/node-sdk/src/kimi-harness.ts @@ -1,23 +1,13 @@ import { - ensureConfigFile, ErrorCodes, KimiError, - getRootLogger, - noopTelemetryClient, - resolveConfigPath, - resolveKimiHome, - resolveLoggingConfig, withTelemetryContext, type ExperimentalFlagMap, - type TelemetryClient, - type TelemetryContextPatch, - type TelemetryProperties, } from '@moonshot-ai/agent-core'; -import { assertKimiHostIdentity } from '@moonshot-ai/kimi-code-oauth'; -import { KimiAuthFacade } from '#/auth'; -import { SDKRpcClient } from '#/rpc'; import { Session } from '#/session'; +import type { KimiAuthFacade } from '#/auth'; +import type { SDKRpcClient } from '#/rpc'; import type { CreateSessionOptions, ExportSessionInput, @@ -26,14 +16,27 @@ import type { GetConfigOptions, KimiConfig, KimiConfigPatch, - KimiHarnessOptions, KimiHostIdentity, ListSessionsOptions, RenameSessionInput, ResumeSessionInput, SessionSummary, + TelemetryClient, + TelemetryContextPatch, + TelemetryProperties, } from '#/types'; +export interface KimiHarnessRuntimeOptions { + readonly identity?: KimiHostIdentity; + readonly uiMode?: string; + readonly homeDir: string; + readonly configPath: string; + readonly auth: KimiAuthFacade; + readonly telemetry: TelemetryClient; + readonly ensureConfigFile: () => Promise; + readonly onClose: () => void | Promise; +} + export class KimiHarness { readonly homeDir: string; readonly configPath: string; @@ -43,39 +46,21 @@ export class KimiHarness { private readonly uiMode: string; private readonly telemetry: TelemetryClient; private readonly activeSessions = new Map(); - private readonly rpc: SDKRpcClient; - - constructor(options: KimiHarnessOptions) { - this.identity = - options.identity === undefined ? undefined : assertKimiHostIdentity(options.identity); + private readonly ensureConfigFileImpl: () => Promise; + private readonly closeImpl: () => void | Promise; + + constructor( + private readonly rpc: SDKRpcClient, + options: KimiHarnessRuntimeOptions, + ) { + this.identity = options.identity; this.uiMode = options.uiMode ?? DEFAULT_SESSION_STARTED_UI_MODE; - this.homeDir = resolveKimiHome(options.homeDir); - this.configPath = resolveConfigPath({ - homeDir: this.homeDir, - configPath: options.configPath, - }); - this.configureLogging(); - this.telemetry = options.telemetry ?? noopTelemetryClient; - this.auth = new KimiAuthFacade({ - homeDir: this.homeDir, - configPath: this.configPath, - identity: this.identity, - onRefresh: options.onOAuthRefresh, - }); - this.rpc = new SDKRpcClient({ - homeDir: options.homeDir, - configPath: this.configPath, - identity: this.identity, - resolveOAuthTokenProvider: this.auth.resolveOAuthTokenProvider, - skillDirs: options.skillDirs, - telemetry: this.telemetry, - }); - } - - private configureLogging(): void { - // Fresh configure completes synchronously on the first-time path; pre-init - // noop covers any caller that races before this returns. - void getRootLogger().configure(resolveLoggingConfig({ homeDir: this.homeDir })); + this.homeDir = options.homeDir; + this.configPath = options.configPath; + this.telemetry = options.telemetry; + this.auth = options.auth; + this.ensureConfigFileImpl = options.ensureConfigFile; + this.closeImpl = options.onClose; } get sessions(): ReadonlyMap { @@ -198,7 +183,7 @@ export class KimiHarness { } async ensureConfigFile(): Promise { - await ensureConfigFile(this.configPath); + await this.ensureConfigFileImpl(); } async setConfig(patch: KimiConfigPatch): Promise { @@ -211,11 +196,7 @@ export class KimiHarness { async close(): Promise { await Promise.all(Array.from(this.activeSessions.values(), (session) => session.close())); - try { - await getRootLogger().flush(); - } catch { - // never let logger flush block process exit - } + await this.closeImpl(); } private trackSessionEvent(eventSessionId: string, event: string): void { diff --git a/packages/node-sdk/src/local-rpc.ts b/packages/node-sdk/src/local-rpc.ts new file mode 100644 index 0000000000..cbf5fb11ff --- /dev/null +++ b/packages/node-sdk/src/local-rpc.ts @@ -0,0 +1,113 @@ +import { + createRPC, + ensureConfigFile, + getRootLogger, + KimiCore, + noopTelemetryClient, + resolveConfigPath, + resolveKimiHome, + resolveLoggingConfig, + type CoreAPI, + type OAuthTokenProviderResolver, + type RPCMethods, + type SDKAPI, + type TelemetryClient, +} from '@moonshot-ai/agent-core'; +import { assertKimiHostIdentity, createKimiDefaultHeaders } from '@moonshot-ai/kimi-code-oauth'; + +import { KimiAuthFacade } from '#/auth'; +import { KimiHarness } from '#/kimi-harness'; +import { ClientAPI, SDKRpcClient } from '#/rpc'; +import type { KimiHarnessOptions, KimiHostIdentity, OAuthRefreshOutcome } from '#/types'; + +export interface LocalSDKRpcClientOptions { + readonly homeDir?: string; + readonly configPath?: string; + readonly identity?: KimiHostIdentity; + readonly resolveOAuthTokenProvider?: OAuthTokenProviderResolver; + readonly skillDirs?: readonly string[]; + readonly telemetry?: TelemetryClient; + readonly onOAuthRefresh?: (outcome: OAuthRefreshOutcome) => void; +} + +export class LocalSDKRpcClient extends SDKRpcClient { + readonly homeDir: string; + readonly configPath: string; + readonly identity: KimiHostIdentity | undefined; + readonly telemetry: TelemetryClient; + readonly auth: KimiAuthFacade; + readonly core: KimiCore; + + private readonly ready: Promise>; + + constructor(options: LocalSDKRpcClientOptions = {}) { + super(); + this.identity = + options.identity === undefined ? undefined : assertKimiHostIdentity(options.identity); + this.homeDir = resolveKimiHome(options.homeDir); + this.configPath = resolveConfigPath({ + homeDir: this.homeDir, + configPath: options.configPath, + }); + this.telemetry = options.telemetry ?? noopTelemetryClient; + this.auth = new KimiAuthFacade({ + homeDir: this.homeDir, + configPath: this.configPath, + identity: this.identity, + onRefresh: options.onOAuthRefresh, + }); + + void getRootLogger().configure(resolveLoggingConfig({ homeDir: this.homeDir })); + + const [coreRpc, sdkRpc] = createRPC(); + this.core = new KimiCore(coreRpc, { + homeDir: options.homeDir, + configPath: this.configPath, + kimiRequestHeaders: this.createKimiRequestHeaders(), + resolveOAuthTokenProvider: + options.resolveOAuthTokenProvider ?? this.auth.resolveOAuthTokenProvider, + skillDirs: options.skillDirs, + telemetry: this.telemetry, + }); + this.ready = sdkRpc(new ClientAPI(this)); + } + + async ensureConfigFile(): Promise { + await ensureConfigFile(this.configPath); + } + + async close(): Promise { + try { + await getRootLogger().flush(); + } catch { + // never let logger flush block process exit + } + } + + protected async getRpc(): Promise> { + return this.ready; + } + + private createKimiRequestHeaders(): Record | undefined { + if (this.identity === undefined) return undefined; + return createKimiDefaultHeaders({ + homeDir: this.homeDir, + userAgentProduct: this.identity.userAgentProduct, + version: this.identity.version, + }); + } +} + +export function createLocalKimiHarness(options: KimiHarnessOptions): KimiHarness { + const rpc = new LocalSDKRpcClient(options); + return new KimiHarness(rpc, { + identity: rpc.identity, + uiMode: options.uiMode, + homeDir: rpc.homeDir, + configPath: rpc.configPath, + auth: rpc.auth, + telemetry: rpc.telemetry, + ensureConfigFile: () => rpc.ensureConfigFile(), + onClose: () => rpc.close(), + }); +} diff --git a/packages/node-sdk/src/rpc.ts b/packages/node-sdk/src/rpc.ts index 7346e5a5f0..e2fedc9e55 100644 --- a/packages/node-sdk/src/rpc.ts +++ b/packages/node-sdk/src/rpc.ts @@ -1,25 +1,19 @@ import { - createRPC, ErrorCodes, - KimiCore, makeErrorPayload, - resolveKimiHome, type AgentContextData, type ApprovalRequest, type ApprovalResponse, type CoreAPI, type Event, type ExperimentalFlagMap, - type OAuthTokenProviderResolver, type QuestionRequest, type QuestionResult, + type RPCMethods, type SDKAPI, - type SDKRPCClient, - type TelemetryClient, type ToolCallRequest, type ToolCallResponse, } from '@moonshot-ai/agent-core'; -import { createKimiDefaultHeaders } from '@moonshot-ai/kimi-code-oauth'; import type { ApprovalHandler, QuestionHandler } from '#/events'; import type { @@ -49,20 +43,10 @@ import type { SessionSummary, SkillSummary, Unsubscribe, - KimiHostIdentity, } from '#/types'; const MAIN_AGENT_ID = 'main'; -export interface SDKRpcClientOptions { - readonly homeDir?: string | undefined; - readonly configPath?: string | undefined; - readonly identity?: KimiHostIdentity | undefined; - readonly resolveOAuthTokenProvider?: OAuthTokenProviderResolver | undefined; - readonly skillDirs?: readonly string[]; - readonly telemetry?: TelemetryClient | undefined; -} - export interface SessionPromptRpcInput { readonly sessionId: string; readonly input: PromptInput; @@ -102,44 +86,15 @@ export interface ReconnectMcpServerRpcInput extends SessionIdRpcInput { readonly name: string; } -type ResolvedCoreAPI = Awaited>; +type ResolvedCoreAPI = RPCMethods; -export class SDKRpcClient { - readonly core: KimiCore; +export abstract class SDKRpcClient { interactiveAgentId = MAIN_AGENT_ID; - private readonly ready: Promise; - private rpc: ResolvedCoreAPI | undefined; private readonly eventListeners = new Set<(event: Event) => void>(); private readonly approvalHandlers = new Map(); private readonly questionHandlers = new Map(); - constructor(options: SDKRpcClientOptions = {}) { - const [coreRpc, sdkRpc] = createRPC(); - const homeDir = resolveKimiHome(options.homeDir); - const kimiRequestHeaders = - options.identity === undefined - ? undefined - : createKimiDefaultHeaders({ homeDir, ...options.identity }); - this.core = new KimiCore(coreRpc, { - homeDir: options.homeDir, - configPath: options.configPath, - kimiRequestHeaders, - resolveOAuthTokenProvider: options.resolveOAuthTokenProvider, - skillDirs: options.skillDirs, - telemetry: options.telemetry, - }); - this.ready = sdkRpc(new ClientAPI(this)).then((rpc) => { - this.rpc = rpc; - }); - } - - get homeDir(): string { - return this.core.homeDir; - } - - get configPath(): string { - return this.core.configPath; - } + protected abstract getRpc(): Promise; async createSession(input: CreateSessionOptions): Promise { const rpc = await this.getRpc(); @@ -577,13 +532,6 @@ export class SDKRpcClient { }; } - private async getRpc(): Promise { - await this.ready; - if (this.rpc === undefined) { - throw new Error('SDK RPC client was not initialized.'); - } - return this.rpc; - } } export class ClientAPI implements SDKAPI { diff --git a/packages/node-sdk/src/session.ts b/packages/node-sdk/src/session.ts index 6dc395ef71..33d6d12178 100644 --- a/packages/node-sdk/src/session.ts +++ b/packages/node-sdk/src/session.ts @@ -1,4 +1,10 @@ -import { ErrorCodes, KimiError, type AgentContextData, type KimiErrorCode } from '@moonshot-ai/agent-core'; +import { + ErrorCodes, + KimiError, + type AgentContextData, + type KimiErrorCode, +} from '@moonshot-ai/agent-core'; + import { type ApprovalHandler, type Event, type QuestionHandler } from '#/events'; import type { SDKRpcClient } from '#/rpc'; import type { diff --git a/packages/node-sdk/test/auth-facade.test.ts b/packages/node-sdk/test/auth-facade.test.ts index ea99de6efe..e90397392a 100644 --- a/packages/node-sdk/test/auth-facade.test.ts +++ b/packages/node-sdk/test/auth-facade.test.ts @@ -6,7 +6,7 @@ import { join } from 'node:path'; import { FileTokenStorage, KIMI_CODE_PROVIDER_NAME, type TokenInfo } from '@moonshot-ai/kimi-code-oauth'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { KimiHarness } from '#/index'; +import { createLocalKimiHarness } from '#/index'; import { ProviderManager } from '../../agent-core/src/session/provider-manager'; import { TEST_IDENTITY } from './test-identity'; @@ -41,12 +41,12 @@ afterEach(() => { describe('KimiHarness.auth', () => { it('can construct auth facade without host identity', () => { - expect(() => new KimiHarness({ homeDir })).not.toThrow(); + expect(() => createLocalKimiHarness({ homeDir })).not.toThrow(); }); it('exposes a cached access token without refreshing auth state', async () => { await new FileTokenStorage(join(homeDir, 'credentials')).save('kimi-code', freshToken()); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); await expect(harness.auth.getCachedAccessToken()).resolves.toBe('oauth-access-token'); }); @@ -73,7 +73,7 @@ describe('KimiHarness.auth', () => { ); vi.stubGlobal('fetch', fetchMock); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); const result = await harness.auth.login(); const config = await harness.getConfig({ reload: true }); @@ -149,7 +149,7 @@ model = "kimi-for-coding" ), ); - expect(() => new KimiHarness({ homeDir, identity: TEST_IDENTITY })).toThrow( + expect(() => createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY })).toThrow( /Model "kimi-code\/kimi-for-coding" must define a positive max_context_size/, ); }); @@ -192,7 +192,7 @@ oauth = { storage = "file", key = "oauth/kimi-code" } `, ); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); await expect(harness.auth.logout()).resolves.toMatchObject({ providerName: KIMI_CODE_PROVIDER_NAME, @@ -230,7 +230,7 @@ oauth = { storage = "file", key = "oauth/kimi-code" } ); vi.stubGlobal('fetch', fetchMock); - const harness = new KimiHarness({ homeDir }); + const harness = createLocalKimiHarness({ homeDir }); const result = await harness.auth.getManagedUsage(); expect(result).toMatchObject({ @@ -250,7 +250,7 @@ oauth = { storage = "file", key = "oauth/kimi-code" } const fetchMock = vi.fn(async () => new Response('', { status: 200 })); vi.stubGlobal('fetch', fetchMock); - const harness = new KimiHarness({ homeDir }); + const harness = createLocalKimiHarness({ homeDir }); const result = await harness.auth.submitFeedback({ content: 'great tool', sessionId: 'sess-42', @@ -292,7 +292,7 @@ oauth = { storage = "file", key = "oauth/kimi-code" } ), ); - const harness = new KimiHarness({ homeDir }); + const harness = createLocalKimiHarness({ homeDir }); const result = await harness.auth.submitFeedback({ content: 'x', sessionId: 's', diff --git a/packages/node-sdk/test/config.test.ts b/packages/node-sdk/test/config.test.ts index 3518e848d5..36c89a1b30 100644 --- a/packages/node-sdk/test/config.test.ts +++ b/packages/node-sdk/test/config.test.ts @@ -4,7 +4,7 @@ import { join } from 'node:path'; import { afterEach, describe, expect, it } from 'vitest'; -import { KimiError, KimiHarness } from '#/index'; +import { createLocalKimiHarness, KimiError } from '#/index'; import { parseConfigString, @@ -223,7 +223,7 @@ describe('KimiHarness config API', () => { const configPath = join(homeDir, 'config.toml'); await writeFile(configPath, COMPLETE_TOML, 'utf-8'); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); await harness.setConfig({ providers: { @@ -260,7 +260,7 @@ describe('KimiHarness config API', () => { await writeFile(configPath, COMPLETE_TOML, 'utf-8'); const before = await readFile(configPath, 'utf-8'); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); const setInvalidConfig = harness.setConfig({ providers: { @@ -280,7 +280,7 @@ describe('KimiHarness config API', () => { it('uses default config when the config file is absent', async () => { const homeDir = await makeTempDir(); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); await expect(harness.getConfig()).resolves.toEqual({ providers: {} }); }); @@ -288,7 +288,7 @@ describe('KimiHarness config API', () => { it('can create the default config scaffold without selecting a model', async () => { const homeDir = await makeTempDir(); const configPath = join(homeDir, 'config.toml'); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); await harness.ensureConfigFile(); diff --git a/packages/node-sdk/test/create-session-transport.test.ts b/packages/node-sdk/test/create-session-transport.test.ts index b46ff7f55a..8be29c2431 100644 --- a/packages/node-sdk/test/create-session-transport.test.ts +++ b/packages/node-sdk/test/create-session-transport.test.ts @@ -3,8 +3,8 @@ import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; -import { KimiHarness } from '#/index'; -import type { KimiError } from '#/index'; +import { createLocalKimiHarness } from '#/index'; +import type { KimiError, KimiHarness } from '#/index'; import { afterEach, describe, expect, it } from 'vitest'; import { waitForAgentWireEvent } from './session-runtime-helpers'; @@ -48,7 +48,7 @@ describe('KimiHarness.createSession transport link', () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); const records: TelemetryRecord[] = []; - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir, telemetry: recordingTelemetry(records), @@ -106,7 +106,7 @@ describe('KimiHarness.createSession transport link', () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); const records: TelemetryRecord[] = []; - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir, telemetry: recordingTelemetry(records), @@ -138,7 +138,7 @@ describe('KimiHarness.createSession transport link', () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); const records: TelemetryRecord[] = []; - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir, telemetry: recordingTelemetry(records), @@ -180,7 +180,7 @@ describe('KimiHarness.createSession transport link', () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); const records: TelemetryRecord[] = []; - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ homeDir, telemetry: recordingTelemetry(records), }); @@ -210,7 +210,7 @@ describe('KimiHarness.createSession transport link', () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); await writeTestModelConfig(homeDir); - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -280,7 +280,7 @@ effort = "medium" `, 'utf-8', ); - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -309,7 +309,7 @@ effort = "medium" it('does not require provider config or API keys before prompt is implemented', async () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -326,7 +326,7 @@ effort = "medium" it('requires a non-empty workDir on createSession', async () => { const homeDir = await makeTempDir(); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { await expect( @@ -352,7 +352,7 @@ effort = "medium" // Project-local mcp.json is intentionally ignored, so plant the malformed // file under the user home dir where the loader actually reads from. await writeFile(join(homeDir, 'mcp.json'), '{not json}', 'utf-8'); - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -375,7 +375,7 @@ effort = "medium" const homeDir = await makeTempDir(); const workDir = await makeTempDir(); await writeTestModelConfig(homeDir); - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -408,7 +408,7 @@ effort = "medium" it('applies initial thinking and permission runtime options', async () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -452,7 +452,7 @@ effort = "medium" const homeDir = await makeTempDir(); const workDir = await makeTempDir(); await writeFile(join(homeDir, 'config.toml'), 'default_permission_mode = "auto"\n', 'utf-8'); - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir, }); diff --git a/packages/node-sdk/test/export-session.test.ts b/packages/node-sdk/test/export-session.test.ts index 58d65cd182..40f09e0152 100644 --- a/packages/node-sdk/test/export-session.test.ts +++ b/packages/node-sdk/test/export-session.test.ts @@ -7,8 +7,8 @@ import * as zlib from 'node:zlib'; import { afterEach, describe, expect, it } from 'vitest'; import { + createLocalKimiHarness, KimiError, - KimiHarness, type SessionSummary, } from '#/index'; import { resolveGlobalLogPath } from '../../agent-core/src/logging/logger'; @@ -266,7 +266,7 @@ describe('KimiHarness.exportSession', () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); const records: TelemetryRecord[] = []; - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir, telemetry: recordingTelemetry(records), @@ -301,7 +301,7 @@ describe('KimiHarness.exportSession', () => { it('rejects missing session ids', async () => { const homeDir = await makeTempDir(); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); const missingExport = harness.exportSession({ id: 'ses_missing', version: '1.0.0-test' }); await expect(missingExport).rejects.toBeInstanceOf(KimiError); diff --git a/packages/node-sdk/test/list-sessions.test.ts b/packages/node-sdk/test/list-sessions.test.ts index f9c5775492..4a71713cdf 100644 --- a/packages/node-sdk/test/list-sessions.test.ts +++ b/packages/node-sdk/test/list-sessions.test.ts @@ -5,7 +5,7 @@ import { basename, dirname, join } from 'node:path'; import { afterEach, describe, expect, it } from 'vitest'; -import { KimiHarness } from '#/index'; +import { createLocalKimiHarness } from '#/index'; import type { KimiError } from '#/index'; import { @@ -273,7 +273,7 @@ describe('SessionStore.list', () => { describe('KimiHarness.listSessions', () => { it('rejects whitespace-only workDir with request.work_dir_required', async () => { const homeDir = await makeTempDir(); - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -292,7 +292,7 @@ describe('KimiHarness.listSessions', () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); const otherWorkDir = await makeTempDir(); - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -314,7 +314,7 @@ describe('KimiHarness.listSessions', () => { it('resolves relative workDir inputs before filtering', async () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -335,7 +335,7 @@ describe('KimiHarness.listSessions', () => { it('lists persisted sessions after the active Session has been closed', async () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir, }); diff --git a/packages/node-sdk/test/local-logging.test.ts b/packages/node-sdk/test/local-logging.test.ts index 417b939d34..535d5c5fc8 100644 --- a/packages/node-sdk/test/local-logging.test.ts +++ b/packages/node-sdk/test/local-logging.test.ts @@ -5,7 +5,7 @@ import * as zlib from 'node:zlib'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { KimiHarness, log } from '#/index'; +import { createLocalKimiHarness, log } from '#/index'; import { __resetRootLoggerForTest, getRootLogger } from '../../agent-core/src/logging/logger'; import { TEST_IDENTITY } from './test-identity'; @@ -102,7 +102,7 @@ describe('Local logging — harness integration', () => { const homeDir = await makeTempDir('kimi-log-home-'); const workDir = await makeTempDir('kimi-log-work-'); - const harness = new KimiHarness({ identity: TEST_IDENTITY, homeDir }); + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir }); const session = await harness.createSession({ id: 'ses_logging_int', workDir, @@ -140,7 +140,7 @@ describe('Local logging — harness integration', () => { it('default export bundles session log only; no globalLogPath in manifest', async () => { const homeDir = await makeTempDir('kimi-log-home-'); const workDir = await makeTempDir('kimi-log-work-'); - const harness = new KimiHarness({ identity: TEST_IDENTITY, homeDir }); + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir }); const session = await harness.createSession({ id: 'ses_default_export', workDir }); log.warn('session export marker', { sessionId: session.id }); @@ -168,7 +168,7 @@ describe('Local logging — harness integration', () => { it('default export works when no session log file exists', async () => { const homeDir = await makeTempDir('kimi-log-home-'); const workDir = await makeTempDir('kimi-log-work-'); - const harness = new KimiHarness({ identity: TEST_IDENTITY, homeDir }); + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir }); const session = await harness.createSession({ id: 'ses_no_session_log', workDir }); const outputPath = join(workDir, 'no-log.zip'); @@ -193,7 +193,7 @@ describe('Local logging — harness integration', () => { try { const homeDir = await makeTempDir('kimi-log-home-'); const workDir = await makeTempDir('kimi-log-work-'); - const harness = new KimiHarness({ identity: TEST_IDENTITY, homeDir }); + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir }); const session = await harness.createSession({ id: 'ses_rotated_export', workDir }); for (let i = 0; i < 16; i++) { log.warn(`rotated session marker ${i}`, { @@ -228,7 +228,7 @@ describe('Local logging — harness integration', () => { it('--include-global-log bundles global active and sets manifest field', async () => { const homeDir = await makeTempDir('kimi-log-home-'); const workDir = await makeTempDir('kimi-log-work-'); - const harness = new KimiHarness({ identity: TEST_IDENTITY, homeDir }); + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir }); const session = await harness.createSession({ id: 'ses_global_export', workDir }); log.warn('untagged probe'); @@ -254,9 +254,9 @@ describe('Local logging — harness integration', () => { const firstHome = await makeTempDir('kimi-log-home-a-'); const secondHome = await makeTempDir('kimi-log-home-b-'); const workDir = await makeTempDir('kimi-log-work-'); - const first = new KimiHarness({ identity: TEST_IDENTITY, homeDir: firstHome }); + const first = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir: firstHome }); const firstSession = await first.createSession({ id: 'ses_first_global_export', workDir }); - const second = new KimiHarness({ identity: TEST_IDENTITY, homeDir: secondHome }); + const second = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir: secondHome }); log.warn('active-global-export-marker'); await getRootLogger().flushGlobal(); @@ -281,7 +281,7 @@ describe('Local logging — harness integration', () => { it('logs export flush failures without failing the export', async () => { const homeDir = await makeTempDir('kimi-log-home-'); const workDir = await makeTempDir('kimi-log-work-'); - const harness = new KimiHarness({ identity: TEST_IDENTITY, homeDir }); + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir }); const session = await harness.createSession({ id: 'ses_flush_warning', workDir }); log.warn('flush warning setup', { sessionId: session.id }); log.warn('global untagged marker'); @@ -316,16 +316,16 @@ describe('Local logging — harness integration', () => { it('multiple KimiHarness constructions in the same process do not throw', async () => { const homeDir = await makeTempDir('kimi-log-home-'); - expect(() => new KimiHarness({ identity: TEST_IDENTITY, homeDir })).not.toThrow(); - expect(() => new KimiHarness({ identity: TEST_IDENTITY, homeDir })).not.toThrow(); - expect(() => new KimiHarness({ identity: TEST_IDENTITY, homeDir })).not.toThrow(); + expect(() => createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir })).not.toThrow(); + expect(() => createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir })).not.toThrow(); + expect(() => createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir })).not.toThrow(); }); it('uses the latest harness homeDir for global diagnostic logging', async () => { const firstHome = await makeTempDir('kimi-log-home-a-'); const secondHome = await makeTempDir('kimi-log-home-b-'); - const first = new KimiHarness({ identity: TEST_IDENTITY, homeDir: firstHome }); - const second = new KimiHarness({ identity: TEST_IDENTITY, homeDir: secondHome }); + const first = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir: firstHome }); + const second = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir: secondHome }); log.warn('second-home-marker'); await getRootLogger().flushGlobal(); @@ -361,7 +361,7 @@ describe('Local logging — harness integration', () => { try { const homeDir = await makeTempDir('kimi-log-home-'); const workDir = await makeTempDir('kimi-log-work-'); - const harness = new KimiHarness({ identity: TEST_IDENTITY, homeDir }); + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir }); await harness.createSession({ id: 'ses_off', workDir }); log.error('this should not write'); let logsDir: string[] = []; @@ -378,7 +378,7 @@ describe('Local logging — harness integration', () => { it('KimiHarness.close() flushes the global log', async () => { const homeDir = await makeTempDir('kimi-log-home-'); - const harness = new KimiHarness({ identity: TEST_IDENTITY, homeDir }); + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir }); log.warn('untagged before close'); // No `await flush()` here on purpose — close() must do it. await harness.close(); diff --git a/packages/node-sdk/test/rename-session.test.ts b/packages/node-sdk/test/rename-session.test.ts index 693b1050f9..bfdf4c7c09 100644 --- a/packages/node-sdk/test/rename-session.test.ts +++ b/packages/node-sdk/test/rename-session.test.ts @@ -5,7 +5,7 @@ import { join } from 'node:path'; import { afterEach, describe, expect, it } from 'vitest'; -import { KimiError, KimiHarness, type Event } from '#/index'; +import { createLocalKimiHarness, KimiError, type Event } from '#/index'; import { SessionStore } from '../../agent-core/src/session/store'; import { TEST_IDENTITY } from './test-identity'; @@ -125,7 +125,7 @@ describe('KimiHarness.renameSession', () => { it('persists titles through the public Harness API and emits an active session event', async () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -172,7 +172,7 @@ describe('KimiHarness.renameSession', () => { it('renames persisted sessions even when they are not active in memory', async () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -206,7 +206,7 @@ describe('KimiHarness.renameSession', () => { it('rejects missing session ids', async () => { const homeDir = await makeTempDir(); - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir, }); diff --git a/packages/node-sdk/test/session-background-tasks.test.ts b/packages/node-sdk/test/session-background-tasks.test.ts index 735df1a6bc..8deb815527 100644 --- a/packages/node-sdk/test/session-background-tasks.test.ts +++ b/packages/node-sdk/test/session-background-tasks.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it } from 'vitest'; -import { KimiHarness, type KimiError } from '#/index'; +import { createLocalKimiHarness, type KimiError } from '#/index'; import { makeTempDir, removeTempDirs } from './session-runtime-helpers'; import { TEST_IDENTITY } from './test-identity'; @@ -15,7 +15,7 @@ describe('Session.listBackgroundTasks / getBackgroundTaskOutput / getBackgroundT it('lists an empty task set for a fresh session', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-bgtask-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-bgtask-work-'); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_bg_list_empty', workDir }); @@ -32,7 +32,7 @@ describe('Session.listBackgroundTasks / getBackgroundTaskOutput / getBackgroundT it('returns empty output and undefined path for an unknown task id', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-bgtask-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-bgtask-work-'); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_bg_unknown', workDir }); @@ -47,7 +47,7 @@ describe('Session.listBackgroundTasks / getBackgroundTaskOutput / getBackgroundT it('rejects empty task ids with a stable error code', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-bgtask-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-bgtask-work-'); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_bg_empty_id', workDir }); @@ -71,7 +71,7 @@ describe('Session.listBackgroundTasks / getBackgroundTaskOutput / getBackgroundT it('rejects after the session is closed', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-bgtask-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-bgtask-work-'); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_bg_closed', workDir }); @@ -101,7 +101,7 @@ describe('Session.listBackgroundTasks / getBackgroundTaskOutput / getBackgroundT it('stopBackgroundTask is a no-op for an unknown task id', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-bgtask-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-bgtask-work-'); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_bg_stop_unknown', workDir }); diff --git a/packages/node-sdk/test/session-cancel.test.ts b/packages/node-sdk/test/session-cancel.test.ts index 51899247ab..3ab1740e25 100644 --- a/packages/node-sdk/test/session-cancel.test.ts +++ b/packages/node-sdk/test/session-cancel.test.ts @@ -4,7 +4,7 @@ import { join } from 'node:path'; import type * as KosongModule from '@moonshot-ai/kosong'; import { afterEach, describe, expect, it, vi } from 'vitest'; -import type { KimiError, Event } from '#/index'; +import { createLocalKimiHarness, type KimiError, type Event } from '#/index'; import { makeTempDir, removeTempDirs, waitForSDKEvent } from './session-runtime-helpers'; import { TEST_IDENTITY } from './test-identity'; @@ -33,8 +33,6 @@ vi.mock('@moonshot-ai/kosong', async (importOriginal) => { }; }); -const { KimiHarness } = await import('#/index'); - const tempDirs: string[] = []; afterEach(async () => { @@ -46,7 +44,7 @@ describe('Session.cancel', () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-cancel-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-cancel-work-'); await writeFakeModelConfig(homeDir); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_cancel_active_turn', workDir }); @@ -84,7 +82,7 @@ describe('Session.cancel', () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-cancel-compact-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-cancel-compact-work-'); await writeFakeModelConfig(homeDir); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_cancel_compaction', workDir }); @@ -101,7 +99,7 @@ describe('Session.cancel', () => { it('rejects after the session is closed', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-cancel-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-cancel-work-'); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_cancel_closed', workDir }); diff --git a/packages/node-sdk/test/session-plan-compact-usage-resume.test.ts b/packages/node-sdk/test/session-plan-compact-usage-resume.test.ts index 11a8c12548..a0c2965181 100644 --- a/packages/node-sdk/test/session-plan-compact-usage-resume.test.ts +++ b/packages/node-sdk/test/session-plan-compact-usage-resume.test.ts @@ -3,7 +3,7 @@ import { dirname, join } from 'node:path'; import { afterEach, describe, expect, it } from 'vitest'; -import { KimiHarness, type Event, type KimiError } from '#/index'; +import { createLocalKimiHarness, type Event, type KimiError } from '#/index'; import { makeTempDir, removeTempDirs } from './session-runtime-helpers'; import { TEST_IDENTITY } from './test-identity'; @@ -19,7 +19,7 @@ describe('Session plan, compact, usage, and resume APIs', () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-plan-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-plan-work-'); await writeTestConfig(homeDir); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_plan_runtime', workDir }); @@ -58,7 +58,7 @@ describe('Session plan, compact, usage, and resume APIs', () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-plan-toggle-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-plan-toggle-work-'); await writeTestConfig(homeDir); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_plan_toggle_runtime', workDir }); @@ -86,7 +86,7 @@ describe('Session plan, compact, usage, and resume APIs', () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-compact-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-compact-work-'); await writeTestConfig(homeDir); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_compact_runtime', workDir }); @@ -104,7 +104,7 @@ describe('Session plan, compact, usage, and resume APIs', () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-usage-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-usage-work-'); await writeTestConfig(homeDir); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_usage_runtime', workDir }); @@ -119,7 +119,7 @@ describe('Session plan, compact, usage, and resume APIs', () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-resume-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-resume-work-'); await writeTestConfig(homeDir); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const created = await harness.createSession({ @@ -156,7 +156,7 @@ describe('Session plan, compact, usage, and resume APIs', () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-resume-legacy-plan-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-resume-legacy-plan-work-'); await writeTestConfig(homeDir); - const createdHarness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const createdHarness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); let sessionId = ''; let sessionDir = ''; @@ -177,7 +177,7 @@ describe('Session plan, compact, usage, and resume APIs', () => { await removeManualPlanIds(sessionDir); - const resumedHarness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const resumedHarness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const resumed = await resumedHarness.resumeSession({ id: sessionId }); @@ -194,7 +194,7 @@ describe('Session plan, compact, usage, and resume APIs', () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-fork-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-fork-work-'); await writeTestConfig(homeDir); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const source = await harness.createSession({ @@ -264,7 +264,7 @@ describe('Session plan, compact, usage, and resume APIs', () => { it('rejects an empty resume id', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-resume-empty-home-'); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { await expect(harness.resumeSession({ id: ' ' })).rejects.toMatchObject({ diff --git a/packages/node-sdk/test/session-prompt-events.test.ts b/packages/node-sdk/test/session-prompt-events.test.ts index 98c39aaf29..69eb5a6fa3 100644 --- a/packages/node-sdk/test/session-prompt-events.test.ts +++ b/packages/node-sdk/test/session-prompt-events.test.ts @@ -8,7 +8,7 @@ import { KIMI_CODE_PLATFORM } from '@moonshot-ai/kimi-code-oauth'; import type * as KosongModule from '@moonshot-ai/kosong'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import type { Event } from '#/index'; +import { createLocalKimiHarness, type Event, type KimiHarness } from '#/index'; import { TEST_IDENTITY } from './test-identity'; @@ -56,8 +56,6 @@ vi.mock('@moonshot-ai/kosong', async (importOriginal) => { }; }); -const { KimiHarness } = await import('#/index'); - const tempDirs: string[] = []; beforeEach(() => { @@ -99,7 +97,7 @@ describe('Session.prompt events', () => { it('persists sanitized prompt metadata without marking the title custom', async () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -175,7 +173,7 @@ describe('Session.prompt events', () => { it('emits mapped turn events through Session.onEvent', async () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -228,7 +226,7 @@ describe('Session.prompt events', () => { it('supports onEvent unsubscribe without touching runtime wire directly', async () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -255,7 +253,7 @@ describe('Session.prompt events', () => { it('runs init through generateAgentsMd RPC as a system trigger without prompt metadata updates', async () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -314,7 +312,7 @@ describe('Session.prompt events', () => { it('rejects empty prompt input', async () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); - const harness = new KimiHarness({ + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -331,7 +329,7 @@ describe('Session.prompt events', () => { }); }); -async function configureFakeProvider(harness: InstanceType): Promise { +async function configureFakeProvider(harness: KimiHarness): Promise { await harness.setConfig({ providers: { local: { diff --git a/packages/node-sdk/test/session-set-model.test.ts b/packages/node-sdk/test/session-set-model.test.ts index b3bcdcf837..bf31a2aea0 100644 --- a/packages/node-sdk/test/session-set-model.test.ts +++ b/packages/node-sdk/test/session-set-model.test.ts @@ -3,7 +3,7 @@ import { join } from 'node:path'; import { FileTokenStorage, type TokenInfo } from '@moonshot-ai/kimi-code-oauth'; import { afterEach, describe, expect, it } from 'vitest'; -import { KimiHarness, type KimiError } from '#/index'; +import { createLocalKimiHarness, type KimiError, type KimiHarness } from '#/index'; import { makeTempDir, removeTempDirs, waitForAgentWireEvent } from './session-runtime-helpers'; import { TEST_IDENTITY } from './test-identity'; @@ -28,7 +28,7 @@ describe('Session.setModel', () => { it('updates the runtime model and sends config.update with the resolved model', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-model-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-model-work-'); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { await configureLocalProvider(harness); @@ -61,7 +61,7 @@ describe('Session.setModel', () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-model-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-model-work-'); await new FileTokenStorage(join(homeDir, 'credentials')).save('kimi-code', freshToken()); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { await harness.setConfig({ @@ -117,7 +117,7 @@ describe('Session.setModel', () => { it('rejects empty model names', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-model-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-model-work-'); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { await configureLocalProvider(harness); @@ -135,7 +135,7 @@ describe('Session.setModel', () => { it('rejects after the session is closed', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-model-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-model-work-'); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { await configureLocalProvider(harness); diff --git a/packages/node-sdk/test/session-set-permission.test.ts b/packages/node-sdk/test/session-set-permission.test.ts index bf597364c6..df92959a14 100644 --- a/packages/node-sdk/test/session-set-permission.test.ts +++ b/packages/node-sdk/test/session-set-permission.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it } from 'vitest'; -import { KimiHarness, type KimiError, type PermissionMode } from '#/index'; +import { createLocalKimiHarness, type KimiError, type PermissionMode } from '#/index'; import { makeTempDir, removeTempDirs, waitForAgentWireEvent } from './session-runtime-helpers'; import { TEST_IDENTITY } from './test-identity'; @@ -16,7 +16,7 @@ describe('Session.setPermission', () => { async (mode: PermissionMode) => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-permission-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-permission-work-'); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ @@ -46,7 +46,7 @@ describe('Session.setPermission', () => { it('rejects invalid permission modes', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-permission-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-permission-work-'); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_permission_invalid', workDir }); @@ -63,7 +63,7 @@ describe('Session.setPermission', () => { it('rejects after the session is closed', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-permission-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-permission-work-'); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_permission_closed', workDir }); diff --git a/packages/node-sdk/test/session-set-thinking.test.ts b/packages/node-sdk/test/session-set-thinking.test.ts index 70e957a667..b21bb73981 100644 --- a/packages/node-sdk/test/session-set-thinking.test.ts +++ b/packages/node-sdk/test/session-set-thinking.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it } from 'vitest'; -import { KimiHarness, type KimiError } from '#/index'; +import { createLocalKimiHarness, type KimiError } from '#/index'; import { makeTempDir, removeTempDirs, waitForAgentWireEvent } from './session-runtime-helpers'; import { TEST_IDENTITY } from './test-identity'; @@ -15,7 +15,7 @@ describe('Session.setThinking', () => { it('sends config.update with the new thinking level', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-thinking-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-thinking-work-'); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_thinking_wire', workDir }); @@ -41,7 +41,7 @@ describe('Session.setThinking', () => { it('rejects empty thinking levels', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-thinking-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-thinking-work-'); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_thinking_empty', workDir }); @@ -58,7 +58,7 @@ describe('Session.setThinking', () => { it('rejects after the session is closed', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-thinking-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-thinking-work-'); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_thinking_closed', workDir }); diff --git a/packages/node-sdk/test/session-skills.test.ts b/packages/node-sdk/test/session-skills.test.ts index 6f785bb40f..8d17e8467f 100644 --- a/packages/node-sdk/test/session-skills.test.ts +++ b/packages/node-sdk/test/session-skills.test.ts @@ -4,7 +4,13 @@ import { join } from 'node:path'; import type * as KosongModule from '@moonshot-ai/kosong'; import { afterEach, beforeEach, describe, expect, expectTypeOf, it, vi } from 'vitest'; -import type { Event, KimiError, SkillActivatedEvent, SkillSummary } from '#/index'; +import { + createLocalKimiHarness, + type Event, + type KimiError, + type SkillActivatedEvent, + type SkillSummary, +} from '#/index'; import type { SDKRpcClient } from '#/rpc'; import { @@ -52,7 +58,7 @@ vi.mock('@moonshot-ai/kosong', async (importOriginal) => { }; }); -const { KimiHarness, Session } = await import('#/index'); +const { Session } = await import('#/index'); const tempDirs: string[] = []; @@ -79,7 +85,7 @@ describe('Session skills', () => { '', 'Review the requested file.', ]); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_sdk_skill_list', workDir }); @@ -111,7 +117,7 @@ describe('Session skills', () => { '', 'Review the requested file.', ]); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_sdk_skill_activate', workDir }); @@ -200,7 +206,7 @@ describe('Session skills', () => { vi.stubEnv('KIMI_CODE_HOME', homeDir); await writeUserSkill(processHome, 'sdk-real-home-only', 'SDK real home skill'); await writeUserSkill(homeDir, 'sdk-sandbox-only', 'SDK sandbox skill'); - const harness = new KimiHarness({ identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_sdk_skill_env_home', workDir }); diff --git a/packages/node-sdk/test/session-steer.test.ts b/packages/node-sdk/test/session-steer.test.ts index 727dbf18ef..eb19fabc4d 100644 --- a/packages/node-sdk/test/session-steer.test.ts +++ b/packages/node-sdk/test/session-steer.test.ts @@ -1,7 +1,7 @@ import type * as KosongModule from '@moonshot-ai/kosong'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import type { KimiError } from '#/index'; +import { createLocalKimiHarness, type KimiError } from '#/index'; import { makeTempDir, removeTempDirs, waitForAgentWireEvent } from './session-runtime-helpers'; import { TEST_IDENTITY } from './test-identity'; @@ -41,8 +41,6 @@ vi.mock('@moonshot-ai/kosong', async (importOriginal) => { }; }); -const { KimiHarness } = await import('#/index'); - const tempDirs: string[] = []; beforeEach(() => { @@ -57,7 +55,7 @@ describe('Session.steer', () => { it('sends turn.steer to the core session runtime', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-steer-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-steer-work-'); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_steer_wire', workDir }); @@ -80,7 +78,7 @@ describe('Session.steer', () => { it('rejects empty steer input', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-steer-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-steer-work-'); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_steer_empty', workDir }); @@ -97,7 +95,7 @@ describe('Session.steer', () => { it('rejects after the session is closed', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-steer-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-steer-work-'); - const harness = new KimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_steer_closed', workDir }); From e7890230cdb99786499e6a97175a1fca7e7b2952 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Tue, 2 Jun 2026 21:26:15 +0800 Subject: [PATCH 2/5] test: update main CLI harness mock --- apps/kimi-code/test/cli/main.test.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/kimi-code/test/cli/main.test.ts b/apps/kimi-code/test/cli/main.test.ts index 8d674d4422..b5955b6b7b 100644 --- a/apps/kimi-code/test/cli/main.test.ts +++ b/apps/kimi-code/test/cli/main.test.ts @@ -46,6 +46,7 @@ const mocks = vi.hoisted(() => { track: vi.fn(), }, KimiHarness: vi.fn(), + createLocalKimiHarness: vi.fn(), }; }); @@ -74,6 +75,10 @@ vi.mock('@moonshot-ai/kimi-code-sdk', async () => { } return { ...actual, + createLocalKimiHarness: (...args: unknown[]) => { + mocks.createLocalKimiHarness(...args); + return mocks.harness; + }, KimiHarness: MockKimiHarness, log: mocks.log, }; @@ -272,7 +277,7 @@ describe('main entry command handling', () => { expect(exitCode).toBe(0); expect(mocks.createCliTelemetryBootstrap).toHaveBeenCalledTimes(1); - expect(mocks.KimiHarness).toHaveBeenCalledWith(expect.objectContaining({ + expect(mocks.createLocalKimiHarness).toHaveBeenCalledWith(expect.objectContaining({ homeDir: '/tmp/kimi-home', telemetry: { track: mocks.track, From c5592775588899c293d33d0e412a7689b903bdeb Mon Sep 17 00:00:00 2001 From: _Kerman Date: Tue, 2 Jun 2026 21:28:03 +0800 Subject: [PATCH 3/5] fix: preserve SDK user agent suffix --- packages/node-sdk/src/local-rpc.ts | 3 +-- .../test/runtime-provider-identity.test.ts | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/packages/node-sdk/src/local-rpc.ts b/packages/node-sdk/src/local-rpc.ts index 2c9ef21d1e..072df7fdae 100644 --- a/packages/node-sdk/src/local-rpc.ts +++ b/packages/node-sdk/src/local-rpc.ts @@ -93,8 +93,7 @@ export class LocalSDKRpcClient extends SDKRpcClient { if (this.identity === undefined) return undefined; return createKimiDefaultHeaders({ homeDir: this.homeDir, - userAgentProduct: this.identity.userAgentProduct, - version: this.identity.version, + ...this.identity, }); } } diff --git a/packages/node-sdk/test/runtime-provider-identity.test.ts b/packages/node-sdk/test/runtime-provider-identity.test.ts index 0d2ef15687..14542ed8a5 100644 --- a/packages/node-sdk/test/runtime-provider-identity.test.ts +++ b/packages/node-sdk/test/runtime-provider-identity.test.ts @@ -8,6 +8,7 @@ import type { KimiConfig } from '@moonshot-ai/agent-core'; import { createKimiDefaultHeaders, KIMI_CODE_PLATFORM } from '@moonshot-ai/kimi-code-oauth'; import { ProviderManager } from '../../agent-core/src/session/provider-manager'; +import { LocalSDKRpcClient } from '#/index'; import { TEST_IDENTITY } from './test-identity'; const tempDirs: string[] = []; @@ -41,6 +42,29 @@ afterEach(async () => { }); describe('runtime provider identity headers', () => { + it('preserves the host user agent suffix in local RPC headers', async () => { + const homeDir = await makeTempDir(); + const client = new LocalSDKRpcClient({ + homeDir, + identity: { + ...TEST_IDENTITY, + userAgentSuffix: 'web-runtime', + }, + }); + const core = client.core as unknown as { + readonly kimiRequestHeaders?: Record; + }; + + try { + expect(core.kimiRequestHeaders).toMatchObject({ + 'User-Agent': 'kimi-code-cli/0.0.0-test (web-runtime)', + 'X-Msh-Version': '0.0.0-test', + }); + } finally { + await client.close(); + } + }); + it('adds kimi-code-cli User-Agent and complete X-Msh headers to the default Kimi provider', async () => { const homeDir = await makeTempDir(); const kimiRequestHeaders = createKimiDefaultHeaders({ homeDir, ...TEST_IDENTITY }); From fbfed8edd85815ef10a070a34eb1b34f16d6170b Mon Sep 17 00:00:00 2001 From: _Kerman Date: Wed, 3 Jun 2026 12:37:46 +0800 Subject: [PATCH 4/5] rename --- apps/kimi-code/src/cli/run-prompt.ts | 4 +-- apps/kimi-code/src/cli/run-shell.ts | 4 +-- apps/kimi-code/src/cli/sub/export.ts | 4 +-- apps/kimi-code/src/cli/sub/provider.ts | 4 +-- apps/kimi-code/src/main.ts | 4 +-- apps/kimi-code/test/cli/export.test.ts | 2 +- apps/kimi-code/test/cli/main.test.ts | 8 ++--- apps/kimi-code/test/cli/run-prompt.test.ts | 2 +- apps/kimi-code/test/cli/run-shell.test.ts | 2 +- .../test/e2e/local-logging-export.e2e.test.ts | 4 +-- .../test/e2e/real-llm-smoke.e2e.test.ts | 4 +-- .../examples/kimi-harness-auth-smoke.ts | 4 +-- .../examples/kimi-harness-cancel-smoke.ts | 4 +-- .../examples/kimi-harness-config-smoke.ts | 4 +-- .../examples/kimi-harness-export-smoke.ts | 4 +-- .../examples/kimi-harness-list-smoke.ts | 4 +-- .../examples/kimi-harness-log-marker.ts | 4 +-- .../examples/kimi-harness-logging-smoke.ts | 4 +-- .../examples/kimi-harness-prompt-demo.ts | 4 +-- .../examples/kimi-harness-rename-smoke.ts | 4 +-- .../examples/kimi-harness-set-model-smoke.ts | 4 +-- .../kimi-harness-set-permission-smoke.ts | 4 +-- .../kimi-harness-set-thinking-smoke.ts | 4 +-- .../node-sdk/examples/kimi-harness-smoke.ts | 4 +-- .../examples/kimi-harness-steer-smoke.ts | 4 +-- .../examples/t8-race-create-single.ts | 4 +-- packages/node-sdk/examples/t8-race-create.ts | 6 ++-- packages/node-sdk/src/index.ts | 10 +++--- packages/node-sdk/src/kimi-harness.ts | 4 +-- packages/node-sdk/src/rpc.ts | 4 +-- .../src/{local-rpc.ts => sdk-rpc-client.ts} | 12 +++---- packages/node-sdk/src/session.ts | 6 ++-- packages/node-sdk/test/auth-facade.test.ts | 18 +++++------ packages/node-sdk/test/config.test.ts | 10 +++--- .../test/create-session-transport.test.ts | 26 +++++++-------- packages/node-sdk/test/export-session.test.ts | 6 ++-- packages/node-sdk/test/list-sessions.test.ts | 10 +++--- packages/node-sdk/test/local-logging.test.ts | 32 +++++++++---------- packages/node-sdk/test/rename-session.test.ts | 8 ++--- .../test/runtime-provider-identity.test.ts | 6 ++-- .../test/session-approval-handler.test.ts | 6 ++-- .../test/session-background-tasks.test.ts | 12 +++---- packages/node-sdk/test/session-cancel.test.ts | 8 ++--- .../session-plan-compact-usage-resume.test.ts | 20 ++++++------ .../test/session-prompt-events.test.ts | 12 +++---- .../test/session-prompt-input.test.ts | 4 +-- .../test/session-question-handler.test.ts | 6 ++-- .../node-sdk/test/session-set-model.test.ts | 10 +++--- .../test/session-set-permission.test.ts | 8 ++--- .../test/session-set-thinking.test.ts | 8 ++--- packages/node-sdk/test/session-skills.test.ts | 14 ++++---- packages/node-sdk/test/session-steer.test.ts | 8 ++--- 52 files changed, 188 insertions(+), 188 deletions(-) rename packages/node-sdk/src/{local-rpc.ts => sdk-rpc-client.ts} (90%) diff --git a/apps/kimi-code/src/cli/run-prompt.ts b/apps/kimi-code/src/cli/run-prompt.ts index 274ecb49a9..d3b2f0a899 100644 --- a/apps/kimi-code/src/cli/run-prompt.ts +++ b/apps/kimi-code/src/cli/run-prompt.ts @@ -7,7 +7,7 @@ import { } from '@moonshot-ai/kimi-telemetry'; import chalk from 'chalk'; import { - createLocalKimiHarness, + createKimiHarness, log, type Event, type HookResultEvent, @@ -61,7 +61,7 @@ export async function runPrompt( withContext: withTelemetryContext, setContext: setTelemetryContext, }; - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ homeDir: telemetryBootstrap.homeDir, identity: createKimiCodeHostIdentity(version), uiMode: PROMPT_UI_MODE, diff --git a/apps/kimi-code/src/cli/run-shell.ts b/apps/kimi-code/src/cli/run-shell.ts index 179e2c2ae0..4ec4330a57 100644 --- a/apps/kimi-code/src/cli/run-shell.ts +++ b/apps/kimi-code/src/cli/run-shell.ts @@ -10,7 +10,7 @@ import { withTelemetryContext, } from '@moonshot-ai/kimi-telemetry'; import { - createLocalKimiHarness, + createKimiHarness, log, type KimiHarness, type TelemetryClient, @@ -56,7 +56,7 @@ export async function runShell( withContext: withTelemetryContext, setContext: setTelemetryContext, }; - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ homeDir: telemetryBootstrap.homeDir, identity: createKimiCodeHostIdentity(version), telemetry: telemetryClient, diff --git a/apps/kimi-code/src/cli/sub/export.ts b/apps/kimi-code/src/cli/sub/export.ts index 267eed4770..a93c3e2719 100644 --- a/apps/kimi-code/src/cli/sub/export.ts +++ b/apps/kimi-code/src/cli/sub/export.ts @@ -14,7 +14,7 @@ import { withTelemetryContext, } from '@moonshot-ai/kimi-telemetry'; import { - createLocalKimiHarness, + createKimiHarness, type ExportSessionInput, type ExportSessionResult, type KimiHarness, @@ -145,7 +145,7 @@ function createDefaultExportDeps(overrides: Partial = {}): ExportDep }; const getHarness = (): KimiHarness => { const currentTelemetryBootstrap = getTelemetryBootstrap(); - harness ??= createLocalKimiHarness({ + harness ??= createKimiHarness({ homeDir: currentTelemetryBootstrap.homeDir, identity, telemetry: telemetryClient, diff --git a/apps/kimi-code/src/cli/sub/provider.ts b/apps/kimi-code/src/cli/sub/provider.ts index 0a8809a2eb..38cb755219 100644 --- a/apps/kimi-code/src/cli/sub/provider.ts +++ b/apps/kimi-code/src/cli/sub/provider.ts @@ -23,7 +23,7 @@ import { catalogBaseUrl, catalogProviderModels, CatalogFetchError, - createLocalKimiHarness, + createKimiHarness, DEFAULT_CATALOG_URL, fetchCatalog, inferWireType, @@ -488,7 +488,7 @@ function resolveDeps(overrides: Partial = {}): ProviderDeps { getHarness: overrides.getHarness ?? (() => { - harness ??= createLocalKimiHarness({ identity }); + harness ??= createKimiHarness({ identity }); return harness; }), stdout: overrides.stdout ?? process.stdout, diff --git a/apps/kimi-code/src/main.ts b/apps/kimi-code/src/main.ts index 1d519f37c2..e1251be2fd 100644 --- a/apps/kimi-code/src/main.ts +++ b/apps/kimi-code/src/main.ts @@ -6,7 +6,7 @@ */ import { - createLocalKimiHarness, + createKimiHarness, flushDiagnosticLogs, log, resolveGlobalLogPath, @@ -78,7 +78,7 @@ export async function handleUpgradeCommand(version: string): Promise { withContext: withTelemetryContext, setContext: setTelemetryContext, }; - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ homeDir: telemetryBootstrap.homeDir, identity: createKimiCodeHostIdentity(version), telemetry: telemetryClient, diff --git a/apps/kimi-code/test/cli/export.test.ts b/apps/kimi-code/test/cli/export.test.ts index 8407a5c881..39963536e9 100644 --- a/apps/kimi-code/test/cli/export.test.ts +++ b/apps/kimi-code/test/cli/export.test.ts @@ -52,7 +52,7 @@ vi.mock('@moonshot-ai/kimi-code-sdk', async (importOriginal) => { return { ...actual, resolveKimiHome: mocks.resolveKimiHome, - createLocalKimiHarness: (...args: unknown[]) => { + createKimiHarness: (...args: unknown[]) => { const options = args[0] as { readonly homeDir?: string } | undefined; const homeDir = options?.homeDir ?? '/tmp/kimi-export-home'; if (mocks.harnessCreatesDeviceIdOnConstruction) { diff --git a/apps/kimi-code/test/cli/main.test.ts b/apps/kimi-code/test/cli/main.test.ts index b5955b6b7b..4a5ac62ad3 100644 --- a/apps/kimi-code/test/cli/main.test.ts +++ b/apps/kimi-code/test/cli/main.test.ts @@ -46,7 +46,7 @@ const mocks = vi.hoisted(() => { track: vi.fn(), }, KimiHarness: vi.fn(), - createLocalKimiHarness: vi.fn(), + createKimiHarness: vi.fn(), }; }); @@ -75,8 +75,8 @@ vi.mock('@moonshot-ai/kimi-code-sdk', async () => { } return { ...actual, - createLocalKimiHarness: (...args: unknown[]) => { - mocks.createLocalKimiHarness(...args); + createKimiHarness: (...args: unknown[]) => { + mocks.createKimiHarness(...args); return mocks.harness; }, KimiHarness: MockKimiHarness, @@ -277,7 +277,7 @@ describe('main entry command handling', () => { expect(exitCode).toBe(0); expect(mocks.createCliTelemetryBootstrap).toHaveBeenCalledTimes(1); - expect(mocks.createLocalKimiHarness).toHaveBeenCalledWith(expect.objectContaining({ + expect(mocks.createKimiHarness).toHaveBeenCalledWith(expect.objectContaining({ homeDir: '/tmp/kimi-home', telemetry: { track: mocks.track, diff --git a/apps/kimi-code/test/cli/run-prompt.test.ts b/apps/kimi-code/test/cli/run-prompt.test.ts index 766380b0fb..3b7f33898c 100644 --- a/apps/kimi-code/test/cli/run-prompt.test.ts +++ b/apps/kimi-code/test/cli/run-prompt.test.ts @@ -78,7 +78,7 @@ vi.mock('@moonshot-ai/kimi-code-sdk', async (importOriginal) => { return { ...actual, resolveKimiHome: mocks.resolveKimiHome, - createLocalKimiHarness: (...args: unknown[]) => { + createKimiHarness: (...args: unknown[]) => { const options = args[0] as { readonly homeDir?: string } | undefined; const homeDir = options?.homeDir ?? '/tmp/kimi-code-test-home'; if (mocks.harnessCreatesDeviceIdOnConstruction) { diff --git a/apps/kimi-code/test/cli/run-shell.test.ts b/apps/kimi-code/test/cli/run-shell.test.ts index fbd198e580..c55a5e5904 100644 --- a/apps/kimi-code/test/cli/run-shell.test.ts +++ b/apps/kimi-code/test/cli/run-shell.test.ts @@ -68,7 +68,7 @@ vi.mock('@moonshot-ai/kimi-code-sdk', async (importOriginal) => { return { ...actual, resolveKimiHome: mocks.resolveKimiHome, - createLocalKimiHarness: (...args: unknown[]) => { + createKimiHarness: (...args: unknown[]) => { const options = args[0] as { readonly homeDir?: string } | undefined; const homeDir = options?.homeDir ?? '/tmp/kimi-code-test-home'; if (mocks.harnessCreatesDeviceIdOnConstruction) { diff --git a/apps/kimi-code/test/e2e/local-logging-export.e2e.test.ts b/apps/kimi-code/test/e2e/local-logging-export.e2e.test.ts index dc402a982e..1a7020ea86 100644 --- a/apps/kimi-code/test/e2e/local-logging-export.e2e.test.ts +++ b/apps/kimi-code/test/e2e/local-logging-export.e2e.test.ts @@ -8,7 +8,7 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { registerExportCommand } from '#/cli/sub/export'; import { createKimiCodeHostIdentity } from '#/cli/version'; -import { createLocalKimiHarness, log } from '@moonshot-ai/kimi-code-sdk'; +import { createKimiHarness, log } from '@moonshot-ai/kimi-code-sdk'; import { __resetRootLoggerForTest } from '../../../../packages/agent-core/src/logging/logger'; const SESSION_LOG = 'logs/kimi-code.log'; @@ -41,7 +41,7 @@ afterEach(async () => { describe.skipIf(!ENABLED)('local logging export e2e', () => { it('exports session log and global log by default, and allows skipping global log', async () => { - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ homeDir, identity: createKimiCodeHostIdentity('0.1.1'), }); diff --git a/apps/kimi-code/test/e2e/real-llm-smoke.e2e.test.ts b/apps/kimi-code/test/e2e/real-llm-smoke.e2e.test.ts index 91d5684767..b31faefd08 100644 --- a/apps/kimi-code/test/e2e/real-llm-smoke.e2e.test.ts +++ b/apps/kimi-code/test/e2e/real-llm-smoke.e2e.test.ts @@ -14,7 +14,7 @@ import { mkdirSync } from 'node:fs'; import process from 'node:process'; -import { createLocalKimiHarness, type Event } from '@moonshot-ai/kimi-code-sdk'; +import { createKimiHarness, type Event } from '@moonshot-ai/kimi-code-sdk'; import { describe, expect, test } from 'vitest'; import { createKimiCodeHostIdentity, getVersion } from '#/cli/version'; @@ -41,7 +41,7 @@ describe.skipIf(!ENABLED)('SDK e2e — real LLM smoke', () => { `[smoke] prompt=${JSON.stringify(prompt)}\n`, ); - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: createKimiCodeHostIdentity(version), }); diff --git a/packages/node-sdk/examples/kimi-harness-auth-smoke.ts b/packages/node-sdk/examples/kimi-harness-auth-smoke.ts index 2dfd9d39fc..90ce644a8d 100644 --- a/packages/node-sdk/examples/kimi-harness-auth-smoke.ts +++ b/packages/node-sdk/examples/kimi-harness-auth-smoke.ts @@ -2,7 +2,7 @@ import { mkdtemp, rm } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; -import { createLocalKimiHarness, type KimiHarness } from '@moonshot-ai/kimi-code-sdk'; +import { createKimiHarness, type KimiHarness } from '@moonshot-ai/kimi-code-sdk'; import { smokeIdentityFromEnv, runPromptToEnd } from './runtime-smoke-helpers'; @@ -17,7 +17,7 @@ async function main(): Promise { const forceLogin = process.env['KIMI_SDK_AUTH_SMOKE_FORCE_LOGIN'] === '1'; const prompt = process.env['KIMI_SDK_AUTH_SMOKE_PROMPT'] ?? 'Reply with exactly: Kimi SDK auth smoke ok'; - const harness = createLocalKimiHarness({ homeDir, identity: smokeIdentityFromEnv() }); + const harness = createKimiHarness({ homeDir, identity: smokeIdentityFromEnv() }); process.stdout.write(`home: ${homeDir}\n`); process.stdout.write(`workDir: ${workDir}\n`); diff --git a/packages/node-sdk/examples/kimi-harness-cancel-smoke.ts b/packages/node-sdk/examples/kimi-harness-cancel-smoke.ts index 398bd4b7d4..5f6c87aea0 100644 --- a/packages/node-sdk/examples/kimi-harness-cancel-smoke.ts +++ b/packages/node-sdk/examples/kimi-harness-cancel-smoke.ts @@ -1,4 +1,4 @@ -import { createLocalKimiHarness } from '@moonshot-ai/kimi-code-sdk'; +import { createKimiHarness } from '@moonshot-ai/kimi-code-sdk'; import { smokeIdentityFromEnv, @@ -10,7 +10,7 @@ const PROMPT = 'Write a detailed multi-paragraph explanation of how cancellation should work in an SDK streaming session.'; async function main(): Promise { - const harness = createLocalKimiHarness({ identity: smokeIdentityFromEnv() }); + const harness = createKimiHarness({ identity: smokeIdentityFromEnv() }); try { const session = await createConfiguredSession(harness); diff --git a/packages/node-sdk/examples/kimi-harness-config-smoke.ts b/packages/node-sdk/examples/kimi-harness-config-smoke.ts index 5b81251bb4..0294b8ff9b 100644 --- a/packages/node-sdk/examples/kimi-harness-config-smoke.ts +++ b/packages/node-sdk/examples/kimi-harness-config-smoke.ts @@ -2,13 +2,13 @@ import { mkdtemp, readFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; -import { createLocalKimiHarness } from '@moonshot-ai/kimi-code-sdk'; +import { createKimiHarness } from '@moonshot-ai/kimi-code-sdk'; import { smokeIdentityFromEnv } from './runtime-smoke-helpers'; async function main(): Promise { const homeDir = await mkdtemp(join(tmpdir(), 'kimi-harness-config-home-')); - const harness = createLocalKimiHarness({ homeDir, identity: smokeIdentityFromEnv() }); + const harness = createKimiHarness({ homeDir, identity: smokeIdentityFromEnv() }); const initial = await harness.getConfig(); if (Object.keys(initial.providers).length > 0) { diff --git a/packages/node-sdk/examples/kimi-harness-export-smoke.ts b/packages/node-sdk/examples/kimi-harness-export-smoke.ts index 7b1b3ffac3..86807cbc63 100644 --- a/packages/node-sdk/examples/kimi-harness-export-smoke.ts +++ b/packages/node-sdk/examples/kimi-harness-export-smoke.ts @@ -2,14 +2,14 @@ import { mkdir, mkdtemp, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; -import { createLocalKimiHarness } from '@moonshot-ai/kimi-code-sdk'; +import { createKimiHarness } from '@moonshot-ai/kimi-code-sdk'; import { smokeIdentityFromEnv } from './runtime-smoke-helpers'; async function main(): Promise { const homeDir = await mkdtemp(join(tmpdir(), 'kimi-harness-export-home-')); const workDir = await mkdtemp(join(tmpdir(), 'kimi-harness-export-work-')); - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: smokeIdentityFromEnv(), homeDir, }); diff --git a/packages/node-sdk/examples/kimi-harness-list-smoke.ts b/packages/node-sdk/examples/kimi-harness-list-smoke.ts index f14728b5f6..c51ec64442 100644 --- a/packages/node-sdk/examples/kimi-harness-list-smoke.ts +++ b/packages/node-sdk/examples/kimi-harness-list-smoke.ts @@ -3,7 +3,7 @@ import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { setTimeout as delay } from 'node:timers/promises'; -import { createLocalKimiHarness } from '@moonshot-ai/kimi-code-sdk'; +import { createKimiHarness } from '@moonshot-ai/kimi-code-sdk'; import { smokeIdentityFromEnv } from './runtime-smoke-helpers'; @@ -11,7 +11,7 @@ async function main(): Promise { const homeDir = await mkdtemp(join(tmpdir(), 'kimi-harness-list-home-')); const workDirA = await mkdtemp(join(tmpdir(), 'kimi-harness-list-work-a-')); const workDirB = await mkdtemp(join(tmpdir(), 'kimi-harness-list-work-b-')); - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: smokeIdentityFromEnv(), homeDir, }); diff --git a/packages/node-sdk/examples/kimi-harness-log-marker.ts b/packages/node-sdk/examples/kimi-harness-log-marker.ts index 89321631a7..8062f5d528 100644 --- a/packages/node-sdk/examples/kimi-harness-log-marker.ts +++ b/packages/node-sdk/examples/kimi-harness-log-marker.ts @@ -3,7 +3,7 @@ import { join } from 'node:path'; import { parseArgs } from 'node:util'; import { - createLocalKimiHarness, + createKimiHarness, flushDiagnosticLogs, log, resolveGlobalLogPath, @@ -41,7 +41,7 @@ Options: async function main(): Promise { const options = parseCliArgs(); const resolvedHome = resolveKimiHome(options.homeDir); - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: { userAgentProduct: 'kimi-code-cli', version: 'log-marker' }, homeDir: options.homeDir, }); diff --git a/packages/node-sdk/examples/kimi-harness-logging-smoke.ts b/packages/node-sdk/examples/kimi-harness-logging-smoke.ts index 6688a36a01..090cb50c4c 100644 --- a/packages/node-sdk/examples/kimi-harness-logging-smoke.ts +++ b/packages/node-sdk/examples/kimi-harness-logging-smoke.ts @@ -2,7 +2,7 @@ import { mkdir, readFile, readdir, rm, stat, writeFile } from 'node:fs/promises' import { homedir } from 'node:os'; import { join } from 'node:path'; -import { createLocalKimiHarness, log } from '@moonshot-ai/kimi-code-sdk'; +import { createKimiHarness, log } from '@moonshot-ai/kimi-code-sdk'; const SESSION_LOG = 'logs/kimi-code.log'; const GLOBAL_LOG = 'logs/global/kimi-code.log'; @@ -72,7 +72,7 @@ async function main(): Promise { const globalOnly = `SMOKE_GLOBAL_ONLY_${runId}`; const longEntry = `SMOKE_LONG_TRUNCATED_${runId}`; const finalEntry = `SMOKE_FINAL_AFTER_ROTATION_SHOULD_APPEAR_${runId}`; - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: { userAgentProduct: 'kimi-code-cli', version: '0.1.1' }, homeDir: TEST_HOME, }); diff --git a/packages/node-sdk/examples/kimi-harness-prompt-demo.ts b/packages/node-sdk/examples/kimi-harness-prompt-demo.ts index 1f96f495fa..eebbdccfbf 100644 --- a/packages/node-sdk/examples/kimi-harness-prompt-demo.ts +++ b/packages/node-sdk/examples/kimi-harness-prompt-demo.ts @@ -1,4 +1,4 @@ -import { createLocalKimiHarness, type Event, type Session } from '@moonshot-ai/kimi-code-sdk'; +import { createKimiHarness, type Event, type Session } from '@moonshot-ai/kimi-code-sdk'; import { smokeIdentityFromEnv } from './runtime-smoke-helpers'; @@ -8,7 +8,7 @@ const PROMPT = async function main(): Promise { const workDir = process.cwd(); - const harness = createLocalKimiHarness({ identity: smokeIdentityFromEnv() }); + const harness = createKimiHarness({ identity: smokeIdentityFromEnv() }); try { const config = await harness.getConfig(); diff --git a/packages/node-sdk/examples/kimi-harness-rename-smoke.ts b/packages/node-sdk/examples/kimi-harness-rename-smoke.ts index 4a95398cb8..9e77080096 100644 --- a/packages/node-sdk/examples/kimi-harness-rename-smoke.ts +++ b/packages/node-sdk/examples/kimi-harness-rename-smoke.ts @@ -2,7 +2,7 @@ import { mkdtemp, readFile, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; -import { createLocalKimiHarness } from '@moonshot-ai/kimi-code-sdk'; +import { createKimiHarness } from '@moonshot-ai/kimi-code-sdk'; import type { Event } from '@moonshot-ai/kimi-code-sdk'; import { smokeIdentityFromEnv } from './runtime-smoke-helpers'; @@ -10,7 +10,7 @@ import { smokeIdentityFromEnv } from './runtime-smoke-helpers'; async function main(): Promise { const homeDir = await mkdtemp(join(tmpdir(), 'kimi-harness-rename-home-')); const workDir = await mkdtemp(join(tmpdir(), 'kimi-harness-rename-work-')); - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: smokeIdentityFromEnv(), homeDir, }); diff --git a/packages/node-sdk/examples/kimi-harness-set-model-smoke.ts b/packages/node-sdk/examples/kimi-harness-set-model-smoke.ts index 22a7b0dd08..40c9b09111 100644 --- a/packages/node-sdk/examples/kimi-harness-set-model-smoke.ts +++ b/packages/node-sdk/examples/kimi-harness-set-model-smoke.ts @@ -1,4 +1,4 @@ -import { createLocalKimiHarness } from '@moonshot-ai/kimi-code-sdk'; +import { createKimiHarness } from '@moonshot-ai/kimi-code-sdk'; import { smokeIdentityFromEnv, @@ -7,7 +7,7 @@ import { } from './runtime-smoke-helpers'; async function main(): Promise { - const harness = createLocalKimiHarness({ identity: smokeIdentityFromEnv() }); + const harness = createKimiHarness({ identity: smokeIdentityFromEnv() }); try { const config = await harness.getConfig(); diff --git a/packages/node-sdk/examples/kimi-harness-set-permission-smoke.ts b/packages/node-sdk/examples/kimi-harness-set-permission-smoke.ts index 7275d1bd2d..4b161dbc2f 100644 --- a/packages/node-sdk/examples/kimi-harness-set-permission-smoke.ts +++ b/packages/node-sdk/examples/kimi-harness-set-permission-smoke.ts @@ -1,4 +1,4 @@ -import { createLocalKimiHarness, type PermissionMode } from '@moonshot-ai/kimi-code-sdk'; +import { createKimiHarness, type PermissionMode } from '@moonshot-ai/kimi-code-sdk'; import { smokeIdentityFromEnv, @@ -7,7 +7,7 @@ import { } from './runtime-smoke-helpers'; async function main(): Promise { - const harness = createLocalKimiHarness({ identity: smokeIdentityFromEnv() }); + const harness = createKimiHarness({ identity: smokeIdentityFromEnv() }); const mode: PermissionMode = 'yolo'; try { diff --git a/packages/node-sdk/examples/kimi-harness-set-thinking-smoke.ts b/packages/node-sdk/examples/kimi-harness-set-thinking-smoke.ts index 056829c7ee..a5d49ba5e0 100644 --- a/packages/node-sdk/examples/kimi-harness-set-thinking-smoke.ts +++ b/packages/node-sdk/examples/kimi-harness-set-thinking-smoke.ts @@ -1,4 +1,4 @@ -import { createLocalKimiHarness } from '@moonshot-ai/kimi-code-sdk'; +import { createKimiHarness } from '@moonshot-ai/kimi-code-sdk'; import { smokeIdentityFromEnv, @@ -7,7 +7,7 @@ import { } from './runtime-smoke-helpers'; async function main(): Promise { - const harness = createLocalKimiHarness({ identity: smokeIdentityFromEnv() }); + const harness = createKimiHarness({ identity: smokeIdentityFromEnv() }); try { const session = await createConfiguredSession(harness); diff --git a/packages/node-sdk/examples/kimi-harness-smoke.ts b/packages/node-sdk/examples/kimi-harness-smoke.ts index fd9bf85fb1..eb1375384b 100644 --- a/packages/node-sdk/examples/kimi-harness-smoke.ts +++ b/packages/node-sdk/examples/kimi-harness-smoke.ts @@ -2,7 +2,7 @@ import { mkdir, mkdtemp, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; -import { createLocalKimiHarness } from '@moonshot-ai/kimi-code-sdk'; +import { createKimiHarness } from '@moonshot-ai/kimi-code-sdk'; import { smokeIdentityFromEnv } from './runtime-smoke-helpers'; @@ -10,7 +10,7 @@ async function main(): Promise { const workDir = await mkdtemp(join(tmpdir(), 'kimi-harness-smoke-')); const homeDir = await mkdtemp(join(tmpdir(), 'kimi-harness-home-')); - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: smokeIdentityFromEnv(), homeDir, }); diff --git a/packages/node-sdk/examples/kimi-harness-steer-smoke.ts b/packages/node-sdk/examples/kimi-harness-steer-smoke.ts index 6db6f6aa54..776290320c 100644 --- a/packages/node-sdk/examples/kimi-harness-steer-smoke.ts +++ b/packages/node-sdk/examples/kimi-harness-steer-smoke.ts @@ -1,4 +1,4 @@ -import { createLocalKimiHarness } from '@moonshot-ai/kimi-code-sdk'; +import { createKimiHarness } from '@moonshot-ai/kimi-code-sdk'; import { smokeIdentityFromEnv, @@ -10,7 +10,7 @@ const PROMPT = 'Draft a long checklist for validating a TypeScript SDK runtime.' const STEER = 'Also include cancellation and permission-mode checks.'; async function main(): Promise { - const harness = createLocalKimiHarness({ identity: smokeIdentityFromEnv() }); + const harness = createKimiHarness({ identity: smokeIdentityFromEnv() }); try { const session = await createConfiguredSession(harness); diff --git a/packages/node-sdk/examples/t8-race-create-single.ts b/packages/node-sdk/examples/t8-race-create-single.ts index 05e5381c31..a6974caee1 100644 --- a/packages/node-sdk/examples/t8-race-create-single.ts +++ b/packages/node-sdk/examples/t8-race-create-single.ts @@ -1,5 +1,5 @@ // One creator: meant to run twice as separate processes simultaneously. -import { createLocalKimiHarness } from '@moonshot-ai/kimi-code-sdk'; +import { createKimiHarness } from '@moonshot-ai/kimi-code-sdk'; const workDir = process.argv[2]!; const homeDir = process.argv[3]!; @@ -7,7 +7,7 @@ const sessionId = process.argv[4]!; const label = process.argv[5] ?? 'P'; const identity: any = { userAgentProduct: 'kimi-code-cli', version: '0.0.1-test' }; -const h = createLocalKimiHarness({ identity, homeDir }); +const h = createKimiHarness({ identity, homeDir }); try { const s = await h.createSession({ workDir, id: sessionId, model: 'kimi-code/kimi-for-coding' }); diff --git a/packages/node-sdk/examples/t8-race-create.ts b/packages/node-sdk/examples/t8-race-create.ts index 50bba44026..0c6a74d2f4 100644 --- a/packages/node-sdk/examples/t8-race-create.ts +++ b/packages/node-sdk/examples/t8-race-create.ts @@ -1,13 +1,13 @@ // T8.4 driver: create session with explicit id, twice concurrently in same process. -import { createLocalKimiHarness, type KimiHarness } from '@moonshot-ai/kimi-code-sdk'; +import { createKimiHarness, type KimiHarness } from '@moonshot-ai/kimi-code-sdk'; const workDir = process.argv[2]!; const homeDir = process.argv[3]!; const sessionId = process.argv[4]!; const identity: any = { userAgentProduct: 'kimi-code-cli', version: '0.0.1-test' }; -const harnessA = createLocalKimiHarness({ identity, homeDir }); -const harnessB = createLocalKimiHarness({ identity, homeDir }); +const harnessA = createKimiHarness({ identity, homeDir }); +const harnessB = createKimiHarness({ identity, homeDir }); async function run(label: string, h: KimiHarness): Promise { try { diff --git a/packages/node-sdk/src/index.ts b/packages/node-sdk/src/index.ts index 8e3fa52e5e..4117d285ff 100644 --- a/packages/node-sdk/src/index.ts +++ b/packages/node-sdk/src/index.ts @@ -3,11 +3,11 @@ export type { KimiHarnessRuntimeOptions } from '#/kimi-harness'; export { Session } from '#/session'; export { KimiAuthFacade } from '#/auth'; export { - createLocalKimiHarness, - LocalSDKRpcClient, - type LocalSDKRpcClientOptions, -} from '#/local-rpc'; -export { SDKRpcClient } from '#/rpc'; + createKimiHarness, + SDKRpcClient, + type SDKRpcClientOptions, +} from '#/sdk-rpc-client'; +export { SDKRpcClientBase } from '#/rpc'; export { KimiForCodingProvider } from '#/kimi-code-model-provider'; export type { KimiForCodingProviderOptions } from '#/kimi-code-model-provider'; diff --git a/packages/node-sdk/src/kimi-harness.ts b/packages/node-sdk/src/kimi-harness.ts index bececda403..499ce75a01 100644 --- a/packages/node-sdk/src/kimi-harness.ts +++ b/packages/node-sdk/src/kimi-harness.ts @@ -7,7 +7,7 @@ import { import { Session } from '#/session'; import type { KimiAuthFacade } from '#/auth'; -import type { SDKRpcClient } from '#/rpc'; +import type { SDKRpcClientBase } from '#/rpc'; import type { CreateSessionOptions, ExportSessionInput, @@ -50,7 +50,7 @@ export class KimiHarness { private readonly closeImpl: () => void | Promise; constructor( - private readonly rpc: SDKRpcClient, + private readonly rpc: SDKRpcClientBase, options: KimiHarnessRuntimeOptions, ) { this.identity = options.identity; diff --git a/packages/node-sdk/src/rpc.ts b/packages/node-sdk/src/rpc.ts index 3b8dbd1835..8f6a6309c6 100644 --- a/packages/node-sdk/src/rpc.ts +++ b/packages/node-sdk/src/rpc.ts @@ -88,7 +88,7 @@ export interface ReconnectMcpServerRpcInput extends SessionIdRpcInput { type ResolvedCoreAPI = RPCMethods; -export abstract class SDKRpcClient { +export abstract class SDKRpcClientBase { interactiveAgentId = MAIN_AGENT_ID; private readonly eventListeners = new Set<(event: Event) => void>(); private readonly approvalHandlers = new Map(); @@ -533,7 +533,7 @@ export abstract class SDKRpcClient { } export class ClientAPI implements SDKAPI { - constructor(readonly client: SDKRpcClient) {} + constructor(readonly client: SDKRpcClientBase) {} emitEvent(event: Event): void { this.client.receiveEvent(event); diff --git a/packages/node-sdk/src/local-rpc.ts b/packages/node-sdk/src/sdk-rpc-client.ts similarity index 90% rename from packages/node-sdk/src/local-rpc.ts rename to packages/node-sdk/src/sdk-rpc-client.ts index 072df7fdae..010b29ecad 100644 --- a/packages/node-sdk/src/local-rpc.ts +++ b/packages/node-sdk/src/sdk-rpc-client.ts @@ -17,10 +17,10 @@ import { assertKimiHostIdentity, createKimiDefaultHeaders } from '@moonshot-ai/k import { KimiAuthFacade } from '#/auth'; import { KimiHarness } from '#/kimi-harness'; -import { ClientAPI, SDKRpcClient } from '#/rpc'; +import { ClientAPI, SDKRpcClientBase } from '#/rpc'; import type { KimiHarnessOptions, KimiHostIdentity, OAuthRefreshOutcome } from '#/types'; -export interface LocalSDKRpcClientOptions { +export interface SDKRpcClientOptions { readonly homeDir?: string; readonly configPath?: string; readonly identity?: KimiHostIdentity; @@ -30,7 +30,7 @@ export interface LocalSDKRpcClientOptions { readonly onOAuthRefresh?: (outcome: OAuthRefreshOutcome) => void; } -export class LocalSDKRpcClient extends SDKRpcClient { +export class SDKRpcClient extends SDKRpcClientBase { readonly homeDir: string; readonly configPath: string; readonly identity: KimiHostIdentity | undefined; @@ -40,7 +40,7 @@ export class LocalSDKRpcClient extends SDKRpcClient { private readonly ready: Promise>; - constructor(options: LocalSDKRpcClientOptions = {}) { + constructor(options: SDKRpcClientOptions = {}) { super(); this.identity = options.identity === undefined ? undefined : assertKimiHostIdentity(options.identity); @@ -98,8 +98,8 @@ export class LocalSDKRpcClient extends SDKRpcClient { } } -export function createLocalKimiHarness(options: KimiHarnessOptions): KimiHarness { - const rpc = new LocalSDKRpcClient(options); +export function createKimiHarness(options: KimiHarnessOptions): KimiHarness { + const rpc = new SDKRpcClient(options); return new KimiHarness(rpc, { identity: rpc.identity, uiMode: options.uiMode, diff --git a/packages/node-sdk/src/session.ts b/packages/node-sdk/src/session.ts index b22bb2d73f..2e8bf8478f 100644 --- a/packages/node-sdk/src/session.ts +++ b/packages/node-sdk/src/session.ts @@ -6,7 +6,7 @@ import { } from '@moonshot-ai/agent-core'; import { type ApprovalHandler, type Event, type QuestionHandler } from '#/events'; -import type { SDKRpcClient } from '#/rpc'; +import type { SDKRpcClientBase } from '#/rpc'; import type { BackgroundTaskInfo, CompactOptions, @@ -33,7 +33,7 @@ export interface SessionOptions { readonly workDir: string; readonly summary?: SessionSummary | undefined; readonly resumeState?: ResumedSessionState | undefined; - readonly rpc: SDKRpcClient; + readonly rpc: SDKRpcClientBase; readonly onClose?: (() => void | Promise) | undefined; } @@ -43,7 +43,7 @@ export class Session { readonly summary?: SessionSummary | undefined; private readonly resumeState: ResumedSessionState | undefined; - private readonly rpc: SDKRpcClient; + private readonly rpc: SDKRpcClientBase; private readonly onClose?: (() => void | Promise) | undefined; private closed = false; diff --git a/packages/node-sdk/test/auth-facade.test.ts b/packages/node-sdk/test/auth-facade.test.ts index e90397392a..77cd36c904 100644 --- a/packages/node-sdk/test/auth-facade.test.ts +++ b/packages/node-sdk/test/auth-facade.test.ts @@ -6,7 +6,7 @@ import { join } from 'node:path'; import { FileTokenStorage, KIMI_CODE_PROVIDER_NAME, type TokenInfo } from '@moonshot-ai/kimi-code-oauth'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { createLocalKimiHarness } from '#/index'; +import { createKimiHarness } from '#/index'; import { ProviderManager } from '../../agent-core/src/session/provider-manager'; import { TEST_IDENTITY } from './test-identity'; @@ -41,12 +41,12 @@ afterEach(() => { describe('KimiHarness.auth', () => { it('can construct auth facade without host identity', () => { - expect(() => createLocalKimiHarness({ homeDir })).not.toThrow(); + expect(() => createKimiHarness({ homeDir })).not.toThrow(); }); it('exposes a cached access token without refreshing auth state', async () => { await new FileTokenStorage(join(homeDir, 'credentials')).save('kimi-code', freshToken()); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); await expect(harness.auth.getCachedAccessToken()).resolves.toBe('oauth-access-token'); }); @@ -73,7 +73,7 @@ describe('KimiHarness.auth', () => { ); vi.stubGlobal('fetch', fetchMock); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); const result = await harness.auth.login(); const config = await harness.getConfig({ reload: true }); @@ -149,7 +149,7 @@ model = "kimi-for-coding" ), ); - expect(() => createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY })).toThrow( + expect(() => createKimiHarness({ homeDir, identity: TEST_IDENTITY })).toThrow( /Model "kimi-code\/kimi-for-coding" must define a positive max_context_size/, ); }); @@ -192,7 +192,7 @@ oauth = { storage = "file", key = "oauth/kimi-code" } `, ); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); await expect(harness.auth.logout()).resolves.toMatchObject({ providerName: KIMI_CODE_PROVIDER_NAME, @@ -230,7 +230,7 @@ oauth = { storage = "file", key = "oauth/kimi-code" } ); vi.stubGlobal('fetch', fetchMock); - const harness = createLocalKimiHarness({ homeDir }); + const harness = createKimiHarness({ homeDir }); const result = await harness.auth.getManagedUsage(); expect(result).toMatchObject({ @@ -250,7 +250,7 @@ oauth = { storage = "file", key = "oauth/kimi-code" } const fetchMock = vi.fn(async () => new Response('', { status: 200 })); vi.stubGlobal('fetch', fetchMock); - const harness = createLocalKimiHarness({ homeDir }); + const harness = createKimiHarness({ homeDir }); const result = await harness.auth.submitFeedback({ content: 'great tool', sessionId: 'sess-42', @@ -292,7 +292,7 @@ oauth = { storage = "file", key = "oauth/kimi-code" } ), ); - const harness = createLocalKimiHarness({ homeDir }); + const harness = createKimiHarness({ homeDir }); const result = await harness.auth.submitFeedback({ content: 'x', sessionId: 's', diff --git a/packages/node-sdk/test/config.test.ts b/packages/node-sdk/test/config.test.ts index 36c89a1b30..3ec7986592 100644 --- a/packages/node-sdk/test/config.test.ts +++ b/packages/node-sdk/test/config.test.ts @@ -4,7 +4,7 @@ import { join } from 'node:path'; import { afterEach, describe, expect, it } from 'vitest'; -import { createLocalKimiHarness, KimiError } from '#/index'; +import { createKimiHarness, KimiError } from '#/index'; import { parseConfigString, @@ -223,7 +223,7 @@ describe('KimiHarness config API', () => { const configPath = join(homeDir, 'config.toml'); await writeFile(configPath, COMPLETE_TOML, 'utf-8'); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); await harness.setConfig({ providers: { @@ -260,7 +260,7 @@ describe('KimiHarness config API', () => { await writeFile(configPath, COMPLETE_TOML, 'utf-8'); const before = await readFile(configPath, 'utf-8'); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); const setInvalidConfig = harness.setConfig({ providers: { @@ -280,7 +280,7 @@ describe('KimiHarness config API', () => { it('uses default config when the config file is absent', async () => { const homeDir = await makeTempDir(); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); await expect(harness.getConfig()).resolves.toEqual({ providers: {} }); }); @@ -288,7 +288,7 @@ describe('KimiHarness config API', () => { it('can create the default config scaffold without selecting a model', async () => { const homeDir = await makeTempDir(); const configPath = join(homeDir, 'config.toml'); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); await harness.ensureConfigFile(); diff --git a/packages/node-sdk/test/create-session-transport.test.ts b/packages/node-sdk/test/create-session-transport.test.ts index 8be29c2431..eb35de8e8b 100644 --- a/packages/node-sdk/test/create-session-transport.test.ts +++ b/packages/node-sdk/test/create-session-transport.test.ts @@ -3,7 +3,7 @@ import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; -import { createLocalKimiHarness } from '#/index'; +import { createKimiHarness } from '#/index'; import type { KimiError, KimiHarness } from '#/index'; import { afterEach, describe, expect, it } from 'vitest'; @@ -48,7 +48,7 @@ describe('KimiHarness.createSession transport link', () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); const records: TelemetryRecord[] = []; - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir, telemetry: recordingTelemetry(records), @@ -106,7 +106,7 @@ describe('KimiHarness.createSession transport link', () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); const records: TelemetryRecord[] = []; - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir, telemetry: recordingTelemetry(records), @@ -138,7 +138,7 @@ describe('KimiHarness.createSession transport link', () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); const records: TelemetryRecord[] = []; - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir, telemetry: recordingTelemetry(records), @@ -180,7 +180,7 @@ describe('KimiHarness.createSession transport link', () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); const records: TelemetryRecord[] = []; - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ homeDir, telemetry: recordingTelemetry(records), }); @@ -210,7 +210,7 @@ describe('KimiHarness.createSession transport link', () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); await writeTestModelConfig(homeDir); - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -280,7 +280,7 @@ effort = "medium" `, 'utf-8', ); - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -309,7 +309,7 @@ effort = "medium" it('does not require provider config or API keys before prompt is implemented', async () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -326,7 +326,7 @@ effort = "medium" it('requires a non-empty workDir on createSession', async () => { const homeDir = await makeTempDir(); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { await expect( @@ -352,7 +352,7 @@ effort = "medium" // Project-local mcp.json is intentionally ignored, so plant the malformed // file under the user home dir where the loader actually reads from. await writeFile(join(homeDir, 'mcp.json'), '{not json}', 'utf-8'); - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -375,7 +375,7 @@ effort = "medium" const homeDir = await makeTempDir(); const workDir = await makeTempDir(); await writeTestModelConfig(homeDir); - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -408,7 +408,7 @@ effort = "medium" it('applies initial thinking and permission runtime options', async () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -452,7 +452,7 @@ effort = "medium" const homeDir = await makeTempDir(); const workDir = await makeTempDir(); await writeFile(join(homeDir, 'config.toml'), 'default_permission_mode = "auto"\n', 'utf-8'); - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir, }); diff --git a/packages/node-sdk/test/export-session.test.ts b/packages/node-sdk/test/export-session.test.ts index 40f09e0152..e1459ad06f 100644 --- a/packages/node-sdk/test/export-session.test.ts +++ b/packages/node-sdk/test/export-session.test.ts @@ -7,7 +7,7 @@ import * as zlib from 'node:zlib'; import { afterEach, describe, expect, it } from 'vitest'; import { - createLocalKimiHarness, + createKimiHarness, KimiError, type SessionSummary, } from '#/index'; @@ -266,7 +266,7 @@ describe('KimiHarness.exportSession', () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); const records: TelemetryRecord[] = []; - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir, telemetry: recordingTelemetry(records), @@ -301,7 +301,7 @@ describe('KimiHarness.exportSession', () => { it('rejects missing session ids', async () => { const homeDir = await makeTempDir(); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); const missingExport = harness.exportSession({ id: 'ses_missing', version: '1.0.0-test' }); await expect(missingExport).rejects.toBeInstanceOf(KimiError); diff --git a/packages/node-sdk/test/list-sessions.test.ts b/packages/node-sdk/test/list-sessions.test.ts index 4a71713cdf..ed958d1fd8 100644 --- a/packages/node-sdk/test/list-sessions.test.ts +++ b/packages/node-sdk/test/list-sessions.test.ts @@ -5,7 +5,7 @@ import { basename, dirname, join } from 'node:path'; import { afterEach, describe, expect, it } from 'vitest'; -import { createLocalKimiHarness } from '#/index'; +import { createKimiHarness } from '#/index'; import type { KimiError } from '#/index'; import { @@ -273,7 +273,7 @@ describe('SessionStore.list', () => { describe('KimiHarness.listSessions', () => { it('rejects whitespace-only workDir with request.work_dir_required', async () => { const homeDir = await makeTempDir(); - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -292,7 +292,7 @@ describe('KimiHarness.listSessions', () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); const otherWorkDir = await makeTempDir(); - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -314,7 +314,7 @@ describe('KimiHarness.listSessions', () => { it('resolves relative workDir inputs before filtering', async () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -335,7 +335,7 @@ describe('KimiHarness.listSessions', () => { it('lists persisted sessions after the active Session has been closed', async () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir, }); diff --git a/packages/node-sdk/test/local-logging.test.ts b/packages/node-sdk/test/local-logging.test.ts index 535d5c5fc8..09b82fa315 100644 --- a/packages/node-sdk/test/local-logging.test.ts +++ b/packages/node-sdk/test/local-logging.test.ts @@ -5,7 +5,7 @@ import * as zlib from 'node:zlib'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { createLocalKimiHarness, log } from '#/index'; +import { createKimiHarness, log } from '#/index'; import { __resetRootLoggerForTest, getRootLogger } from '../../agent-core/src/logging/logger'; import { TEST_IDENTITY } from './test-identity'; @@ -102,7 +102,7 @@ describe('Local logging — harness integration', () => { const homeDir = await makeTempDir('kimi-log-home-'); const workDir = await makeTempDir('kimi-log-work-'); - const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir }); + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir }); const session = await harness.createSession({ id: 'ses_logging_int', workDir, @@ -140,7 +140,7 @@ describe('Local logging — harness integration', () => { it('default export bundles session log only; no globalLogPath in manifest', async () => { const homeDir = await makeTempDir('kimi-log-home-'); const workDir = await makeTempDir('kimi-log-work-'); - const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir }); + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir }); const session = await harness.createSession({ id: 'ses_default_export', workDir }); log.warn('session export marker', { sessionId: session.id }); @@ -168,7 +168,7 @@ describe('Local logging — harness integration', () => { it('default export works when no session log file exists', async () => { const homeDir = await makeTempDir('kimi-log-home-'); const workDir = await makeTempDir('kimi-log-work-'); - const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir }); + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir }); const session = await harness.createSession({ id: 'ses_no_session_log', workDir }); const outputPath = join(workDir, 'no-log.zip'); @@ -193,7 +193,7 @@ describe('Local logging — harness integration', () => { try { const homeDir = await makeTempDir('kimi-log-home-'); const workDir = await makeTempDir('kimi-log-work-'); - const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir }); + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir }); const session = await harness.createSession({ id: 'ses_rotated_export', workDir }); for (let i = 0; i < 16; i++) { log.warn(`rotated session marker ${i}`, { @@ -228,7 +228,7 @@ describe('Local logging — harness integration', () => { it('--include-global-log bundles global active and sets manifest field', async () => { const homeDir = await makeTempDir('kimi-log-home-'); const workDir = await makeTempDir('kimi-log-work-'); - const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir }); + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir }); const session = await harness.createSession({ id: 'ses_global_export', workDir }); log.warn('untagged probe'); @@ -254,9 +254,9 @@ describe('Local logging — harness integration', () => { const firstHome = await makeTempDir('kimi-log-home-a-'); const secondHome = await makeTempDir('kimi-log-home-b-'); const workDir = await makeTempDir('kimi-log-work-'); - const first = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir: firstHome }); + const first = createKimiHarness({ identity: TEST_IDENTITY, homeDir: firstHome }); const firstSession = await first.createSession({ id: 'ses_first_global_export', workDir }); - const second = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir: secondHome }); + const second = createKimiHarness({ identity: TEST_IDENTITY, homeDir: secondHome }); log.warn('active-global-export-marker'); await getRootLogger().flushGlobal(); @@ -281,7 +281,7 @@ describe('Local logging — harness integration', () => { it('logs export flush failures without failing the export', async () => { const homeDir = await makeTempDir('kimi-log-home-'); const workDir = await makeTempDir('kimi-log-work-'); - const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir }); + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir }); const session = await harness.createSession({ id: 'ses_flush_warning', workDir }); log.warn('flush warning setup', { sessionId: session.id }); log.warn('global untagged marker'); @@ -316,16 +316,16 @@ describe('Local logging — harness integration', () => { it('multiple KimiHarness constructions in the same process do not throw', async () => { const homeDir = await makeTempDir('kimi-log-home-'); - expect(() => createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir })).not.toThrow(); - expect(() => createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir })).not.toThrow(); - expect(() => createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir })).not.toThrow(); + expect(() => createKimiHarness({ identity: TEST_IDENTITY, homeDir })).not.toThrow(); + expect(() => createKimiHarness({ identity: TEST_IDENTITY, homeDir })).not.toThrow(); + expect(() => createKimiHarness({ identity: TEST_IDENTITY, homeDir })).not.toThrow(); }); it('uses the latest harness homeDir for global diagnostic logging', async () => { const firstHome = await makeTempDir('kimi-log-home-a-'); const secondHome = await makeTempDir('kimi-log-home-b-'); - const first = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir: firstHome }); - const second = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir: secondHome }); + const first = createKimiHarness({ identity: TEST_IDENTITY, homeDir: firstHome }); + const second = createKimiHarness({ identity: TEST_IDENTITY, homeDir: secondHome }); log.warn('second-home-marker'); await getRootLogger().flushGlobal(); @@ -361,7 +361,7 @@ describe('Local logging — harness integration', () => { try { const homeDir = await makeTempDir('kimi-log-home-'); const workDir = await makeTempDir('kimi-log-work-'); - const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir }); + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir }); await harness.createSession({ id: 'ses_off', workDir }); log.error('this should not write'); let logsDir: string[] = []; @@ -378,7 +378,7 @@ describe('Local logging — harness integration', () => { it('KimiHarness.close() flushes the global log', async () => { const homeDir = await makeTempDir('kimi-log-home-'); - const harness = createLocalKimiHarness({ identity: TEST_IDENTITY, homeDir }); + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir }); log.warn('untagged before close'); // No `await flush()` here on purpose — close() must do it. await harness.close(); diff --git a/packages/node-sdk/test/rename-session.test.ts b/packages/node-sdk/test/rename-session.test.ts index bfdf4c7c09..446ff4cbe0 100644 --- a/packages/node-sdk/test/rename-session.test.ts +++ b/packages/node-sdk/test/rename-session.test.ts @@ -5,7 +5,7 @@ import { join } from 'node:path'; import { afterEach, describe, expect, it } from 'vitest'; -import { createLocalKimiHarness, KimiError, type Event } from '#/index'; +import { createKimiHarness, KimiError, type Event } from '#/index'; import { SessionStore } from '../../agent-core/src/session/store'; import { TEST_IDENTITY } from './test-identity'; @@ -125,7 +125,7 @@ describe('KimiHarness.renameSession', () => { it('persists titles through the public Harness API and emits an active session event', async () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -172,7 +172,7 @@ describe('KimiHarness.renameSession', () => { it('renames persisted sessions even when they are not active in memory', async () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -206,7 +206,7 @@ describe('KimiHarness.renameSession', () => { it('rejects missing session ids', async () => { const homeDir = await makeTempDir(); - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir, }); diff --git a/packages/node-sdk/test/runtime-provider-identity.test.ts b/packages/node-sdk/test/runtime-provider-identity.test.ts index 14542ed8a5..e1156e627c 100644 --- a/packages/node-sdk/test/runtime-provider-identity.test.ts +++ b/packages/node-sdk/test/runtime-provider-identity.test.ts @@ -8,7 +8,7 @@ import type { KimiConfig } from '@moonshot-ai/agent-core'; import { createKimiDefaultHeaders, KIMI_CODE_PLATFORM } from '@moonshot-ai/kimi-code-oauth'; import { ProviderManager } from '../../agent-core/src/session/provider-manager'; -import { LocalSDKRpcClient } from '#/index'; +import { SDKRpcClient } from '#/index'; import { TEST_IDENTITY } from './test-identity'; const tempDirs: string[] = []; @@ -42,9 +42,9 @@ afterEach(async () => { }); describe('runtime provider identity headers', () => { - it('preserves the host user agent suffix in local RPC headers', async () => { + it('preserves the host user agent suffix in SDK RPC headers', async () => { const homeDir = await makeTempDir(); - const client = new LocalSDKRpcClient({ + const client = new SDKRpcClient({ homeDir, identity: { ...TEST_IDENTITY, diff --git a/packages/node-sdk/test/session-approval-handler.test.ts b/packages/node-sdk/test/session-approval-handler.test.ts index 72fd0f34d2..e3d63b08d8 100644 --- a/packages/node-sdk/test/session-approval-handler.test.ts +++ b/packages/node-sdk/test/session-approval-handler.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it, vi } from 'vitest'; import type { ApprovalHandler, ApprovalRequest, ApprovalResponse } from '#/index'; import { Session } from '#/index'; -import type { SDKRpcClient } from '#/rpc'; +import type { SDKRpcClientBase } from '#/rpc'; describe('Session approval handler', () => { it('registers an approval handler and returns approved responses', async () => { @@ -183,8 +183,8 @@ class FakeSDKRpcClient { private readonly approvalHandlers = new Map(); readonly closeSession = vi.fn(async (_input: { readonly sessionId: string }) => {}); - asRpc(): SDKRpcClient { - return this as unknown as SDKRpcClient; + asRpc(): SDKRpcClientBase { + return this as unknown as SDKRpcClientBase; } setApprovalHandler(sessionId: string, handler: ApprovalHandler | undefined): void { diff --git a/packages/node-sdk/test/session-background-tasks.test.ts b/packages/node-sdk/test/session-background-tasks.test.ts index ea516f364b..bc124a6d70 100644 --- a/packages/node-sdk/test/session-background-tasks.test.ts +++ b/packages/node-sdk/test/session-background-tasks.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it } from 'vitest'; -import { createLocalKimiHarness, type KimiError } from '#/index'; +import { createKimiHarness, type KimiError } from '#/index'; import { makeTempDir, removeTempDirs } from './session-runtime-helpers'; import { TEST_IDENTITY } from './test-identity'; @@ -15,7 +15,7 @@ describe('Session.listBackgroundTasks / getBackgroundTaskOutput', () => { it('lists an empty task set for a fresh session', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-bgtask-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-bgtask-work-'); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_bg_list_empty', workDir }); @@ -32,7 +32,7 @@ describe('Session.listBackgroundTasks / getBackgroundTaskOutput', () => { it('returns empty output for an unknown task id', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-bgtask-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-bgtask-work-'); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_bg_unknown', workDir }); @@ -46,7 +46,7 @@ describe('Session.listBackgroundTasks / getBackgroundTaskOutput', () => { it('rejects empty task ids with a stable error code', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-bgtask-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-bgtask-work-'); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_bg_empty_id', workDir }); @@ -66,7 +66,7 @@ describe('Session.listBackgroundTasks / getBackgroundTaskOutput', () => { it('rejects after the session is closed', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-bgtask-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-bgtask-work-'); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_bg_closed', workDir }); @@ -92,7 +92,7 @@ describe('Session.listBackgroundTasks / getBackgroundTaskOutput', () => { it('stopBackgroundTask is a no-op for an unknown task id', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-bgtask-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-bgtask-work-'); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_bg_stop_unknown', workDir }); diff --git a/packages/node-sdk/test/session-cancel.test.ts b/packages/node-sdk/test/session-cancel.test.ts index 3ab1740e25..efb1ce1c81 100644 --- a/packages/node-sdk/test/session-cancel.test.ts +++ b/packages/node-sdk/test/session-cancel.test.ts @@ -4,7 +4,7 @@ import { join } from 'node:path'; import type * as KosongModule from '@moonshot-ai/kosong'; import { afterEach, describe, expect, it, vi } from 'vitest'; -import { createLocalKimiHarness, type KimiError, type Event } from '#/index'; +import { createKimiHarness, type KimiError, type Event } from '#/index'; import { makeTempDir, removeTempDirs, waitForSDKEvent } from './session-runtime-helpers'; import { TEST_IDENTITY } from './test-identity'; @@ -44,7 +44,7 @@ describe('Session.cancel', () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-cancel-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-cancel-work-'); await writeFakeModelConfig(homeDir); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_cancel_active_turn', workDir }); @@ -82,7 +82,7 @@ describe('Session.cancel', () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-cancel-compact-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-cancel-compact-work-'); await writeFakeModelConfig(homeDir); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_cancel_compaction', workDir }); @@ -99,7 +99,7 @@ describe('Session.cancel', () => { it('rejects after the session is closed', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-cancel-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-cancel-work-'); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_cancel_closed', workDir }); diff --git a/packages/node-sdk/test/session-plan-compact-usage-resume.test.ts b/packages/node-sdk/test/session-plan-compact-usage-resume.test.ts index a0c2965181..366bb2df74 100644 --- a/packages/node-sdk/test/session-plan-compact-usage-resume.test.ts +++ b/packages/node-sdk/test/session-plan-compact-usage-resume.test.ts @@ -3,7 +3,7 @@ import { dirname, join } from 'node:path'; import { afterEach, describe, expect, it } from 'vitest'; -import { createLocalKimiHarness, type Event, type KimiError } from '#/index'; +import { createKimiHarness, type Event, type KimiError } from '#/index'; import { makeTempDir, removeTempDirs } from './session-runtime-helpers'; import { TEST_IDENTITY } from './test-identity'; @@ -19,7 +19,7 @@ describe('Session plan, compact, usage, and resume APIs', () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-plan-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-plan-work-'); await writeTestConfig(homeDir); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_plan_runtime', workDir }); @@ -58,7 +58,7 @@ describe('Session plan, compact, usage, and resume APIs', () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-plan-toggle-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-plan-toggle-work-'); await writeTestConfig(homeDir); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_plan_toggle_runtime', workDir }); @@ -86,7 +86,7 @@ describe('Session plan, compact, usage, and resume APIs', () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-compact-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-compact-work-'); await writeTestConfig(homeDir); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_compact_runtime', workDir }); @@ -104,7 +104,7 @@ describe('Session plan, compact, usage, and resume APIs', () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-usage-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-usage-work-'); await writeTestConfig(homeDir); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_usage_runtime', workDir }); @@ -119,7 +119,7 @@ describe('Session plan, compact, usage, and resume APIs', () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-resume-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-resume-work-'); await writeTestConfig(homeDir); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const created = await harness.createSession({ @@ -156,7 +156,7 @@ describe('Session plan, compact, usage, and resume APIs', () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-resume-legacy-plan-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-resume-legacy-plan-work-'); await writeTestConfig(homeDir); - const createdHarness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const createdHarness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); let sessionId = ''; let sessionDir = ''; @@ -177,7 +177,7 @@ describe('Session plan, compact, usage, and resume APIs', () => { await removeManualPlanIds(sessionDir); - const resumedHarness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const resumedHarness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const resumed = await resumedHarness.resumeSession({ id: sessionId }); @@ -194,7 +194,7 @@ describe('Session plan, compact, usage, and resume APIs', () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-fork-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-fork-work-'); await writeTestConfig(homeDir); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const source = await harness.createSession({ @@ -264,7 +264,7 @@ describe('Session plan, compact, usage, and resume APIs', () => { it('rejects an empty resume id', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-resume-empty-home-'); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { await expect(harness.resumeSession({ id: ' ' })).rejects.toMatchObject({ diff --git a/packages/node-sdk/test/session-prompt-events.test.ts b/packages/node-sdk/test/session-prompt-events.test.ts index 69eb5a6fa3..5cf4167bc9 100644 --- a/packages/node-sdk/test/session-prompt-events.test.ts +++ b/packages/node-sdk/test/session-prompt-events.test.ts @@ -8,7 +8,7 @@ import { KIMI_CODE_PLATFORM } from '@moonshot-ai/kimi-code-oauth'; import type * as KosongModule from '@moonshot-ai/kosong'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { createLocalKimiHarness, type Event, type KimiHarness } from '#/index'; +import { createKimiHarness, type Event, type KimiHarness } from '#/index'; import { TEST_IDENTITY } from './test-identity'; @@ -97,7 +97,7 @@ describe('Session.prompt events', () => { it('persists sanitized prompt metadata without marking the title custom', async () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -173,7 +173,7 @@ describe('Session.prompt events', () => { it('emits mapped turn events through Session.onEvent', async () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -226,7 +226,7 @@ describe('Session.prompt events', () => { it('supports onEvent unsubscribe without touching runtime wire directly', async () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -253,7 +253,7 @@ describe('Session.prompt events', () => { it('runs init through generateAgentsMd RPC as a system trigger without prompt metadata updates', async () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir, }); @@ -312,7 +312,7 @@ describe('Session.prompt events', () => { it('rejects empty prompt input', async () => { const homeDir = await makeTempDir(); const workDir = await makeTempDir(); - const harness = createLocalKimiHarness({ + const harness = createKimiHarness({ identity: TEST_IDENTITY, homeDir, }); diff --git a/packages/node-sdk/test/session-prompt-input.test.ts b/packages/node-sdk/test/session-prompt-input.test.ts index df32e2b4be..1874910ea2 100644 --- a/packages/node-sdk/test/session-prompt-input.test.ts +++ b/packages/node-sdk/test/session-prompt-input.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from 'vitest'; -import type { SDKRpcClient } from '../src/rpc'; +import type { SDKRpcClientBase } from '../src/rpc'; import { Session } from '../src/session'; describe('Session.prompt input normalization', () => { @@ -9,7 +9,7 @@ describe('Session.prompt input normalization', () => { const session = new Session({ id: 'ses_multimodal_prompt', workDir: '/tmp/work', - rpc: { prompt } as unknown as SDKRpcClient, + rpc: { prompt } as unknown as SDKRpcClientBase, }); const input = [ { type: 'text', text: 'describe these' }, diff --git a/packages/node-sdk/test/session-question-handler.test.ts b/packages/node-sdk/test/session-question-handler.test.ts index 807202b32f..8d0c2855b7 100644 --- a/packages/node-sdk/test/session-question-handler.test.ts +++ b/packages/node-sdk/test/session-question-handler.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import { Session, type QuestionHandler, type QuestionRequest, type QuestionResult } from '#/index'; -import type { SDKRpcClient } from '#/rpc'; +import type { SDKRpcClientBase } from '#/rpc'; describe('Session question handler', () => { it('registers a question handler and returns handler results', async () => { @@ -116,8 +116,8 @@ class FakeSDKRpcClient { private readonly questionHandlers = new Map(); readonly closeSession = vi.fn(async (_input: { readonly sessionId: string }) => {}); - asRpc(): SDKRpcClient { - return this as unknown as SDKRpcClient; + asRpc(): SDKRpcClientBase { + return this as unknown as SDKRpcClientBase; } setQuestionHandler(sessionId: string, handler: QuestionHandler | undefined): void { diff --git a/packages/node-sdk/test/session-set-model.test.ts b/packages/node-sdk/test/session-set-model.test.ts index bf31a2aea0..b0bf7f65af 100644 --- a/packages/node-sdk/test/session-set-model.test.ts +++ b/packages/node-sdk/test/session-set-model.test.ts @@ -3,7 +3,7 @@ import { join } from 'node:path'; import { FileTokenStorage, type TokenInfo } from '@moonshot-ai/kimi-code-oauth'; import { afterEach, describe, expect, it } from 'vitest'; -import { createLocalKimiHarness, type KimiError, type KimiHarness } from '#/index'; +import { createKimiHarness, type KimiError, type KimiHarness } from '#/index'; import { makeTempDir, removeTempDirs, waitForAgentWireEvent } from './session-runtime-helpers'; import { TEST_IDENTITY } from './test-identity'; @@ -28,7 +28,7 @@ describe('Session.setModel', () => { it('updates the runtime model and sends config.update with the resolved model', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-model-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-model-work-'); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { await configureLocalProvider(harness); @@ -61,7 +61,7 @@ describe('Session.setModel', () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-model-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-model-work-'); await new FileTokenStorage(join(homeDir, 'credentials')).save('kimi-code', freshToken()); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { await harness.setConfig({ @@ -117,7 +117,7 @@ describe('Session.setModel', () => { it('rejects empty model names', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-model-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-model-work-'); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { await configureLocalProvider(harness); @@ -135,7 +135,7 @@ describe('Session.setModel', () => { it('rejects after the session is closed', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-model-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-model-work-'); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { await configureLocalProvider(harness); diff --git a/packages/node-sdk/test/session-set-permission.test.ts b/packages/node-sdk/test/session-set-permission.test.ts index df92959a14..285b9a4722 100644 --- a/packages/node-sdk/test/session-set-permission.test.ts +++ b/packages/node-sdk/test/session-set-permission.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it } from 'vitest'; -import { createLocalKimiHarness, type KimiError, type PermissionMode } from '#/index'; +import { createKimiHarness, type KimiError, type PermissionMode } from '#/index'; import { makeTempDir, removeTempDirs, waitForAgentWireEvent } from './session-runtime-helpers'; import { TEST_IDENTITY } from './test-identity'; @@ -16,7 +16,7 @@ describe('Session.setPermission', () => { async (mode: PermissionMode) => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-permission-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-permission-work-'); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ @@ -46,7 +46,7 @@ describe('Session.setPermission', () => { it('rejects invalid permission modes', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-permission-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-permission-work-'); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_permission_invalid', workDir }); @@ -63,7 +63,7 @@ describe('Session.setPermission', () => { it('rejects after the session is closed', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-permission-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-permission-work-'); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_permission_closed', workDir }); diff --git a/packages/node-sdk/test/session-set-thinking.test.ts b/packages/node-sdk/test/session-set-thinking.test.ts index b21bb73981..6664bb021a 100644 --- a/packages/node-sdk/test/session-set-thinking.test.ts +++ b/packages/node-sdk/test/session-set-thinking.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it } from 'vitest'; -import { createLocalKimiHarness, type KimiError } from '#/index'; +import { createKimiHarness, type KimiError } from '#/index'; import { makeTempDir, removeTempDirs, waitForAgentWireEvent } from './session-runtime-helpers'; import { TEST_IDENTITY } from './test-identity'; @@ -15,7 +15,7 @@ describe('Session.setThinking', () => { it('sends config.update with the new thinking level', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-thinking-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-thinking-work-'); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_thinking_wire', workDir }); @@ -41,7 +41,7 @@ describe('Session.setThinking', () => { it('rejects empty thinking levels', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-thinking-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-thinking-work-'); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_thinking_empty', workDir }); @@ -58,7 +58,7 @@ describe('Session.setThinking', () => { it('rejects after the session is closed', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-thinking-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-thinking-work-'); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_thinking_closed', workDir }); diff --git a/packages/node-sdk/test/session-skills.test.ts b/packages/node-sdk/test/session-skills.test.ts index d8de6e4b16..e007d431d4 100644 --- a/packages/node-sdk/test/session-skills.test.ts +++ b/packages/node-sdk/test/session-skills.test.ts @@ -5,13 +5,13 @@ import type * as KosongModule from '@moonshot-ai/kosong'; import { afterEach, beforeEach, describe, expect, expectTypeOf, it, vi } from 'vitest'; import { - createLocalKimiHarness, + createKimiHarness, type Event, type KimiError, type SkillActivatedEvent, type SkillSummary, } from '#/index'; -import type { SDKRpcClient } from '#/rpc'; +import type { SDKRpcClientBase } from '#/rpc'; import { makeTempDir, @@ -85,7 +85,7 @@ describe('Session skills', () => { '', 'Review the requested file.', ]); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_sdk_skill_list', workDir }); @@ -117,7 +117,7 @@ describe('Session skills', () => { '', 'Review the requested file.', ]); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_sdk_skill_activate', workDir }); @@ -206,7 +206,7 @@ describe('Session skills', () => { vi.stubEnv('KIMI_CODE_HOME', homeDir); await writeUserSkill(processHome, 'sdk-real-home-only', 'SDK real home skill'); await writeUserSkill(homeDir, 'sdk-sandbox-only', 'SDK sandbox skill'); - const harness = createLocalKimiHarness({ identity: TEST_IDENTITY }); + const harness = createKimiHarness({ identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_sdk_skill_env_home', workDir }); @@ -232,7 +232,7 @@ describe('Session skills', () => { closeSession, clearSessionHandlers, listSkills, - } as unknown as SDKRpcClient, + } as unknown as SDKRpcClientBase, }); await expect(session.activateSkill(' ')).rejects.toMatchObject({ @@ -269,7 +269,7 @@ describe('Session skills', () => { closeSession, clearSessionHandlers, listSkills, - } as unknown as SDKRpcClient, + } as unknown as SDKRpcClientBase, }); await expect(session.close()).rejects.toThrow('flush failed'); diff --git a/packages/node-sdk/test/session-steer.test.ts b/packages/node-sdk/test/session-steer.test.ts index eb19fabc4d..5cceec0580 100644 --- a/packages/node-sdk/test/session-steer.test.ts +++ b/packages/node-sdk/test/session-steer.test.ts @@ -1,7 +1,7 @@ import type * as KosongModule from '@moonshot-ai/kosong'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { createLocalKimiHarness, type KimiError } from '#/index'; +import { createKimiHarness, type KimiError } from '#/index'; import { makeTempDir, removeTempDirs, waitForAgentWireEvent } from './session-runtime-helpers'; import { TEST_IDENTITY } from './test-identity'; @@ -55,7 +55,7 @@ describe('Session.steer', () => { it('sends turn.steer to the core session runtime', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-steer-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-steer-work-'); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_steer_wire', workDir }); @@ -78,7 +78,7 @@ describe('Session.steer', () => { it('rejects empty steer input', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-steer-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-steer-work-'); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_steer_empty', workDir }); @@ -95,7 +95,7 @@ describe('Session.steer', () => { it('rejects after the session is closed', async () => { const homeDir = await makeTempDir(tempDirs, 'kimi-sdk-steer-home-'); const workDir = await makeTempDir(tempDirs, 'kimi-sdk-steer-work-'); - const harness = createLocalKimiHarness({ homeDir, identity: TEST_IDENTITY }); + const harness = createKimiHarness({ homeDir, identity: TEST_IDENTITY }); try { const session = await harness.createSession({ id: 'ses_steer_closed', workDir }); From 7a066c7a4db93e7363365c67ba4e65f2ba58d063 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Wed, 3 Jun 2026 12:59:43 +0800 Subject: [PATCH 5/5] fix --- apps/kimi-code/test/cli/goal-prompt.test.ts | 25 ++++++++++----------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/apps/kimi-code/test/cli/goal-prompt.test.ts b/apps/kimi-code/test/cli/goal-prompt.test.ts index 3103e4d155..3a5bcf9a4c 100644 --- a/apps/kimi-code/test/cli/goal-prompt.test.ts +++ b/apps/kimi-code/test/cli/goal-prompt.test.ts @@ -119,19 +119,18 @@ vi.mock('@moonshot-ai/kimi-code-sdk', async (importOriginal) => { const actual = await importOriginal(); return { ...actual, - KimiHarness: class { - homeDir = '/tmp/kimi-goal-home'; - auth = { getCachedAccessToken: vi.fn() }; - ensureConfigFile = vi.fn(); - getConfig = vi.fn(async () => ({ providers: {}, defaultModel: 'k2', telemetry: true })); - getExperimentalFlags = vi.fn(async () => mocks.experimentalFlags); - createSession = vi.fn(async () => mocks.session); - resumeSession = vi.fn(async () => mocks.session); - listSessions = vi.fn(async () => mocks.sessions); - close = vi.fn(); - track = vi.fn(); - constructor() {} - }, + createKimiHarness: () => ({ + homeDir: '/tmp/kimi-goal-home', + auth: { getCachedAccessToken: vi.fn() }, + ensureConfigFile: vi.fn(), + getConfig: vi.fn(async () => ({ providers: {}, defaultModel: 'k2', telemetry: true })), + getExperimentalFlags: vi.fn(async () => mocks.experimentalFlags), + createSession: vi.fn(async () => mocks.session), + resumeSession: vi.fn(async () => mocks.session), + listSessions: vi.fn(async () => mocks.sessions), + close: vi.fn(), + track: vi.fn(), + }), }; });