feat: Page AI Reasonix desktop session alignment, settings IA cleanup, knowledge RAG hardening

- ACP client/session manager: Reasonix desktop live session context
- Hermes tools: knowledge_rag tool manifest and skill updates
- Browser runtime: sidebar page AI permission/profile/render/session/tree modules
- Routes: hermes_client, hermes_tools, knowledge_rag, web_shell
- Scripts: reasonix ACP wrapper, LightRAG MCP, smoke tasks 159/558/559/561/562
- Skills: mnote-knowledge-rag and mnote-lightrag-bridge SKILL.md updates
This commit is contained in:
lix-2026
2026-06-13 22:20:01 +08:00
parent 2236a053c0
commit 4a7efd4a30
30 changed files with 5321 additions and 291 deletions
@@ -356,11 +356,12 @@ function openEditorUploadFilePicker(detail, deps) {
async function fetchWithTimeout(input, init, timeoutMs, label) {
var controller = typeof AbortController === 'function' ? new AbortController() : null;
var timer = 0;
var timeout = Math.max(1000, Number(timeoutMs) || 15000);
try {
if (controller) {
timer = window.setTimeout(function() {
controller.abort();
}, Math.max(1000, Number(timeoutMs) || 15000));
}, timeout);
}
var nextInit = Object.assign({}, init || {});
if (controller) nextInit.signal = controller.signal;
@@ -375,6 +376,14 @@ async function fetchWithTimeout(input, init, timeoutMs, label) {
}
}
function uploadTimeoutMsForFile(file, requestedMs) {
var requested = Number(requestedMs) || 0;
var size = Number(file && file.size || 0);
if (!Number.isFinite(size) || size <= 0) return Math.max(requested, 15000);
var sizeBased = 15000 + Math.ceil(size / (1024 * 1024)) * 12000;
return Math.min(10 * 60 * 1000, Math.max(requested, 60000, sizeBased));
}
async function uploadLocalFolderAsset(file, plan, context) {
var rootUri = String(context && context.rootUri || '').trim() || currentRootUri();
var documentId = String(
@@ -401,7 +410,7 @@ async function uploadLocalFolderAsset(file, plan, context) {
method: 'POST',
credentials: 'include',
body: localForm
}, Number(context && context.timeoutMs) || 15000, '本地上传');
}, uploadTimeoutMsForFile(file, context && context.timeoutMs), '本地上传');
var localPayload = await localResponse.json().catch(function() { return null; });
if (!localResponse.ok || !localPayload || !localPayload.asset) {
throw new Error(localPayload && localPayload.error ? localPayload.error : '上传失败');
@@ -552,7 +561,7 @@ async function uploadFileToMediaAsset(file, plan, options, deps) {
var localResult = await uploadLocalFolderAsset(file, plan, {
rootUri: rootUri,
documentId: documentId,
timeoutMs: 15000
timeoutMs: uploadTimeoutMsForFile(file, 15000)
});
var localAsset = localResult && localResult.asset ? localResult.asset : null;
if (!localAsset) {
@@ -753,6 +762,7 @@ window.__mnoteLocalUploadRuntime = {
editorRootFromUploadOptions: editorRootFromUploadOptions,
openEditorUploadFilePicker: openEditorUploadFilePicker,
fetchWithTimeout: fetchWithTimeout,
uploadTimeoutMsForFile: uploadTimeoutMsForFile,
uploadLocalFolderAsset: uploadLocalFolderAsset,
uploadMediaAsset: uploadMediaAsset,
insertUploadedAssetIntoEditor: insertUploadedAssetIntoEditor,
@@ -7,26 +7,133 @@ export function createSidebarPageAiPermissionRuntime(context) {
} = context;
const doc = documentRef || document;
function pageAiPermissionStorageKey() {
var sessionId = String(pageUiState.pageAiActiveSessionId || 'no-session').trim() || 'no-session';
var path = doc && doc.location ? String(doc.location.pathname || '') : '';
return 'mnote.page_ai.permission_queue.v1:' + path + ':' + sessionId;
}
function pageAiPersistPermissionRequests() {
try {
var pending = pageUiState.pageAiPermissionRequests.filter(function(item) {
return item && item.kind === 'permission' && !item.resolved;
}).slice(-20);
if (window.localStorage) window.localStorage.setItem(pageAiPermissionStorageKey(), JSON.stringify(pending));
} catch (_) {}
}
function pageAiRestorePermissionRequests() {
try {
var raw = window.localStorage ? window.localStorage.getItem(pageAiPermissionStorageKey()) : '';
var items = raw ? JSON.parse(raw) : [];
if (!Array.isArray(items)) items = [];
pageUiState.pageAiPermissionRequests = items.filter(function(item) {
return item && item.kind === 'permission' && !item.resolved;
}).slice(-20);
pageUiState.pageAiPermissionRequests.forEach(function(item) {
var exists = pageUiState.pageAiMessages.some(function(message) {
return message.kind === 'permission' && message.permissionId === item.permissionId;
});
if (!exists) pageUiState.pageAiMessages.push(item);
});
var pending = pageUiState.pageAiPermissionRequests.find(function(item) { return !item.resolved; });
if (pending) {
if (!pageAiApplyPermissionMode(pending)) pageAiShowPermissionDialog(pending);
}
return pageUiState.pageAiPermissionRequests;
} catch (_) {
return [];
}
}
function pageAiPermissionMessage(payload, eventType) {
payload = payload && typeof payload === 'object' ? payload : {};
var permissionId = String(payload.permissionId || payload.permission_id || payload.id || ('perm_' + Date.now())).trim();
var toolName = String(payload.toolName || payload.tool || payload.name || payload.method || 'session/request_permission').trim();
var detail = pageAiPermissionDetail(payload, permissionId);
var toolName = detail.toolName || String(payload.toolName || payload.tool || payload.name || payload.method || 'session/request_permission').trim();
var args = payload.arguments || payload.args || payload.input || payload.params || payload;
var decision = String(payload.decision || payload.result || '').trim();
if (!decision && eventType === 'permission.denied') decision = 'denied';
if (!decision && eventType === 'permission.allowed') decision = 'allowed';
var options = pageAiPermissionOptions(payload);
return {
role: 'tool',
kind: 'permission',
permissionId: permissionId,
runId: String(payload.runId || payload.run_id || pageUiState.pageAiCurrentRunId || '').trim(),
toolName: toolName,
argsSummary: pageAiPreviewValue(args),
argsSummary: detail.summary || pageAiPreviewValue(args),
content: decision === 'denied' ? '已自动拒绝权限请求' : (decision === 'allowed' ? '已自动允许权限请求' : '等待权限确认'),
resolved: decision === 'denied' || decision === 'allowed',
decision: decision
decision: decision,
options: options,
permissionDetails: detail
};
}
function pageAiPermissionDetail(payload, permissionId) {
var params = payload && payload.params && typeof payload.params === 'object' ? payload.params : {};
var toolCall = params.toolCall && typeof params.toolCall === 'object' ? params.toolCall : {};
var rawInput = toolCall.rawInput && typeof toolCall.rawInput === 'object' ? toolCall.rawInput : {};
var title = String(toolCall.title || payload.toolName || payload.tool || payload.name || '').trim();
var kind = String(toolCall.kind || params.kind || '').trim();
var command = String(rawInput.command || rawInput.cmd || '').trim();
var path = String(rawInput.path || rawInput.file || rawInput.target || '').trim();
var toolName = title || String(payload.toolName || payload.tool || payload.name || 'session/request_permission').trim();
var primary = command || path || String(rawInput.pattern || rawInput.query || '').trim() || title || '权限请求';
var rows = [];
rows.push({ label: '操作', value: toolName });
if (kind) rows.push({ label: '类型', value: pageAiPermissionKindLabel(kind) });
if (command) rows.push({ label: '命令', value: command });
return {
toolName: toolName,
kind: kind,
kindLabel: pageAiPermissionKindLabel(kind),
command: command,
path: path,
summary: primary,
rows: rows
};
}
function pageAiPermissionKindLabel(kind) {
var normalized = String(kind || '').trim().toLowerCase();
if (normalized === 'execute') return '执行命令';
if (normalized === 'edit') return '写入文件';
if (normalized === 'read') return '读取';
if (normalized === 'other') return '其他';
return kind || '';
}
function pageAiPermissionOptions(payload) {
var rawOptions = payload && Array.isArray(payload.options)
? payload.options
: (payload && payload.params && Array.isArray(payload.params.options) ? payload.params.options : []);
return rawOptions.map(function(option) {
option = option && typeof option === 'object' ? option : {};
var optionId = String(option.optionId || option.option_id || option.id || '').trim();
if (!optionId) return null;
return {
optionId: optionId,
name: pageAiPermissionOptionLabel(optionId, option),
kind: String(option.kind || '').trim()
};
}).filter(Boolean).slice(0, 8);
}
function pageAiPermissionOptionLabel(optionId, option) {
var id = String(optionId || '').trim().toLowerCase();
var raw = String((option && (option.name || option.title)) || '').trim();
if (id === 'allow_once') return raw && !/^allow$/i.test(raw) ? raw.replace(/^Allow\b/i, '允许') : '允许一次';
if (id === 'allow_always') return '本会话允许';
if (id === 'allow_persistent') return '始终允许';
if (id === 'reject_once' || id === 'reject') return '拒绝';
if (id === 'cancel') return '取消';
if (id === 'refine' || id === 'revise') return '要求修改';
if (id === 'accept') return '接受';
return raw || optionId || '选择';
}
function pageAiApplyPermissionEvent(eventName, payloadText) {
var payload = null;
try {
@@ -46,6 +153,10 @@ export function createSidebarPageAiPermissionRuntime(context) {
pageUiState.pageAiPermissionRequests = pageUiState.pageAiPermissionRequests.filter(function(item) {
return item.permissionId !== message.permissionId;
}).concat([message]).slice(-20);
pageAiPersistPermissionRequests();
if (!message.resolved && pageAiApplyPermissionMode(message)) {
return;
}
if (!message.resolved) {
pageAiShowPermissionDialog(message);
} else {
@@ -53,15 +164,76 @@ export function createSidebarPageAiPermissionRuntime(context) {
}
}
function pageAiResolvePermission(permissionId, decision) {
function pageAiReasonixApprovalMode() {
var preferences = pageUiState.pageAiSkillPreferences && typeof pageUiState.pageAiSkillPreferences === 'object'
? pageUiState.pageAiSkillPreferences
: {};
var mode = String(preferences['ai.agent.reasonix.approval_mode'] || 'ask').trim() || 'ask';
return mode === 'allow' || mode === 'deny' ? mode : 'ask';
}
function pageAiApplyPermissionMode(message) {
if (!message || message.resolved) return false;
var mode = pageAiReasonixApprovalMode();
if (mode === 'ask') return false;
var optionId = pageAiPermissionPreferredOptionId(message, mode);
window.setTimeout(function() {
pageAiResolvePermission(message.permissionId, mode, optionId);
}, 0);
return true;
}
function pageAiPermissionPreferredOptionId(message, decision) {
var options = Array.isArray(message && message.options) ? message.options : [];
if (!options.length) return '';
var allowKeywords = ['allow_once', 'allow', 'approve', 'yes', 'allow_always', 'allow_persistent'];
var denyKeywords = ['deny_once', 'reject_once', 'deny', 'reject', 'no', 'cancel', 'stop'];
var keywords = decision === 'allow' ? allowKeywords : denyKeywords;
for (var i = 0; i < keywords.length; i += 1) {
var keyword = keywords[i];
var found = options.find(function(option) {
var text = [option && option.optionId, option && option.kind, option && option.name].map(function(value) {
return String(value || '').toLowerCase();
}).join(' ');
return text.indexOf(keyword) >= 0;
});
if (found && found.optionId) return String(found.optionId || '');
}
if (decision === 'allow') {
var allowFallback = options.find(function(option) { return !pageAiPermissionOptionRejectLike(option); });
return allowFallback && allowFallback.optionId ? String(allowFallback.optionId || '') : '';
}
return '';
}
function pageAiResolvePermission(permissionId, decision, optionId) {
permissionId = String(permissionId || '').trim();
decision = String(decision || '').trim() || 'deny';
optionId = String(optionId || '').trim();
if (!permissionId) return;
var runId = pageUiState.pageAiCurrentRunId;
var runId = String(pageUiState.pageAiCurrentRunId || '').trim();
if (!runId && pageUiState.pageAiActiveSessionId && Array.isArray(pageUiState.pageAiSessions)) {
var activeSession = pageUiState.pageAiSessions.find(function(session) {
return session && session.id === pageUiState.pageAiActiveSessionId;
});
runId = String(activeSession && (activeSession.runId || activeSession.hostRunId) || '').trim();
}
if (!runId && Array.isArray(pageUiState.pageAiPermissionRequests)) {
var pending = pageUiState.pageAiPermissionRequests.find(function(item) {
return item && item.permissionId === permissionId;
});
runId = String(pending && pending.runId || '').trim();
}
if (!runId && doc && doc.documentElement) {
runId = String(doc.documentElement.getAttribute('data-mnote-page-ai-run-id') || doc.documentElement.getAttribute('data-mnote-page-ai-active-host-run-id') || '').trim();
}
if (runId) {
var body = { permissionId: permissionId, decision: decision };
if (optionId) body.optionId = optionId;
fetch('/api/hermes/client/runs/' + encodeURIComponent(runId) + '/resolve-permission', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ permissionId: permissionId, decision: decision })
body: JSON.stringify(body)
}).then(function(response) {
if (!response.ok) console.warn('resolve-permission 后端返回异常', response.status);
}).catch(function(err) {
@@ -79,6 +251,7 @@ export function createSidebarPageAiPermissionRuntime(context) {
});
var dialog = doc.querySelector('[data-page-ai-permission-dialog]');
if (dialog instanceof HTMLElement) dialog.hidden = true;
pageAiPersistPermissionRequests();
renderPageAiConversation();
}
@@ -101,31 +274,70 @@ export function createSidebarPageAiPermissionRuntime(context) {
dialog.innerHTML = '' +
'<div class="wolai-page-ai-permission-panel" role="dialog" aria-modal="false">' +
'<div class="wolai-page-ai-memory-title" data-page-ai-permission-tool></div>' +
'<div class="wolai-page-ai-tool-meta" data-page-ai-permission-args></div>' +
'<div class="wolai-page-ai-message-actions">' +
'<button type="button" class="wolai-page-ai-ghost" data-page-ai-permission-action="allow" data-page-ai-permission-dialog-action>允许</button>' +
'<button type="button" class="wolai-page-ai-ghost" data-page-ai-permission-action="deny" data-page-ai-permission-dialog-action>拒绝</button>' +
'</div>' +
'<div class="wolai-page-ai-permission-summary" data-page-ai-permission-args></div>' +
'<div class="wolai-page-ai-message-actions" data-page-ai-permission-actions></div>' +
'</div>';
doc.body.appendChild(dialog);
}
var tool = dialog.querySelector('[data-page-ai-permission-tool]');
if (tool instanceof HTMLElement) tool.textContent = message.toolName || 'session/request_permission';
if (tool instanceof HTMLElement) tool.textContent = "权限 - " + (message.toolName || "session/request_permission");
var args = dialog.querySelector('[data-page-ai-permission-args]');
if (args instanceof HTMLElement) args.textContent = message.argsSummary || message.content || '';
dialog.querySelectorAll('[data-page-ai-permission-dialog-action]').forEach(function(button) {
if (button instanceof HTMLButtonElement) {
button.setAttribute('data-page-ai-permission-id', message.permissionId || '');
button.disabled = Boolean(message.resolved);
}
});
if (args instanceof HTMLElement) args.innerHTML = pageAiPermissionDetailsHtml(message);
var actions = dialog.querySelector('[data-page-ai-permission-actions]');
if (actions instanceof HTMLElement) actions.innerHTML = pageAiPermissionActionsHtml(message);
dialog.hidden = false;
}
function pageAiPermissionDetailsHtml(message) {
var details = message && message.permissionDetails && typeof message.permissionDetails === 'object'
? message.permissionDetails
: {};
var rows = Array.isArray(details.rows) ? details.rows : [];
if (!rows.length) {
return '<div class="wolai-page-ai-permission-row"><span>请求</span><strong>' + escapeHtml(message.argsSummary || message.content || '') + '</strong></div>';
}
return rows.slice(0, 6).map(function(row) {
return '<div class="wolai-page-ai-permission-row"><span>' + escapeHtml(row.label || '') + '</span><strong>' + escapeHtml(row.value || '') + '</strong></div>';
}).join('');
}
function pageAiPermissionActionsHtml(message) {
var options = Array.isArray(message.options) ? message.options : [];
if (!options.length) {
return '' +
'<button type="button" class="wolai-page-ai-ghost" data-page-ai-permission-action="allow" data-page-ai-permission-id="' + escapeAttr(message.permissionId || '') + '" data-page-ai-permission-dialog-action>允许</button>' +
'<button type="button" class="wolai-page-ai-ghost" data-page-ai-permission-action="deny" data-page-ai-permission-id="' + escapeAttr(message.permissionId || '') + '" data-page-ai-permission-dialog-action>拒绝</button>';
}
return options.map(function(option) {
var denyLike = pageAiPermissionOptionRejectLike(option);
return '<button type="button" class="wolai-page-ai-ghost' + (denyLike ? ' wolai-page-ai-ghost--danger' : '') + '" data-page-ai-permission-action="' + (denyLike ? 'deny' : 'allow') + '" data-page-ai-permission-option-id="' + escapeAttr(option.optionId || '') + '" data-page-ai-permission-id="' + escapeAttr(message.permissionId || '') + '" data-page-ai-permission-dialog-action>' + escapeHtml(option.name || option.optionId || '选择') + '</button>';
}).join('');
}
function pageAiPermissionOptionRejectLike(option) {
var text = String((option && option.optionId) || '') + ' ' + String((option && option.kind) || '') + ' ' + String((option && option.name) || '');
text = text.toLowerCase();
return /reject|deny|cancel|stop|no/.test(text);
}
function escapeAttr(value) {
return escapeHtml(String(value || ''));
}
function escapeHtml(value) {
return String(value == null ? '' : value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
return {
pageAiPermissionMessage,
pageAiApplyPermissionEvent,
pageAiResolvePermission,
pageAiRestorePermissionRequests,
pageAiHidePermissionDialog,
pageAiShowPermissionDialog
};
@@ -163,6 +163,50 @@ export function createSidebarPageAiProfileRuntime(context) {
});
}
function pageAiSessionStatusFilterValue(session) {
var status = String(session && session.status || '').trim();
var mode = String(session && (session.runtimeMode || session.mode) || '').trim();
if (pageUiState.pageAiActiveSessionId && session && session.id === pageUiState.pageAiActiveSessionId) return 'active';
if (session && session.replaySeen) return 'replay_seen';
if (mode === 'native_live' || mode === 'cold_resumed' || mode === 'replay_seen') return mode;
if (['running', 'tool_calling', 'queued', 'pending', 'acp_pending'].indexOf(status) >= 0) return 'active';
if (['failed', 'aborted', 'cancelled', 'canceled'].indexOf(status) >= 0) return 'failed';
if (status === 'completed') return 'completed';
return status || 'unknown';
}
function pageAiSessionStatusLabel(value) {
return {
all: '全部状态',
active: 'Active',
completed: 'Completed',
failed: 'Failed',
native_live: 'Reasonix native-live',
cold_resumed: 'Cold resumed',
replay_seen: 'Replay seen',
unknown: 'Unknown'
}[value] || value;
}
function pageAiSessionStatusFilterOptions(rows) {
var byValue = {
all: pageAiSessionStatusLabel('all'),
active: pageAiSessionStatusLabel('active'),
completed: pageAiSessionStatusLabel('completed'),
failed: pageAiSessionStatusLabel('failed'),
native_live: pageAiSessionStatusLabel('native_live'),
cold_resumed: pageAiSessionStatusLabel('cold_resumed'),
replay_seen: pageAiSessionStatusLabel('replay_seen')
};
pageAiNormalizeArray(rows).filter(pageAiVisibleHistorySession).forEach(function(session) {
var value = pageAiSessionStatusFilterValue(session);
byValue[value] = pageAiSessionStatusLabel(value);
});
return Object.keys(byValue).map(function(value) {
return { value: value, label: byValue[value] };
});
}
function pageAiVisibleHistorySession(session) {
var source = String(session && session.source || '').trim();
var status = String(session && session.status || '').trim();
@@ -172,10 +216,11 @@ export function createSidebarPageAiProfileRuntime(context) {
function pageAiFilteredHistoryRows(rows) {
var filterValue = String(pageUiState.pageAiSessionAgentFilter || 'all').trim() || 'all';
var statusFilter = String(pageUiState.pageAiSessionStatusFilter || 'all').trim() || 'all';
var normalized = pageAiNormalizeArray(rows).filter(pageAiVisibleHistorySession);
if (filterValue === 'all') return normalized;
return normalized.filter(function(session) {
return pageAiSessionAgentFilterValue(session) === filterValue;
return (filterValue === 'all' || pageAiSessionAgentFilterValue(session) === filterValue)
&& (statusFilter === 'all' || pageAiSessionStatusFilterValue(session) === statusFilter);
});
}
@@ -220,6 +265,9 @@ export function createSidebarPageAiProfileRuntime(context) {
pageAiSessionAgentLabel,
pageAiSessionPreviewText,
pageAiSessionAgentFilterOptions,
pageAiSessionStatusFilterValue,
pageAiSessionStatusLabel,
pageAiSessionStatusFilterOptions,
pageAiFilteredHistoryRows,
pageAiTimestamp,
pageAiUsageSummary
@@ -39,6 +39,9 @@ export function createSidebarPageAiRenderRuntime(context) {
pageAiSessionAgentFilterOptions,
pageAiSessionAgentLabel,
pageAiSessionAgentFilterValue,
pageAiSessionStatusFilterOptions,
pageAiSessionStatusFilterValue,
pageAiSessionStatusLabel,
pageAiSessionPreviewText,
pageAiSessionStorageLabel,
pageAiSessionSelected,
@@ -192,6 +195,50 @@ export function createSidebarPageAiRenderRuntime(context) {
'</label>';
}
function pageAiReasonixQuickControlLabel(key, fallback) {
if (key === 'ai.agent.reasonix.model_id') return '模型';
if (key === 'ai.agent.reasonix.approval_mode') return '审批';
if (key === 'ai.agent.reasonix.plan_mode') return 'Plan';
return fallback || key;
}
function pageAiRenderReasonixQuickControl(field) {
var key = String(field && field.key || '').trim();
if (!key || field.secret === true) return '';
var value = pageAiDescriptorFieldValue(field);
var options = pageAiDescriptorFieldOptions(field);
if (!options.length) return '';
return '' +
'<label class="wolai-page-ai-reasonix-control" data-page-ai-reasonix-control-row="' + escapeHtml(key) + '">' +
'<span>' + escapeHtml(pageAiReasonixQuickControlLabel(key, field.label)) + '</span>' +
'<select data-page-ai-descriptor-field="' + escapeHtml(key) + '" data-page-ai-descriptor-kind="select" data-page-ai-reasonix-quick-control="' + escapeHtml(key) + '">' +
options.map(function(option) {
return '<option value="' + escapeHtml(option.value) + '"' + (String(value) === String(option.value) ? ' selected' : '') + '>' + escapeHtml(option.label) + '</option>';
}).join('') +
'</select>' +
'</label>';
}
function pageAiReasonixQuickControlsHtml() {
if (pageAiCurrentAgentId() !== 'reasonix') return '';
var descriptor = pageAiAgentDescriptor('reasonix') || pageAiAgentRecord('reasonix');
var wanted = {
'ai.agent.reasonix.model_id': true,
'ai.agent.reasonix.approval_mode': true,
'ai.agent.reasonix.plan_mode': true
};
var fields = pageAiNormalizeArray(descriptor && descriptor.fields)
.filter(function(field) { return wanted[String(field && field.key || '').trim()] === true; })
.map(pageAiRenderReasonixQuickControl)
.filter(Boolean)
.join('');
if (!fields) return '';
return '' +
'<div class="wolai-page-ai-reasonix-controls" data-page-ai-reasonix-quick-controls>' +
fields +
'</div>';
}
function pageAiRenderAgentDescriptorCard(agentId, fallbackTitle) {
var descriptor = pageAiAgentDescriptor(agentId) || pageAiAgentRecord(agentId);
var title = String(descriptor && (descriptor.displayName || descriptor.label) || fallbackTitle || agentId).trim();
@@ -210,11 +257,27 @@ export function createSidebarPageAiRenderRuntime(context) {
function pageAiCurrentModelLabel() {
var profile = pageAiCurrentProfileRecord();
var prefs = pageUiState.pageAiSkillPreferences || {};
var toolModel = pageAiMnoteToolModel();
var reasonixModel = String(prefs['ai.agent.reasonix.model_id'] || '').trim();
var approvalMode = String(prefs['ai.agent.reasonix.approval_mode'] || 'ask').trim() || 'ask';
var planMode = String(prefs['ai.agent.reasonix.plan_mode'] || 'manual').trim() || 'manual';
var approvalLabel = {
ask: '审批:询问',
allow: '审批:自动允许',
deny: '审批:自动拒绝'
}[approvalMode] || ('审批:' + approvalMode);
var planLabel = {
manual: '计划:手动',
auto: '计划:自动'
}[planMode] || ('计划:' + planMode);
if (!profile) return 'tool: ' + toolModel;
var model = String(profile.model || '').trim();
var gateway = String(profile.gateway || '').trim();
var profileLabel = [model, gateway].filter(Boolean).join(' · ') || '由 Hermes 决定';
if (pageAiCurrentAgentId() === 'reasonix') {
return ['Reasonix', reasonixModel || toolModel, approvalLabel, planLabel].filter(Boolean).join(' · ');
}
return 'tool: ' + toolModel + ' · profile: ' + profileLabel;
}
@@ -283,6 +346,88 @@ export function createSidebarPageAiRenderRuntime(context) {
});
}
function pageAiRuntimeStatusValue(path, fallback) {
var node = pageUiState.pageAiRuntimeStatus || {};
String(path || '').split('.').forEach(function(part) {
if (!part || node == null || typeof node !== 'object') return;
node = node[part];
});
if (node == null || node === '') return fallback || '';
return String(node);
}
function pageAiRuntimeStatusLabel() {
var status = pageUiState.pageAiRuntimeStatus || {};
var runtime = String(status.runtime || pageUiState.pageAiAcpRuntime || 'reasonix');
var mode = String(status.mode || 'new_session');
if (runtime === 'reasonix') return pageAiCurrentModelLabel() + ' · ' + mode.replace(/_/g, '-');
return runtime + ' · ' + mode.replace(/_/g, '-');
}
function pageAiRuntimeDetailHtml() {
var status = pageUiState.pageAiRuntimeStatus || {};
var acp = status.acp && typeof status.acp === 'object' ? status.acp : {};
var roots = status.roots && typeof status.roots === 'object' ? status.roots : {};
var tools = status.tools && typeof status.tools === 'object' ? status.tools : {};
var mcp = status.mcp && typeof status.mcp === 'object' ? status.mcp : {};
var queue = status.session && status.session.queue && typeof status.session.queue === 'object' ? status.session.queue : {};
var logs = status.logs && Array.isArray(status.logs.items) ? status.logs.items : [];
var rows = [
['runtime', pageAiRuntimeStatusLabel()],
['session', String(status.mnoteSessionId || pageUiState.pageAiActiveSessionId || '无')],
['acpSessionId', String(acp.acpSessionId || '无')],
['load/replay', String(acp.loadStatus || 'not_applicable') + ' / replay=' + String(acp.replaySeen === true)],
['live binding', acp.liveBinding && typeof acp.liveBinding === 'object' ? String(acp.liveBinding.status || 'unknown') : '无'],
['roots', 'write=' + String(roots.write || 0) + ' readOnly=' + String(roots.readOnly || 0)],
['MCP/tools', String(mcp.status || 'unknown') + ' · enabled=' + String(tools.enabled || 0) + ' disabled=' + String(tools.disabled || 0)],
['queue', String(queue.queuedCount || pageUiState.pageAiQueueLength || 0)],
['jobs', pageAiRuntimeStatusValue('jobs.running', '0')]
];
var html = rows.map(function(row) {
return '<div class="wolai-page-ai-tool-row"><span class="wolai-page-ai-tool-name">' + escapeHtml(row[0]) + '</span><span class="wolai-page-ai-tool-meta">' + escapeHtml(row[1]) + '</span></div>';
}).join('');
if (pageUiState.pageAiRuntimeStatusError) {
html += '<div class="wolai-page-ai-inline-error">' + escapeHtml(pageUiState.pageAiRuntimeStatusError) + '</div>';
}
if (logs.length) {
html += logs.slice(-4).map(function(item) {
return '<div class="wolai-page-ai-tool-row"><span class="wolai-page-ai-tool-name">' + escapeHtml(String(item.kind || 'log')) + '</span><span class="wolai-page-ai-tool-meta">' + escapeHtml(String(item.seq || '')) + '</span></div>';
}).join('');
}
return html || '<div class="wolai-page-ai-empty">Runtime status 尚未加载。</div>';
}
function pageAiUsageDetailHtml() {
var status = pageUiState.pageAiRuntimeStatus || {};
var usage = status.usage && typeof status.usage === 'object' ? status.usage : {};
var rows = [
['last run', usage.lastRunTokens || usage.lastRunCost || 'unknown'],
['session total', usage.sessionTokens || usage.sessionCost || 'unknown'],
['cache', usage.cacheTokens || usage.cacheHit || 'unknown']
];
return rows.map(function(row) {
return '<div class="wolai-page-ai-tool-row"><span class="wolai-page-ai-tool-name">' + escapeHtml(row[0]) + '</span><span class="wolai-page-ai-tool-meta">' + escapeHtml(String(row[1])) + '</span></div>';
}).join('');
}
function pageAiSettingsTabsHtml(activeTab) {
return [
['agent', 'MNote'],
['reasonix-settings', 'Reasonix'],
['hermes-settings', 'Hermes']
].map(function(tab) {
var active = tab[0] === activeTab;
return '<button type="button" class="wolai-page-ai-tab' + (active ? ' is-active' : '') + '" data-page-ai-tab="' + escapeHtml(tab[0]) + '" role="tab" aria-selected="' + (active ? 'true' : 'false') + '">' + escapeHtml(tab[1]) + '</button>';
}).join('');
}
function pageAiStatusTabsHtml() {
return '' +
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="chat" role="tab" aria-selected="false">聊天</button>' +
'<button type="button" class="wolai-page-ai-tab is-active" data-page-ai-tab="status" role="tab" aria-selected="true">状态</button>' +
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="agent" role="tab" aria-selected="false">设置</button>';
}
function renderPageAiProviderButtons() {
var drawer = ensurePageAiDrawer();
var isAcp = pageUiState.pageAiAcpRuntime !== '';
@@ -303,6 +448,8 @@ export function createSidebarPageAiRenderRuntime(context) {
var isAcp = pageUiState.pageAiAcpRuntime !== '';
var activeAgentId = pageAiCurrentAgentId();
var activeProfile = pageAiCurrentProfile();
if (pageUiState.pageAiPage === 'runtime') pageUiState.pageAiPage = 'status';
if (pageUiState.pageAiPage === 'chat-only-settings') pageUiState.pageAiPage = 'agent';
drawer.setAttribute('data-page-ai-page', pageUiState.pageAiPage || 'chat');
drawer.setAttribute('data-page-ai-active-agent-id', activeAgentId);
drawer.setAttribute('data-mnote-acp-runtime', pageUiState.pageAiAcpRuntime || 'reasonix');
@@ -478,16 +625,32 @@ export function createSidebarPageAiRenderRuntime(context) {
if (agentPanel instanceof HTMLElement) {
agentPanel.innerHTML = '' +
(pageUiState.pageAiAgentDescriptorError ? '<div class="wolai-page-ai-inline-error">' + escapeHtml(pageUiState.pageAiAgentDescriptorError) + '</div>' : '') +
'<section class="wolai-page-ai-memory-card" data-page-ai-agent-descriptor-card="mnote">' +
'<div class="wolai-page-ai-memory-head"><div><div class="wolai-page-ai-memory-title">MNote</div><div class="wolai-page-ai-memory-scope">上下文、权限、工具、会话历史和审计</div></div></div>' +
'<div class="wolai-page-ai-tool-meta">这里配置 MNote 发送给 agent 的 envelope,不配置 Reasonix 或 Hermes 原生 runtime。</div>' +
'</section>' +
'<section class="wolai-page-ai-memory-card">' +
'<div class="wolai-page-ai-memory-head"><div class="wolai-page-ai-memory-title">授权区域</div><div class="wolai-page-ai-memory-scope">' + escapeHtml(pageAiBuildAllowedRoots().length ? 'SQLite directory_grants' : '需要授权') + '</div></div>' +
'<div class="wolai-page-ai-memory-head"><div class="wolai-page-ai-memory-title">授权区域</div><div class="wolai-page-ai-memory-scope">SQLite directory_grants</div></div>' +
'<div class="wolai-page-ai-tool-list" data-page-ai-allowed-roots></div>' +
'</section>' +
pageAiRenderAgentDescriptorCard(activeAgentId, pageAiCurrentAgentSelectionLabel()) +
'<section class="wolai-page-ai-memory-card">' +
'<div class="wolai-page-ai-memory-head"><div class="wolai-page-ai-memory-title">默认上下文</div><div class="wolai-page-ai-memory-scope">ContextRefs</div></div>' +
'<div class="wolai-page-ai-tool-meta">由 MNote 生成 run envelope,不写入 Reasonix/Hermes native memory。</div>' +
'</section>' +
'<section class="wolai-page-ai-memory-card">' +
'<div class="wolai-page-ai-memory-head"><div class="wolai-page-ai-memory-title">审计</div><div class="wolai-page-ai-memory-scope">changed files 默认摘要</div></div>' +
'<div class="wolai-page-ai-memory-head"><div class="wolai-page-ai-memory-title">会话与审计</div><div class="wolai-page-ai-memory-scope">history / export / changed files</div></div>' +
'<div class="wolai-page-ai-tool-meta">删除会话只删除 MNote history;外部 Hermes/provider 会话不会默认删除。</div>' +
'</section>';
var mnoteAllowedRootsNode = agentPanel.querySelector('[data-page-ai-allowed-roots]');
if (mnoteAllowedRootsNode instanceof HTMLElement) {
var mnoteAllowedRoots = pageAiBuildAllowedRoots();
mnoteAllowedRootsNode.innerHTML = mnoteAllowedRoots.length
? mnoteAllowedRoots.map(function(root) {
var label = root.rootUri.replace(/^file:\/\//, '') || root.rootUri;
return '<span class="wolai-page-ai-model-chip is-active" data-page-ai-allowed-root="' + escapeHtml(root.rootUri) + '" data-page-ai-allowed-root-permission="' + escapeHtml(root.permission) + '">' + escapeHtml(root.permission + ' · ' + label) + '</span>';
}).join('')
: '<span class="wolai-page-ai-tool-meta">' + escapeHtml(String(pageUiState.pageAiAllowedRootsError || '').trim() || '未选择授权区域') + '</span>';
}
}
var profileSummary = drawer.querySelector('[data-page-ai-profile-summary]');
if (profileSummary instanceof HTMLElement) profileSummary.textContent = '上下文可选';
@@ -512,6 +675,40 @@ export function createSidebarPageAiRenderRuntime(context) {
var queueSuffix = pageUiState.pageAiQueueLength > 0 ? ' · 队列 ' + pageUiState.pageAiQueueLength : '';
runStatus.textContent = pageAiRunStatusLabel(pageUiState.pageAiRunStatus) + queueSuffix;
}
drawer.querySelectorAll('[data-page-ai-runtime-badge]').forEach(function(runtimeBadge) {
if (runtimeBadge instanceof HTMLElement) runtimeBadge.textContent = pageAiRuntimeStatusLabel();
});
var runtimeActive = drawer.querySelector('[data-page-ai-runtime-active-run]');
if (runtimeActive instanceof HTMLElement) {
var activeRunStatus = pageAiRuntimeStatusValue('activeRun.status', pageUiState.pageAiRunStatus || 'idle');
runtimeActive.textContent = pageAiRunStatusLabel(activeRunStatus);
}
var runtimeAcp = drawer.querySelector('[data-page-ai-runtime-acp]');
if (runtimeAcp instanceof HTMLElement) runtimeAcp.textContent = pageAiRuntimeStatusValue('acp.acpSessionId', '无 ACP session');
var runtimeDetail = drawer.querySelector('[data-page-ai-runtime-detail]');
if (runtimeDetail instanceof HTMLElement) runtimeDetail.innerHTML = pageAiRuntimeDetailHtml();
var jobsList = drawer.querySelector('[data-page-ai-jobs-list]');
if (jobsList instanceof HTMLElement) {
var runtimeStatus = pageUiState.pageAiRuntimeStatus || {};
var jobs = runtimeStatus.jobs && Array.isArray(runtimeStatus.jobs.items) ? runtimeStatus.jobs.items : [];
jobsList.innerHTML = jobs.length
? jobs.map(function(job) {
return '<div class="wolai-page-ai-tool-row"><span class="wolai-page-ai-tool-name">' + escapeHtml(String(job.id || job.name || 'job')) + '</span><span class="wolai-page-ai-tool-meta">' + escapeHtml(String(job.status || 'running')) + '</span></div>';
}).join('')
: '<div class="wolai-page-ai-empty">暂无运行中的 job。</div>';
}
var logsList = drawer.querySelector('[data-page-ai-logs-list]');
if (logsList instanceof HTMLElement) {
var logStatus = pageUiState.pageAiRuntimeStatus || {};
var logs = logStatus.logs && Array.isArray(logStatus.logs.items) ? logStatus.logs.items : [];
logsList.innerHTML = logs.length
? logs.slice(-8).map(function(item) {
return '<div class="wolai-page-ai-tool-row"><span class="wolai-page-ai-tool-name">' + escapeHtml(String(item.kind || 'log')) + '</span><span class="wolai-page-ai-tool-meta">' + escapeHtml(String(item.seq || '')) + '</span></div>';
}).join('')
: '<div class="wolai-page-ai-empty">暂无 runtime logs。</div>';
}
var usageDetail = drawer.querySelector('[data-page-ai-usage-detail]');
if (usageDetail instanceof HTMLElement) usageDetail.innerHTML = pageAiUsageDetailHtml();
var stopButton = drawer.querySelector('[data-page-ai-action="stop-run"]');
if (stopButton instanceof HTMLButtonElement) {
var canStop = ['queued', 'running', 'tool_calling'].indexOf(pageUiState.pageAiRunStatus) >= 0 && pageUiState.pageAiCurrentRunId;
@@ -530,9 +727,10 @@ export function createSidebarPageAiRenderRuntime(context) {
} else {
queueList.innerHTML = pageUiState.pageAiQueuedItems.map(function(item, index) {
var label = '队列 ' + (index + 1);
var preview = String(item.preview || item.traceId || item.queueId || '').trim();
return '' +
'<div class="wolai-page-ai-tool-row" data-page-ai-queue-item="' + escapeHtml(item.queueId) + '">' +
'<div><strong>' + escapeHtml(label) + '</strong><br /><span>' + escapeHtml(item.queueId) + '</span></div>' +
'<div><strong>' + escapeHtml(label) + '</strong><br /><span>' + escapeHtml(preview || item.queueId) + '</span></div>' +
'<button type="button" class="wolai-page-ai-ghost" data-page-ai-action="cancel-queued-run" data-page-ai-queue-id="' + escapeHtml(item.queueId) + '">取消</button>' +
'</div>';
}).join('');
@@ -547,7 +745,17 @@ export function createSidebarPageAiRenderRuntime(context) {
: '等待 AI session';
}
var modelNode = drawer.querySelector('[data-page-ai-model-status]');
if (modelNode instanceof HTMLElement) modelNode.textContent = pageAiBuildAllowedRoots().length ? '已授权' : '需授权';
if (modelNode instanceof HTMLElement) modelNode.textContent = pageAiCurrentModelLabel();
var reasonixControls = drawer.querySelector('[data-page-ai-reasonix-quick-controls]');
if (reasonixControls instanceof HTMLElement) {
if (pageAiCurrentAgentId() === 'reasonix') {
reasonixControls.innerHTML = pageAiReasonixQuickControlsHtml().replace(/^<div[^>]*>|<\/div>$/g, '');
reasonixControls.hidden = false;
} else {
reasonixControls.innerHTML = '';
reasonixControls.hidden = true;
}
}
var scopeSelect = drawer.querySelector('[data-page-ai-context-scope]');
if (scopeSelect instanceof HTMLSelectElement) scopeSelect.value = pageUiState.pageAiContextScope;
var scopeLabel = drawer.querySelector('[data-page-ai-context-scope-label]');
@@ -630,6 +838,20 @@ export function createSidebarPageAiRenderRuntime(context) {
sessionAgentFilter.value = activeFilter;
pageUiState.pageAiSessionAgentFilter = activeFilter;
}
var sessionStatusFilter = drawer.querySelector('[data-page-ai-session-status-filter]');
if (sessionStatusFilter instanceof HTMLSelectElement) {
var historyRowsForStatusFilter = pageUiState.pageAiSessionSearchResults.length
? pageUiState.pageAiSessionSearchResults
: pageUiState.pageAiSessions;
var statusOptions = pageAiSessionStatusFilterOptions(historyRowsForStatusFilter);
var activeStatusFilter = String(pageUiState.pageAiSessionStatusFilter || 'all').trim() || 'all';
if (!statusOptions.some(function(option) { return option.value === activeStatusFilter; })) activeStatusFilter = 'all';
sessionStatusFilter.innerHTML = statusOptions.map(function(option) {
return '<option value="' + escapeHtml(option.value) + '"' + (option.value === activeStatusFilter ? ' selected' : '') + '>' + escapeHtml(option.label) + '</option>';
}).join('');
sessionStatusFilter.value = activeStatusFilter;
pageUiState.pageAiSessionStatusFilter = activeStatusFilter;
}
var skillSearch = drawer.querySelector('[data-page-ai-skill-search]');
if (skillSearch instanceof HTMLInputElement && documentRef.activeElement !== skillSearch) {
skillSearch.value = pageUiState.pageAiSkillQuery;
@@ -754,7 +976,7 @@ export function createSidebarPageAiRenderRuntime(context) {
}
if (gatewayDetail instanceof HTMLElement) {
if (!gatewayHealth) {
gatewayDetail.innerHTML = '<div class="wolai-page-ai-empty">打开高级后会检查 agent runtime 与当前 profile。</div>';
gatewayDetail.innerHTML = '<div class="wolai-page-ai-empty">打开 Hermes 设置后会检查 gateway 与当前 profile。</div>';
} else {
var gatewayInfo = gatewayHealth.gateway || {};
var profileInfo = gatewayHealth.profile || {};
@@ -798,8 +1020,8 @@ export function createSidebarPageAiRenderRuntime(context) {
}
var chatOnlyPanel = drawer.querySelector('[data-page-ai-chat-only-panel]');
if (chatOnlyPanel instanceof HTMLElement) {
chatOnlyPanel.innerHTML = pageAiRenderAgentDescriptorCard('chat_only', 'Chat-only') +
'<section class="wolai-page-ai-memory-card"><div class="wolai-page-ai-memory-head"><div class="wolai-page-ai-memory-title">Chat-only</div><div class="wolai-page-ai-memory-scope">默认不申请文件写权限</div></div></section>';
chatOnlyPanel.innerHTML = pageAiRenderAgentDescriptorCard('chat_only', 'API Chat / Chat-only profile') +
'<section class="wolai-page-ai-memory-card"><div class="wolai-page-ai-memory-head"><div class="wolai-page-ai-memory-title">API Chat / Chat-only profile</div><div class="wolai-page-ai-memory-scope">默认不申请文件写权限,归属 MNote 设置。</div></div></section>';
}
}
@@ -859,6 +1081,11 @@ export function createSidebarPageAiRenderRuntime(context) {
'<button type="button" class="wolai-page-ai-icon" data-page-ai-action="close" aria-label="关闭页面 AI">×</button>' +
'</div>' +
'</div>' +
'<button type="button" class="wolai-page-ai-runtime-strip" data-page-ai-runtime-strip data-page-ai-tab="status" aria-label="打开 Page AI 状态">' +
'<span data-page-ai-runtime-badge>reasonix · new-session</span>' +
'<span data-page-ai-runtime-active-run>空闲</span>' +
'<span data-page-ai-runtime-acp>无 ACP session</span>' +
'</button>' +
'<div class="wolai-page-ai-body">' +
'<section class="wolai-page-ai-panel-section" data-page-ai-panel="chat">' +
'<div class="wolai-page-ai-chat-meta">' +
@@ -885,26 +1112,36 @@ export function createSidebarPageAiRenderRuntime(context) {
'<div class="wolai-page-ai-settings-head">' +
'<button type="button" class="wolai-page-ai-back" data-page-ai-tab="chat">← 聊天</button>' +
'<div class="wolai-page-ai-settings-tabs" role="tablist" aria-label="页面 AI 设置">' +
'<button type="button" class="wolai-page-ai-tab is-active" data-page-ai-tab="agent" role="tab" aria-selected="true">Common</button>' +
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="hermes-settings" role="tab" aria-selected="false">Hermes</button>' +
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="reasonix-settings" role="tab" aria-selected="false">Reasonix</button>' +
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="chat-only-settings" role="tab" aria-selected="false">Chat-only</button>' +
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="runtime" role="tab" aria-selected="false">高级</button>' +
pageAiSettingsTabsHtml('agent') +
'</div>' +
'</div>' +
'<div class="wolai-page-ai-inline-error" data-page-ai-profile-error hidden></div>' +
'<div class="wolai-page-ai-inline-error" data-page-ai-memory-error hidden></div>' +
'<div class="wolai-page-ai-memory-grid" data-page-ai-agent-panel></div>' +
'<label class="wolai-page-ai-context-select">' +
'<span>默认上下文 scope</span>' +
'<select data-page-ai-context-scope>' +
'<option value="page">当前页</option>' +
'<option value="selection">当前选区</option>' +
'<option value="block">当前块</option>' +
'<option value="options">页面设置</option>' +
'</select>' +
'</label>' +
'<div class="wolai-page-ai-settings-stack" data-page-ai-agent-panel></div>' +
'<div class="wolai-page-ai-settings-stack" data-page-ai-chat-only-panel></div>' +
'<div class="wolai-page-ai-tools-panel">' +
'<div class="wolai-page-ai-tools-head">' +
'<span>MNote 工具</span>' +
'<span data-page-ai-last-tool>暂无 tool call</span>' +
'</div>' +
'<div class="wolai-page-ai-inline-error" data-page-ai-tool-error hidden></div>' +
'<div class="wolai-page-ai-tool-list" data-page-ai-tool-list></div>' +
'</div>' +
'</section>' +
'<section class="wolai-page-ai-panel-section" data-page-ai-panel="hermes-settings" hidden>' +
'<div class="wolai-page-ai-settings-head">' +
'<button type="button" class="wolai-page-ai-back" data-page-ai-tab="chat">← 聊天</button>' +
'<div class="wolai-page-ai-settings-tabs" role="tablist" aria-label="页面 AI 设置">' +
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="agent" role="tab" aria-selected="false">Common</button>' +
'<button type="button" class="wolai-page-ai-tab is-active" data-page-ai-tab="hermes-settings" role="tab" aria-selected="true">Hermes</button>' +
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="reasonix-settings" role="tab" aria-selected="false">Reasonix</button>' +
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="chat-only-settings" role="tab" aria-selected="false">Chat-only</button>' +
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="runtime" role="tab" aria-selected="false">高级</button>' +
pageAiSettingsTabsHtml('hermes-settings') +
'</div>' +
'</div>' +
'<div class="wolai-page-ai-settings-grid">' +
@@ -914,17 +1151,21 @@ export function createSidebarPageAiRenderRuntime(context) {
'</label>' +
'</div>' +
'<button type="button" class="wolai-page-ai-settings-link" data-page-ai-action="open-hermes-settings">打开 Hermes 设置</button>' +
'<div class="wolai-page-ai-inline-error" data-page-ai-gateway-error hidden></div>' +
'<div class="wolai-page-ai-tools-panel">' +
'<div class="wolai-page-ai-tools-head">' +
'<span>Hermes gateway</span>' +
'<span data-page-ai-gateway-status>未检查</span>' +
'</div>' +
'<div class="wolai-page-ai-tool-list" data-page-ai-gateway-detail></div>' +
'</div>' +
'<div class="wolai-page-ai-memory-grid" data-page-ai-hermes-panel></div>' +
'</section>' +
'<section class="wolai-page-ai-panel-section" data-page-ai-panel="reasonix-settings" hidden>' +
'<div class="wolai-page-ai-settings-head">' +
'<button type="button" class="wolai-page-ai-back" data-page-ai-tab="chat">← 聊天</button>' +
'<div class="wolai-page-ai-settings-tabs" role="tablist" aria-label="页面 AI 设置">' +
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="agent" role="tab" aria-selected="false">Common</button>' +
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="hermes-settings" role="tab" aria-selected="false">Hermes</button>' +
'<button type="button" class="wolai-page-ai-tab is-active" data-page-ai-tab="reasonix-settings" role="tab" aria-selected="true">Reasonix</button>' +
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="chat-only-settings" role="tab" aria-selected="false">Chat-only</button>' +
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="runtime" role="tab" aria-selected="false">高级</button>' +
pageAiSettingsTabsHtml('reasonix-settings') +
'</div>' +
'</div>' +
'<div class="wolai-page-ai-settings-grid">' +
@@ -935,26 +1176,13 @@ export function createSidebarPageAiRenderRuntime(context) {
'</div>' +
'<div class="wolai-page-ai-memory-grid" data-page-ai-reasonix-panel></div>' +
'</section>' +
'<section class="wolai-page-ai-panel-section" data-page-ai-panel="chat-only-settings" hidden>' +
'<div class="wolai-page-ai-settings-head">' +
'<button type="button" class="wolai-page-ai-back" data-page-ai-tab="chat">← 聊天</button>' +
'<div class="wolai-page-ai-settings-tabs" role="tablist" aria-label="页面 AI 设置">' +
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="agent" role="tab" aria-selected="false">Common</button>' +
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="hermes-settings" role="tab" aria-selected="false">Hermes</button>' +
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="reasonix-settings" role="tab" aria-selected="false">Reasonix</button>' +
'<button type="button" class="wolai-page-ai-tab is-active" data-page-ai-tab="chat-only-settings" role="tab" aria-selected="true">Chat-only</button>' +
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="runtime" role="tab" aria-selected="false">高级</button>' +
'</div>' +
'</div>' +
'<div class="wolai-page-ai-memory-grid" data-page-ai-chat-only-panel></div>' +
'</section>' +
'<section class="wolai-page-ai-panel-section" data-page-ai-panel="skills" hidden>' +
'<div class="wolai-page-ai-settings-head">' +
'<button type="button" class="wolai-page-ai-back" data-page-ai-tab="chat">← 聊天</button>' +
'<div class="wolai-page-ai-settings-tabs" role="tablist" aria-label="页面 AI 设置">' +
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="agent" role="tab" aria-selected="false">Agent</button>' +
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="agent" role="tab" aria-selected="false">设置</button>' +
'<button type="button" class="wolai-page-ai-tab is-active" data-page-ai-tab="skills" role="tab" aria-selected="true">能力</button>' +
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="runtime" role="tab" aria-selected="false">Runtime</button>' +
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="status" role="tab" aria-selected="false">状态</button>' +
'</div>' +
'</div>' +
'<div class="wolai-page-ai-skills-toolbar">' +
@@ -974,47 +1202,51 @@ export function createSidebarPageAiRenderRuntime(context) {
'<div class="wolai-page-ai-inline-error" data-page-ai-skill-error hidden></div>' +
'<div class="wolai-page-ai-skill-list" data-page-ai-skill-list></div>' +
'</section>' +
'<section class="wolai-page-ai-panel-section" data-page-ai-panel="runtime" hidden>' +
'<section class="wolai-page-ai-panel-section" data-page-ai-panel="status" hidden>' +
'<div class="wolai-page-ai-settings-head">' +
'<button type="button" class="wolai-page-ai-back" data-page-ai-tab="chat">← 聊天</button>' +
'<div class="wolai-page-ai-settings-tabs" role="tablist" aria-label="页面 AI 设置">' +
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="agent" role="tab" aria-selected="false">Agent</button>' +
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="skills" role="tab" aria-selected="false">能力</button>' +
'<button type="button" class="wolai-page-ai-tab is-active" data-page-ai-tab="runtime" role="tab" aria-selected="true">Runtime</button>' +
pageAiStatusTabsHtml() +
'</div>' +
'</div>' +
'<button type="button" class="wolai-page-ai-settings-link" data-page-ai-action="open-hermes-settings">打开 Hermes 设置</button>' +
'<label class="wolai-page-ai-context-select">' +
'<span>兼容上下文 scope</span>' +
'<select data-page-ai-context-scope>' +
'<option value="page">当前页</option>' +
'<option value="selection">当前选区</option>' +
'<option value="block">当前块</option>' +
'<option value="options">页面设置</option>' +
'</select>' +
'</label>' +
'<div class="wolai-page-ai-inline-error" data-page-ai-gateway-error hidden></div>' +
'<div class="wolai-page-ai-tools-panel">' +
'<div class="wolai-page-ai-tools-head">' +
'<span>gateway</span>' +
'<span data-page-ai-gateway-status>未检查</span>' +
'</div>' +
'<div class="wolai-page-ai-tool-list" data-page-ai-gateway-detail></div>' +
'<div class="wolai-page-ai-toolbar">' +
'<button type="button" class="wolai-page-ai-ghost" data-page-ai-action="stop-run">Stop turn</button>' +
'<button type="button" class="wolai-page-ai-ghost" data-page-ai-action="reset-runtime">Reset current runtime</button>' +
'</div>' +
'<div class="wolai-page-ai-tools-panel">' +
'<div class="wolai-page-ai-tools-head">' +
'<span>queue</span>' +
'<span>Current runtime</span>' +
'<span data-page-ai-runtime-badge>未加载</span>' +
'</div>' +
'<div class="wolai-page-ai-tool-list" data-page-ai-runtime-detail></div>' +
'</div>' +
'<div class="wolai-page-ai-tools-panel">' +
'<div class="wolai-page-ai-tools-head">' +
'<span>Queue</span>' +
'<span data-page-ai-queue-status>由 mnote-web 管理</span>' +
'</div>' +
'<div class="wolai-page-ai-tool-list" data-page-ai-queue-list></div>' +
'</div>' +
'<div class="wolai-page-ai-tools-panel" hidden>' +
'<div class="wolai-page-ai-tools-panel">' +
'<div class="wolai-page-ai-tools-head">' +
'<span>MNote 工具调试</span>' +
'<span data-page-ai-last-tool>暂无 tool call</span>' +
'<span>Jobs</span>' +
'<button type="button" class="wolai-page-ai-ghost" data-page-ai-action="stop-all-jobs">Stop all</button>' +
'</div>' +
'<div class="wolai-page-ai-inline-error" data-page-ai-tool-error hidden></div>' +
'<div class="wolai-page-ai-tool-list" data-page-ai-tool-list></div>' +
'<div class="wolai-page-ai-tool-list" data-page-ai-jobs-list></div>' +
'</div>' +
'<div class="wolai-page-ai-tools-panel">' +
'<div class="wolai-page-ai-tools-head">' +
'<span>Logs tail</span>' +
'<span>ACP stderr / tool failed / MCP failed</span>' +
'</div>' +
'<div class="wolai-page-ai-tool-list" data-page-ai-logs-list></div>' +
'</div>' +
'<div class="wolai-page-ai-tools-panel">' +
'<div class="wolai-page-ai-tools-head">' +
'<span>Usage</span>' +
'<span>last run / session / cache</span>' +
'</div>' +
'<div class="wolai-page-ai-tool-list" data-page-ai-usage-detail></div>' +
'</div>' +
'</section>' +
'<section class="wolai-page-ai-panel-section" data-page-ai-panel="history" hidden>' +
@@ -1034,6 +1266,10 @@ export function createSidebarPageAiRenderRuntime(context) {
'<span>按 agent 筛选</span>' +
'<select data-page-ai-session-agent-filter><option value="all">全部 agent</option></select>' +
'</label>' +
'<label class="wolai-page-ai-skill-search">' +
'<span>按状态筛选</span>' +
'<select data-page-ai-session-status-filter><option value="all">全部状态</option></select>' +
'</label>' +
'<div class="wolai-page-ai-inline-error" data-page-ai-session-error hidden></div>' +
'<div class="wolai-page-ai-conversation" data-page-ai-conversation></div>' +
'</section>' +
@@ -1041,6 +1277,7 @@ export function createSidebarPageAiRenderRuntime(context) {
'<div class="wolai-page-ai-footer">' +
'<div class="wolai-page-ai-composer">' +
'<textarea class="wolai-page-ai-input" data-page-ai-input rows="3" placeholder="输入消息…"></textarea>' +
'<div class="wolai-page-ai-reasonix-controls" data-page-ai-reasonix-quick-controls hidden></div>' +
'<div class="wolai-page-ai-composer-bar">' +
'<button type="button" class="wolai-page-ai-tool-button" data-page-ai-action="new-session" title="新会话"></button>' +
'<div class="wolai-page-ai-agent-picker">' +
@@ -1209,6 +1446,98 @@ export function createSidebarPageAiRenderRuntime(context) {
'</div>';
}
function pageAiLooksLikeReasonixPlanMessage(item) {
if (!item || item.role !== 'assistant') return false;
var text = String(item.content || '').trim();
if (!text || text.length < 40) return false;
if (/ACP connection closed|AI 当前请求失败|run failed/i.test(text)) return false;
var hasPlanWord = /计划|步骤|执行|修改|写入|新建|验证|plan/i.test(text);
var awaitsApproval = /请批准|待批准|批准后|确认后|审批|批准,|开始执行|approve/i.test(text);
var hasStepShape = /(^|\n)\s*(?:\d+[\.\、)]|[-*]\s+|\|\s*\d+\s*\|)/.test(text);
return hasPlanWord && awaitsApproval && hasStepShape;
}
function pageAiExtractReasonixPlanSteps(text) {
var seen = {};
var steps = [];
String(text || '').replace(/\r\n/g, '\n').split('\n').forEach(function(line) {
var trimmed = line.trim();
if (!trimmed) return;
var step = '';
var tableCells = trimmed.indexOf('|') >= 0
? trimmed.split('|').map(function(cell) { return cell.trim(); }).filter(Boolean)
: [];
if (tableCells.length >= 2 && /^\d+$/.test(tableCells[0])) {
step = tableCells.slice(1).join(' · ');
} else {
var numbered = trimmed.match(/^\s*\d+[\.\、)]\s+(.+)$/);
var bullet = trimmed.match(/^\s*[-*]\s+(?:\[[ xX~>|-]\]\s*)?(.+)$/);
step = numbered ? numbered[1] : (bullet ? bullet[1] : '');
}
step = String(step || '').replace(/\s+/g, ' ').trim();
if (!step || step.length < 3 || /^[-:|]+$/.test(step)) return;
if (seen[step]) return;
seen[step] = true;
var lower = step.toLowerCase();
var status = /✅|已完成|done|completed/.test(step) ? 'completed'
: (/当前|进行中|执行中|待批准|in progress|running/.test(lower) ? 'inProgress' : 'pending');
steps.push({ text: step, status: status });
});
return steps.slice(0, 8);
}
function pageAiPlanStepIcon(status) {
if (status === 'completed') return '✓';
if (status === 'inProgress') return '…';
return String.fromCharCode(8226);
}
function pageAiRenderReasonixPlanCard(item) {
var text = String(item.content || '').trim();
var steps = pageAiExtractReasonixPlanSteps(text);
var stepHtml = steps.length ? (
'<ol class="wolai-page-ai-plan-step-list">' + steps.map(function(step) {
return '' +
'<li class="wolai-page-ai-plan-step-item" data-status="' + escapeHtml(step.status || 'pending') + '">' +
'<span class="wolai-page-ai-plan-step-status" data-status="' + escapeHtml(step.status || 'pending') + '">' + escapeHtml(pageAiPlanStepIcon(step.status)) + '</span>' +
'<span class="wolai-page-ai-plan-step-text">' + escapeHtml(step.text || '') + '</span>' +
'</li>';
}).join('') + '</ol>'
) : '';
return '' +
'<div class="wolai-page-ai-message wolai-page-ai-message--assistant wolai-page-ai-plan-card" data-page-ai-reasonix-plan-card="true">' +
'<div class="wolai-page-ai-plan-card-header">' +
'<div class="wolai-page-ai-plan-card-title-wrap">' +
'<span class="wolai-page-ai-plan-card-kicker">Reasonix</span>' +
'<strong class="wolai-page-ai-plan-card-title">执行计划</strong>' +
'</div>' +
'<span class="wolai-page-ai-plan-card-badge">待批准</span>' +
'</div>' +
'<div class="wolai-page-ai-plan-card-markdown">' + renderPageAiMarkdown(text) + '</div>' +
stepHtml +
'<div class="wolai-page-ai-plan-card-actions">' +
'<button type="button" class="wolai-page-ai-plan-card-button wolai-page-ai-plan-card-button--primary" data-page-ai-plan-action="approve">批准执行</button>' +
'<button type="button" class="wolai-page-ai-plan-card-button" data-page-ai-plan-action="revise">要求修改</button>' +
'<button type="button" class="wolai-page-ai-plan-card-button wolai-page-ai-plan-card-button--danger" data-page-ai-plan-action="cancel">取消</button>' +
'</div>' +
'</div>';
}
function pageAiRenderPermissionDetails(item) {
var details = item && item.permissionDetails && typeof item.permissionDetails === 'object'
? item.permissionDetails
: {};
var rows = Array.isArray(details.rows) ? details.rows : [];
if (!rows.length) {
rows = [
{ label: '请求', value: item.argsSummary || item.content || '' }
];
}
return '<div class="wolai-page-ai-permission-summary">' + rows.slice(0, 6).map(function(row) {
return '<div class="wolai-page-ai-permission-row"><span>' + escapeHtml(row.label || '') + '</span><strong>' + escapeHtml(row.value || '') + '</strong></div>';
}).join('') + '</div>';
}
function pageAiRenderNonToolMessage(item) {
var roleLabel = item.role === 'user' ? '你' : (item.role === 'tool' ? '工具' : 'AI');
if (item.kind === 'thought') {
@@ -1224,18 +1553,34 @@ export function createSidebarPageAiRenderRuntime(context) {
'</div>';
}
if (item.kind === 'permission') {
var permissionActions = item.resolved ? '' : (
var permissionOptions = Array.isArray(item.options) ? item.options : [];
var permissionActions = '';
if (!item.resolved) {
if (permissionOptions.length) {
permissionActions = '<div class="wolai-page-ai-message-actions">' + permissionOptions.map(function(option) {
var optionText = String(option.optionId || '') + ' ' + String(option.kind || '') + ' ' + String(option.name || '');
var denyLike = /reject|deny|cancel|stop|no/.test(optionText.toLowerCase());
return '<button type="button" class="wolai-page-ai-ghost' + (denyLike ? ' wolai-page-ai-ghost--danger' : '') + '" data-page-ai-permission-action="' + (denyLike ? 'deny' : 'allow') + '" data-page-ai-permission-option-id="' + escapeHtml(option.optionId || '') + '" data-page-ai-permission-id="' + escapeHtml(item.permissionId || '') + '">' + escapeHtml(option.name || option.optionId || '选择') + '</button>';
}).join('') + '</div>';
} else {
permissionActions = '' +
'<div class="wolai-page-ai-message-actions">' +
'<button type="button" class="wolai-page-ai-ghost" data-page-ai-permission-action="allow" data-page-ai-permission-id="' + escapeHtml(item.permissionId || '') + '">允许</button>' +
'<button type="button" class="wolai-page-ai-ghost" data-page-ai-permission-action="deny" data-page-ai-permission-id="' + escapeHtml(item.permissionId || '') + '">拒绝</button>' +
'</div>'
);
'</div>';
}
}
return '' +
'<div class="wolai-page-ai-message wolai-page-ai-message--tool" data-page-ai-permission-request="' + escapeHtml(item.permissionId || '') + '">' +
'<div class="wolai-page-ai-message-role">权限</div>' +
'<div class="wolai-page-ai-message wolai-page-ai-message--tool wolai-page-ai-permission-card" data-page-ai-permission-request="' + escapeHtml(item.permissionId || '') + '">' +
'<div class="wolai-page-ai-permission-card-header">' +
'<div class="wolai-page-ai-plan-card-title-wrap">' +
'<span class="wolai-page-ai-plan-card-kicker">权限</span>' +
'<strong class="wolai-page-ai-plan-card-title">' + escapeHtml(item.toolName || 'session/request_permission') + '</strong>' +
'</div>' +
'<span class="wolai-page-ai-permission-badge">' + escapeHtml(item.resolved ? (item.decision === 'allow' || item.decision === 'allowed' ? '已允许' : '已处理') : '待确认') + '</span>' +
'</div>' +
'<div class="wolai-page-ai-message-text">' +
'<strong>' + escapeHtml(item.toolName || 'session/request_permission') + '</strong><br />' +
'<span>' + escapeHtml(item.argsSummary || item.content || '') + '</span>' +
pageAiRenderPermissionDetails(item) +
permissionActions +
'</div>' +
'</div>';
@@ -1255,6 +1600,7 @@ export function createSidebarPageAiRenderRuntime(context) {
'</details>' +
'</div>';
}
if (pageAiLooksLikeReasonixPlanMessage(item)) return pageAiRenderReasonixPlanCard(item);
var streamingAttr = item.streaming ? ' data-page-ai-streaming="true"' : '';
return '' +
'<div class="wolai-page-ai-message wolai-page-ai-message--' + escapeHtml(item.role || 'assistant') + '"' + streamingAttr + '>' +
@@ -1345,9 +1691,10 @@ export function createSidebarPageAiRenderRuntime(context) {
var selected = pageAiSessionSelected(session.id);
var usage = pageAiUsageSummary(session.usage);
var agentLabel = pageAiSessionAgentLabel(session);
var meta = [agentLabel, pageAiSessionStorageLabel(session), session.permissionLevel, session.shareId, session.status, usage].filter(Boolean).join(' · ');
var statusValue = pageAiSessionStatusFilterValue(session);
var meta = [agentLabel, pageAiSessionStorageLabel(session), session.runtimeMode, session.permissionLevel, session.shareId, pageAiSessionStatusLabel(statusValue), usage].filter(Boolean).join(' · ');
return '' +
'<div class="wolai-page-ai-message wolai-page-ai-message--history' + (active ? ' is-active' : '') + (selected ? ' is-selected' : '') + '" data-page-ai-session-row="' + escapeHtml(session.id) + '" data-page-ai-session-agent="' + escapeHtml(pageAiSessionAgentFilterValue(session)) + '">' +
'<div class="wolai-page-ai-message wolai-page-ai-message--history' + (active ? ' is-active' : '') + (selected ? ' is-selected' : '') + '" data-page-ai-session-row="' + escapeHtml(session.id) + '" data-page-ai-session-agent="' + escapeHtml(pageAiSessionAgentFilterValue(session)) + '" data-page-ai-session-status="' + escapeHtml(statusValue) + '">' +
'<button type="button" class="wolai-page-ai-session-check" data-page-ai-session-select="' + escapeHtml(session.id) + '" aria-checked="' + (selected ? 'true' : 'false') + '" aria-label="选择会话 ' + escapeHtml(session.title || session.id) + '">' +
'<span>' + (selected ? '✓' : '') + '</span>' +
'</button>' +
@@ -140,6 +140,7 @@ export function createSidebarPageAiRuntime(context) {
pageAiAcpRuntimes: [],
pageAiQueueLength: 0,
pageAiQueuedItems: [],
pageAiClientQueuedPrompts: [],
pageAiStoppedRunIds: {},
pageAiAbortController: null,
pageAiContextScope: 'page',
@@ -184,8 +185,11 @@ export function createSidebarPageAiRuntime(context) {
pageAiSessionSearchResults: [],
pageAiSessionSearchTimer: 0,
pageAiSessionAgentFilter: 'all',
pageAiSessionStatusFilter: 'all',
pageAiSelectedSessionIds: {},
pageAiSessionError: '',
pageAiRuntimeStatus: null,
pageAiRuntimeStatusError: '',
pageAiPermissionRequests: []
};
Object.keys(defaults).forEach(function(key) {
@@ -205,6 +209,9 @@ export function createSidebarPageAiRuntime(context) {
if (!state.pageAiSkills || typeof state.pageAiSkills !== 'object') {
state.pageAiSkills = clonePageAiDefaultValue(defaults.pageAiSkills);
}
if (!Array.isArray(state.pageAiClientQueuedPrompts)) {
state.pageAiClientQueuedPrompts = [];
}
return state;
}
@@ -435,6 +442,9 @@ export function createSidebarPageAiRuntime(context) {
const pageAiSessionAgentLabel = (...args) => pageAiProfileRuntime.pageAiSessionAgentLabel(...args);
const pageAiSessionPreviewText = (...args) => pageAiProfileRuntime.pageAiSessionPreviewText(...args);
const pageAiSessionAgentFilterOptions = (...args) => pageAiProfileRuntime.pageAiSessionAgentFilterOptions(...args);
const pageAiSessionStatusFilterValue = (...args) => pageAiProfileRuntime.pageAiSessionStatusFilterValue(...args);
const pageAiSessionStatusLabel = (...args) => pageAiProfileRuntime.pageAiSessionStatusLabel(...args);
const pageAiSessionStatusFilterOptions = (...args) => pageAiProfileRuntime.pageAiSessionStatusFilterOptions(...args);
const pageAiFilteredHistoryRows = (...args) => pageAiProfileRuntime.pageAiFilteredHistoryRows(...args);
const pageAiTimestamp = (...args) => pageAiProfileRuntime.pageAiTimestamp(...args);
const pageAiUsageSummary = (...args) => pageAiProfileRuntime.pageAiUsageSummary(...args);
@@ -447,6 +457,7 @@ export function createSidebarPageAiRuntime(context) {
const pageAiPermissionMessage = (...args) => pageAiPermissionRuntime.pageAiPermissionMessage(...args);
const pageAiApplyPermissionEvent = (...args) => pageAiPermissionRuntime.pageAiApplyPermissionEvent(...args);
const pageAiResolvePermission = (...args) => pageAiPermissionRuntime.pageAiResolvePermission(...args);
const pageAiRestorePermissionRequests = (...args) => pageAiPermissionRuntime.pageAiRestorePermissionRequests(...args);
const pageAiHidePermissionDialog = (...args) => pageAiPermissionRuntime.pageAiHidePermissionDialog(...args);
const pageAiShowPermissionDialog = (...args) => pageAiPermissionRuntime.pageAiShowPermissionDialog(...args);
const pageAiSessionRuntime = createSidebarPageAiSessionRuntime({
@@ -638,6 +649,9 @@ export function createSidebarPageAiRuntime(context) {
pageAiSessionAgentFilterValue,
pageAiSessionAgentLabel,
pageAiSessionPreviewText,
pageAiSessionStatusFilterOptions,
pageAiSessionStatusFilterValue,
pageAiSessionStatusLabel,
pageAiSessionStorageLabel,
pageAiSessionSelected,
pageAiSelectedSessionCount,
@@ -806,6 +820,28 @@ export function createSidebarPageAiRuntime(context) {
renderPageAiControls();
}
function pageAiReasonixPreference(key, fallback) {
var preferences = pageAiDescriptorPreferenceValues();
var value = preferences[key];
if (value == null || String(value).trim() === '') return fallback;
return String(value).trim();
}
function pageAiReasonixControlState() {
return {
modelId: pageAiReasonixPreference('ai.agent.reasonix.model_id', 'deepseek-flash'),
approvalMode: pageAiReasonixPreference('ai.agent.reasonix.approval_mode', 'ask'),
planMode: pageAiReasonixPreference('ai.agent.reasonix.plan_mode', 'manual')
};
}
function pageAiApplyReasonixPlanMode(prompt) {
if (pageAiCurrentAgentId() !== 'reasonix') return prompt;
var control = pageAiReasonixControlState();
if (control.planMode !== 'auto') return prompt;
return '请按 Reasonix 自动计划模式处理:复杂或会写文件的任务先给出简短计划,并等待我批准后再执行。\n\n' + prompt;
}
function pageAiApplyAgentDescriptorsPayload(payload) {
var descriptors = pageAiNormalizeArray(payload && payload.descriptors)
.map(pageAiNormalizeAgentDescriptor)
@@ -1316,6 +1352,68 @@ export function createSidebarPageAiRuntime(context) {
}
}
function pageAiApplyRuntimeStatus(payload) {
if (!payload || typeof payload !== 'object') return null;
pageUiState.pageAiRuntimeStatus = payload;
pageUiState.pageAiRuntimeStatusError = '';
var activeRun = payload.activeRun && typeof payload.activeRun === 'object' ? payload.activeRun : null;
if (activeRun && activeRun.hostRunId) {
pageAiSetRunStatus(String(activeRun.status || 'running'), String(activeRun.hostRunId || ''));
} else if (pageAiRunStatusIsTerminal(pageUiState.pageAiRunStatus)) {
pageAiUpdateActiveRunSnapshotFromStatus(pageUiState.pageAiRunStatus, pageUiState.pageAiCurrentRunId);
} else if (!activeRun && ['running', 'tool_calling', 'queued', 'pending', 'acp_pending'].indexOf(pageUiState.pageAiRunStatus) >= 0) {
pageAiSetRunStatus('idle', pageUiState.pageAiCurrentRunId);
}
var queue = payload.session && payload.session.queue && typeof payload.session.queue === 'object' ? payload.session.queue : null;
if (queue) {
var queuedCount = Number(queue.queuedCount || 0);
pageUiState.pageAiQueueLength = Number.isFinite(queuedCount) ? Math.max(0, queuedCount) : 0;
if (Array.isArray(queue.items)) {
pageUiState.pageAiQueuedItems = queue.items.map(function(item) {
return {
queueId: String(item.queueId || ''),
sessionId: String(item.sessionId || payload.mnoteSessionId || ''),
traceId: String(item.traceId || ''),
preview: String(item.preview || ''),
queuedAt: Number(item.queuedAt || Date.now())
};
}).filter(function(item) { return item.queueId; });
}
}
var acpSessionId = payload.acp && typeof payload.acp === 'object' ? String(payload.acp.acpSessionId || '') : '';
var current = pageAiCurrentSession();
if (current && acpSessionId) {
current.acpSessionId = acpSessionId;
current.runtimeMode = String(payload.mode || '');
current.updatedAt = Date.now();
pageAiPersistSessions();
}
document.documentElement.setAttribute('data-mnote-page-ai-runtime-status-loaded', 'true');
document.documentElement.setAttribute('data-mnote-page-ai-runtime', String(payload.runtime || ''));
document.documentElement.setAttribute('data-mnote-page-ai-runtime-mode', String(payload.mode || ''));
return payload;
}
async function pageAiLoadRuntimeStatus() {
var params = new URLSearchParams();
var sessionId = String(pageUiState.pageAiActiveSessionId || '').trim();
if (sessionId) params.set('sessionId', sessionId);
var workspaceId = String(resolveWorkspaceId(document.body) || '').trim();
if (workspaceId) params.set('workspaceId', workspaceId);
var documentId = String(currentDocumentId() || '').trim();
if (documentId) params.set('documentId', documentId);
var profile = String(pageAiCurrentProfile() || '').trim();
if (profile) params.set('profile', profile);
var response = await fetch('/api/page-ai/runtime/status?' + params.toString(), {
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, 'page_ai_runtime_status_failed_' + response.status));
}
return pageAiApplyRuntimeStatus(payload);
}
function pageAiActiveRunSnapshotKey() {
return pageAiStorageKey() + ':active-run';
}
@@ -1409,6 +1507,60 @@ export function createSidebarPageAiRuntime(context) {
return true;
}
function pageAiRefreshClientQueueState() {
var clientItems = pageAiNormalizeArray(pageUiState.pageAiClientQueuedPrompts);
var serverItems = pageAiNormalizeArray(pageUiState.pageAiQueuedItems)
.filter(function(item) { return String(item.queueId || '').indexOf('client_') !== 0; });
pageUiState.pageAiQueuedItems = serverItems.concat(clientItems.map(function(item) {
return {
queueId: item.queueId,
sessionId: item.sessionId || pageUiState.pageAiActiveSessionId || '',
traceId: item.traceId || '',
preview: item.preview || item.prompt || '',
queuedAt: item.queuedAt || Date.now()
};
}));
pageUiState.pageAiQueueLength = pageUiState.pageAiQueuedItems.length;
}
function pageAiEnqueueClientPrompt(prompt) {
var queueId = 'client_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2, 8);
pageUiState.pageAiClientQueuedPrompts.push({
queueId: queueId,
prompt: prompt,
preview: String(prompt || '').slice(0, 120),
sessionId: pageUiState.pageAiActiveSessionId || '',
queuedAt: Date.now()
});
pageAiRefreshClientQueueState();
pageAiSetRunStatus('queued', pageUiState.pageAiCurrentRunId);
return queueId;
}
function pageAiRemoveClientQueuedPrompt(queueId) {
var before = pageUiState.pageAiClientQueuedPrompts.length;
pageUiState.pageAiClientQueuedPrompts = pageUiState.pageAiClientQueuedPrompts.filter(function(item) {
return item.queueId !== queueId;
});
pageAiRefreshClientQueueState();
return before !== pageUiState.pageAiClientQueuedPrompts.length;
}
function pageAiStartNextClientQueuedPrompt() {
if (pageUiState.pageAiBusy) return false;
if (['running', 'tool_calling', 'pending', 'acp_pending'].indexOf(pageUiState.pageAiRunStatus) >= 0) return false;
var next = pageUiState.pageAiClientQueuedPrompts.shift();
if (!next) {
pageAiRefreshClientQueueState();
return false;
}
pageAiRefreshClientQueueState();
window.setTimeout(function() {
void sendPageAiMessage(next.prompt);
}, 0);
return true;
}
function pageAiUnwrapSearchCitationUrl(url) {
try {
var raw = '';
@@ -1668,13 +1820,21 @@ export function createSidebarPageAiRuntime(context) {
async function pageAiCancelQueuedRun(queueId) {
queueId = String(queueId || '').trim();
if (!queueId) return;
if (queueId.indexOf('client_') === 0) {
if (pageAiRemoveClientQueuedPrompt(queueId)) {
pageUiState.pageAiMessages.push({ role: 'assistant', content: '已取消一条 AI 队列项。' });
}
renderPageAiConversation();
renderPageAiControls();
return;
}
var item = pageUiState.pageAiQueuedItems.find(function(entry) {
return entry.queueId === queueId;
});
var sessionId = String(item && item.sessionId || pageUiState.pageAiActiveSessionId || '').trim();
if (!sessionId) return;
try {
var response = await fetch('/api/hermes/client/sessions/' + encodeURIComponent(sessionId) + '/queue/' + encodeURIComponent(queueId), {
var response = await fetch('/api/page-ai/sessions/' + encodeURIComponent(sessionId) + '/queue/' + encodeURIComponent(queueId), {
method: 'DELETE',
headers: { 'accept': 'application/json' }
});
@@ -1696,6 +1856,40 @@ export function createSidebarPageAiRuntime(context) {
renderPageAiControls();
}
async function pageAiResetRuntime() {
var sessionId = String(pageUiState.pageAiActiveSessionId || '').trim();
if (!sessionId) return;
try {
var response = await fetch('/api/page-ai/runtime/reset', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
sessionId: sessionId,
runtime: pageUiState.pageAiAcpRuntime || '',
profile: pageAiCurrentProfile()
})
});
var payload = await response.json().catch(function(){ return null; });
if (!response.ok || !payload || payload.ok !== true) {
throw new Error(pageAiErrorMessage(payload, 'runtime_reset_failed_' + response.status));
}
pageUiState.pageAiRuntimeStatus = null;
pageUiState.pageAiRuntimeStatusError = '';
pageUiState.pageAiMessages.push({
role: 'assistant',
content: '已重置 runtimeMNote 会话历史保留,外部 provider 会话未删除。'
});
await pageAiLoadRuntimeStatus().catch(function(error) {
pageUiState.pageAiRuntimeStatusError = error instanceof Error ? error.message : String(error);
return null;
});
} catch (error) {
pageUiState.pageAiRuntimeStatusError = error instanceof Error ? error.message : String(error);
}
renderPageAiConversation();
renderPageAiControls();
}
function pageAiSetContextScope(scope) {
var next = String(scope || '').trim() || 'page';
pageUiState.pageAiContextScope = next;
@@ -2141,6 +2335,7 @@ export function createSidebarPageAiRuntime(context) {
pageUiState.pageAiAgentId = pageAiCurrentAgentId();
pageAiEnsureContextRefState();
pageAiLoadSessions();
pageAiRestorePermissionRequests();
renderPageAiSuggestions();
renderPageAiConversation();
renderPageAiProviderButtons();
@@ -2153,6 +2348,12 @@ export function createSidebarPageAiRuntime(context) {
void pageAiCheckAndResumeActiveRun().catch(function(error) {
pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
renderPageAiControls();
}).then(function() {
return pageAiLoadRuntimeStatus().catch(function(error) {
pageUiState.pageAiRuntimeStatusError = error instanceof Error ? error.message : String(error);
renderPageAiControls();
return null;
});
});
void pageAiLoadBackendSessions().then(function() {
return pageAiCheckAndResumeActiveRun();
@@ -2169,7 +2370,8 @@ export function createSidebarPageAiRuntime(context) {
pageAiLoadGatewayHealth(),
pageAiLoadAiPreferences(),
pageAiLoadAllowedRoots(),
pageAiLoadBackendSessions()
pageAiLoadBackendSessions(),
pageAiLoadRuntimeStatus()
]).then(function() {
renderPageAiControls();
}).then(function() {
@@ -2803,10 +3005,27 @@ export function createSidebarPageAiRuntime(context) {
}
async function sendPageAiMessage(text) {
var allowQueue = ['running', 'tool_calling'].indexOf(pageUiState.pageAiRunStatus) >= 0;
var allowQueue = pageUiState.pageAiBusy
|| ['queued', 'running', 'tool_calling', 'pending', 'acp_pending'].indexOf(pageUiState.pageAiRunStatus) >= 0;
if (pageUiState.pageAiBusy && !allowQueue) return;
var prompt = searchText(text);
if (!prompt) return;
if (allowQueue) {
pageAiEnqueueClientPrompt(prompt);
pageUiState.pageAiMessages.push({
role: 'assistant',
content: '已加入 AI 队列,前一条 run 完成后继续处理。'
});
var queuedSession = pageAiCurrentSession();
if (queuedSession) {
queuedSession.messages = pageUiState.pageAiMessages.slice();
queuedSession.updatedAt = Date.now();
}
pageAiPersistSessions();
renderPageAiConversation();
renderPageAiControls();
return;
}
if (!allowQueue) pageUiState.pageAiBusy = true;
var currentSession = null;
try {
@@ -2846,6 +3065,8 @@ export function createSidebarPageAiRuntime(context) {
if (!allowQueue && await pageAiTryBlockEditWorkflow(prompt, scopedContext, runTargetSnapshot)) {
return;
}
var reasonixSettings = pageAiCurrentAgentId() === 'reasonix' ? pageAiReasonixControlState() : null;
var promptForRun = pageAiApplyReasonixPlanMode(prompt);
var response = await fetch('/api/hermes/client/runs', {
method: 'POST',
headers: { 'content-type': 'application/json' },
@@ -2863,13 +3084,14 @@ export function createSidebarPageAiRuntime(context) {
contextScope: pageUiState.pageAiContextScope,
contextRefs: contextRefs,
allowedRoots: allowedRoots,
reasonixSettings: reasonixSettings,
skillPreferences: {
mnote: pageAiSkillPreferenceTable('mnote', ''),
reasonix: pageAiSkillPreferenceTable('reasonix', ''),
hermes: pageAiSkillPreferenceTable('hermes', runProfile)
},
message: prompt,
model: pageAiMnoteToolModel(),
message: promptForRun,
model: reasonixSettings && reasonixSettings.modelId ? reasonixSettings.modelId : pageAiMnoteToolModel(),
pageContext: requestPageContext,
editorTarget: scopedContext.editorTarget,
runTargetSnapshot: runTargetSnapshot,
@@ -2896,6 +3118,10 @@ export function createSidebarPageAiRuntime(context) {
pageAiPersistSessions();
renderPageAiConversation();
renderPageAiControls();
void pageAiLoadRuntimeStatus().catch(function(error) {
pageUiState.pageAiRuntimeStatusError = error instanceof Error ? error.message : String(error);
renderPageAiControls();
});
return;
}
if (allowQueue) {
@@ -3114,8 +3340,13 @@ export function createSidebarPageAiRuntime(context) {
pageAiPersistSessions();
} finally {
if (!allowQueue) pageUiState.pageAiBusy = false;
await pageAiLoadRuntimeStatus().catch(function(error) {
pageUiState.pageAiRuntimeStatusError = error instanceof Error ? error.message : String(error);
return null;
});
renderPageAiConversation();
renderPageAiControls();
pageAiStartNextClientQueuedPrompt();
}
}
@@ -3148,6 +3379,20 @@ export function createSidebarPageAiRuntime(context) {
void pageAiStopRun();
return true;
}
var pageAiReset = closestAction(event.target, '[data-page-ai-action="reset-runtime"]');
if (pageAiReset) {
event.preventDefault();
void pageAiResetRuntime();
return true;
}
var pageAiStopAllJobs = closestAction(event.target, '[data-page-ai-action="stop-all-jobs"]');
if (pageAiStopAllJobs) {
event.preventDefault();
pageUiState.pageAiMessages.push({ role: 'assistant', content: '当前没有可停止的后台 job;长任务 job 将在 runtime status 中独立显示。' });
renderPageAiConversation();
renderPageAiControls();
return true;
}
var pageAiRotate = closestAction(event.target, '[data-page-ai-action="rotate"]');
if (pageAiRotate) {
event.preventDefault();
@@ -3167,7 +3412,15 @@ export function createSidebarPageAiRuntime(context) {
if (pageAiTab) {
event.preventDefault();
pageUiState.pageAiPage = pageAiTab.getAttribute('data-page-ai-tab') || 'chat';
if (pageUiState.pageAiPage === 'runtime') void pageAiLoadGatewayHealth();
if (pageUiState.pageAiPage === 'runtime') pageUiState.pageAiPage = 'status';
if (pageUiState.pageAiPage === 'chat-only-settings') pageUiState.pageAiPage = 'agent';
if (pageUiState.pageAiPage === 'status') {
void pageAiLoadGatewayHealth();
void pageAiLoadRuntimeStatus().catch(function(error) {
pageUiState.pageAiRuntimeStatusError = error instanceof Error ? error.message : String(error);
renderPageAiControls();
});
}
if (pageUiState.pageAiPage === 'skills') void pageAiLoadSkills();
renderPageAiControls();
renderPageAiConversation();
@@ -3351,7 +3604,30 @@ export function createSidebarPageAiRuntime(context) {
var pageAiPermissionAction = closestAction(event.target, '[data-page-ai-permission-action]');
if (pageAiPermissionAction) {
event.preventDefault();
pageAiResolvePermission(pageAiPermissionAction.getAttribute('data-page-ai-permission-id') || '', pageAiPermissionAction.getAttribute('data-page-ai-permission-action') || 'deny');
pageAiResolvePermission(
pageAiPermissionAction.getAttribute('data-page-ai-permission-id') || '',
pageAiPermissionAction.getAttribute('data-page-ai-permission-action') || 'deny',
pageAiPermissionAction.getAttribute('data-page-ai-permission-option-id') || ''
);
return true;
}
var pageAiPlanAction = closestAction(event.target, '[data-page-ai-plan-action]');
if (pageAiPlanAction) {
event.preventDefault();
var action = String(pageAiPlanAction.getAttribute('data-page-ai-plan-action') || '').trim();
var inputNode = ensurePageAiDrawer().querySelector('[data-page-ai-input]');
if (action === 'approve') {
void sendPageAiMessage('批准,开始执行。');
return true;
}
if (action === 'cancel') {
void sendPageAiMessage('取消执行。');
return true;
}
if (inputNode instanceof HTMLTextAreaElement) {
inputNode.value = action === 'revise' ? '请修改计划:' : '';
inputNode.focus();
}
return true;
}
var pageAiOpenLocationAction = closestAction(event.target, '[data-page-ai-open-location]');
@@ -3474,6 +3750,13 @@ export function createSidebarPageAiRuntime(context) {
renderPageAiConversation();
return true;
}
var pageAiSessionStatusFilter = closestAction(event.target, '[data-page-ai-session-status-filter]');
if (pageAiSessionStatusFilter instanceof HTMLSelectElement) {
pageUiState.pageAiSessionStatusFilter = String(pageAiSessionStatusFilter.value || 'all').trim() || 'all';
renderPageAiControls();
renderPageAiConversation();
return true;
}
var pageAiHideHermesBuiltin = closestAction(event.target, '[data-page-ai-hide-hermes-builtin]');
if (pageAiHideHermesBuiltin instanceof HTMLInputElement) {
pageAiSetHideHermesBuiltinSkills(pageAiHideHermesBuiltin.checked);
@@ -98,12 +98,16 @@ export function createSidebarPageAiSessionRuntime(context) {
acpSessionId: String(session && (session.acpSessionId || session.acp_session_id) || '').trim(),
runId: String(session && (session.runId || session.run_id) || '').trim(),
status: String(session && session.status || '').trim(),
runtimeMode: String(session && (session.runtimeMode || session.runtime_mode) || '').trim(),
replaySeen: Boolean(session && session.replaySeen),
usage: session && session.usage && typeof session.usage === 'object' ? session.usage : null,
preview: String(session && session.preview || '').trim(),
messages: Array.isArray(session && session.messages) ? session.messages.slice(-300) : []
};
})
.sort(function(a, b) {
if (a.id === pageUiState.pageAiActiveSessionId && b.id !== pageUiState.pageAiActiveSessionId) return -1;
if (b.id === pageUiState.pageAiActiveSessionId && a.id !== pageUiState.pageAiActiveSessionId) return 1;
return Number(b.updatedAt || 0) - Number(a.updatedAt || 0);
});
}
@@ -117,7 +121,8 @@ export function createSidebarPageAiSessionRuntime(context) {
if (title.length > 28) title = title.slice(0, 28) + '…';
var persistence = String(row.persistence || payload.persistence || '').trim();
var sessionStorage = String(row.sessionStorage || row.session_storage || payload.sessionStorage || '').trim();
var status = String(row.status || (row.runtime && row.runtime.status) || '').trim();
var runtime = row.runtime && typeof row.runtime === 'object' ? row.runtime : {};
var status = String(row.status || runtime.status || '').trim();
var hasConversationSignal = String(payload.message || payload.input || row.snippet || '').trim()
|| pageAiNormalizeArray(row.messages).length > 0;
if (status === 'session.created' && !hasConversationSignal) return null;
@@ -148,6 +153,8 @@ export function createSidebarPageAiSessionRuntime(context) {
acpSessionId: String(row.acpSessionId || row.acp_session_id || payload.acpSessionId || payload.acp_session_id || '').trim(),
runId: String(row.runId || row.run_id || '').trim(),
status: status,
runtimeMode: String(row.runtimeMode || row.runtime_mode || runtime.mode || payload.reasonixSessionMode || '').trim(),
replaySeen: Boolean(row.replaySeen || row.replay_seen || runtime.replaySeen || payload.replay === true),
usage: row.usage && typeof row.usage === 'object' ? row.usage : null,
preview: String(payload.message || row.snippet || '').trim(),
messages: []
@@ -209,7 +216,7 @@ export function createSidebarPageAiSessionRuntime(context) {
}
async function pageAiLoadBackendSessions() {
var response = await fetch('/api/hermes/client/sessions?' + pageAiBackendSessionQuery({ limit: 50 }), {
var response = await fetch('/api/page-ai/sessions?' + pageAiBackendSessionQuery({ limit: 50 }), {
headers: { 'accept': 'application/json' }
});
var payload = await response.json().catch(function(){ return null; });
@@ -374,7 +381,7 @@ export function createSidebarPageAiSessionRuntime(context) {
async function pageAiLoadBackendSessionDetail(sessionId) {
sessionId = String(sessionId || pageUiState.pageAiActiveSessionId || '').trim();
if (!sessionId) return null;
var response = await fetch('/api/hermes/client/sessions/' + encodeURIComponent(sessionId) + '?' + pageAiBackendSessionQuery({ limit: 200 }), {
var response = await fetch('/api/page-ai/sessions/' + encodeURIComponent(sessionId) + '?' + pageAiBackendSessionQuery({ limit: 200 }), {
headers: { 'accept': 'application/json' }
});
var payload = await response.json().catch(function(){ return null; });
@@ -395,7 +402,7 @@ export function createSidebarPageAiSessionRuntime(context) {
renderPageAiConversation();
return [];
}
var response = await fetch('/api/hermes/client/sessions/search?' + pageAiBackendSessionQuery({ q: q, limit: 20 }), {
var response = await fetch('/api/page-ai/sessions/search?' + pageAiBackendSessionQuery({ q: q, limit: 20 }), {
headers: { 'accept': 'application/json' }
});
var payload = await response.json().catch(function(){ return null; });
@@ -414,7 +421,7 @@ export function createSidebarPageAiSessionRuntime(context) {
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 }), {
var response = await fetch('/api/page-ai/sessions/' + encodeURIComponent(sessionId) + '/export?' + pageAiBackendSessionQuery({ limit: 200 }), {
headers: { 'accept': 'application/json' }
});
var payload = await response.json().catch(function(){ return null; });
@@ -454,7 +461,7 @@ export function createSidebarPageAiSessionRuntime(context) {
var current = pageAiCurrentSession();
var runProfile = pageAiRunProfile();
if (!forceCreate && current && String(current.id || '').startsWith('mnote_') && current.profile === runProfile) return current;
var response = await fetch('/api/hermes/client/sessions', {
var response = await fetch('/api/page-ai/sessions', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
@@ -504,7 +511,7 @@ export function createSidebarPageAiSessionRuntime(context) {
async function pageAiRestoreHermesSession() {
var current = pageAiCurrentSession();
if (!current || !String(current.id || '').startsWith('mnote_')) return;
var response = await fetch('/api/hermes/client/sessions/' + encodeURIComponent(current.id) + '/resume?' + pageAiBackendSessionQuery({}), {
var response = await fetch('/api/page-ai/sessions/' + encodeURIComponent(current.id) + '/resume?' + pageAiBackendSessionQuery({}), {
method: 'POST',
headers: { 'accept': 'application/json' }
});
@@ -637,7 +644,7 @@ export function createSidebarPageAiSessionRuntime(context) {
if (title === null) return;
title = String(title || '').trim();
if (!title) return;
var response = await fetch('/api/hermes/client/sessions/' + encodeURIComponent(sessionId) + '/rename?' + pageAiBackendSessionQuery({}), {
var response = await fetch('/api/page-ai/sessions/' + encodeURIComponent(sessionId) + '/rename?' + pageAiBackendSessionQuery({}), {
method: 'POST',
headers: { 'content-type': 'application/json', 'accept': 'application/json' },
body: JSON.stringify({ title: title })
@@ -712,13 +719,13 @@ export function createSidebarPageAiSessionRuntime(context) {
var sessions = ids.map(pageAiFindSessionById).filter(Boolean);
if (!sessions.length) return;
var confirmText = sessions.length === 1
? '确定删除 AI 会话“' + (sessions[0].title || sessions[0].id) + '”吗?'
: '确定删除选中的 ' + String(sessions.length) + ' 个 AI 会话吗?';
? '确定删除 AI 会话“' + (sessions[0].title || sessions[0].id) + '”吗?仅删除 MNote 历史,不删除外部 Hermes/provider 会话。'
: '确定删除选中的 ' + String(sessions.length) + ' 个 AI 会话吗?仅删除 MNote 历史,不删除外部 Hermes/provider 会话。';
if (!win.confirm(confirmText)) return;
for (var index = 0; index < sessions.length; index += 1) {
var session = sessions[index];
if (!pageAiSessionRequiresBackendDelete(session)) continue;
var response = await fetch('/api/hermes/client/sessions/' + encodeURIComponent(session.id) + '?' + pageAiBackendSessionQuery({}), {
var response = await fetch('/api/page-ai/sessions/' + encodeURIComponent(session.id) + '?' + pageAiBackendSessionQuery({}), {
method: 'DELETE',
headers: { 'accept': 'application/json' }
});
@@ -745,7 +752,7 @@ export function createSidebarPageAiSessionRuntime(context) {
sessionId = String(sessionId || pageUiState.pageAiActiveSessionId || '').trim();
if (!sessionId) return;
pageAiSetActiveSession(sessionId);
var response = await fetch('/api/hermes/client/sessions/' + encodeURIComponent(sessionId) + '/resume?' + pageAiBackendSessionQuery({}), {
var response = await fetch('/api/page-ai/sessions/' + encodeURIComponent(sessionId) + '/resume?' + pageAiBackendSessionQuery({}), {
method: 'POST',
headers: { 'accept': 'application/json' }
});
@@ -1684,11 +1684,12 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
}
var controller = typeof AbortController === 'function' ? new AbortController() : null;
var timer = 0;
var timeout = Math.max(1000, Number(timeoutMs) || 15000);
try {
if (controller) {
timer = window.setTimeout(function() {
controller.abort();
}, Math.max(1000, Number(timeoutMs) || 15000));
}, timeout);
}
var nextInit = Object.assign({}, init || {});
if (controller) nextInit.signal = controller.signal;
@@ -1703,6 +1704,16 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
}
}
function uploadTimeoutMsForFile(file, requestedMs) {
var runtimeFn = localUploadRuntimeFunction('uploadTimeoutMsForFile');
if (runtimeFn) return runtimeFn(file, requestedMs);
var requested = Number(requestedMs) || 0;
var size = Number(file && file.size || 0);
if (!Number.isFinite(size) || size <= 0) return Math.max(requested, 15000);
var sizeBased = 15000 + Math.ceil(size / (1024 * 1024)) * 12000;
return Math.min(10 * 60 * 1000, Math.max(requested, 60000, sizeBased));
}
async function insertUploadedAssetIntoEditor(asset, targetRoot) {
var runtimeFn = localUploadRuntimeFunction('insertUploadedAssetIntoEditor');
if (runtimeFn) {
@@ -1842,7 +1853,7 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
var localResult = await localUploadRuntime(file, plan, {
rootUri: rootUri,
documentId: documentId,
timeoutMs: 15000
timeoutMs: uploadTimeoutMsForFile(file, 15000)
});
localPayload = localResult && localResult.asset ? { asset: localResult.asset } : null;
} else {
@@ -1857,7 +1868,7 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
method: 'POST',
credentials: 'include',
body: localForm
}, 15000, '本地上传');
}, uploadTimeoutMsForFile(file, 15000), '本地上传');
localPayload = await localResponse.json().catch(function() { return null; });
if (!localResponse.ok || !localPayload || !localPayload.asset) {
throw new Error(localPayload && localPayload.error ? localPayload.error : '上传失败');