mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-07-10 01:39:25 +00:00
fix(web): clear all per-session state when archiving or removing a session (#984)
Some checks are pending
CI / build (push) Waiting to run
CI / test (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
Some checks are pending
CI / build (push) Waiting to run
CI / test (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
* fix(web): clear all per-session state when archiving or removing a session * fix(web): clear queued and in-flight prompt state in session teardown * fix(web): unsubscribe session before clearing its state in teardown * fix(web): cancel pending WS subscriptions on unsubscribe
This commit is contained in:
parent
8c6cade69e
commit
da81858802
4 changed files with 48 additions and 18 deletions
5
.changeset/fix-web-forget-session.md
Normal file
5
.changeset/fix-web-forget-session.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"@moonshot-ai/kimi-code": patch
|
||||
---
|
||||
|
||||
Clear all per-session state when a session is archived or removed, so archived sessions no longer leave orphaned data behind.
|
||||
|
|
@ -160,6 +160,10 @@ export class DaemonEventSocket {
|
|||
/** Unsubscribe from a session's events. */
|
||||
unsubscribe(sessionId: string): void {
|
||||
this.subscriptions.delete(sessionId);
|
||||
// Also cancel a subscribe that was queued before server_hello; otherwise
|
||||
// onServerHello would merge it back into the active subscription set.
|
||||
const pendingIdx = this.pendingSubscriptions.findIndex((p) => p.sessionId === sessionId);
|
||||
if (pendingIdx !== -1) this.pendingSubscriptions.splice(pendingIdx, 1);
|
||||
if (this.connected && this.ws) {
|
||||
this.send({
|
||||
type: 'unsubscribe',
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ export interface UseWorkspaceStateDeps {
|
|||
updateSession: (id: string, update: (session: AppSession) => AppSession) => void;
|
||||
upsertSessionFront: (session: AppSession) => void;
|
||||
appendSession: (session: AppSession) => void;
|
||||
removeSession: (id: string) => void;
|
||||
forgetSession: (id: string) => void;
|
||||
setActiveSessionId: (id: string | undefined) => void;
|
||||
/** Update one session's message list via a function of the current list. */
|
||||
updateSessionMessages: (
|
||||
|
|
@ -110,7 +110,7 @@ export function useWorkspaceState(rawState: ExtendedState, deps: UseWorkspaceSta
|
|||
updateSession,
|
||||
upsertSessionFront,
|
||||
appendSession,
|
||||
removeSession,
|
||||
forgetSession,
|
||||
setActiveSessionId,
|
||||
updateSessionMessages,
|
||||
nextOptimisticMsgId,
|
||||
|
|
@ -1322,7 +1322,7 @@ export function useWorkspaceState(rawState: ExtendedState, deps: UseWorkspaceSta
|
|||
try {
|
||||
const api = getKimiWebApi();
|
||||
await api.archiveSession(id);
|
||||
removeSession(id);
|
||||
forgetSession(id);
|
||||
sideChat.clearSideChatForSession(id);
|
||||
const { [id]: _removedIds, ...restIds } = rawState.sideChatUserMessageIdsBySession;
|
||||
void _removedIds;
|
||||
|
|
|
|||
|
|
@ -442,6 +442,40 @@ function removeSessionMessages(sessionId: string): void {
|
|||
rawState.messagesBySession = rest;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Session teardown — single place that wipes a session and all its per-session
|
||||
// sidecar state. Both removal entry points (not-found + archive) go through
|
||||
// this, so adding a new per-session map only ever needs one new line here.
|
||||
// ---------------------------------------------------------------------------
|
||||
function forgetSession(sessionId: string): void {
|
||||
// Stop receiving events for this session BEFORE clearing its state: a late or
|
||||
// buffered event for this id would otherwise be reduced and recreate the very
|
||||
// per-session maps we are about to delete.
|
||||
eventConn?.unsubscribe(sessionId);
|
||||
removeSession(sessionId);
|
||||
removeSessionMessages(sessionId);
|
||||
delete rawState.approvalsBySession[sessionId];
|
||||
delete rawState.questionsBySession[sessionId];
|
||||
delete rawState.tasksBySession[sessionId];
|
||||
delete rawState.goalBySession[sessionId];
|
||||
delete rawState.gitStatusBySession[sessionId];
|
||||
delete rawState.lastSeqBySession[sessionId];
|
||||
delete rawState.compactionBySession[sessionId];
|
||||
delete rawState.messagesLoadingMoreBySession[sessionId];
|
||||
delete rawState.messagesHasMoreBySession[sessionId];
|
||||
delete rawState.messagesLoadMoreErrorBySession[sessionId];
|
||||
delete epochBySession[sessionId];
|
||||
sessionsKnownEmpty.delete(sessionId);
|
||||
// In-flight / queued prompt state: drop these too so a queued follow-up
|
||||
// can't be submitted to a session that was just archived when its turn later
|
||||
// goes idle (onSessionIdle drains queuedBySession[sid] without re-checking
|
||||
// that the session still exists).
|
||||
inFlightPromptSessions.delete(sessionId);
|
||||
delete rawState.queuedBySession[sessionId];
|
||||
delete rawState.promptIdBySession[sessionId];
|
||||
delete rawState.sendingBySession[sessionId];
|
||||
}
|
||||
|
||||
// Models + Providers reactive state and helpers live in
|
||||
// ./client/useModelProviderState. It is instantiated below (after the
|
||||
// `activity` computed it depends on) as `modelProvider`.
|
||||
|
|
@ -894,20 +928,7 @@ function goalErrorMessage(err: unknown): string | undefined {
|
|||
}
|
||||
|
||||
async function handleSessionNotFound(sessionId: string): Promise<void> {
|
||||
removeSession(sessionId);
|
||||
removeSessionMessages(sessionId);
|
||||
delete rawState.approvalsBySession[sessionId];
|
||||
delete rawState.questionsBySession[sessionId];
|
||||
delete rawState.tasksBySession[sessionId];
|
||||
delete rawState.goalBySession[sessionId];
|
||||
delete rawState.gitStatusBySession[sessionId];
|
||||
delete rawState.lastSeqBySession[sessionId];
|
||||
delete rawState.compactionBySession[sessionId];
|
||||
delete rawState.messagesLoadingMoreBySession[sessionId];
|
||||
delete rawState.messagesHasMoreBySession[sessionId];
|
||||
delete rawState.messagesLoadMoreErrorBySession[sessionId];
|
||||
delete epochBySession[sessionId];
|
||||
sessionsKnownEmpty.delete(sessionId);
|
||||
forgetSession(sessionId);
|
||||
|
||||
if (rawState.activeSessionId !== sessionId) return;
|
||||
|
||||
|
|
@ -1835,7 +1856,7 @@ const workspaceState = useWorkspaceState(rawState, {
|
|||
updateSession,
|
||||
upsertSessionFront,
|
||||
appendSession,
|
||||
removeSession,
|
||||
forgetSession,
|
||||
setActiveSessionId,
|
||||
updateSessionMessages,
|
||||
nextOptimisticMsgId,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue