Files
memind/chat-agent-run-gate.mjs
john 7f7aced751 feat(runtime): wire personal memory observation and skill runtime config
Hook shadow pipeline observation into session finish and agent runs, and
expose skill runtime settings through auth and runtime status endpoints.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-13 14:00:11 +08:00

79 lines
2.8 KiB
JavaScript

/**
* Agent-run gate vs session Finish ordering (regression guard).
*
* Context (2026-07): session SSE Finish can arrive before agent-run SSE reports
* succeeded. Re-entering streaming after the run gate completes leaves the UI stuck
* on typing dots even though the assistant reply is already visible.
*
* Tests: chat-agent-run-gate.test.mjs
* Consumer: src/hooks/useTKMindChat.ts
*/
/**
* @param {{ chatState?: string; finishedViaPortalDirectChat?: boolean; agentRunSucceeded?: boolean }} input
* @returns {'idle' | 'streaming'}
*/
export function resolvePostAgentRunChatState({
chatState = 'waiting',
finishedViaPortalDirectChat = false,
agentRunSucceeded = false,
} = {}) {
if (finishedViaPortalDirectChat || agentRunSucceeded) return 'idle';
if (chatState === 'idle') return 'idle';
return 'streaming';
}
/**
* @param {string | undefined | null} chatState
* @returns {boolean}
*/
export function shouldPromoteSessionIdToStreaming(chatState) {
return chatState !== 'idle';
}
/**
* Agent-run POST tracks portal request_id; Goose SSE may emit a different chat_request_id.
* Adopt the upstream id so Message/Finish events are not dropped in the UI.
*
* @param {{ activeRequestId?: string | null; chatState?: string; eventType?: string; eventRequestId?: string | null; activeRequestIds?: string[] }} input
* @returns {{ activeRequestId: string | null; promoteStreaming: boolean; allowMissingGrace: boolean }}
*/
export function reconcileSessionEventRequestContext({
activeRequestId = null,
chatState = 'idle',
eventType,
eventRequestId = null,
activeRequestIds = [],
} = {}) {
if (eventType === 'ActiveRequests') {
if (!activeRequestId && activeRequestIds.length > 0) {
return { activeRequestId: activeRequestIds[0], promoteStreaming: true, allowMissingGrace: false };
}
if (activeRequestId && activeRequestIds.includes(activeRequestId)) {
return { activeRequestId, promoteStreaming: false, allowMissingGrace: false };
}
if (activeRequestId && !activeRequestIds.includes(activeRequestId)) {
if (chatState === 'waiting' && activeRequestIds.length > 0) {
return { activeRequestId: activeRequestIds[0], promoteStreaming: true, allowMissingGrace: false };
}
return {
activeRequestId,
promoteStreaming: false,
allowMissingGrace: chatState !== 'waiting',
};
}
return { activeRequestId, promoteStreaming: false, allowMissingGrace: false };
}
if (
eventRequestId &&
(chatState === 'waiting' || chatState === 'streaming') &&
(eventType === 'Message' || eventType === 'Finish') &&
(!activeRequestId || eventRequestId !== activeRequestId)
) {
return { activeRequestId: eventRequestId, promoteStreaming: chatState === 'waiting', allowMissingGrace: false };
}
return { activeRequestId, promoteStreaming: false, allowMissingGrace: false };
}