Skip to content

fix(kimi-web): single-source session status to stop duplicate turn-end notifications - #1542

Merged
wbxl2000 merged 1 commit into
mainfrom
fix/web-duplicate-turn-notification
Jul 10, 2026
Merged

fix(kimi-web): single-source session status to stop duplicate turn-end notifications#1542
wbxl2000 merged 1 commit into
mainfrom
fix/web-duplicate-turn-notification

Conversation

@wbxl2000

Copy link
Copy Markdown
Collaborator

Related Issue

No linked issue — reported by Windows web UI users: the "Turn finished" desktop notification pops twice for every completed turn.

Problem

Each turn end delivered two sessionStatusChanged events to the web client:

  1. the raw turn.ended agent event, projected client-side into sessionStatusChanged (agentEventProjector), and
  2. the daemon's protocol event event.session.status_changed, mapped to the same AppEvent shape (mappers).

onSessionIdle ran for both. The first run consumed the cached prompt id and keyed the notification's dedup tag with it; the second found the cache empty and fell back to a Date.now() tag — a different tag, so the same-tag silent-replace never kicked in and a second "Turn finished" notification popped (the completion sound replayed too). The double event predates #1479, but was invisible while the tag was session-scoped; #1479's per-turn tags exposed it.

What changed

Stop projecting sessionStatusChanged from the raw turn stream, so the daemon's event.session.status_changed is the single source of session-status transitions:

  • agentEventProjector: removed the status projection from turn.started, turn.ended, and the in-flight snapshot seed (seedInFlight still returns the seeded messageCreated; status comes from the snapshot's authoritative session record). turn.ended keeps its content responsibilities — finalizing the assistant message (status + durationMs) and emitting sessionUsageUpdated.
  • This fits the existing design: the daemon event is computed from live state (covers awaitingApproval / awaitingQuestion / aborted with priority rules), carries the authoritative previousStatus / currentPromptId, is deduped per real transition server-side (previous === next), is durable/journaled (survives reconnect), and is globally broadcast so background sessions the client is not subscribed to still update. The projector's hardcoded previousStatus: 'running' could also disagree with the daemon on awaiting states — that divergence goes away too.
  • Updated now-stale comments in client.ts (seedSnapshot) and useWorkspaceState.ts (bindNextPromptId no longer drives Stop; the submit response + daemon status event do).
  • Added projector tests locking the single-sourcing contract.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • 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.

…d notifications

The web client received two sessionStatusChanged events per turn
transition: one projected client-side from the raw turn.started/turn.ended
stream, one mapped from the daemon's event.session.status_changed. After
the tag scheme in #1479 keyed the completion notification by prompt id,
the second (redundant) idle event lost the cached prompt id and fell back
to a Date.now() tag, so every turn end popped a second "Turn finished"
notification and replayed the completion sound.

Stop projecting sessionStatusChanged from the raw turn stream (turn.started,
turn.ended, and the in-flight snapshot seed). The daemon's
event.session.status_changed is the single source of status transitions:
it is computed from live daemon state (covering awaiting-approval /
awaiting-question / aborted), carries the authoritative previousStatus and
currentPromptId, and is deduped per real transition server-side. The turn
stream keeps its content responsibilities (message finalization, usage,
duration); seedInFlight keeps seeding the partially-streamed message while
status comes from the snapshot's authoritative session record.
@changeset-bot

changeset-bot Bot commented Jul 10, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e77fd63

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@moonshot-ai/kimi-code Patch

Not sure what this means? Click here to learn what changesets are.

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

@pkg-pr-new

pkg-pr-new Bot commented Jul 10, 2026

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

commit: e77fd63

@wbxl2000
wbxl2000 merged commit f80b2ea into main Jul 10, 2026
11 checks passed
@wbxl2000
wbxl2000 deleted the fix/web-duplicate-turn-notification branch July 10, 2026 13:18
@github-actions github-actions Bot mentioned this pull request Jul 10, 2026
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