mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-08-01 16:38:48 +02:00
Add files via upload
This commit is contained in:
@@ -3990,6 +3990,17 @@ html[data-theme="dark"] .new-chat-btn:focus-visible {
|
||||
border-top-left-radius: 2px;
|
||||
}
|
||||
|
||||
.message.assistant.assistant-not-finalized .message-bubble {
|
||||
border-color: rgba(245, 124, 0, 0.28);
|
||||
border-left: 4px solid #f57c00;
|
||||
background: linear-gradient(90deg, rgba(245, 124, 0, 0.08), rgba(245, 124, 0, 0.035));
|
||||
box-shadow: 0 6px 18px rgba(245, 124, 0, 0.08);
|
||||
}
|
||||
|
||||
.message.assistant.assistant-not-finalized .message-bubble strong:first-child {
|
||||
color: #b45309;
|
||||
}
|
||||
|
||||
.message.assistant .message-bubble pre {
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
@@ -7381,6 +7392,16 @@ html[data-theme="dark"] .login-card .login-submit:disabled {
|
||||
background: rgba(245, 124, 0, 0.09);
|
||||
}
|
||||
|
||||
.timeline-item-finalization_check {
|
||||
border-left-color: #f57c00;
|
||||
background: linear-gradient(90deg, rgba(245, 124, 0, 0.1), rgba(96, 125, 139, 0.045));
|
||||
}
|
||||
|
||||
.timeline-item-finalization_check .timeline-item-title {
|
||||
color: #9a5200;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.timeline-item-tool_calls_detected {
|
||||
border-left-color: #0277bd;
|
||||
background: rgba(2, 119, 189, 0.06);
|
||||
@@ -22967,6 +22988,18 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.webshell-ai-timeline-finalization_check {
|
||||
border-left: 3px solid #f57c00;
|
||||
padding-left: 10px;
|
||||
background: linear-gradient(90deg, rgba(245, 124, 0, 0.08), transparent);
|
||||
}
|
||||
|
||||
.webshell-ai-timeline-finalization_check .webshell-ai-timeline-title {
|
||||
color: #9a5200;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.webshell-ai-timeline-msg {
|
||||
margin-top: 4px;
|
||||
padding-left: 0;
|
||||
@@ -23144,6 +23177,11 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
.webshell-ai-msg.assistant.webshell-ai-candidate-output {
|
||||
border-color: rgba(245, 124, 0, 0.28);
|
||||
background: linear-gradient(90deg, rgba(245, 124, 0, 0.08), rgba(245, 124, 0, 0.035));
|
||||
box-shadow: 0 6px 16px rgba(245, 124, 0, 0.08);
|
||||
}
|
||||
.webshell-ai-msg.assistant.webshell-ai-msg-error {
|
||||
max-width: 72%;
|
||||
border-color: rgba(220, 53, 69, 0.35);
|
||||
@@ -36232,12 +36270,42 @@ html[data-theme="dark"] .timeline-item-user_interrupt_continue {
|
||||
background: rgba(251, 191, 36, 0.08);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .timeline-item-finalization_check {
|
||||
background: linear-gradient(90deg, rgba(251, 191, 36, 0.1), rgba(30, 41, 59, 0.18));
|
||||
border-left-color: #fbbf24;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .timeline-item-finalization_check .timeline-item-title,
|
||||
html[data-theme="dark"] .webshell-ai-timeline-finalization_check .webshell-ai-timeline-title {
|
||||
color: #fbbf24;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .message.assistant .message-bubble {
|
||||
background: #111827;
|
||||
color: var(--text-primary);
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .message.assistant.assistant-not-finalized .message-bubble {
|
||||
border-color: rgba(251, 191, 36, 0.24);
|
||||
border-left-color: #fbbf24;
|
||||
background: linear-gradient(90deg, rgba(251, 191, 36, 0.08), rgba(17, 24, 39, 0.9));
|
||||
box-shadow: 0 8px 22px rgba(0, 0, 0, 0.22);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .message.assistant.assistant-not-finalized .message-bubble strong:first-child {
|
||||
color: #fbbf24;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .webshell-ai-timeline-finalization_check {
|
||||
border-left-color: #fbbf24;
|
||||
background: linear-gradient(90deg, rgba(251, 191, 36, 0.08), rgba(17, 24, 39, 0.12));
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .webshell-ai-msg.assistant.webshell-ai-candidate-output {
|
||||
background: linear-gradient(90deg, rgba(251, 191, 36, 0.08), rgba(17, 24, 39, 0.12));
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .message-copy-btn {
|
||||
background: #1f2937;
|
||||
border-color: #334155;
|
||||
|
||||
@@ -1147,6 +1147,7 @@ async function sendAssetsToChat(assets, template) {
|
||||
input.value = message;
|
||||
if (typeof adjustTextareaHeight === 'function') adjustTextareaHeight(input);
|
||||
// 消息流可能持续很久;启动发送即可返回,让提交弹窗立即关闭。
|
||||
window.__csNextChatFinalizationPolicy = { requireExecutionEvidence: true };
|
||||
void sendMessage();
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +80,7 @@ let chatAttachmentSeq = 0;
|
||||
|
||||
// 对话模式:eino_single = Eino ADK 单代理(/api/eino-agent/stream);deep / plan_execute / supervisor = Eino 多代理(/api/multi-agent/stream,请求体 orchestration)
|
||||
const AGENT_MODE_STORAGE_KEY = 'cyberstrike-chat-agent-mode';
|
||||
const AGENT_MODE_CONVERSATION_STORAGE_PREFIX = 'cyberstrike-chat-agent-mode:conversation';
|
||||
const AI_CHANNEL_STORAGE_KEY = 'cyberstrike-chat-ai-channel';
|
||||
const REASONING_MODE_LS = 'cyberstrike-chat-reasoning-mode';
|
||||
const REASONING_EFFORT_LS = 'cyberstrike-chat-reasoning-effort';
|
||||
@@ -733,6 +734,44 @@ function chatAgentModeNormalizeStored(stored, cfg) {
|
||||
return CHAT_AGENT_MODE_EINO_SINGLE;
|
||||
}
|
||||
|
||||
function normalizeConversationAgentModeForUI(mode) {
|
||||
const v = String(mode || '').trim().toLowerCase().replace(/-/g, '_');
|
||||
if (chatAgentModeIsEinoSingle(v)) return v;
|
||||
if (chatAgentModeIsEino(v)) {
|
||||
return multiAgentAPIEnabled ? v : CHAT_AGENT_MODE_EINO_SINGLE;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function conversationAgentModeStorageKey(conversationId) {
|
||||
return `${AGENT_MODE_CONVERSATION_STORAGE_PREFIX}:${String(conversationId || '').trim()}`;
|
||||
}
|
||||
|
||||
function readConversationAgentModePreference(conversationId) {
|
||||
if (!conversationId) return '';
|
||||
try {
|
||||
return normalizeConversationAgentModeForUI(localStorage.getItem(conversationAgentModeStorageKey(conversationId)) || '');
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function saveConversationAgentModePreference(conversationId, mode) {
|
||||
const normalized = normalizeConversationAgentModeForUI(mode);
|
||||
if (!conversationId || !normalized) return;
|
||||
try {
|
||||
localStorage.setItem(conversationAgentModeStorageKey(conversationId), normalized);
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
|
||||
function applyConversationAgentMode(conversationId, conversation) {
|
||||
const saved = readConversationAgentModePreference(conversationId);
|
||||
const fromServer = normalizeConversationAgentModeForUI(conversation && (conversation.agentMode || conversation.agent_mode));
|
||||
const mode = saved || fromServer;
|
||||
if (!mode) return;
|
||||
syncAgentModeFromValue(mode);
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.csaiHitlGlobalToolWhitelist = window.csaiHitlGlobalToolWhitelist || [];
|
||||
window.csaiHitlDefaultReviewer = window.csaiHitlDefaultReviewer || 'human';
|
||||
@@ -1098,6 +1137,7 @@ function toggleAgentModePanel() {
|
||||
function selectAgentMode(mode) {
|
||||
const ok = chatAgentModeIsEinoSingle(mode) || chatAgentModeIsEino(mode);
|
||||
if (!ok) return;
|
||||
saveConversationAgentModePreference(currentConversationId, mode);
|
||||
try {
|
||||
localStorage.setItem(AGENT_MODE_STORAGE_KEY, mode);
|
||||
} catch (e) { /* ignore */ }
|
||||
@@ -1351,6 +1391,10 @@ async function sendMessage() {
|
||||
conversationId: currentConversationId,
|
||||
role: typeof getCurrentRole === 'function' ? getCurrentRole() : ''
|
||||
};
|
||||
if (window.__csNextChatFinalizationPolicy && typeof window.__csNextChatFinalizationPolicy === 'object') {
|
||||
body.finalization = window.__csNextChatFinalizationPolicy;
|
||||
window.__csNextChatFinalizationPolicy = null;
|
||||
}
|
||||
let streamConversationId = body.conversationId ? String(body.conversationId) : null;
|
||||
const isStreamStillVisibleForRequest = function () {
|
||||
if (!document.getElementById(progressId)) return false;
|
||||
@@ -1405,6 +1449,7 @@ async function sendMessage() {
|
||||
try {
|
||||
const modeSel = document.getElementById('agent-mode-select');
|
||||
let modeVal = modeSel ? modeSel.value : CHAT_AGENT_MODE_EINO_SINGLE;
|
||||
saveConversationAgentModePreference(streamConversationId || currentConversationId, modeVal);
|
||||
const useMulti = multiAgentAPIEnabled && chatAgentModeIsEino(modeVal);
|
||||
const streamPath = useMulti ? '/api/multi-agent/stream' : '/api/eino-agent/stream';
|
||||
if (useMulti && modeVal) {
|
||||
@@ -4845,6 +4890,7 @@ async function loadConversation(conversationId) {
|
||||
if (typeof window.setCurrentRole === 'function') {
|
||||
window.setCurrentRole(conversationRoleName || '默认');
|
||||
}
|
||||
applyConversationAgentMode(conversationId, conversation);
|
||||
try {
|
||||
window.currentConversationId = conversationId;
|
||||
} catch (e) { /* ignore */ }
|
||||
|
||||
@@ -1334,6 +1334,7 @@ function scanFofaRow(encodedRowJson, clickEvent) {
|
||||
}
|
||||
if (autoSend) {
|
||||
if (typeof sendMessage === 'function') {
|
||||
window.__csNextChatFinalizationPolicy = { requireExecutionEvidence: true };
|
||||
sendMessage();
|
||||
} else {
|
||||
alert(_t('infoCollect.noSendMessage'));
|
||||
|
||||
+122
-3
@@ -198,6 +198,92 @@ function resolveFinalAssistantResponseText(finalMessage, streamState) {
|
||||
return finalMessage;
|
||||
}
|
||||
|
||||
function isFinalizedResponseData(data) {
|
||||
return !!(data && data.finalized === true);
|
||||
}
|
||||
|
||||
function hasFinalizationContract(data) {
|
||||
if (!data || typeof data !== 'object') return false;
|
||||
return Object.prototype.hasOwnProperty.call(data, 'finalized')
|
||||
|| Object.prototype.hasOwnProperty.call(data, 'finalizable')
|
||||
|| Object.prototype.hasOwnProperty.call(data, 'completionReason')
|
||||
|| Object.prototype.hasOwnProperty.call(data, 'evidenceVerified')
|
||||
|| Object.prototype.hasOwnProperty.call(data, 'missingChecks');
|
||||
}
|
||||
|
||||
function finalizationCheckTitle(data) {
|
||||
return isFinalizedResponseData(data) ? '最终回复检查通过' : '最终回复检查未通过';
|
||||
}
|
||||
|
||||
function finalizationReasonLabel(reason, status) {
|
||||
const key = String(reason || status || '').trim();
|
||||
const labels = {
|
||||
pending_tool_executions: '等待工具执行完成',
|
||||
missing_execution_evidence: '缺少完成态证据',
|
||||
awaiting_hitl: '等待人工确认',
|
||||
empty_response: '未捕获到有效回复',
|
||||
missing_finalization_contract: '缺少最终化证明',
|
||||
in_progress: '仍在验证',
|
||||
blocked: '检查未通过',
|
||||
failed: '任务失败',
|
||||
cancelled: '任务已取消',
|
||||
verified: '已验证'
|
||||
};
|
||||
return labels[key] || key || '检查未通过';
|
||||
}
|
||||
|
||||
function finalizationMissingCheckLabel(check) {
|
||||
const s = String(check || '').trim();
|
||||
if (!s) return '';
|
||||
if (s.indexOf('tool execution still queued or running') !== -1) return '仍有工具执行未结束';
|
||||
if (s.indexOf('execution evidence is required but no completed tool execution was recorded') !== -1) return '本轮要求执行证据,但没有 completed 工具记录';
|
||||
if (s.indexOf('workflow is awaiting HITL approval') !== -1) return '工作流正在等待人工确认';
|
||||
if (s.indexOf('assistant final text is empty') !== -1) return '未捕获到有效最终文本';
|
||||
if (s.indexOf('agent run status is ') === 0) return '任务状态仍为 ' + s.replace('agent run status is ', '');
|
||||
return s;
|
||||
}
|
||||
|
||||
function compactStringList(values, limit) {
|
||||
const arr = Array.isArray(values) ? values.filter(Boolean).map(String) : [];
|
||||
const max = limit || 3;
|
||||
if (arr.length <= max) return arr;
|
||||
return arr.slice(0, max).concat('另 ' + (arr.length - max) + ' 项');
|
||||
}
|
||||
|
||||
function finalizationNoticeMarkdown(responseData, eventMessage) {
|
||||
const hasContract = hasFinalizationContract(responseData);
|
||||
const reason = hasContract
|
||||
? finalizationReasonLabel(responseData && responseData.completionReason, responseData && responseData.status)
|
||||
: finalizationReasonLabel('missing_finalization_contract');
|
||||
const lines = ['**仍在验证,暂不生成最终结论**', '', '状态:' + reason];
|
||||
const pending = compactStringList(responseData && responseData.pendingExecutionIds, 3);
|
||||
if (pending.length) {
|
||||
lines.push('待完成工具:`' + pending.join('`, `') + '`');
|
||||
}
|
||||
const rawMissingChecks = responseData && responseData.missingChecks;
|
||||
const missingChecks = Array.isArray(rawMissingChecks)
|
||||
? rawMissingChecks
|
||||
: (rawMissingChecks ? [rawMissingChecks] : []);
|
||||
const missing = compactStringList(missingChecks.map(finalizationMissingCheckLabel).filter(Boolean), 3);
|
||||
if (missing.length) {
|
||||
lines.push('待完成检查:' + missing.join(';'));
|
||||
}
|
||||
if (!hasContract && eventMessage != null && String(eventMessage).trim() !== '') {
|
||||
lines.push('', '候选输出已移入过程详情,避免误判为最终结论。');
|
||||
}
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
function markAssistantFinalizationState(assistantMessageId, responseData) {
|
||||
const assistantElement = document.getElementById(assistantMessageId);
|
||||
if (!assistantElement) return;
|
||||
const finalized = isFinalizedResponseData(responseData);
|
||||
assistantElement.dataset.finalized = finalized ? 'true' : 'false';
|
||||
assistantElement.dataset.finalizationStatus = responseData && responseData.status ? String(responseData.status) : '';
|
||||
assistantElement.classList.toggle('assistant-finalized', finalized);
|
||||
assistantElement.classList.toggle('assistant-not-finalized', !finalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* 主通道 response 结束时:将流式占位条目固化为 planning(与后端 flushResponsePlan 落库类型一致),
|
||||
* 避免 integrateProgressToMCPSection 快照前删除占位导致「助手输出」仅刷新后才出现。
|
||||
@@ -2440,6 +2526,26 @@ function handleStreamEvent(event, progressElement, progressId,
|
||||
});
|
||||
break;
|
||||
|
||||
case 'finalization_check':
|
||||
const finalizationCheckData = event.data || {};
|
||||
const finalizationCheckPassed = isFinalizedResponseData(finalizationCheckData);
|
||||
addTimelineItem(timeline, 'finalization_check', {
|
||||
title: finalizationCheckTitle(finalizationCheckData),
|
||||
message: finalizationCheckPassed ? (event.message || '最终回复检查通过。') : finalizationNoticeMarkdown(finalizationCheckData, event.message),
|
||||
data: event.data,
|
||||
expanded: !finalizationCheckPassed
|
||||
});
|
||||
break;
|
||||
|
||||
case 'finalization_auto_continue':
|
||||
addTimelineItem(timeline, 'progress', {
|
||||
title: '继续验证',
|
||||
message: event.message,
|
||||
data: event.data,
|
||||
expanded: false
|
||||
});
|
||||
break;
|
||||
|
||||
case 'hitl_interrupt':
|
||||
const hitlTargetItem = findToolCallItemForHitl(timeline, event.data || {});
|
||||
if (hitlTargetItem && hitlTargetItem.id) {
|
||||
@@ -2958,7 +3064,12 @@ function handleStreamEvent(event, progressElement, progressId,
|
||||
const streamState = responseStreamStateByProgressId.get(progressId);
|
||||
const existingAssistantId = streamState?.assistantId || getAssistantId();
|
||||
let assistantIdFinal = existingAssistantId;
|
||||
const bubbleText = resolveFinalAssistantResponseText(event.message, streamState);
|
||||
const responseFinalized = isFinalizedResponseData(responseData);
|
||||
const responseHasFinalizationContract = hasFinalizationContract(responseData);
|
||||
const resolvedResponseText = resolveFinalAssistantResponseText(event.message, streamState);
|
||||
const bubbleText = responseFinalized
|
||||
? resolvedResponseText
|
||||
: finalizationNoticeMarkdown(responseData, event.message);
|
||||
|
||||
if (!assistantIdFinal) {
|
||||
assistantIdFinal = addMessage('assistant', bubbleText, mcpIds, progressId);
|
||||
@@ -2967,11 +3078,12 @@ function handleStreamEvent(event, progressElement, progressId,
|
||||
setAssistantId(assistantIdFinal);
|
||||
updateAssistantBubbleContent(assistantIdFinal, bubbleText, true);
|
||||
}
|
||||
markAssistantFinalizationState(assistantIdFinal, responseData);
|
||||
|
||||
// 将 response_start/response_delta 占位固化为 planning,与后端落库一致后再快照过程详情
|
||||
if (streamState && streamState.itemId) {
|
||||
finalizeMainResponseStreamItem(streamState, event.message, responseData);
|
||||
} else if (timeline && bubbleText && String(bubbleText).trim() && !isEinoEmptyResponsePlaceholder(event.message)) {
|
||||
finalizeMainResponseStreamItem(streamState, responseFinalized ? event.message : '', responseData);
|
||||
} else if (timeline && responseFinalized && bubbleText && String(bubbleText).trim() && !isEinoEmptyResponsePlaceholder(event.message)) {
|
||||
addTimelineItem(timeline, 'planning', {
|
||||
title: typeof einoMainStreamPlanningTitle === 'function'
|
||||
? einoMainStreamPlanningTitle(responseData)
|
||||
@@ -2980,6 +3092,13 @@ function handleStreamEvent(event, progressElement, progressId,
|
||||
data: responseData,
|
||||
expanded: false
|
||||
});
|
||||
} else if (timeline && !responseFinalized && !responseHasFinalizationContract && resolvedResponseText && String(resolvedResponseText).trim()) {
|
||||
addTimelineItem(timeline, 'finalization_check', {
|
||||
title: '候选输出缺少最终化证明',
|
||||
message: resolvedResponseText,
|
||||
data: Object.assign({}, responseData, { missingFinalizationContract: true }),
|
||||
expanded: true
|
||||
});
|
||||
}
|
||||
|
||||
// 最终回复时隐藏进度卡片(多代理模式下,迭代过程已完整展示)
|
||||
|
||||
@@ -1338,6 +1338,55 @@ function escapeHtmlAttr(s) {
|
||||
return escapeHtml(s).replace(/"/g, '"').replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function webshellFinalizationReasonLabel(reason, status) {
|
||||
var key = String(reason || status || '').trim();
|
||||
var labels = {
|
||||
pending_tool_executions: '等待工具执行完成',
|
||||
missing_execution_evidence: '缺少完成态证据',
|
||||
awaiting_hitl: '等待人工确认',
|
||||
empty_response: '未捕获到有效回复',
|
||||
missing_finalization_contract: '缺少最终化证明',
|
||||
in_progress: '仍在验证',
|
||||
blocked: '检查未通过',
|
||||
failed: '任务失败',
|
||||
cancelled: '任务已取消',
|
||||
verified: '已验证'
|
||||
};
|
||||
return labels[key] || key || '检查未通过';
|
||||
}
|
||||
|
||||
function webshellFinalizationMissingCheckLabel(check) {
|
||||
var s = String(check || '').trim();
|
||||
if (!s) return '';
|
||||
if (s.indexOf('tool execution still queued or running') !== -1) return '仍有工具执行未结束';
|
||||
if (s.indexOf('execution evidence is required but no completed tool execution was recorded') !== -1) return '本轮要求执行证据,但没有 completed 工具记录';
|
||||
if (s.indexOf('workflow is awaiting HITL approval') !== -1) return '工作流正在等待人工确认';
|
||||
if (s.indexOf('assistant final text is empty') !== -1) return '未捕获到有效最终文本';
|
||||
if (s.indexOf('agent run status is ') === 0) return '任务状态仍为 ' + s.replace('agent run status is ', '');
|
||||
return s;
|
||||
}
|
||||
|
||||
function webshellFinalizationNotice(data, eventMessage, hasContract) {
|
||||
var reason = hasContract
|
||||
? webshellFinalizationReasonLabel(data && data.completionReason, data && data.status)
|
||||
: webshellFinalizationReasonLabel('missing_finalization_contract');
|
||||
var lines = ['仍在验证,暂不生成最终结论', '状态:' + reason];
|
||||
var pending = Array.isArray(data && data.pendingExecutionIds) ? data.pendingExecutionIds.filter(Boolean).map(String) : [];
|
||||
if (pending.length) {
|
||||
lines.push('待完成工具:' + pending.slice(0, 3).join(', ') + (pending.length > 3 ? ' 等' : ''));
|
||||
}
|
||||
var missing = Array.isArray(data && data.missingChecks)
|
||||
? data.missingChecks.map(webshellFinalizationMissingCheckLabel).filter(Boolean)
|
||||
: [];
|
||||
if (missing.length) {
|
||||
lines.push('待完成检查:' + missing.slice(0, 2).join(';') + (missing.length > 2 ? ' 等' : ''));
|
||||
}
|
||||
if (!hasContract && eventMessage) {
|
||||
lines.push('候选输出已移入过程详情。');
|
||||
}
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
function escapeSingleQuotedShellArg(value) {
|
||||
var s = value == null ? '' : String(value);
|
||||
return "'" + s.replace(/'/g, "'\\''") + "'";
|
||||
@@ -3393,7 +3442,10 @@ function runWebshellAiSend(conn, inputEl, sendBtn, messagesContainer) {
|
||||
message: message,
|
||||
webshellConnectionId: conn.id,
|
||||
conversationId: convId,
|
||||
role: wsRole
|
||||
role: wsRole,
|
||||
finalization: {
|
||||
requireExecutionEvidence: true
|
||||
}
|
||||
};
|
||||
if (!convId) {
|
||||
var wsPid = getWebshellAiProjectSelection(conn);
|
||||
@@ -3465,6 +3517,8 @@ function runWebshellAiSend(conn, inputEl, sendBtn, messagesContainer) {
|
||||
streamingTarget = '';
|
||||
webshellStreamingTypingId += 1;
|
||||
streamingTypingId = webshellStreamingTypingId;
|
||||
assistantDiv.dataset.finalized = 'false';
|
||||
assistantDiv.classList.add('webshell-ai-candidate-output');
|
||||
assistantDiv.textContent = '…';
|
||||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||||
} else if (_et === 'response_delta') {
|
||||
@@ -3485,6 +3539,23 @@ function runWebshellAiSend(conn, inputEl, sendBtn, messagesContainer) {
|
||||
}
|
||||
} else if (_et === 'response') {
|
||||
var text = (_em != null && _em !== '') ? _em : (typeof _ed === 'string' ? _ed : '');
|
||||
var finalized = !!(_ed && _ed.finalized === true);
|
||||
var hasFinalizationContract = !!(_ed && (
|
||||
Object.prototype.hasOwnProperty.call(_ed, 'finalized') ||
|
||||
Object.prototype.hasOwnProperty.call(_ed, 'finalizable') ||
|
||||
Object.prototype.hasOwnProperty.call(_ed, 'completionReason') ||
|
||||
Object.prototype.hasOwnProperty.call(_ed, 'evidenceVerified') ||
|
||||
Object.prototype.hasOwnProperty.call(_ed, 'missingChecks')
|
||||
));
|
||||
assistantDiv.dataset.finalized = finalized ? 'true' : 'false';
|
||||
assistantDiv.classList.toggle('webshell-ai-candidate-output', !finalized);
|
||||
assistantDiv.classList.toggle('webshell-ai-finalized-output', finalized);
|
||||
if (!finalized && !hasFinalizationContract && text) {
|
||||
appendTimelineItem('finalization_check', '候选输出缺少最终化证明', text, Object.assign({}, _ed || {}, { missingFinalizationContract: true }));
|
||||
text = webshellFinalizationNotice(_ed || {}, text, false);
|
||||
} else if (!finalized && hasFinalizationContract) {
|
||||
text = webshellFinalizationNotice(_ed || {}, text, true);
|
||||
}
|
||||
if (text) {
|
||||
streamingTarget = String(text);
|
||||
webshellStreamingTypingId += 1;
|
||||
@@ -3493,6 +3564,11 @@ function runWebshellAiSend(conn, inputEl, sendBtn, messagesContainer) {
|
||||
}
|
||||
|
||||
// ─── Terminal events ───
|
||||
} else if (_et === 'finalization_check') {
|
||||
var finalizationOk = !!(_ed && _ed.finalized === true);
|
||||
appendTimelineItem('finalization_check', finalizationOk ? '最终回复检查通过' : '最终回复检查未通过', finalizationOk ? (_em || '最终回复检查通过。') : webshellFinalizationNotice(_ed || {}, _em, true), _ed);
|
||||
} else if (_et === 'finalization_auto_continue') {
|
||||
appendTimelineItem('progress', '继续验证', _em, _ed);
|
||||
} else if (_et === 'error' && _em) {
|
||||
streamingTypingId += 1;
|
||||
var errLabel = wsTOr('chat.error', '错误');
|
||||
|
||||
Reference in New Issue
Block a user