How to Migrate from the Legacy Node-SDK to the New Klient SDK Architecture
Migrate from @moonshot-ai/kimi-code-sdk to @moonshot-ai/klient by replacing direct RPC calls with contract-driven façade methods, switching to transport-agnostic initialization, and adopting the v2 engine bootstrap pattern.
The @moonshot-ai/klient SDK replaces the legacy @moonshot-ai/kimi-code-sdk Node-SDK with a contract-driven façade architecture built on the v2 engine (agent-core-v2). This migration guide walks through the architectural differences, step-by-step code changes, and key source files you need to understand when moving your codebase.
Architectural Differences Between Legacy and Klient SDK
The two SDKs represent fundamentally different design philosophies. Understanding these differences clarifies why the migration changes your import patterns and API calls.
Entry Points and Public API
| Aspect | Legacy Node‑SDK | New Klient SDK |
|---|---|---|
| Entry point | export * from '#/sdk-rpc-client' (exposes SDKRpcClient, createKimiHarness) |
export * from '#/core/facade/*' (exposes klient.global.*, klient.session(id).*, klient.session(id).agent(name).*) |
| Transport | RPC‑client talks to the server over HTTP/WebSocket via a custom wire format. | Two transports (memory for in‑process, ipc for socket) share a single KlientChannel SPI; the transport is chosen once when the client is created. |
| API style | Mostly imperative functions that accept many optional parameters. | Fully facade‑oriented: single‑object parameter calls, all inputs validated by zod contracts. |
| Session lifecycle | new Session(...) + explicit session.start() etc. |
klient.global.sessions.create({ workDir }) → klient.session(id) gives a typed session object. |
| Agent interaction | KimiHarness.prompt(...), KimiHarness.steer(...) |
klient.session(id).agent('main').prompt({ input }), agent.steer({ ... }). |
| Configuration & Auth | config-rpc, auth modules expose raw RPC methods. |
klient.global.config.*, klient.global.auth.* are high‑level, contract‑validated helpers. |
| Event handling | Direct EventEmitter on KimiHarness. |
Centralised klient.events.on(...) with typed event names (assistant.delta, prompt.completed, …). |
| Error handling | KimiError types from agent‑core. |
Same error types re‑exported, but now wrapped in façade methods that automatically convert payloads. |
The Klient design deliberately isolates the consumer from the engine's internal services. All communication happens through a typed contract in packages/klient/src/contract/*. This makes the SDK version-stable, easier to test, and transport-agnostic as implemented in the MoonshotAI/kimi-code repository.
Step-by-Step Migration Guide
Follow these steps to convert your existing Node-SDK code to the Klient architecture.
1. Install the New Package
Replace the legacy package with the new Klient SDK:
pnpm add @moonshot-ai/klient
Remove the old dependency if present:
pnpm remove @moonshot-ai/kimi-code-sdk
2. Replace Imports
Update your import statements to use the new entry points:
// Old: packages/node-sdk/src/index.ts
import { createKimiHarness, SDKRpcClient } from '@moonshot-ai/kimi-code-sdk';
// New: packages/klient/src/index.ts
import { createKlient } from '@moonshot-ai/klient/memory'; // or `/ipc`
The /memory transport is recommended for testing and in-process usage. Use /ipc for socket-based communication.
3. Bootstrap the Engine and Create a Klient Instance
The Klient SDK requires explicit engine initialization using the v2 bootstrap pattern. This replaces the implicit engine startup in the legacy SDK.
import { bootstrap, logSeed, resolveLoggingConfig } from '@moonshot-ai/agent-core-v2';
import { createKlient } from '@moonshot-ai/klient/memory';
const { app } = bootstrap({ homeDir: '~/.kimi-code' }, [
...logSeed(resolveLoggingConfig({ homeDir: '~/.kimi-code', env: process.env })),
]);
const klient = createKlient({ scope: app });
Reference: packages/klient/src/transports/memory/index.ts
4. Create and Access Sessions
Replace direct Session instantiation with the façade-based session management:
// Old: packages/node-sdk/src/session.ts
const session = new Session({ workDir: process.cwd() });
await session.start();
// New: packages/klient/src/core/facade/session.ts
const { id } = await klient.global.sessions.create({ workDir: process.cwd() });
const session = klient.session(id);
The new approach uses klient.global.sessions.create() to spawn sessions and klient.session(id) to obtain a typed session handle.
5. Interact with Agents
Agent interaction moves from the KimiHarness class to the agent façade:
// Old: packages/node-sdk/src/kimi-harness.ts
const harness = await createKimiHarness(session);
await harness.prompt({ input: [{ type: 'text', text: 'Hello' }] });
// New: packages/klient/src/core/facade/agent.ts
const agent = klient.session(id).agent('main');
await agent.prompt({ input: [{ type: 'text', text: 'Hello' }] });
The agent('main') method returns an agent façade with typed methods for prompt, steer, and other operations.
6. Handle Events
Event handling shifts from per-instance emitters to the central event hub:
// Old: packages/node-sdk/src/events.ts
harness.events.on('assistant.delta', e => process.stdout.write(e.delta));
// New: packages/klient/src/core/events/hub.ts
agent.events.on('assistant.delta', e => process.stdout.write(e.delta));
The event names remain consistent, but the subscription mechanism uses the agent's event interface.
7. Update Configuration and Authentication Calls
Replace raw RPC configuration calls with high-level façade methods:
// Old: packages/node-sdk/src/config-rpc.ts
const cfg = await configRpcClient.getConfig();
const auth = await authFacade.login(...);
// New: packages/klient/src/contract/global/config.ts
const cfg = await klient.global.config.get();
const authResult = await klient.global.auth.login(...);
8. Close the Client Properly
Add explicit cleanup to release resources:
await klient.close(); // flushes transports and releases resources
9. Verify with the Smoke Test
Run the bundled verification test to confirm your migration:
pnpm -C packages/klient smoke
Reference: packages/klient/examples/smoke.ts
Complete Code Examples
Minimal "Hello-World" Using the Legacy SDK
import { createKimiHarness } from '@moonshot-ai/kimi-code-sdk';
async function legacyHello() {
const harness = await createKimiHarness();
await harness.prompt({ input: [{ type: 'text', text: 'Hello from legacy SDK!' }] });
}
legacyHello();
Equivalent Functionality with the Klient SDK
import { bootstrap, logSeed, resolveLoggingConfig } from '@moonshot-ai/agent-core-v2';
import { createKlient } from '@moonshot-ai/klient/memory';
async function klientHello() {
const { app } = bootstrap({ homeDir: '~/.kimi-code' }, [
...logSeed(resolveLoggingConfig({ homeDir: '~/.kimi-code', env: process.env })),
]);
const klient = createKlient({ scope: app });
const { id } = await klient.global.sessions.create({ workDir: process.cwd() });
const agent = klient.session(id).agent('main');
await agent.prompt({ input: [{ type: 'text', text: 'Hello from Klient SDK!' }] });
await klient.close();
}
klientHello();
Event-Driven Streaming Comparison
// Legacy approach
harness.events.on('assistant.delta', e => process.stdout.write(e.delta));
await harness.prompt({ input: [{ type: 'text', text: 'Stream me' }] });
// Klient approach
const agent = klient.session(id).agent('main');
agent.events.on('assistant.delta', e => process.stdout.write(e.delta));
await agent.prompt({ input: [{ type: 'text', text: 'Stream me' }] });
Key Source Files for Migration Reference
| Package | Important File | What It Provides |
|---|---|---|
| node‑sdk | packages/node-sdk/src/index.ts |
Public exports of the legacy SDK. |
packages/node-sdk/src/kimi-harness.ts |
High‑level session façade used in old examples. | |
packages/node-sdk/src/sdk-rpc-client.ts |
Low‑level RPC client implementation. | |
| klient | packages/klient/src/index.ts |
Main entry point that re‑exports the façade. |
packages/klient/src/core/facade/session.ts |
Session creation & utility methods (create, list, …). |
|
packages/klient/src/core/facade/agent.ts |
Agent‑level RPC methods (prompt, steer, …). |
|
packages/klient/src/transports/memory/index.ts |
In‑process "memory" transport (default for tests & smoke). | |
packages/klient/src/contract/* |
Zod‑validated contract schemas for every RPC call. | |
packages/klient/examples/smoke.ts |
Minimal end‑to‑end usage demonstration. |
Summary
- Switch imports from
@moonshot-ai/kimi-code-sdkto@moonshot-ai/klient/memoryor/ipc - Bootstrap the v2 engine explicitly using
bootstrap()fromagent-core-v2before creating any Klient instance - Use the
globalfaçade (klient.global.sessions.create(),klient.global.config.get()) for lifecycle and configuration operations - Access agents through the session façade (
klient.session(id).agent('main')) rather than standalone harness creation - Rely on zod-validated contracts for type-safe, version-stable API calls
- Call
klient.close()to properly release resources when finished
Frequently Asked Questions
What happens to my existing SDKRpcClient code?
The SDKRpcClient class from packages/node-sdk/src/sdk-rpc-client.ts is not directly exposed in the Klient SDK. Replace direct RPC client instantiation with createKlient({ scope: app }) and use the façade methods. The underlying RPC mechanism still exists but is encapsulated behind the contract layer.
Can I use both SDKs simultaneously during migration?
Technically possible but not recommended. Both SDKs expect ownership of the engine bootstrap process. Running bootstrap() twice or mixing Session instances with Klient sessions causes resource conflicts. Complete the migration in a single codebase transition instead.
Why does Klient require explicit engine bootstrap?
The legacy SDK hid engine initialization inside createKimiHarness() or new Session(). The Klient architecture separates engine lifecycle from client usage to support multiple transport types and testing scenarios. This explicitness matches the agent-core-v2 design philosophy in the MoonshotAI/kimi-code repository.
How do I choose between memory and ipc transports?
Use memory for in-process integration, unit tests, and single-machine deployments where the engine runs in the same Node.js process. Use ipc for multi-process architectures, containerized deployments, or when the engine runs as a separate service. The transport choice is fixed at createKlient() time.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →