kimi-code/packages/server
qer ec758c747a
fix(web): make uploaded videos play in the chat (#1343)
* feat(media): materialize video uploads to cache and reference by path

- copy TUI video placeholders into the shared cache instead of
  inlining the original source path
- emit <video path="..."> tags so ReadMediaFile / the provider's
  VideoUploader owns upload behavior
- apply the same cache materialization to server prompt video
  submissions, matching the TUI flow
- update TUI unit tests and server e2e test to assert cache-path
  behavior

* fix(web): make uploaded videos play in the chat

Render the server's <video path> tag as a real video and reconcile the echoed user message so the bubble no longer shows raw markup or a duplicate. Serve file downloads with byte-range support and fetch video bytes with the bearer credential into a blob URL, since browsers cannot authorize a <video> src on their own. Also let users click an uploaded image to open it in the preview panel.

* fix(web): use authenticated source for uploaded image previews

openMediaPreview stored the raw getFileUrl as sourceUrl, and FilePreview renders it with a native <img> that sends no Authorization header, so the enlarge action 401'd for uploaded images. When the media carries a fileId, fetch the bytes through the authenticated API client and preview a blob URL instead, revoking it when the preview is replaced or closed.

* fix(web): ignore stale authenticated media fetches

AuthMedia fetches the file bytes asynchronously; when the component is reused with a new fileId before a prior fetch resolves (e.g. queued thumbnails keyed by index), the older response could still create a blob URL and show the previous file. Add a per-request sequence guard (and an unmount guard) so a stale response is discarded and its blob URL revoked instead of being applied.

* fix(web): gate media path tags on file-store id shape

Treating any standalone <video path="..."> text as an uploaded daemon file and stripping the basename into getFileUrl is only valid for server cache files named after the file-store id (f_…). TUI/ReadMediaFile tags use arbitrary cache names like <uuid>-<label>, and older transcripts may point at paths like /tmp/foo.mp4; those produced a broken /files/<basename> request. Only extract a fileId when the basename matches the file-store id shape, otherwise leave the raw tag as text.

* fix(web): invalidate pending media preview on close

Closing an uploaded-image preview before getFileBlob() resolved left previewRequestSeq untouched, so the fetch callback still passed its seq check, created a blob URL, then skipped attaching it because previewFile was already null — leaking up to the file size until another preview opened. Bump previewRequestSeq on close so the in-flight callback bails before creating the blob URL.

* fix(web): defer authenticated media fetch until near viewport

AuthMedia fetched the full image/video into a Blob on mount whenever a fileId was present, bypassing native loading="lazy" and preload="metadata". Opening a session with several historical large video uploads started many full downloads and held all blobs in memory even if the user never scrolled to or played them. Use an IntersectionObserver to defer the fetch until the element nears the viewport.

* fix(web): revoke preview blob when leaving the file panel

Switching to another detail panel only flips detailTarget and never calls closeFilePreview, so an in-flight getFileBlob could still create a blob URL after the file panel hid, and an already-shown blob URL was held until the next file preview. Check detailTarget before creating the blob URL, and reset/revoke the preview when detailTarget leaves 'file'.

---------

Co-authored-by: haozhe.yang <yanghaozhe@moonshot.ai>
2026-07-04 00:36:00 +08:00
..
scratch feat(web): introduce Kimi web app and daemon gateway (#625) 2026-06-17 20:53:46 +08:00
src fix(web): make uploaded videos play in the chat (#1343) 2026-07-04 00:36:00 +08:00
test fix(web): make uploaded videos play in the chat (#1343) 2026-07-04 00:36:00 +08:00
AGENTS.md feat(web): introduce Kimi web app and daemon gateway (#625) 2026-06-17 20:53:46 +08:00
CHANGELOG.md ci: release packages (#1268) 2026-07-02 11:12:20 +08:00
package.json ci: release packages (#1268) 2026-07-02 11:12:20 +08:00
README.md feat(web): introduce Kimi web app and daemon gateway (#625) 2026-06-17 20:53:46 +08:00
SECURITY.md feat(server): add --allowed-host flag for DNS-rebinding allowlist (#1128) 2026-06-26 17:06:01 +08:00
tsconfig.json feat(web): introduce Kimi web app and daemon gateway (#625) 2026-06-17 20:53:46 +08:00
tsdown.config.ts feat(web): introduce Kimi web app and daemon gateway (#625) 2026-06-17 20:53:46 +08:00
vitest.config.ts ci: skip server e2e tests on windows (#1126) 2026-06-26 18:01:22 +08:00

@moonshot-ai/server

Local REST + WebSocket server that exposes the Kimi Code SDK over a stable wire protocol. It hosts agent-core sessions and serves them under a single /api/v1 prefix. This package is private — it is not published on its own; it ships inside the kimi CLI (apps/kimi-code) and is launched via kimi server run.

What it does

  • Hosts agent-core sessions, prompts, tools, approvals, questions, and workspaces in process.
  • Exposes them over REST (Fastify) and WebSocket (ws) under /api/v1.
  • Serves the built-in web UI (apps/kimi-web) as static assets when a webAssetsDir is provided.
  • Publishes machine-readable contract docs: /openapi.json, /asyncapi.json.

Running it

# From the repo root — dev server with auto-restart
pnpm dev:server
pnpm dev:server:restart

# Checks
pnpm --filter @moonshot-ai/server typecheck   # tsc --noEmit
pnpm --filter @moonshot-ai/server test        # vitest run
pnpm --filter @moonshot-ai/server build       # tsdown

The public entry point is startServer(opts) in src/start.ts, which returns a RunningServer. In production the CLI command kimi server run (apps/kimi-code/src/cli/sub/server/run.ts) imports and calls it. This package has no dev script of its own — always start it from the repo root or via the CLI.

By default the server listens on 127.0.0.1:58627; e2e clients target it with KIMI_SERVER_URL (default http://127.0.0.1:58627).

Architecture

apps/kimi-code (CLI)            apps/kimi-web (browser)
        │                              │
        └──────────┬───────────────────┘
                   │  REST + WebSocket, /api/v1
        ┌──────────▼───────────┐
        │  @moonshot-ai/server │
        │  Fastify REST        │
        │  ws gateway          │
        │  DI container        │  ← @moonshot-ai/agent-core
        │  agent-core sessions │  ← @moonshot-ai/agent-core
        └──────────────────────┘
  • REST (src/routes/): domain modules aggregated by registerApiV1Routes.ts. Routes are declared with middleware/defineRoute.ts, which bundles Zod validators with the OpenAPI response schema.
  • WebSocket (src/ws/, src/services/gateway/): per-session seq, server_hello / ack / event / resync_required frames, replay and fan-out.
  • DI (src/services/serviceCollection.ts): seeds the container from @moonshot-ai/agent-core (getSingletonServiceDescriptors()) and layers in server-owned gateways plus IApprovalService / IQuestionService implementations.
  • OS service managers (src/svc/): launchd / systemd / schtasks backends for kimi server install/start.

Wire protocol notes

  • Envelope: every REST response is { code, msg, data, request_id } and the HTTP status is effectively always 200 — check code (0 = ok), not the status.
  • :action endpoints: some routes use an :id:action suffix (e.g. /sessions/{id}:undo); the suffix is parsed by routes/action-suffix.ts.
  • Single-instance lock: a running server acquires a lock; a second start on the same home throws ServerLockedError. Tests pass a unique lockPath / port.
  • @moonshot-ai/agent-core — the agent engine the server hosts, including the in-process DI service layer it wires together.
  • @moonshot-ai/protocol — wire types and the AsyncAPI document.
  • @moonshot-ai/node-sdk — typed in-process facade for user code (KimiHarness, Session); prefer it over hand-rolling REST/WS calls.
  • @moonshot-ai/server-e2e — wire-level e2e client and scenarios against a running server.

Development

For conventions, gotchas, and the boot wiring order, see packages/server/AGENTS.md. For the service naming and registration rules, see packages/services/AGENTS.md.