)
1. kap-server 的定位1.1 在 kimi-code 架构中的位置packages/kap-server是 agent-core-v2 的 HTTP 外围服务层。它不是一个独立的应用——它是引擎的外壳将 DI x Scope 容器内的所有能力暴露为标准化的 REST WebSocket 接口。在 kimi-code 系统的分层结构中它位于以下位置┌───────────────────────────────────────────────────────────────────┐ │ 消费者层 │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌───────────────────┐ │ │ │ kimi-web │ │kimi-inspect│ │ pi-tui │ │ kimi-code CLI │ │ │ │ (Web UI) │ │ (调试面板)│ │ (TUI) │ │ (web/daemon) │ │ │ └─────┬─────┘ └─────┬─────┘ └────┬────┘ └─────────┬─────────┘ │ │ │ │ │ │ │ │ │ HTTP WebSocket │ process.fork() │ │ └──────────────┴──────────────┘ │ │ │ │ │ │ │ ▼ ▼ │ │ ┌─────────────────────────────────┐ ┌──────────────────────────┐ │ │ │ kap-server │ │ kimi-code CLI │ │ │ │ Fastify HTTP Server WS │ │ (Embedding Host via SDK)│ │ │ │ │ │ startServer({hostIdentity,...})│ │ │ ┌──────────────────────────┐ │ └──────────────────────────┘ │ │ │ │ agent-core-v2 (Core) │ │ │ │ │ │ DI × Scope 容器 │ │ │ │ │ └──────────────────────────┘ │ │ │ └─────────────────────────────────┘ │ └───────────────────────────────────────────────────────────────────┘kap-server 是 kimi-webWeb 前端和 kimi-inspect调试面板的后端。CLI 的kimi web命令本质上就是启动一个 kap-server 实例并将 web 静态资源挂载上去。每当用户在浏览器中打开 Web UI、提交 Prompt、查看会话记录、或者调试面板通过 RPC 调用引擎内部服务时请求都先到达 kap-server。1.2 核心职责会话管理创建、列表、更新、归档、fork、compact、undo、abort 会话Prompt 提交接收用户输入、图片附件、文件引用转发给引擎调度事件广播通过 WebSocket 实时推送会话事件每个 token、工具调用、审批请求文件操作上传/下载、工作空间文件系统浏览审批与问题工具审批流、Agent 问题交互配置与模型暴露 provider 和 model 目录、用户配置读写认证与安全Bearer Token 认证、Host/Origin 校验、速率限制2. 技术栈全景2.1 核心依赖技术角色说明FastifyHTTP 框架高性能 Node.js Web 框架内置日志Pino、Schema 验证、插件系统agent-core-v2引擎核心DI x Scope 容器提供 ISessionLifecycleService、IAgentPromptService 等全部引擎服务transcript会话数据层TranscriptStore 分级存储、WireRecord 持久化、实时增量投影fastify/swaggerAPI 文档从 Zod Schema 自动生成 OpenAPI 3.0 文档暴露 /openapi.jsonWebSocket (ws)实时通道基于ws库的 WebSocket 服务器noServer 模式与 Fastify 共享 HTTP ServerulidID 生成连接 ID、Server ID 的唯一标识Zod验证层类型安全的请求/响应 Schema 验证同时驱动 Swagger 文档生成2.2 defineRoute声明式路由定义kap-server 没有使用 Fastify 原生的 AJV 验证。它通过自研的defineRoute中间件实现了一套声明式路由系统一个对象同时声明 Zod Schema运行时验证和 OpenAPI SchemaSwagger 文档。// packages/kap-server/src/routes/prompts.ts const submitRoute defineRoute( { method: POST, path: /sessions/{session_id}/prompts, body: promptSubmissionSchema, // Zod — 运行时验证 params: sessionIdParamSchema, success: { data: promptSubmitResultSchema }, // 成功响应 errors: { 40001: { detailsSchema: z.array(/* ... */) }, // 校验失败 40401: {}, // 会话不存在 }, description: Submit a prompt to a session, tags: [prompts], }, async (req, reply) { // req.body → PromptSubmission (自动推断) // req.params → { session_id: string } // ... }, ); app.post(submitRoute.path, submitRoute.options, submitRoute.handler);这套系统带来的好处类型安全Handler 中的req.body和req.params自动推断为正确的 Zod 类型统一错误格式200 响应中通过oneOf包含成功信封和所有可能的错误信封文档即代码定义 route 的同时就完成了 OpenAPI 文档的声明零运行时开销验证只在 preHandler 层执行一次2.3 统一信封格式所有 REST 响应都包裹在统一的信封中// packages/kap-server/src/protocol/envelope.ts interface EnvelopeT { code: number; // 0 成功4xxxx/5xxxx 业务错误 msg: string; // success 或错误描述 data: T | null; // 业务数据 request_id: string; // 请求追踪 ID details?: unknown; // 结构化错误详情 stack?: string; // 堆栈信息仅错误时 }code0 表示成功非零值为业务错误码。这与 HTTP 状态码分离——所有 kap-server 响应都是 HTTP 200真正的结果通过信封中的code字段传达。Fastify 的 access log 因此被禁用由 kap-server 自有的请求日志替代。3. REST API 路由体系3.1 路由注册总览所有路由通过registerApiV1Routes统一注册挂载在/api/v1前缀下。// packages/kap-server/src/routes/registerApiV1Routes.ts export async function registerApiV1Routes(app, core, opts) { await app.register(async (apiV1) { registerHealthRoute(apiV1); // /healthz registerMetaRoute(apiV1); // /meta registerAuthRoute(apiV1, core); // /auth/* registerOAuthRoutes(apiV1, core); // /oauth/* registerConfigRoutes(apiV1, core); // /config/* registerModelCatalogRoutes(apiV1); // /models, /providers registerSessionsRoutes(apiV1, core); // /sessions registerPromptRoutes(apiV1, core); // /sessions/:id/prompts registerMessagesRoutes(apiV1, core); // /sessions/:id/messages registerApprovalsRoutes(apiV1, core); // /sessions/:id/approvals registerQuestionsRoutes(apiV1, core); // /sessions/:id/questions registerWorkspacesRoutes(apiV1); // /workspaces registerFilesRoutes(apiV1, core); // /files registerFsRoutes(apiV1, core); // /fs registerToolsRoutes(apiV1, core); // /tools registerTasksRoutes(apiV1, core); // /sessions/:id/tasks registerTerminalsRoutes(apiV1, core); // /terminals registerSkillsRoutes(apiV1, core); // /skills registerTranscriptRoutes(apiV1); // /sessions/:id/transcript registerSearchRoutes(apiV1, core); // /search // ... 调试、快照、shutdown 等 }, { prefix: /api/v1 }); }3.2 核心路由详解会话管理 —/api/v1/sessions会话路由是 kap-server 最复杂的路由模块实现了 v1 的完整 wire contract方法路径功能POST/sessions创建新会话需 workspace_id 或 metadata.cwdGET/sessions列表会话支持 before_id/after_id 游标分页、workspace_id/status 过滤GET/sessions/:id获取单个会话POST/sessions/:id/profile更新标题、metadata、agent_configGET/sessions/:id/children列出子会话POST/sessions/:id/children创建子会话fork tagPOST/sessions/:id/forkFork 会话复制上下文到新会话POST/sessions/:id/compact触发上下文压缩POST/sessions/:id/undo撤销最后 N 轮对话POST/sessions/:id/abort取消当前正在运行的 turnPOST/sessions/:id/archive归档会话POST/sessions/:id/restore恢复已归档会话POST/sessions/:id/btw启动后台 Agentside-channel这些 action 路由通过统一的/sessions/{tail}模式处理——parseActionSuffix从 tail 中解析出{ session_id, action }然后 dispatch 到对应的引擎服务。Prompt 提交 —/api/v1/sessions/:id/promptsPrompt 路由处理用户输入的全流程会话恢复通过resumeSessionById获取或冷加载会话 Scope图片处理提取 ContentPart 中的 base64 图片、解析kimi-file://URL、压缩为模型可接受的尺寸权限与策略应用IAgentPermissionModeService和IAgentToolPolicyServiceProfile 绑定通过IAgentProfileService解析系统提示、工具集、Skills调度执行调用IAgentPromptService.prompt()启动一次 turn事件广播引擎产生的 token 流、工具调用、结果等事件通过 WebSocket 实时推送给前端工作空间管理 —/api/v1/workspaces工作空间路由负责目录注册和文件浏览GET /workspaces— 列表所有已注册的工作空间从IWorkspaceService读取POST /workspaces/register— 注册新目录为工作空间GET /workspaces/:id/files— 浏览工作空间目录树folder picker4. WebSocket 实时通信4.1 WebSocket 端点与升级流程kap-server 在/api/v1/ws端点提供 WebSocket 实时通信。与传统的独立 WebSocket 服务器不同它使用ws库的 noServer 模式——WebSocket 服务器不监听独立端口而是挂载在 Fastify 的 HTTP Server 上通过监听upgrade事件处理 WebSocket 握手。// packages/kap-server/src/start.ts const wssV1 registerWsV1(core, { validateCredential, registry: connectionRegistry, broadcaster, fsWatchBridge, logger, }); app.server.on(upgrade, (req, socket, head) { void handleUpgrade(req, socket, head).catch((error) logger.error({ err: error }, ws upgrade handler failed), ); });升级流程中会执行与 HTTP 路由相同的安全检查Host/Origin 校验、Bearer Token 认证。所有检查通过后WebSocket 连接才被建立。4.2 WsConnectionV1连接级协议每个 WebSocket 连接由WsConnectionV1实例管理该实例实现了BroadcastTarget接口能够接收来自SessionEventBroadcaster的事件并转发给客户端。连接建立后服务器立即发送server_hello帧// packages/kap-server/src/transport/ws/v1/wsConnectionV1.ts this.sendImmediateFrame( buildServerHello({ ws_connection_id: this.id, protocol_version: WS_PROTOCOL_VERSION, max_event_buffer_size: this.maxBufferSize, capabilities: { event_batching: false, compression: false }, }), );4.3 控制帧协议客户端通过 JSON 帧与服务器通信支持以下控制帧类型帧类型方向说明server_helloServer→Client连接建立后立即发送宣告协议版本和能力client_helloClient→Server客户端握手可携带 initial subscriptions 和 cursorssubscribeClient→Server订阅会话事件指定 session_id agents 事件游标subscribe_v2Client→Serverv2 订阅按 transcript grade 分级订阅text、tool_call、thinking 等unsubscribeClient→Server取消订阅指定会话unsubscribe_v2Client→Server取消 v2 的分级订阅ackServer→Client确认客户端的事件序列号resync_requiredServer→Client服务器无法增量补齐事件客户端需全量重同步watch_fs_addClient→Server请求监听文件系统变更watch_fs_removeClient→Server取消文件系统监听4.4 事件广播机制SessionEventBroadcaster是事件分发的核心。它维护一个持久化的事件日志SessionEventJournal每个会话事件被写入日志后广播给所有订阅该会话的连接。事件分发分为两个通道Global 通道全局事件session created/deleted、workspace 变更、配置更新推送给所有已建立连接的客户端无需订阅Subscription 通道会话级事件token 增量、工具调用、审批请求仅推送给已订阅该会话的连接4.5 事件缓冲与背压控制高频事件尤其是 token 级别的文本增量如果逐帧发送会造成大量小包。WsConnectionV1 使用了一个发送缓冲区订阅事件的发送采用 16ms 刷新间隔约 60fps支持最多 64 帧的批量发送立即帧公共事件、控制帧响应作为 FIFO 屏障会先刷新缓冲区中的订阅帧当socket.bufferedAmount超过 1 MiB 时触发背压延迟发送直到缓冲区清空// 默认参数 const DEFAULT_FLUSH_INTERVAL_MS 16; // 刷新间隔约 60fps const DEFAULT_MAX_BATCH_SIZE 64; // 单批最大帧数 const DEFAULT_HIGH_WATER_MARK_BYTES 1 20; // 1 MiB const DEFAULT_BACKPRESSURE_RETRY_MS 5;4.6 Transcript 增量同步connect_v2 的subscribe_v2帧引入了按Transcript Grade的分级订阅。客户端可以只订阅自己关心的 Grade如text、tool_call、thinking服务器只推送对应类型的事件。这大幅减少了不必要的数据传输尤其是在长对话场景下。TranscriptService为每个活跃会话维护一个TranscriptStore引擎产生的每个 WireRecord 都会实时投影到 Store 中。当 WebSocket 客户端订阅某个 Grade 时Store 会从客户端的游标位置开始增量推送如果游标落后太多则发送resync_required要求客户端执行全量重同步。5. 会话生命周期管理5.1 会话创建流程会话创建是 kap-server 中最关键的流程之一。从 REST 请求到引擎实例化涉及多个步骤// packages/kap-server/src/routes/sessions.ts — POST /sessions async (req, reply) { // 1. 解析 cwd从 workspace_id 或 metadata.cwd 中获取工作目录 const workDir workspaceId ? workspace.root : body.metadata.cwd; // 2. 注册工作空间createOrTouch 是幂等的 const touched await core.accessor.get(IWorkspaceService).createOrTouch(workDir); // 3. 获取工作空间的生命周期 handler const handler await core.accessor.get(IWorkspaceLifecycleService).handlerFor({ root: workDir }); // 4. 通过 handler 的 SessionLifecycleService 创建会话 const handle await handler.accessor.get(ISessionLifecycleService).create({ workDir }); // 5. 设置标题、读取元数据 await handle.accessor.get(ISessionMetadata).setTitle(body.title); const meta await handle.accessor.get(ISessionMetadata).read(); // 6. 发布 session.created 事件WebSocket 广播 core.accessor.get(IEventService).publish({ type: event.session.created, payload: { agentId: main, sessionId: session.id, session }, }); }5.2 Session Store 持久化kap-server 的会话数据持久化完全委托给 agent-core-v2 引擎。在bootstrap()阶段引擎通过IFileSystemStorageService将存储根路径设定为homeDir。所有会话相关的持久化元数据通过ISessionMetadata保存到 append-log 中id、title、createdAt、custom metadata会话索引ISessionIndex维护FileSessionIndex按 recency 排序Wire Records每个 Agent 的消息、工具调用、任务状态等以 JSONL 格式写入agents/agentId/wire.jsonl二进制数据上传的文件、图片等通过IBlobStorageService存储会话的cwd保存在ISessionMetadata的自定义字段中gap G3 关闭。即使工作空间被注销会话仍然可以通过自有的 cwd 信息被列出和访问。5.3 多 Agent 支持kap-server 的会话模型支持多个 Agent 共存Main Agent每个会话默认有一个主 Agent负责接收用户 Prompt 并生成回复Subagents / Side-channel通过POST /sessions/:id/btw启动后台 Agent可以在不干扰主会话的情况下执行独立任务Children Sessions通过POST /sessions/:id/children创建子会话fork parent_tag每个 Agent 都是一个独立的 Scope 实例拥有自己的上下文记忆IAgentContextMemoryService、工具集和生命周期。WebSocket 的subscribe帧通过agents字段指定订阅哪些 Agent 的事件。全局搜索扫描所有 Agent 的 WireRecord。5.4 会话的暂停、恢复与 Forkkap-server 中的会话并非始终在内存中。当连接断开或会话空闲时会话 Scope 可以被释放当客户端再次访问时通过resumeSessionById从磁盘重建。Fork 操作在ISessionLifecycleService.fork()中实现——它会创建一个新会话复制源会话的上下文历史作为系统消息注入使新会话继承源会话的全部对话上下文但拥有独立的对话未来。6. V2 引擎集成6.1 引擎初始化kap-server 在startServer()中通过agent-core-v2的bootstrap()函数创建 Core Scope// packages/kap-server/src/start.ts const { app: core } bootstrap( { homeDir, configPath, clientIdentity: opts.hostIdentity, }, [ ...logSeed(logging), // 日志配置 ...hostRequestHeadersSeed(kimiHeaders), // HTTP 请求头 ...skillCatalogRuntimeOptionsSeed(skillDirs), // Skill 目录 ...hostIdentitySeed(opts.hostIdentity), // 宿主身份 ...(opts.seeds ?? []), // 额外配置 ], );bootstrap()返回的core是一个 App 级别的 Scope它包含了所有引擎服务的注册。kap-server 的每个路由 handler 都通过core.accessor.get(ISomeService)获取需要的引擎服务实例。6.2 DI x Scope 在服务层的应用kap-server 不直接持有引擎状态——所有状态都在 Scope 层次结构中App Scope (core) ├── ISessionIndex — 全局会话索引 ├── IWorkspaceService — 工作空间注册表 ├── IConfigService — 配置读写 ├── IEventService — 事件总线 ├── IProviderDiscoveryService — Provider 发现 ├── IWorkspaceLifecycleService │ └── handlerFor(root) → Workspace Scope │ ├── ISessionLifecycleService — 会话的创建/fork/归档 │ │ └── create({ workDir }) → Session Scope │ │ ├── ISessionMetadata — 会话元数据 │ │ ├── ISessionContext — cwd、workspaceId │ │ ├── IAgentLifecycleService — Agent 生命周期 │ │ │ └── createMainAgent() → Agent Scope │ │ │ ├── IAgentPromptService — Prompt 处理 │ │ │ ├── IAgentContextMemoryService — 对话历史 │ │ │ ├── IAgentToolPolicyService — 工具策略 │ │ │ ├── IAgentLoopService — Agent 循环 │ │ │ └── ... │ │ └── ... │ └── ... └── ...这种三级嵌套 DI 的语义是App 级别的服务是全局单例Workspace 级别的服务在同一个工作空间的所有会话之间共享Session 和 Agent 级别的服务是每个会话/Agent 独立的。kap-server 路由 handler 从 App Scope 进入通过handlerFor、resumeSessionById、ensureMainAgent等函数逐步下沉到更细粒度的 Scope。6.3 请求 → Agent → 响应的完整路径以用户提交一个 Prompt 为例完整路径如下POST /api/v1/sessions/abc/prompts (HTTP) │ ▼ registerPromptsRoutes → defineRoute (Zod 验证 body/params) │ ▼ resumeSessionById(core.accessor, sessionId) — 获取/冷加载 Session Scope │ ▼ ensureMainAgent(session) — 获取 Main Agent Scope │ ▼ IAgentPromptService.prompt(content, options) — 调度执行 │ ├─→ IAgentLoopService — Agent 循环think → act → observe │ │ │ ├─→ LLM 调用 → token 流 │ ├─→ Tool 调用 → Bash / File / Search ... │ └─→ 事件发射 → IEventService.publish(...) │ ▼ SessionEventBroadcaster — 事件持久化 广播 │ ├─→ SessionEventJournal — 写入事件日志 └─→ WebSocket 推送 — 分发给所有订阅的客户端 │ ▼ kimi-web / kimi-inspect — 实时渲染6.4 引擎事件的 WebSocket 转发引擎的IEventService是事件源。kap-server 的SessionEventBroadcaster订阅引擎事件总线的session.*前缀事件**agent.***agent.turn_started、agent.turn_ended、agent.text_delta、agent.tool_call等 — 推送给订阅了该会话的 WebSocket 连接**session.***session.created、session.meta.updated、session.archived— 全局广播**workspace.***workspace.created、workspace.deleted— 全局广播TranscriptService在这些事件的基础上构建 TranscriptStore将原始事件转换为结构化的 Transcript 操作upsert、reset供 REST transcript 端点和 WebSocket 的subscribe_v2使用。7. 多引擎支持7.1 V1 与 V2 的架构差异kap-server 是 agent-core-v2 引擎的服务层但 kimi-code 历史上还有一个基于agent-core(V1) 的服务器packages/server。两者的架构差异显著对比维度V1 Server (agent-core)V2 Server (kap-server)DI 容器IInstantiationService扁平 DIDI × Scope三级嵌套 DI事件模型EventEmitter wsGatewayServiceIEventService SessionEventBroadcaster会话存储SessionService单文件ISessionMetadata FileSessionIndex WireRecordAgent 模型单一 Agent多 Agentmain subagent childrenTranscript无标准 TranscriptTranscriptStore Grade 分级订阅路由定义Express 风格defineRouteZod → Swagger7.2 引擎切换机制在 kimi-code CLI 中引擎切换通过配置项控制KIMI_CODE_USE_V2环境变量1启用 V2 引擎config.json配置文件中的engine_version字段CLI flag--use-v2命令行参数当 V2 引擎被激活时CLI 的kimi web命令调用startServer启动 kap-server否则启动 V1 Server。两者的/api/v1接口保持兼容kimi-web 前端无需感知后端是哪个版本——它通过同一个 API 路径与任一引擎通信。7.3 开发模式下的双引擎调试在开发模式下可以同时运行 V1 和 V2 引擎的服务器V1 Server 默认在 58627 端口V2 (kap-server) 默认也在 58627 端口使用 port1 重试机制如果端口被占用自动尝试 58628、58629……最多重试 100 次两个服务器通过instanceRegistry独立注册在homeDir/server/instances/下互不冲突这种设计使得在迁移期间前端可以同时连接两个后端进行对比测试。每个 kap-server 实例的注册信息PID、host、port、启动时间、serverVersion以 JSON 文件持久化kimi server ps和kimi server kill命令可以列出和管理所有实例。8. 调试接口8.1 Debug RPC 接口kap-server 提供了一套完整的调试 RPC 接口挂载在/api/v1/debug/*路径下。该接口仅在以下条件同时满足时启用启动时传入--debug-endpoints参数服务器绑定在 loopback 地址127.0.0.1请确保携带有效的 Bearer Token// packages/kap-server/src/start.ts const debugEndpoints exposureClass loopback opts.debugEndpoints true; // ... if (debugEndpoints true) { registerDebugRoutes(apiV1, core); }这些限制确保了调试接口不会在非安全环境下暴露——它允许调用者访问引擎内部的所有 Service是具有完全权限的管理接口。8.2 DI 容器反射Debug 路由实际注册的是registerServiceDispatcherRoutes——一个基于 DI 反射的 Service 调度器// packages/kap-server/src/transport/registerDebugRoutes.ts export function registerDebugRoutes(app, core) { registerServiceDispatcherRoutes(app, core, /debug, { lookup: resolveAnyScopedServiceId, // 跨所有 Scope 查找 Service describe: describeAllChannels, // 列出所有可用的 RPC 通道 }); }resolveAnyScopedServiceId不仅能在 App Scope 中查找 Service还能穿透 Session 和 Agent Scope——通过session_id和agent_id参数定位到正确的嵌套 Scope然后从中取出目标 Service 实例。describeAllChannels暴露了所有可调用服务通道的完整列表包括每个通道的输入/输出 Schema 和描述信息。这实际上是一个运行时的 DI 容器反射 API。8.3 Service 面板kimi-inspect 调试面板正是通过这组 Debug RPC 接口与 kap-server 交互。它提供两种核心操作**数据查询GET**读取 Service 的当前状态。例如读取IAgentContextMemoryService的对话历史、ISessionMetadata的元数据、IConfigService的当前配置**触发按钮POST**调用 Service 的方法。例如触发 compaction、重置对话上下文、切换 permission mode典型的 debug 请求路径为/api/v1/debug/serviceId/method其中serviceId是 DI 注册的 Service 唯一标识method是 Service 暴露的 RPC 方法名。8.4 kimi-inspect 的消费模式kimi-inspect 是一个独立的 Web 应用它作为 kap-server 的客户端运行。它通过以下方式消费调试接口启动时调用describeAllChannels获取完整的 Service 清单构建左侧导航树选中 Service 时自动调用该 Service 的只读方法来填充数据面板用户点击按钮时发送 POST 请求调用对应的 RPC 方法实时更新通过 WebSocket 订阅会话事件面板中的数据实时刷新这种设计使得 kimi-inspect 成为一个完全动态的调试工具——它不需要硬编码任何 Service 的名称或方法所有能力都通过运行时的 DI 反射发现。