feat(page-ai): align ACP session runtime

This commit is contained in:
lix-2026
2026-06-10 07:09:04 +08:00
parent f698aad964
commit 2cfeb97763
13 changed files with 3377 additions and 48 deletions
@@ -1359,6 +1359,7 @@ export function createSidebarPageAiRenderRuntime(context) {
'<div class="wolai-page-ai-message-actions wolai-page-ai-history-actions">' +
'<button type="button" class="wolai-page-ai-ghost" data-page-ai-session-resume="' + escapeHtml(session.id) + '">恢复</button>' +
'<button type="button" class="wolai-page-ai-ghost" data-page-ai-session-rename="' + escapeHtml(session.id) + '">重命名</button>' +
'<button type="button" class="wolai-page-ai-ghost" data-page-ai-session-export="' + escapeHtml(session.id) + '">导出</button>' +
'<button type="button" class="wolai-page-ai-ghost wolai-page-ai-ghost--danger" data-page-ai-session-delete="' + escapeHtml(session.id) + '">删除</button>' +
'</div>' +
'</div>';
@@ -494,6 +494,7 @@ export function createSidebarPageAiRuntime(context) {
const pageAiSetActiveSession = (...args) => pageAiSessionRuntime.pageAiSetActiveSession(...args);
const pageAiStartNewSession = (...args) => pageAiSessionRuntime.pageAiStartNewSession(...args);
const pageAiRenameBackendSession = (...args) => pageAiSessionRuntime.pageAiRenameBackendSession(...args);
const pageAiExportBackendSession = (...args) => pageAiSessionRuntime.pageAiExportBackendSession(...args);
const pageAiDeleteBackendSession = (...args) => pageAiSessionRuntime.pageAiDeleteBackendSession(...args);
const pageAiDeleteSelectedBackendSessions = (...args) => pageAiSessionRuntime.pageAiDeleteSelectedBackendSessions(...args);
const pageAiResumeBackendSession = (...args) => pageAiSessionRuntime.pageAiResumeBackendSession(...args);
@@ -1380,6 +1381,12 @@ export function createSidebarPageAiRuntime(context) {
pageAiSetActiveSession(sessionId);
}
if (run) await pageAiResumeActiveRunJournal(run);
if (!run && snapshot && snapshot.hostRunId) {
pageAiStopStreamingAssistantMessage(snapshot.hostRunId);
pageAiClearActiveRunSnapshot(snapshot.hostRunId);
pageUiState.pageAiBusy = false;
pageAiSetRunStatus('idle', snapshot.hostRunId);
}
return run;
}
@@ -2475,6 +2482,21 @@ export function createSidebarPageAiRuntime(context) {
void pageAiAppendKnowledgeRagFallbackCitations(id, promptText);
}
function pageAiStopStreamingAssistantMessage(runId) {
var id = String(runId || pageUiState.pageAiCurrentRunId || '');
var changed = false;
pageUiState.pageAiMessages.forEach(function(message) {
if (message && message.role === 'assistant' && message.streaming === true && (!id || message.runId === id)) {
message.streaming = false;
changed = true;
}
});
if (!changed) return;
pageAiSyncCurrentSessionMessages();
pageAiPersistSessions();
renderPageAiConversation();
}
function pageAiToolOutputText(value) {
var parts = [];
function visit(node) {
@@ -2612,6 +2634,10 @@ export function createSidebarPageAiRuntime(context) {
if (!event || typeof event !== 'object') return;
var eventName = String(event.kind || event.eventType || event.event || '').trim();
var payload = event.payload && typeof event.payload === 'object' ? event.payload : {};
if (payload.source === 'adapter_replay' || payload.replay === true) {
document.documentElement.setAttribute('data-mnote-page-ai-adapter-replay-seen', 'true');
return;
}
var payloadText = pageAiJournalPayloadText(event);
var prompt = pageAiLastUserPrompt();
if (eventName === 'message.delta') {
@@ -2624,6 +2650,7 @@ export function createSidebarPageAiRuntime(context) {
pageAiSetRunStatus('failed', runId);
} else if (eventName === 'run.aborted' || eventName === 'run.cancelled' || eventName === 'run.canceled') {
pageUiState.pageAiStoppedRunIds[runId] = true;
pageAiStopStreamingAssistantMessage(runId);
pageAiSetRunStatus('aborted', runId);
} else if (eventName === 'tool.started' || eventName === 'tool.completed' || eventName === 'tool.failed') {
var toolItem = pageAiApplyToolEvent(eventName, payloadText, runId, runTraceId);
@@ -2896,6 +2923,14 @@ export function createSidebarPageAiRuntime(context) {
var autoCitations = [];
await streamPageAiResponse(eventResponse, function(eventName, payloadText) {
pageAiTrackStreamingRunEvent(runId, eventName, payloadText);
var streamPayload = null;
try {
streamPayload = JSON.parse(payloadText || 'null');
} catch (_) {}
if (streamPayload && (streamPayload.source === 'adapter_replay' || streamPayload.replay === true)) {
document.documentElement.setAttribute('data-mnote-page-ai-adapter-replay-seen', 'true');
return;
}
if (eventName === 'message.delta') {
if (pageUiState.pageAiStoppedRunIds[runId]) return;
assistantText = pageAiAppendStreamingAssistantDelta(runId, pageAiDecodeDeltaText(payloadText));
@@ -2995,6 +3030,7 @@ export function createSidebarPageAiRuntime(context) {
}
if (eventName === 'run.aborted' || eventName === 'run.cancelled' || eventName === 'run.canceled') {
pageUiState.pageAiStoppedRunIds[runId] = true;
pageAiStopStreamingAssistantMessage(runId);
pageAiSetRunStatus('aborted', runId);
}
if (eventName === 'session.info.updated') {
@@ -3294,6 +3330,15 @@ export function createSidebarPageAiRuntime(context) {
});
return true;
}
var pageAiSessionExport = closestAction(event.target, '[data-page-ai-session-export]');
if (pageAiSessionExport) {
event.preventDefault();
void pageAiExportBackendSession(pageAiSessionExport.getAttribute('data-page-ai-session-export') || '').catch(function(error) {
pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
renderPageAiControls();
});
return true;
}
var pageAiSessionDelete = closestAction(event.target, '[data-page-ai-session-delete]');
if (pageAiSessionDelete) {
event.preventDefault();
@@ -3501,6 +3546,7 @@ export function createSidebarPageAiRuntime(context) {
pageAiToggleTool: (...args) => pageAiToggleTool(...args),
pageAiResumeBackendSession: (...args) => pageAiResumeBackendSession(...args),
pageAiRenameBackendSession: (...args) => pageAiRenameBackendSession(...args),
pageAiExportBackendSession: (...args) => pageAiExportBackendSession(...args),
pageAiDeleteBackendSession: (...args) => pageAiDeleteBackendSession(...args),
pageAiResolvePermission: (...args) => pageAiResolvePermission(...args),
pageAiOpenLocation: (...args) => pageAiOpenLocation(...args),
@@ -255,6 +255,7 @@ export function createSidebarPageAiSessionRuntime(context) {
if (!event || typeof event !== 'object') return null;
var eventType = String(event.eventType || event.event_type || event.event || '').trim();
var payload = event.payload && typeof event.payload === 'object' ? event.payload : event;
if (payload && (payload.source === 'adapter_replay' || payload.replay === true)) return null;
if (eventType === 'message.delta') {
var delta = String(payload.delta || payload.text || payload.output_text || '');
return delta ? { role: 'assistant', content: delta } : null;
@@ -410,6 +411,30 @@ export function createSidebarPageAiSessionRuntime(context) {
return pageUiState.pageAiSessionSearchResults;
}
async function pageAiExportBackendSession(sessionId) {
sessionId = String(sessionId || pageUiState.pageAiActiveSessionId || '').trim();
if (!sessionId) return null;
var response = await fetch('/api/hermes/client/sessions/' + encodeURIComponent(sessionId) + '/export?' + pageAiBackendSessionQuery({ limit: 200 }), {
headers: { 'accept': 'application/json' }
});
var payload = await response.json().catch(function(){ return null; });
if (!response.ok || !payload || payload.ok !== true) {
throw new Error(pageAiErrorMessage(payload, 'session_export_failed_' + response.status));
}
var exported = payload.export && typeof payload.export === 'object' ? payload.export : {};
var session = pageAiFindSessionById(sessionId) || pageAiCurrentSession();
if (session) {
session.exportedAt = Date.now();
session.exportMarkdown = String(exported.markdown || '');
session.updatedAt = Date.now();
}
doc.documentElement.setAttribute('data-mnote-page-ai-session-exported', sessionId);
pageAiPersistSessions();
renderPageAiConversation();
renderPageAiControls();
return payload;
}
function pageAiPersistSessions() {
doc.documentElement.setAttribute('data-mnote-page-ai-session-owner', 'hermes');
doc.documentElement.setAttribute('data-mnote-page-ai-session-key', pageAiStorageKey());
@@ -621,8 +646,15 @@ export function createSidebarPageAiSessionRuntime(context) {
if (!response.ok || !payload || payload.ok !== true) {
throw new Error(pageAiErrorMessage(payload, 'session_rename_failed_' + response.status));
}
session.title = String((payload.result && payload.result.title) || title);
session.updatedAt = Date.now();
var nextTitle = String((payload.result && payload.result.title) || title);
[pageUiState.pageAiSessions, pageUiState.pageAiSessionSearchResults].forEach(function(list) {
pageAiNormalizeArray(list).forEach(function(item) {
if (String(item && item.id || '').trim() === sessionId) {
item.title = nextTitle;
item.updatedAt = Date.now();
}
});
});
pageAiPersistSessions();
renderPageAiConversation();
renderPageAiControls();
@@ -784,6 +816,7 @@ export function createSidebarPageAiSessionRuntime(context) {
pageAiSetActiveSession,
pageAiStartNewSession,
pageAiRenameBackendSession,
pageAiExportBackendSession,
pageAiDeleteBackendSession,
pageAiDeleteSelectedBackendSessions,
pageAiResumeBackendSession,