fix: harden long conversation continuity
Memind CI / Test, build, and release guards (push) Successful in 1m28s

This commit is contained in:
john
2026-07-27 21:39:34 +08:00
parent 5ae166fc44
commit 48c61a3279
16 changed files with 1635 additions and 94 deletions
+8
View File
@@ -23,6 +23,7 @@ import {
} from '../utils/imageUpload';
import { buildAbsoluteAssetDownloadUrl } from '../utils/mindspaceCards';
import { resolveAgentRunOptions } from '../utils/agentRunMode';
import { shouldIgnoreZeroActivityFinish } from '../../chat-agent-run-gate.mjs';
import {
buildUserMessage,
normalizeConversationMessages,
@@ -183,6 +184,13 @@ export function usePageEditSubChat({
activeRequestId.current = null;
return;
case 'Finish':
if (shouldIgnoreZeroActivityFinish({
eventType: event.type,
tokenState: event.token_state,
hasActiveRequest: Boolean(activeRequestId.current),
})) {
return;
}
setChatState('idle');
setPendingTool(null);
activeRequestId.current = null;
+33 -1
View File
@@ -58,8 +58,10 @@ import {
import {
reconcileSessionEventRequestContext,
resolvePostAgentRunChatState,
shouldIgnoreZeroActivityFinish,
shouldKeepStreamingAfterRunError,
shouldPromoteSessionIdToStreaming,
shouldScheduleMissingActiveRequestGrace,
} from '../../chat-agent-run-gate.mjs';
import { mergeConversationSnapshot } from '../../chat-finish-sync.mjs';
import {
@@ -372,6 +374,7 @@ export function useTKMindChat(
const seenNotificationIdsRef = useRef<Set<string>>(new Set());
const activeRequestId = useRef<string | null>(null);
const agentRunPendingRef = useRef(false);
const activeRequestMissingTimerRef = useRef<ReturnType<typeof window.setTimeout> | null>(null);
const chatStateRef = useRef<ChatState>(chatState);
const subscribedSessionIdRef = useRef<string | null>(null);
@@ -930,14 +933,18 @@ export function useTKMindChat(
activeRequestId.current = retryRequestId;
setChatState('waiting');
setError(null);
clearActiveRequestMissingTimer();
agentRunPendingRef.current = true;
const retryRun = await createAgentRun(sessionRef.current!.id, retryRequestId, lastUser);
const finishedRetryRun =
retryRun.status === 'succeeded' ? retryRun : await waitForAgentRun(retryRun.id);
agentRunPendingRef.current = false;
if (!finishedRetryRun.sessionId) {
throw new Error('后台任务已提交,但未返回会话');
}
setChatState('streaming');
} catch (err) {
agentRunPendingRef.current = false;
setError(err instanceof Error ? err.message : String(err));
setChatState('error');
activeRequestId.current = null;
@@ -950,6 +957,7 @@ export function useTKMindChat(
: '云端 Relay 不可用,且本地 fallback 重试失败或未启用',
);
setChatState('error');
agentRunPendingRef.current = false;
activeRequestId.current = null;
}
return;
@@ -986,11 +994,20 @@ export function useTKMindChat(
case 'Error':
setError(event.error);
setChatState('error');
agentRunPendingRef.current = false;
activeRequestId.current = null;
return;
case 'Finish':
if (shouldIgnoreZeroActivityFinish({
eventType: event.type,
tokenState: event.token_state,
hasActiveRequest: Boolean(activeRequestId.current),
})) {
return;
}
setChatState('idle');
setPendingTool(null);
agentRunPendingRef.current = false;
activeRequestId.current = null;
if (isDirectChatSessionId(sessionId)) {
unsubscribeRef.current?.();
@@ -1054,7 +1071,12 @@ export function useTKMindChat(
rid = activeRequestContext.activeRequestId;
} else if (rid && event.request_ids.includes(rid)) {
clearActiveRequestMissingTimer();
} else if (activeRequestContext.allowMissingGrace) {
} else if (
shouldScheduleMissingActiveRequestGrace({
allowMissingGrace: activeRequestContext.allowMissingGrace,
agentRunPending: agentRunPendingRef.current,
})
) {
// The backend can briefly report no active request between tool phases.
// Confirm the absence before turning the UI idle, otherwise MindSpace
// refreshes the page while tools are still mutating it.
@@ -1062,6 +1084,7 @@ export function useTKMindChat(
activeRequestMissingTimerRef.current = window.setTimeout(() => {
activeRequestMissingTimerRef.current = null;
if (activeRequestId.current !== rid) return;
if (agentRunPendingRef.current) return;
activeRequestId.current = null;
setChatState('idle');
setPendingTool(null);
@@ -1139,6 +1162,7 @@ export function useTKMindChat(
clearActiveRequestMissingTimer();
setError(null);
setPendingTool(null);
agentRunPendingRef.current = false;
activeRequestId.current = null;
messagesRef.current = [];
messageHistoryLoadedCountRef.current = 0;
@@ -1173,6 +1197,7 @@ export function useTKMindChat(
}
setError(null);
setPendingTool(null);
agentRunPendingRef.current = false;
activeRequestId.current = null;
let completed = false;
@@ -1393,6 +1418,7 @@ export function useTKMindChat(
connectTokenRef.current += 1;
unsubscribeRef.current?.();
clearActiveRequestMissingTimer();
agentRunPendingRef.current = false;
};
// Re-run when the signed-in user changes so session storage stays per-user.
}, [clearActiveRequestMissingTimer, ensureProvider, loadProjectMemory, notifyInsufficientBalance, refreshSessions, user?.id]);
@@ -1529,6 +1555,8 @@ export function useTKMindChat(
requestId,
mindspaceContext: options?.mindspaceContext ?? null,
});
clearActiveRequestMissingTimer();
agentRunPendingRef.current = true;
const createdRun = await createAgentRun(
activeSessionId,
requestId,
@@ -1587,6 +1615,7 @@ export function useTKMindChat(
},
});
if (submitToken !== connectTokenRef.current) return;
agentRunPendingRef.current = false;
activeSessionId = finishedRun.sessionId;
if (!activeSessionId) {
throw new Error('后台任务已提交,但未返回会话');
@@ -1697,6 +1726,7 @@ export function useTKMindChat(
scheduleReplyRecoverySync(activeSessionId, submitToken);
return;
}
agentRunPendingRef.current = false;
if (session && activeSessionId) setSessions((prev) => touchSession(prev, activeSessionId!, -1));
if (err instanceof ApiError && err.status === 402) {
notifyInsufficientBalance();
@@ -1728,6 +1758,7 @@ export function useTKMindChat(
await cancelRequest(session.id, activeRequestId.current);
} finally {
clearActiveRequestMissingTimer();
agentRunPendingRef.current = false;
activeRequestId.current = null;
setChatState('idle');
}
@@ -1762,6 +1793,7 @@ export function useTKMindChat(
subscribedSessionIdRef.current = null;
clearActiveRequestMissingTimer();
clearStoredSessionId(userRef.current?.id);
agentRunPendingRef.current = false;
activeRequestId.current = null;
messagesRef.current = [];
messageHistoryLoadedCountRef.current = 0;