From 3a0efdd8aa4fb117c3f8232a59bf125ebcb5e1ca Mon Sep 17 00:00:00 2001 From: agap-mcp Date: Sat, 4 Jul 2026 18:29:47 +0000 Subject: [PATCH] Document conversation<->session mapping and per-conversation working dirs --- Kimi-Agent.md | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/Kimi-Agent.md b/Kimi-Agent.md index 4067701..9a8ff13 100644 --- a/Kimi-Agent.md +++ b/Kimi-Agent.md @@ -16,7 +16,26 @@ Open WebUI -> LiteLLM (model: kimi-agent) -> kimi-agent container (HTTP wrap | 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` only. Spawns `kimi -p "" --output-format text` per request. | +| `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 -p ""`, 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//`, 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 @@ -31,7 +50,7 @@ kimi-agent: ``` - **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`) across container restarts/rebuilds. +- `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 @@ -60,9 +79,11 @@ Prints a `https://www.kimi.com/code/authorize_device?user_code=...` URL — open docker exec kimi-agent kimi doctor ``` -### Key CLI gotcha +### 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. `server.js` calls plain `kimi -p "" --output-format text`. +- `-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 -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