mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-08-29 06:00:52 +02:00
Fix finalization cleanup for pending tool executions
This commit is contained in:
+106
-12
@@ -3372,6 +3372,18 @@ function handleStreamEvent(event, progressElement, progressId,
|
||||
});
|
||||
break;
|
||||
|
||||
case 'finalization_pending_tools_cancelled': {
|
||||
const d = event.data || {};
|
||||
markToolExecutionItemsCancelled(timeline, autoCancelledExecutionIdsFromData(d));
|
||||
addTimelineItem(timeline, 'progress', {
|
||||
title: '工具执行已收尾',
|
||||
message: event.message,
|
||||
data: d,
|
||||
expanded: false
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case 'hitl_audit_agent_started': {
|
||||
const auditData = Object.assign({}, event.data || {}, {
|
||||
reviewer: 'audit_agent',
|
||||
@@ -3984,6 +3996,7 @@ function handleStreamEvent(event, progressElement, progressId,
|
||||
const responseData = event.data || {};
|
||||
const mcpIds = mergeMcpExecutionIDLists(typeof getMcpIds === 'function' ? (getMcpIds() || []) : [], responseData.mcpExecutionIds || []);
|
||||
setMcpIds(mcpIds);
|
||||
markToolExecutionItemsCancelled(timeline, autoCancelledExecutionIdsFromData(responseData));
|
||||
|
||||
// 更新对话ID
|
||||
if (responseData.conversationId) {
|
||||
@@ -5886,6 +5899,9 @@ function getToolResultDisplayState(data, opts) {
|
||||
}
|
||||
return { kind: 'background_running', isError: false, success: false };
|
||||
}
|
||||
if (explicitStatus === 'cancelled' || explicitStatus === 'canceled') {
|
||||
return { kind: 'cancelled', isError: true, success: false };
|
||||
}
|
||||
const parts = [];
|
||||
if (opts.rawText != null) parts.push(String(opts.rawText));
|
||||
collectToolResultTextParts(data.result, parts, 0);
|
||||
@@ -5917,6 +5933,13 @@ function getBackgroundRunningToolLabel() {
|
||||
return '后台执行中';
|
||||
}
|
||||
|
||||
function toolDisplayStatusFromState(displayState) {
|
||||
if (!displayState) return 'completed';
|
||||
if (displayState.kind === 'background_running') return 'background_running';
|
||||
if (displayState.kind === 'cancelled') return 'cancelled';
|
||||
return displayState.isError ? 'failed' : 'completed';
|
||||
}
|
||||
|
||||
function buildToolResultSectionHtml(data, opts) {
|
||||
opts = opts || {};
|
||||
const _t = function (k, o) {
|
||||
@@ -6158,7 +6181,10 @@ function mergeToolResultIntoCallItem(item, data, options) {
|
||||
}
|
||||
item.dataset.toolResultMerged = '1';
|
||||
item.dataset.toolSuccess = (!displayState.isError && !backgroundRunning) ? '1' : '0';
|
||||
item.dataset.toolDisplayStatus = backgroundRunning ? 'background_running' : (displayState.isError ? 'failed' : 'completed');
|
||||
item.dataset.toolDisplayStatus = toolDisplayStatusFromState(displayState);
|
||||
if (data.executionId != null && String(data.executionId).trim() !== '') {
|
||||
item.dataset.toolExecutionId = String(data.executionId).trim();
|
||||
}
|
||||
item.classList.remove('tool-call-running', 'tool-call-completed', 'tool-call-failed');
|
||||
item.classList.add(backgroundRunning ? 'tool-call-running' : (displayState.isError ? 'tool-call-failed' : 'tool-call-completed'));
|
||||
applyToolCallStatus(item, item.dataset.toolDisplayStatus);
|
||||
@@ -6198,7 +6224,10 @@ function mergeToolResultIntoCallItem(item, data, options) {
|
||||
|
||||
item.dataset.toolResultMerged = '1';
|
||||
item.dataset.toolSuccess = (!displayState.isError && !backgroundRunning) ? '1' : '0';
|
||||
item.dataset.toolDisplayStatus = backgroundRunning ? 'background_running' : (displayState.isError ? 'failed' : 'completed');
|
||||
item.dataset.toolDisplayStatus = toolDisplayStatusFromState(displayState);
|
||||
if (data.executionId != null && String(data.executionId).trim() !== '') {
|
||||
item.dataset.toolExecutionId = String(data.executionId).trim();
|
||||
}
|
||||
item.classList.remove('tool-call-running', 'tool-call-completed', 'tool-call-failed');
|
||||
item.classList.add(backgroundRunning ? 'tool-call-running' : (displayState.isError ? 'tool-call-failed' : 'tool-call-completed'));
|
||||
applyToolCallStatus(item, item.dataset.toolDisplayStatus);
|
||||
@@ -6361,6 +6390,9 @@ function getToolCallStatusPresentation(status) {
|
||||
if (normalized === 'failed') {
|
||||
return { status: normalized, itemClass: 'tool-call-failed', badgeClass: 'tool-status-failed', label: translate('timeline.execFailed', '执行失败'), icon: '❌ ' };
|
||||
}
|
||||
if (normalized === 'cancelled' || normalized === 'canceled') {
|
||||
return { status: 'cancelled', itemClass: 'tool-call-failed', badgeClass: 'tool-status-failed', label: translate('tasks.statusCancelled', '已取消'), icon: '⛔ ' };
|
||||
}
|
||||
if (normalized === 'result_missing') {
|
||||
return { status: normalized, itemClass: 'tool-call-incomplete', badgeClass: 'tool-status-incomplete', label: translate('timeline.resultMissing', '结果记录缺失'), icon: '⚠️ ' };
|
||||
}
|
||||
@@ -6401,6 +6433,52 @@ function updateToolCallStatus(progressId, toolCallId, status) {
|
||||
applyToolCallStatus(item, status);
|
||||
}
|
||||
|
||||
function normalizeExecutionIdList(value) {
|
||||
const input = Array.isArray(value) ? value : (value == null ? [] : [value]);
|
||||
const seen = new Set();
|
||||
const out = [];
|
||||
input.forEach(function (v) {
|
||||
const s = String(v == null ? '' : v).trim();
|
||||
if (!s || seen.has(s)) return;
|
||||
seen.add(s);
|
||||
out.push(s);
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
function autoCancelledExecutionIdsFromData(data) {
|
||||
data = data || {};
|
||||
return normalizeExecutionIdList(data.autoCancelledPendingExecutionIds || data.autoCancelledExecutionIds || []);
|
||||
}
|
||||
|
||||
function markToolExecutionItemsCancelled(root, executionIds) {
|
||||
const ids = normalizeExecutionIdList(executionIds);
|
||||
if (!root || ids.length === 0) return 0;
|
||||
const idSet = new Set(ids);
|
||||
let count = 0;
|
||||
root.querySelectorAll('.timeline-item[data-tool-execution-id]').forEach(function (item) {
|
||||
const execId = String(item.dataset.toolExecutionId || '').trim();
|
||||
if (!execId || !idSet.has(execId)) return;
|
||||
item.dataset.toolSuccess = '0';
|
||||
item.dataset.toolDisplayStatus = 'cancelled';
|
||||
item.classList.remove('tool-call-running', 'tool-call-completed', 'tool-call-incomplete');
|
||||
item.classList.add('tool-call-failed');
|
||||
const state = toolCallDetailStateByItemId.get(item.id);
|
||||
if (state && state.resultData && typeof state.resultData === 'object') {
|
||||
state.resultData = Object.assign({}, state.resultData, {
|
||||
status: 'cancelled',
|
||||
success: false,
|
||||
isError: true
|
||||
});
|
||||
state.pending = false;
|
||||
setToolCallDetailState(item, state);
|
||||
}
|
||||
applyToolCallStatus(item, 'cancelled');
|
||||
count++;
|
||||
});
|
||||
return count;
|
||||
}
|
||||
|
||||
// 添加时间线项目
|
||||
function buildWorkflowConditionResultHtml(data) {
|
||||
const output = (data && data.output) || {};
|
||||
@@ -6547,17 +6625,23 @@ function addTimelineItem(timeline, type, options) {
|
||||
const mergedDisplayState = merged ? getToolResultDisplayState(merged) : null;
|
||||
const mergedBackgroundRunning = mergedDisplayState && mergedDisplayState.kind === 'background_running';
|
||||
const terminalStatus = String(options.toolStatus || '').toLowerCase();
|
||||
const forcedStatus = (terminalStatus === 'completed' || terminalStatus === 'failed' || terminalStatus === 'cancelled' || terminalStatus === 'canceled')
|
||||
? (terminalStatus === 'canceled' ? 'cancelled' : terminalStatus)
|
||||
: '';
|
||||
if (merged) {
|
||||
item.dataset.toolResultMerged = '1';
|
||||
item.dataset.toolSuccess = (!mergedDisplayState.isError && !mergedBackgroundRunning) ? '1' : '0';
|
||||
item.dataset.toolDisplayStatus = mergedBackgroundRunning ? 'background_running' : (mergedDisplayState.isError ? 'failed' : 'completed');
|
||||
item.classList.add(mergedBackgroundRunning ? 'tool-call-running' : (mergedDisplayState.isError ? 'tool-call-failed' : 'tool-call-completed'));
|
||||
item.dataset.toolSuccess = forcedStatus ? (forcedStatus === 'completed' ? '1' : '0') : ((!mergedDisplayState.isError && !mergedBackgroundRunning) ? '1' : '0');
|
||||
item.dataset.toolDisplayStatus = forcedStatus || toolDisplayStatusFromState(mergedDisplayState);
|
||||
if (merged.executionId != null && String(merged.executionId).trim() !== '') {
|
||||
item.dataset.toolExecutionId = String(merged.executionId).trim();
|
||||
}
|
||||
item.classList.add(item.dataset.toolDisplayStatus === 'background_running' ? 'tool-call-running' : (item.dataset.toolDisplayStatus === 'completed' ? 'tool-call-completed' : 'tool-call-failed'));
|
||||
if (d._mergedResultDetailId) {
|
||||
item.dataset.toolResultDetailId = String(d._mergedResultDetailId);
|
||||
}
|
||||
} else if (terminalStatus === 'completed' || terminalStatus === 'failed') {
|
||||
} else if (terminalStatus === 'completed' || terminalStatus === 'failed' || terminalStatus === 'cancelled' || terminalStatus === 'canceled') {
|
||||
item.dataset.toolSuccess = terminalStatus === 'completed' ? '1' : '0';
|
||||
item.dataset.toolDisplayStatus = terminalStatus;
|
||||
item.dataset.toolDisplayStatus = terminalStatus === 'canceled' ? 'cancelled' : terminalStatus;
|
||||
item.classList.add(terminalStatus === 'completed' ? 'tool-call-completed' : 'tool-call-failed');
|
||||
} else if (terminalStatus === 'result_missing') {
|
||||
item.dataset.toolDisplayStatus = 'result_missing';
|
||||
@@ -6588,7 +6672,10 @@ function addTimelineItem(timeline, type, options) {
|
||||
}
|
||||
item.dataset.toolName = (d.toolName != null && d.toolName !== '') ? String(d.toolName) : '';
|
||||
item.dataset.toolSuccess = (!displayState.isError && displayState.kind !== 'background_running') ? '1' : '0';
|
||||
item.dataset.toolDisplayStatus = displayState.kind === 'background_running' ? 'background_running' : (displayState.isError ? 'failed' : 'completed');
|
||||
item.dataset.toolDisplayStatus = toolDisplayStatusFromState(displayState);
|
||||
if (d.executionId != null && String(d.executionId).trim() !== '') {
|
||||
item.dataset.toolExecutionId = String(d.executionId).trim();
|
||||
}
|
||||
}
|
||||
if (type === 'eino_usage_summary' && options.data) {
|
||||
const d = options.data;
|
||||
@@ -6658,10 +6745,14 @@ function addTimelineItem(timeline, type, options) {
|
||||
const mergedDisplayState = merged ? getToolResultDisplayState(merged) : null;
|
||||
const mergedBackgroundRunning = mergedDisplayState && mergedDisplayState.kind === 'background_running';
|
||||
const terminalStatus = String(options.toolStatus || '').toLowerCase();
|
||||
const hasTerminalStatus = terminalStatus === 'completed' || terminalStatus === 'failed';
|
||||
const forcedStatus = (terminalStatus === 'completed' || terminalStatus === 'failed' || terminalStatus === 'cancelled' || terminalStatus === 'canceled')
|
||||
? (terminalStatus === 'canceled' ? 'cancelled' : terminalStatus)
|
||||
: '';
|
||||
const hasTerminalStatus = terminalStatus === 'completed' || terminalStatus === 'failed' || terminalStatus === 'cancelled' || terminalStatus === 'canceled';
|
||||
const hasHistoricalStatus = hasTerminalStatus || terminalStatus === 'result_missing';
|
||||
if (merged) {
|
||||
item.classList.add(mergedBackgroundRunning ? 'tool-call-running' : (mergedDisplayState.isError ? 'tool-call-failed' : 'tool-call-completed'));
|
||||
const statusForClass = forcedStatus || toolDisplayStatusFromState(mergedDisplayState);
|
||||
item.classList.add(statusForClass === 'background_running' ? 'tool-call-running' : (statusForClass === 'completed' ? 'tool-call-completed' : 'tool-call-failed'));
|
||||
} else if (hasTerminalStatus) {
|
||||
item.classList.add(terminalStatus === 'completed' ? 'tool-call-completed' : 'tool-call-failed');
|
||||
} else if (terminalStatus === 'result_missing') {
|
||||
@@ -6671,7 +6762,7 @@ function addTimelineItem(timeline, type, options) {
|
||||
}
|
||||
setToolCallDetailState(item, {
|
||||
args: args,
|
||||
resultData: merged || null,
|
||||
resultData: (merged && forcedStatus) ? Object.assign({}, merged, { status: forcedStatus, success: forcedStatus === 'completed', isError: forcedStatus !== 'completed' }) : (merged || null),
|
||||
pending: !merged && !hasHistoricalStatus && !options.skipPendingResult,
|
||||
processDetailId: options.processDetailId || '',
|
||||
resultDetailId: data._mergedResultDetailId || (merged && merged.processDetailId) || '',
|
||||
@@ -6723,7 +6814,10 @@ function addTimelineItem(timeline, type, options) {
|
||||
payloadDeferred: data._payloadDeferred === true,
|
||||
payloadLoaded: data._payloadDeferred !== true
|
||||
});
|
||||
item.dataset.toolDisplayStatus = displayState.kind === 'background_running' ? 'background_running' : (displayState.isError ? 'failed' : 'completed');
|
||||
item.dataset.toolDisplayStatus = toolDisplayStatusFromState(displayState);
|
||||
if (data.executionId != null && String(data.executionId).trim() !== '') {
|
||||
item.dataset.toolExecutionId = String(data.executionId).trim();
|
||||
}
|
||||
item.classList.add(displayState.kind === 'background_running' ? 'tool-call-running' : (displayState.isError ? 'tool-call-failed' : 'tool-call-completed'));
|
||||
} else if (type === 'cancelled') {
|
||||
const taskCancelledLabel = typeof window.t === 'function' ? window.t('chat.taskCancelled') : '任务已取消';
|
||||
|
||||
Reference in New Issue
Block a user