export function createSidebarPageAiRenderRuntime(context) { const { contextRefRegistry, cssEscape, currentDocumentId, documentRef, escapeHtml, localMarkdownRelativePathFromPageAiDocumentId, pageAiAgentDescriptor, pageAiAgentRecord, pageAiAllSkillEntries, pageAiBuildAllowedRoots, pageAiChatOnlyProfileEntries, pageAiChatOnlyProfileSpec, pageAiCurrentAgentId, pageAiCurrentProfile, pageAiCurrentProfileRecord, pageAiCurrentSession, pageAiCurrentSkillSource, pageAiCurrentSkillSourceLabel, pageAiDefaultChatOnlyProfileSpec, pageAiDescriptorFieldOptions, pageAiDescriptorFieldValue, pageAiEditorTargetCandidates, pageAiEnsureContextRefState, pageAiFilteredHistoryRows, pageAiHideHermesBuiltinSkills, pageAiHermesProfileEntries, pageAiHermesSettingsUrl, pageAiMnoteToolModel, pageAiNormalizeAcpRuntimes, pageAiNormalizeArray, pageAiNormalizeSkillSource, pageAiProfileDisplayLabel, pageAiProfileValue, pageAiProviderLabel, pageAiReasonixMemoryEnabled, pageAiRunProfile, pageAiSessionAgentFilterOptions, pageAiSessionAgentLabel, pageAiSessionAgentFilterValue, pageAiSessionStatusFilterOptions, pageAiSessionStatusFilterValue, pageAiSessionStatusLabel, pageAiSessionPreviewText, pageAiSessionStorageLabel, pageAiSessionSelected, pageAiSelectedSessionCount, pageAiSkillEnabled, pageAiSkillGroupCollapsed, pageAiSkillListEntries, pageAiSkillOriginLabel, pageAiSkillSourceOptions, pageAiSkillSourceParts, pageAiToggleableSkillEntries, pageAiUsageSummary, pageAiBoardFirstEnabled, pageAiBoardWorkerOptions, pageAiBoardWorkflowOptions, pageAiBoardSelectedWorkerId, pageAiBoardSelectedWorkflowId, pageAiBoardWorkerLabel, pageAiBoardWorkflowLabel, pageAiBoardModelOptions, pageAiBoardSelectedModelOverride, pageAiBoardModelLabel, pageUiState, renderPageAiMarkdown, searchText, currentPageAiEditorTarget, currentPageAiSelectedText, } = context; const PAGE_AI_CONTEXT_REF_REGISTRY = Array.isArray(contextRefRegistry) ? contextRefRegistry : []; function pageAiLocalBoardFirstEnabled() { try { return window.localStorage ? window.localStorage.getItem('mnote.page_ai.legacy_provider_mode') !== '1' : true; } catch (_error) { return true; } } function isPageAiBoardFirstEnabled() { return typeof pageAiBoardFirstEnabled === 'function' ? pageAiBoardFirstEnabled() : pageAiLocalBoardFirstEnabled(); } function pageAiTargetLabel(target) { var workspacePath = target && target.workspacePath && typeof target.workspacePath === 'object' ? target.workspacePath : {}; var kind = String(target && (target.resourceKind || target.editorKind) || workspacePath.resourceKind || '').trim(); var title = String(target && target.title || '').trim(); var relativePath = String(workspacePath.relativePath || target && target.path || '').trim(); if (title) return title; if (relativePath) return relativePath.split('/').filter(Boolean).pop() || relativePath; if (kind === 'mindmap') return '思维导图'; if (kind === 'only_office') return 'Office 资源'; return '当前页'; } function pageAiTargetDetail(target) { var workspacePath = target && target.workspacePath && typeof target.workspacePath === 'object' ? target.workspacePath : {}; var kind = String(target && (target.resourceKind || target.editorKind) || workspacePath.resourceKind || '').trim(); var pane = String(target && target.paneRole || '').trim(); var relativePath = String(workspacePath.relativePath || target && target.path || '').trim(); return [kind || 'page', pane, relativePath].filter(Boolean).join(' · '); } function pageAiContextRefLabel(kind) { var record = PAGE_AI_CONTEXT_REF_REGISTRY.find(function(ref) { return ref.id === kind; }); return record ? record.label : kind; } function pageAiContextButtonSummary() { var selected = pageAiEnsureContextRefState(); var labels = PAGE_AI_CONTEXT_REF_REGISTRY .filter(function(ref) { return selected[ref.id] === true; }) .map(function(ref) { return ref.label; }); if (!labels.length) return '选择上下文'; var head = labels.slice(0, 2).join(' + '); return labels.length > 2 ? head + ' +' + String(labels.length - 2) : head; } function pageAiContextRefDetail(kind) { var documentId = currentDocumentId(); var relativePath = localMarkdownRelativePathFromPageAiDocumentId(documentId); if (kind === 'current_page') return relativePath || documentId || '当前页面'; if (kind === 'selection') return currentPageAiSelectedText() ? '当前选区可用' : '当前没有选区'; if (kind === 'active_editor') { var target = currentPageAiEditorTarget(); return String(target && (target.title || target.documentId) || relativePath || '当前打开资源'); } if (kind === 'file') return relativePath || '当前文件'; if (kind === 'folder') { var roots = pageAiBuildAllowedRoots(); if (!roots.length) return '需要授权后可用'; return roots.map(function(root) { return root.rootUri.replace(/^file:\/\//, ''); }).filter(Boolean)[0] || '已授权文件夹'; } if (kind === 'changed_files') return '本次 run 后可用于追问'; return ''; } function pageAiContextRefDisabled(kind) { if (kind === 'selection') return !currentPageAiSelectedText(); return false; } function pageAiSuggestions() { var title = searchText(documentRef.querySelector('[data-page-title-current="true"]')?.textContent) || '当前页面'; return [ '帮我总结《' + title + '》当前内容', '把当前页面改写得更简洁一些', '提炼当前页的关键待办和行动项', '基于当前页内容生成一个三段式摘要' ]; } function pageAiCurrentAgentSelectionLabel() { var agentId = pageAiCurrentAgentId(); var descriptor = pageAiAgentDescriptor(agentId); var descriptorLabel = String(descriptor && (descriptor.displayName || descriptor.label) || '').trim(); if (agentId === 'reasonix') return descriptorLabel || 'Reasonix'; var profile = pageAiCurrentProfileRecord(); var chatOnlySpec = pageAiChatOnlyProfileSpec(profile || pageAiCurrentProfile()); if (agentId === 'chat_only') { return 'ChatOnly / ' + (chatOnlySpec || pageAiDefaultChatOnlyProfileSpec()).label; } if (chatOnlySpec) { return 'ChatOnly / ' + chatOnlySpec.label; } if (agentId === 'hermes') return (descriptorLabel || 'Hermes') + ' / ' + pageAiProfileDisplayLabel(profile, pageAiCurrentProfile()); return descriptorLabel || pageAiAgentRecord(agentId).label; } function pageAiRenderAgentProfileOption(agentId, profile, label, detail) { var profileId = pageAiProfileValue(profile); var active = pageAiCurrentAgentId() === agentId && profileId === pageAiCurrentProfile(); return '' + ''; } function pageAiAgentDescriptorDetail(descriptor) { if (!descriptor) return ''; var capabilities = pageAiNormalizeArray(descriptor.capabilities); var toolCount = Number(descriptor.toolCount || pageAiNormalizeArray(descriptor.tools).length || 0); var parts = []; parts.push(descriptor.canWriteFiles === false ? '只读/聊天' : '可写授权文件'); if (capabilities.length) parts.push('capability ' + capabilities.length); if (toolCount) parts.push('tool ' + toolCount); return parts.join(' · '); } function pageAiRenderDescriptorField(field) { var key = String(field && field.key || '').trim(); if (!key || field.secret === true) return ''; var label = String(field.label || key).trim(); var kind = String(field.kind || 'string').trim(); var value = pageAiDescriptorFieldValue(field); var options = pageAiDescriptorFieldOptions(field); var control = ''; if (kind === 'select' || options.length) { control = ''; } else if (kind === 'boolean' || kind === 'toggle') { control = ''; } else { control = ''; } return '' + ''; } 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 '' + ''; } 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 '' + '
' + fields + '
'; } function pageAiRenderAgentDescriptorCard(agentId, fallbackTitle) { var descriptor = pageAiAgentDescriptor(agentId) || pageAiAgentRecord(agentId); var title = String(descriptor && (descriptor.displayName || descriptor.label) || fallbackTitle || agentId).trim(); var fields = pageAiNormalizeArray(descriptor && descriptor.fields).map(pageAiRenderDescriptorField).filter(Boolean).join(''); var disabled = pageAiNormalizeArray(descriptor && descriptor.disabledCapabilities); var disabledText = disabled.length ? '
disabled: ' + escapeHtml(disabled.join(', ')) + '
' : ''; return '' + '
' + '
' + '
' + escapeHtml(title) + '
' + escapeHtml(pageAiAgentDescriptorDetail(descriptor) || 'descriptor fallback') + '
' + '
' + disabledText + (fields ? '
' + fields + '
' : '
当前 agent 没有可配置字段。
') + '
'; } 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; } function pageAiFormatChangedFiles(files) { return pageAiNormalizeArray(files).map(function(file) { var path = String(file && file.path || '').trim(); var changeType = String(file && file.changeType || file.change_type || 'modified').trim(); var summary = String(file && file.summary || '').trim(); var version = String(file && (file.version || file.revision || '') || '').trim(); var hashBefore = String(file && file.hashBefore || '').trim(); var hashAfter = String(file && file.hashAfter || '').trim(); if (!version && (hashBefore || hashAfter)) version = 'hash ' + [hashBefore || '0', hashAfter || '0'].join('→'); var modifiedBefore = Number(file && file.modifiedBeforeMs || 0) || 0; var modifiedAfter = Number(file && file.modifiedAfterMs || 0) || 0; if (!version && (modifiedBefore || modifiedAfter)) version = 'mtime ' + [String(modifiedBefore), String(modifiedAfter)].join('→'); var actor = [file && file.agentKind, file && file.actorType, file && file.actorId].map(function(value) { return String(value || '').trim(); }).filter(Boolean).join('/'); return [changeType, path, version, summary, actor].filter(Boolean).join(' · '); }).filter(Boolean).join('\n'); } function pageAiRunStatusLabel(status) { if (status === 'queued') return '排队中'; if (status === 'running') return '运行中'; if (status === 'tool_calling') return '调用工具'; if (status === 'completed') return '已完成'; if (status === 'failed') return '失败'; if (status === 'aborted') return '已停止'; return '空闲'; } function pageAiMemoryFileLabel(section) { if (section === 'soul') return 'SOUL.md'; if (section === 'user') return 'USER.md'; return 'MEMORY.md'; } function pageAiContextScopeLabel(scope) { if (scope === 'selection') return '当前选区'; if (scope === 'block') return '当前块'; if (scope === 'options') return '页面设置'; return '当前页'; } function pageAiFilteredSkillEntries() { var query = String(pageUiState.pageAiSkillQuery || '').trim().toLowerCase(); var parts = pageAiSkillSourceParts(pageAiCurrentSkillSource()); var entries = pageAiToggleableSkillEntries(parts.group, parts.profile); if (!entries.length) { entries = pageAiSkillListEntries().map(function(skill) { return Object.assign({}, skill, { group: parts.group, profile: parts.profile || '' }); }); } return entries.filter(function(skill) { if (parts.group === 'hermes' && pageAiHideHermesBuiltinSkills(parts.profile) && skill.builtin === true) return false; if (!query) return true; return String(skill.name || skill.id || '').toLowerCase().indexOf(query) >= 0 || String(skill.title || '').toLowerCase().indexOf(query) >= 0 || String(skill.description || '').toLowerCase().indexOf(query) >= 0 || String(skill.category || '').toLowerCase().indexOf(query) >= 0 || pageAiSkillOriginLabel(skill).toLowerCase().indexOf(query) >= 0; }); } 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 '
' + escapeHtml(row[0]) + '' + escapeHtml(row[1]) + '
'; }).join(''); if (pageUiState.pageAiRuntimeStatusError) { html += '
' + escapeHtml(pageUiState.pageAiRuntimeStatusError) + '
'; } if (logs.length) { html += logs.slice(-4).map(function(item) { return '
' + escapeHtml(String(item.kind || 'log')) + '' + escapeHtml(String(item.seq || '')) + '
'; }).join(''); } return html || '
Runtime status 尚未加载。
'; } 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 '
' + escapeHtml(row[0]) + '' + escapeHtml(String(row[1])) + '
'; }).join(''); } function pageAiSettingsTabsHtml(activeTab) { return [ ['agent', 'MNote'], ['reasonix-settings', 'Reasonix'], ['hermes-settings', 'Hermes'] ].map(function(tab) { var active = tab[0] === activeTab; return ''; }).join(''); } function pageAiStatusTabsHtml() { return '' + '' + '' + ''; } function renderPageAiProviderButtons() { var drawer = ensurePageAiDrawer(); if (drawer.getAttribute('data-page-ai-opencode-host') === 'true') return; var boardFirst = isPageAiBoardFirstEnabled(); var isAcp = pageUiState.pageAiAcpRuntime !== ''; drawer.querySelectorAll('[data-page-ai-provider]').forEach(function(button) { var provider = button.getAttribute('data-page-ai-provider') || 'hermes'; var active = provider === pageUiState.pageAiProvider; button.classList.toggle('is-active', active); button.setAttribute('aria-pressed', active ? 'true' : 'false'); }); var providerNode = drawer.querySelector('.wolai-page-ai-subtitle span:first-child'); if (providerNode instanceof HTMLElement) { providerNode.textContent = boardFirst ? 'Agent Board' : pageAiAgentRecord(pageAiCurrentAgentId()).label; } } function renderPageAiControls() { var drawer = ensurePageAiDrawer(); if (drawer.getAttribute('data-page-ai-opencode-host') === 'true') return; var boardFirst = isPageAiBoardFirstEnabled(); 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'); documentRef.documentElement.setAttribute('data-mnote-page-ai-agent-id', activeAgentId); var agentButton = drawer.querySelector('[data-page-ai-agent-button]'); var agentPopoverId = 'mnote-page-ai-agent-popover'; var activeAgentLabel = boardFirst && typeof pageAiBoardWorkerLabel === 'function' ? pageAiBoardWorkerLabel() + ' / ' + (typeof pageAiBoardModelLabel === 'function' ? pageAiBoardModelLabel() : '默认') : pageAiCurrentAgentSelectionLabel(); if (agentButton instanceof HTMLElement) { agentButton.textContent = boardFirst ? 'Board' : 'AI'; agentButton.setAttribute('aria-expanded', pageUiState.pageAiAgentPopoverOpen ? 'true' : 'false'); agentButton.setAttribute('aria-controls', agentPopoverId); agentButton.setAttribute('data-page-ai-agent-summary', activeAgentLabel); agentButton.setAttribute('aria-label', (boardFirst ? '运行控制:' : 'Agent:') + activeAgentLabel); agentButton.setAttribute('title', (boardFirst ? '运行控制:' : 'Agent:') + activeAgentLabel); } var agentPopover = drawer.querySelector('[data-page-ai-agent-popover]'); if (agentPopover instanceof HTMLElement) { agentPopover.id = agentPopoverId; agentPopover.hidden = !pageUiState.pageAiAgentPopoverOpen; if (boardFirst) { var selectedWorkerId = typeof pageAiBoardSelectedWorkerId === 'function' ? pageAiBoardSelectedWorkerId() : 'mnote-page-ai-zcode'; var selectedWorkflowId = typeof pageAiBoardSelectedWorkflowId === 'function' ? pageAiBoardSelectedWorkflowId() : 'builtin-mnote-page-ai-chat'; var workerOptions = typeof pageAiBoardWorkerOptions === 'function' ? pageAiBoardWorkerOptions() : []; var workflowOptions = typeof pageAiBoardWorkflowOptions === 'function' ? pageAiBoardWorkflowOptions() : []; var modelOptions = typeof pageAiBoardModelOptions === 'function' ? pageAiBoardModelOptions(selectedWorkerId) : []; var selectedModelOverride = typeof pageAiBoardSelectedModelOverride === 'function' ? pageAiBoardSelectedModelOverride() : 'zcode-default'; var workerSelect = workerOptions.length > 1 ? '' : '
Worker' + escapeHtml(typeof pageAiBoardWorkerLabel === 'function' ? pageAiBoardWorkerLabel() : 'MNote 页面 AI · ZCode') + '
'; var workflowSelect = workflowOptions.length > 1 ? '' : '
Workflow' + escapeHtml(typeof pageAiBoardWorkflowLabel === 'function' ? pageAiBoardWorkflowLabel() : 'MNote 页面 AI') + '
'; var modelSelect = ''; agentPopover.innerHTML = '' + '
' + 'Agent Board' + '' + '
' + '
' + workerSelect + modelSelect + workflowSelect + '
' + '
MNote 只发送 capability/envelope;provider、执行和 workflow 由 Agent Board 负责。
'; } else { var chatOnlyOptions = pageAiChatOnlyProfileEntries().map(function(profile) { var spec = pageAiChatOnlyProfileSpec(profile); var label = profile.menuLabel || (spec && spec.label) || pageAiProfileDisplayLabel(profile, ''); var providerKind = String(profile.providerKind || (spec && spec.providerKind) || '').trim(); var detail = providerKind === 'api-chat' ? 'API 聊天 · 不申请文件写权限' : '网页问答 · 不申请文件写权限'; return pageAiRenderAgentProfileOption('chat_only', profile, label, detail); }).join(''); var hermesOptions = pageAiHermesProfileEntries().map(function(profile) { var profileId = pageAiProfileValue(profile); var scope = profile.kind === 'shared' ? (profile.readonly ? '共享 · 只读配置' : '共享') : (profile.kind === 'personal' ? '个人 profile' : 'Hermes profile'); return pageAiRenderAgentProfileOption('hermes', profile, pageAiProfileDisplayLabel(profile, profileId), scope); }).join(''); var reasonixActive = activeAgentId === 'reasonix'; var reasonixDescriptor = pageAiAgentDescriptor('reasonix'); var reasonixLabel = String(reasonixDescriptor && (reasonixDescriptor.displayName || reasonixDescriptor.label) || 'Reasonix').trim() || 'Reasonix'; var reasonixDetail = pageAiAgentDescriptorDetail(reasonixDescriptor) || '可读取并在授权目录内写文件'; agentPopover.innerHTML = '' + '
' + '选择 Agent' + '' + '
' + '
' + '
ChatOnly
' + '
' + chatOnlyOptions + '
' + '
' + '
' + '
Hermes
' + '
' + hermesOptions + '
' + '
' + '
' + '
Reasonix
' + '
' + '' + '
' + '
'; } } var agentChip = drawer.querySelector('[data-page-ai-agent-chip]'); if (agentChip instanceof HTMLElement) { agentChip.textContent = boardFirst && typeof pageAiBoardWorkerLabel === 'function' ? pageAiBoardWorkerLabel() + ' / ' + (typeof pageAiBoardModelLabel === 'function' ? pageAiBoardModelLabel() : '默认') : activeAgentLabel; agentChip.setAttribute('title', boardFirst ? '当前 worker/model:' + agentChip.textContent : '当前 Agent:' + activeAgentLabel); } var contextButton = drawer.querySelector('[data-page-ai-context-button]'); var contextPopoverId = 'mnote-page-ai-context-popover'; if (contextButton instanceof HTMLElement) { var summary = pageAiContextButtonSummary(); contextButton.textContent = '⇅'; contextButton.setAttribute('aria-expanded', pageUiState.pageAiContextPopoverOpen ? 'true' : 'false'); contextButton.setAttribute('aria-controls', contextPopoverId); contextButton.setAttribute('data-page-ai-context-summary', summary); contextButton.setAttribute('aria-label', '上下文:' + summary); contextButton.setAttribute('title', '上下文:' + summary); } var contextPopover = drawer.querySelector('[data-page-ai-context-popover]'); if (contextPopover instanceof HTMLElement) { var selectedRefs = pageAiEnsureContextRefState(); contextPopover.id = contextPopoverId; contextPopover.hidden = !pageUiState.pageAiContextPopoverOpen; contextPopover.innerHTML = '' + '
' + '发送给 AI 的上下文' + '' + '
' + '
' + PAGE_AI_CONTEXT_REF_REGISTRY.map(function(ref) { var checked = selectedRefs[ref.id] === true; var disabled = pageAiContextRefDisabled(ref.id); return '' + ''; }).join('') + '
' + '
' + '授权区域' + '' + escapeHtml(pageAiBuildAllowedRoots().length ? pageAiBuildAllowedRoots().map(function(root) { return root.permission + ' · ' + root.rootUri.replace(/^file:\/\//, ''); }).join(' / ') : '未选择授权区域') + '' + '
'; } var targetButton = drawer.querySelector('[data-page-ai-target-button]'); var targetPopover = drawer.querySelector('[data-page-ai-target-popover]'); var targetChip = drawer.querySelector('[data-page-ai-target-chip]'); var targetPopoverId = 'mnote-page-ai-target-popover'; var targetCandidates = pageAiEditorTargetCandidates(); var activeTarget = currentPageAiEditorTarget(); var activeTargetLabel = pageAiTargetLabel(activeTarget); if (targetButton instanceof HTMLElement) { targetButton.textContent = '◎'; targetButton.setAttribute('aria-expanded', pageUiState.pageAiTargetPopoverOpen ? 'true' : 'false'); targetButton.setAttribute('aria-controls', targetPopoverId); targetButton.setAttribute('aria-label', '目标:' + activeTargetLabel); targetButton.setAttribute('title', '目标:' + activeTargetLabel); } if (targetChip instanceof HTMLElement) { targetChip.textContent = activeTargetLabel; targetChip.setAttribute('title', '当前目标:' + activeTargetLabel + ' · ' + pageAiTargetDetail(activeTarget)); } if (targetPopover instanceof HTMLElement) { targetPopover.id = targetPopoverId; targetPopover.hidden = !pageUiState.pageAiTargetPopoverOpen; targetPopover.innerHTML = '' + '
' + '选择写入目标' + '' + '
' + '
' + targetCandidates.map(function(target) { var targetId = String(target && target.targetId || '').trim(); var active = targetId && targetId === String(activeTarget && activeTarget.targetId || '').trim(); return '' + ''; }).join('') + '
'; } var allowedRootsNode = drawer.querySelector('[data-page-ai-allowed-roots]'); if (allowedRootsNode instanceof HTMLElement) { var allowedRoots = pageAiBuildAllowedRoots(); if (!allowedRoots.length) { var errorText = String(pageUiState.pageAiAllowedRootsError || '').trim(); allowedRootsNode.innerHTML = '' + escapeHtml(errorText || '未选择授权区域') + ''; } else { allowedRootsNode.innerHTML = allowedRoots.map(function(root) { var label = root.rootUri.replace(/^file:\/\//, '') || root.rootUri; return '' + escapeHtml(root.permission + ' · ' + label) + ''; }).join(''); } } // Populate ACP runtime dropdown var acpSelect = drawer.querySelector('[data-page-ai-acp-runtime]'); if (acpSelect instanceof HTMLSelectElement) { var runtimes = pageUiState.pageAiAcpRuntimes.length ? pageUiState.pageAiAcpRuntimes : pageAiNormalizeAcpRuntimes([]); acpSelect.innerHTML = runtimes.map(function(rt) { return ''; }).join(''); acpSelect.value = pageUiState.pageAiAcpRuntime || 'reasonix'; } // Show/hide Hermes-specific profile select var profileLabel = drawer.querySelector('[data-page-ai-hermes-profile]'); if (profileLabel instanceof HTMLElement) { profileLabel.style.display = pageUiState.pageAiAcpRuntime === 'reasonix' ? 'none' : ''; } // When ACP is selected, populate agent panel with ACP runtime info var agentPanel = drawer.querySelector('[data-page-ai-agent-panel]'); if (agentPanel instanceof HTMLElement) { agentPanel.innerHTML = '' + (pageUiState.pageAiAgentDescriptorError ? '
' + escapeHtml(pageUiState.pageAiAgentDescriptorError) + '
' : '') + '
' + '
MNote
上下文、权限、工具、会话历史和审计
' + '
这里配置 MNote 发送给 agent 的 envelope,不配置 Reasonix 或 Hermes 原生 runtime。
' + '
' + '
' + '
授权区域
SQLite directory_grants
' + '
' + '
' + '
' + '
默认上下文
ContextRefs
' + '
由 MNote 生成 run envelope,不写入 Reasonix/Hermes native memory。
' + '
' + '
' + '
会话与审计
history / export / changed files
' + '
删除会话只删除 MNote history;外部 Hermes/provider 会话不会默认删除。
' + '
'; 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 '' + escapeHtml(root.permission + ' · ' + label) + ''; }).join('') : '' + escapeHtml(String(pageUiState.pageAiAllowedRootsError || '').trim() || '未选择授权区域') + ''; } } var profileSummary = drawer.querySelector('[data-page-ai-profile-summary]'); if (profileSummary instanceof HTMLElement) profileSummary.textContent = '上下文可选'; var profileSummary = drawer.querySelector('[data-page-ai-profile-summary]'); if (profileSummary instanceof HTMLElement) profileSummary.textContent = '上下文可选'; drawer.querySelectorAll('[data-page-ai-profile-select]').forEach(function(profileSelect) { if (!(profileSelect instanceof HTMLSelectElement)) return; var profiles = pageUiState.pageAiProfiles.length ? pageUiState.pageAiProfiles : [{ name: activeProfile, active: true }]; profileSelect.innerHTML = profiles.map(function(profile) { var name = pageAiProfileValue(profile) || 'default'; var model = [profile.model, profile.gateway].filter(Boolean).join(' / '); var scope = profile.kind === 'shared' ? (profile.readonly ? 'shared · readonly' : 'shared · admin') : (profile.kind === 'personal' ? 'personal · 可配置' : ''); var label = [profile.alias || name, scope, model].filter(Boolean).join(' · '); return ''; }).join(''); profileSelect.value = activeProfile; }); var runStatus = drawer.querySelector('[data-page-ai-run-status]'); if (runStatus instanceof HTMLElement) { 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) { var workflowLabel = boardFirst && typeof pageAiBoardWorkflowLabel === 'function' ? pageAiBoardWorkflowLabel() : ''; runtimeBadge.textContent = boardFirst ? 'Agent Board · ' + workflowLabel : 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 = boardFirst ? 'provider 由 Board worker 决定' : 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 '
' + escapeHtml(String(job.id || job.name || 'job')) + '' + escapeHtml(String(job.status || 'running')) + '
'; }).join('') : '
暂无运行中的 job。
'; } 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 '
' + escapeHtml(String(item.kind || 'log')) + '' + escapeHtml(String(item.seq || '')) + '
'; }).join('') : '
暂无 runtime logs。
'; } var usageDetail = drawer.querySelector('[data-page-ai-usage-detail]'); if (usageDetail instanceof HTMLElement) usageDetail.innerHTML = pageAiUsageDetailHtml(); var canStop = ['queued', 'running', 'tool_calling'].indexOf(pageUiState.pageAiRunStatus) >= 0 && pageUiState.pageAiCurrentRunId; drawer.querySelectorAll('[data-page-ai-action="stop-run"]').forEach(function(stopButton) { if (!(stopButton instanceof HTMLButtonElement)) return; stopButton.disabled = !canStop; stopButton.setAttribute('aria-disabled', canStop ? 'false' : 'true'); }); var settingsLink = drawer.querySelector('[data-page-ai-action="open-hermes-settings"]'); if (settingsLink instanceof HTMLButtonElement) { settingsLink.disabled = !pageAiHermesSettingsUrl(); settingsLink.title = pageAiHermesSettingsUrl() ? '打开 Hermes 设置' : '未配置 MNOTE_WEB_HERMES_UPSTREAM_URL'; } var queueList = drawer.querySelector('[data-page-ai-queue-list]'); if (queueList instanceof HTMLElement) { if (!pageUiState.pageAiQueuedItems.length) { queueList.innerHTML = '
暂无排队项。
'; } else { queueList.innerHTML = pageUiState.pageAiQueuedItems.map(function(item, index) { var label = '队列 ' + (index + 1); var preview = String(item.preview || item.traceId || item.queueId || '').trim(); return '' + '
' + '
' + escapeHtml(label) + '
' + escapeHtml(preview || item.queueId) + '
' + '' + '
'; }).join(''); } } var sessionNode = drawer.querySelector('[data-page-ai-session-status]'); if (sessionNode instanceof HTMLElement) { var session = pageAiCurrentSession(); var usageText = session && session.usage ? pageAiUsageSummary(session.usage) : ''; sessionNode.textContent = session && session.id ? [String(session.title || '当前页问答'), pageAiSessionStorageLabel(session), usageText].filter(Boolean).join(' · ') : '等待 AI session'; } var modelNode = drawer.querySelector('[data-page-ai-model-status]'); 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>$/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]'); if (scopeLabel instanceof HTMLElement) scopeLabel.textContent = pageAiContextScopeLabel(pageUiState.pageAiContextScope); drawer.querySelectorAll('[data-page-ai-tab]').forEach(function(button) { var target = button.getAttribute('data-page-ai-tab') || 'chat'; var active = target === pageUiState.pageAiPage; button.classList.toggle('is-active', active); button.setAttribute('aria-selected', active ? 'true' : 'false'); }); drawer.querySelectorAll('[data-page-ai-panel]').forEach(function(panel) { if (panel instanceof HTMLElement) { panel.hidden = panel.getAttribute('data-page-ai-panel') !== pageUiState.pageAiPage; } }); if (boardFirst) { var subtitle = drawer.querySelector('.wolai-page-ai-subtitle'); if (subtitle instanceof HTMLElement) { var workerLabel = typeof pageAiBoardWorkerLabel === 'function' ? pageAiBoardWorkerLabel() : 'ZCode 默认 developer'; subtitle.innerHTML = 'Agent Board' + escapeHtml(workerLabel) + 'local-first 文件编辑'; } drawer.querySelectorAll('[data-page-ai-tab="agent"], [data-page-ai-tab="reasonix-settings"], [data-page-ai-tab="hermes-settings"]').forEach(function(node) { if (node instanceof HTMLElement) node.hidden = true; }); drawer.querySelectorAll('[data-page-ai-panel="agent"], [data-page-ai-panel="reasonix-settings"], [data-page-ai-panel="hermes-settings"]').forEach(function(node) { if (node instanceof HTMLElement) node.setAttribute('data-page-ai-legacy-provider-panel', 'true'); }); } var profileError = drawer.querySelector('[data-page-ai-profile-error]'); if (profileError instanceof HTMLElement) { profileError.textContent = pageUiState.pageAiProfileError || ''; profileError.hidden = !pageUiState.pageAiProfileError; } var memoryError = drawer.querySelector('[data-page-ai-memory-error]'); if (memoryError instanceof HTMLElement) { var memoryErrorText = pageUiState.pageAiProfileMemoryError || ''; if (memoryErrorText && memoryErrorText === pageUiState.pageAiProfileError) memoryErrorText = ''; memoryError.textContent = memoryErrorText; memoryError.hidden = !memoryErrorText; } var hermesMemoryPanel = drawer.querySelector('[data-page-ai-hermes-panel]'); if (hermesMemoryPanel instanceof HTMLElement) { hermesMemoryPanel.innerHTML = pageAiRenderAgentDescriptorCard('hermes', 'Hermes') + ['soul', 'user', 'memory'].map(function(section) { var label = pageAiMemoryFileLabel(section); var value = pageUiState.pageAiProfileMemoryDrafts[section] || ''; return '' + '
' + '
' + '
' + '
' + escapeHtml(label) + '
' + '
保存到 Hermes profile: ' + escapeHtml(activeProfile) + '
' + '
' + '' + '
' + '' + '
'; }).join(''); } var skillError = drawer.querySelector('[data-page-ai-skill-error]'); if (skillError instanceof HTMLElement) { skillError.textContent = pageUiState.pageAiSkillError || ''; skillError.hidden = !pageUiState.pageAiSkillError; } var sessionError = drawer.querySelector('[data-page-ai-session-error]'); if (sessionError instanceof HTMLElement) { sessionError.textContent = pageUiState.pageAiSessionError || ''; sessionError.hidden = !pageUiState.pageAiSessionError; } var selectedSessionCount = pageAiSelectedSessionCount(); var selectedSessionToolbar = drawer.querySelector('[data-page-ai-history-selection-toolbar]'); if (selectedSessionToolbar instanceof HTMLElement) { selectedSessionToolbar.hidden = selectedSessionCount <= 0; } var selectedSessionCountNode = drawer.querySelector('[data-page-ai-history-selection-count]'); if (selectedSessionCountNode instanceof HTMLElement) { selectedSessionCountNode.textContent = '已选 ' + String(selectedSessionCount) + ' 个'; } var sessionSearch = drawer.querySelector('[data-page-ai-session-search]'); if (sessionSearch instanceof HTMLInputElement && documentRef.activeElement !== sessionSearch) { sessionSearch.value = pageUiState.pageAiSessionSearchQuery || ''; } var sessionAgentFilter = drawer.querySelector('[data-page-ai-session-agent-filter]'); if (sessionAgentFilter instanceof HTMLSelectElement) { var historyRowsForFilter = pageUiState.pageAiSessionSearchResults.length ? pageUiState.pageAiSessionSearchResults : pageUiState.pageAiSessions; var options = pageAiSessionAgentFilterOptions(historyRowsForFilter); var activeFilter = String(pageUiState.pageAiSessionAgentFilter || 'all').trim() || 'all'; if (!options.some(function(option) { return option.value === activeFilter; })) activeFilter = 'all'; sessionAgentFilter.innerHTML = options.map(function(option) { return ''; }).join(''); 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 ''; }).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; } var skillSourceSelect = drawer.querySelector('[data-page-ai-skill-source-select]'); if (skillSourceSelect instanceof HTMLSelectElement) { var activeSkillSource = pageAiCurrentSkillSource(); var sourceOptions = pageAiSkillSourceOptions(); skillSourceSelect.innerHTML = sourceOptions.map(function(option) { return ''; }).join(''); skillSourceSelect.value = activeSkillSource; var sourceControl = skillSourceSelect.closest('[data-page-ai-skill-source-control]'); if (sourceControl instanceof HTMLElement) sourceControl.hidden = sourceOptions.length <= 1; } var hermesBuiltinToggle = drawer.querySelector('[data-page-ai-hide-hermes-builtin]'); if (hermesBuiltinToggle instanceof HTMLInputElement) { var skillSourceParts = pageAiSkillSourceParts(pageAiCurrentSkillSource()); var showHermesFilter = skillSourceParts.group === 'hermes'; var hermesFilterLabel = hermesBuiltinToggle.closest('.wolai-page-ai-skill-filter-toggle'); if (hermesFilterLabel instanceof HTMLElement) hermesFilterLabel.hidden = !showHermesFilter; hermesBuiltinToggle.checked = showHermesFilter && pageAiHideHermesBuiltinSkills(skillSourceParts.profile); } var skillList = drawer.querySelector('[data-page-ai-skill-list]'); if (skillList instanceof HTMLElement) { var skills = pageAiFilteredSkillEntries(); if (!skills.length) { skillList.innerHTML = '
没有匹配的能力。
'; } else { var sourceParts = pageAiSkillSourceParts(pageAiCurrentSkillSource()); var group = sourceParts.group; var headingExtra = group === 'hermes' ? ' · ' + sourceParts.profile : ''; var collapsed = pageAiSkillGroupCollapsed(pageAiCurrentSkillSource()); skillList.innerHTML = '' + '
' + '' + (collapsed ? '' : skills.map(function(skill) { var toolCount = Number(skill.toolCount || pageAiNormalizeArray(skill.tools).length || 0); var disabledToolCount = Number(skill.disabledToolCount || 0); var capabilityMeta = []; if (toolCount > 0) capabilityMeta.push(String(toolCount) + ' 工具'); if (disabledToolCount > 0) capabilityMeta.push(String(disabledToolCount) + ' 已关闭'); if (pageAiNormalizeArray(skill.requiresContextRefs).length > 0) capabilityMeta.push('需要上下文'); var categoryText = group === 'mnote' ? String(skill.categoryTitle || skill.category || '').trim() : ''; var sourceMetaParts = []; if (group === 'mnote') { sourceMetaParts.push(categoryText || 'MNote'); } else { sourceMetaParts.push(pageAiSkillOriginLabel(skill)); sourceMetaParts.push('只读查看'); } if (capabilityMeta.length) sourceMetaParts = sourceMetaParts.concat(capabilityMeta); if (skill.readOnly && group === 'mnote') sourceMetaParts.push('只读'); if (skill.modified) sourceMetaParts.push('modified'); var sourceText = sourceMetaParts.filter(Boolean).join(' · '); var description = String(skill.description || '').trim(); var hasDescription = description && description !== '---' && description !== '无描述'; var displayName = String(skill.title || skill.name || skill.id || '').trim(); var disabled = skill.toggleable === false || skill.readOnly === true; var switchControl = group === 'mnote' ? '' : '查看'; return '' + '
' + '
' + '
' + '
' + escapeHtml(displayName) + '
' + '
' + escapeHtml(sourceText) + '
' + '
' + (hasDescription ? '
' + escapeHtml(description) + '
' : '') + '
' + switchControl + '
'; }).join('')) + '
'; } } var toolsList = drawer.querySelector('[data-page-ai-tool-list]'); if (toolsList instanceof HTMLElement) { var tools = pageAiNormalizeArray(pageUiState.pageAiTools); if (!tools.length) { toolsList.innerHTML = '
尚未读取到 mnote tool manifest。
'; } else { toolsList.innerHTML = tools.map(function(tool) { return '' + '
' + '
' + '
' + escapeHtml(tool.name) + '
' + '
' + escapeHtml(tool.scope || tool.kind || 'mnote') + ' · ' + escapeHtml(tool.status || 'available') + '
' + (tool.unavailableReason ? '
' + escapeHtml(tool.unavailableReason) + '
' : '') + '
' + '' + '
'; }).join(''); } } var gatewayError = drawer.querySelector('[data-page-ai-gateway-error]'); if (gatewayError instanceof HTMLElement) { gatewayError.textContent = pageUiState.pageAiGatewayHealthError || ''; gatewayError.hidden = !pageUiState.pageAiGatewayHealthError; } var gatewayStatusNode = drawer.querySelector('[data-page-ai-gateway-status]'); var gatewayDetail = drawer.querySelector('[data-page-ai-gateway-detail]'); var gatewayHealth = pageUiState.pageAiGatewayHealth; if (gatewayStatusNode instanceof HTMLElement) { if (!gatewayHealth) { gatewayStatusNode.textContent = '未检查'; } else { var gateway = gatewayHealth.gateway || {}; var profile = gatewayHealth.profile || {}; gatewayStatusNode.textContent = gatewayHealth.ok ? '可用' : '需要设置'; if (gateway.status) gatewayStatusNode.textContent += ' · ' + gateway.status; if (profile.name) gatewayStatusNode.textContent += ' · ' + profile.name; } } if (gatewayDetail instanceof HTMLElement) { if (!gatewayHealth) { gatewayDetail.innerHTML = '
打开 Hermes 设置后会检查 gateway 与当前 profile。
'; } else { var gatewayInfo = gatewayHealth.gateway || {}; var profileInfo = gatewayHealth.profile || {}; var suggestionText = pageAiNormalizeArray(gatewayHealth.suggestions).join(';'); gatewayDetail.innerHTML = '' + '
' + '
' + escapeHtml(profileInfo.name || activeProfile) + '
' + '
model.default: ' + escapeHtml(profileInfo.modelDefault || '未设置') + '
' + '
provider: ' + escapeHtml(profileInfo.provider || '未设置') + ' · API key: ' + escapeHtml(profileInfo.apiKeyConfigured ? '已配置' : '未检测到') + '
' + '
' + '
' + '
' + escapeHtml(gatewayInfo.upstream || '未配置 upstream') + '
' + '
gateway: ' + escapeHtml(gatewayInfo.status || 'unknown') + (gatewayInfo.httpStatus ? ' · HTTP ' + escapeHtml(gatewayInfo.httpStatus) : '') + '
' + (suggestionText ? '
' + escapeHtml(suggestionText) + '
' : '') + '
'; } } var toolError = drawer.querySelector('[data-page-ai-tool-error]'); if (toolError instanceof HTMLElement) { toolError.textContent = pageUiState.pageAiToolsError || ''; toolError.hidden = !pageUiState.pageAiToolsError; } var lastTool = drawer.querySelector('[data-page-ai-last-tool]'); if (lastTool instanceof HTMLElement) { var call = pageUiState.pageAiLastToolCall; lastTool.textContent = call ? [call.event, call.name, call.runId, call.traceId || call.auditId].filter(Boolean).join(' · ') : '暂无 tool call'; } var reasonixPanel = drawer.querySelector('[data-page-ai-reasonix-panel]'); if (reasonixPanel instanceof HTMLElement) { reasonixPanel.innerHTML = '' + pageAiRenderAgentDescriptorCard('reasonix', 'Reasonix') + '
' + '
' + '
Reasonix 专属设置
ACP runtime / memory
' + '' + '
' + '
' + escapeHtml(pageAiReasonixMemoryEnabled() ? '当前用户已开启 Reasonix global/project memory。' : '默认关闭 Reasonix memory,避免跨用户偏好混入。') + '
' + '
'; } var chatOnlyPanel = drawer.querySelector('[data-page-ai-chat-only-panel]'); if (chatOnlyPanel instanceof HTMLElement) { chatOnlyPanel.innerHTML = pageAiRenderAgentDescriptorCard('chat_only', 'API Chat / Chat-only profile') + '
API Chat / Chat-only profile
默认不申请文件写权限,归属 MNote 设置。
'; } } function humanizePageAiResponse(rawText, promptText) { var providerLabel = pageAiProviderLabel(pageUiState.pageAiProvider); var text = String(rawText || '').trim(); if (!text) { return providerLabel + ' 已收到你的问题“' + searchText(promptText) + '”,但这次没有返回可读内容。'; } if (text.startsWith('{')) { try { var payload = JSON.parse(text); var operation = payload && payload.operation ? payload.operation : {}; var normalized = operation && operation.normalized_input ? operation.normalized_input : {}; var args = normalized && normalized.args ? normalized.args : {}; var documentId = searchText(args.documentId || args.pageId || currentDocumentId()); var toolName = searchText(operation.tool_name || normalized.toolName || 'doc_get'); return providerLabel + ' 已收到你的问题“' + searchText(promptText) + '”。当前已通过 Hermes 调用 mnote plugin 工具,目标页面是 ' + (documentId || '当前页面') + ',本次选择的工具是 ' + toolName + '。如果你继续追问,我会沿当前页面上下文继续回复。'; } catch (_) { return providerLabel + ' 已返回结果,但当前结果是结构化文本,已为你保留原始内容。'; } } return text; } function ensurePageAiDrawer() { var existing = documentRef.querySelector('[data-testid="wolai-page-ai-drawer"]'); if (existing instanceof HTMLElement) return existing; var drawer = documentRef.createElement('aside'); drawer.className = 'wolai-page-ai-drawer'; drawer.setAttribute('data-testid', 'wolai-page-ai-drawer'); drawer.setAttribute('data-mnote-surface', 'page-ai'); drawer.hidden = true; drawer.innerHTML = '' + '' + ''; documentRef.body.appendChild(drawer); return drawer; } function renderPageAiSuggestions() { var drawer = ensurePageAiDrawer(); var list = drawer.querySelector('[data-page-ai-panel="chat"] [data-page-ai-suggestion-list]'); if (!(list instanceof HTMLElement)) return; var container = list.closest('.wolai-page-ai-suggestions'); if (container instanceof HTMLElement) { container.hidden = pageUiState.pageAiPage !== 'chat' || pageUiState.pageAiMessages.length > 0; } var suggestions = pageAiSuggestions(); var offset = pageUiState.pageAiSuggestionIndex % suggestions.length; var ordered = suggestions.slice(offset).concat(suggestions.slice(0, offset)).slice(0, 3); list.innerHTML = ordered.map(function(text) { return ''; }).join(''); } function pageAiConversationShouldStickToBottom(conversation) { var distance = conversation.scrollHeight - conversation.scrollTop - conversation.clientHeight; return distance <= 48; } function pageAiCaptureOpenToolDetails(conversation) { var openToolDetails = {}; conversation.querySelectorAll('[data-page-ai-collapse-card], [data-page-ai-tool-card]').forEach(function(card) { if (!(card instanceof HTMLElement)) return; var key = String(card.getAttribute('data-page-ai-collapse-id') || card.getAttribute('data-page-ai-tool-group-id') || card.getAttribute('data-page-ai-tool-call-id') || '').trim(); var details = card.querySelector('[data-page-ai-collapse-details], .wolai-page-ai-tool-details'); if (key && details instanceof HTMLDetailsElement && details.open) { openToolDetails[key] = true; } }); return openToolDetails; } function pageAiRestoreOpenToolDetails(conversation, openToolDetails) { if (!openToolDetails || typeof openToolDetails !== 'object') return; conversation.querySelectorAll('[data-page-ai-collapse-card], [data-page-ai-tool-card]').forEach(function(card) { if (!(card instanceof HTMLElement)) return; var key = String(card.getAttribute('data-page-ai-collapse-id') || card.getAttribute('data-page-ai-tool-group-id') || card.getAttribute('data-page-ai-tool-call-id') || '').trim(); if (!key || openToolDetails[key] !== true) return; var details = card.querySelector('[data-page-ai-collapse-details], .wolai-page-ai-tool-details'); if (details instanceof HTMLDetailsElement) details.open = true; }); } function pageAiRenderToolDetailRows(item) { var locationRows = Array.isArray(item.locations) && item.locations.length ? '
位置:' + item.locations.map(function(loc) { return '' + '' + escapeHtml(loc) + '' + '' + ''; }).join('') + '
' : ''; return [ locationRows, item.argsSummary ? '
参数 ' + escapeHtml(item.argsSummary) + '
' : '', item.resultSummary ? '
结果 ' + escapeHtml(item.resultSummary) + '
' : '', item.changedFiles && item.changedFiles.length ? '
' + escapeHtml(pageAiFormatChangedFiles(item.changedFiles)) + '
' : '', (item.traceId || item.auditId) ? '
' + escapeHtml([item.traceId, item.auditId].filter(Boolean).join(' · ')) + '
' : '' ].filter(Boolean).join(''); } function pageAiToolStatusLabel(status) { if (status === 'completed') return '完成'; if (status === 'failed') return '失败'; return '运行中'; } function pageAiToolGroupStatus(items) { if (items.some(function(item) { return item.status === 'failed'; })) return 'failed'; if (items.some(function(item) { return item.status !== 'completed'; })) return 'running'; return 'completed'; } function pageAiRenderToolGroup(items, groupIndex) { var tools = pageAiNormalizeArray(items); if (!tools.length) return ''; var status = pageAiToolGroupStatus(tools); var completedCount = tools.filter(function(item) { return item.status === 'completed'; }).length; var failedCount = tools.filter(function(item) { return item.status === 'failed'; }).length; var runningCount = tools.length - completedCount - failedCount; var countParts = [String(tools.length) + ' 个']; if (completedCount) countParts.push('完成 ' + String(completedCount)); if (failedCount) countParts.push('失败 ' + String(failedCount)); if (runningCount) countParts.push('运行中 ' + String(runningCount)); var firstToolId = String(tools[0] && tools[0].toolCallId || '').trim(); var groupId = 'tool-group-' + String(groupIndex) + '-' + (firstToolId || String(tools.length)); var rows = tools.map(function(item) { var statusLabel = pageAiToolStatusLabel(item.status); var meta = [statusLabel, item.toolKind, item.toolCallId].filter(Boolean).join(' · '); return '' + '
' + '
' + '' + escapeHtml(item.toolName || item.content || 'tool') + '' + '' + escapeHtml(meta) + '' + '
' + (pageAiRenderToolDetailRows(item) || '
暂无参数或结果详情。
') + '
'; }).join(''); return '' + '
' + '
工具
' + '
' + '
' + '' + '调用工具' + '' + escapeHtml(countParts.join(' · ')) + '' + '' + '
' + rows + '
' + '
' + '
' + '
'; } function pageAiRenderThoughtGroup(items, groupIndex) { var thoughts = pageAiNormalizeArray(items).filter(function(item) { return item && item.kind === 'thought' && String(item.content || '').trim(); }); if (!thoughts.length) return ''; var firstText = String(thoughts[0] && thoughts[0].content || '').slice(0, 24).replace(/\s+/g, ' ').trim(); var groupId = 'thought-group-' + String(groupIndex) + '-' + String(thoughts.length) + '-' + (firstText || 'delta'); var content = thoughts.map(function(item) { return String(item.content || '').trim(); }).filter(Boolean).join('\n\n'); var countLabel = thoughts.length > 1 ? String(thoughts.length) + ' 段' : '1 段'; return '' + '
' + '
AI
' + '
' + '
' + '' + '思考过程' + '' + escapeHtml(countLabel) + '' + '' + '
' + escapeHtml(content) + '
' + '
' + '
' + '
'; } 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 ? ( '
    ' + steps.map(function(step) { return '' + '
  1. ' + '' + escapeHtml(pageAiPlanStepIcon(step.status)) + '' + '' + escapeHtml(step.text || '') + '' + '
  2. '; }).join('') + '
' ) : ''; return '' + '
' + '
' + '
' + 'Reasonix' + '执行计划' + '
' + '待批准' + '
' + '
' + renderPageAiMarkdown(text) + '
' + stepHtml + '
' + '' + '' + '' + '
' + '
'; } 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 '
' + rows.slice(0, 6).map(function(row) { return '
' + escapeHtml(row.label || '') + '' + escapeHtml(row.value || '') + '
'; }).join('') + '
'; } function pageAiRenderNonToolMessage(item) { var roleLabel = item.role === 'user' ? '你' : (item.role === 'tool' ? '工具' : 'AI'); if (item.kind === 'status') { return '' + '
' + '
AI
' + '
' + escapeHtml(item.content || '') + '
' + '
'; } if (item.kind === 'thought') { return '' + '
' + '
AI
' + '
' + '
' + '思考过程1 段' + '
' + escapeHtml(item.content || '') + '
' + '
' + '
' + '
'; } if (item.kind === 'permission') { var permissionOptions = Array.isArray(item.options) ? item.options : []; var permissionActions = ''; if (!item.resolved) { if (permissionOptions.length) { permissionActions = '
' + 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 ''; }).join('') + '
'; } else { permissionActions = '' + '
' + '' + '' + '
'; } } return '' + '
' + '
' + '
' + '权限' + '' + escapeHtml(item.toolName || 'session/request_permission') + '' + '
' + '' + escapeHtml(item.resolved ? (item.decision === 'allow' || item.decision === 'allowed' ? '已允许' : '已处理') : '待确认') + '' + '
' + '
' + pageAiRenderPermissionDetails(item) + permissionActions + '
' + '
'; } if (item.kind === 'plan') { var planEntries = Array.isArray(item.entries) ? item.entries : []; var listHtml = planEntries.map(function(entry) { return '
  • ' + escapeHtml(String(entry || '')) + '
  • '; }).join(''); return '' + '
    ' + '
    ' + '执行计划 · ' + planEntries.length + ' 步' + '
    ' + '
      ' + listHtml + '
    ' + '
    ' + '
    ' + '
    '; } if (pageAiLooksLikeReasonixPlanMessage(item)) return pageAiRenderReasonixPlanCard(item); var streamingAttr = item.streaming ? ' data-page-ai-streaming="true"' : ''; var detailButton = item.boardRunId ? '
    ' + (item.retryPrompt ? '' : '') + '
    ' : ''; return '' + '
    ' + '
    ' + escapeHtml(roleLabel) + '
    ' + '
    ' + (item.role === 'assistant' ? renderPageAiMarkdown(item.content || '') : escapeHtml(item.content || '')) + '
    ' + detailButton + '
    '; } function pageAiConversationRenderHtml(messages) { var turns = []; var current = null; function ensureTurn() { if (!current) current = { user: null, tools: [], status: [], assistants: [], others: [] }; return current; } function flushTurn() { if (!current) return; turns.push(current); current = null; } pageAiNormalizeArray(messages).forEach(function(item) { if (item && item.role === 'user') { flushTurn(); current = { user: item, tools: [], status: [], assistants: [], others: [] }; return; } var turn = ensureTurn(); if (item && item.role === 'tool' && item.kind !== 'permission') { turn.tools.push(item); } else if (item && (item.kind === 'thought' || item.kind === 'plan' || item.kind === 'permission')) { turn.status.push(item); } else if (item && item.role === 'assistant') { turn.assistants.push(item); } else { turn.others.push(item); } }); flushTurn(); var toolGroupIndex = 0; var thoughtGroupIndex = 0; return turns.map(function(turn) { var html = []; if (turn.user) html.push(pageAiRenderNonToolMessage(turn.user)); if (turn.tools.length) html.push(pageAiRenderToolGroup(turn.tools, toolGroupIndex++)); var thoughts = turn.status.filter(function(item) { return item && item.kind === 'thought'; }); var otherStatus = turn.status.filter(function(item) { return !(item && item.kind === 'thought'); }); if (thoughts.length) html.push(pageAiRenderThoughtGroup(thoughts, thoughtGroupIndex++)); html = html.concat(otherStatus.map(pageAiRenderNonToolMessage)); html = html.concat(turn.assistants.map(pageAiRenderNonToolMessage)); html = html.concat(turn.others.map(pageAiRenderNonToolMessage)); return html.join(''); }).join(''); } function pageAiHistoryTextKey(value) { return String(value || '') .replace(/\s+/g, ' ') .replace(/[.…]+$/g, '') .trim(); } function pageAiHistoryPreviewIsDuplicate(title, preview) { var titleKey = pageAiHistoryTextKey(title); var previewKey = pageAiHistoryTextKey(preview); if (!titleKey || !previewKey) return false; return titleKey === previewKey || previewKey.indexOf(titleKey) === 0 || titleKey.indexOf(previewKey) === 0; } function pageAiBoardRunDetailHtml(runId) { var current = typeof pageAiCurrentSession === 'function' ? pageAiCurrentSession() : null; var detail = pageUiState.pageAiBoardRunDetails && pageUiState.pageAiBoardRunDetails[runId] || current && current.boardRuns && current.boardRuns[runId] || null; if (!detail) return '
    暂无 run detail。
    '; var changedFiles = pageAiNormalizeArray(detail.changedFiles).map(function(file) { return '
  • ' + escapeHtml(String(file.path || file || '')) + (file.status ? ' · ' + escapeHtml(file.status) : '') + '
  • '; }).join(''); var verification = pageAiNormalizeArray(detail.verification).map(function(item) { return '
  • ' + escapeHtml([item.command, item.status, item.output].filter(Boolean).join(' · ')) + '
  • '; }).join(''); var timeline = pageAiNormalizeArray(detail.timeline).map(function(event) { return '
  • ' + escapeHtml([event.type, event.message, event.timestamp].filter(Boolean).join(' · ')) + '
  • '; }).join(''); return '' + '
    ' + '
    ' + '
    Run detail' + escapeHtml(detail.runId || runId) + '
    ' + '' + escapeHtml(detail.status || '') + '' + '
    ' + '
    ' + '
    Worker' + escapeHtml(detail.workerName || detail.workerPresetId || '') + '
    ' + '
    Model' + escapeHtml(detail.modelLabel || detail.modelOverride || '') + '
    ' + '
    Workflow' + escapeHtml(detail.workflowName || detail.workflowId || '') + '
    ' + '
    ' + (changedFiles ? '
    changed files' + String(pageAiNormalizeArray(detail.changedFiles).length) + '
      ' + changedFiles + '
    ' : '') + (verification ? '
    verification' + String(pageAiNormalizeArray(detail.verification).length) + '
      ' + verification + '
    ' : '') + (timeline ? '
    timeline' + String(pageAiNormalizeArray(detail.timeline).length) + '
      ' + timeline + '
    ' : '') + '
    '; } function renderPageAiConversation() { var drawer = ensurePageAiDrawer(); renderPageAiSuggestions(); var conversation = drawer.querySelector('[data-page-ai-panel="' + cssEscape(pageUiState.pageAiPage || 'chat') + '"] [data-page-ai-conversation]'); if (!(conversation instanceof HTMLElement)) return; if (pageUiState.pageAiPage === 'history') { var sourceRows = pageUiState.pageAiSessionSearchResults.length ? pageUiState.pageAiSessionSearchResults : pageUiState.pageAiSessions; var historyRows = pageAiFilteredHistoryRows(sourceRows); if (!historyRows.length) { conversation.innerHTML = '
    没有匹配的历史会话。
    '; return; } conversation.innerHTML = historyRows.map(function(session) { var title = String(session.title || '新会话').trim() || '新会话'; var preview = pageAiSessionPreviewText(session); var previewHtml = pageAiHistoryPreviewIsDuplicate(title, preview) ? '' : '' + escapeHtml(preview) + ''; var active = session.id === pageUiState.pageAiActiveSessionId; var selected = pageAiSessionSelected(session.id); var usage = pageAiUsageSummary(session.usage); var agentLabel = pageAiSessionAgentLabel(session); var statusValue = pageAiSessionStatusFilterValue(session); var meta = [agentLabel, pageAiSessionStorageLabel(session), session.runtimeMode, session.permissionLevel, session.shareId, pageAiSessionStatusLabel(statusValue), usage].filter(Boolean).join(' · '); return '' + '
    ' + '' + '' + '
    ' + '' + '' + '' + '' + '
    ' + '
    '; }).join(''); return; } if (!pageUiState.pageAiMessages.length) { conversation.innerHTML = '
    围绕当前页面提问,我会优先使用当前页内容、页面结构和已保存设置。
    '; return; } var shouldStickToBottom = pageAiConversationShouldStickToBottom(conversation); var previousScrollTop = conversation.scrollTop; var openToolDetails = pageAiCaptureOpenToolDetails(conversation); conversation.innerHTML = pageAiConversationRenderHtml(pageUiState.pageAiMessages); if (pageUiState.pageAiBoardActiveDetailRunId) { conversation.insertAdjacentHTML('beforeend', pageAiBoardRunDetailHtml(pageUiState.pageAiBoardActiveDetailRunId)); } pageAiRestoreOpenToolDetails(conversation, openToolDetails); if (shouldStickToBottom) { conversation.scrollTop = conversation.scrollHeight; } else { var maxScrollTop = Math.max(0, conversation.scrollHeight - conversation.clientHeight); conversation.scrollTop = Math.min(previousScrollTop, maxScrollTop); } } return { ensurePageAiDrawer, humanizePageAiResponse, pageAiContextButtonSummary, pageAiContextRefDetail, pageAiContextRefDisabled, pageAiContextRefLabel, pageAiCurrentAgentSelectionLabel, pageAiCurrentModelLabel, pageAiFilteredSkillEntries, pageAiFormatChangedFiles, pageAiMemoryFileLabel, pageAiRenderAgentProfileOption, pageAiRunStatusLabel, pageAiSuggestions, pageAiTargetDetail, pageAiTargetLabel, pageAiContextScopeLabel, renderPageAiControls, renderPageAiConversation, renderPageAiProviderButtons, renderPageAiSuggestions, }; }