mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-08-01 00:27:35 +02:00
Add files via upload
This commit is contained in:
+6
-1
@@ -107,6 +107,11 @@ shodan:
|
||||
agent:
|
||||
max_iterations: 12000 # 全局最大迭代次数(单代理 / Deep / Supervisor / Plan-Execute 主执行器 / 子代理均沿用;agents/*.md 中 max_iterations>0 可单独覆盖)
|
||||
tool_timeout_minutes: 60 # 单次工具执行最大时长(分钟),超时自动终止;0 表示不限制(不推荐,易出现长时间挂起)
|
||||
tool_wait_timeout_seconds: 300 # 工具本轮最多等待(秒);到时返回 execution_id,worker 继续后台执行,可用 wait_tool_execution 继续等待;0=等到完成
|
||||
external_mcp_max_concurrent_per_server: 5 # 单个外部 MCP server 同时运行的工具数;0=默认2;负数=不限制
|
||||
external_mcp_max_concurrent_total: 16 # 所有外部 MCP 工具全局并发上限;0=默认16;负数=不限制
|
||||
external_mcp_circuit_failure_threshold: 5 # 单个外部 MCP server 连续失败多少次后熔断;0=默认3;负数=关闭熔断
|
||||
external_mcp_circuit_cooldown_seconds: 60 # 熔断冷却秒数;0=默认60
|
||||
shell_no_output_timeout_seconds: 1200 # execute/exec 连续无新输出则终止(秒);通用防挂死;0=默认300;-1=关闭
|
||||
workspace_root_dir: "" # 会话工作目录根路径(curl/wget 下载、read_file/glob/grep 本地分析);空=tmp/workspace,其下按 projects/{id} 或 conversations/{id} 隔离;勿用系统 /tmp
|
||||
# system_prompt_path: prompts/single-agent.md # 可选:单代理系统提示文件(相对本配置文件所在目录);非空且可读时替换内置提示
|
||||
@@ -214,7 +219,7 @@ multi_agent:
|
||||
tool_search_enable: true # true:工具数 ≥ min 时启用 tool_search,仅前 N 个工具常驻,其余按正则按需解锁,省 token、减误选;false:全量工具进上下文
|
||||
tool_search_min_tools: 20 # 达到该数量才启用 tool_search(避免工具很少时多此一举);与 always_visible 配合使用
|
||||
tool_search_always_visible: 12 # 始终直接暴露给模型的工具个数(顺序与角色工具列表一致);其余工具进入动态池,需 tool_search 解锁
|
||||
tool_search_always_visible_tools: [read_file, glob, grep, analyze_image, write_file, edit_file, execute, task, transfer_to_agent, exit, write_todos, skill, tool_search, TaskCreate, TaskGet, TaskUpdate, TaskList, record_vulnerability, list_vulnerabilities, get_vulnerability, list_knowledge_risk_types, search_knowledge_base, webshell_exec, webshell_file_list, webshell_file_read, webshell_file_write, manage_webshell_list, manage_webshell_add, manage_webshell_update, manage_webshell_delete, manage_webshell_test, batch_task_list, batch_task_get, batch_task_start, batch_task_rerun, batch_task_pause, batch_task_update_metadata, batch_task_update_schedule, batch_task_schedule_enabled, batch_task_update_task, batch_task_remove_task, batch_task_delete, batch_task_create, batch_task_add_task, http-framework-test, exec] # 后端内置常驻工具白名单(优先于 always_visible 数量策略)
|
||||
tool_search_always_visible_tools: [read_file, glob, grep, analyze_image, write_file, edit_file, execute, task, transfer_to_agent, exit, write_todos, skill, tool_search, TaskCreate, TaskGet, TaskUpdate, TaskList, record_vulnerability, list_vulnerabilities, get_vulnerability, get_tool_execution, wait_tool_execution, cancel_tool_execution, list_knowledge_risk_types, search_knowledge_base, webshell_exec, webshell_file_list, webshell_file_read, webshell_file_write, manage_webshell_list, manage_webshell_add, manage_webshell_update, manage_webshell_delete, manage_webshell_test, batch_task_list, batch_task_get, batch_task_start, batch_task_rerun, batch_task_pause, batch_task_update_metadata, batch_task_update_schedule, batch_task_schedule_enabled, batch_task_update_task, batch_task_remove_task, batch_task_delete, batch_task_create, batch_task_add_task, http-framework-test, exec] # 后端内置常驻工具白名单(优先于 always_visible 数量策略)
|
||||
plantask_enable: true # P0:主代理挂载 TaskCreate/Get/Update/List 结构化任务板;需 eino_skills 可用且 skills_dir 存在
|
||||
plantask_rel_dir: .eino/plantask # 任务文件相对 skills_dir,按会话分子目录:skills/.eino/plantask/<conversationId>/
|
||||
reduction_enable: true # true:大工具输出截断/落盘以控上下文;后端会独立创建,不依赖 eino_skills 是否启用
|
||||
|
||||
@@ -14014,16 +14014,31 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
.monitor-status-chip.queued {
|
||||
background: rgba(124, 58, 237, 0.12);
|
||||
color: #7c3aed;
|
||||
}
|
||||
|
||||
.monitor-status-chip.failed {
|
||||
background: rgba(220, 53, 69, 0.12);
|
||||
color: var(--error-color);
|
||||
}
|
||||
|
||||
.monitor-status-chip.hard_timeout {
|
||||
background: rgba(245, 158, 11, 0.14);
|
||||
color: #b45309;
|
||||
}
|
||||
|
||||
.monitor-status-chip.cancelled {
|
||||
background: rgba(108, 117, 125, 0.15);
|
||||
color: var(--text-muted, #6c757d);
|
||||
}
|
||||
|
||||
.monitor-status-chip.orphaned {
|
||||
background: rgba(100, 116, 139, 0.14);
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.monitor-execution-actions .btn-monitor-abort {
|
||||
border-color: rgba(253, 126, 20, 0.55);
|
||||
color: #fd7e14;
|
||||
|
||||
@@ -854,6 +854,7 @@
|
||||
"executionId": "Execution ID:",
|
||||
"noResult": "No result",
|
||||
"running": "Running...",
|
||||
"backgroundRunning": "Running in background",
|
||||
"completed": "Completed",
|
||||
"execFailed": "Execution failed",
|
||||
"resultMissing": "Result record missing",
|
||||
@@ -2139,11 +2140,14 @@
|
||||
"mcpMonitor": {
|
||||
"deselectAll": "Deselect all",
|
||||
"statusPending": "Pending",
|
||||
"statusQueued": "Queued",
|
||||
"statusSuccess": "Succeeded",
|
||||
"statusCompleted": "Completed",
|
||||
"statusRunning": "Running",
|
||||
"statusFailed": "Failed",
|
||||
"statusHardTimeout": "Timed out",
|
||||
"statusCancelled": "Cancelled",
|
||||
"statusOrphaned": "Orphaned",
|
||||
"terminateExecution": "Stop",
|
||||
"loading": "Loading...",
|
||||
"noStatsData": "No statistical data",
|
||||
@@ -2595,6 +2599,16 @@
|
||||
"fofaApiKeyHint": "Stored in server config (config.yaml) only.",
|
||||
"maxIterations": "Max iterations",
|
||||
"iterationsPlaceholder": "30",
|
||||
"toolWaitTimeoutSeconds": "Tool wait seconds",
|
||||
"toolWaitTimeoutSecondsHint": "Per-turn wait limit for tools. When reached, the agent receives an execution_id while the worker keeps running; use wait_tool_execution to continue waiting. 0 waits until completion.",
|
||||
"externalMcpConcurrencyServer": "Per-MCP concurrency limit",
|
||||
"externalMcpConcurrencyServerHint": "Concurrent tool runs per external MCP server. 0 uses default 2; -1 disables the limit.",
|
||||
"externalMcpConcurrencyTotal": "Global external MCP concurrency",
|
||||
"externalMcpConcurrencyTotalHint": "Total concurrent external MCP tool runs. 0 uses default 16; -1 disables the limit.",
|
||||
"externalMcpCircuitThreshold": "MCP circuit failure threshold",
|
||||
"externalMcpCircuitThresholdHint": "Temporarily opens the circuit after this many consecutive failures for one MCP server. 0 uses default 3; -1 disables circuit breaking.",
|
||||
"externalMcpCircuitCooldown": "MCP circuit cooldown seconds",
|
||||
"externalMcpCircuitCooldownHint": "How long a server stays circuit-open. 0 uses default 60 seconds.",
|
||||
"enableMultiAgent": "Enable Eino multi-agent",
|
||||
"enableMultiAgentHint": "After enabling, the chat page can use multi-agent mode; sub-agents are set in multi_agent.sub_agents or the agents/ directory. Orchestration is configured below.",
|
||||
"multiAgentOrchestration": "Multi-agent orchestration",
|
||||
|
||||
@@ -842,6 +842,7 @@
|
||||
"executionId": "执行ID:",
|
||||
"noResult": "无结果",
|
||||
"running": "执行中...",
|
||||
"backgroundRunning": "后台执行中",
|
||||
"completed": "已完成",
|
||||
"execFailed": "执行失败",
|
||||
"resultMissing": "结果记录缺失",
|
||||
@@ -2127,11 +2128,14 @@
|
||||
"mcpMonitor": {
|
||||
"deselectAll": "取消全选",
|
||||
"statusPending": "等待中",
|
||||
"statusQueued": "排队中",
|
||||
"statusSuccess": "成功",
|
||||
"statusCompleted": "已完成",
|
||||
"statusRunning": "执行中",
|
||||
"statusFailed": "失败",
|
||||
"statusHardTimeout": "执行超时",
|
||||
"statusCancelled": "已终止",
|
||||
"statusOrphaned": "孤儿任务",
|
||||
"terminateExecution": "终止",
|
||||
"loading": "加载中...",
|
||||
"noStatsData": "暂无统计数据",
|
||||
@@ -2583,6 +2587,16 @@
|
||||
"fofaApiKeyHint": "仅保存在服务器配置中(`config.yaml`)。",
|
||||
"maxIterations": "最大迭代次数",
|
||||
"iterationsPlaceholder": "30",
|
||||
"toolWaitTimeoutSeconds": "工具单轮等待秒数",
|
||||
"toolWaitTimeoutSecondsHint": "Agent 等待工具的单轮上限;到时返回 execution_id,worker 继续后台执行,可用 wait_tool_execution 继续等待。0 表示等到完成。",
|
||||
"externalMcpConcurrencyServer": "单 MCP 并发上限",
|
||||
"externalMcpConcurrencyServerHint": "每个外部 MCP server 同时运行的工具数;0 使用默认 2,-1 不限制。",
|
||||
"externalMcpConcurrencyTotal": "外部 MCP 全局并发上限",
|
||||
"externalMcpConcurrencyTotalHint": "所有外部 MCP 工具的总并发;0 使用默认 16,-1 不限制。",
|
||||
"externalMcpCircuitThreshold": "MCP 熔断失败阈值",
|
||||
"externalMcpCircuitThresholdHint": "同一 MCP server 连续失败达到阈值后临时熔断;0 使用默认 3,-1 关闭熔断。",
|
||||
"externalMcpCircuitCooldown": "MCP 熔断冷却秒数",
|
||||
"externalMcpCircuitCooldownHint": "server 熔断后的等待时间;0 使用默认 60 秒。",
|
||||
"enableMultiAgent": "启用 Eino 多代理",
|
||||
"enableMultiAgentHint": "开启后对话页可选「多代理」模式;子代理在 multi_agent.sub_agents 或 agents 目录配置;编排方式见下方「预置编排」。",
|
||||
"multiAgentOrchestration": "多代理预置编排",
|
||||
|
||||
@@ -604,10 +604,10 @@ function setUserMenuOpen(open) {
|
||||
function getStatusText(status) {
|
||||
const s = (status && String(status).toLowerCase()) || '';
|
||||
if (typeof window.t !== 'function') {
|
||||
const fallback = { pending: '等待中', running: '执行中', completed: '已完成', failed: '失败', cancelled: '已终止' };
|
||||
const fallback = { pending: '等待中', queued: '排队中', running: '执行中', background_running: '后台执行中', completed: '已完成', failed: '失败', cancelled: '已终止', hard_timeout: '硬超时', orphaned: '孤儿记录' };
|
||||
return fallback[s] || status;
|
||||
}
|
||||
const keyMap = { pending: 'mcpDetailModal.statusPending', running: 'mcpDetailModal.statusRunning', completed: 'mcpDetailModal.statusCompleted', failed: 'mcpDetailModal.statusFailed', cancelled: 'mcpDetailModal.statusCancelled' };
|
||||
const keyMap = { pending: 'mcpDetailModal.statusPending', queued: 'mcpDetailModal.statusQueued', running: 'mcpDetailModal.statusRunning', background_running: 'timeline.backgroundRunning', completed: 'mcpDetailModal.statusCompleted', failed: 'mcpDetailModal.statusFailed', cancelled: 'mcpDetailModal.statusCancelled', hard_timeout: 'mcpMonitor.statusHardTimeout', orphaned: 'mcpMonitor.statusOrphaned' };
|
||||
const key = keyMap[s];
|
||||
return key ? window.t(key) : status;
|
||||
}
|
||||
|
||||
+40
-15
@@ -2821,9 +2821,18 @@ function renderProcessDetails(messageId, processDetails, options) {
|
||||
itemTitle = agPx + '🔧 ' + callTitle;
|
||||
} else if (eventType === 'tool_result') {
|
||||
const toolName = data.toolName || (typeof window.t === 'function' ? window.t('chat.unknownTool') : '未知工具');
|
||||
const success = data.success !== false;
|
||||
const statusIcon = success ? '✅' : '❌';
|
||||
const execText = success ? (typeof window.t === 'function' ? window.t('chat.toolExecComplete', { name: escapeHtml(toolName) }) : '工具 ' + escapeHtml(toolName) + ' 执行完成') : (typeof window.t === 'function' ? window.t('chat.toolExecFailed', { name: escapeHtml(toolName) }) : '工具 ' + escapeHtml(toolName) + ' 执行失败');
|
||||
const noResultText = typeof window.t === 'function' ? window.t('timeline.noResult') : '无结果';
|
||||
const result = data.result != null ? data.result : (data.error != null ? data.error : (data.resultPreview != null ? data.resultPreview : noResultText));
|
||||
const resultStr = typeof result === 'string' ? result : JSON.stringify(result);
|
||||
const displayState = typeof window.getToolResultDisplayState === 'function'
|
||||
? window.getToolResultDisplayState(data, { rawText: resultStr })
|
||||
: { kind: (data.success !== false ? 'success' : 'error'), isError: data.success === false };
|
||||
const backgroundRunning = displayState.kind === 'background_running';
|
||||
const success = !displayState.isError && !backgroundRunning;
|
||||
const statusIcon = backgroundRunning ? '⏳' : (success ? '✅' : '❌');
|
||||
const execText = backgroundRunning
|
||||
? ((typeof window.getBackgroundRunningToolLabel === 'function' ? window.getBackgroundRunningToolLabel() : '后台执行中') + ': ' + escapeHtml(toolName))
|
||||
: (success ? (typeof window.t === 'function' ? window.t('chat.toolExecComplete', { name: escapeHtml(toolName) }) : '工具 ' + escapeHtml(toolName) + ' 执行完成') : (typeof window.t === 'function' ? window.t('chat.toolExecFailed', { name: escapeHtml(toolName) }) : '工具 ' + escapeHtml(toolName) + ' 执行失败'));
|
||||
let execLine = statusIcon + ' ' + execText;
|
||||
if (toolName === BuiltinTools.SEARCH_KNOWLEDGE_BASE && success) {
|
||||
execLine = '📚 ' + execLine + ' - ' + (typeof window.t === 'function' ? window.t('chat.knowledgeRetrievalTag') : '知识检索');
|
||||
@@ -2862,11 +2871,17 @@ function renderProcessDetails(messageId, processDetails, options) {
|
||||
processDetailId: detail.id || '',
|
||||
createdAt: detail.createdAt
|
||||
};
|
||||
if (eventType === 'tool_call' && detail.id && toolStatusByProcessDetailId.has(String(detail.id))) {
|
||||
timelineOpts.toolStatus = toolStatusByProcessDetailId.get(String(detail.id));
|
||||
}
|
||||
if (eventType === 'tool_call' && data._mergedResult) {
|
||||
timelineOpts.mergedResult = data._mergedResult;
|
||||
if (typeof window.getToolResultDisplayState === 'function') {
|
||||
const displayState = window.getToolResultDisplayState(data._mergedResult);
|
||||
if (displayState && displayState.kind === 'background_running') {
|
||||
timelineOpts.toolStatus = 'background_running';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!timelineOpts.toolStatus && eventType === 'tool_call' && detail.id && toolStatusByProcessDetailId.has(String(detail.id))) {
|
||||
timelineOpts.toolStatus = toolStatusByProcessDetailId.get(String(detail.id));
|
||||
}
|
||||
const itemId = addTimelineItem(timeline, eventType, timelineOpts);
|
||||
if (prependMode && itemId) {
|
||||
@@ -3537,11 +3552,14 @@ async function fetchProcessDetailDataForModal(detailId) {
|
||||
|
||||
function processToolResultToMCPResult(resultData, rawText) {
|
||||
if (!resultData) return null;
|
||||
const isError = !!(resultData.isError || resultData.success === false);
|
||||
const displayState = typeof window.getToolResultDisplayState === 'function'
|
||||
? window.getToolResultDisplayState(resultData, { rawText: rawText })
|
||||
: { kind: ((resultData.isError || resultData.success === false) ? 'error' : 'success'), isError: !!(resultData.isError || resultData.success === false) };
|
||||
const isError = !!displayState.isError;
|
||||
let text = rawText;
|
||||
if (text == null || String(text) === '') {
|
||||
const noResultText = typeof window.t === 'function' ? window.t('timeline.noResult') : '无结果';
|
||||
const result = resultData.result != null ? resultData.result : (resultData.error != null ? resultData.error : noResultText);
|
||||
const result = resultData.result != null ? resultData.result : (resultData.error != null ? resultData.error : (resultData.resultPreview != null ? resultData.resultPreview : noResultText));
|
||||
text = typeof result === 'string' ? result : JSON.stringify(result);
|
||||
}
|
||||
return {
|
||||
@@ -3577,10 +3595,16 @@ async function showMCPDetailFromProcessToolItem(messageElement, summary, index)
|
||||
resultData = await fetchProcessDetailDataForModal(resultDetailId);
|
||||
}
|
||||
const toolName = (summary && summary.toolName) || target.dataset.toolName || (typeof window.t === 'function' ? window.t('chat.unknownTool') : '未知工具');
|
||||
const success = resultData ? !(resultData.isError || resultData.success === false) : target.dataset.toolSuccess !== '0';
|
||||
const status = resultData || target.classList.contains('tool-call-completed') || target.classList.contains('tool-call-failed')
|
||||
? (success ? 'completed' : 'failed')
|
||||
: 'running';
|
||||
const displayState = resultData && typeof window.getToolResultDisplayState === 'function'
|
||||
? window.getToolResultDisplayState(resultData, { rawText: rawText })
|
||||
: null;
|
||||
const backgroundRunning = (displayState && displayState.kind === 'background_running') || target.dataset.toolDisplayStatus === 'background_running';
|
||||
const success = resultData ? !(displayState ? displayState.isError : (resultData.isError || resultData.success === false)) : target.dataset.toolSuccess !== '0';
|
||||
const status = backgroundRunning
|
||||
? 'background_running'
|
||||
: (resultData || target.classList.contains('tool-call-completed') || target.classList.contains('tool-call-failed')
|
||||
? (success ? 'completed' : 'failed')
|
||||
: 'running');
|
||||
const exec = {
|
||||
id: (resultData && resultData.executionId) || (summary && summary.executionId) || '',
|
||||
toolName: toolName,
|
||||
@@ -3898,7 +3922,8 @@ function renderMCPDetailModal(exec) {
|
||||
const statusEl = document.getElementById('detail-status');
|
||||
const normalizedStatus = (exec.status || 'unknown').toLowerCase();
|
||||
statusEl.textContent = getStatusText(exec.status);
|
||||
statusEl.className = `status-chip status-${normalizedStatus}`;
|
||||
const statusClass = normalizedStatus === 'background_running' ? 'running' : normalizedStatus;
|
||||
statusEl.className = `status-chip status-${statusClass}`;
|
||||
try {
|
||||
statusEl.dataset.detailStatus = (exec.status || '') + '';
|
||||
} catch (e) { /* ignore */ }
|
||||
@@ -3960,7 +3985,7 @@ function renderMCPDetailModal(exec) {
|
||||
setMCPResultDetailTabs('success', true);
|
||||
}
|
||||
} else {
|
||||
if (normalizedStatus === 'running') {
|
||||
if (normalizedStatus === 'running' || normalizedStatus === 'background_running') {
|
||||
responseElement.textContent = typeof window.t === 'function' ? window.t('mcpDetailModal.runningNoResponseYet') : '尚无返回,工具可能仍在执行。若长时间无响应,可在下方终止本次调用。';
|
||||
} else {
|
||||
responseElement.textContent = typeof window.t === 'function' ? window.t('chat.noResponseData') : '暂无响应数据';
|
||||
@@ -3971,7 +3996,7 @@ function renderMCPDetailModal(exec) {
|
||||
const abortSection = document.getElementById('detail-abort-section');
|
||||
const abortBtn = document.getElementById('detail-abort-btn');
|
||||
if (abortSection && abortBtn) {
|
||||
if (normalizedStatus === 'running' && exec.id) {
|
||||
if ((normalizedStatus === 'running' || normalizedStatus === 'background_running') && exec.id) {
|
||||
abortSection.style.display = 'block';
|
||||
abortBtn.dataset.execId = exec.id || '';
|
||||
abortBtn.textContent = typeof window.t === 'function' ? window.t('mcpDetailModal.abortBtn') : '终止工具';
|
||||
|
||||
+130
-36
@@ -2567,9 +2567,14 @@ function handleStreamEvent(event, progressElement, progressId,
|
||||
const resultInfo = event.data || {};
|
||||
const resultToolName = resultInfo.toolName || (typeof window.t === 'function' ? window.t('chat.unknownTool') : '未知工具');
|
||||
const success = resultInfo.success !== false;
|
||||
const statusIcon = success ? '✅' : '❌';
|
||||
const resultDisplayState = getToolResultDisplayState(resultInfo, { rawText: event.message || '' });
|
||||
const backgroundRunning = resultDisplayState.kind === 'background_running';
|
||||
const statusIcon = backgroundRunning ? '⏳' : (success ? '✅' : '❌');
|
||||
const resultToolCallId = resultInfo.toolCallId || null;
|
||||
const resultExecText = success ? (typeof window.t === 'function' ? window.t('chat.toolExecComplete', { name: escapeHtml(resultToolName) }) : '工具 ' + escapeHtml(resultToolName) + ' 执行完成') : (typeof window.t === 'function' ? window.t('chat.toolExecFailed', { name: escapeHtml(resultToolName) }) : '工具 ' + escapeHtml(resultToolName) + ' 执行失败');
|
||||
const resultStatusForCall = backgroundRunning ? 'background_running' : (success ? 'completed' : 'failed');
|
||||
const resultExecText = backgroundRunning
|
||||
? (getBackgroundRunningToolLabel() + ': ' + escapeHtml(resultToolName))
|
||||
: (success ? (typeof window.t === 'function' ? window.t('chat.toolExecComplete', { name: escapeHtml(resultToolName) }) : '工具 ' + escapeHtml(resultToolName) + ' 执行完成') : (typeof window.t === 'function' ? window.t('chat.toolExecFailed', { name: escapeHtml(resultToolName) }) : '工具 ' + escapeHtml(resultToolName) + ' 执行失败'));
|
||||
|
||||
if (resultToolCallId) {
|
||||
const key = toolResultStreamKey(progressId, resultToolCallId);
|
||||
@@ -2582,24 +2587,24 @@ function handleStreamEvent(event, progressElement, progressId,
|
||||
toolResultStreamStateByKey.delete(key);
|
||||
const mapKey = toolCallMapKey(progressId, resultToolCallId);
|
||||
if (toolCallStatusMap.has(mapKey)) {
|
||||
updateToolCallStatus(progressId, resultToolCallId, success ? 'completed' : 'failed');
|
||||
toolCallStatusMap.get(mapKey).terminalStatus = success ? 'completed' : 'failed';
|
||||
updateToolCallStatus(progressId, resultToolCallId, resultStatusForCall);
|
||||
toolCallStatusMap.get(mapKey).terminalStatus = resultStatusForCall;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (attachToolResultToCall(progressId, resultToolCallId, resultInfo)) {
|
||||
const mapKey = toolCallMapKey(progressId, resultToolCallId);
|
||||
if (toolCallStatusMap.has(mapKey)) {
|
||||
updateToolCallStatus(progressId, resultToolCallId, success ? 'completed' : 'failed');
|
||||
toolCallStatusMap.get(mapKey).terminalStatus = success ? 'completed' : 'failed';
|
||||
updateToolCallStatus(progressId, resultToolCallId, resultStatusForCall);
|
||||
toolCallStatusMap.get(mapKey).terminalStatus = resultStatusForCall;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (resultToolCallId && toolCallStatusMap.has(toolCallMapKey(progressId, resultToolCallId))) {
|
||||
updateToolCallStatus(progressId, resultToolCallId, success ? 'completed' : 'failed');
|
||||
toolCallStatusMap.get(toolCallMapKey(progressId, resultToolCallId)).terminalStatus = success ? 'completed' : 'failed';
|
||||
updateToolCallStatus(progressId, resultToolCallId, resultStatusForCall);
|
||||
toolCallStatusMap.get(toolCallMapKey(progressId, resultToolCallId)).terminalStatus = resultStatusForCall;
|
||||
}
|
||||
addTimelineItem(timeline, 'tool_result', {
|
||||
title: timelineAgentBracketPrefix(resultInfo) + statusIcon + ' ' + resultExecText,
|
||||
@@ -3770,6 +3775,66 @@ function formatToolCallTimelineTitle(toolName, index, total) {
|
||||
return '调用工具: ' + name + (tot ? ' (' + idx + '/' + tot + ')' : '');
|
||||
}
|
||||
|
||||
function collectToolResultTextParts(value, parts, depth) {
|
||||
if (value == null || depth > 4) return;
|
||||
if (typeof value === 'string') {
|
||||
parts.push(value);
|
||||
return;
|
||||
}
|
||||
if (typeof value !== 'object') return;
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach(function (v) { collectToolResultTextParts(v, parts, depth + 1); });
|
||||
return;
|
||||
}
|
||||
if (typeof value.text === 'string') parts.push(value.text);
|
||||
if (typeof value.result === 'string') parts.push(value.result);
|
||||
if (typeof value.error === 'string') parts.push(value.error);
|
||||
if (value.content != null) collectToolResultTextParts(value.content, parts, depth + 1);
|
||||
}
|
||||
|
||||
function getToolResultDisplayState(data, opts) {
|
||||
opts = opts || {};
|
||||
data = data || {};
|
||||
const toolName = String(data.toolName || data.name || '').trim().toLowerCase();
|
||||
const isObservationTool = toolName === 'wait_tool_execution' || toolName === 'get_tool_execution';
|
||||
const explicitStatus = String(data.displayStatus || data.status || '').toLowerCase();
|
||||
if (explicitStatus === 'background_running') {
|
||||
if (isObservationTool) {
|
||||
return { kind: 'success', isError: false, success: true };
|
||||
}
|
||||
return { kind: 'background_running', isError: false, success: false };
|
||||
}
|
||||
const parts = [];
|
||||
if (opts.rawText != null) parts.push(String(opts.rawText));
|
||||
collectToolResultTextParts(data.result, parts, 0);
|
||||
collectToolResultTextParts(data.error, parts, 0);
|
||||
collectToolResultTextParts(data.content, parts, 0);
|
||||
if (data.executionId != null) parts.push('execution_id: ' + String(data.executionId));
|
||||
if (data.status != null) parts.push('status: ' + String(data.status));
|
||||
const text = parts.join('\n');
|
||||
const errorLike = data.isError === true || data.success === false;
|
||||
const hasExecutionId = !!data.executionId ||
|
||||
/execution[_-]?id\\?["']?\s*[:=]\s*\\?["']?[0-9a-f]{8}-[0-9a-f-]{12,}/i.test(text);
|
||||
const hasRunningStatus = /status\\?["']?\s*[:=]\s*\\?["']?(running|queued)\\?["']?/i.test(text) ||
|
||||
/\bstatus:\s*(running|queued)\b/i.test(text);
|
||||
const hasSoftWaitSignal = /(工具已提交到后台执行|本次等待已到达|wait_timeout|wait timeout|background execution|后台执行|仍未完成|still running)/i.test(text);
|
||||
if (errorLike && hasExecutionId && hasRunningStatus && hasSoftWaitSignal) {
|
||||
if (isObservationTool) {
|
||||
return { kind: 'success', isError: false, success: true };
|
||||
}
|
||||
return { kind: 'background_running', isError: false, success: false };
|
||||
}
|
||||
return { kind: errorLike ? 'error' : 'success', isError: errorLike, success: !errorLike };
|
||||
}
|
||||
|
||||
function getBackgroundRunningToolLabel() {
|
||||
if (typeof window.t === 'function') {
|
||||
const translated = window.t('timeline.backgroundRunning');
|
||||
if (translated && translated !== 'timeline.backgroundRunning') return translated;
|
||||
}
|
||||
return '后台执行中';
|
||||
}
|
||||
|
||||
function buildToolResultSectionHtml(data, opts) {
|
||||
opts = opts || {};
|
||||
const _t = function (k, o) {
|
||||
@@ -3786,13 +3851,14 @@ function buildToolResultSectionHtml(data, opts) {
|
||||
'</div>'
|
||||
);
|
||||
}
|
||||
const isError = data.isError || data.success === false;
|
||||
const noResultText = _t('timeline.noResult');
|
||||
const result = data.result != null ? data.result : (data.error != null ? data.error : noResultText);
|
||||
const result = data.result != null ? data.result : (data.error != null ? data.error : (data.resultPreview != null ? data.resultPreview : noResultText));
|
||||
const resultStr = typeof result === 'string' ? result : JSON.stringify(result);
|
||||
const rawText = opts.rawText != null ? String(opts.rawText) : resultStr;
|
||||
const displayState = getToolResultDisplayState(data, { rawText: rawText });
|
||||
const sectionClass = displayState.kind === 'background_running' ? 'pending' : (displayState.isError ? 'error' : 'success');
|
||||
return (
|
||||
'<div class="tool-result-section ' + (isError ? 'error' : 'success') + '">' +
|
||||
'<div class="tool-result-section ' + sectionClass + '">' +
|
||||
'<strong data-i18n="timeline.executionResult">' + escapeHtml(execResultLabel) + '</strong>' +
|
||||
'<pre class="tool-result">' + escapeHtml(rawText) + '</pre>' +
|
||||
(data.executionId ? '<div class="tool-execution-id"><span data-i18n="timeline.executionId">' +
|
||||
@@ -3970,11 +4036,12 @@ function ensureToolCallResultSlot(item) {
|
||||
function mergeToolResultIntoCallItem(item, data, options) {
|
||||
if (!item || !data) return false;
|
||||
options = options || {};
|
||||
const isError = data.isError || data.success === false;
|
||||
const noResultText = typeof window.t === 'function' ? window.t('timeline.noResult') : '无结果';
|
||||
const result = data.result != null ? data.result : (data.error != null ? data.error : noResultText);
|
||||
const result = data.result != null ? data.result : (data.error != null ? data.error : (data.resultPreview != null ? data.resultPreview : noResultText));
|
||||
const resultStr = typeof result === 'string' ? result : JSON.stringify(result);
|
||||
const text = options.rawText != null ? String(options.rawText) : resultStr;
|
||||
const displayState = getToolResultDisplayState(data, { rawText: text });
|
||||
const backgroundRunning = displayState.kind === 'background_running';
|
||||
|
||||
if (item.classList.contains('tool-call-collapsible')) {
|
||||
const state = toolCallDetailStateByItemId.get(item.id) || {};
|
||||
@@ -3982,6 +4049,8 @@ function mergeToolResultIntoCallItem(item, data, options) {
|
||||
state.rawText = text;
|
||||
state.resultDetailId = data.processDetailId || state.resultDetailId || '';
|
||||
state.pending = false;
|
||||
state.payloadDeferred = data._payloadDeferred === true;
|
||||
state.payloadLoaded = data._payloadDeferred !== true;
|
||||
setToolCallDetailState(item, state);
|
||||
const expanded = item.classList.contains('tool-call-detail-expanded');
|
||||
const content = item.querySelector('.timeline-item-content.tool-call-detail-content');
|
||||
@@ -3991,9 +4060,10 @@ function mergeToolResultIntoCallItem(item, data, options) {
|
||||
renderToolCallDetailContent(item);
|
||||
}
|
||||
item.dataset.toolResultMerged = '1';
|
||||
item.dataset.toolSuccess = data.success !== false ? '1' : '0';
|
||||
item.classList.remove('tool-call-running');
|
||||
item.classList.add(data.success !== false ? 'tool-call-completed' : 'tool-call-failed');
|
||||
item.dataset.toolSuccess = (!displayState.isError && !backgroundRunning) ? '1' : '0';
|
||||
item.dataset.toolDisplayStatus = backgroundRunning ? 'background_running' : (displayState.isError ? 'failed' : 'completed');
|
||||
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'));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -4005,7 +4075,7 @@ function mergeToolResultIntoCallItem(item, data, options) {
|
||||
if (!section) return false;
|
||||
|
||||
section.classList.remove('pending');
|
||||
section.className = 'tool-result-section ' + (isError ? 'error' : 'success');
|
||||
section.className = 'tool-result-section ' + (backgroundRunning ? 'pending' : (displayState.isError ? 'error' : 'success'));
|
||||
const pre = section.querySelector('pre.tool-result');
|
||||
if (pre) {
|
||||
pre.classList.remove('tool-result-pending');
|
||||
@@ -4029,9 +4099,10 @@ function mergeToolResultIntoCallItem(item, data, options) {
|
||||
}
|
||||
|
||||
item.dataset.toolResultMerged = '1';
|
||||
item.dataset.toolSuccess = data.success !== false ? '1' : '0';
|
||||
item.classList.remove('tool-call-running');
|
||||
item.classList.add(data.success !== false ? 'tool-call-completed' : 'tool-call-failed');
|
||||
item.dataset.toolSuccess = (!displayState.isError && !backgroundRunning) ? '1' : '0';
|
||||
item.dataset.toolDisplayStatus = backgroundRunning ? 'background_running' : (displayState.isError ? 'failed' : 'completed');
|
||||
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'));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -4138,6 +4209,8 @@ window.attachToolResultToCall = attachToolResultToCall;
|
||||
window.mergeToolResultIntoCallItem = mergeToolResultIntoCallItem;
|
||||
window.formatToolCallTimelineTitle = formatToolCallTimelineTitle;
|
||||
window.parseToolCallArgsFromData = parseToolCallArgsFromData;
|
||||
window.getToolResultDisplayState = getToolResultDisplayState;
|
||||
window.getBackgroundRunningToolLabel = getBackgroundRunningToolLabel;
|
||||
window.buildToolResultSectionHtml = buildToolResultSectionHtml;
|
||||
|
||||
// 更新工具调用状态
|
||||
@@ -4157,10 +4230,13 @@ function updateToolCallStatus(progressId, toolCallId, status) {
|
||||
const runningLabel = typeof window.t === 'function' ? window.t('timeline.running') : '执行中...';
|
||||
const completedLabel = typeof window.t === 'function' ? window.t('timeline.completed') : '已完成';
|
||||
const failedLabel = typeof window.t === 'function' ? window.t('timeline.execFailed') : '执行失败';
|
||||
const backgroundRunningLabel = getBackgroundRunningToolLabel();
|
||||
let statusText = '';
|
||||
if (status === 'running') {
|
||||
if (status === 'running' || status === 'background_running') {
|
||||
item.classList.add('tool-call-running');
|
||||
statusText = ' <span class="tool-status-badge tool-status-running">' + escapeHtml(runningLabel) + '</span>';
|
||||
statusText = ' <span class="tool-status-badge tool-status-running">' +
|
||||
escapeHtml(status === 'background_running' ? backgroundRunningLabel : runningLabel) +
|
||||
'</span>';
|
||||
} else if (status === 'completed') {
|
||||
item.classList.add('tool-call-completed');
|
||||
statusText = ' <span class="tool-status-badge tool-status-completed">✅ ' + escapeHtml(completedLabel) + '</span>';
|
||||
@@ -4319,11 +4395,14 @@ function addTimelineItem(timeline, type, options) {
|
||||
item.dataset.toolCallId = String(d.toolCallId).trim();
|
||||
}
|
||||
const merged = options.mergedResult || d._mergedResult;
|
||||
const mergedDisplayState = merged ? getToolResultDisplayState(merged) : null;
|
||||
const mergedBackgroundRunning = mergedDisplayState && mergedDisplayState.kind === 'background_running';
|
||||
const terminalStatus = String(options.toolStatus || '').toLowerCase();
|
||||
if (merged) {
|
||||
item.dataset.toolResultMerged = '1';
|
||||
item.dataset.toolSuccess = merged.success !== false ? '1' : '0';
|
||||
item.classList.add(merged.success !== false ? 'tool-call-completed' : 'tool-call-failed');
|
||||
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'));
|
||||
if (d._mergedResultDetailId) {
|
||||
item.dataset.toolResultDetailId = String(d._mergedResultDetailId);
|
||||
}
|
||||
@@ -4346,11 +4425,16 @@ function addTimelineItem(timeline, type, options) {
|
||||
}
|
||||
if (type === 'tool_result' && options.data) {
|
||||
const d = options.data;
|
||||
const noResultText = typeof window.t === 'function' ? window.t('timeline.noResult') : '无结果';
|
||||
const result = d.result != null ? d.result : (d.error != null ? d.error : (d.resultPreview != null ? d.resultPreview : noResultText));
|
||||
const resultStr = typeof result === 'string' ? result : JSON.stringify(result);
|
||||
const displayState = getToolResultDisplayState(d, { rawText: resultStr });
|
||||
if (options.processDetailId) {
|
||||
item.dataset.processDetailId = String(options.processDetailId);
|
||||
}
|
||||
item.dataset.toolName = (d.toolName != null && d.toolName !== '') ? String(d.toolName) : '';
|
||||
item.dataset.toolSuccess = d.success !== false ? '1' : '0';
|
||||
item.dataset.toolSuccess = (!displayState.isError && displayState.kind !== 'background_running') ? '1' : '0';
|
||||
item.dataset.toolDisplayStatus = displayState.kind === 'background_running' ? 'background_running' : (displayState.isError ? 'failed' : 'completed');
|
||||
}
|
||||
if (options.data && options.data.einoAgent != null && String(options.data.einoAgent).trim() !== '') {
|
||||
item.dataset.einoAgent = String(options.data.einoAgent).trim();
|
||||
@@ -4406,15 +4490,13 @@ function addTimelineItem(timeline, type, options) {
|
||||
const data = options.data;
|
||||
const args = parseToolCallArgsFromData(data);
|
||||
const merged = options.mergedResult || data._mergedResult;
|
||||
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 hasHistoricalStatus = hasTerminalStatus || terminalStatus === 'result_missing';
|
||||
if (merged) {
|
||||
if (merged.success !== false) {
|
||||
item.classList.add('tool-call-completed');
|
||||
} else {
|
||||
item.classList.add('tool-call-failed');
|
||||
}
|
||||
item.classList.add(mergedBackgroundRunning ? 'tool-call-running' : (mergedDisplayState.isError ? 'tool-call-failed' : 'tool-call-completed'));
|
||||
} else if (hasTerminalStatus) {
|
||||
item.classList.add(terminalStatus === 'completed' ? 'tool-call-completed' : 'tool-call-failed');
|
||||
} else if (terminalStatus === 'result_missing') {
|
||||
@@ -4462,10 +4544,10 @@ function addTimelineItem(timeline, type, options) {
|
||||
content += buildWorkflowBranchDetailHtml(options.data);
|
||||
} else if (type === 'tool_result' && options.data) {
|
||||
const data = options.data;
|
||||
const isError = data.isError || !data.success;
|
||||
const noResultText = typeof window.t === 'function' ? window.t('timeline.noResult') : '无结果';
|
||||
const result = data.result || data.error || noResultText;
|
||||
const result = data.result || data.error || data.resultPreview || noResultText;
|
||||
const resultStr = typeof result === 'string' ? result : JSON.stringify(result);
|
||||
const displayState = getToolResultDisplayState(data, { rawText: resultStr });
|
||||
setToolCallDetailState(item, {
|
||||
resultData: data,
|
||||
rawText: resultStr,
|
||||
@@ -4476,7 +4558,8 @@ function addTimelineItem(timeline, type, options) {
|
||||
payloadDeferred: data._payloadDeferred === true,
|
||||
payloadLoaded: data._payloadDeferred !== true
|
||||
});
|
||||
item.classList.add(isError ? 'tool-call-failed' : 'tool-call-completed');
|
||||
item.dataset.toolDisplayStatus = displayState.kind === 'background_running' ? 'background_running' : (displayState.isError ? 'failed' : 'completed');
|
||||
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') : '任务已取消';
|
||||
content += `
|
||||
@@ -6455,7 +6538,16 @@ function renderMonitorExecutions(executions = [], statusFilter = 'all') {
|
||||
const deleteLabel = typeof window.t === 'function' ? window.t('mcpMonitor.delete') : '删除';
|
||||
const deleteExecTitle = typeof window.t === 'function' ? window.t('mcpMonitor.deleteExecTitle') : '删除此执行记录';
|
||||
const terminateLabel = typeof window.t === 'function' ? window.t('mcpMonitor.terminateExecution') : '终止';
|
||||
const statusKeyMap = { pending: 'statusPending', running: 'statusRunning', completed: 'statusCompleted', failed: 'statusFailed', cancelled: 'statusCancelled' };
|
||||
const statusKeyMap = {
|
||||
pending: 'statusPending',
|
||||
queued: 'statusQueued',
|
||||
running: 'statusRunning',
|
||||
completed: 'statusCompleted',
|
||||
failed: 'statusFailed',
|
||||
cancelled: 'statusCancelled',
|
||||
hard_timeout: 'statusHardTimeout',
|
||||
orphaned: 'statusOrphaned'
|
||||
};
|
||||
const locale = (typeof window.__locale === 'string' && window.__locale.startsWith('zh')) ? 'zh-CN' : undefined;
|
||||
const rowEntries = executions
|
||||
.map(exec => {
|
||||
@@ -6997,9 +7089,11 @@ function refreshProgressAndTimelineI18n() {
|
||||
titleSpan.textContent = ap + '\uD83D\uDD27 ' + callTitle;
|
||||
} else if (type === 'tool_result' && (item.dataset.toolName !== undefined || item.dataset.toolSuccess !== undefined)) {
|
||||
const name = (item.dataset.toolName != null && item.dataset.toolName !== '') ? item.dataset.toolName : _t('chat.unknownTool');
|
||||
const displayStatus = item.dataset.toolDisplayStatus || '';
|
||||
const backgroundRunning = displayStatus === 'background_running';
|
||||
const success = item.dataset.toolSuccess === '1';
|
||||
const icon = success ? '\u2705 ' : '\u274C ';
|
||||
titleSpan.textContent = ap + icon + (success ? _t('chat.toolExecComplete', { name: name }) : _t('chat.toolExecFailed', { name: name }));
|
||||
const icon = backgroundRunning ? '\u23F3 ' : (success ? '\u2705 ' : '\u274C ');
|
||||
titleSpan.textContent = ap + icon + (backgroundRunning ? (getBackgroundRunningToolLabel() + ': ' + name) : (success ? _t('chat.toolExecComplete', { name: name }) : _t('chat.toolExecFailed', { name: name })));
|
||||
} else if (type === 'eino_agent_reply') {
|
||||
titleSpan.textContent = ap + '\uD83D\uDCAC ' + _t('chat.einoAgentReplyTitle');
|
||||
} else if (type === 'cancelled') {
|
||||
|
||||
@@ -757,6 +757,22 @@ async function loadConfig(loadTools = true, options = {}) {
|
||||
|
||||
// 填充Agent配置
|
||||
document.getElementById('agent-max-iterations').value = currentConfig.agent.max_iterations || 30;
|
||||
const toolWaitTimeoutEl = document.getElementById('agent-tool-wait-timeout-seconds');
|
||||
if (toolWaitTimeoutEl) {
|
||||
const v = currentConfig.agent.tool_wait_timeout_seconds;
|
||||
toolWaitTimeoutEl.value = (v !== undefined && v !== null && !Number.isNaN(Number(v))) ? String(Number(v)) : '60';
|
||||
}
|
||||
[
|
||||
['agent-external-mcp-concurrency-server', 'external_mcp_max_concurrent_per_server', '2'],
|
||||
['agent-external-mcp-concurrency-total', 'external_mcp_max_concurrent_total', '16'],
|
||||
['agent-external-mcp-circuit-threshold', 'external_mcp_circuit_failure_threshold', '3'],
|
||||
['agent-external-mcp-circuit-cooldown', 'external_mcp_circuit_cooldown_seconds', '60']
|
||||
].forEach(([id, key, fallback]) => {
|
||||
const el = document.getElementById(id);
|
||||
if (!el) return;
|
||||
const v = currentConfig.agent[key];
|
||||
el.value = (v !== undefined && v !== null && !Number.isNaN(Number(v))) ? String(Number(v)) : fallback;
|
||||
});
|
||||
|
||||
const ma = currentConfig.multi_agent || {};
|
||||
const maEn = document.getElementById('multi-agent-enabled');
|
||||
@@ -1941,7 +1957,12 @@ async function applySettings() {
|
||||
audit_agent_prompt_review_edit: document.getElementById('hitl-audit-agent-prompt-review-edit-settings')?.value.trim() || ''
|
||||
},
|
||||
agent: {
|
||||
max_iterations: parseInt(document.getElementById('agent-max-iterations').value) || 30
|
||||
max_iterations: parseInt(document.getElementById('agent-max-iterations').value) || 30,
|
||||
tool_wait_timeout_seconds: Math.max(0, parseInt(document.getElementById('agent-tool-wait-timeout-seconds')?.value || '60', 10) || 0),
|
||||
external_mcp_max_concurrent_per_server: parseInt(document.getElementById('agent-external-mcp-concurrency-server')?.value || '2', 10) || 0,
|
||||
external_mcp_max_concurrent_total: parseInt(document.getElementById('agent-external-mcp-concurrency-total')?.value || '16', 10) || 0,
|
||||
external_mcp_circuit_failure_threshold: parseInt(document.getElementById('agent-external-mcp-circuit-threshold')?.value || '3', 10) || 0,
|
||||
external_mcp_circuit_cooldown_seconds: Math.max(0, parseInt(document.getElementById('agent-external-mcp-circuit-cooldown')?.value || '60', 10) || 0)
|
||||
},
|
||||
multi_agent: (function () {
|
||||
const peRaw = document.getElementById('multi-agent-pe-loop')?.value;
|
||||
|
||||
@@ -1947,9 +1947,20 @@ function buildWebshellTimelineItemFromDetail(detail) {
|
||||
: ((typeof window.t === 'function') ? window.t('chat.callTool', { name: tn, index: idx, total: total }) : ('调用: ' + tn + (total ? ' (' + idx + '/' + total + ')' : '')));
|
||||
title = ap + '🔧 ' + wsCallTitle;
|
||||
} else if (eventType === 'tool_result') {
|
||||
var success = data.success !== false;
|
||||
var tname = data.toolName || '工具';
|
||||
title = ap + (success ? '✅ ' : '❌ ') + ((typeof window.t === 'function') ? (success ? window.t('chat.toolExecComplete', { name: tname }) : window.t('chat.toolExecFailed', { name: tname })) : (tname + (success ? ' 执行完成' : ' 执行失败')));
|
||||
var wsNoResultText = (typeof window.t === 'function') ? window.t('timeline.noResult') : '无结果';
|
||||
var wsResult = data.result != null ? data.result : (data.error != null ? data.error : (data.resultPreview != null ? data.resultPreview : wsNoResultText));
|
||||
var wsResultStr = (typeof wsResult === 'string') ? wsResult : JSON.stringify(wsResult);
|
||||
var wsDisplayState = (typeof window.getToolResultDisplayState === 'function')
|
||||
? window.getToolResultDisplayState(data, { rawText: wsResultStr })
|
||||
: { kind: ((data.isError || data.success === false) ? 'error' : 'success'), isError: (data.isError || data.success === false) };
|
||||
var wsBackgroundRunning = wsDisplayState.kind === 'background_running';
|
||||
var success = !wsDisplayState.isError && !wsBackgroundRunning;
|
||||
var wsIcon = wsBackgroundRunning ? '⏳ ' : (success ? '✅ ' : '❌ ');
|
||||
var wsLabel = wsBackgroundRunning
|
||||
? (((typeof window.getBackgroundRunningToolLabel === 'function') ? window.getBackgroundRunningToolLabel() : '后台执行中') + ': ' + tname)
|
||||
: ((typeof window.t === 'function') ? (success ? window.t('chat.toolExecComplete', { name: tname }) : window.t('chat.toolExecFailed', { name: tname })) : (tname + (success ? ' 执行完成' : ' 执行失败')));
|
||||
title = ap + wsIcon + wsLabel;
|
||||
} else if (eventType === 'eino_agent_reply') {
|
||||
title = ap + '💬 ' + ((typeof window.t === 'function') ? window.t('chat.einoAgentReplyTitle') : '子代理回复');
|
||||
} else if (eventType === 'progress') {
|
||||
@@ -1978,13 +1989,16 @@ function buildWebshellTimelineItemFromDetail(detail) {
|
||||
if (eventType === 'tool_call' && data && data._mergedResult && typeof window.buildToolResultSectionHtml === 'function') {
|
||||
html += '<div class="webshell-ai-timeline-msg tool-result-slot">' + window.buildToolResultSectionHtml(data._mergedResult) + '</div>';
|
||||
} else if (eventType === 'tool_result' && data) {
|
||||
var isError = data.isError || data.success === false;
|
||||
var noResultText = (typeof window.t === 'function') ? window.t('timeline.noResult') : '无结果';
|
||||
var result = data.result != null ? data.result : (data.error != null ? data.error : noResultText);
|
||||
var result = data.result != null ? data.result : (data.error != null ? data.error : (data.resultPreview != null ? data.resultPreview : noResultText));
|
||||
var resultStr = (typeof result === 'string') ? result : JSON.stringify(result);
|
||||
var displayState = (typeof window.getToolResultDisplayState === 'function')
|
||||
? window.getToolResultDisplayState(data, { rawText: resultStr })
|
||||
: { kind: ((data.isError || data.success === false) ? 'error' : 'success'), isError: (data.isError || data.success === false) };
|
||||
var execResultLabel = (typeof window.t === 'function') ? window.t('timeline.executionResult') : '执行结果:';
|
||||
var execIdLabel = (typeof window.t === 'function') ? window.t('timeline.executionId') : '执行ID:';
|
||||
html += '<div class="webshell-ai-timeline-msg"><div class="tool-result-section ' + (isError ? 'error' : 'success') + '"><strong>' + escapeHtml(execResultLabel) + '</strong><pre class="tool-result">' + escapeHtml(resultStr) + '</pre>' + (data.executionId ? '<div class="tool-execution-id"><span>' + escapeHtml(execIdLabel) + '</span> <code>' + escapeHtml(String(data.executionId)) + '</code></div>' : '') + '</div></div>';
|
||||
var sectionClass = displayState.kind === 'background_running' ? 'pending' : (displayState.isError ? 'error' : 'success');
|
||||
html += '<div class="webshell-ai-timeline-msg"><div class="tool-result-section ' + sectionClass + '"><strong>' + escapeHtml(execResultLabel) + '</strong><pre class="tool-result">' + escapeHtml(resultStr) + '</pre>' + (data.executionId ? '<div class="tool-execution-id"><span>' + escapeHtml(execIdLabel) + '</span> <code>' + escapeHtml(String(data.executionId)) + '</code></div>' : '') + '</div></div>';
|
||||
} else if (detail.message && detail.message !== title) {
|
||||
html += '<div class="webshell-ai-timeline-msg">' + escapeHtml(detail.message) + '</div>';
|
||||
}
|
||||
@@ -3140,14 +3154,17 @@ function runWebshellAiSend(conn, inputEl, sendBtn, messagesContainer) {
|
||||
html += '<div class="webshell-ai-timeline-msg"><pre style="white-space:pre-wrap;">' + escapeHtml(message) + '</pre></div>';
|
||||
} else if (type === 'tool_result' && data) {
|
||||
// 工具调用出参
|
||||
var isError = data.isError || data.success === false;
|
||||
var noResultText = (typeof window.t === 'function') ? window.t('timeline.noResult') : '无结果';
|
||||
var result = data.result != null ? data.result : (data.error != null ? data.error : noResultText);
|
||||
var result = data.result != null ? data.result : (data.error != null ? data.error : (data.resultPreview != null ? data.resultPreview : noResultText));
|
||||
var resultStr = (typeof result === 'string') ? result : JSON.stringify(result);
|
||||
var displayState = (typeof window.getToolResultDisplayState === 'function')
|
||||
? window.getToolResultDisplayState(data, { rawText: resultStr })
|
||||
: { kind: ((data.isError || data.success === false) ? 'error' : 'success'), isError: (data.isError || data.success === false) };
|
||||
var execResultLabel = (typeof window.t === 'function') ? window.t('timeline.executionResult') : '执行结果:';
|
||||
var execIdLabel = (typeof window.t === 'function') ? window.t('timeline.executionId') : '执行ID:';
|
||||
var sectionClass = displayState.kind === 'background_running' ? 'pending' : (displayState.isError ? 'error' : 'success');
|
||||
html += '<div class="webshell-ai-timeline-msg"><div class="tool-result-section ' +
|
||||
(isError ? 'error' : 'success') +
|
||||
sectionClass +
|
||||
'"><strong>' + escapeHtml(execResultLabel) + '</strong><pre class="tool-result">' +
|
||||
escapeHtml(resultStr) +
|
||||
'</pre>' +
|
||||
|
||||
@@ -1510,8 +1510,11 @@
|
||||
<option value="all" data-i18n="mcp.filterAll">全部</option>
|
||||
<option value="completed" data-i18n="mcpMonitor.statusCompleted">已完成</option>
|
||||
<option value="running" data-i18n="mcpMonitor.statusRunning">执行中</option>
|
||||
<option value="queued" data-i18n="mcpMonitor.statusQueued">排队中</option>
|
||||
<option value="failed" data-i18n="mcpMonitor.statusFailed">失败</option>
|
||||
<option value="hard_timeout" data-i18n="mcpMonitor.statusHardTimeout">执行超时</option>
|
||||
<option value="cancelled" data-i18n="mcpMonitor.statusCancelled">已终止</option>
|
||||
<option value="orphaned" data-i18n="mcpMonitor.statusOrphaned">孤儿任务</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
@@ -3543,6 +3546,31 @@
|
||||
<label for="agent-max-iterations" data-i18n="settingsBasic.maxIterations">最大迭代次数</label>
|
||||
<input type="number" id="agent-max-iterations" min="1" data-i18n="settingsBasic.iterationsPlaceholder" data-i18n-attr="placeholder" placeholder="30" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="agent-tool-wait-timeout-seconds" data-i18n="settingsBasic.toolWaitTimeoutSeconds">外部 MCP 单轮等待秒数</label>
|
||||
<input type="number" id="agent-tool-wait-timeout-seconds" min="0" step="1" placeholder="60" />
|
||||
<small class="form-hint" data-i18n="settingsBasic.toolWaitTimeoutSecondsHint">Agent 等待工具的单轮上限;到时返回 execution_id,worker 继续后台执行,可用 wait_tool_execution 继续等待。0 表示等到完成。</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="agent-external-mcp-concurrency-server" data-i18n="settingsBasic.externalMcpConcurrencyServer">单 MCP 并发上限</label>
|
||||
<input type="number" id="agent-external-mcp-concurrency-server" min="-1" step="1" placeholder="2" />
|
||||
<small class="form-hint" data-i18n="settingsBasic.externalMcpConcurrencyServerHint">每个外部 MCP server 同时运行的工具数;0 使用默认 2,-1 不限制。</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="agent-external-mcp-concurrency-total" data-i18n="settingsBasic.externalMcpConcurrencyTotal">外部 MCP 全局并发上限</label>
|
||||
<input type="number" id="agent-external-mcp-concurrency-total" min="-1" step="1" placeholder="16" />
|
||||
<small class="form-hint" data-i18n="settingsBasic.externalMcpConcurrencyTotalHint">所有外部 MCP 工具的总并发;0 使用默认 16,-1 不限制。</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="agent-external-mcp-circuit-threshold" data-i18n="settingsBasic.externalMcpCircuitThreshold">MCP 熔断失败阈值</label>
|
||||
<input type="number" id="agent-external-mcp-circuit-threshold" min="-1" step="1" placeholder="3" />
|
||||
<small class="form-hint" data-i18n="settingsBasic.externalMcpCircuitThresholdHint">同一 MCP server 连续失败达到阈值后临时熔断;0 使用默认 3,-1 关闭熔断。</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="agent-external-mcp-circuit-cooldown" data-i18n="settingsBasic.externalMcpCircuitCooldown">MCP 熔断冷却秒数</label>
|
||||
<input type="number" id="agent-external-mcp-circuit-cooldown" min="0" step="1" placeholder="60" />
|
||||
<small class="form-hint" data-i18n="settingsBasic.externalMcpCircuitCooldownHint">server 熔断后的等待时间;0 使用默认 60 秒。</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="multi-agent-enabled" class="modern-checkbox" />
|
||||
|
||||
Reference in New Issue
Block a user