mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-08-12 18:27:39 +00:00
docs(server): add local server guide and API reference (#2839)
* docs(server): add local server guide and API reference * docs(server): qualify binary endpoint HTTP semantics
This commit is contained in:
parent
26ddc1d0fb
commit
dc8db90cdd
9 changed files with 928 additions and 2 deletions
|
|
@ -56,6 +56,7 @@ const config = withMermaid(defineConfig({
|
|||
{ text: '会话与上下文', link: '/zh/guides/sessions' },
|
||||
{ text: '使用目标模式', link: '/zh/guides/goals' },
|
||||
{ text: '在 IDE 中使用', link: '/zh/guides/ides' },
|
||||
{ text: '本地服务与 API', link: '/zh/guides/server' },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
@ -90,6 +91,7 @@ const config = withMermaid(defineConfig({
|
|||
items: [
|
||||
{ text: 'kimi 命令', link: '/zh/reference/kimi-command' },
|
||||
{ text: 'kimi acp 子命令', link: '/zh/reference/kimi-acp' },
|
||||
{ text: '服务 API', link: '/zh/reference/server-api' },
|
||||
{ text: '内置工具', link: '/zh/reference/tools' },
|
||||
{ text: '斜杠命令', link: '/zh/reference/slash-commands' },
|
||||
{ text: '键盘快捷键', link: '/zh/reference/keyboard' },
|
||||
|
|
@ -133,6 +135,7 @@ const config = withMermaid(defineConfig({
|
|||
{ text: 'Sessions and Context', link: '/en/guides/sessions' },
|
||||
{ text: 'Using Goals', link: '/en/guides/goals' },
|
||||
{ text: 'Using in IDEs', link: '/en/guides/ides' },
|
||||
{ text: 'Local Server and API', link: '/en/guides/server' },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
@ -167,6 +170,7 @@ const config = withMermaid(defineConfig({
|
|||
items: [
|
||||
{ text: 'kimi Command', link: '/en/reference/kimi-command' },
|
||||
{ text: 'kimi acp Subcommand', link: '/en/reference/kimi-acp' },
|
||||
{ text: 'Server API', link: '/en/reference/server-api' },
|
||||
{ text: 'Built-in Tools', link: '/en/reference/tools' },
|
||||
{ text: 'Slash Commands', link: '/en/reference/slash-commands' },
|
||||
{ text: 'Keyboard Shortcuts', link: '/en/reference/keyboard' },
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ Switches that control the behavior of subsystems such as telemetry, background t
|
|||
| Variable | Purpose | Valid values |
|
||||
| --- | --- | --- |
|
||||
| `KIMI_DISABLE_TELEMETRY` | Disable anonymous telemetry reporting | `1`, `true`, `yes`, `y` (case-insensitive) |
|
||||
| `KIMI_CODE_PASSWORD` | Set a parallel auth credential for the `kimi web` local server, valid alongside the bearer token; recommended when binding the server beyond loopback — see [Local server and API](../guides/server.md#authentication) | Any non-empty string; when unset, only the token is valid |
|
||||
| `KIMI_CODE_BACKGROUND_KEEP_ALIVE_ON_EXIT` | Whether to keep background tasks when the session closes; takes higher priority than `config.toml`. The default is to stop them on exit | Truthy: `1`/`true`/`yes`/`on`; falsy: `0`/`false`/`no`/`off` |
|
||||
| `KIMI_CODE_BACKGROUND_MAX_RUNNING_TASKS` | Cap on concurrently running background tasks; takes higher priority than `[background] max_running_tasks` in `config.toml` (unset means no cap) | Positive integer; invalid values are ignored |
|
||||
| `KIMI_IMAGE_MAX_EDGE_PX` | Longest-edge ceiling (px) for image compression; takes higher priority than `[image] max_edge_px` in `config.toml` (default `2000`) | Positive integer; invalid values are ignored |
|
||||
|
|
|
|||
116
docs/en/guides/server.md
Normal file
116
docs/en/guides/server.md
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
# Local Server and API
|
||||
|
||||
Kimi Code CLI ships with a built-in local server: running `kimi web` starts a foreground process that mounts three things at once — the web UI in your browser, a REST API (`/api/v1`), and a WebSocket event stream (`/api/v1/ws`). The web UI lets you use Kimi Code in a browser; the REST and WebSocket APIs are for scripts and third-party tools, letting you create sessions, submit prompts, and follow execution from code — all reading and writing the same session data as the TUI and the web UI.
|
||||
|
||||
> Make sure Kimi Code CLI is installed and ready to use first — either logged in via `/login` (in the TUI, or `kimi login`), or with a provider configured in `config.toml`. The server shares the CLI's login state and configuration, so no separate credential is needed for it.
|
||||
|
||||
::: warning
|
||||
The REST and WebSocket APIs described on this page are experimental: interface stability is not guaranteed, and endpoints, fields, and event types may change in any release. When integrating, rely on the `/openapi.json` and `/asyncapi.json` documents served by your version.
|
||||
:::
|
||||
|
||||
## Start the server
|
||||
|
||||
```sh
|
||||
kimi web # run the server in the foreground and open the browser
|
||||
kimi web --no-open # run the server only, don't open the browser
|
||||
kimi web --port 58628 # pick a specific bind port
|
||||
```
|
||||
|
||||
The server binds to `127.0.0.1:58627` by default (loopback only). If the port is taken it automatically retries with the next one, so multiple instances can coexist on the same machine; each instance registers under `~/.kimi-code/server/instances/`. The startup banner prints the access URL and the plaintext token:
|
||||
|
||||
```text
|
||||
Local: http://127.0.0.1:58627/#token=...
|
||||
Token: ...
|
||||
Stop: Ctrl+C
|
||||
```
|
||||
|
||||
The server runs in the foreground; press `Ctrl-C` for a clean shutdown. For the full option list such as `--host` and `--log-level`, see the [kimi command reference](../reference/kimi-command.md#kimi-web).
|
||||
|
||||
## Authentication
|
||||
|
||||
Every `/api/*` endpoint requires a bearer token (any request carrying this string is treated as authorized). The token is generated on the first server boot, persisted at `~/.kimi-code/server.token` (file mode 0600), and reused across restarts.
|
||||
|
||||
Pick the carrying method that fits your client:
|
||||
|
||||
- **REST**: the `Authorization: Bearer <token>` request header.
|
||||
- **web UI**: the URL in the startup banner carries a `#token=` fragment, so opening it in a browser completes sign-in automatically. The fragment is never sent to the server.
|
||||
- **WebSocket**: clients that can set headers use `Authorization: Bearer`; clients that cannot (such as browsers) pass the subprotocol (a protocol name declared during the WebSocket handshake) `kimi-code.bearer.<token>` instead.
|
||||
|
||||
If the token leaks, run `kimi web rotate-token`: the new token is written to `server.token` immediately, the old one stops working at once, and running instances pick up the new token without a restart.
|
||||
|
||||
If you bind the server to a non-loopback address (`--host`), also set the `KIMI_CODE_PASSWORD` environment variable as a parallel credential; the server then rate-limits authentication failures automatically.
|
||||
|
||||
::: danger
|
||||
`--dangerous-bypass-auth` disables authentication entirely — anyone who can reach the port can control your sessions, file system, and shell. Only use it on trusted networks or behind your own authenticating proxy. See the [kimi command reference](../reference/kimi-command.md#kimi-web).
|
||||
:::
|
||||
|
||||
## Drive a session over the API
|
||||
|
||||
The minimal flow with curl: check the server → create a session → subscribe to events → submit a prompt → read history back. The examples assume the server runs at the default address and the token is stored in the shell variable `TOKEN`.
|
||||
|
||||
1. Check server status:
|
||||
|
||||
```sh
|
||||
curl -s -H "Authorization: Bearer $TOKEN" http://127.0.0.1:58627/api/v1/meta
|
||||
```
|
||||
|
||||
Every JSON response is wrapped in a uniform envelope — `{ "code": 0, "msg": "success", "data": ..., "request_id": "..." }`. The business outcome lives in `code` (`0` means success); the HTTP status only reports transport-level results.
|
||||
|
||||
2. Create a session; `metadata.cwd` sets the working directory:
|
||||
|
||||
```sh
|
||||
curl -s -X POST http://127.0.0.1:58627/api/v1/sessions \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"metadata": {"cwd": "/path/to/project"}}'
|
||||
```
|
||||
|
||||
The returned `data.id` (shaped like `session_...`) is the session id used by every subsequent request.
|
||||
|
||||
3. Connect to the WebSocket and subscribe to session events. Any WebSocket client works; below is a dependency-free Node.js script (Node.js 22+ ships a built-in `WebSocket` client):
|
||||
|
||||
```js
|
||||
// subscribe.mjs — usage: TOKEN=... node subscribe.mjs session_...
|
||||
const ws = new WebSocket('ws://127.0.0.1:58627/api/v1/ws', [
|
||||
`kimi-code.bearer.${process.env.TOKEN}`,
|
||||
]);
|
||||
ws.onmessage = (e) => console.log(e.data);
|
||||
ws.onopen = () =>
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
type: 'subscribe',
|
||||
id: '1',
|
||||
payload: { session_ids: [process.argv[2]] },
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
4. Submit a prompt:
|
||||
|
||||
```sh
|
||||
curl -s -X POST http://127.0.0.1:58627/api/v1/sessions/<session_id>/prompts \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"content": [{"type": "text", "text": "Introduce this repository in one sentence"}]}'
|
||||
```
|
||||
|
||||
The subscriber sees, in order: `turn.started` (turn begins) → `assistant.delta` (streaming text increments) → `tool.call.started` / `tool.result` when tool calls happen → `turn.ended` (turn finishes).
|
||||
|
||||
5. Read history back over REST at any time:
|
||||
|
||||
```sh
|
||||
curl -s -H "Authorization: Bearer $TOKEN" \
|
||||
"http://127.0.0.1:58627/api/v1/sessions/<session_id>/messages?page_size=20"
|
||||
```
|
||||
|
||||
## Live specification documents
|
||||
|
||||
While running, the server describes itself with two specification documents, both requiring the bearer token:
|
||||
|
||||
- `GET /openapi.json` — an OpenAPI document for the REST API, with request/response schemas for every endpoint; import it into Swagger UI, Postman, and similar tools.
|
||||
- `GET /asyncapi.json` — an AsyncAPI document for the WebSocket protocol, covering control frames and event types.
|
||||
|
||||
## Next steps
|
||||
|
||||
- [Server API](../reference/server-api.md) — full REST endpoint inventory, error codes, WebSocket events, and the transcript protocol
|
||||
- [kimi command](../reference/kimi-command.md#kimi-web) — all `kimi web` command-line options
|
||||
|
|
@ -157,7 +157,7 @@ kimi acp
|
|||
|
||||
Run the local Kimi server in the foreground of the current terminal — a single process that exposes the REST + WebSocket API and serves the web UI from the same origin — and open the web UI in the default browser once it is ready. The command stays attached to the terminal and shuts down cleanly on `SIGINT` / `SIGTERM` (e.g. `Ctrl-C`).
|
||||
|
||||
When the server is running, `GET /openapi.json` returns the REST OpenAPI document and `GET /asyncapi.json` returns the local WebSocket AsyncAPI document.
|
||||
When the server is running, `GET /openapi.json` returns the REST OpenAPI document and `GET /asyncapi.json` returns the local WebSocket AsyncAPI document. For an end-to-end walkthrough of driving sessions over the API, see [Local server and API](../guides/server.md); for the protocol details, see the [Server API](./server-api.md) reference.
|
||||
|
||||
```sh
|
||||
kimi web # run the server in the foreground and open the browser
|
||||
|
|
|
|||
344
docs/en/reference/server-api.md
Normal file
344
docs/en/reference/server-api.md
Normal file
|
|
@ -0,0 +1,344 @@
|
|||
# Server API
|
||||
|
||||
The local server started by `kimi web` exposes two programmatic surfaces: a REST API (`/api/v1`, plus `/api/v2/sessions`) and a WebSocket event stream (`/api/v1/ws`). This page is the protocol reference for both. For how to start the server and its command-line options, see the [kimi command](./kimi-command.md#kimi-web) reference; for an end-to-end walkthrough, see [Local server and API](../guides/server.md).
|
||||
|
||||
The complete request/response schema of every endpoint is owned by the server's live specification documents: `GET /openapi.json` (OpenAPI) and `GET /asyncapi.json` (AsyncAPI). Both require authentication.
|
||||
|
||||
::: warning
|
||||
The REST and WebSocket APIs described on this page are experimental: interface stability is not guaranteed, and endpoints, fields, and event types may change in any release. When integrating, rely on the `/openapi.json` and `/asyncapi.json` documents served by your version.
|
||||
:::
|
||||
|
||||
## Conventions
|
||||
|
||||
### Address
|
||||
|
||||
The default address is `http://127.0.0.1:58627`. When the port is taken, the server retries with the next port (up to 100 times); use `--port` / `--host` to change the bind. Multiple instances can coexist under the same home directory; running instances register under `~/.kimi-code/server/instances/`.
|
||||
|
||||
### Authentication
|
||||
|
||||
All `/api/*` paths (including `/openapi.json` and `/asyncapi.json`) require the bearer token, except:
|
||||
|
||||
- `OPTIONS` preflight requests
|
||||
- `GET /api/v1/healthz` (liveness probe)
|
||||
- Static web assets (non-`/api/` paths)
|
||||
|
||||
How to carry it: REST uses the `Authorization: Bearer <token>` header; the WebSocket upgrade accepts the same header or the subprotocol `kimi-code.bearer.<token>`. Token generation and rotation are covered in [Local server and API: Authentication](../guides/server.md#authentication).
|
||||
|
||||
Failed authentication returns HTTP 401 with envelope code `40101`. On non-loopback binds, a source that fails authentication 10 times within 60 seconds is banned for 60 seconds, during which every request gets HTTP 429 (code `42901`).
|
||||
|
||||
### Response envelope
|
||||
|
||||
Every JSON response is wrapped in a uniform envelope:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": {},
|
||||
"request_id": "01JZX4A6E7M8V0R3Q0N2K2M5Q9"
|
||||
}
|
||||
```
|
||||
|
||||
- `code`: the business outcome; `0` means success. See the error-code bands below.
|
||||
- `data`: the payload on success. Note that some "error" envelopes also carry a non-null `data` — for example, resolving an already-resolved approval returns `40902` with `data.resolved` set to `false` — so clients should check `code` first, then `data`.
|
||||
- `request_id`: a ULID for this request. Clients may supply one via the `X-Request-Id` header; invalid values are regenerated by the server.
|
||||
|
||||
The HTTP status is almost always 200; the business outcome lives in `code`. Exceptions:
|
||||
|
||||
| Situation | HTTP status |
|
||||
| --- | --- |
|
||||
| Authentication failure / rate limit | 401 / 429 |
|
||||
| Provider created, provider catalog imported | 201 |
|
||||
| Provider deleted | 204 |
|
||||
| Binary/streaming endpoints | 206 (Range) / 304 (ETag unchanged) where supported — capabilities differ per endpoint, see [Binary and streaming endpoints](#binary-and-streaming-endpoints) |
|
||||
| `GET /api/v1/files/{file_id}` download errors | real 404 / 500 (still carrying an envelope body) |
|
||||
|
||||
The 201 responses still carry the standard envelope (`code` 0) — only the status line follows the REST convention for resource creation. A 204 response has no body by definition, so a successful delete is reported by the status code itself.
|
||||
|
||||
### Error codes
|
||||
|
||||
Error codes are grouped by band:
|
||||
|
||||
| Band | Meaning | Examples |
|
||||
| --- | --- | --- |
|
||||
| `0` | Success | |
|
||||
| `400xx` | Bad request | `40001` validation failed (`details` lists each field), `40003` provider is OAuth-managed |
|
||||
| `401xx` | Auth and readiness | `40101` unauthorized, `40110` no provider configured, `40113` model not resolved |
|
||||
| `404xx` | Not found | `40401` session, `40408` MCP server, `40409` file path |
|
||||
| `409xx` | State conflict | `40901` session busy, `40902` approval already resolved, `40922` page conditions mismatch `page_token` |
|
||||
| `410xx` | Expired | `41001` approval timed out, `41002` question timed out, `41003` temporary file expired |
|
||||
| `413xx` | Size or boundary exceeded | `41302` file read over 10 MB, `41304` path escapes the session directory |
|
||||
| `429xx` | Rate limited | `42901` auth-failure ban, `42902` too many fs watches |
|
||||
| `500xx` | Server internal error | `50001` uncaught exception, `50003` persistence failure |
|
||||
| `6xxxx` / `7xxxx` / `8xxxx` | Tool runtime / LLM provider / MCP passthrough errors; `msg` carries the upstream text | |
|
||||
|
||||
### Pagination
|
||||
|
||||
List endpoints come in two styles:
|
||||
|
||||
- **Cursor style**: `before_id` / `after_id` (mutually exclusive) plus `page_size` (1–100), responding with `{ items, has_more }`. Used by the session list, message list, transcript, and others.
|
||||
- **`page_token`**: an opaque token (bound to a fingerprint of the query conditions), used by `POST /api/v1/search` and `GET /api/v2/sessions`. Changing any query condition mid-pagination invalidates the token: v2 returns `40922`, search returns `40001`.
|
||||
|
||||
## REST endpoints
|
||||
|
||||
Endpoints are grouped by resource below. A `:{action}` suffix in a path is the action convention — POST to `path:action` on a single resource for non-CRUD operations (such as `:fork` and `:archive` on a session).
|
||||
|
||||
### Server and metadata
|
||||
|
||||
| Method and path | Description |
|
||||
| --- | --- |
|
||||
| `GET /api/v1/healthz` | Liveness probe; auth-exempt |
|
||||
| `GET /api/v1/meta` | Server version, capability map, `server_id`, experimental flags |
|
||||
| `POST /api/v1/shutdown` | Graceful shutdown (replies 200 first); mounted only on loopback binds |
|
||||
|
||||
### Login and usage
|
||||
|
||||
| Method and path | Description |
|
||||
| --- | --- |
|
||||
| `GET /api/v1/auth` | Auth readiness snapshot |
|
||||
| `POST /api/v1/oauth/login` | Start the OAuth device-code login flow |
|
||||
| `GET /api/v1/oauth/login` | Poll the login flow state |
|
||||
| `DELETE /api/v1/oauth/login` | Cancel a pending login flow |
|
||||
| `POST /api/v1/oauth/logout` | Log out the managed provider |
|
||||
| `GET /api/v1/oauth/usage` | Plan usage and limits |
|
||||
| `GET /api/v1/oauth/userinfo` | Account profile |
|
||||
|
||||
### Config
|
||||
|
||||
| Method and path | Description |
|
||||
| --- | --- |
|
||||
| `GET /api/v1/config` | Read the global config (secret fields redacted) |
|
||||
| `POST /api/v1/config` | Merge-patch the config; broadcasts `event.config.changed` |
|
||||
|
||||
### Models and providers
|
||||
|
||||
| Method and path | Description |
|
||||
| --- | --- |
|
||||
| `GET /api/v1/models` | List configured model aliases |
|
||||
| `POST /api/v1/models/{model_id}:set_default` | Set the global default model |
|
||||
| `GET /api/v1/providers` | List providers |
|
||||
| `POST /api/v1/providers` | Create a provider (201) |
|
||||
| `GET /api/v1/providers/{provider_id}` | Read a provider (reveals the stored key) |
|
||||
| `PUT /api/v1/providers/{provider_id}` | Replace a provider |
|
||||
| `DELETE /api/v1/providers/{provider_id}` | Delete a provider (204) |
|
||||
| `POST /api/v1/providers/{provider_id}:refresh` | Refresh one provider's model metadata |
|
||||
| `POST /api/v1/providers:{action}` | Collection actions: `refresh` / `refresh_oauth` / `import_catalog` / `import_registry` |
|
||||
| `GET /api/v1/catalog/providers` | Browse the models.dev directory (server-proxied) |
|
||||
| `GET /api/v1/catalog/providers/{catalog_id}` | Read one directory entry |
|
||||
|
||||
### Sessions
|
||||
|
||||
| Method and path | Description |
|
||||
| --- | --- |
|
||||
| `POST /api/v1/sessions` | Create a session (requires `workspace_id` or `metadata.cwd`) |
|
||||
| `GET /api/v1/sessions` | List sessions; cursor pagination with filters such as `busy` and `archived_only` |
|
||||
| `GET /api/v1/sessions/{session_id}` | Read one session |
|
||||
| `GET /api/v1/sessions/{session_id}/profile` | Read the session profile |
|
||||
| `POST /api/v1/sessions/{session_id}/profile` | Update title, metadata, agent config |
|
||||
| `POST /api/v1/sessions/{session_id}:{action}` | Session actions: `fork` / `compact` / `undo` / `abort` / `btw` / `archive` / `restore` |
|
||||
| `GET /api/v1/sessions/{session_id}/children` | List child sessions |
|
||||
| `POST /api/v1/sessions/{session_id}/children` | Create a child session (fork with a tag) |
|
||||
| `GET /api/v1/sessions/{session_id}/status` | Realtime status rollup |
|
||||
| `GET /api/v1/sessions/{session_id}/goal` | Current goal snapshot (`null` when none) |
|
||||
| `GET /api/v1/sessions/{session_id}/warnings` | Session-level warnings |
|
||||
| `POST /api/v1/sessions/{session_id}/export` | Export the session with diagnostics (zip stream, not enveloped) |
|
||||
| `GET /api/v1/sessions/{session_id}/snapshot` | Full snapshot for client rebuilds (with `as_of_seq` and `epoch`) |
|
||||
|
||||
### Messages and transcript
|
||||
|
||||
| Method and path | Description |
|
||||
| --- | --- |
|
||||
| `GET /api/v1/sessions/{session_id}/messages` | Page messages (`before_id` / `after_id` / `role`) |
|
||||
| `GET /api/v1/sessions/{session_id}/messages/{message_id}` | Read one message |
|
||||
| `GET /api/v1/sessions/{session_id}/transcript` | Turn-paged transcript (requires `agent_id`); global state rides along unpaginated |
|
||||
| `GET /api/v1/sessions/{session_id}/transcript/ops` | Op-batch catch-up (`since_seq`); `complete: false` means a full refresh is needed |
|
||||
| `GET /api/v1/sessions/{session_id}/transcript/user-messages` | Turn-opening user inputs, unpaginated |
|
||||
| `GET /api/v1/sessions/{session_id}/transcript/plan` | ExitPlanMode plan content, path, and review outcome |
|
||||
|
||||
### Prompts
|
||||
|
||||
| Method and path | Description |
|
||||
| --- | --- |
|
||||
| `GET /api/v1/sessions/{session_id}/prompts` | Active and queued prompts |
|
||||
| `POST /api/v1/sessions/{session_id}/prompts` | Submit a prompt (content-part array, optional model / permission-mode overrides) |
|
||||
| `POST /api/v1/sessions/{session_id}/prompts:steer` | Steer queued prompts into the active turn |
|
||||
| `POST /api/v1/sessions/{session_id}/prompts/{prompt_id}:abort` | Abort a running prompt |
|
||||
| `POST /api/v1/sessions/{session_id}/prompts/{prompt_id}:steer` | Steer one queued prompt |
|
||||
|
||||
### Approvals and questions
|
||||
|
||||
| Method and path | Description |
|
||||
| --- | --- |
|
||||
| `GET /api/v1/sessions/{session_id}/approvals` | List approval requests (filter with `status=pending`) |
|
||||
| `POST /api/v1/sessions/{session_id}/approvals/{approval_id}` | Resolve an approval |
|
||||
| `GET /api/v1/sessions/{session_id}/questions` | List questions |
|
||||
| `POST /api/v1/sessions/{session_id}/questions/{question_id}` | Answer a question |
|
||||
| `POST /api/v1/sessions/{session_id}/questions/{question_id}:dismiss` | Dismiss a question |
|
||||
|
||||
### Background tasks
|
||||
|
||||
| Method and path | Description |
|
||||
| --- | --- |
|
||||
| `GET /api/v1/sessions/{session_id}/tasks` | List background tasks |
|
||||
| `GET /api/v1/sessions/{session_id}/tasks/{task_id}` | Read a task (optional output preview) |
|
||||
| `POST /api/v1/sessions/{session_id}/tasks/{task_id}:cancel` | Cancel a task |
|
||||
|
||||
### Skills, tools, and MCP
|
||||
|
||||
| Method and path | Description |
|
||||
| --- | --- |
|
||||
| `GET /api/v1/sessions/{session_id}/skills` | Per-session skill catalog |
|
||||
| `GET /api/v1/workspaces/{workspace_id}/skills` | Session-less skill catalog for a workspace |
|
||||
| `POST /api/v1/sessions/{session_id}/skills/{skill_name}:activate` | Activate a skill (starts a turn) |
|
||||
| `GET /api/v1/tools` | List tools of the effective agent |
|
||||
| `GET /api/v1/mcp/servers` | List MCP servers |
|
||||
| `POST /api/v1/mcp/servers/{mcp_server_id}:restart` | Restart an MCP server |
|
||||
|
||||
### Terminals
|
||||
|
||||
PTY terminal endpoints; mounted only on loopback binds.
|
||||
|
||||
| Method and path | Description |
|
||||
| --- | --- |
|
||||
| `GET /api/v1/sessions/{session_id}/terminals` | List terminals |
|
||||
| `POST /api/v1/sessions/{session_id}/terminals` | Create a terminal |
|
||||
| `GET /api/v1/sessions/{session_id}/terminals/{terminal_id}` | Read a terminal (including scrollback) |
|
||||
| `POST /api/v1/sessions/{session_id}/terminals/{terminal_id}:close` | Close a terminal |
|
||||
|
||||
### Workspaces
|
||||
|
||||
| Method and path | Description |
|
||||
| --- | --- |
|
||||
| `GET /api/v1/workspaces` | List registered workspaces |
|
||||
| `POST /api/v1/workspaces` | Register a workspace (idempotent on the root path) |
|
||||
| `PATCH /api/v1/workspaces/{workspace_id}` | Rename |
|
||||
| `DELETE /api/v1/workspaces/{workspace_id}` | Unregister (keeps on-disk content) |
|
||||
| `GET /api/v1/workspaces/{workspace_id}/trust` | Read the trust state |
|
||||
| `POST /api/v1/workspaces/{workspace_id}/trust` | Grant trust |
|
||||
| `POST /api/v1/workspaces/{workspace_id}/untrust` | Revoke trust |
|
||||
|
||||
### File system
|
||||
|
||||
In-session file operations go through `POST /api/v1/sessions/{session_id}/fs:{action}` with JSON bodies; actions are `list` / `read` / `list_many` / `stat` / `stat_many` / `mkdir` / `search` / `grep` / `git_status` / `diff` / `open` / `open-in` / `reveal`. In addition:
|
||||
|
||||
| Method and path | Description |
|
||||
| --- | --- |
|
||||
| `POST /api/v1/workspace/fs:search` | Session-less workspace search (the body carries the workspace reference) |
|
||||
| `GET /api/v1/sessions/{session_id}/fs/{path}:download` | Download a session file (binary, see below) |
|
||||
| `GET /api/v1/fs:browse` | List host directories (folder picker) |
|
||||
| `GET /api/v1/fs:home` | The user's home directory and recent workspaces |
|
||||
| `GET /api/v1/fs:content` | Raw bytes of any host file (gated only by the token — be careful when exposing the port) |
|
||||
| `POST /api/v1/fs:mkdir` | Create a directory by absolute path |
|
||||
|
||||
### File uploads
|
||||
|
||||
| Method and path | Description |
|
||||
| --- | --- |
|
||||
| `POST /api/v1/files` | Multipart upload (`file` field, optional `name` and `expires_in_sec`); returns file metadata |
|
||||
| `GET /api/v1/files/{file_id}` | Download (binary; errors use real HTTP statuses) |
|
||||
| `DELETE /api/v1/files/{file_id}` | Delete |
|
||||
|
||||
### Global search and misc
|
||||
|
||||
| Method and path | Description |
|
||||
| --- | --- |
|
||||
| `POST /api/v1/search` | Cross-session full-text search; `mode` is `terms` (default) or `literal` (exact substring); `page_token` pagination |
|
||||
| `GET /api/v1/connections` | List live WebSocket connections |
|
||||
| `GET /api/v2/sessions` | Next-generation session list, see below |
|
||||
| `/api/v1/debug/*` | Reflection debug RPC; mounted only with `--debug-endpoints` on loopback, not a stable protocol |
|
||||
|
||||
### `GET /api/v2/sessions`
|
||||
|
||||
A next-generation session query for list views — filtering, sorting, and field groups all travel in query parameters:
|
||||
|
||||
| Parameter | Description |
|
||||
| --- | --- |
|
||||
| `workspace.id` | Filter by workspace; repeatable |
|
||||
| `activity.status` | Filter by activity status: `running` / `approval` / `question` / `failed` / `idle`; repeatable |
|
||||
| `meta.updated_after` | Only sessions updated after this time (epoch milliseconds) |
|
||||
| `meta.archived` | `true` / `false` (default) / `all` |
|
||||
| `sort` | `meta.updated_at_desc` (default) / `meta.updated_at_asc` / `meta.created_at_desc` |
|
||||
| `include` | Comma-separated extra field groups; currently only `git` (branch and PR info, deduplicated per directory and cached for 60 seconds) |
|
||||
| `page_size` | 1–100, default 50 |
|
||||
| `page_token` | Pagination token from the previous page |
|
||||
|
||||
Every response item carries the `workspace`, `meta`, and `activity` groups, plus `git` when `include=git`. The page token binds the first page's query conditions; changing them mid-pagination returns `40922`.
|
||||
|
||||
## WebSocket protocol
|
||||
|
||||
### Connect
|
||||
|
||||
The only endpoint is `ws://<host>:<port>/api/v1/ws`; authentication happens at the upgrade request (see [Authentication](#authentication) above). Once connected, the server immediately sends `server_hello`:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "server_hello",
|
||||
"timestamp": "2026-01-01T00:00:00.000Z",
|
||||
"payload": {
|
||||
"ws_connection_id": "conn_01JZX4...",
|
||||
"protocol_version": 2,
|
||||
"max_event_buffer_size": 1000,
|
||||
"capabilities": { "event_batching": false, "compression": false }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that the server never sends heartbeats and never disconnects an idle connection — keepalive and reconnection are the client's job.
|
||||
|
||||
### Control frames
|
||||
|
||||
Clients send JSON frames `{ "type", "id"?, "payload" }`; every request frame gets an acknowledgement `{ "type": "ack", "id", "code", "msg", "payload" }`, where `code` 0 means success.
|
||||
|
||||
| Frame | payload | Description |
|
||||
| --- | --- | --- |
|
||||
| `subscribe` | `{ session_ids, cursors?, agent_filter? }` | Subscribe to session events; with `cursors` (per-session `{seq, epoch}`) the server replays missed durable events |
|
||||
| `unsubscribe` | `{ session_ids }` | Drop session subscriptions |
|
||||
| `subscribe_v2` | `{ session_id, transcript, transcript_since? }` | Subscribe to transcript streams (the only transcript channel); `transcript` sets per-agent grades |
|
||||
| `unsubscribe_v2` | `{ session_id, agent_ids? }` | Detach transcript streams; omitting `agent_ids` means the whole session |
|
||||
| `watch_fs_add` / `watch_fs_remove` | `{ session_id, paths, recursive? }` | Subscribe to / unsubscribe from file-change notifications (`event.fs.changed`) |
|
||||
| `client_hello` | `{ client_id }` | Handshake frame; the remaining fields are legacy compatibility |
|
||||
|
||||
### Events
|
||||
|
||||
Event frames look like `{ "type", "seq", "epoch"?, "volatile"?, "offset"?, "session_id"?, "timestamp", "payload" }`, where `type` is the event type itself. Two delivery scopes:
|
||||
|
||||
- **Global events**: sent to every established connection, no subscription needed — `session.meta.updated`, `event.session.created`, `event.session.work_changed`, `event.session.status_changed`, `event.workspace.*`, `event.config.*`.
|
||||
- **Session events**: sent only to connections subscribed to that session, subject to `agent_filter`. Main families:
|
||||
|
||||
| Family | Main events |
|
||||
| --- | --- |
|
||||
| Turns | `turn.started`, `turn.ended`, `turn.step.started` / `completed` / `interrupted` / `retrying` |
|
||||
| Streaming text | `assistant.delta`, `thinking.delta` (carry `offset` for alignment) |
|
||||
| Tool calls | `tool.call.started`, `tool.call.delta`, `tool.progress`, `tool.result` |
|
||||
| Interactions | `event.approval.requested` / `resolved`, `event.question.requested` / `answered` / `dismissed` |
|
||||
| Subagents | `subagent.spawned` / `started` / `suspended` / `completed` / `failed` |
|
||||
| Background | `task.started` / `terminated`, `shell.started` / `output` / `completed` |
|
||||
| Misc | `compaction.*`, `skill.activated`, `goal.updated`, `prompt.*`, `error`, `warning` |
|
||||
|
||||
Events also split into durable and volatile: durable events carry a strictly increasing `seq`, are journaled, and can be replayed; volatile events (the `*.delta` family, `tool.progress`, `shell.*`, and similar) are marked `volatile: true` and never replayed. When consuming a volatile text stream, compare `offset` (the cumulative character offset within the turn) against your locally accumulated text: below the local length means a duplicate frame; above means a gap that needs snapshot recovery.
|
||||
|
||||
### Reconnect and recovery
|
||||
|
||||
After reconnecting, pass each session's last applied `{seq, epoch}` in `subscribe`'s `cursors`; the server replays the gap. If you fall more than the buffer (1000 events) behind, or the cursor is no longer valid, you get `resync_required` instead. In that case, call `GET /api/v1/sessions/{session_id}/snapshot` for a full snapshot (with `as_of_seq` and `epoch`), then subscribe again with the fresh cursor.
|
||||
|
||||
### Transcript protocol
|
||||
|
||||
`subscribe_v2`'s `transcript` field sets a per-agent grade: `off` / `turn` / `block` / `delta` (the `"*"` key sets the default grade), with higher grades pushing finer detail. An agent with a non-`off` grade receives two frame types: `transcript.reset` (a baseline snapshot; history pages in over REST) and `transcript.ops` (incremental op batches with a per-agent strictly increasing `seq`). The agent's legacy events are suppressed on that connection and carried by transcript frames instead. After a disconnect, resume with `transcript_since`; when the server's op journal cannot cover the gap (REST catch-up returns `complete: false`), do a full refresh. The REST counterparts are `GET .../transcript` (turn-paged) and `GET .../transcript/ops?since_seq=` (op-batch catch-up).
|
||||
|
||||
## Binary and streaming endpoints
|
||||
|
||||
The following endpoints stream binary bodies instead of a JSON payload. Their HTTP capabilities differ per endpoint:
|
||||
|
||||
| Method and path | Description | Range (206) | ETag / 304 |
|
||||
| --- | --- | --- | --- |
|
||||
| `GET /api/v1/files/{file_id}` | Download an uploaded file | Yes | No (sends an `etag` header but ignores `If-None-Match`) |
|
||||
| `GET /api/v1/sessions/{session_id}/fs/{path}:download` | Download a session workspace file | Yes | Yes |
|
||||
| `GET /api/v1/fs:content` | Raw bytes of any host file (gated only by the token — be careful when exposing the port) | Yes | Yes |
|
||||
| `POST /api/v1/sessions/{session_id}/export` | Export the session with diagnostics (zip stream) | No | No |
|
||||
|
||||
Error semantics differ as well: `GET /api/v1/files/{file_id}` answers lookup and storage failures with real 404 / 500 statuses (parameter validation still uses the HTTP 200 envelope), while the other three report every failure through the standard [response envelope](#response-envelope) — clients must keep checking the envelope `code` on those endpoints.
|
||||
|
||||
## Next steps
|
||||
|
||||
- [Local server and API](../guides/server.md) — startup, authentication, and the end-to-end calling flow
|
||||
- [kimi command](./kimi-command.md#kimi-web) — all `kimi web` command-line options
|
||||
|
|
@ -121,6 +121,7 @@ kimi
|
|||
| 环境变量 | 用途 | 合法值 |
|
||||
| --- | --- | --- |
|
||||
| `KIMI_DISABLE_TELEMETRY` | 关闭匿名遥测上报 | `1`、`true`、`yes`、`y`(不区分大小写) |
|
||||
| `KIMI_CODE_PASSWORD` | 为 `kimi web` 本地服务设置并列鉴权密码,与 bearer token 同时有效;把服务绑定到非本机地址时建议设置,见[本地服务与 API](../guides/server.md#鉴权) | 任意非空字符串;未设置时仅 token 有效 |
|
||||
| `KIMI_CODE_BACKGROUND_KEEP_ALIVE_ON_EXIT` | 会话关闭时是否保留后台任务,优先级高于 `config.toml`。默认会在退出时停止后台任务 | 真值:`1`/`true`/`yes`/`on`;假值:`0`/`false`/`no`/`off` |
|
||||
| `KIMI_CODE_BACKGROUND_MAX_RUNNING_TASKS` | 同时运行的后台任务数上限,优先级高于 `config.toml` 的 `[background] max_running_tasks`(不设置表示无上限) | 正整数;非法值被忽略 |
|
||||
| `KIMI_IMAGE_MAX_EDGE_PX` | 图片压缩的最长边上限(像素),优先级高于 `config.toml` 的 `[image] max_edge_px`(默认 `2000`) | 正整数;非法值被忽略 |
|
||||
|
|
|
|||
116
docs/zh/guides/server.md
Normal file
116
docs/zh/guides/server.md
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
# 本地服务与 API
|
||||
|
||||
Kimi Code CLI 内置一个本地服务:运行 `kimi web` 会在前台启动一个进程,同时挂载浏览器里的 web UI、REST API(`/api/v1`)和 WebSocket 事件流(`/api/v1/ws`)。web UI 用于在浏览器里直接使用 Kimi Code;REST 与 WebSocket API 面向脚本和第三方工具,可以用代码创建会话、提交提示词、实时跟进执行过程——它们与 TUI、web UI 读写同一份会话数据。
|
||||
|
||||
> 开始前请确认 Kimi Code CLI 已安装并处于可用状态——完成 `/login` 登录(TUI 内或 `kimi login`),或已在 `config.toml` 配置供应商。服务与 CLI 共享同一份登录态与配置,无需为服务单独准备凭证。
|
||||
|
||||
::: warning 注意
|
||||
本页介绍的 REST 与 WebSocket API 为实验性特性:不保证接口稳定性,端点、字段与事件类型可能随版本随时更改。集成时请以当前版本服务的 `/openapi.json` 与 `/asyncapi.json` 为准。
|
||||
:::
|
||||
|
||||
## 启动服务
|
||||
|
||||
```sh
|
||||
kimi web # 前台运行服务并打开浏览器
|
||||
kimi web --no-open # 只运行服务,不打开浏览器
|
||||
kimi web --port 58628 # 指定绑定端口
|
||||
```
|
||||
|
||||
服务默认绑定 `127.0.0.1:58627`(仅本机访问);端口被占用时自动 +1 重试,同一台机器因此可以并存多个实例,每个实例登记在 `~/.kimi-code/server/instances/` 下。启动横幅会打印访问地址和明文 token:
|
||||
|
||||
```text
|
||||
Local: http://127.0.0.1:58627/#token=...
|
||||
Token: ...
|
||||
Stop: Ctrl+C
|
||||
```
|
||||
|
||||
服务在前台运行,按 `Ctrl-C` 干净退出。`--host`、`--log-level` 等完整选项见 [kimi 命令参考](../reference/kimi-command.md#kimi-web)。
|
||||
|
||||
## 鉴权
|
||||
|
||||
所有 `/api/*` 接口都要求 bearer token(持有者令牌:任何携带该字符串的请求都被视为已授权)。token 在首次启动服务时生成,持久化在 `~/.kimi-code/server.token`(文件权限 0600),跨重启复用。
|
||||
|
||||
按客户端类型选择携带方式:
|
||||
|
||||
- **REST**:请求头 `Authorization: Bearer <token>`。
|
||||
- **web UI**:启动横幅里的地址自带 `#token=` 片段,浏览器打开后自动完成登录;该片段不会发送到服务端。
|
||||
- **WebSocket**:能自定义请求头的客户端用 `Authorization: Bearer`;浏览器等不能自定义头的客户端改用子协议(WebSocket 握手时声明的协议名)`kimi-code.bearer.<token>`。
|
||||
|
||||
token 泄露时运行 `kimi web rotate-token` 轮换:新 token 立即写入 `server.token`,旧 token 即刻失效,正在运行的实例无需重启。
|
||||
|
||||
如果把服务绑定到非本机地址(`--host`),建议额外设置 `KIMI_CODE_PASSWORD` 环境变量作为并列凭证;此时服务端会对鉴权失败自动限流。
|
||||
|
||||
::: danger 警告
|
||||
`--dangerous-bypass-auth` 会彻底关闭鉴权,任何能访问该端口的人都能控制你的会话、文件系统和 shell。仅在可信网络或自有鉴权代理之后使用,详见 [kimi 命令参考](../reference/kimi-command.md#kimi-web)。
|
||||
:::
|
||||
|
||||
## 用 API 驱动一个会话
|
||||
|
||||
下面用 curl 走一遍最小流程:确认服务状态 → 创建会话 → 订阅事件 → 提交提示词 → 回读历史。示例假设服务跑在默认地址,token 已存入 shell 变量 `TOKEN`。
|
||||
|
||||
1. 确认服务状态:
|
||||
|
||||
```sh
|
||||
curl -s -H "Authorization: Bearer $TOKEN" http://127.0.0.1:58627/api/v1/meta
|
||||
```
|
||||
|
||||
所有 JSON 响应都包在统一信封里——`{ "code": 0, "msg": "success", "data": ..., "request_id": "..." }`,业务结果以 `code` 为准(`0` 表示成功),HTTP 状态码只表达传输层结果。
|
||||
|
||||
2. 创建会话,`metadata.cwd` 指定工作目录:
|
||||
|
||||
```sh
|
||||
curl -s -X POST http://127.0.0.1:58627/api/v1/sessions \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"metadata": {"cwd": "/path/to/project"}}'
|
||||
```
|
||||
|
||||
返回的 `data.id`(形如 `session_...`)就是后续所有请求要用的会话 id。
|
||||
|
||||
3. 连接 WebSocket 并订阅会话事件。任何 WebSocket 客户端都可以;下面是一个零依赖的 Node.js 脚本(Node.js 22+ 内置 `WebSocket` 客户端):
|
||||
|
||||
```js
|
||||
// subscribe.mjs —— 用法:TOKEN=... node subscribe.mjs session_...
|
||||
const ws = new WebSocket('ws://127.0.0.1:58627/api/v1/ws', [
|
||||
`kimi-code.bearer.${process.env.TOKEN}`,
|
||||
]);
|
||||
ws.onmessage = (e) => console.log(e.data);
|
||||
ws.onopen = () =>
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
type: 'subscribe',
|
||||
id: '1',
|
||||
payload: { session_ids: [process.argv[2]] },
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
4. 提交提示词:
|
||||
|
||||
```sh
|
||||
curl -s -X POST http://127.0.0.1:58627/api/v1/sessions/<session_id>/prompts \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"content": [{"type": "text", "text": "用一句话介绍这个仓库"}]}'
|
||||
```
|
||||
|
||||
订阅端会依次看到 `turn.started`(轮次开始)→ `assistant.delta`(流式文本增量)→ 发生工具调用时的 `tool.call.started` / `tool.result` → `turn.ended`(轮次结束)。
|
||||
|
||||
5. 随时可以用 REST 回读历史消息:
|
||||
|
||||
```sh
|
||||
curl -s -H "Authorization: Bearer $TOKEN" \
|
||||
"http://127.0.0.1:58627/api/v1/sessions/<session_id>/messages?page_size=20"
|
||||
```
|
||||
|
||||
## 在线规范文档
|
||||
|
||||
服务运行时会自描述两份规范文档,同样需要 bearer token:
|
||||
|
||||
- `GET /openapi.json` — REST API 的 OpenAPI 文档,含每个端点的请求 / 响应 schema,可直接导入 Swagger UI、Postman 等工具。
|
||||
- `GET /asyncapi.json` — WebSocket 协议的 AsyncAPI 文档,覆盖控制帧与事件类型。
|
||||
|
||||
## 下一步
|
||||
|
||||
- [服务 API](../reference/server-api.md) — REST 端点全集、错误码、WebSocket 事件与转录协议
|
||||
- [kimi 命令](../reference/kimi-command.md#kimi-web) — `kimi web` 的全部命令行选项
|
||||
|
|
@ -157,7 +157,7 @@ kimi acp
|
|||
|
||||
在当前终端前台运行本地 Kimi 服务 —— 同一个进程同时挂载 REST + WebSocket API 与 web UI —— 并在服务就绪后用默认浏览器打开 web UI。命令会一直挂在终端,直到收到 `SIGINT` / `SIGTERM`(如 `Ctrl-C`)时干净退出。
|
||||
|
||||
服务运行时,`GET /openapi.json` 会返回 REST OpenAPI 文档,`GET /asyncapi.json` 会返回本地 WebSocket 协议的 AsyncAPI 文档。
|
||||
服务运行时,`GET /openapi.json` 会返回 REST OpenAPI 文档,`GET /asyncapi.json` 会返回本地 WebSocket 协议的 AsyncAPI 文档。用 API 驱动会话的完整流程见[本地服务与 API](../guides/server.md),协议细节见[服务 API](./server-api.md)。
|
||||
|
||||
```sh
|
||||
kimi web # 前台运行服务并打开浏览器
|
||||
|
|
|
|||
344
docs/zh/reference/server-api.md
Normal file
344
docs/zh/reference/server-api.md
Normal file
|
|
@ -0,0 +1,344 @@
|
|||
# 服务 API
|
||||
|
||||
`kimi web` 启动的本地服务暴露两组程序化接口:REST API(`/api/v1`,另有 `/api/v2/sessions`)和 WebSocket 事件流(`/api/v1/ws`)。本页是这两组接口的协议参考;服务的启动方式与命令行选项见 [kimi 命令](./kimi-command.md#kimi-web),端到端的上手流程见[本地服务与 API](../guides/server.md)。
|
||||
|
||||
每个端点的完整请求 / 响应 schema 以服务自描述的规范文档为准:`GET /openapi.json`(OpenAPI)与 `GET /asyncapi.json`(AsyncAPI),两者都需要鉴权。
|
||||
|
||||
::: warning 注意
|
||||
本页描述的 REST 与 WebSocket API 为实验性特性:不保证接口稳定性,端点、字段与事件类型可能随版本随时更改。集成时请以当前版本服务的 `/openapi.json` 与 `/asyncapi.json` 为准。
|
||||
:::
|
||||
|
||||
## 基础约定
|
||||
|
||||
### 地址
|
||||
|
||||
默认地址 `http://127.0.0.1:58627`;端口被占用时自动 +1 重试(至多 100 次),可用 `--port` / `--host` 修改。同一 home 目录可并存多个实例,运行中的实例登记在 `~/.kimi-code/server/instances/`。
|
||||
|
||||
### 鉴权
|
||||
|
||||
除以下例外,所有 `/api/*` 路径(含 `/openapi.json` 与 `/asyncapi.json`)都要求 bearer token:
|
||||
|
||||
- `OPTIONS` 预检请求
|
||||
- `GET /api/v1/healthz`(探活)
|
||||
- 静态 web 资源(非 `/api/` 路径)
|
||||
|
||||
携带方式:REST 用 `Authorization: Bearer <token>` 请求头;WebSocket 升级请求可用同一请求头,或子协议 `kimi-code.bearer.<token>`。token 的生成与轮换见[本地服务与 API:鉴权](../guides/server.md#鉴权)。
|
||||
|
||||
鉴权失败返回 HTTP 401,信封 `code` 为 `40101`。在非 loopback 绑定上,同一来源 60 秒内鉴权失败 10 次会被封禁 60 秒,期间一律返回 HTTP 429(`code` 为 `42901`)。
|
||||
|
||||
### 响应信封
|
||||
|
||||
所有 JSON 响应统一包在信封里:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": {},
|
||||
"request_id": "01JZX4A6E7M8V0R3Q0N2K2M5Q9"
|
||||
}
|
||||
```
|
||||
|
||||
- `code`:业务结果,`0` 表示成功;错误码分段见下文。
|
||||
- `data`:成功时的业务数据。注意部分「错误」信封也携带非空 `data`——例如重复解决审批返回 `40902` 且 `data.resolved` 为 `false`——客户端应先判 `code` 再看 `data`。
|
||||
- `request_id`:本次请求的 ULID;客户端可用 `X-Request-Id` 请求头指定,非法值会被服务端重新生成。
|
||||
|
||||
HTTP 状态码几乎总是 200,业务结果以 `code` 为准。例外情况:
|
||||
|
||||
| 场景 | HTTP 状态 |
|
||||
| --- | --- |
|
||||
| 鉴权失败 / 触发限流 | 401 / 429 |
|
||||
| 创建供应商、导入供应商目录成功 | 201 |
|
||||
| 删除供应商成功 | 204 |
|
||||
| 二进制与流式端点 | 支持时返回 206(Range 分段)/ 304(ETag 未变),各端点能力不同,详见「[二进制与流式端点](#二进制与流式端点)」 |
|
||||
| `GET /api/v1/files/{file_id}` 下载错误 | 真实 404 / 500(响应体仍为信封) |
|
||||
|
||||
其中 201 的响应体仍是标准信封(`code` 为 `0`),只是状态行遵循 REST 的资源创建习惯;204 按 HTTP 语义没有响应体,删除成功以状态码本身为准。
|
||||
|
||||
### 错误码
|
||||
|
||||
错误码按段位分组:
|
||||
|
||||
| 段位 | 含义 | 示例 |
|
||||
| --- | --- | --- |
|
||||
| `0` | 成功 | |
|
||||
| `400xx` | 请求参数错误 | `40001` 校验失败(`details` 逐字段说明)、`40003` 供应商由 OAuth 托管 |
|
||||
| `401xx` | 鉴权与就绪状态 | `40101` 未授权、`40110` 未配置供应商、`40113` 模型未解析 |
|
||||
| `404xx` | 资源不存在 | `40401` 会话、`40408` MCP 服务、`40409` 文件路径 |
|
||||
| `409xx` | 状态冲突 | `40901` 会话忙、`40902` 审批已解决、`40922` 分页条件与 `page_token` 不符 |
|
||||
| `410xx` | 资源已过期 | `41001` 审批超时、`41002` 提问超时、`41003` 临时文件过期 |
|
||||
| `413xx` | 体积或边界超限 | `41302` 读取文件超 10 MB、`41304` 路径越出会话目录 |
|
||||
| `429xx` | 限流 | `42901` 鉴权失败封禁、`42902` 文件监听数超限 |
|
||||
| `500xx` | 服务端内部错误 | `50001` 未捕获异常、`50003` 持久化失败 |
|
||||
| `6xxxx` / `7xxxx` / `8xxxx` | 工具运行时 / LLM 供应商 / MCP 透传错误,`msg` 保留上游原文 | |
|
||||
|
||||
### 分页
|
||||
|
||||
列表端点有两种分页风格:
|
||||
|
||||
- **游标式**:`before_id` / `after_id`(互斥)加 `page_size`(1–100),响应为 `{ items, has_more }`。用于会话列表、消息列表、转录等。
|
||||
- **`page_token`**:不透明令牌(内部绑定了查询条件指纹),用于 `POST /api/v1/search` 与 `GET /api/v2/sessions`。翻页途中改变任何查询条件会使令牌失效:v2 返回 `40922`,search 返回 `40001`。
|
||||
|
||||
## REST 端点
|
||||
|
||||
按资源分组列出端点。路径里的 `:{action}` 是动作后缀约定——对单个资源 POST 到 `路径:动作` 执行非 CRUD 操作(如会话的 `:fork`、`:archive`)。
|
||||
|
||||
### 服务与元信息
|
||||
|
||||
| 方法与路径 | 说明 |
|
||||
| --- | --- |
|
||||
| `GET /api/v1/healthz` | 探活,免鉴权 |
|
||||
| `GET /api/v1/meta` | 服务版本、能力集、`server_id`、实验开关等 |
|
||||
| `POST /api/v1/shutdown` | 优雅退出(先回 200 再关闭);仅 loopback 绑定时挂载 |
|
||||
|
||||
### 登录与用量
|
||||
|
||||
| 方法与路径 | 说明 |
|
||||
| --- | --- |
|
||||
| `GET /api/v1/auth` | 登录就绪状态快照 |
|
||||
| `POST /api/v1/oauth/login` | 发起 OAuth device-code 登录流程 |
|
||||
| `GET /api/v1/oauth/login` | 轮询登录流程状态 |
|
||||
| `DELETE /api/v1/oauth/login` | 取消进行中的登录流程 |
|
||||
| `POST /api/v1/oauth/logout` | 登出托管供应商 |
|
||||
| `GET /api/v1/oauth/usage` | 查询套餐用量与限额 |
|
||||
| `GET /api/v1/oauth/userinfo` | 查询账号资料 |
|
||||
|
||||
### 配置
|
||||
|
||||
| 方法与路径 | 说明 |
|
||||
| --- | --- |
|
||||
| `GET /api/v1/config` | 读取全局配置(密钥字段脱敏) |
|
||||
| `POST /api/v1/config` | 合并式更新配置,并广播 `event.config.changed` |
|
||||
|
||||
### 模型与供应商
|
||||
|
||||
| 方法与路径 | 说明 |
|
||||
| --- | --- |
|
||||
| `GET /api/v1/models` | 列出已配置的模型别名 |
|
||||
| `POST /api/v1/models/{model_id}:set_default` | 设置全局默认模型 |
|
||||
| `GET /api/v1/providers` | 列出供应商 |
|
||||
| `POST /api/v1/providers` | 创建供应商(201) |
|
||||
| `GET /api/v1/providers/{provider_id}` | 读取供应商(含已存密钥) |
|
||||
| `PUT /api/v1/providers/{provider_id}` | 整体替换供应商配置 |
|
||||
| `DELETE /api/v1/providers/{provider_id}` | 删除供应商(204) |
|
||||
| `POST /api/v1/providers/{provider_id}:refresh` | 刷新该供应商的模型元数据 |
|
||||
| `POST /api/v1/providers:{action}` | 集合级动作:`refresh` / `refresh_oauth` / `import_catalog` / `import_registry` |
|
||||
| `GET /api/v1/catalog/providers` | 浏览 models.dev 目录(服务端代理) |
|
||||
| `GET /api/v1/catalog/providers/{catalog_id}` | 读取目录中单个条目 |
|
||||
|
||||
### 会话
|
||||
|
||||
| 方法与路径 | 说明 |
|
||||
| --- | --- |
|
||||
| `POST /api/v1/sessions` | 创建会话(需 `workspace_id` 或 `metadata.cwd`) |
|
||||
| `GET /api/v1/sessions` | 列出会话,游标分页,支持 `busy` / `archived_only` 等过滤 |
|
||||
| `GET /api/v1/sessions/{session_id}` | 读取单个会话 |
|
||||
| `GET /api/v1/sessions/{session_id}/profile` | 读取会话档案 |
|
||||
| `POST /api/v1/sessions/{session_id}/profile` | 更新标题、元数据、agent 配置 |
|
||||
| `POST /api/v1/sessions/{session_id}:{action}` | 会话动作:`fork` / `compact` / `undo` / `abort` / `btw` / `archive` / `restore` |
|
||||
| `GET /api/v1/sessions/{session_id}/children` | 列出子会话 |
|
||||
| `POST /api/v1/sessions/{session_id}/children` | 创建子会话(fork 并打标) |
|
||||
| `GET /api/v1/sessions/{session_id}/status` | 实时状态汇总 |
|
||||
| `GET /api/v1/sessions/{session_id}/goal` | 当前目标快照(无则 `null`) |
|
||||
| `GET /api/v1/sessions/{session_id}/warnings` | 会话级告警 |
|
||||
| `POST /api/v1/sessions/{session_id}/export` | 导出会话与诊断信息(zip 流,不走信封) |
|
||||
| `GET /api/v1/sessions/{session_id}/snapshot` | 客户端重建用全量快照(含 `as_of_seq` 与 `epoch`) |
|
||||
|
||||
### 消息与转录
|
||||
|
||||
| 方法与路径 | 说明 |
|
||||
| --- | --- |
|
||||
| `GET /api/v1/sessions/{session_id}/messages` | 消息分页(`before_id` / `after_id` / `role`) |
|
||||
| `GET /api/v1/sessions/{session_id}/messages/{message_id}` | 读取单条消息 |
|
||||
| `GET /api/v1/sessions/{session_id}/transcript` | 转录按轮次分页(需 `agent_id`),全局状态不分页随响应返回 |
|
||||
| `GET /api/v1/sessions/{session_id}/transcript/ops` | 转录批次补漏(`since_seq`),`complete: false` 时需全量刷新 |
|
||||
| `GET /api/v1/sessions/{session_id}/transcript/user-messages` | 各轮次的用户输入,不分页 |
|
||||
| `GET /api/v1/sessions/{session_id}/transcript/plan` | ExitPlanMode 计划内容、路径与审阅结果 |
|
||||
|
||||
### 提示词
|
||||
|
||||
| 方法与路径 | 说明 |
|
||||
| --- | --- |
|
||||
| `GET /api/v1/sessions/{session_id}/prompts` | 进行中与排队中的提示词 |
|
||||
| `POST /api/v1/sessions/{session_id}/prompts` | 提交提示词(内容块数组,可带模型 / 权限模式等覆盖) |
|
||||
| `POST /api/v1/sessions/{session_id}/prompts:steer` | 把排队的提示词插入当前轮次 |
|
||||
| `POST /api/v1/sessions/{session_id}/prompts/{prompt_id}:abort` | 中止进行中的提示词 |
|
||||
| `POST /api/v1/sessions/{session_id}/prompts/{prompt_id}:steer` | 插入单个排队提示词 |
|
||||
|
||||
### 审批与提问
|
||||
|
||||
| 方法与路径 | 说明 |
|
||||
| --- | --- |
|
||||
| `GET /api/v1/sessions/{session_id}/approvals` | 列出审批请求(可按 `status=pending` 过滤) |
|
||||
| `POST /api/v1/sessions/{session_id}/approvals/{approval_id}` | 答复审批 |
|
||||
| `GET /api/v1/sessions/{session_id}/questions` | 列出提问 |
|
||||
| `POST /api/v1/sessions/{session_id}/questions/{question_id}` | 回答提问 |
|
||||
| `POST /api/v1/sessions/{session_id}/questions/{question_id}:dismiss` | 忽略提问 |
|
||||
|
||||
### 后台任务
|
||||
|
||||
| 方法与路径 | 说明 |
|
||||
| --- | --- |
|
||||
| `GET /api/v1/sessions/{session_id}/tasks` | 列出后台任务 |
|
||||
| `GET /api/v1/sessions/{session_id}/tasks/{task_id}` | 读取任务(可选输出预览) |
|
||||
| `POST /api/v1/sessions/{session_id}/tasks/{task_id}:cancel` | 取消任务 |
|
||||
|
||||
### 技能、工具与 MCP
|
||||
|
||||
| 方法与路径 | 说明 |
|
||||
| --- | --- |
|
||||
| `GET /api/v1/sessions/{session_id}/skills` | 会话级技能目录 |
|
||||
| `GET /api/v1/workspaces/{workspace_id}/skills` | 无会话的工作区技能目录 |
|
||||
| `POST /api/v1/sessions/{session_id}/skills/{skill_name}:activate` | 激活技能(开启一个轮次) |
|
||||
| `GET /api/v1/tools` | 列出当前生效 agent 的工具 |
|
||||
| `GET /api/v1/mcp/servers` | 列出 MCP 服务 |
|
||||
| `POST /api/v1/mcp/servers/{mcp_server_id}:restart` | 重启 MCP 服务 |
|
||||
|
||||
### 终端
|
||||
|
||||
PTY 终端接口,仅 loopback 绑定时挂载。
|
||||
|
||||
| 方法与路径 | 说明 |
|
||||
| --- | --- |
|
||||
| `GET /api/v1/sessions/{session_id}/terminals` | 列出终端 |
|
||||
| `POST /api/v1/sessions/{session_id}/terminals` | 创建终端 |
|
||||
| `GET /api/v1/sessions/{session_id}/terminals/{terminal_id}` | 读取终端(含回滚缓冲) |
|
||||
| `POST /api/v1/sessions/{session_id}/terminals/{terminal_id}:close` | 关闭终端 |
|
||||
|
||||
### 工作区
|
||||
|
||||
| 方法与路径 | 说明 |
|
||||
| --- | --- |
|
||||
| `GET /api/v1/workspaces` | 列出已注册工作区 |
|
||||
| `POST /api/v1/workspaces` | 注册工作区(按根路径幂等) |
|
||||
| `PATCH /api/v1/workspaces/{workspace_id}` | 重命名 |
|
||||
| `DELETE /api/v1/workspaces/{workspace_id}` | 注销(保留磁盘内容) |
|
||||
| `GET /api/v1/workspaces/{workspace_id}/trust` | 读取信任状态 |
|
||||
| `POST /api/v1/workspaces/{workspace_id}/trust` | 授予信任 |
|
||||
| `POST /api/v1/workspaces/{workspace_id}/untrust` | 撤销信任 |
|
||||
|
||||
### 文件系统
|
||||
|
||||
会话内文件操作为 `POST /api/v1/sessions/{session_id}/fs:{action}`,动作包括 `list` / `read` / `list_many` / `stat` / `stat_many` / `mkdir` / `search` / `grep` / `git_status` / `diff` / `open` / `open-in` / `reveal`,请求体为 JSON。另有:
|
||||
|
||||
| 方法与路径 | 说明 |
|
||||
| --- | --- |
|
||||
| `POST /api/v1/workspace/fs:search` | 无会话的工作区搜索(body 携带工作区引用) |
|
||||
| `GET /api/v1/sessions/{session_id}/fs/{path}:download` | 下载会话文件(二进制,见下文) |
|
||||
| `GET /api/v1/fs:browse` | 列出本机目录(文件夹选择器用) |
|
||||
| `GET /api/v1/fs:home` | 用户主目录与最近工作区 |
|
||||
| `GET /api/v1/fs:content` | 读取本机任意文件原始字节(仅受 token 保护,谨慎暴露端口) |
|
||||
| `POST /api/v1/fs:mkdir` | 按绝对路径创建目录 |
|
||||
|
||||
### 文件上传
|
||||
|
||||
| 方法与路径 | 说明 |
|
||||
| --- | --- |
|
||||
| `POST /api/v1/files` | multipart 上传(字段 `file`,可选 `name`、`expires_in_sec`),返回文件元信息 |
|
||||
| `GET /api/v1/files/{file_id}` | 下载(二进制,错误用真实 HTTP 状态码) |
|
||||
| `DELETE /api/v1/files/{file_id}` | 删除 |
|
||||
|
||||
### 全局搜索与其他
|
||||
|
||||
| 方法与路径 | 说明 |
|
||||
| --- | --- |
|
||||
| `POST /api/v1/search` | 跨会话全文搜索,`mode` 为 `terms`(默认)或 `literal`(精确子串),`page_token` 分页 |
|
||||
| `GET /api/v1/connections` | 列出当前在线的 WebSocket 连接 |
|
||||
| `GET /api/v2/sessions` | 新一代会话列表,见下节 |
|
||||
| `/api/v1/debug/*` | 反射式调试 RPC,仅 `--debug-endpoints` 且 loopback 时挂载,不属于稳定协议 |
|
||||
|
||||
### `GET /api/v2/sessions`
|
||||
|
||||
面向列表页的新一代会话查询,筛选、排序、字段组都在查询参数里:
|
||||
|
||||
| 参数 | 说明 |
|
||||
| --- | --- |
|
||||
| `workspace.id` | 按工作区过滤,可重复 |
|
||||
| `activity.status` | 按活动状态过滤:`running` / `approval` / `question` / `failed` / `idle`,可重复 |
|
||||
| `meta.updated_after` | 只看该时间(epoch 毫秒)之后更新过的会话 |
|
||||
| `meta.archived` | `true` / `false`(默认)/ `all` |
|
||||
| `sort` | `meta.updated_at_desc`(默认)/ `meta.updated_at_asc` / `meta.created_at_desc` |
|
||||
| `include` | 逗号分隔的附加字段组;目前支持 `git`(分支与 PR 信息,按目录去重并缓存 60 秒) |
|
||||
| `page_size` | 1–100,默认 50 |
|
||||
| `page_token` | 上一页返回的翻页令牌 |
|
||||
|
||||
响应每项固定包含 `workspace`、`meta`、`activity` 三组,`include=git` 时附加 `git` 组。翻页令牌绑定首页查询条件,中途改条件返回 `40922`。
|
||||
|
||||
## WebSocket 协议
|
||||
|
||||
### 建立连接
|
||||
|
||||
唯一端点是 `ws://<host>:<port>/api/v1/ws`,升级请求即完成鉴权(方式见上文「鉴权」)。连接建立后服务端立即发送 `server_hello`:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "server_hello",
|
||||
"timestamp": "2026-01-01T00:00:00.000Z",
|
||||
"payload": {
|
||||
"ws_connection_id": "conn_01JZX4...",
|
||||
"protocol_version": 2,
|
||||
"max_event_buffer_size": 1000,
|
||||
"capabilities": { "event_batching": false, "compression": false }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
注意服务端不发送心跳,也不会主动断开空闲连接——保活与重连由客户端自己负责。
|
||||
|
||||
### 控制帧
|
||||
|
||||
客户端发送 JSON 帧 `{ "type", "id"?, "payload" }`;每个请求帧都会收到应答 `{ "type": "ack", "id", "code", "msg", "payload" }`,`code` 为 `0` 表示成功。
|
||||
|
||||
| 帧 | payload | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `subscribe` | `{ session_ids, cursors?, agent_filter? }` | 订阅会话事件;带 `cursors`(每会话 `{seq, epoch}`)时回放错过的持久事件 |
|
||||
| `unsubscribe` | `{ session_ids }` | 取消会话订阅 |
|
||||
| `subscribe_v2` | `{ session_id, transcript, transcript_since? }` | 订阅转录流(唯一的转录订阅通道),`transcript` 按 agent 指定粒度 |
|
||||
| `unsubscribe_v2` | `{ session_id, agent_ids? }` | 退订转录流;省略 `agent_ids` 表示整个会话 |
|
||||
| `watch_fs_add` / `watch_fs_remove` | `{ session_id, paths, recursive? }` | 订阅 / 取消文件变更通知(`event.fs.changed`) |
|
||||
| `client_hello` | `{ client_id }` | 握手帧,其余字段为遗留兼容 |
|
||||
|
||||
### 事件
|
||||
|
||||
事件帧形状为 `{ "type", "seq", "epoch"?, "volatile"?, "offset"?, "session_id"?, "timestamp", "payload" }`,`type` 即事件类型。按投递范围分两类:
|
||||
|
||||
- **全局事件**:发送到每个已建立连接,无需订阅——`session.meta.updated`、`event.session.created`、`event.session.work_changed`、`event.session.status_changed`、`event.workspace.*`、`event.config.*`。
|
||||
- **会话事件**:只发给订阅了该会话的连接,受 `agent_filter` 过滤。主要事件族:
|
||||
|
||||
| 事件族 | 主要事件 |
|
||||
| --- | --- |
|
||||
| 轮次 | `turn.started`、`turn.ended`、`turn.step.started` / `completed` / `interrupted` / `retrying` |
|
||||
| 流式文本 | `assistant.delta`、`thinking.delta`(带 `offset` 用于对齐) |
|
||||
| 工具调用 | `tool.call.started`、`tool.call.delta`、`tool.progress`、`tool.result` |
|
||||
| 交互 | `event.approval.requested` / `resolved`、`event.question.requested` / `answered` / `dismissed` |
|
||||
| 子 Agent | `subagent.spawned` / `started` / `suspended` / `completed` / `failed` |
|
||||
| 后台 | `task.started` / `terminated`、`shell.started` / `output` / `completed` |
|
||||
| 其他 | `compaction.*`、`skill.activated`、`goal.updated`、`prompt.*`、`error`、`warning` |
|
||||
|
||||
事件另分持久与易失两种:持久事件带严格递增的 `seq`,落盘并可回放;易失事件(各 `*.delta`、`tool.progress`、`shell.*` 等)标 `volatile: true`,不回放。消费易失文本流时用 `offset`(该轮次内的累计字符偏移)与本地已累积文本比对:小于本地长度说明是重复帧,大于说明有缺漏、需走快照恢复。
|
||||
|
||||
### 断线恢复
|
||||
|
||||
重连后在 `subscribe` 的 `cursors` 里带上每个会话最后应用事件的 `{seq, epoch}`,服务端会回放缺口;落后超过缓冲(1000 条)或游标失效时改为收到 `resync_required`。此时调用 `GET /api/v1/sessions/{session_id}/snapshot` 拿全量快照(含 `as_of_seq` 与 `epoch`),再以新游标重新订阅。
|
||||
|
||||
### 转录协议
|
||||
|
||||
`subscribe_v2` 的 `transcript` 按 agent 指定粒度:`off` / `turn` / `block` / `delta`(键 `"*"` 表示默认粒度),粒度越高推送越细。粒度非 `off` 的 agent 走两帧推送:`transcript.reset`(基线快照,历史经 REST 分页回读)和 `transcript.ops`(增量批次,带每个 agent 连续递增的 `seq`);该 agent 的旧式事件在同一连接上被抑制,改由转录帧承载。断线时用 `transcript_since` 续传;服务端批次日志无法覆盖缺口时(REST 补漏返回 `complete: false`)需全量刷新。REST 侧对应 `GET .../transcript`(按轮次分页)与 `GET .../transcript/ops?since_seq=`(批次补漏)。
|
||||
|
||||
## 二进制与流式端点
|
||||
|
||||
以下端点返回二进制流而非 JSON 载荷,各端点的 HTTP 能力并不相同:
|
||||
|
||||
| 方法与路径 | 说明 | Range 分段(206) | ETag / 304 |
|
||||
| --- | --- | --- | --- |
|
||||
| `GET /api/v1/files/{file_id}` | 下载已上传文件 | 支持 | 不支持(会发送 `etag` 头,但不处理 `If-None-Match`) |
|
||||
| `GET /api/v1/sessions/{session_id}/fs/{path}:download` | 下载会话工作区文件 | 支持 | 支持 |
|
||||
| `GET /api/v1/fs:content` | 读取本机任意文件(仅受 token 保护,谨慎暴露端口) | 支持 | 支持 |
|
||||
| `POST /api/v1/sessions/{session_id}/export` | 导出会话与诊断信息(zip 流) | 不支持 | 不支持 |
|
||||
|
||||
错误语义也不相同:`GET /api/v1/files/{file_id}` 对查找和存储失败返回真实 404 / 500 状态码(参数校验失败仍走 HTTP 200 信封),其余三个端点的所有失败都走标准[响应信封](#响应信封)——客户端在这三个端点上仍需检查信封中的 `code`。
|
||||
|
||||
## 下一步
|
||||
|
||||
- [本地服务与 API](../guides/server.md) — 启动、鉴权与端到端调用流程
|
||||
- [kimi 命令](./kimi-command.md#kimi-web) — `kimi web` 的全部命令行选项
|
||||
Loading…
Add table
Add a link
Reference in a new issue