2
Kimi Agent
agap-mcp edited this page 2026-07-04 18:29:47 +00:00

Kimi Code CLI Agent

Autonomous coding agent, invokable as a model through LiteLLM/Open WebUI, powered by the user's own Kimi/Moonshot subscription (not the Moonshot pay-per-token API).

Pipeline

Open WebUI  ->  LiteLLM (model: kimi-agent)  ->  kimi-agent container (HTTP wrapper)
            ->  kimi-code CLI  ->  Moonshot cloud (user's Kimi subscription, via `kimi login`)

Location

~/agap_git/openai/kimi-agent/ — built as part of the openai compose project (alongside litellm, open-webui).

File Purpose
Dockerfile node:22-slim + npm install -g @moonshot-ai/kimi-code
server.js Minimal OpenAI-compatible wrapper (/v1/models, /v1/chat/completions) — no external deps, built-in http/child_process/crypto/fs only. Maps each Open WebUI conversation to a persistent kimi session (see below).

Conversation <-> session continuity

Open WebUI (like any OpenAI chat client) re-sends the entire message history on every turn, but the CLI's -p mode is stateless — one prompt in, one answer out. The wrapper bridges the two so an Open WebUI thread maps 1:1 onto a persistent kimi session instead of starting cold each turn.

Per request the wrapper:

  1. Keys the conversation by SHA-256 hashing the user/assistant history up to but excluding the newest user turn (a "content-hash chain"). This depends only on the standard messages array, so it survives LiteLLM sitting in the middle — no reliance on a chat_id that LiteLLM may strip.
  2. Looks the key up in a persisted key -> {convId, sessionId, dir} map:
    • Hit -> resumes the same kimi session with kimi -r <session_id> -p "<new message only>", in that conversation's own working dir.
    • No prior history -> brand-new conversation: fresh kimi -p, new dir + convId.
    • Miss with prior history (wrapper restarted / thread edited) -> reseeds a fresh session with the full transcript so continuity is preserved.
  3. Captures the session_id from the stream-json session.resume_hint meta line and stores the forward mapping (history + this reply -> session), which is exactly what the next turn will hash to.

Per-conversation working dirs: each conversation gets its own directory /workspace/conversations/<convId>/, used as the CLI cwd. File state is therefore isolated between threads and persists across turns within a thread.

State file: key -> session map lives at /workspace/.kimi-agent/sessions.json (host-backed, survives restarts; LRU-capped at 1000 entries).

Both the wrapper-level session map and kimi's own session store (/root/.kimi-code, named volume) persist across container rebuilds, so live Open WebUI threads keep working after a redeploy.

Docker Compose

kimi-agent:
  build: ./kimi-agent
  container_name: kimi-agent
  volumes:
    - /home/alvis/kimi-workspace:/workspace
    - kimi-agent-home:/root/.kimi-code
  restart: unless-stopped
  • Mount scope is deliberately narrow: only ~/kimi-workspace is mounted as /workspace, not the full home directory. The agent runs prompts via -p with no interactive approval gate (see below), so it must never see ~/.ssh, ~/.claude, ~/bin/bw, etc.
  • kimi-agent-home is a named volume persisting /root/.kimi-code (the OAuth session from kimi login and kimi's own session history) across container restarts/rebuilds.
  • No ports are published — only reachable from other containers on the openai_default network (i.e. litellm), not from the host or LAN.

LiteLLM registration

In litellm-config.yaml:

- model_name: kimi-agent
  litellm_params:
    model: openai/kimi-agent
    api_base: http://kimi-agent:8000/v1
    api_key: dummy

Shows up automatically in Open WebUI since OPENAI_API_BASE_URL there already points at http://host.docker.internal:4000/v1 (LiteLLM).

Auth (one-time, manual)

docker exec -it kimi-agent kimi login

Prints a https://www.kimi.com/code/authorize_device?user_code=... URL — open it and authorize with the Kimi subscription account. Session persists in the kimi-agent-home volume. Check status any time with:

docker exec kimi-agent kimi doctor

Key CLI gotchas

  • -p (non-interactive single-prompt mode) cannot be combined with --yolo or --auto — and doesn't need to be. In -p mode there's no TTY to prompt for approval, so tool calls (file read/write, shell) execute automatically already.
  • Resume a session with kimi -r <session_id> -p "...". With --output-format stream-json, the CLI emits a {"role":"meta","type":"session.resume_hint","session_id":"session_..."} line the wrapper parses to learn/track the id.
  • The node:22-slim image has no curl — test the wrapper with Node's http module (e.g. docker exec kimi-agent node -e '...').

Rebuild / restart

cd ~/agap_git/openai
docker compose up -d --build kimi-agent