export function createSidebarPageAiPermissionRuntime(context) { const { documentRef, pageAiPreviewValue, pageUiState, renderPageAiConversation, } = 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 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: detail.summary || pageAiPreviewValue(args), content: decision === 'denied' ? '已自动拒绝权限请求' : (decision === 'allowed' ? '已自动允许权限请求' : '等待权限确认'), resolved: decision === 'denied' || decision === 'allowed', 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 { payload = JSON.parse(payloadText || 'null'); } catch (_) { payload = {}; } var message = pageAiPermissionMessage(payload, eventName); var existing = pageUiState.pageAiMessages.find(function(item) { return item.kind === 'permission' && item.permissionId === message.permissionId; }); if (existing) { Object.assign(existing, message); } else { pageUiState.pageAiMessages.push(message); } 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 { pageAiHidePermissionDialog(); } } 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 = 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(body) }).then(function(response) { if (!response.ok) console.warn('resolve-permission 后端返回异常', response.status); }).catch(function(err) { console.warn('resolve-permission 请求失败', err); }); } else { console.warn('resolve-permission: 无活跃 runId,只能本地更新'); } pageUiState.pageAiMessages.forEach(function(item) { if (item.kind === 'permission' && item.permissionId === permissionId) { item.resolved = true; item.decision = decision; item.content = decision === 'allow' ? '已允许权限请求' : '已拒绝权限请求'; } }); var dialog = doc.querySelector('[data-page-ai-permission-dialog]'); if (dialog instanceof HTMLElement) dialog.hidden = true; pageAiPersistPermissionRequests(); renderPageAiConversation(); } function pageAiHidePermissionDialog() { var dialog = doc.querySelector('[data-page-ai-permission-dialog]'); if (dialog instanceof HTMLElement) dialog.hidden = true; } function pageAiShowPermissionDialog(message) { if (!message || message.kind !== 'permission') return; if (message.resolved) { pageAiHidePermissionDialog(); return; } var dialog = doc.querySelector('[data-page-ai-permission-dialog]'); if (!(dialog instanceof HTMLElement)) { dialog = doc.createElement('div'); dialog.className = 'wolai-page-ai-permission-dialog'; dialog.setAttribute('data-page-ai-permission-dialog', 'true'); dialog.innerHTML = '' + '
'; doc.body.appendChild(dialog); } var tool = dialog.querySelector('[data-page-ai-permission-tool]'); 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.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 ''; } return rows.slice(0, 6).map(function(row) { return ''; }).join(''); } function pageAiPermissionActionsHtml(message) { var options = Array.isArray(message.options) ? message.options : []; if (!options.length) { return '' + '' + ''; } return options.map(function(option) { var denyLike = pageAiPermissionOptionRejectLike(option); return ''; }).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, '''); } return { pageAiPermissionMessage, pageAiApplyPermissionEvent, pageAiResolvePermission, pageAiRestorePermissionRequests, pageAiHidePermissionDialog, pageAiShowPermissionDialog }; }