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)

Open
#3,561 2 comments 0 reactions 0 assignees View on GitHub

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

  1. Have a session imported by the old migrator (state.json: custom.imported_from_kimi_cli=true, kimi_cli_wire_protocol="1.10"; wire.jsonl contains no turn.prompt/turn.ended records).
  2. Open it in the Web UI (0.41.0) and send any message.
  3. 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 from turn.prompt / context.append_loop_event records. 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 get turnId 0, 1, … and are projected as t0, t1 — colliding with imported turns at the top of history, outside the default last-N-turns window.
  • healEndedTurns then 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=true and wire lacks turn records → synthesize turn structure in place (the #3446 buildTurnRecords logic is reusable), preserving any live turns added after import; or
  • an explicit kimi migrate --repair-imported command.

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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.