Skip to content

fix(kap-server): render fork seed user messages in the unnumbered seed turn - #3759

Merged
sailist merged 1 commit into
MoonshotAI:devfrom
sailist:bug-211-09-14-btw-seed-user-messages
Sep 14, 2026
Merged

sailist merged 1 commit into
MoonshotAI:devfrom
sailist:bug-211-09-14-btw-seed-user-messages

Conversation

@sailist

@sailist sailist commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator

Related Issue

Follow-up to #3754 (bug-201). That PR aligned btw fork seed turns with engine turn ids but deliberately left seed-zone user messages unrendered; this PR closes that gap.

Problem

In a btw (side question) agent's history, the seed section — the main conversation copied into the fork — showed only assistant replies. Every user question from the main conversation was missing, because the history fold treats role: 'user' append_message records with undo-anchor origins as pure undo-anchor registrations (correct dedup for live turns, where the user entity comes from the turn.prompt record). The fork seed contains only append_message records and no turn.prompt records, so seed user messages never produced entities.

What changed

  • Cold history fold: in the seed zone (before the agent.fork boundary record or, for legacy wires, the first turn.prompt), user append_message records now render as user message entities inside the unnumbered t-1 seed turn — text, timestamp, origin, attachments, and skill activations preserved, interleaved in wire order. They no longer register degenerate undo anchors pointing at rawId 0. The orphan-turn synthesis is extracted into a shared helper used by both the assistant and user paths.
  • Live projection seed (foldTimelineSeed): same seed-zone handling — seed user messages materialize the t-1 skeleton and skip anchor registration, staying consistent with the cold fold.
  • Tests: the existing fork-seed regression test is extended (seed user entity rendering, anchor ordinals unchanged, legacy wire without a boundary record falls back identically); test count is unchanged.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue (external PRs: the issue must have a maintainer's /approve).
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

@changeset-bot

changeset-bot Bot commented Sep 14, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 61ef69f

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Sep 14, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@61ef69f
npx https://pkg.pr.new/@moonshot-ai/kimi-code@61ef69f

commit: 61ef69f

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

Copy link
Copy Markdown

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: 4a26b190b3

ℹ️ 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 +882 to +887
const attachmentIds: string[] = [];
for (const part of content.slice(skipBlocks)) {
if (part.type === 'text') continue;
if (daemonFileRefFromPart(part) === undefined) continue;
entry.attachmentSeq += 1;
attachmentIds.push(attachmentIdOf(seedTurnId, entry.attachmentSeq));

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 Count origin-backed seed attachments

When a fork seed prompt carries a regular file attachment in message.origin.attachments, this loop inspects only content parts, so the rendered seed user receives no attachment_ids. These files are stored separately from message.content—the numbered-turn path handles them via promptAttachmentCount(input, origin)—so fork history silently loses their attachment indicators; include the origin attachment count when allocating the seed IDs.

Useful? React with 👍 / 👎.

Comment on lines +889 to +892
const messageId =
typeof message.id === 'string'
? message.id
: `${seedTurnId}.u${(phantomUserSeq += 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 Keep seed fallback IDs out of the numbered-turn counter

When a seed user message lacks message.id, incrementing phantomUserSeq here leaks the seed's t-1.uN allocation into the first numbered turn because onTurnPrompt carries that counter into its serverUserSeq. A subsequent ID-less steer in t0 is therefore folded as t0.u2 (or later), while the live projector—whose timeline seed does not restore this counter—emits t0.u1; reloading history can consequently change that entity's ID. Allocate missing seed IDs from the seed turn's own scratch sequence instead.

Useful? React with 👍 / 👎.

@sailist
sailist force-pushed the bug-211-09-14-btw-seed-user-messages branch from 4a26b19 to 5f88ae4 Compare September 14, 2026 09:00

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

Copy link
Copy Markdown

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: 5f88ae40f2

ℹ️ 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".

return;
}
if (!isUndoAnchorOrigin(message.origin)) return;
if (!seedEnded) {

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 Render shell-command inputs in the seed turn

When a fork seed contains a shell_command input, this new block is never entered because the preceding isUndoAnchorOrigin check excludes that origin. The engine persists shell input as a user message, and forkTurnSlice.ts explicitly treats phase: 'input' as user-visible, so the forked history drops the command and may omit t-1 entirely when there is no assistant message. Extend seed eligibility to shell inputs while continuing to exclude shell outputs.

Useful? React with 👍 / 👎.

attachmentIds: attachmentIds.length > 0 ? attachmentIds : undefined,
skillActivations: skillActivationsOf(message.origin),
};
users.set(messageId, draft);

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 Remove seed entities during context clears

When a forked agent containing one of these newly stored seed users later receives context.clear, the seed user survives cold folding: onClear only purges IDs accepted by turnOrdinalOf, whose ^t\d+$ check rejects t-1. The clear event therefore reports t-1 as removed while a subsequent history reload still emits the seed turn, user, and any associated details, unlike the live projector's full clear; handle the seed turn explicitly when removing timeline entities.

Useful? React with 👍 / 👎.

@sailist
sailist force-pushed the bug-211-09-14-btw-seed-user-messages branch from 5f88ae4 to 61ef69f Compare September 14, 2026 09:18
@sailist

sailist commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator Author

Addressing the second-round review comments:

  • Remove seed entities during context clears — fixed in the latest push. turnOrdinalOf now parses the seed turn id (t-1 → ordinal -1), so the clear/undo purge paths in the cold fold, the timeline seed, and the live projector all treat the seed turn as a turn and drop its details. Undo semantics are preserved: the seed turn's ordinal (-1) sorts before any numbered turn, so undo cuts still spare it. The fork seed regression test now covers the clear case (seed turn, its users and steps are all purged).

  • Render shell-command inputs in the seed turn — not changed. Shell inputs are persisted as context append messages only and never produce turn.prompt records, so the main agent's own history fold drops them as well; rendering them in the fork seed but not in main history would just move the inconsistency. If shell inputs should appear in history at all, that is a separate fold-wide change covering the main fold and the live projector, out of scope here.

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

Copy link
Copy Markdown

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: 61ef69fbdf

ℹ️ 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 +130 to +133
if (!visibleTurnOrdinals.has(SEED_TURN_RAW_ID)) {
visibleTurnOrdinals.add(SEED_TURN_RAW_ID);
timelineIds.push(turnIdOf(SEED_TURN_RAW_ID));
}

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 Re-add the seed turn after a pre-boundary clear

When a legacy append-only wire contains a seed user, context.clear, and then another seed user before any turn.prompt or agent.fork, the clear empties timelineIds but leaves visibleTurnOrdinals containing -1. This branch therefore skips re-adding t-1, while foldWireHistory recreates that turn and user, so the live projector is seeded with a different timeline from /history and subsequent timeline rewrites omit t-1. Clear the seed ordinal with the timeline or determine visibility from the current timeline.

Useful? React with 👍 / 👎.

@sailist
sailist merged commit 6cfa828 into MoonshotAI:dev Sep 14, 2026
13 checks passed
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.

1 participant