MoonshotAI / MoonshotAI/kimi-code
ACP: session/set_mode "plan" fails with -32603 "Already in plan mode" after session/new reports currentModeId "default"
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 7.5k
- Forks
- 1.2k
- Avg merge
- 11h 53m
- Merged PRs (30d)
- 350
Description
Summary
Over ACP, session/set_mode with modeId: "plan" fails with -32603 Internal error ("Already in plan mode") immediately after session/new reports currentModeId: "default". The session state reported to the client and the server's internal state contradict each other, so ACP clients that set an explicit mode after session creation fail to create the session at all.
Environment
- kimi-code versions: 0.39.0 and 0.39.1 (both reproduce)
- Platform: Windows 10 (install via official installer /
npm i @moonshot-ai/kimi-code) - Client: raw ACP over stdio (reproduced below); originally hit through Paseo 0.6.1
Steps to reproduce
- Start
kimi acp. initializewithprotocolVersion: 1.session/new(any cwd). Response contains:
"modes": {"currentModeId": "default", "availableModes": [
{"id": "default"}, {"id": "plan"}, {"id": "auto"}, {"id": "yolo"}]}
session/set_modewith{"sessionId": "<id>", "modeId": "plan"}.
Expected
set_mode plan succeeds (mode switches default → plan), or — if the session really is in plan mode already — session/new should report currentModeId: "plan" so clients skip the redundant set.
Actual
{"jsonrpc": "2.0", "id": 20, "error": {"code": -32603, "message": "Internal error",
"data": {"details": "Already in plan mode"}}}
Note: set_mode auto works fine on the same session, so this is specific to plan.
Minimal repro (Python, stdlib only, no model call / no quota consumed)
import json, subprocess, threading, queue, time
proc = subprocess.Popen(["kimi", "acp"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
q = queue.Queue()
threading.Thread(target=lambda: [q.put(l.decode().strip()) for l in proc.stdout], daemon=True).start()
def send(o): proc.stdin.write((json.dumps(o) + "\n").encode()); proc.stdin.flush()
def wait(rid, t=20):
end = time.time() + t
while time.time() < end:
try: line = q.get(timeout=max(0.1, end - time.time()))
except queue.Empty: return None
if line:
m = json.loads(line)
if m.get("id") == rid: return m
send({"jsonrpc": "2.0", "id": 1, "method": "initialize",
"params": {"protocolVersion": 1, "clientCapabilities": {}}})
wait(1)
send({"jsonrpc": "2.0", "id": 2, "method": "session/new",
"params": {"cwd": ".", "mcpServers": []}})
r = wait(2)
print("currentModeId:", r["result"]["modes"]["currentModeId"]) # -> "default"
sid = r["result"]["sessionId"]
send({"jsonrpc": "2.0", "id": 3, "method": "session/set_mode",
"params": {"sessionId": sid, "modeId": "plan"}})
print(wait(3)) # -> error -32603 "Already in plan mode"
proc.kill()
Impact
ACP hosts that apply a configured mode right after session/new (standard flow in Paseo's ACP adapter) abort session creation entirely, so plan mode is unusable through ACP clients even though it works in the interactive CLI.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start the kimi acp entry point and trace the session/new and session/set_mode handlers, comparing the reported currentModeId with the server's stored mode. Reproduce the sequence with the provided Python stdlib script; done means default followed by set_mode to plan succeeds, or both responses consistently report plan.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100