# kap-server 与协议

> REST 与三条 WS 通道、反射式调试 RPC、事件分发的三层过滤，以及新增的全局搜索与 Remote Control。

- 作者：David（道雾轩）
- 专栏：Kimi Code 源码解读（https://daiw.org/manual/kimi-code.md）
- 最后更新：2026-09-19
- 原文：https://daiw.org/manual/kimi-code/kap-server
- 转载与引用：请注明出处并附原文链接（https://daiw.org/about/copyright）

`packages/kap-server`（约 3.8 万行）把 agent 引擎暴露成网络服务，包描述是「Kimi Code server backed by the DI × Scope agent engine (agent-core-v2)」。v0.29.1 时它就已跑在 v2 上，如今 v2 成了唯一引擎。消费者是随 CLI 发布的 Web UI、`apps/kimi-inspect`，以及经 Remote Control 从远端接入的客户端；VS Code 插件依赖的则是 `@moonshot-ai/kimi-code-sdk`。

## 结构

```text
packages/kap-server/src/
├── start.ts            引导入口（startServer）
├── routes/             REST 路由（/api/v1、/api/v2）
├── transport/          WS v1 / v3 / debug，反射 RPC 分发
├── services/           transcript、projection、history 等
├── protocol/           线协议 schema 与 asyncapi.ts
├── search/             全局消息搜索
├── middleware/ security/ openapi/ lib/
└── instanceRegistry.ts 实例注册表
```

依赖 `agent-core-v2`、`transcript`、`minidb`、`remote-control`，HTTP 用 fastify。原先独立的 `packages/protocol` 已不存在：v0.25.0 起 v2 服务端栈就不再用它（v1 wire schema 挪进了引擎各域与本包），现在是 `src/protocol/`——`envelope`、`error-codes`、`pagination`、`ws-control`、各个 `rest-*.ts`，以及 v3 用的 `messages/`。协议仍有机器可读文档：OpenAPI 由 `@fastify/swagger` 生成，另有 `protocol/asyncapi.ts`。

## 表面

```text
/api/v1           REST
/api/v1/ws        WebSocket v1（session_event + transcript 帧）
/api/v2           REST v2（sessions、mcp）
/api/v3/ws        WebSocket v3（message protocol v3）
/api/v1/debug/*   反射式 RPC（需 --debug-endpoints）
/api/v1/debug/ws  调试 WS
```

v3 是新增的消息协议：`V3_PROTOCOL_VERSION = '3'`，能力位 `step_replay_v1`，推送 assistant、thinking、tool_call 及其 delta、interaction、task、todo、session.state 这类「实体消息」，历史走 `GET /api/v1/sessions/{session_id}/history`。kimi-inspect 的会话视图已迁到 v3；随 CLI 发布的 Web UI 产物里能搜到 `subscribe_v2` 这类 v1 + transcript 协议字面量，搜不到 `/api/v3/ws`。`/api/v2/sessions` 则多了 `view: 'flat' | 'by_workspace'` 的分组视图。

## 反射式调试面

根 `AGENTS.md` 的说法仍然成立：`/api/v1/debug/*` 是对**整个 scoped DI 注册表**的反射分发，每个 Service 都能调，没有白名单。`registerDebugRoutes()` 用 `resolveAnyScopedServiceId` 按名字找服务，用 `describeAllChannels` 生成 `GET /api/v1/debug/channels` 的全量描述。挂载条件在 `start.ts` 第 166 行：

```ts
  const debugEndpoints = exposureClass === 'loopback' && opts.debugEndpoints === true;
```

三道锁：**显式开关**（`kimi web --debug-endpoints`）、**只在回环地址生效**（`security/bindClassify.ts` 把监听地址分成 `loopback` / `lan` / `public`）、**全局 bearer 鉴权**。非回环地址还有别的收紧：没有 TLS 就拒绝监听（除非 `--insecure-no-tls`），终端能力只在回环地址开放。

## 事件分发的三层

`/api/v1/ws` 的规则在 `transport/ws/v1/sessionEventBroadcaster.ts`：

**第一层：全局 vs 会话级。** `isGlobalEvent()` 认定的全局事件——`session.meta.updated`，以及 `event.session.*`、`event.workspace.*`、`event.config.*`、`event.model_catalog.*`、`event.plugin.*`、`event.capability.*`、`event.di.*` 几族——广播给每个连接（`addGlobalTarget`）；会话级事件只发给订阅了该会话的连接。

**第二层：agent 过滤。** 会话级事件还受 `agent_filter` 约束。

**第三层：transcript 抑制。** 第 1272 行：

```ts
function suppressedByTranscript(
  envelope: EventEnvelope,
  spec: TranscriptGradeSpec | undefined,
): boolean {
  if (spec === undefined) return false;
  if (isGlobalEvent(envelope.type)) return false;
  if (isAgentLifecycleEvent(envelope.type)) return false;
  const payload = envelope.payload;
  const agentId =
    typeof payload === 'object' && payload !== null
      ? (payload as { agentId?: unknown }).agentId
      : undefined;
  if (typeof agentId !== 'string') return false;
  if (gradeFor(spec, agentId) === 'off') return false;
  return TRANSCRIPT_PROJECTED_EVENT_TYPES.has(envelope.type);
}
```

订阅了 [transcript](https://daiw.org/manual/kimi-code/transcript) 的连接，不再收到已被投影过的旧式事件，否则同一件事收两遍。它只是**每连接的发送视图**：实时发送与游标重放（`getBufferedSince`）都套这层过滤，缓冲里的事件一条不少。

## 会话活动的单一真相

「会话是否在忙」由引擎的 `ISessionActivityView`（`session/sessionActivity/`）拥有，`state()` 返回 `busy`、`mainTurnActive`、`pendingInteraction`（`none` / `approval` / `question`）与 `lastTurnReason`。kap-server 的 `resolveSessionFacts()` 直接读它，REST 会话列表、快照、v2 路由与 v3 WS 共用同一份结果——**不在边缘自己折算**。同一个事实若有两处计算，迟早分叉。

## 有界 journal 与追赶

`TranscriptService.dispatchOps` 给每批 op 分配 per-agent 连续 `seq`，存进内存 journal（`TRANSCRIPT_OPS_JOURNAL_CAPACITY = 2000`，超出丢最旧的一批），随实时 store 一起消亡。`GET /sessions/{session_id}/transcript/ops?since_seq=` 覆盖不了时回 `complete: false`，调用方回退全量刷新。基线帧 `transcript.reset` 不带历史条目（`TRANSCRIPT_RESET_TAIL_TURNS = 0`）——**历史一律走 REST 分页**。

## 新增：全局搜索与 Remote Control

**全局搜索**：`POST /api/v1/search` 背后是 `IGlobalSearchService`，索引存在 [minidb](https://daiw.org/manual/kimi-code/minidb) 里（`search-index` 目录，缺省由 worker 线程维护）。响应里的 `source` 标明结果来自内存中的实时转录还是持久化索引，`index_state` 报告索引覆盖了多少会话。

**Remote Control**：`packages/remote-control` 是隧道客户端，把本机注册到 relay，再把 HTTP / WebSocket 流量转发到本地服务端，并用机器级锁保证单实例。kap-server 暴露 `GET` / `POST /api/v1/remote-control` 开关，CLI 对应 `kimi web --rc`（`--remote-control`）。两边都要求监听回环地址，CLI 还拒绝与 `--dangerous-bypass-auth` 同用。v0.42.0 起它不再需要实验开关。

## 实例注册表

`instanceRegistry.ts` 把运行中的实例登记在 `~/.kimi-code/server/instances`（设了 `KIMI_CODE_HOME` 就在它下面）。kimi-inspect 的 Vite 开发服务器通过 `GET /__inspect/servers` 扫这个目录，实现零配置自动连接与运行时切换。

## 与 CLI 的关系

`kimi web` 在本地调用 kap-server 的 `startServer()`，并托管随 CLI 发布的 `dist-web` 预构建产物——Web UI 的源码已不在本仓库，缺了产物时服务端只提供 API。所以 Web 形态不需要单独部署，CLI 自带。

下一篇：[klient、SDK 与 ACP](https://daiw.org/manual/kimi-code/klient-acp)。
