feat: 支持 Agent 运行刷新恢复

- 解耦 Runtime 与浏览器 SSE 订阅并增加 Redis 游标日志

- 支持正式聊天与草稿试运行刷新重连和权威终态恢复

- 完善显式取消、owner 丢失、容量限制与故障测试
This commit is contained in:
2026-08-20 11:18:10 +08:00
parent 310fc1fb58
commit fa07134cf8
36 changed files with 4876 additions and 322 deletions

View File

@@ -711,10 +711,7 @@ async function sendContent(rawContent: string) {
})),
imageUploadIds: composer.images.uploadIds.value,
images: composer.images.readyItems.value.map((item) => ({ ...item })),
onInputAccepted: () =>
composer.markAccepted().catch(() => {
ElMessage.warning('消息已发送,草稿将在过期后自动清理');
}),
onInputAccepted: markComposerInputAccepted,
prompt: content,
sessionId: composer.sessionId.value,
});
@@ -752,6 +749,12 @@ async function activateComposer(agentId: string, sessionId?: string) {
}
}
function markComposerInputAccepted() {
return composer.markAccepted().catch(() => {
ElMessage.warning('消息已发送,草稿将在过期后自动清理');
});
}
function chooseAttachmentFiles() {
attachmentFileInputRef.value?.click();
}
@@ -961,7 +964,7 @@ function handlePromptKeydown(event: Event | KeyboardEvent) {
}
}
function handleStop() {
async function handleStop() {
if (!canStopRuntime.value) {
return;
}
@@ -970,7 +973,12 @@ function handleStop() {
currentSessionId.value
? currentSessionId.value
: agentChatRuntimeManager.getLatestSnapshot()?.sessionId;
agentChatRuntimeManager.stop(runningSessionId);
try {
await agentChatRuntimeManager.stop(runningSessionId);
} catch (error) {
ElMessage.error(error instanceof Error ? error.message : '停止失败');
return;
}
if (runningSessionId === currentSessionId.value) {
syncRuntimeSnapshot(currentSessionId.value);
} else {
@@ -1094,6 +1102,13 @@ async function bootstrap() {
await Promise.all([loadAgents(), loadSessions(), loadKnowledges()]);
const routeSessionId = String(route.query.sessionId || '');
if (routeSessionId) {
const runtimeSnapshot = agentChatRuntimeManager.getSnapshot(routeSessionId);
if (runtimeSnapshot?.sending) {
syncRuntimeSnapshot(routeSessionId);
await activateComposer(selectedAgentId.value, routeSessionId);
agentChatRuntimeManager.resume(routeSessionId, markComposerInputAccepted);
return;
}
await loadConversation(routeSessionId);
return;
}
@@ -1101,6 +1116,10 @@ async function bootstrap() {
if (latestSnapshot?.items.length) {
syncRuntimeSnapshot(latestSnapshot.sessionId);
await activateComposer(selectedAgentId.value, latestSnapshot.sessionId);
agentChatRuntimeManager.resume(
latestSnapshot.sessionId,
markComposerInputAccepted,
);
return;
}
await activateComposer(selectedAgentId.value);