npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@moonshot-ai/kimi-agent-sdk

v0.0.3

Published

SDK for interacting with Kimi Code CLI

Downloads

281

Readme

@moonshot-ai/kimi-agent-sdk

TypeScript SDK for interacting with Kimi Code CLI via wire protocol.

Installation

npm install @moonshot-ai/kimi-agent-sdk
# or
pnpm add @moonshot-ai/kimi-agent-sdk

Quick Start

import { createSession } from '@moonshot-ai/kimi-agent-sdk';

const session = createSession({
  workDir: '/path/to/project',
  model: 'kimi-latest',
  thinking: true,
});

const turn = session.prompt('Explain this codebase');

for await (const event of turn) {
  if (event.type === 'ContentPart' && event.payload.type === 'text') {
    process.stdout.write(event.payload.text);
  }
}

await session.close();

API Reference

Session Management

createSession(options: SessionOptions): Session

Creates a new session instance.

interface SessionOptions {
  workDir: string;           // Working directory path
  sessionId?: string;        // Optional session ID (auto-generated if omitted)
  model?: string;            // Model identifier
  thinking?: boolean;        // Enable thinking mode
  yoloMode?: boolean;        // Auto-approve all tool calls
  executable?: string;       // Path to CLI executable (default: "kimi")
  env?: Record<string, string>; // Environment variables
}

Session

interface Session {
  readonly sessionId: string;
  readonly workDir: string;
  readonly state: SessionState;  // 'idle' | 'active' | 'closed'
  
  // Configurable properties
  model: string | undefined;
  thinking: boolean;
  yoloMode: boolean;
  executable: string;
  env: Record<string, string>;
  
  // Methods
  prompt(content: string | ContentPart[]): Turn;
  close(): Promise<void>;
  [Symbol.asyncDispose](): Promise<void>;
}

Turn

Represents an ongoing conversation turn.

interface Turn {
  [Symbol.asyncIterator](): AsyncIterator<StreamEvent, RunResult, undefined>;
  interrupt(): Promise<void>;
  approve(requestId: string, response: ApprovalResponse): Promise<void>;
  readonly result: Promise<RunResult>;
}

prompt(content, options): Promise<{ result, events }>

One-shot prompt helper for simple use cases.

import { prompt } from '@moonshot-ai/kimi-agent-sdk';

const { result, events } = await prompt('What does this code do?', {
  workDir: '/path/to/project',
  model: 'kimi-latest',
});

Stream Events

Events emitted during a turn:

| Event Type | Payload | Description | |------------|---------|-------------| | TurnBegin | { user_input } | Turn started | | StepBegin | { n } | New step started | | StepInterrupted | {} | Step was interrupted | | ContentPart | ContentPart | Text or thinking content | | ToolCall | ToolCall | Tool invocation started | | ToolCallPart | { arguments_part } | Streaming tool arguments | | ToolResult | ToolResult | Tool execution result | | SubagentEvent | SubagentEvent | Nested agent event | | StatusUpdate | StatusUpdate | Token usage and context info | | CompactionBegin | {} | Context compaction started | | CompactionEnd | {} | Context compaction finished | | ApprovalRequest | ApprovalRequestPayload | Tool needs approval |


Content Types

ContentPart

type ContentPart =
  | { type: 'text'; text: string }
  | { type: 'think'; think: string; encrypted?: string | null }
  | { type: 'image_url'; image_url: { url: string; id?: string | null } }
  | { type: 'audio_url'; audio_url: { url: string; id?: string | null } }
  | { type: 'video_url'; video_url: { url: string; id?: string | null } };

ToolCall

interface ToolCall {
  type: 'function';
  id: string;
  function: {
    name: string;
    arguments: string | null;
  };
  extras?: Record<string, unknown> | null;
}

ToolResult

interface ToolResult {
  tool_call_id: string;
  return_value: {
    is_error: boolean;
    output: string | ContentPart[];
    message: string;
    display: DisplayBlock[];
    extras?: Record<string, unknown> | null;
  };
}

DisplayBlock

type DisplayBlock =
  | { type: 'brief'; text: string }
  | { type: 'diff'; path: string; old_text: string; new_text: string }
  | { type: 'todo'; items: Array<{ title: string; status: 'pending' | 'in_progress' | 'done' }> }
  | { type: string; data: Record<string, unknown> };  // Unknown block

RunResult

interface RunResult {
  status: 'finished' | 'cancelled' | 'max_steps_reached';
  steps?: number;
}

ApprovalResponse

type ApprovalResponse = 'approve' | 'approve_for_session' | 'reject';

Session Storage

listSessions(workDir: string): Promise<SessionInfo[]>

Lists all sessions for a workspace.

interface SessionInfo {
  id: string;
  workDir: string;
  contextFile: string;
  updatedAt: number;   // Timestamp in milliseconds
  brief: string;       // First user message preview
}

deleteSession(workDir: string, sessionId: string): Promise<boolean>

Deletes a session. Returns true if successful.

parseSessionEvents(workDir: string, sessionId: string): Promise<StreamEvent[]>

Parses and returns all events from a session's history.


Configuration

parseConfig(): KimiConfig

Reads and parses the CLI configuration file.

interface KimiConfig {
  defaultModel: string | null;
  defaultThinking: boolean;
  models: ModelConfig[];
}

interface ModelConfig {
  id: string;
  name: string;
  capabilities: string[];  // 'thinking' | 'always_thinking' | 'image_in' | 'video_in'
}

saveDefaultModel(modelId: string, thinking?: boolean): void

Updates the default model in the configuration file.

getModelById(models: ModelConfig[], modelId: string): ModelConfig | undefined

Finds a model by ID.

getModelThinkingMode(model: ModelConfig): ThinkingMode

Returns the thinking mode for a model.

type ThinkingMode = 'none' | 'switch' | 'always';

isModelThinking(models: ModelConfig[], modelId: string): boolean

Checks if a model supports thinking.


MCP Server Management

authMCP(serverName: string, executable?: string): Promise<void>

Initiates OAuth authentication for an MCP server.

resetAuthMCP(serverName: string, executable?: string): Promise<void>

Resets authentication for an MCP server.

testMCP(serverName: string, executable?: string): Promise<MCPTestResult>

Tests connection to an MCP server.

interface MCPTestResult {
  success: boolean;
  message?: string;
  tools?: string[];
  error?: string;
}

MCPServerConfig

interface MCPServerConfig {
  name: string;
  transport: 'http' | 'stdio';
  url?: string;              // For HTTP transport
  command?: string;          // For stdio transport
  args?: string[];
  env?: Record<string, string>;
  headers?: Record<string, string>;
  auth?: 'oauth';
}

File Paths

KimiPaths

Utility object for Kimi CLI file paths.

const KimiPaths = {
  home: string;                                    // ~/.kimi
  config: string;                                  // ~/.kimi/config.toml
  mcpConfig: string;                               // ~/.kimi/mcp.json
  sessionsDir(workDir: string): string;            // Session storage directory
  sessionDir(workDir: string, sessionId: string): string;
  shadowGitDir(workDir: string, sessionId: string): string;
};

Error Handling

All errors extend AgentSdkError:

abstract class AgentSdkError extends Error {
  abstract readonly code: string;
  abstract readonly category: ErrorCategory;
  readonly cause?: unknown;
  readonly context?: Record<string, unknown>;
}

type ErrorCategory = 'transport' | 'protocol' | 'session' | 'cli';

Error Classes

| Class | Category | Codes | |-------|----------|-------| | TransportError | transport | SPAWN_FAILED, STDIN_NOT_WRITABLE, PROCESS_CRASHED, CLI_NOT_FOUND, ALREADY_STARTED, HANDSHAKE_TIMEOUT | | ProtocolError | protocol | INVALID_JSON, SCHEMA_MISMATCH, UNKNOWN_EVENT_TYPE, UNKNOWN_REQUEST_TYPE, REQUEST_TIMEOUT, REQUEST_CANCELLED | | SessionError | session | SESSION_CLOSED, SESSION_BUSY, TURN_INTERRUPTED, APPROVAL_FAILED | | CliError | cli | INVALID_STATE, LLM_NOT_SET, LLM_NOT_SUPPORTED, CHAT_PROVIDER_ERROR, UNKNOWN |

Error Utilities

// Check if error is from this SDK
isAgentSdkError(err: unknown): err is AgentSdkError

// Get error code (returns 'UNKNOWN' for non-SDK errors)
getErrorCode(err: unknown): string

// Get error category (returns 'unknown' for non-SDK errors)
getErrorCategory(err: unknown): ErrorCategory | 'unknown'

Utility Functions

extractBrief(display?: DisplayBlock[]): string

Extracts brief text from display blocks.

extractTextFromContentParts(parts: ContentPart[]): string

Extracts all text content from content parts.

formatContentOutput(output: string | ContentPart[]): string

Formats content output as a string.


Usage Examples

Handling Tool Approvals

const turn = session.prompt('Delete all .tmp files');

for await (const event of turn) {
  if (event.type === 'ApprovalRequest') {
    const { id, action, description } = event.payload;
    console.log(`Approval needed: ${action} - ${description}`);
    
    // Approve or reject
    await turn.approve(id, 'approve');
  }
}

Streaming with Token Usage

for await (const event of turn) {
  if (event.type === 'StatusUpdate') {
    const { token_usage, context_usage } = event.payload;
    if (token_usage) {
      console.log(`Tokens: ${token_usage.input_other} in, ${token_usage.output} out`);
    }
  }
}

Handling Subagent Events

for await (const event of turn) {
  if (event.type === 'SubagentEvent') {
    const { task_tool_call_id, event: subEvent } = event.payload;
    console.log(`Subagent ${task_tool_call_id}: ${subEvent.type}`);
  }
}

Interrupting a Turn

const turn = session.prompt('Long running task...');

// Interrupt after 10 seconds
setTimeout(() => turn.interrupt(), 10000);

for await (const event of turn) {
  // Handle events until interrupted
}

const result = await turn.result;
console.log(result.status);  // 'cancelled'

Multi-turn Conversation with Image Input

import { createSession, type ContentPart } from '@moonshot-ai/kimi-agent-sdk';

async function analyzeImage() {
  const session = createSession({
    workDir: process.cwd(),
    model: 'kimi-vision',
    thinking: true,
  });

  // First turn: send image with question
  const imageContent: ContentPart[] = [
    { type: 'text', text: 'What is shown in this image?' },
    { type: 'image_url', image_url: { url: 'data:image/png;base64,iVBORw0KGgo...' } },
  ];

  const turn1 = session.prompt(imageContent);
  for await (const event of turn1) {
    if (event.type === 'ContentPart' && event.payload.type === 'text') {
      process.stdout.write(event.payload.text);
    }
  }

  // Second turn: follow-up question (session maintains context)
  const turn2 = session.prompt('Can you identify any potential issues?');
  for await (const event of turn2) {
    if (event.type === 'ContentPart' && event.payload.type === 'text') {
      process.stdout.write(event.payload.text);
    }
  }

  await session.close();
}

Resuming a Previous Session

import { 
  createSession, 
  listSessions, 
  parseSessionEvents,
  type StreamEvent 
} from '@moonshot-ai/kimi-agent-sdk';

async function resumeSession(workDir: string) {
  // List existing sessions
  const sessions = await listSessions(workDir);
  
  if (sessions.length === 0) {
    console.log('No previous sessions found');
    return;
  }

  // Get the most recent session
  const latestSession = sessions[0];
  console.log(`Resuming session: ${latestSession.brief}`);

  // Load session history
  const history = await parseSessionEvents(workDir, latestSession.id);
  
  // Display previous messages
  for (const event of history) {
    if (event.type === 'TurnBegin') {
      const input = event.payload.user_input;
      const text = typeof input === 'string' 
        ? input 
        : input.filter(p => p.type === 'text').map(p => p.text).join('\n');
      console.log(`\nUser: ${text}`);
    }
    if (event.type === 'ContentPart' && event.payload.type === 'text') {
      process.stdout.write(event.payload.text);
    }
  }

  // Create session with existing ID to continue conversation
  const session = createSession({
    workDir,
    sessionId: latestSession.id,
    model: 'kimi-latest',
  });

  // Continue the conversation
  const turn = session.prompt('Please continue from where we left off');
  for await (const event of turn) {
    if (event.type === 'ContentPart' && event.payload.type === 'text') {
      process.stdout.write(event.payload.text);
    }
  }

  await session.close();
}