MoonshotAI / MoonshotAI/kimi-code
Migrated sessions imported by pre-0.40.0 migrator never heal on ≥0.40.1: turn-clock reset collides new turns with imported transcript (live UI shows only old content)
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 7.5k
- Forks
- 1.2k
- Avg merge
- 11h 53m
- Merged PRs (30d)
- 350
Description
Summary
Sessions imported from legacy Python kimi-cli by a pre-0.40.0 migrator carry no turn records in wire.jsonl (only metadata + context.append_message). PR #3446 (shipped in 0.40.0) fixed the migration writer to synthesize turn.prompt/turn.ended for new migrations, and 0.40.0 briefly shipped an in-place repair (repair-imported.ts) for sessions already broken by the old migrator. PR #3467 (0.40.1) removed that repair path entirely. As a result, on any ≥0.40.1 build (including current 0.41.0), previously migrated sessions stay broken forever: every new turn is fully processed by the backend but never rendered in the Web UI.
Reproduction
- Have a session imported by the old migrator (state.json:
custom.imported_from_kimi_cli=true,kimi_cli_wire_protocol="1.10";wire.jsonlcontains noturn.prompt/turn.endedrecords). - Open it in the Web UI (0.41.0) and send any message.
- Observe: neither the user bubble nor the assistant reply appears. The sidebar title/activity updates correctly (separate channel).
Backend is healthy throughout: wire.jsonl records prompt.accepted, assistant content.part text, turn.ended, prompt.completed. Nothing is lost — it is a live-transcript rendering defect.
Evidence from an affected production session (redacted)
{"type":"metadata","protocol_version":"1.5","created_at":1787620582752}
{"type":"context.append_message","message":{"role":"user","content":[{"type":"text","text":"<imported legacy user text, redacted>"}],"toolCalls":[]}}
// … 50 imported legacy records total, all shaped like the line above:
// no "agentId", no "time", and no turn records anywhere in the imported segment …
{"type":"turn.prompt","agentId":"main","input":[{"type":"text","text":"hello"}],"origin":{"kind":"user"},"promptId":"msg_01M1SC9KEB8TXWA1WQ0DGXMSV2","time":1788631895605}
{"type":"turn.ended","agentId":"main","turnId":0,"reason":"completed","durationMs":18779,"time":1788631914385}
{"type":"turn.prompt","agentId":"main","input":[{"type":"text","text":"hello"}],"origin":{"kind":"user"},"promptId":"msg_01M1SCAEWZWXDV2A9RRH5NZHKN","time":1788631923623}
{"type":"turn.ended","agentId":"main","turnId":1,"reason":"completed","durationMs":5985,"time":1788631929609}
Note the turn clock restarting at turnId 0 for the first post-import turn.
Root cause (verified against 0.38.0 source and production wire data)
- On resume, the engine's turn clock (
turnKey.nextTurnId) is rebuilt only fromturn.prompt/context.append_loop_eventrecords. Imported wires contain none, so the clock restarts at 0. - The transcript backfill numbers all imported messages sequentially as turns
t0..t(k-1). New live turns getturnId 0, 1, …and are projected ast0,t1— colliding with imported turns at the top of history, outside the default last-N-turns window. healEndedTurnsthen processes only the terminal ordinals {0,1} and lets the imported snapshot win, so the live store converges back to "old content only" after every turn. This repeats on every send until a server restart forces a cold rebuild (which renumbers sequentially and displays correctly until the next send).
Why ≥0.40.1 cannot recover
- #3446 only fixes future migrations.
- The 0.40.0-only in-place repair (which preserved live history added after import) was deleted by #3467. There is currently no code path that repairs an already-imported session, so upgrading never heals existing installs.
Requested fix
Restore a repair path for sessions imported by pre-0.40.0 migrators, e.g.:
- a one-shot startup/resume check:
custom.imported_from_kimi_cli=trueand wire lacks turn records → synthesize turn structure in place (the #3446buildTurnRecordslogic is reusable), preserving any live turns added after import; or - an explicit
kimi migrate --repair-importedcommand.
Environment
- kimi-code 0.41.0 (win32-x64), Web UI served by the app server.
- Session imported 2026-08-25 by migrator 0.1.1 from Python kimi-cli 1.49.
References
- PR #3446 (writer fix, 0.40.0): https://github.com/MoonshotAI/kimi-code/pull/3446
- PR #3467 (removed the in-place repair, 0.40.1): https://github.com/MoonshotAI/kimi-code/pull/3467
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 with the removed repair-imported.ts path and the buildTurnRecords logic from PR #3446, then trace the session startup/resume flow that rebuilds turnKey.nextTurnId. Reproduce with an affected wire.jsonl containing context.append_message records but no turn records. Done means pre-0.40.0 imports are repaired without losing post-import turns, and new messages render instead of colliding with imported turns.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python, typescript
- Domain
- backend, cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100