kimi-code/apps/kimi-web
qer f5a7f21c68 feat(web): land P3 — goal / swarm / subagent + terminal + view split
Implements the locked P3 design end-to-end:
- subagent lifecycle projection (spawned→started→suspended→completed/failed) +
  inline Agent / AgentGroup cards; swarm progress card (multi-column) derived
  from swarmIndex; goal dock strip (expandable) from goal.updated; plan/goal/
  swarm activation badges in the composer status line.
- terminal as a view (xterm + WS terminal_* frames with since_seq replay) and a
  tab/view-dimension split (usePaneLayout tree + ViewGroup + SplitLayout, VSCode
  editor-group style), persisted to localStorage.
Adds swarm-groups / subagent-goal / agent-group-turns unit tests and stub-daemon
seeds. 98 tests pass; vue-tsc + oxlint clean; production build OK.

Accepted by review (see reports/web-p3-acceptance.md); no blocking issues.
2026-06-13 22:53:20 +08:00
..
dev feat(web): land P3 — goal / swarm / subagent + terminal + view split 2026-06-13 22:53:20 +08:00
public feat(web): use kimi.com favicon as the site icon 2026-06-11 15:47:43 +08:00
src feat(web): land P3 — goal / swarm / subagent + terminal + view split 2026-06-13 22:53:20 +08:00
test feat(web): land P3 — goal / swarm / subagent + terminal + view split 2026-06-13 22:53:20 +08:00
icon-preview.html refactor(kimi-web): use large bubble-plus icon for new session button 2026-06-11 10:14:48 +08:00
index.html feat(web): add Kimi design-language theme, drop Terminal from pickers 2026-06-11 18:25:53 +08:00
package.json feat(web): land P3 — goal / swarm / subagent + terminal + view split 2026-06-13 22:53:20 +08:00
README.md feat(server)!: rename daemon to server with service management 2026-06-11 16:44:41 +08:00
share-icons-preview.html feat(web): session list, chat pane, and daemon client updates 2026-06-12 03:08:12 +08:00
test-new-session.mjs feat(kimi-web): remove font settings and add dark mode support 2026-06-11 10:14:48 +08:00
tsconfig.json feat(web): add Kimi web client (apps/kimi-web) 2026-06-11 10:14:44 +08:00
vite.config.ts style(web): hide session time on hover and move kebab to rightmost 2026-06-12 03:33:03 +08:00

Kimi Web

A browser client for Kimi Code — a peer to the TUI (apps/kimi-code) that talks to a local server over REST + WebSocket. Vue 3 + Vite + TypeScript.


Quick start

# 1) Against a REAL server (the server must be running and reachable)
WEB_PORT=5197 KIMI_SERVER_URL=http://192.168.97.91:7878 pnpm -C apps/kimi-web run dev
#   …or from the repo root:  pnpm dev:web   (uses the defaults below)

# 2) Offline / no server — a stub that fakes the server API + event stream
pnpm -C apps/kimi-web run dev:stub      # then run dev in another shell

# checks
pnpm -C apps/kimi-web run typecheck     # vue-tsc --noEmit
pnpm -C apps/kimi-web run test          # vitest
pnpm -C apps/kimi-web run build         # vite build

How it connects to the server

The browser cannot reach the server cross-origin (no CORS), so Vite same-origin proxies /api/v1 (HTTP + WS) to the server (vite.config.ts):

env var default meaning
WEB_PORT 5175 port the dev server listens on
KIMI_SERVER_URL http://127.0.0.1:7878 where /api/v1 (and /api/v1/ws) is forwarded

Behind a corporate HTTP proxy, also set NO_PROXY=<server-host> (e.g. NO_PROXY=192.168.97.91) so the proxy forward reaches the server directly.


Architecture

A strict one-direction data flow; components never touch the network or the reducer — they consume computed view props and call actions.

server (REST + WS)
  └─ src/api/daemon/client.ts      REST adapter  (envelope → AppX types)
  └─ src/api/daemon/ws.ts          WS frames → classify → projector/reducer
       └─ agentEventProjector.ts   RAW agent-core events → AppEvent[]
       └─ eventReducer.ts          AppEvent[] → state
  └─ src/composables/useKimiWebClient.ts   the ONLY place that imports api + state;
                                           exposes computed view props + actions
  └─ src/components/*.vue          render props, emit intents (no api access)

The directory name src/api/daemon/ is historical and kept to minimise diff churn; conceptually it is the server adapter.

  • Adapter (src/api/): wire types are snake_case; AppX types are camelCase. config.ts builds /api/v1 URLs.
  • Event projector (agentEventProjector.ts): the server streams raw agent-core events (no event. prefix). classifyFrame routes raw vs protocol (event.*) frames; the projector converts them to AppEvents.
  • i18n (src/i18n/): vue-i18n, en/zh, per-namespace flat camelCase keys. Detect order: localStorage('kimi-locale')navigator.languageen.
  • Tests: Vitest + @vue/test-utils + jsdom, colocated under __tests__/.

Server contract — non-obvious notes

The server's wire protocol has a few things that will bite you if forgotten:

  • Envelope: every response is { code, msg, data, request_id } and the HTTP status is always 200 — check code (0 = ok), not the status.
  • Prompts require five fields. POST /sessions/{id}/prompts must carry { content, model, thinking, permission_mode, plan_mode }. The web fills these from settings (model ← session/default_model, thinking/permission/plan ← the StatusLine controls). Sending only { content }40001 model ….
  • Creating a session needs a registered workspace. workspace_id must be a wd_<slug>_<hash> id that exists in the server's registry. Sessions get one auto-assigned by cwd, but it isn't registered until you POST /workspaces { root } (idempotent). The web registers on demand before createSession (otherwise: workspace not found: wd_…).
  • Persisted sessions are directly promptable — selecting an old session and sending a message just works; there is no :activate step.
  • Workspaces = real folders. GET/POST/PATCH/DELETE /workspaces, GET /fs:browse?path=, GET /fs:home back the rail + folder picker.

What's still missing / blocked on the server

See docs/main-flow-gaps.md (the main-flow gap audit) and docs/backend-workspace-session-asks.md (the endpoint asks for the backend).

Server endpoints that are not live yet (probed; the web degrades gracefully):

  • /sessions/{id}:compact, :fork, :steer, /undo400 (no such action)
  • line-by-line diff404
  • GET /sessions/{id}/status404 (the /status panel is rendered from client state instead)
  • /goal, /btw, /mcp, /init, /reload, /settings, /plugins → absent

Everything client-side (workspace rail, sessions, chat/stream, approvals, tools/diff/files, model/provider/login, thinking/plan/permission controls, /status, queue edit, syntax highlighting, i18n) is implemented.


Design docs

Living under docs/ (design rationale + plans):

  • docs/workspace-session-design.html — workspace ⇄ session model + flows
  • docs/dual-sidebar-exploration.html — sidebar layout options (Variant B shipped)
  • docs/kimi-web-final-form.html — target-state UI mockup
  • docs/main-flow-gaps.md — feature gap audit (what to build next)
  • docs/backend-workspace-session-asks.md — endpoints the server still needs