feat(goal-run): add multi-checkpoint goal orchestration with H5 and admin surfaces.
Persist goal runs in MySQL, bind agent runs to checkpoints, expose awaiting-approval UX in chat, and add admin inspection routes with local verify scripts. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1041,7 +1041,7 @@ export function useTKMindChat(
|
||||
return;
|
||||
}
|
||||
},
|
||||
[clearActiveRequestMissingTimer, rememberRecentContext, syncSessionMessages],
|
||||
[canUseLongTermMemory, clearActiveRequestMissingTimer, rememberRecentContext, syncSessionMessages],
|
||||
);
|
||||
|
||||
const subscribeToSession = useCallback(
|
||||
@@ -1473,6 +1473,7 @@ export function useTKMindChat(
|
||||
imageGenerationMode?: ImageGenerationMode;
|
||||
selectedChatSkill?: string;
|
||||
fileAttachments?: ChatFileAttachment[];
|
||||
goalRunId?: string;
|
||||
},
|
||||
imageUrls?: string[],
|
||||
previewImageUrls?: string[],
|
||||
@@ -1571,6 +1572,7 @@ export function useTKMindChat(
|
||||
.filter(Boolean),
|
||||
}
|
||||
: {}),
|
||||
...(options?.goalRunId ? { goalRunId: options.goalRunId } : {}),
|
||||
},
|
||||
);
|
||||
const runSessionId = createdRun.sessionId ?? activeSessionId;
|
||||
@@ -1774,6 +1776,87 @@ export function useTKMindChat(
|
||||
[session, pendingTool],
|
||||
);
|
||||
|
||||
const followAgentRun = useCallback(
|
||||
async (createdRun: AgentRun) => {
|
||||
if (
|
||||
chatStateRef.current === 'streaming' ||
|
||||
chatStateRef.current === 'loading' ||
|
||||
chatStateRef.current === 'connecting' ||
|
||||
chatStateRef.current === 'waiting'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const submitToken = connectTokenRef.current;
|
||||
let activeSessionId = sessionRef.current?.id ?? createdRun.sessionId ?? null;
|
||||
if (createdRun.requestId) {
|
||||
activeRequestId.current = createdRun.requestId;
|
||||
}
|
||||
setChatState('waiting');
|
||||
chatStateRef.current = 'waiting';
|
||||
setError(null);
|
||||
agentRunPendingRef.current = true;
|
||||
|
||||
try {
|
||||
const finishedRun =
|
||||
createdRun.status === 'succeeded'
|
||||
? createdRun
|
||||
: await waitForAgentRunWithDirectChatPreview(createdRun.id, {
|
||||
isCancelled: () => submitToken !== connectTokenRef.current,
|
||||
onSessionId: (sessionId) => {
|
||||
if (submitToken !== connectTokenRef.current) return;
|
||||
if (activeSessionId !== sessionId) {
|
||||
activeSessionId = sessionId;
|
||||
const nextSession: Session = {
|
||||
id: sessionId,
|
||||
name: 'New Chat',
|
||||
message_count: messagesRef.current.length,
|
||||
working_dir: '',
|
||||
};
|
||||
writeStoredSessionId(userRef.current?.id, sessionId);
|
||||
setSession(nextSession);
|
||||
setSessions((prev) => prependUnique(prev, nextSession));
|
||||
}
|
||||
if (!isDirectChatSessionId(sessionId)) {
|
||||
subscribeToSession(sessionId);
|
||||
if (shouldPromoteSessionIdToStreaming(chatStateRef.current)) {
|
||||
setChatState('streaming');
|
||||
}
|
||||
}
|
||||
},
|
||||
onMessages: (snapshotMessages) => {
|
||||
if (submitToken !== connectTokenRef.current) return;
|
||||
messagesRef.current = mergeConversationSnapshot(
|
||||
messagesRef.current,
|
||||
snapshotMessages,
|
||||
);
|
||||
messageHistoryLoadedCountRef.current = messagesRef.current.length;
|
||||
setMessages(messagesRef.current);
|
||||
},
|
||||
});
|
||||
if (submitToken !== connectTokenRef.current) return;
|
||||
agentRunPendingRef.current = false;
|
||||
activeSessionId = finishedRun.sessionId ?? activeSessionId;
|
||||
if (!activeSessionId) {
|
||||
clearActiveRequestMissingTimer();
|
||||
activeRequestId.current = null;
|
||||
setChatState('idle');
|
||||
return;
|
||||
}
|
||||
subscribeToSession(activeSessionId);
|
||||
setChatState('streaming');
|
||||
scheduleReplyRecoverySync(activeSessionId, submitToken);
|
||||
} catch (err) {
|
||||
agentRunPendingRef.current = false;
|
||||
setError(err instanceof Error ? err.message : String(err));
|
||||
setChatState('error');
|
||||
clearActiveRequestMissingTimer();
|
||||
activeRequestId.current = null;
|
||||
}
|
||||
},
|
||||
[clearActiveRequestMissingTimer, scheduleReplyRecoverySync, subscribeToSession],
|
||||
);
|
||||
|
||||
const newSession = useCallback(async () => {
|
||||
const token = ++connectTokenRef.current;
|
||||
const previousSession = sessionRef.current;
|
||||
@@ -1974,6 +2057,7 @@ export function useTKMindChat(
|
||||
grantedSkills,
|
||||
submit,
|
||||
stop,
|
||||
followAgentRun,
|
||||
approveTool,
|
||||
newSession,
|
||||
rememberCurrentContext,
|
||||
|
||||
Reference in New Issue
Block a user