mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-07-13 15:37:26 +02:00
Add files via upload
This commit is contained in:
+56
-17
@@ -5366,12 +5366,17 @@ html[data-theme="dark"] .user-menu-dropdown {
|
||||
}
|
||||
|
||||
.timeline-live-pruned-marker {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 3;
|
||||
margin: 8px 0;
|
||||
padding: 8px 12px;
|
||||
border: 1px dashed rgba(148, 163, 184, 0.35);
|
||||
border-radius: 6px;
|
||||
color: var(--text-muted);
|
||||
background: rgba(148, 163, 184, 0.08);
|
||||
backdrop-filter: blur(8px);
|
||||
box-shadow: 0 2px 8px rgba(15, 23, 42, 0.08);
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
@@ -11507,27 +11512,35 @@ html[data-theme="dark"] .robot-binding-one-time-badge {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
flex: 1 1 auto;
|
||||
flex-wrap: nowrap;
|
||||
padding: 1px 1px 3px;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(148, 163, 184, 0.45) transparent;
|
||||
}
|
||||
|
||||
.mcp-stats-timeline-moment {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
max-width: 144px;
|
||||
min-width: 156px;
|
||||
max-width: none;
|
||||
padding: 4px 8px;
|
||||
border-radius: 999px;
|
||||
background: rgba(59, 130, 246, 0.08);
|
||||
color: #1d4ed8;
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 600;
|
||||
flex: 0 0 auto;
|
||||
flex: 1 0 156px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.mcp-stats-timeline-moment__time {
|
||||
min-width: 0;
|
||||
flex: 1 1 auto;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
@@ -11544,6 +11557,7 @@ html[data-theme="dark"] .robot-binding-one-time-badge {
|
||||
color: #fff;
|
||||
background: #2563eb;
|
||||
box-shadow: inset 0 0 0 999px rgba(0, 0, 0, 0.04);
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.mcp-stats-timeline-moment__fail {
|
||||
@@ -11557,12 +11571,16 @@ html[data-theme="dark"] .robot-binding-one-time-badge {
|
||||
font-size: 0.625rem;
|
||||
font-weight: 700;
|
||||
white-space: nowrap;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.mcp-stats-timeline-moment--more {
|
||||
min-width: 42px;
|
||||
color: var(--text-secondary);
|
||||
background: rgba(148, 163, 184, 0.14);
|
||||
font-weight: 700;
|
||||
flex: 0 0 auto;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.mcp-stats-timeline__chart-wrap {
|
||||
@@ -11632,7 +11650,8 @@ html[data-theme="dark"] .robot-binding-one-time-badge {
|
||||
}
|
||||
.mcp-stats-timeline-moments__list {
|
||||
flex-wrap: nowrap;
|
||||
overflow: hidden;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30291,7 +30310,7 @@ html[data-theme="dark"] .chat-files-toast.chat-toast--error {
|
||||
position: relative;
|
||||
z-index: 20;
|
||||
margin: 0;
|
||||
padding: 10px 0 12px;
|
||||
padding: 6px 0;
|
||||
background: var(--bg-primary);
|
||||
border-top: 1px solid var(--border-color);
|
||||
}
|
||||
@@ -30351,23 +30370,27 @@ html[data-theme="dark"] .chat-files-toast.chat-toast--error {
|
||||
}
|
||||
.projects-graph-legend {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-wrap: nowrap;
|
||||
flex: 0 0 auto;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 8px 14px;
|
||||
}
|
||||
.projects-graph-legend-group {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
flex-wrap: nowrap;
|
||||
flex: 0 0 auto;
|
||||
align-items: center;
|
||||
gap: 6px 10px;
|
||||
}
|
||||
.projects-graph-legend-heading {
|
||||
flex: 0 0 auto;
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
color: #94a3b8;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.projects-graph-legend-divider {
|
||||
display: inline-block;
|
||||
@@ -30378,10 +30401,12 @@ html[data-theme="dark"] .chat-files-toast.chat-toast--error {
|
||||
}
|
||||
.projects-graph-legend-item {
|
||||
display: inline-flex;
|
||||
flex: 0 0 auto;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 0.75rem;
|
||||
color: #64748b;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.projects-graph-legend-item--edge i {
|
||||
display: inline-block;
|
||||
@@ -30619,8 +30644,8 @@ html[data-theme="dark"] .chat-files-toast.chat-toast--error {
|
||||
color: #64748b;
|
||||
}
|
||||
.project-fact-graph-edges-wrap {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
flex: 0 0 auto;
|
||||
min-height: min-content;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
@@ -30643,8 +30668,8 @@ html[data-theme="dark"] .chat-files-toast.chat-toast--error {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.project-fact-graph-edges-list {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
flex: 0 1 auto;
|
||||
max-height: 180px;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -30715,6 +30740,11 @@ html[data-theme="dark"] .chat-files-toast.chat-toast--error {
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.project-fact-graph-edge-delete svg {
|
||||
display: block;
|
||||
flex: 0 0 auto;
|
||||
pointer-events: none;
|
||||
}
|
||||
.project-fact-graph-edge-delete:hover {
|
||||
background: #fef2f2;
|
||||
border-color: #f87171;
|
||||
@@ -30764,6 +30794,7 @@ html[data-theme="dark"] .chat-files-toast.chat-toast--error {
|
||||
color: #94a3b8;
|
||||
}
|
||||
.project-fact-graph-sidebar-actions {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
@@ -30772,24 +30803,31 @@ html[data-theme="dark"] .chat-files-toast.chat-toast--error {
|
||||
}
|
||||
.project-fact-graph-footer {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px 12px;
|
||||
margin: 10px 0 0;
|
||||
gap: 12px;
|
||||
margin: 0;
|
||||
flex: 0 0 auto;
|
||||
flex-shrink: 0;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
.project-fact-graph-footer::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.project-fact-graph-stats {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin: 0;
|
||||
flex: 0 1 auto;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.projects-graph-stat-badge {
|
||||
display: inline-flex;
|
||||
flex: 0 0 auto;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 0.8125rem;
|
||||
@@ -30798,6 +30836,7 @@ html[data-theme="dark"] .chat-files-toast.chat-toast--error {
|
||||
border: 1px solid #e2e8f0;
|
||||
padding: 4px 12px;
|
||||
border-radius: 999px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.projects-graph-stat-badge strong {
|
||||
font-size: 0.9375rem;
|
||||
|
||||
@@ -575,6 +575,8 @@
|
||||
"viewToolDetail": "View details",
|
||||
"collapseToolDetail": "Collapse",
|
||||
"liveTimelinePruned": "Collapsed the first {{count}} live process details. View the full record page by page after the task completes.",
|
||||
"liveTimelinePrunedSingleRound": "main-agent round {{n}}",
|
||||
"liveTimelinePrunedRoundRange": "main-agent rounds {{from}}–{{to}}",
|
||||
"toolExecutionsCount": "{{n}} tool runs",
|
||||
"collapseToolExecutions": "Collapse tool runs",
|
||||
"noProcessDetail": "No process details (execution may be too fast or no detailed events)",
|
||||
@@ -2658,28 +2660,37 @@
|
||||
},
|
||||
"settingsRobotsExtra": {
|
||||
"botCommandsTitle": "Bot command instructions",
|
||||
"botCommandsEntryDesc": "View robot commands for identity, authorization, conversations, roles, and projects.",
|
||||
"botCommandsEntryDesc": "View robot commands for identity, authorization, conversations, roles, modes, projects, and safety diagnostics.",
|
||||
"viewAllCommands": "View all commands",
|
||||
"botCommandsDesc": "You can send the following commands in chat (Chinese and English supported):",
|
||||
"botCmdCategoryGeneral": "General",
|
||||
"botCmdCategoryIdentity": "Identity and authorization",
|
||||
"botCmdCategoryConversation": "Conversation",
|
||||
"botCmdCategoryRole": "Role",
|
||||
"botCmdCategoryMode": "Mode",
|
||||
"botCmdCategoryDiagnostics": "Diagnostics and safety",
|
||||
"botCmdCategoryProject": "Project",
|
||||
"botCmdHelp": "Show this help",
|
||||
"botCmdIdentity": "Show the platform sender, authorization mode, and effective RBAC identity",
|
||||
"botCmdBindUser": "Bind this platform identity to an RBAC user (user-binding mode only)",
|
||||
"botCmdUnbindUser": "Remove this platform identity binding (user-binding mode only)",
|
||||
"botCmdUnbindUser": "Remove this platform identity binding (user-binding mode only; confirmation required)",
|
||||
"botAuthHint": "Service-account mode does not accept bind or unbind commands; only configured allowlisted senders are accepted.",
|
||||
"botCmdList": "List conversations",
|
||||
"botCmdSwitch": "Switch to conversation",
|
||||
"botCmdNew": "Start new conversation",
|
||||
"botCmdClear": "Clear context",
|
||||
"botCmdCurrent": "Show current conversation, role and project",
|
||||
"botCmdStatus": "Show the current conversation, mode, role and project",
|
||||
"botCmdTask": "Show current task status and elapsed time",
|
||||
"botCmdRename": "Rename the current conversation",
|
||||
"botCmdPermissions": "Show effective business permissions",
|
||||
"botCmdDoctor": "Check key configuration status",
|
||||
"botCmdConfirmation": "Confirm or cancel a risky action",
|
||||
"botCmdStop": "Stop running task",
|
||||
"botCmdRoles": "List roles",
|
||||
"botCmdRole": "Switch role",
|
||||
"botCmdDelete": "Delete conversation",
|
||||
"botCmdModes": "List conversation modes and the current selection",
|
||||
"botCmdMode": "Switch conversation mode",
|
||||
"botCmdDelete": "Delete conversation (confirmation required)",
|
||||
"botCmdVersion": "Show version",
|
||||
"botCmdProjects": "List projects",
|
||||
"botCmdNewProject": "Create project and bind current conversation",
|
||||
|
||||
@@ -563,6 +563,8 @@
|
||||
"viewToolDetail": "查看详情",
|
||||
"collapseToolDetail": "收起",
|
||||
"liveTimelinePruned": "已收起前 {{count}} 条实时过程详情,任务完成后可按页查看完整记录",
|
||||
"liveTimelinePrunedSingleRound": "主代理第 {{n}} 轮",
|
||||
"liveTimelinePrunedRoundRange": "主代理第 {{from}}–{{to}} 轮",
|
||||
"toolExecutionsCount": "{{n}}次工具执行",
|
||||
"collapseToolExecutions": "收起工具执行",
|
||||
"noProcessDetail": "暂无过程详情(可能执行过快或未触发详细事件)",
|
||||
@@ -2646,28 +2648,37 @@
|
||||
},
|
||||
"settingsRobotsExtra": {
|
||||
"botCommandsTitle": "机器人命令说明",
|
||||
"botCommandsEntryDesc": "查看机器人对话中可用的中英文命令,包括身份鉴权、对话、角色和项目操作。",
|
||||
"botCommandsEntryDesc": "查看机器人对话中可用的中英文命令,包括身份鉴权、对话、角色、模式、项目和安全诊断。",
|
||||
"viewAllCommands": "查看全部命令",
|
||||
"botCommandsDesc": "在对话中可发送以下命令(支持中英文):",
|
||||
"botCmdCategoryGeneral": "通用",
|
||||
"botCmdCategoryIdentity": "身份与鉴权",
|
||||
"botCmdCategoryConversation": "对话",
|
||||
"botCmdCategoryRole": "角色",
|
||||
"botCmdCategoryMode": "模式",
|
||||
"botCmdCategoryDiagnostics": "诊断与安全",
|
||||
"botCmdCategoryProject": "项目",
|
||||
"botCmdHelp": "显示本帮助 | Show this help",
|
||||
"botCmdIdentity": "显示平台发送者、鉴权模式及当前实际 RBAC 身份 | Show effective identity",
|
||||
"botCmdBindUser": "绑定当前平台账号到 RBAC 用户(仅逐用户绑定模式) | Bind RBAC user",
|
||||
"botCmdUnbindUser": "解除当前平台账号绑定(仅逐用户绑定模式) | Unbind RBAC user",
|
||||
"botCmdUnbindUser": "解除当前平台账号绑定(仅逐用户绑定模式,需确认) | Unbind RBAC user",
|
||||
"botAuthHint": "专用服务账号模式不接受“绑定/解绑”命令,仅允许机器人配置中白名单内的发送者。",
|
||||
"botCmdList": "列出所有对话标题与 ID | List conversations",
|
||||
"botCmdSwitch": "指定对话继续 | Switch to conversation",
|
||||
"botCmdNew": "开启新对话 | Start new conversation",
|
||||
"botCmdClear": "清空当前上下文 | Clear context",
|
||||
"botCmdCurrent": "显示当前对话、角色与项目 | Show current conversation",
|
||||
"botCmdStatus": "汇总当前对话、模式、角色与项目 | Show current status",
|
||||
"botCmdTask": "查看当前任务状态与运行时长 | Show current task",
|
||||
"botCmdRename": "修改当前对话标题 | Rename conversation",
|
||||
"botCmdPermissions": "查看当前业务权限 | Show effective permissions",
|
||||
"botCmdDoctor": "检查关键配置状态 | Check configuration status",
|
||||
"botCmdConfirmation": "确认或取消高风险操作 | Confirm or cancel risky action",
|
||||
"botCmdStop": "中断当前任务 | Stop running task",
|
||||
"botCmdRoles": "列出所有可用角色 | List roles",
|
||||
"botCmdRole": "切换当前角色 | Switch role",
|
||||
"botCmdDelete": "删除指定对话 | Delete conversation",
|
||||
"botCmdModes": "列出对话模式与当前选择 | List conversation modes",
|
||||
"botCmdMode": "切换对话模式 | Switch conversation mode",
|
||||
"botCmdDelete": "删除指定对话(需确认) | Delete conversation",
|
||||
"botCmdVersion": "显示当前版本号 | Show version",
|
||||
"botCmdProjects": "列出所有项目 | List projects",
|
||||
"botCmdNewProject": "创建项目并绑定当前对话 | Create & bind project",
|
||||
|
||||
+24
-26
@@ -1,7 +1,13 @@
|
||||
let currentConversationId = null;
|
||||
let loadConversationRequestSeq = 0;
|
||||
|
||||
/** 轻量会话 LRU 缓存:来回切换已加载会话时避免重复网络 + 全量 DOM 重建 */
|
||||
/**
|
||||
* 轻量会话 LRU 缓存。
|
||||
*
|
||||
* 缓存只用作请求失败时的降级数据,不能先于服务端响应直接渲染:
|
||||
* 运行中会话的 process details 会持续写入,直接渲染旧快照会让
|
||||
* UI 暂时回退到旧轮次,等 task-events 接管后又突然跳到最新轮次。
|
||||
*/
|
||||
const CONVERSATION_LITE_CACHE_MAX = 12;
|
||||
const conversationLiteCache = new Map();
|
||||
|
||||
@@ -4131,32 +4137,24 @@ async function loadConversation(conversationId) {
|
||||
const seq = ++loadConversationRequestSeq;
|
||||
try {
|
||||
const cachedConversation = getConversationLiteFromCache(conversationId);
|
||||
const fetchPromise = apiFetch(`/api/conversations/${conversationId}?include_process_details=0`)
|
||||
.then(async (response) => {
|
||||
const data = await response.json();
|
||||
return { response, data };
|
||||
});
|
||||
|
||||
let conversation;
|
||||
let response;
|
||||
if (cachedConversation) {
|
||||
let conversation = null;
|
||||
let response = null;
|
||||
try {
|
||||
response = await apiFetch(`/api/conversations/${conversationId}?include_process_details=0`);
|
||||
conversation = await response.json();
|
||||
} catch (fetchError) {
|
||||
if (!cachedConversation) throw fetchError;
|
||||
console.warn('加载最新对话失败,使用本地缓存:', fetchError);
|
||||
conversation = cachedConversation;
|
||||
fetchPromise.then(({ response: freshResp, data }) => {
|
||||
if (freshResp.ok && data && seq === loadConversationRequestSeq && currentConversationId === conversationId) {
|
||||
putConversationLiteCache(conversationId, data);
|
||||
}
|
||||
}).catch(() => {});
|
||||
} else {
|
||||
const fetched = await fetchPromise;
|
||||
response = fetched.response;
|
||||
conversation = fetched.data;
|
||||
if (seq !== loadConversationRequestSeq) {
|
||||
return;
|
||||
}
|
||||
if (!response.ok) {
|
||||
showChatToast('加载对话失败: ' + (conversation.error || '未知错误'), 'error');
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (seq !== loadConversationRequestSeq) {
|
||||
return;
|
||||
}
|
||||
if (response && !response.ok) {
|
||||
showChatToast('加载对话失败: ' + (conversation.error || '未知错误'), 'error');
|
||||
return;
|
||||
}
|
||||
if (response && response.ok) {
|
||||
putConversationLiteCache(conversationId, conversation);
|
||||
}
|
||||
if (seq !== loadConversationRequestSeq) {
|
||||
|
||||
+72
-13
@@ -319,7 +319,10 @@ function finalizeOutstandingToolCallsForProgress(progressId, finalStatus) {
|
||||
if (!mapping) continue;
|
||||
if (mapping.progressId != null && String(mapping.progressId) !== pid) continue;
|
||||
const tcid = mapping.toolCallId || (String(mapKey).includes('::') ? String(mapKey).split('::').slice(1).join('::') : String(mapKey));
|
||||
updateToolCallStatus(mapping.progressId || progressId, tcid, finalStatus);
|
||||
// 已收到终态结果的调用仅清理索引,不能在任务收尾时被改写成失败。
|
||||
if (!mapping.terminalStatus) {
|
||||
updateToolCallStatus(mapping.progressId || progressId, tcid, finalStatus);
|
||||
}
|
||||
toolCallStatusMap.delete(mapKey);
|
||||
}
|
||||
}
|
||||
@@ -1866,15 +1869,30 @@ function handleStreamEvent(event, progressElement, progressId,
|
||||
? String(prevMainIter.workflowNodeId).trim()
|
||||
: '';
|
||||
const curNode = d.workflowNodeId != null ? String(d.workflowNodeId).trim() : '';
|
||||
const prevAgent = prevMainIter && prevMainIter.einoAgent != null
|
||||
? String(prevMainIter.einoAgent).trim()
|
||||
: '';
|
||||
const curAgent = d.einoAgent != null ? String(d.einoAgent).trim() : '';
|
||||
const prevOrchestration = prevMainIter && prevMainIter.orchestration != null
|
||||
? String(prevMainIter.orchestration).trim()
|
||||
: '';
|
||||
const curOrchestration = d.orchestration != null ? String(d.orchestration).trim() : '';
|
||||
const duplicateMainIteration = prevN != null && String(prevN) === String(n) &&
|
||||
prevNode === curNode && prevAgent === curAgent &&
|
||||
prevOrchestration === curOrchestration;
|
||||
mainIterationStateByProgressId.set(String(progressId), {
|
||||
iteration: n,
|
||||
orchestration: d.orchestration != null ? d.orchestration : '',
|
||||
workflowNodeId: curNode
|
||||
workflowNodeId: curNode,
|
||||
einoAgent: curAgent
|
||||
});
|
||||
// 主通道进入新轮次或图编排切换到新 Agent 节点后,不复用上一段的流式时间线条目
|
||||
if (prevN != null && (n < prevN || prevN !== n || (curNode && prevNode && curNode !== prevNode))) {
|
||||
clearTimelineStreamStates(progressId);
|
||||
}
|
||||
// 同一主代理可能从“进入模型”和“检测到工具批次”两条路径补发同一轮次。
|
||||
// 状态缓存仍更新,但时间线只保留一个幂等条目。
|
||||
if (duplicateMainIteration) break;
|
||||
}
|
||||
let iterTitle;
|
||||
if (d.orchestration === 'plan_execute' && d.einoScope === 'main') {
|
||||
@@ -2306,7 +2324,9 @@ function handleStreamEvent(event, progressElement, progressId,
|
||||
const existingItem = document.getElementById(existing.itemId);
|
||||
if (existingItem) {
|
||||
// 同一 toolCallId 的重复 tool_call(重试/补发)只更新状态,不重复追加条目。
|
||||
updateToolCallStatus(progressId, toolCallId, 'running');
|
||||
if (!existing.terminalStatus) {
|
||||
updateToolCallStatus(progressId, toolCallId, 'running');
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2359,7 +2379,7 @@ function handleStreamEvent(event, progressElement, progressId,
|
||||
const mapKey = toolCallMapKey(progressId, resultToolCallId);
|
||||
if (toolCallStatusMap.has(mapKey)) {
|
||||
updateToolCallStatus(progressId, resultToolCallId, success ? 'completed' : 'failed');
|
||||
toolCallStatusMap.delete(mapKey);
|
||||
toolCallStatusMap.get(mapKey).terminalStatus = success ? 'completed' : 'failed';
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2367,7 +2387,7 @@ function handleStreamEvent(event, progressElement, progressId,
|
||||
const mapKey = toolCallMapKey(progressId, resultToolCallId);
|
||||
if (toolCallStatusMap.has(mapKey)) {
|
||||
updateToolCallStatus(progressId, resultToolCallId, success ? 'completed' : 'failed');
|
||||
toolCallStatusMap.delete(mapKey);
|
||||
toolCallStatusMap.get(mapKey).terminalStatus = success ? 'completed' : 'failed';
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2375,7 +2395,7 @@ function handleStreamEvent(event, progressElement, progressId,
|
||||
|
||||
if (resultToolCallId && toolCallStatusMap.has(toolCallMapKey(progressId, resultToolCallId))) {
|
||||
updateToolCallStatus(progressId, resultToolCallId, success ? 'completed' : 'failed');
|
||||
toolCallStatusMap.delete(toolCallMapKey(progressId, resultToolCallId));
|
||||
toolCallStatusMap.get(toolCallMapKey(progressId, resultToolCallId)).terminalStatus = success ? 'completed' : 'failed';
|
||||
}
|
||||
addTimelineItem(timeline, 'tool_result', {
|
||||
title: timelineAgentBracketPrefix(resultInfo) + statusIcon + ' ' + resultExecText,
|
||||
@@ -3948,6 +3968,25 @@ function isLiveProgressTimeline(timeline) {
|
||||
return !!(timeline && timeline.id && /^progress-\d+-\d+-timeline$/.test(timeline.id));
|
||||
}
|
||||
|
||||
function formatLiveTimelinePrunedMarker(marker) {
|
||||
const count = parseInt(marker.dataset.prunedCount || '0', 10) || 0;
|
||||
const minIteration = parseInt(marker.dataset.prunedMainIterationMin || '0', 10) || 0;
|
||||
const maxIteration = parseInt(marker.dataset.prunedMainIterationMax || '0', 10) || 0;
|
||||
const translate = typeof window.t === 'function' ? window.t : null;
|
||||
const baseText = translate
|
||||
? translate('chat.liveTimelinePruned', { count: count })
|
||||
: ('已收起前 ' + count + ' 条实时过程详情,任务完成后可按页查看完整记录');
|
||||
if (minIteration <= 0 || maxIteration < minIteration) return baseText;
|
||||
if (minIteration === maxIteration) {
|
||||
return baseText + ' · ' + (translate
|
||||
? translate('chat.liveTimelinePrunedSingleRound', { n: minIteration })
|
||||
: ('主代理第 ' + minIteration + ' 轮'));
|
||||
}
|
||||
return baseText + ' · ' + (translate
|
||||
? translate('chat.liveTimelinePrunedRoundRange', { from: minIteration, to: maxIteration })
|
||||
: ('主代理第 ' + minIteration + '–' + maxIteration + ' 轮'));
|
||||
}
|
||||
|
||||
function pruneLiveTimelineIfNeeded(timeline) {
|
||||
if (!isLiveProgressTimeline(timeline)) return;
|
||||
const items = Array.from(timeline.children).filter(function (el) {
|
||||
@@ -3970,16 +4009,37 @@ function pruneLiveTimelineIfNeeded(timeline) {
|
||||
if (!marker) {
|
||||
marker = document.createElement('div');
|
||||
marker.className = 'timeline-live-pruned-marker';
|
||||
marker.setAttribute('role', 'status');
|
||||
marker.setAttribute('aria-live', 'polite');
|
||||
timeline.insertBefore(marker, timeline.firstChild);
|
||||
}
|
||||
let prunedMainIterationMin = parseInt(marker.dataset.prunedMainIterationMin || '0', 10) || 0;
|
||||
let prunedMainIterationMax = parseInt(marker.dataset.prunedMainIterationMax || '0', 10) || 0;
|
||||
for (let i = 0; i < removeCount; i++) {
|
||||
removable[i].remove();
|
||||
const removed = removable[i];
|
||||
if (removed && removed.dataset && removed.dataset.timelineType === 'iteration' &&
|
||||
removed.dataset.einoScope !== 'sub') {
|
||||
const iteration = parseInt(removed.dataset.iterationN || '0', 10) || 0;
|
||||
if (iteration > 0) {
|
||||
if (prunedMainIterationMin === 0 || iteration < prunedMainIterationMin) {
|
||||
prunedMainIterationMin = iteration;
|
||||
}
|
||||
if (iteration > prunedMainIterationMax) {
|
||||
prunedMainIterationMax = iteration;
|
||||
}
|
||||
}
|
||||
}
|
||||
removed.remove();
|
||||
}
|
||||
pruned += removeCount;
|
||||
marker.dataset.prunedCount = String(pruned);
|
||||
marker.textContent = typeof window.t === 'function'
|
||||
? window.t('chat.liveTimelinePruned', { count: pruned })
|
||||
: ('已收起前 ' + pruned + ' 条实时过程详情,任务完成后可按页查看完整记录');
|
||||
marker.dataset.prunedMainIterationMin = String(prunedMainIterationMin);
|
||||
marker.dataset.prunedMainIterationMax = String(prunedMainIterationMax);
|
||||
marker.textContent = formatLiveTimelinePrunedMarker(marker);
|
||||
// 始终放在已保留详情之前;配合 sticky 样式,查看最新内容时也能感知历史已裁剪。
|
||||
if (timeline.firstChild !== marker) {
|
||||
timeline.insertBefore(marker, timeline.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
function addTimelineItem(timeline, type, options) {
|
||||
@@ -4816,7 +4876,7 @@ function updateMonitorTimelineSection() {
|
||||
}
|
||||
|
||||
|
||||
const MCP_STATS_TOP_N = 6;
|
||||
const MCP_STATS_TOP_N = 3;
|
||||
const MCP_TIMELINE_RANGES = ['24h', '7d', '30d'];
|
||||
|
||||
function getMcpMonitorTimelineRange() {
|
||||
@@ -6565,8 +6625,7 @@ function refreshProgressAndTimelineI18n() {
|
||||
});
|
||||
|
||||
document.querySelectorAll('.timeline-live-pruned-marker').forEach(function (marker) {
|
||||
const count = parseInt(marker.dataset.prunedCount || '0', 10) || 0;
|
||||
marker.textContent = _t('chat.liveTimelinePruned', { count: count });
|
||||
marker.textContent = formatLiveTimelinePrunedMarker(marker);
|
||||
});
|
||||
|
||||
// 详情区「展开/收起」按钮
|
||||
|
||||
@@ -1178,7 +1178,7 @@ function renderGraphEdgesListHtml(factKey, graphData, selectedEdgeId) {
|
||||
const synthetic = isSyntheticGraphEdge(e);
|
||||
const deleteBtn = synthetic
|
||||
? `<span class="project-fact-graph-edge-synthetic" title="${escapeHtml(tp('projects.graphEdgeSynthetic'))}">—</span>`
|
||||
: `<button type="button" class="project-fact-graph-edge-delete" data-edge-id="${escapeHtml(e.id)}" onclick="event.stopPropagation(); deleteProjectFactEdge(this.dataset.edgeId)" title="${escapeHtml(tp('projects.graphDeleteEdge'))}">×</button>`;
|
||||
: `<button type="button" class="project-fact-graph-edge-delete" data-edge-id="${escapeHtml(e.id)}" onclick="event.stopPropagation(); deleteProjectFactEdge(this.dataset.edgeId)" title="${escapeHtml(tp('projects.graphDeleteEdge'))}"><svg width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true"><path d="M2 2l8 8M10 2l-8 8" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg></button>`;
|
||||
return `<div class="project-fact-graph-edge-item${selected}" data-edge-id="${escapeHtml(e.id)}" onclick="focusProjectFactGraphEdge(${JSON.stringify(e.id)})">
|
||||
<span class="project-fact-graph-edge-dir">${escapeHtml(dirLabel)}</span>
|
||||
<span class="project-fact-graph-edge-type">${escapeHtml(e.type || '')}</span>
|
||||
@@ -2675,15 +2675,31 @@ function initChatProjectSelector() {
|
||||
});
|
||||
}
|
||||
|
||||
function initProjectGraphFooterWheelScroll() {
|
||||
const footer = document.querySelector('.project-fact-graph-footer');
|
||||
if (!footer || footer.dataset.wheelScrollBound === 'true') return;
|
||||
footer.dataset.wheelScrollBound = 'true';
|
||||
footer.addEventListener('wheel', (event) => {
|
||||
if (footer.scrollWidth <= footer.clientWidth || Math.abs(event.deltaX) > Math.abs(event.deltaY)) return;
|
||||
const maxScrollLeft = footer.scrollWidth - footer.clientWidth;
|
||||
const nextScrollLeft = Math.max(0, Math.min(maxScrollLeft, footer.scrollLeft + event.deltaY));
|
||||
if (nextScrollLeft === footer.scrollLeft) return;
|
||||
footer.scrollLeft = nextScrollLeft;
|
||||
event.preventDefault();
|
||||
}, { passive: false });
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initChatProjectSelector();
|
||||
initProjectListActionMenu();
|
||||
initProjectGraphFooterWheelScroll();
|
||||
refreshProjectsFilterSelects();
|
||||
});
|
||||
} else {
|
||||
initChatProjectSelector();
|
||||
initProjectListActionMenu();
|
||||
initProjectGraphFooterWheelScroll();
|
||||
refreshProjectsFilterSelects();
|
||||
}
|
||||
|
||||
|
||||
@@ -3980,7 +3980,7 @@
|
||||
<ul class="robot-cmd-list">
|
||||
<li><code>身份</code> <code>whoami</code> — <span data-i18n="settingsRobotsExtra.botCmdIdentity">显示平台发送者、鉴权模式及当前实际 RBAC 身份 | Show effective identity</span></li>
|
||||
<li><code>绑定 <绑定码></code> <code>bind <code></code> — <span data-i18n="settingsRobotsExtra.botCmdBindUser">绑定当前平台账号到 RBAC 用户(仅逐用户绑定模式) | Bind RBAC user</span></li>
|
||||
<li><code>解绑</code> <code>unbind</code> — <span data-i18n="settingsRobotsExtra.botCmdUnbindUser">解除当前平台账号绑定(仅逐用户绑定模式) | Unbind RBAC user</span></li>
|
||||
<li><code>解绑</code> <code>unbind</code> — <span data-i18n="settingsRobotsExtra.botCmdUnbindUser">解除当前平台账号绑定(仅逐用户绑定模式,需确认) | Unbind RBAC user</span></li>
|
||||
</ul>
|
||||
<p class="settings-description" data-i18n="settingsRobotsExtra.botAuthHint">专用服务账号模式不接受“绑定/解绑”命令,仅允许机器人配置中白名单内的发送者。</p>
|
||||
|
||||
@@ -3990,9 +3990,11 @@
|
||||
<li><code>切换 <ID></code> <code>switch <ID></code> — <span data-i18n="settingsRobotsExtra.botCmdSwitch">指定对话继续 | Switch to conversation</span></li>
|
||||
<li><code>新对话</code> <code>new</code> — <span data-i18n="settingsRobotsExtra.botCmdNew">开启新对话 | Start new conversation</span></li>
|
||||
<li><code>清空</code> <code>clear</code> — <span data-i18n="settingsRobotsExtra.botCmdClear">清空当前上下文 | Clear context</span></li>
|
||||
<li><code>当前</code> <code>current</code> — <span data-i18n="settingsRobotsExtra.botCmdCurrent">显示当前对话、角色与项目 | Show current conversation</span></li>
|
||||
<li><code>状态</code> <code>status</code> — <span data-i18n="settingsRobotsExtra.botCmdStatus">汇总当前对话、模式、角色与项目 | Show current status</span></li>
|
||||
<li><code>任务</code> <code>task</code> — <span data-i18n="settingsRobotsExtra.botCmdTask">查看当前任务状态与运行时长 | Show current task</span></li>
|
||||
<li><code>重命名 <名称></code> <code>rename <name></code> — <span data-i18n="settingsRobotsExtra.botCmdRename">修改当前对话标题 | Rename conversation</span></li>
|
||||
<li><code>停止</code> <code>stop</code> — <span data-i18n="settingsRobotsExtra.botCmdStop">中断当前任务 | Stop running task</span></li>
|
||||
<li><code>删除 <ID></code> <code>delete <ID></code> — <span data-i18n="settingsRobotsExtra.botCmdDelete">删除指定对话 | Delete conversation</span></li>
|
||||
<li><code>删除 <ID></code> <code>delete <ID></code> — <span data-i18n="settingsRobotsExtra.botCmdDelete">删除指定对话(需确认) | Delete conversation</span></li>
|
||||
</ul>
|
||||
|
||||
<p class="robot-cmd-category" data-i18n="settingsRobotsExtra.botCmdCategoryRole">角色</p>
|
||||
@@ -4001,6 +4003,12 @@
|
||||
<li><code>角色 <名></code> <code>role <name></code> — <span data-i18n="settingsRobotsExtra.botCmdRole">切换当前角色 | Switch role</span></li>
|
||||
</ul>
|
||||
|
||||
<p class="robot-cmd-category" data-i18n="settingsRobotsExtra.botCmdCategoryMode">模式</p>
|
||||
<ul class="robot-cmd-list">
|
||||
<li><code>模式</code> <code>modes</code> — <span data-i18n="settingsRobotsExtra.botCmdModes">列出对话模式与当前选择 | List conversation modes</span></li>
|
||||
<li><code>模式 <名称></code> <code>mode <name></code> — <span data-i18n="settingsRobotsExtra.botCmdMode">切换对话模式 | Switch conversation mode</span></li>
|
||||
</ul>
|
||||
|
||||
<p class="robot-cmd-category" data-i18n="settingsRobotsExtra.botCmdCategoryProject">项目</p>
|
||||
<ul class="robot-cmd-list">
|
||||
<li><code>项目</code> <code>projects</code> — <span data-i18n="settingsRobotsExtra.botCmdProjects">列出所有项目 | List projects</span></li>
|
||||
@@ -4009,6 +4017,13 @@
|
||||
<li><code>解除项目</code> <code>unbind project</code> — <span data-i18n="settingsRobotsExtra.botCmdUnbindProject">解除当前对话的项目绑定 | Unbind project</span></li>
|
||||
</ul>
|
||||
|
||||
<p class="robot-cmd-category" data-i18n="settingsRobotsExtra.botCmdCategoryDiagnostics">诊断与安全</p>
|
||||
<ul class="robot-cmd-list">
|
||||
<li><code>权限</code> <code>permissions</code> — <span data-i18n="settingsRobotsExtra.botCmdPermissions">查看当前业务权限 | Show effective permissions</span></li>
|
||||
<li><code>诊断</code> <code>doctor</code> — <span data-i18n="settingsRobotsExtra.botCmdDoctor">检查关键配置状态 | Check configuration status</span></li>
|
||||
<li><code>确认</code> <code>confirm</code> / <code>取消</code> <code>cancel</code> — <span data-i18n="settingsRobotsExtra.botCmdConfirmation">确认或取消高风险操作 | Confirm or cancel risky action</span></li>
|
||||
</ul>
|
||||
|
||||
<p class="settings-description robot-cmd-footer" data-i18n="settingsRobotsExtra.botCommandsFooter">除以上命令外,直接输入内容将按绑定用户或服务账号的实时 RBAC 权限发送给 AI。Otherwise, text is sent to AI under the effective RBAC identity.</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -4017,7 +4032,7 @@
|
||||
<div class="settings-subsection robot-command-entry">
|
||||
<div class="robot-command-entry-copy">
|
||||
<h4 data-i18n="settingsRobotsExtra.botCommandsTitle">机器人命令说明</h4>
|
||||
<p class="settings-description" data-i18n="settingsRobotsExtra.botCommandsEntryDesc">查看机器人对话中可用的中英文命令,包括身份鉴权、对话、角色和项目操作。</p>
|
||||
<p class="settings-description" data-i18n="settingsRobotsExtra.botCommandsEntryDesc">查看机器人对话中可用的中英文命令,包括身份鉴权、对话、角色、模式、项目和安全诊断。</p>
|
||||
</div>
|
||||
<button type="button" class="btn-secondary" onclick="openRobotCommandsModal()" data-i18n="settingsRobotsExtra.viewAllCommands">查看全部命令</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user