Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/skills/gen-changesets/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ All other `@moonshot-ai/*` packages are treated as internal packages, including
4. **Internal package source changes that enter the CLI bundle must manually list the CLI.** `@moonshot-ai/kimi-code` inline-bundles `@moonshot-ai/*` source, but those internal packages are devDependencies from the CLI's perspective, so changesets will not automatically propagate bumps. If a change enters the CLI output, list `@moonshot-ai/kimi-code`.
- **Web app (`@moonshot-ai/kimi-web`) changes always enter the CLI bundle.** `@moonshot-ai/kimi-web` is ignored by changesets (see `.changeset/config.json`) and cannot be mixed with `@moonshot-ai/kimi-code` in one changeset frontmatter. Describe the web change in the changelog text, but list `@moonshot-ai/kimi-code` so the CLI release carries the bundled `dist-web` output.
5. **Docs-only and tests-only changes usually do not need a changeset.** README, internal docs, and `test/` changes that do not enter package output do not trigger a CLI bump.
6. `@moonshot-ai/vis` / `vis-server` / `vis-web` are ignored by changesets and should not be handled.
6. `@moonshot-ai/vis` / `vis-server` / `vis-web` are ignored by changesets and should not be handled. `@moonshot-ai/kimi-inspect` (a private dev app that never ships) is likewise ignored and must never appear in a changeset frontmatter.

## Workflow

Expand Down
3 changes: 2 additions & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"ignore": [
"@moonshot-ai/vis",
"@moonshot-ai/vis-server",
"@moonshot-ai/vis-web"
"@moonshot-ai/vis-web",
"@moonshot-ai/kimi-inspect"
],
"snapshot": {
"useCalculatedVersion": true,
Expand Down
5 changes: 5 additions & 0 deletions .changeset/debug-rpc-surface.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---

Mount the dev-only /api/v1/debug RPC surface behind the --debug-endpoints flag, exposing every scoped service for local debugging on loopback binds. Pass --debug-endpoints to kimi server run to enable it.
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ This is a TypeScript monorepo built for agent-assisted development. Keep the roo
- `apps/kimi-code`: the CLI / TUI application. It consumes core capabilities through `@moonshot-ai/kimi-code-sdk` and must not depend directly on `@moonshot-ai/agent-core`. When writing or modifying its terminal UI, use the `write-tui` skill (`.agents/skills/write-tui/SKILL.md`).
- `apps/kimi-web`: the browser web UI, a peer to the TUI. Vue 3 + Vite + vue-i18n; talks to the server over REST + WebSocket under `/api/v1`. It must not depend on `@moonshot-ai/agent-core` (wire types are re-implemented locally). Debug against the two engines via the root `pnpm dev:v1` / `pnpm dev:v2` backend scripts — the dev Sidebar shows the active backend and switches it at runtime. See `apps/kimi-web/AGENTS.md`.
- `apps/vis`, `apps/vis/server`, `apps/vis/web`: visual debugging tools for sessions and replays.
- `apps/kimi-inspect`: web inspector for the v2 (kap-server) `/api/v2` surface — workspace/session browser, per-session chat, and live Service panels (data + trigger buttons) for the Session and Agent scopes. Built on its own old-klient-style channel layer (`src/channel/`: the VS Code `ProxyChannel` model — service-bound `IChannel`, HTTP `ProxyChannel` for calls routed to `/api/v2`, `WsChannel` over the shared `/api/v2/ws` socket for events), typed by `agent-core-v2` Service interfaces; `GET {rpcBasePath}/channels` loads every wire protocol 1:1 — probing `/api/v1/debug` first (dev, whitelist-free) and falling back to `/api/v2`. The Vite dev server proxies `/api` to a running kap-server (`KIMI_SERVER_URL`, default `http://127.0.0.1:58627`) and exposes `GET /__inspect/servers` (`vite/serverDiscovery.ts`), which scans the local kap-server instance registry (`~/.kimi-code/server/instances` + legacy `lock`) and the home token so the app can zero-config auto-connect and switch servers from the header dropdown at runtime.
- `packages/agent-core`: the unified agent engine, including Agent, Session, profile, skills, tools, plan, permission, background, records, the in-process DI service layer (`src/services/`), and other core capabilities.
- `packages/node-sdk`: the public TypeScript SDK and harness.
- `packages/kosong`: the LLM / provider abstraction layer.
- `packages/kaos`: the execution environment and file/process abstractions.
- `packages/oauth`: Kimi OAuth and managed auth utilities.
- `packages/telemetry`: shared client-side telemetry infrastructure.
- `packages/kap-server`: the Kimi Code server, backed by the DI × Scope agent engine (`@moonshot-ai/agent-core-v2`). Exposes sessions over REST + WebSocket (`/api/v1` and the native `/api/v2` RPC surface); bootstrapped from `src/start.ts` and consumed by `apps/kimi-code`.
- `packages/kap-server`: the Kimi Code server, backed by the DI × Scope agent engine (`@moonshot-ai/agent-core-v2`). Exposes sessions over REST + WebSocket (`/api/v1` and the native `/api/v2` RPC surface); bootstrapped from `src/start.ts` and consumed by `apps/kimi-code`. With `--debug-endpoints` on a loopback bind it additionally mounts `/api/v1/debug/*` — the same reflection dispatcher as `/api/v2` but without the channel whitelist (every scoped Service callable, `src/transport/registerDebugRoutes.ts`); internal only, repo dev scripts pass the flag.
- `packages/klient`: the client SDK — a contract-driven facade over agent-core-v2 with aggregated `global.*` / `session(id).*` / `agent(id).*` methods, zod validation on every call, and klient-level typed event forwarding. Transport is chosen once at creation via subpath entry (`@moonshot-ai/klient/http|ipc|memory`); all three return the same `Klient`. The package also hosts the e2e suites: dual-backend session/agent suites (`test/e2e/dual/`, in-memory + in-process server), `/api/v2` wire tests (`test/e2e/v2/`), the legacy `/api/v1` live suites (`test/e2e/legacy/`), and the docker e2e runner (`pnpm --filter @moonshot-ai/klient docker:e2e`). See `packages/klient/AGENTS.md`.

## Environment Requirements
Expand Down
6 changes: 3 additions & 3 deletions apps/kimi-code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@
"test:native:smoke": "node scripts/native/smoke.mjs",
"dev": "node scripts/dev.mjs",
"dev:cli-only": "tsx --import ../../build/register-raw-text-loader.mjs ./src/main.ts",
"dev:server": "tsx --tsconfig ./tsconfig.dev.json --import ../../build/register-raw-text-loader.mjs ./src/main.ts server run --foreground",
"dev:kap-server": "tsx --tsconfig ./tsconfig.dev.json --import ../../build/register-raw-text-loader.mjs ./src/main.ts server run --foreground",
"dev:kap-server:multi": "KIMI_CODE_EXPERIMENTAL_MULTI_SERVER=1 tsx --tsconfig ./tsconfig.dev.json --import ../../build/register-raw-text-loader.mjs ./src/main.ts server run --foreground",
"dev:server": "tsx --tsconfig ./tsconfig.dev.json --import ../../build/register-raw-text-loader.mjs ./src/main.ts server run --foreground --debug-endpoints",
"dev:kap-server": "tsx --tsconfig ./tsconfig.dev.json --import ../../build/register-raw-text-loader.mjs ./src/main.ts server run --foreground --debug-endpoints",
"dev:kap-server:multi": "KIMI_CODE_EXPERIMENTAL_MULTI_SERVER=1 tsx --tsconfig ./tsconfig.dev.json --import ../../build/register-raw-text-loader.mjs ./src/main.ts server run --foreground --debug-endpoints",
"dev:server:restart": "node scripts/dev-server-restart.mjs",
"dev:plugin-marketplace": "node scripts/dev-plugin-marketplace-server.mjs",
"build:plugin-marketplace": "node scripts/build-plugin-marketplace-cdn.mjs",
Expand Down
12 changes: 12 additions & 0 deletions apps/kimi-inspect/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Kimi Inspect</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
40 changes: 40 additions & 0 deletions apps/kimi-inspect/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "@moonshot-ai/kimi-inspect",
"version": "0.0.0",
"private": true,
"license": "MIT",
"type": "module",
"imports": {
"#/*": {
"types": [
"./src/*.ts",
"./src/*.tsx",
"./src/*/index.ts",
"./src/*/index.tsx"
],
"default": "./src/*"
}
},
"scripts": {
"dev": "vite",
"build": "vite build",
"typecheck": "tsc --noEmit",
"test": "vitest run"
},
"dependencies": {
"@moonshot-ai/agent-core-v2": "workspace:^",
"@tanstack/react-query": "^5.74.4",
"react": "^19.1.0",
"react-dom": "^19.1.0"
},
"devDependencies": {
"@tailwindcss/vite": "^4.1.4",
"@types/react": "^19.1.2",
"@types/react-dom": "^19.1.2",
"@vitejs/plugin-react": "^4.4.1",
"tailwindcss": "^4.1.4",
"typescript": "6.0.2",
"vite": "^6.3.3",
"vitest": "4.1.4"
}
}
126 changes: 126 additions & 0 deletions apps/kimi-inspect/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/**
* App shell — selection state, session resume, and the three WS event
* subscriptions that feed the live bus:
* core `events` — process-wide domain events
* session `interactions` — pending approvals / questions
* agent `events` — the active agent's live event stream
* Layout: header / left sidebar (workspaces + sessions) / chat / inspector.
*/

import { useEffect, useRef, useState } from 'react';

import { ISessionLifecycleService } from '@moonshot-ai/agent-core-v2/app/sessionLifecycle/sessionLifecycle';

import { ChatView } from './components/ChatView';
import { Inspector } from './components/Inspector';
import { ServerSwitcher } from './components/ServerSwitcher';
import { Sidebar } from './components/Sidebar';
import { useConnection } from './connection';
import { LiveBusProvider, type Emit, type LiveEvent } from './live';
import { Badge, errorMessage } from './ui';

export function App() {
const { klient, wsState, baseUrl, disconnect } = useConnection();
const [sessionId, setSessionId] = useState<string | null>(null);
const [agentId, setAgentId] = useState('main');
const [ready, setReady] = useState(false);
const [resumeError, setResumeError] = useState<unknown>(null);
const emitRef = useRef<Emit | null>(null);
const publish = (source: LiveEvent['source'], data: unknown) =>
emitRef.current?.({ source, data, at: Date.now() });

// Core events — for the lifetime of the connection.
useEffect(() => {
const sub = klient.ws().listen('events', (data) => publish('core', data));
return () => sub.dispose();
}, [klient]);

// Session interactions — re-subscribe when the session changes.
useEffect(() => {
if (sessionId === null || !ready) return;
const session = klient.ws().session(sessionId);
const a = session.listen('interactions', (data) => publish('session', data));
const b = session.listen('interactions:resolved', (data) => publish('session', data));
return () => {
a.dispose();
b.dispose();
};
}, [klient, sessionId, ready]);

// Agent events — re-subscribe when session or agent changes.
useEffect(() => {
if (sessionId === null || !ready) return;
const sub = klient
.ws()
.session(sessionId)
.agent(agentId)
.listen('events', (data) => publish('agent', data));
return () => sub.dispose();
}, [klient, sessionId, agentId, ready]);

// Resume (materialize) the session on the server when it is selected, so
// session / agent scoped Services become reachable.
useEffect(() => {
if (sessionId === null) return;
let cancelled = false;
setReady(false);
setResumeError(null);
klient
.core(ISessionLifecycleService)
.resume(sessionId)
.then(() => {
if (!cancelled) setReady(true);
})
.catch((error: unknown) => {
if (!cancelled) setResumeError(error);
});
return () => {
cancelled = true;
};
}, [klient, sessionId]);

// Switching servers invalidates every session/agent selection: sessions
// belong to the server they were listed from. The client and its WS
// subscriptions rebuild from the new config on their own.
useEffect(() => {
setSessionId(null);
setAgentId('main');
}, [baseUrl]);

return (
<LiveBusProvider busRef={emitRef}>
<div className="flex h-screen flex-col">
<header className="flex items-center gap-3 border-b border-neutral-800 px-4 py-1.5">
<span className="text-[12px] font-bold tracking-widest text-neutral-200">KIMI INSPECT</span>
<ServerSwitcher />
<Badge tone={wsState === 'open' ? 'green' : wsState === 'connecting' ? 'amber' : 'red'}>
ws: {wsState}
</Badge>
<div className="flex-1" />
<button
className="text-[11px] text-neutral-500 hover:text-neutral-300"
onClick={disconnect}
>
Disconnect
</button>
</header>
<div className="flex min-h-0 flex-1">
<Sidebar activeSessionId={sessionId} onSelectSession={setSessionId} />
{resumeError !== null ? (
<div className="flex flex-1 items-center justify-center p-6 text-center text-[12px] text-red-400">
Failed to open session: {errorMessage(resumeError)}
</div>
) : (
<ChatView sessionId={sessionId} agentId={agentId} ready={ready} />
)}
<Inspector
sessionId={sessionId}
agentId={agentId}
onAgentChange={setAgentId}
ready={ready}
/>
</div>
</div>
</LiveBusProvider>
);
}
Loading
Loading