feat: enrich page ai ACP runtime UI
This commit is contained in:
@@ -3936,6 +3936,7 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
id: 'sess_' + now + '_' + Math.random().toString(16).slice(2, 8),
|
||||
title: title || '新会话',
|
||||
profile: pageAiCurrentProfile(),
|
||||
acpRuntime: pageUiState.pageAiAcpRuntime || '',
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
messages: []
|
||||
@@ -3950,9 +3951,10 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
id: String(session && session.id || '').trim() || pageAiNewSession().id,
|
||||
title: String(session && session.title || '').trim() || '新会话',
|
||||
profile: String(session && session.profile || pageAiCurrentProfile()).trim() || 'default',
|
||||
acpRuntime: String(session && session.acpRuntime || '').trim(),
|
||||
createdAt: Number(session && session.createdAt || Date.now()),
|
||||
updatedAt: Number(session && session.updatedAt || Date.now()),
|
||||
messages: Array.isArray(session && session.messages) ? session.messages.slice(-40) : []
|
||||
messages: Array.isArray(session && session.messages) ? session.messages.slice(-300) : []
|
||||
};
|
||||
})
|
||||
.sort(function(a, b) {
|
||||
@@ -3967,7 +3969,19 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
var parsed = raw ? JSON.parse(raw) : null;
|
||||
var activeId = String(parsed && parsed.activeSessionId || '').trim();
|
||||
var activeProfile = String(parsed && parsed.activeProfileName || '').trim();
|
||||
var activeAcpRuntime = String(parsed && parsed.activeAcpRuntime || '').trim();
|
||||
var sessions = pageAiNormalizeSessions(parsed && parsed.sessions);
|
||||
if (activeAcpRuntime) pageUiState.pageAiAcpRuntime = activeAcpRuntime;
|
||||
if (activeProfile) pageAiSetActiveProfile(activeProfile);
|
||||
if (sessions.length) {
|
||||
pageUiState.pageAiSessions = sessions;
|
||||
var activeSession = sessions.find(function(session) { return session.id === activeId; }) || sessions[0];
|
||||
pageUiState.pageAiActiveSessionId = activeSession.id;
|
||||
if (activeSession.profile) pageAiSetActiveProfile(activeSession.profile);
|
||||
if (activeSession.acpRuntime) pageUiState.pageAiAcpRuntime = activeSession.acpRuntime;
|
||||
pageUiState.pageAiMessages = Array.isArray(activeSession.messages) ? activeSession.messages.slice() : [];
|
||||
return;
|
||||
}
|
||||
if (activeId) {
|
||||
pageUiState.pageAiSessions = [pageAiNewSession('当前页问答')];
|
||||
pageUiState.pageAiSessions[0].id = activeId;
|
||||
@@ -3986,9 +4000,13 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
document.documentElement.setAttribute('data-mnote-page-ai-session-owner', 'hermes');
|
||||
document.documentElement.setAttribute('data-mnote-page-ai-session-key', pageAiStorageKey());
|
||||
try {
|
||||
pageAiSyncCurrentSessionMessages();
|
||||
window.localStorage.setItem(pageAiStorageKey(), JSON.stringify({
|
||||
version: 2,
|
||||
activeSessionId: pageUiState.pageAiActiveSessionId,
|
||||
activeProfileName: pageAiCurrentProfile()
|
||||
activeProfileName: pageAiCurrentProfile(),
|
||||
activeAcpRuntime: pageUiState.pageAiAcpRuntime || '',
|
||||
sessions: pageAiNormalizeSessions(pageUiState.pageAiSessions)
|
||||
}));
|
||||
} catch (_) {}
|
||||
}
|
||||
@@ -4016,6 +4034,7 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
id: String(payload.sessionId || '').trim(),
|
||||
title: String(payload.title || '当前页问答'),
|
||||
profile: String(payload.profile || pageAiCurrentProfile()).trim() || 'default',
|
||||
acpRuntime: pageUiState.pageAiAcpRuntime || '',
|
||||
createdAt: Date.now(),
|
||||
updatedAt: Date.now(),
|
||||
messages: pageUiState.pageAiMessages.slice()
|
||||
@@ -4046,7 +4065,7 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
renderPageAiControls();
|
||||
return;
|
||||
}
|
||||
pageUiState.pageAiMessages = messages.slice(-40).map(function(message) {
|
||||
pageUiState.pageAiMessages = messages.slice(-300).map(function(message) {
|
||||
return {
|
||||
role: message.role === 'user' ? 'user' : (message.role === 'tool' ? 'tool' : 'assistant'),
|
||||
content: String(message.content || '')
|
||||
@@ -4064,6 +4083,15 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
}) || null;
|
||||
}
|
||||
|
||||
function pageAiSyncCurrentSessionMessages() {
|
||||
var session = pageAiCurrentSession();
|
||||
if (!session) return;
|
||||
session.messages = Array.isArray(pageUiState.pageAiMessages) ? pageUiState.pageAiMessages.slice(-300) : [];
|
||||
session.profile = pageAiCurrentProfile();
|
||||
session.acpRuntime = pageUiState.pageAiAcpRuntime || '';
|
||||
session.updatedAt = Date.now();
|
||||
}
|
||||
|
||||
function pageAiSetActiveSession(sessionId) {
|
||||
var session = pageUiState.pageAiSessions.find(function(item) { return item.id === sessionId; });
|
||||
if (!session) return;
|
||||
@@ -4359,9 +4387,14 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
} catch (_) {
|
||||
toolEvent = {};
|
||||
}
|
||||
var toolName = String(toolEvent && (toolEvent.name || toolEvent.tool || toolEvent.toolName || eventName) || eventName);
|
||||
var rawToolName = toolEvent && (toolEvent.name || toolEvent.tool || toolEvent.toolName || '');
|
||||
var toolName = String(rawToolName || eventName);
|
||||
var toolCallId = String(toolEvent && (toolEvent.tool_call_id || toolEvent.toolCallId || toolEvent.id) || (runId + ':' + toolName));
|
||||
var status = eventName === 'tool.completed' ? 'completed' : (eventName === 'tool.failed' ? 'failed' : 'running');
|
||||
var eventStatus = String(toolEvent && toolEvent.status || '').trim();
|
||||
var status = eventName === 'tool.completed'
|
||||
? (toolEvent && toolEvent.error ? 'failed' : 'completed')
|
||||
: (eventName === 'tool.failed' ? 'failed' : (eventStatus || 'running'));
|
||||
if (status === 'in_progress' || status === 'pending') status = 'running';
|
||||
var traceId = String(toolEvent && (toolEvent.trace_id || toolEvent.traceId) || runTraceId || '');
|
||||
var auditId = String(toolEvent && (toolEvent.audit_id || toolEvent.auditId) || '');
|
||||
var argsSummary = pageAiPreviewValue(toolEvent && (toolEvent.arguments || toolEvent.args || toolEvent.input));
|
||||
@@ -4380,12 +4413,14 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
var existing = pageUiState.pageAiMessages.find(function(item) {
|
||||
return item.role === 'tool' && item.toolCallId === toolCallId;
|
||||
});
|
||||
if (!rawToolName && existing && existing.toolName) toolName = existing.toolName;
|
||||
if (!existing) {
|
||||
existing = {
|
||||
role: 'tool',
|
||||
content: toolName,
|
||||
toolCallId: toolCallId,
|
||||
toolName: toolName,
|
||||
toolKind: String(toolEvent && toolEvent.kind || ''),
|
||||
status: status,
|
||||
argsSummary: '',
|
||||
resultSummary: '',
|
||||
@@ -4396,6 +4431,7 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
}
|
||||
existing.content = toolName;
|
||||
existing.toolName = toolName;
|
||||
existing.toolKind = String(toolEvent && toolEvent.kind || existing.toolKind || '');
|
||||
existing.status = status;
|
||||
existing.traceId = traceId || existing.traceId || '';
|
||||
existing.auditId = auditId || existing.auditId || '';
|
||||
@@ -4864,7 +4900,7 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
// When ACP is selected, populate agent panel with ACP runtime info
|
||||
var agentPanel = drawer.querySelector('[data-page-ai-agent-panel]');
|
||||
if (agentPanel instanceof HTMLElement) {
|
||||
if (isAcp) {
|
||||
if (pageUiState.pageAiAcpRuntime === 'reasonix') {
|
||||
var rt = pageUiState.pageAiAcpRuntimes.find(function(r) { return r.name === pageUiState.pageAiAcpRuntime; }) || {};
|
||||
var keyStatus = rt.apiKeyConfigured ? '已配置' : '未检测到 DEEPSEEK_API_KEY';
|
||||
agentPanel.innerHTML = '' +
|
||||
@@ -4966,8 +5002,7 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
memoryError.hidden = !memoryErrorText;
|
||||
}
|
||||
var agentPanel = drawer.querySelector('[data-page-ai-agent-panel]');
|
||||
if (agentPanel instanceof HTMLElement && !isAcp) {
|
||||
// Only populate Hermes memory when NOT in ACP mode (ACP handles this earlier in the function)
|
||||
if (agentPanel instanceof HTMLElement && pageUiState.pageAiAcpRuntime !== 'reasonix') {
|
||||
agentPanel.innerHTML = ['soul', 'user', 'memory'].map(function(section) {
|
||||
var label = pageAiMemoryFileLabel(section);
|
||||
var value = pageUiState.pageAiProfileMemoryDrafts[section] || '';
|
||||
@@ -5322,20 +5357,28 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
var roleLabel = item.role === 'user' ? '你' : (item.role === 'tool' ? '工具' : 'AI');
|
||||
if (item.role === 'tool') {
|
||||
var statusLabel = item.status === 'completed' ? '完成' : (item.status === 'failed' ? '失败' : '运行中');
|
||||
var detailRows = [
|
||||
item.argsSummary ? '<div class="wolai-page-ai-tool-meta">参数 ' + escapeHtml(item.argsSummary) + '</div>' : '',
|
||||
item.resultSummary ? '<div class="wolai-page-ai-tool-meta">结果 ' + escapeHtml(item.resultSummary) + '</div>' : '',
|
||||
(item.traceId || item.auditId) ? '<div class="wolai-page-ai-tool-meta">' + escapeHtml([item.traceId, item.auditId].filter(Boolean).join(' · ')) + '</div>' : ''
|
||||
].filter(Boolean).join('');
|
||||
return '' +
|
||||
'<div class="wolai-page-ai-message wolai-page-ai-message--tool" data-page-ai-tool-card data-page-ai-tool-call-id="' + escapeHtml(item.toolCallId || '') + '" data-page-ai-tool-status="' + escapeHtml(item.status || '') + '">' +
|
||||
'<div class="wolai-page-ai-message-role">' + escapeHtml(roleLabel) + '</div>' +
|
||||
'<div class="wolai-page-ai-message-text">' +
|
||||
'<strong>' + escapeHtml(item.toolName || item.content || 'tool') + '</strong>' +
|
||||
'<div class="wolai-page-ai-tool-meta">' + escapeHtml(statusLabel) + (item.toolCallId ? ' · ' + escapeHtml(item.toolCallId) : '') + '</div>' +
|
||||
(item.argsSummary ? '<div class="wolai-page-ai-tool-meta">参数 ' + escapeHtml(item.argsSummary) + '</div>' : '') +
|
||||
(item.resultSummary ? '<div class="wolai-page-ai-tool-meta">结果 ' + escapeHtml(item.resultSummary) + '</div>' : '') +
|
||||
((item.traceId || item.auditId) ? '<div class="wolai-page-ai-tool-meta">' + escapeHtml([item.traceId, item.auditId].filter(Boolean).join(' · ')) + '</div>' : '') +
|
||||
'<details class="wolai-page-ai-tool-details">' +
|
||||
'<summary>' +
|
||||
'<strong>' + escapeHtml(item.toolName || item.content || 'tool') + '</strong>' +
|
||||
'<span>' + escapeHtml([statusLabel, item.toolKind, item.toolCallId].filter(Boolean).join(' · ')) + '</span>' +
|
||||
'</summary>' +
|
||||
(detailRows || '<div class="wolai-page-ai-tool-meta">暂无参数或结果详情。</div>') +
|
||||
'</details>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
var streamingAttr = item.streaming ? ' data-page-ai-streaming="true"' : '';
|
||||
return '' +
|
||||
'<div class="wolai-page-ai-message wolai-page-ai-message--' + escapeHtml(item.role || 'assistant') + '">' +
|
||||
'<div class="wolai-page-ai-message wolai-page-ai-message--' + escapeHtml(item.role || 'assistant') + '"' + streamingAttr + '>' +
|
||||
'<div class="wolai-page-ai-message-role">' + escapeHtml(roleLabel) + '</div>' +
|
||||
'<div class="wolai-page-ai-message-text">' + escapeHtml(item.content || '') + '</div>' +
|
||||
'</div>';
|
||||
@@ -5419,6 +5462,62 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
}
|
||||
}
|
||||
|
||||
function pageAiDecodeDeltaText(payloadText) {
|
||||
try {
|
||||
var payload = JSON.parse(payloadText || 'null');
|
||||
return String((payload && (payload.text || payload.delta)) || '');
|
||||
} catch (_) {
|
||||
return String(payloadText || '');
|
||||
}
|
||||
}
|
||||
|
||||
function pageAiEnsureStreamingAssistantMessage(runId) {
|
||||
var id = String(runId || pageUiState.pageAiCurrentRunId || '');
|
||||
var existing = pageUiState.pageAiMessages.find(function(item) {
|
||||
return item.role === 'assistant' && item.streaming === true && item.runId === id;
|
||||
});
|
||||
if (existing) return existing;
|
||||
existing = {
|
||||
role: 'assistant',
|
||||
content: '',
|
||||
runId: id,
|
||||
streaming: true
|
||||
};
|
||||
pageUiState.pageAiMessages.push(existing);
|
||||
return existing;
|
||||
}
|
||||
|
||||
function pageAiAppendStreamingAssistantDelta(runId, deltaText) {
|
||||
var delta = String(deltaText || '');
|
||||
if (!delta) return '';
|
||||
var message = pageAiEnsureStreamingAssistantMessage(runId);
|
||||
message.content = String(message.content || '') + delta;
|
||||
pageAiSyncCurrentSessionMessages();
|
||||
renderPageAiConversation();
|
||||
return message.content;
|
||||
}
|
||||
|
||||
function pageAiFinishStreamingAssistantMessage(runId, finalText, promptText) {
|
||||
var id = String(runId || pageUiState.pageAiCurrentRunId || '');
|
||||
var message = pageUiState.pageAiMessages.find(function(item) {
|
||||
return item.role === 'assistant' && item.streaming === true && item.runId === id;
|
||||
});
|
||||
var text = String(finalText || (message && message.content) || '');
|
||||
var content = humanizePageAiResponse(text, promptText);
|
||||
if (message) {
|
||||
message.content = content;
|
||||
message.streaming = false;
|
||||
} else if (content) {
|
||||
pageUiState.pageAiMessages.push({
|
||||
role: 'assistant',
|
||||
content: content,
|
||||
runId: id
|
||||
});
|
||||
}
|
||||
pageAiSyncCurrentSessionMessages();
|
||||
pageAiPersistSessions();
|
||||
}
|
||||
|
||||
function pageAiLooksLikeBlockEdit(prompt) {
|
||||
var text = searchText(prompt);
|
||||
return ['新增', '添加', '插入', '删除', '删掉', '修改', '替换', '改成', '移动', '移到', 'move', 'replace', 'delete', 'insert'].some(function(word) {
|
||||
@@ -5496,6 +5595,7 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
currentSession.messages = pageUiState.pageAiMessages.slice();
|
||||
currentSession.updatedAt = Date.now();
|
||||
}
|
||||
pageAiPersistSessions();
|
||||
renderPageAiConversation();
|
||||
renderPageAiControls();
|
||||
return true;
|
||||
@@ -5523,6 +5623,7 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
currentSession.messages = pageUiState.pageAiMessages.slice();
|
||||
currentSession.updatedAt = Date.now();
|
||||
}
|
||||
pageAiPersistSessions();
|
||||
renderPageAiConversation();
|
||||
if (!allowQueue && await pageAiTryBlockEditWorkflow(prompt, scopedContext)) {
|
||||
return;
|
||||
@@ -5559,6 +5660,7 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
currentSession.messages = pageUiState.pageAiMessages.slice();
|
||||
currentSession.updatedAt = Date.now();
|
||||
}
|
||||
pageAiPersistSessions();
|
||||
renderPageAiConversation();
|
||||
renderPageAiControls();
|
||||
return;
|
||||
@@ -5583,24 +5685,19 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
await streamPageAiResponse(eventResponse, function(eventName, payloadText) {
|
||||
if (eventName === 'message.delta') {
|
||||
if (pageUiState.pageAiStoppedRunIds[runId]) return;
|
||||
try {
|
||||
var payload = JSON.parse(payloadText || 'null');
|
||||
assistantText += searchText((payload && (payload.text || payload.delta)) || '');
|
||||
} catch (_) {
|
||||
assistantText += searchText(payloadText);
|
||||
}
|
||||
assistantText = pageAiAppendStreamingAssistantDelta(runId, pageAiDecodeDeltaText(payloadText));
|
||||
}
|
||||
if (eventName === 'run.completed') {
|
||||
try {
|
||||
var completed = JSON.parse(payloadText || 'null');
|
||||
if (completed && completed.output) assistantText = searchText(completed.output);
|
||||
if (completed && completed.output) assistantText = String(completed.output || '');
|
||||
} catch (_) {}
|
||||
pageAiSetRunStatus('completed', runId);
|
||||
}
|
||||
if (eventName === 'run.failed') {
|
||||
try {
|
||||
var failed = JSON.parse(payloadText || 'null');
|
||||
assistantText = searchText((failed && (failed.message || failed.code)) || 'Hermes run failed');
|
||||
assistantText = String((failed && (failed.message || failed.code || failed.error)) || 'Hermes run failed');
|
||||
} catch (_) {}
|
||||
pageAiSetRunStatus('failed', runId);
|
||||
}
|
||||
@@ -5610,22 +5707,22 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
}
|
||||
if (eventName === 'tool.started' || eventName === 'tool.completed' || eventName === 'tool.failed') {
|
||||
pageAiApplyToolEvent(eventName, payloadText, runId, runTraceId);
|
||||
pageAiSyncCurrentSessionMessages();
|
||||
pageAiPersistSessions();
|
||||
renderPageAiConversation();
|
||||
pageAiSetRunStatus('tool_calling', runId);
|
||||
renderPageAiControls();
|
||||
}
|
||||
});
|
||||
if (!pageUiState.pageAiStoppedRunIds[runId]) {
|
||||
pageUiState.pageAiMessages.push({
|
||||
role: 'assistant',
|
||||
content: humanizePageAiResponse(assistantText, prompt)
|
||||
});
|
||||
pageAiFinishStreamingAssistantMessage(runId, assistantText, prompt);
|
||||
}
|
||||
currentSession = pageAiCurrentSession();
|
||||
if (currentSession) {
|
||||
currentSession.messages = pageUiState.pageAiMessages.slice();
|
||||
currentSession.updatedAt = Date.now();
|
||||
}
|
||||
pageAiPersistSessions();
|
||||
} catch (error) {
|
||||
pageAiSetRunStatus('failed');
|
||||
pageUiState.pageAiMessages.push({
|
||||
@@ -5637,6 +5734,7 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
currentSession.messages = pageUiState.pageAiMessages.slice();
|
||||
currentSession.updatedAt = Date.now();
|
||||
}
|
||||
pageAiPersistSessions();
|
||||
} finally {
|
||||
if (!allowQueue) pageUiState.pageAiBusy = false;
|
||||
renderPageAiConversation();
|
||||
@@ -6579,7 +6677,9 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
pageUiState.pageAiAcpRuntime = next;
|
||||
pageUiState.pageAiSkills = { categories: [], archived: [] };
|
||||
pageUiState.pageAiSkillError = '';
|
||||
pageAiPersistSessions();
|
||||
void pageAiLoadProfiles();
|
||||
if (next !== 'reasonix') void pageAiLoadProfileMemory();
|
||||
void pageAiLoadSkills();
|
||||
renderPageAiControls();
|
||||
renderPageAiProviderButtons();
|
||||
|
||||
@@ -3388,6 +3388,10 @@ body {
|
||||
background: #F4F8FF;
|
||||
}
|
||||
|
||||
.wolai-page-ai-message[data-page-ai-streaming="true"] .wolai-page-ai-message-role::after {
|
||||
content: " · 输出中";
|
||||
}
|
||||
|
||||
.wolai-page-ai-message-text {
|
||||
color: #1B1C1C;
|
||||
font-size: 13px;
|
||||
@@ -3395,6 +3399,52 @@ body {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.wolai-page-ai-tool-details {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.wolai-page-ai-tool-details summary {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
color: #1B1C1C;
|
||||
cursor: pointer;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.wolai-page-ai-tool-details summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wolai-page-ai-tool-details summary::before {
|
||||
content: "▸";
|
||||
color: #8B8782;
|
||||
}
|
||||
|
||||
.wolai-page-ai-tool-details[open] summary::before {
|
||||
content: "▾";
|
||||
}
|
||||
|
||||
.wolai-page-ai-tool-details summary strong {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.wolai-page-ai-tool-details summary span {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
color: #8B8782;
|
||||
font-size: 11px;
|
||||
line-height: 16px;
|
||||
text-align: right;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.wolai-page-ai-footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
Reference in New Issue
Block a user