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:
@@ -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, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
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: '已重置 runtime;MNote 会话历史保留,外部 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 : '上传失败');
|
||||
|
||||
Reference in New Issue
Block a user