Skip to content

fix(session): map undo wire turns to context turns#2386

Open
Pluviobyte wants to merge 1 commit into
MoonshotAI:mainfrom
Pluviobyte:fix/undo-context-turn-mapping
Open

fix(session): map undo wire turns to context turns#2386
Pluviobyte wants to merge 1 commit into
MoonshotAI:mainfrom
Pluviobyte:fix/undo-context-turn-mapping

Conversation

@Pluviobyte
Copy link
Copy Markdown

@Pluviobyte Pluviobyte commented May 28, 2026

Related Issue

Resolve #1974
Related #2049

Description

/undo and fork currently use a wire.jsonl TurnBegin index for both wire truncation and context truncation. That only works when every wire turn also writes a real user message to context.jsonl.

Local slash-command turns such as /usage or /sessions are present in wire.jsonl, but do not represent real model-visible user turns in context.jsonl. When users select one of the later turns in the undo picker, the context truncation uses a too-large index and keeps extra user/assistant messages. This makes the resumed/forked session land earlier/later than the selected turn and can show history in the UI that the agent does not actually have, which matches #1974 and the history mismatch reported in #2049.

Fix

  • Add a mapping from selected wire turn index to the corresponding real context user turn index.
  • Match TurnBegin.user_input text from wire.jsonl against real non-checkpoint user messages from context.jsonl.
  • Use the mapped context index when truncating context.jsonl; wire truncation still uses the original wire index.
  • Leave the existing best-effort behavior for missing context in place.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked the related issue.
  • I have added tests that prove my fix is effective.
  • I have updated CHANGELOG.md (manual Unreleased entry, following the existing one-line-per-bullet style; make gen-changelog not run because this is a focused bug fix).
  • make gen-docs not run — N/A: no user-visible CLI/config/docs behavior change.

Test plan

  • UV_PYTHON=3.12 uv run ruff check src/kimi_cli/session_fork.py tests/core/test_session_fork.py → clean
  • UV_PYTHON=3.12 uv run ruff format --check src/kimi_cli/session_fork.py tests/core/test_session_fork.py → clean
  • UV_PYTHON=3.12 uv run pytest tests/core/test_session_fork.py -q30 passed

Proof of fix

New regression test: test_fork_maps_wire_turns_to_real_context_users.

It builds a session where wire has five turns:

real 0, /usage, /sessions, real 1, real 2

but context only has three model-visible user turns:

real 0, real 1, real 2

Forking at wire turn 3 now keeps only real 0 and real 1 in context, instead of incorrectly keeping real 2 as well.

Made with Cursor


Open in Devin Review

Undo and fork select turns from wire.jsonl, but slash/local command turns can be present in wire without adding a real user message to context.jsonl. Reusing the wire turn index for context truncation could therefore keep too much context and leave the forked session inconsistent.

Map each selected wire TurnBegin to the matching real context user turn before truncating context, so non-context local command turns no longer shift the cutoff.

Fixes MoonshotAI#1974
Related MoonshotAI#2049

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 688ffecbd6

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +225 to +230
if (
next_context_turn < len(context_user_texts)
and turn.user_text == context_user_texts[next_context_turn]
):
context_turn_index = next_context_turn
next_context_turn += 1
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle context-only user turns when mapping history

When a session contains a model-visible user message that has no matching wire TurnBegin text, this sequential equality check gets stuck and all later wire turns map to the last earlier context turn. One concrete path is a configured Stop hook with a block reason: KimiSoul.run calls _turn(Message(role="user", content=result.reason)) without emitting another TurnBegin, so selecting any later /undo or fork turn truncates away the later real prompt from context.jsonl. Skill slash commands have the same shape because the wire text is /skill:... while the context text is the loaded skill prompt.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 3 additional findings.

Open in Devin Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The undo command lands effectively 4 turns earlier than expected

1 participant