import { createSidebarPageAiMarkdownRuntime } from './sidebar-page-ai-markdown-runtime.js'; import { createSidebarPageAiPermissionRuntime } from './sidebar-page-ai-permission-runtime.js'; import { createSidebarPageAiProfileRuntime } from './sidebar-page-ai-profile-runtime.js'; import { createSidebarPageAiRenderRuntime } from './sidebar-page-ai-render-runtime.js'; import { createSidebarPageAiSessionRuntime } from './sidebar-page-ai-session-runtime.js'; import { createSidebarPageAiSkillRuntime } from './sidebar-page-ai-skill-runtime.js'; import { createSidebarPageAiTargetRuntime } from './sidebar-page-ai-target-runtime.js'; export function createSidebarPageAiRuntime(context) { const { buildLocalFileOpenUrl, currentDocumentId, currentPageAggregate, currentPageOptions, currentRootUri, currentSourceKind, escapeHtml, cssEscape, openLocalResourceInActiveTab, pageUiState, resolveWorkspaceId, searchText, } = context; var PAGE_AI_SESSION_STORAGE_VERSION = 3; // Page AI 请求合同:agentId 只负责路由;contextRefs 是用户勾选的上下文地址; // allowedRoots 只展示 SQLite directory_grants 解析结果,服务端仍会按当前用户重算; // runTargetSnapshot 必须来自 OpenEditorsSnapshot,避免运行中 UI 切换导致目标漂移。 var PAGE_AI_AGENT_REGISTRY = [ { id: 'hermes', label: 'Hermes', acpRuntime: 'hermes', canWriteFiles: true }, { id: 'reasonix', label: 'Reasonix', acpRuntime: 'reasonix', canWriteFiles: true }, { id: 'chat_only', label: 'Chat-only', acpRuntime: 'hermes', canWriteFiles: false } ]; var PAGE_AI_CHAT_ONLY_PROFILE_REGISTRY = [ { profileId: 'shared_deepseek_chat', baseProfile: 'deepseek-chat', label: 'DeepSeek' }, { profileId: 'shared_doubao_chat', baseProfile: 'doubao-chat', label: '豆包' }, { profileId: 'shared_gemini_chat', baseProfile: 'gemini-chat', label: 'Gemini' } ]; var PAGE_AI_CONTEXT_REF_REGISTRY = [ { id: 'current_page', label: '当前页' }, { id: 'selection', label: '选区' }, { id: 'active_editor', label: '打开资源' }, { id: 'file', label: '文件' }, { id: 'folder', label: '文件夹' }, { id: 'changed_files', label: '最近修改' } ]; var pageAiDelegatesInstalled = false; function clonePageAiDefaultValue(value) { if (Array.isArray(value)) return value.slice(); if (value && typeof value === 'object') return Object.assign({}, value); return value; } function ensurePageAiStateFacade(state) { var defaults = { pageAiOpen: false, pageAiBusy: false, pageAiMessages: [], pageAiSuggestionIndex: 0, pageAiProvider: 'hermes', pageAiPage: 'chat', pageAiRunStatus: 'idle', pageAiCurrentRunId: '', pageAiAcpRuntime: 'reasonix', pageAiAcpRuntimes: [], pageAiQueueLength: 0, pageAiQueuedItems: [], pageAiStoppedRunIds: {}, pageAiAbortController: null, pageAiContextScope: 'page', pageAiContextPopoverOpen: false, pageAiTargetPopoverOpen: false, pageAiSelectedTargetId: '', pageAiSelectedContextRefs: null, pageAiCurrentRunTargetSnapshot: null, pageAiAllowedRoots: [], pageAiAllowedRootsError: '', pageAiAgentId: 'reasonix', pageAiAgentPopoverOpen: false, pageAiTools: [], pageAiToolsError: '', pageAiGatewayHealth: null, pageAiGatewayHealthError: '', pageAiLastToolCall: null, pageAiProfiles: [], pageAiActiveProfileName: 'mnoteai', pageAiProfileError: '', pageAiProfileMemory: { memory: '', user: '', soul: '' }, pageAiProfileMemoryDrafts: { memory: '', user: '', soul: '' }, pageAiProfileMemoryError: '', pageAiSkills: { categories: [], archived: [] }, pageAiSkillCatalogs: { mnote: { categories: [], archived: [] }, reasonix: { categories: [], archived: [] }, hermes: { categories: [], archived: [] } }, pageAiSkillPreferences: {}, pageAiCollapsedSkillGroups: {}, pageAiSkillQuery: '', pageAiSkillError: '', pageAiSkillLoadSeq: 0, pageAiActiveSkillSource: 'mnote', pageAiSessions: [], pageAiActiveSessionId: '', pageAiSessionSearchQuery: '', pageAiSessionSearchResults: [], pageAiSessionSearchTimer: 0, pageAiSessionAgentFilter: 'all', pageAiSessionError: '', pageAiPermissionRequests: [] }; Object.keys(defaults).forEach(function(key) { if (typeof state[key] === 'undefined') { state[key] = clonePageAiDefaultValue(defaults[key]); } }); if (!state.pageAiSkillCatalogs || typeof state.pageAiSkillCatalogs !== 'object') { state.pageAiSkillCatalogs = clonePageAiDefaultValue(defaults.pageAiSkillCatalogs); } if (!state.pageAiProfileMemory || typeof state.pageAiProfileMemory !== 'object') { state.pageAiProfileMemory = clonePageAiDefaultValue(defaults.pageAiProfileMemory); } if (!state.pageAiProfileMemoryDrafts || typeof state.pageAiProfileMemoryDrafts !== 'object') { state.pageAiProfileMemoryDrafts = clonePageAiDefaultValue(defaults.pageAiProfileMemoryDrafts); } if (!state.pageAiSkills || typeof state.pageAiSkills !== 'object') { state.pageAiSkills = clonePageAiDefaultValue(defaults.pageAiSkills); } return state; } ensurePageAiStateFacade(pageUiState); function closestAction(target, selector) { var node = target && target.nodeType === Node.TEXT_NODE ? target.parentElement : target; return node && typeof node.closest === 'function' ? node.closest(selector) : null; } const pageAiMarkdown = createSidebarPageAiMarkdownRuntime({ escapeHtml }); const textFromUnknown = (...args) => pageAiMarkdown.textFromUnknown(...args); const renderPageAiMarkdown = (...args) => pageAiMarkdown.renderPageAiMarkdown(...args); let pageAiRenderRuntime = null; function pageAiRenderApi() { if (!pageAiRenderRuntime) throw new Error('page_ai_render_runtime_not_ready'); return pageAiRenderRuntime; } function ensurePageAiDrawer(...args) { return pageAiRenderApi().ensurePageAiDrawer(...args); } function humanizePageAiResponse(...args) { return pageAiRenderApi().humanizePageAiResponse(...args); } function pageAiContextButtonSummary(...args) { return pageAiRenderApi().pageAiContextButtonSummary(...args); } function pageAiContextRefDetail(...args) { return pageAiRenderApi().pageAiContextRefDetail(...args); } function pageAiContextRefDisabled(...args) { return pageAiRenderApi().pageAiContextRefDisabled(...args); } function pageAiContextRefLabel(...args) { return pageAiRenderApi().pageAiContextRefLabel(...args); } function pageAiContextScopeLabel(...args) { return pageAiRenderApi().pageAiContextScopeLabel(...args); } function pageAiCurrentAgentSelectionLabel(...args) { return pageAiRenderApi().pageAiCurrentAgentSelectionLabel(...args); } function pageAiCurrentModelLabel(...args) { return pageAiRenderApi().pageAiCurrentModelLabel(...args); } function pageAiFilteredSkillEntries(...args) { return pageAiRenderApi().pageAiFilteredSkillEntries(...args); } function pageAiFormatChangedFiles(...args) { return pageAiRenderApi().pageAiFormatChangedFiles(...args); } function pageAiMemoryFileLabel(...args) { return pageAiRenderApi().pageAiMemoryFileLabel(...args); } function pageAiRenderAgentProfileOption(...args) { return pageAiRenderApi().pageAiRenderAgentProfileOption(...args); } function pageAiRunStatusLabel(...args) { return pageAiRenderApi().pageAiRunStatusLabel(...args); } function pageAiSuggestions(...args) { return pageAiRenderApi().pageAiSuggestions(...args); } function pageAiTargetDetail(...args) { return pageAiRenderApi().pageAiTargetDetail(...args); } function pageAiTargetLabel(...args) { return pageAiRenderApi().pageAiTargetLabel(...args); } function renderPageAiControls(...args) { return pageAiRenderApi().renderPageAiControls(...args); } function renderPageAiConversation(...args) { return pageAiRenderApi().renderPageAiConversation(...args); } function renderPageAiProviderButtons(...args) { return pageAiRenderApi().renderPageAiProviderButtons(...args); } function renderPageAiSuggestions(...args) { return pageAiRenderApi().renderPageAiSuggestions(...args); } function readLocalEditorBlocks() { var editor = document.querySelector('[data-testid="mnote-leptos-tiptap-island-editor-root"] .editor-surface .ProseMirror'); if (!(editor instanceof HTMLElement)) return []; return Array.from(editor.children).filter(function(node) { return node instanceof HTMLElement; }).map(function(node, index) { var tag = String(node.tagName || '').toUpperCase(); var headingMatch = tag.match(/^H([1-6])$/); var type = headingMatch ? 'heading' : 'paragraph'; var text = searchText(node.textContent || ''); var id = searchText(node.getAttribute('data-id') || node.id || 'local-block-' + index); var props = headingMatch ? { level: Number(headingMatch[1]) } : {}; return { id: id, type: type, props: props, content: text }; }).filter(function(block) { return block.content || block.type === 'heading'; }); } function buildPageAiLocalSubtree(blocks, title) { var documentId = currentDocumentId() || 'current-page'; var rootNodeId = 'page:' + documentId; var headingCounters = [0, 0, 0, 0, 0, 0]; var headingStack = []; var nodes = [{ id: rootNodeId, nodeId: rootNodeId, nodeType: 'page', blockId: null, blockType: 'page', title: title || '', parentNodeId: null, headingLevel: null }]; var outline = []; var evidence = []; blocks.forEach(function(block, index) { var level = block.type === 'heading' ? Math.max(1, Math.min(6, Number(block.props && block.props.level || 1))) : null; if (level != null) { while (headingStack.length && headingStack[headingStack.length - 1].level >= level) headingStack.pop(); } var parentNodeId = headingStack.length ? headingStack[headingStack.length - 1].nodeId : rootNodeId; var nodeId = 'local-node:' + String(block.id || index); var titleText = block.content || (block.type === 'heading' ? '未命名标题' : ''); nodes.push({ id: nodeId, nodeId: nodeId, nodeType: 'block', blockId: block.id, blockType: block.type, title: titleText, parentNodeId: parentNodeId, headingLevel: level }); if (level != null) { headingCounters[level - 1] += 1; for (var i = level; i < headingCounters.length; i += 1) headingCounters[i] = 0; var numbering = headingCounters.slice(0, level).filter(function(count) { return count > 0; }).join('.'); outline.push({ id: 'local-outline:' + String(block.id || index), nodeId: nodeId, anchorBlockId: block.id, level: level, title: titleText, numbering: numbering }); headingStack.push({ level: level, nodeId: nodeId }); } if (titleText) { evidence.push({ id: 'local-evidence:' + String(block.id || index), nodeId: nodeId, anchorBlockId: block.id, text: titleText, kind: block.type }); } }); return { projectionId: 'local-editor-dom:' + documentId, rootNode: { id: rootNodeId, documentId: documentId, title: title || '', nodeType: 'page' }, subtree: { rootNodeId: rootNodeId, nodes: nodes }, outline: outline, evidence: evidence, stats: { nodeCount: nodes.length, headingCount: outline.length, evidenceCount: evidence.length }, source: 'local' }; } function currentPageAiContextSnapshot() { var aggregate = currentPageAggregate(); var body = aggregate.body || {}; var serverSubtree = aggregate.tree && aggregate.tree.pageSubtree ? aggregate.tree.pageSubtree : null; return { aggregate: aggregate, body: body, subtree: serverSubtree, pageSubtreeSource: serverSubtree ? 'server' : 'none' }; } function pageAiNormalizeAgentId(agentId) { var value = String(agentId || '').trim(); return PAGE_AI_AGENT_REGISTRY.some(function(agent) { return agent.id === value; }) ? value : 'reasonix'; } function pageAiAgentRecord(agentId) { var normalized = pageAiNormalizeAgentId(agentId); return PAGE_AI_AGENT_REGISTRY.find(function(agent) { return agent.id === normalized; }) || PAGE_AI_AGENT_REGISTRY[1]; } function pageAiCurrentAgentId() { var explicit = String(pageUiState.pageAiAgentId || '').trim(); if (explicit) return pageAiNormalizeAgentId(explicit); var runtime = String(pageUiState.pageAiAcpRuntime || 'reasonix').trim(); if (runtime === 'hermes') return 'hermes'; return 'reasonix'; } const pageAiProfileRuntime = createSidebarPageAiProfileRuntime({ chatOnlyProfileRegistry: PAGE_AI_CHAT_ONLY_PROFILE_REGISTRY, documentRef: document, pageAiAgentRecord, pageAiCurrentAgentId, pageAiNormalizeAgentId, pageUiState, }); const pageAiProviderLabel = (...args) => pageAiProfileRuntime.pageAiProviderLabel(...args); const pageAiNormalizeArray = (...args) => pageAiProfileRuntime.pageAiNormalizeArray(...args); const pageAiDefaultAcpRuntimes = (...args) => pageAiProfileRuntime.pageAiDefaultAcpRuntimes(...args); const pageAiNormalizeAcpRuntimes = (...args) => pageAiProfileRuntime.pageAiNormalizeAcpRuntimes(...args); const pageAiUnwrapUpstream = (...args) => pageAiProfileRuntime.pageAiUnwrapUpstream(...args); const pageAiProfileValue = (...args) => pageAiProfileRuntime.pageAiProfileValue(...args); const pageAiCurrentProfile = (...args) => pageAiProfileRuntime.pageAiCurrentProfile(...args); const pageAiRunProfile = (...args) => pageAiProfileRuntime.pageAiRunProfile(...args); const pageAiMnoteToolModel = (...args) => pageAiProfileRuntime.pageAiMnoteToolModel(...args); const pageAiCurrentProfileRecord = (...args) => pageAiProfileRuntime.pageAiCurrentProfileRecord(...args); const pageAiChatOnlyProfileSpec = (...args) => pageAiProfileRuntime.pageAiChatOnlyProfileSpec(...args); const pageAiDefaultChatOnlyProfileSpec = (...args) => pageAiProfileRuntime.pageAiDefaultChatOnlyProfileSpec(...args); const pageAiNormalizeChatOnlyProfileId = (...args) => pageAiProfileRuntime.pageAiNormalizeChatOnlyProfileId(...args); const pageAiProfileDisplayLabel = (...args) => pageAiProfileRuntime.pageAiProfileDisplayLabel(...args); const pageAiProfileRecordById = (...args) => pageAiProfileRuntime.pageAiProfileRecordById(...args); const pageAiSessionAgentFilterValue = (...args) => pageAiProfileRuntime.pageAiSessionAgentFilterValue(...args); const pageAiSessionAgentLabel = (...args) => pageAiProfileRuntime.pageAiSessionAgentLabel(...args); const pageAiSessionPreviewText = (...args) => pageAiProfileRuntime.pageAiSessionPreviewText(...args); const pageAiSessionAgentFilterOptions = (...args) => pageAiProfileRuntime.pageAiSessionAgentFilterOptions(...args); const pageAiFilteredHistoryRows = (...args) => pageAiProfileRuntime.pageAiFilteredHistoryRows(...args); const pageAiTimestamp = (...args) => pageAiProfileRuntime.pageAiTimestamp(...args); const pageAiUsageSummary = (...args) => pageAiProfileRuntime.pageAiUsageSummary(...args); const pageAiPermissionRuntime = createSidebarPageAiPermissionRuntime({ documentRef: document, pageAiPreviewValue, pageUiState, renderPageAiConversation, }); const pageAiPermissionMessage = (...args) => pageAiPermissionRuntime.pageAiPermissionMessage(...args); const pageAiApplyPermissionEvent = (...args) => pageAiPermissionRuntime.pageAiApplyPermissionEvent(...args); const pageAiResolvePermission = (...args) => pageAiPermissionRuntime.pageAiResolvePermission(...args); const pageAiHidePermissionDialog = (...args) => pageAiPermissionRuntime.pageAiHidePermissionDialog(...args); const pageAiShowPermissionDialog = (...args) => pageAiPermissionRuntime.pageAiShowPermissionDialog(...args); const pageAiSessionRuntime = createSidebarPageAiSessionRuntime({ currentDocumentId, currentRootUri, currentSourceKind, documentRef: document, pageAiApplyRuntimeState, pageAiCurrentAgentId, pageAiCurrentProfile, pageAiErrorMessage, pageAiNormalizeAgentId, pageAiNormalizeArray, pageAiNormalizeChatOnlyProfileId, pageAiPermissionMessage, pageAiPreviewValue, pageAiRunProfile, pageAiSetActiveProfile, pageAiTimestamp, pageUiState, renderPageAiControls, renderPageAiConversation, resolveWorkspaceId, sessionStorageVersion: PAGE_AI_SESSION_STORAGE_VERSION, windowRef: window, }); const pageAiStorageKey = (...args) => pageAiSessionRuntime.pageAiStorageKey(...args); const pageAiBackendSessionQuery = (...args) => pageAiSessionRuntime.pageAiBackendSessionQuery(...args); const pageAiNewSession = (...args) => pageAiSessionRuntime.pageAiNewSession(...args); const pageAiNormalizeSessions = (...args) => pageAiSessionRuntime.pageAiNormalizeSessions(...args); const pageAiNormalizeBackendSessionRow = (...args) => pageAiSessionRuntime.pageAiNormalizeBackendSessionRow(...args); const pageAiMergeSessions = (...args) => pageAiSessionRuntime.pageAiMergeSessions(...args); const pageAiSessionStorageLabel = (...args) => pageAiSessionRuntime.pageAiSessionStorageLabel(...args); const pageAiLoadSessions = (...args) => pageAiSessionRuntime.pageAiLoadSessions(...args); const pageAiLoadBackendSessions = (...args) => pageAiSessionRuntime.pageAiLoadBackendSessions(...args); const pageAiMessageFromRuntimeEvent = (...args) => pageAiSessionRuntime.pageAiMessageFromRuntimeEvent(...args); const pageAiApplyBackendSessionDetail = (...args) => pageAiSessionRuntime.pageAiApplyBackendSessionDetail(...args); const pageAiLoadBackendSessionDetail = (...args) => pageAiSessionRuntime.pageAiLoadBackendSessionDetail(...args); const pageAiSearchBackendSessions = (...args) => pageAiSessionRuntime.pageAiSearchBackendSessions(...args); const pageAiPersistSessions = (...args) => pageAiSessionRuntime.pageAiPersistSessions(...args); const pageAiEnsureHermesSession = (...args) => pageAiSessionRuntime.pageAiEnsureHermesSession(...args); const pageAiRestoreHermesSession = (...args) => pageAiSessionRuntime.pageAiRestoreHermesSession(...args); const pageAiCurrentSession = (...args) => pageAiSessionRuntime.pageAiCurrentSession(...args); const pageAiSyncCurrentSessionMessages = (...args) => pageAiSessionRuntime.pageAiSyncCurrentSessionMessages(...args); const pageAiSetActiveSession = (...args) => pageAiSessionRuntime.pageAiSetActiveSession(...args); const pageAiStartNewSession = (...args) => pageAiSessionRuntime.pageAiStartNewSession(...args); const pageAiRenameBackendSession = (...args) => pageAiSessionRuntime.pageAiRenameBackendSession(...args); const pageAiDeleteBackendSession = (...args) => pageAiSessionRuntime.pageAiDeleteBackendSession(...args); const pageAiResumeBackendSession = (...args) => pageAiSessionRuntime.pageAiResumeBackendSession(...args); const pageAiSkillRuntime = createSidebarPageAiSkillRuntime({ pageAiCurrentAgentId, pageAiCurrentProfile, pageAiLoadSkills, pageAiNormalizeArray, pageAiPersistAiPreference, pageAiPersistRawAiPreference, pageAiProfileValue, pageUiState, renderPageAiControls, }); const pageAiSkillSourceOptions = (...args) => pageAiSkillRuntime.pageAiSkillSourceOptions(...args); const pageAiDefaultSkillSource = (...args) => pageAiSkillRuntime.pageAiDefaultSkillSource(...args); const pageAiNormalizeSkillSource = (...args) => pageAiSkillRuntime.pageAiNormalizeSkillSource(...args); const pageAiCurrentSkillSource = (...args) => pageAiSkillRuntime.pageAiCurrentSkillSource(...args); const pageAiSetSkillSource = (...args) => pageAiSkillRuntime.pageAiSetSkillSource(...args); const pageAiSkillSourceParts = (...args) => pageAiSkillRuntime.pageAiSkillSourceParts(...args); const pageAiCurrentSkillSourceLabel = (...args) => pageAiSkillRuntime.pageAiCurrentSkillSourceLabel(...args); const pageAiSkillOriginLabel = (...args) => pageAiSkillRuntime.pageAiSkillOriginLabel(...args); const pageAiSkillPreferenceKey = (...args) => pageAiSkillRuntime.pageAiSkillPreferenceKey(...args); const pageAiSkillPreferenceTable = (...args) => pageAiSkillRuntime.pageAiSkillPreferenceTable(...args); const pageAiHermesHideBuiltinPreferenceKey = (...args) => pageAiSkillRuntime.pageAiHermesHideBuiltinPreferenceKey(...args); const pageAiHideHermesBuiltinSkills = (...args) => pageAiSkillRuntime.pageAiHideHermesBuiltinSkills(...args); const pageAiReasonixMemoryEnabled = (...args) => pageAiSkillRuntime.pageAiReasonixMemoryEnabled(...args); const pageAiSetReasonixMemoryEnabled = (...args) => pageAiSkillRuntime.pageAiSetReasonixMemoryEnabled(...args); const pageAiSetHideHermesBuiltinSkills = (...args) => pageAiSkillRuntime.pageAiSetHideHermesBuiltinSkills(...args); const pageAiSkillIsBuiltin = (...args) => pageAiSkillRuntime.pageAiSkillIsBuiltin(...args); const pageAiToggleableSkillEntries = (...args) => pageAiSkillRuntime.pageAiToggleableSkillEntries(...args); const pageAiAllSkillEntries = (...args) => pageAiSkillRuntime.pageAiAllSkillEntries(...args); const pageAiSkillGroupCollapsed = (...args) => pageAiSkillRuntime.pageAiSkillGroupCollapsed(...args); const pageAiToggleSkillGroup = (...args) => pageAiSkillRuntime.pageAiToggleSkillGroup(...args); const pageAiSetSkillPreference = (...args) => pageAiSkillRuntime.pageAiSetSkillPreference(...args); const pageAiSkillEnabled = (...args) => pageAiSkillRuntime.pageAiSkillEnabled(...args); function pageAiEnsureContextRefState() { if (!pageUiState.pageAiSelectedContextRefs || typeof pageUiState.pageAiSelectedContextRefs !== 'object') { pageUiState.pageAiSelectedContextRefs = { current_page: true, selection: false, active_editor: true, file: false, folder: false, changed_files: false }; } return pageUiState.pageAiSelectedContextRefs; } const pageAiTargetRuntime = createSidebarPageAiTargetRuntime({ currentDocumentId, currentPageOptions, currentRootUri, currentSourceKind, documentRef: document, escapeHtml, pageAiEnsureContextRefState, pageAiNormalizeArray, pageUiState, resolveWorkspaceId, searchText, }); const currentPageAiOpenEditorsSnapshot = (...args) => pageAiTargetRuntime.currentPageAiOpenEditorsSnapshot(...args); const currentPageAiEditorTarget = (...args) => pageAiTargetRuntime.currentPageAiEditorTarget(...args); const currentPageAiPageEditorTarget = (...args) => pageAiTargetRuntime.currentPageAiPageEditorTarget(...args); const currentPageAiScopedEditorTarget = (...args) => pageAiTargetRuntime.currentPageAiScopedEditorTarget(...args); const pageAiCloneJson = (...args) => pageAiTargetRuntime.pageAiCloneJson(...args); const pageAiEditorTargetCandidates = (...args) => pageAiTargetRuntime.pageAiEditorTargetCandidates(...args); const pageAiFallbackEditorTarget = (...args) => pageAiTargetRuntime.pageAiFallbackEditorTarget(...args); const pageAiResourceKindForTarget = (...args) => pageAiTargetRuntime.pageAiResourceKindForTarget(...args); const pageAiTargetFromOpenEditor = (...args) => pageAiTargetRuntime.pageAiTargetFromOpenEditor(...args); const pageAiTargetId = (...args) => pageAiTargetRuntime.pageAiTargetId(...args); const localMarkdownRelativePathFromPageAiDocumentId = (...args) => pageAiTargetRuntime.localMarkdownRelativePathFromPageAiDocumentId(...args); const localMarkdownDocumentIdFromPageAiRelativePath = (...args) => pageAiTargetRuntime.localMarkdownDocumentIdFromPageAiRelativePath(...args); const pageAiWorkspacePathForDocument = (...args) => pageAiTargetRuntime.pageAiWorkspacePathForDocument(...args); const pageAiWorkspacePathForTarget = (...args) => pageAiTargetRuntime.pageAiWorkspacePathForTarget(...args); const pageAiBuildAllowedRoots = (...args) => pageAiTargetRuntime.pageAiBuildAllowedRoots(...args); const pageAiBuildContextRefs = (...args) => pageAiTargetRuntime.pageAiBuildContextRefs(...args); const pageAiBuildRunTargetSnapshot = (...args) => pageAiTargetRuntime.pageAiBuildRunTargetSnapshot(...args); const pageAiPageContextForRefs = (...args) => pageAiTargetRuntime.pageAiPageContextForRefs(...args); const pageAiSetRunTargetSnapshot = (...args) => pageAiTargetRuntime.pageAiSetRunTargetSnapshot(...args); const assertPageAiTargetInCurrentWorkspace = (...args) => pageAiTargetRuntime.assertPageAiTargetInCurrentWorkspace(...args); const pageAiBuildAgentTargetPackage = (...args) => pageAiTargetRuntime.pageAiBuildAgentTargetPackage(...args); const pageAiBlockingDirtyState = (...args) => pageAiTargetRuntime.pageAiBlockingDirtyState(...args); const fetchPageAiTargetBufferState = (...args) => pageAiTargetRuntime.fetchPageAiTargetBufferState(...args); const assertPageAiTargetWritable = (...args) => pageAiTargetRuntime.assertPageAiTargetWritable(...args); const currentPageAiSelectedText = (...args) => pageAiTargetRuntime.currentPageAiSelectedText(...args); const pageAiProjectionBlocks = (...args) => pageAiTargetRuntime.pageAiProjectionBlocks(...args); const pageAiBlockText = (...args) => pageAiTargetRuntime.pageAiBlockText(...args); const pageAiSelectedBlockIdsFromSelection = (...args) => pageAiTargetRuntime.pageAiSelectedBlockIdsFromSelection(...args); const pageAiBlocksToPageXml = (...args) => pageAiTargetRuntime.pageAiBlocksToPageXml(...args); const buildPageAiContext = (...args) => pageAiTargetRuntime.buildPageAiContext(...args); const pageAiScopedPageContext = (...args) => pageAiTargetRuntime.pageAiScopedPageContext(...args); pageAiRenderRuntime = createSidebarPageAiRenderRuntime({ contextRefRegistry: PAGE_AI_CONTEXT_REF_REGISTRY, cssEscape, currentDocumentId, currentPageAiEditorTarget, currentPageAiSelectedText, documentRef: document, escapeHtml, localMarkdownRelativePathFromPageAiDocumentId, pageAiAgentRecord, pageAiAllSkillEntries, pageAiBuildAllowedRoots, pageAiChatOnlyProfileEntries, pageAiChatOnlyProfileSpec, pageAiCurrentAgentId, pageAiCurrentProfile, pageAiCurrentProfileRecord, pageAiCurrentSession, pageAiCurrentSkillSource, pageAiCurrentSkillSourceLabel, pageAiDefaultChatOnlyProfileSpec, pageAiEditorTargetCandidates, pageAiEnsureContextRefState, pageAiFilteredHistoryRows, pageAiHermesProfileEntries, pageAiHermesSettingsUrl, pageAiHideHermesBuiltinSkills, pageAiMnoteToolModel, pageAiNormalizeAcpRuntimes, pageAiNormalizeArray, pageAiNormalizeSkillSource, pageAiProfileDisplayLabel, pageAiProfileValue, pageAiProviderLabel, pageAiReasonixMemoryEnabled, pageAiRunProfile, pageAiSessionAgentFilterOptions, pageAiSessionAgentFilterValue, pageAiSessionAgentLabel, pageAiSessionPreviewText, pageAiSessionStorageLabel, pageAiSkillEnabled, pageAiSkillGroupCollapsed, pageAiSkillListEntries, pageAiSkillOriginLabel, pageAiSkillSourceOptions, pageAiSkillSourceParts, pageAiToggleableSkillEntries, pageAiUsageSummary, pageUiState, renderPageAiMarkdown, searchText, }); function pageAiSetAgentId(agentId) { var next = pageAiNormalizeAgentId(agentId); var record = pageAiAgentRecord(next); pageUiState.pageAiAgentId = next; pageUiState.pageAiAcpRuntime = record.acpRuntime || 'reasonix'; pageUiState.pageAiAgentPopoverOpen = next === 'hermes'; if (next === 'hermes') pageAiSetActiveProfile(pageAiCurrentProfile()); if (next === 'chat_only') pageAiSetActiveProfile(pageAiNormalizeChatOnlyProfileId(pageAiCurrentProfile())); document.documentElement.setAttribute('data-mnote-page-ai-agent-id', next); pageAiPersistAiPreference('default_agent_id', next); void pageAiLoadSkills(); renderPageAiProviderButtons(); renderPageAiControls(); } function pageAiToggleContextRef(kind) { var normalized = String(kind || '').trim(); if (!PAGE_AI_CONTEXT_REF_REGISTRY.some(function(ref) { return ref.id === normalized; })) return; var selected = pageAiEnsureContextRefState(); selected[normalized] = !selected[normalized]; if (!Object.keys(selected).some(function(key) { return selected[key]; })) selected.current_page = true; pageAiPersistAiPreference('context_refs.default_selected', selected); renderPageAiControls(); } function pageAiSetContextPopoverOpen(open) { pageUiState.pageAiContextPopoverOpen = Boolean(open); if (open) { pageUiState.pageAiAgentPopoverOpen = false; pageUiState.pageAiTargetPopoverOpen = false; } renderPageAiControls(); } function pageAiSetAgentPopoverOpen(open) { pageUiState.pageAiAgentPopoverOpen = Boolean(open); if (open) { pageUiState.pageAiContextPopoverOpen = false; pageUiState.pageAiTargetPopoverOpen = false; } renderPageAiControls(); } function pageAiSetTargetPopoverOpen(open) { pageUiState.pageAiTargetPopoverOpen = Boolean(open); if (open) { pageUiState.pageAiAgentPopoverOpen = false; pageUiState.pageAiContextPopoverOpen = false; } renderPageAiControls(); } function pageAiSelectTarget(targetId) { var normalized = String(targetId || '').trim(); var candidates = pageAiEditorTargetCandidates(); var target = candidates.find(function(candidate) { return candidate.targetId === normalized; }) || null; if (!target) return; pageUiState.pageAiSelectedTargetId = target.targetId; pageUiState.pageAiTargetPopoverOpen = false; renderPageAiControls(); } function pageAiPersistAiPreference(key, value) { pageAiPersistRawAiPreference('ai.common.' + key, value); } function pageAiPersistRawAiPreference(key, value) { try { var workspaceId = resolveWorkspaceId(document.body); var body = { workspaceId: workspaceId, sourceKind: currentSourceKind(), rootUri: currentRootUri(), documentId: currentDocumentId(), updates: {} }; body.updates[key] = value; void fetch('/api/ui/preferences', { method: 'PUT', headers: { 'content-type': 'application/json', accept: 'application/json' }, body: JSON.stringify(body) }).catch(function() {}); } catch (_) {} } async function pageAiLoadAiPreferences() { try { var url = new URL('/api/ui/preferences/effective', window.location.origin); url.searchParams.set('workspaceId', resolveWorkspaceId(document.body)); url.searchParams.set('sourceKind', currentSourceKind()); url.searchParams.set('rootUri', currentRootUri()); url.searchParams.set('documentId', currentDocumentId()); var response = await fetch(url.toString(), { headers: { accept: 'application/json' }, cache: 'no-store' }); var payload = await response.json().catch(function() { return null; }); if (!response.ok || !payload || !payload.result) return; var preferences = payload.result.aiPreferences && typeof payload.result.aiPreferences === 'object' ? payload.result.aiPreferences : {}; pageUiState.pageAiSkillPreferences = Object.assign({}, preferences); var defaultAgent = String(preferences['ai.common.default_agent_id'] || '').trim(); if (defaultAgent) { var agent = pageAiAgentRecord(defaultAgent); pageUiState.pageAiAgentId = agent.id; pageUiState.pageAiAcpRuntime = agent.acpRuntime || pageUiState.pageAiAcpRuntime || 'reasonix'; } var hermesProfile = String(preferences['ai.agent.hermes.profile_id'] || '').trim(); if (hermesProfile) pageAiSetActiveProfile(hermesProfile); var skillSource = String(preferences['ai.common.skills.active_source'] || '').trim(); if (skillSource) pageUiState.pageAiActiveSkillSource = skillSource; var selected = preferences['ai.common.context_refs.default_selected']; if (selected && typeof selected === 'object' && !Array.isArray(selected)) { pageUiState.pageAiSelectedContextRefs = Object.assign({}, pageAiEnsureContextRefState(), selected); } var collapsedSkillGroups = preferences['ai.common.skills.groups.collapsed']; if (collapsedSkillGroups && typeof collapsedSkillGroups === 'object' && !Array.isArray(collapsedSkillGroups)) { pageUiState.pageAiCollapsedSkillGroups = Object.assign({}, collapsedSkillGroups); } } catch (_) {} renderPageAiControls(); } function pageAiNormalizeAccessGrant(grant) { if (!grant || typeof grant !== 'object') return null; var rootUri = String(grant.rootUri || grant.root_uri || '').trim(); if (!rootUri) return null; var permission = String(grant.permission || grant.access || '').trim() || 'read'; return { id: String(grant.id || '').trim(), rootUri: rootUri, rootPath: String(grant.rootPath || grant.root_path || '').trim(), permission: permission === 'read_write' ? 'write' : permission, recursive: grant.recursive !== false, source: String(grant.source || 'sqlite_directory_grant').trim() || 'sqlite_directory_grant', status: String(grant.status || 'active').trim() || 'active' }; } async function pageAiLoadAllowedRoots() { if (currentSourceKind() !== 'local_folder') { pageUiState.pageAiAllowedRoots = []; renderPageAiControls(); return []; } try { var response = await fetch('/api/user/access-policy', { headers: { accept: 'application/json' }, cache: 'no-store' }); var payload = await response.json().catch(function() { return null; }); if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'access_policy_failed_' + response.status)); var grants = pageAiNormalizeArray(payload && (payload.grants || payload.policy && payload.policy.grants)); var currentRoot = String(currentRootUri() || '').trim(); pageUiState.pageAiAllowedRoots = grants.map(pageAiNormalizeAccessGrant).filter(Boolean).filter(function(grant) { return grant.status === 'active' && (!currentRoot || grant.rootUri === currentRoot); }); pageUiState.pageAiAllowedRootsError = ''; } catch (error) { pageUiState.pageAiAllowedRoots = []; pageUiState.pageAiAllowedRootsError = error instanceof Error ? error.message : String(error); } renderPageAiControls(); return pageUiState.pageAiAllowedRoots; } function updatePageAiTriggerState() { var trigger = document.querySelector('[data-testid="wolai-floating-ai"]'); if (!(trigger instanceof HTMLElement)) return; trigger.setAttribute('data-state', pageUiState.pageAiOpen ? 'open' : 'closed'); trigger.setAttribute('aria-expanded', pageUiState.pageAiOpen ? 'true' : 'false'); } function pageAiOpenLocation(loc) { var path = String(loc || '').trim(); if (!path) return; openLocalResourceInActiveTab({ path: path }).then(function(opened) { if (!opened) { var href = buildLocalFileOpenUrl(path, false); if (href) window.open(href, '_blank', 'noopener,noreferrer'); } }); } function pageAiChatOnlyProfileEntries() { var profiles = pageAiNormalizeArray(pageUiState.pageAiProfiles); return PAGE_AI_CHAT_ONLY_PROFILE_REGISTRY.map(function(spec) { var profile = profiles.find(function(candidate) { var candidateSpec = pageAiChatOnlyProfileSpec(candidate); return candidateSpec && candidateSpec.profileId === spec.profileId; }); return Object.assign({}, profile || { profileId: spec.profileId, name: spec.profileId, alias: spec.label, kind: 'shared', baseProfile: spec.baseProfile, readonly: true }, { menuLabel: spec.label }); }); } function pageAiHermesProfileEntries() { var profiles = pageAiNormalizeArray(pageUiState.pageAiProfiles).filter(function(profile) { return !pageAiChatOnlyProfileSpec(profile); }); if (profiles.length) return profiles; return [{ profileId: pageAiCurrentProfile(), name: pageAiCurrentProfile(), alias: pageAiCurrentProfile(), kind: 'personal' }]; } function pageAiNormalizeProfiles(payload) { var upstream = pageAiUnwrapUpstream(payload); var profiles = pageAiNormalizeArray(upstream && upstream.profiles ? upstream.profiles : upstream); return profiles.map(function(profile) { var profileId = String(profile && (profile.profileId || profile.id || profile.name) || '').trim(); var kind = String(profile && profile.kind || profile && profile.profileKind || '').trim(); var canManageSkills = profile && profile.canManageSkills !== false; return { profileId: profileId || pageAiProfileValue(profile) || 'default', name: profileId || pageAiProfileValue(profile) || 'default', active: Boolean(profile && profile.active), model: String(profile && profile.model || '').trim(), gateway: String(profile && profile.gateway || '').trim(), alias: String(profile && (profile.displayName || profile.alias) || '').trim(), kind: kind, ownerUserId: String(profile && profile.ownerUserId || '').trim(), baseProfile: String(profile && profile.baseProfile || '').trim(), isolatedProfile: String(profile && profile.isolatedProfile || '').trim(), canRun: profile ? profile.canRun !== false : true, canManageSkills: canManageSkills, canManageConfig: profile ? profile.canManageConfig !== false : canManageSkills, readonly: profile ? (profile.readonly === true || !canManageSkills) : false, grantRole: String(profile && profile.grantRole || '').trim() }; }); } function pageAiNormalizeSkills(payload) { var upstream = pageAiUnwrapUpstream(payload); var categories = pageAiNormalizeArray(upstream && upstream.categories ? upstream.categories : []); var archived = pageAiNormalizeArray(upstream && upstream.archived ? upstream.archived : []); return { categories: categories.map(function(category) { return { name: String(category && category.name || '').trim() || 'misc', description: String(category && category.description || '').trim(), skills: pageAiNormalizeArray(category && category.skills).map(function(skill) { return { name: String(skill && skill.name || '').trim(), id: String(skill && skill.id || skill && skill.name || '').trim(), title: String(skill && skill.title || skill && skill.name || skill && skill.id || '').trim(), description: String(skill && skill.description || '').trim(), enabled: skill && skill.enabled !== false, toggleable: skill && skill.toggleable !== false, source: String(skill && skill.source || 'local').trim() || 'local', origin: String(skill && skill.origin || '').trim(), createdBy: String(skill && skill.createdBy || '').trim(), patchCount: Number(skill && skill.patchCount || 0), modified: Boolean(skill && skill.modified), builtin: Boolean(skill && skill.builtin), configurable: skill && skill.configurable !== false, configScope: String(skill && skill.configScope || '').trim(), skillKind: String(skill && skill.skillKind || '').trim(), profileId: String(skill && skill.profileId || '').trim(), readOnly: Boolean(skill && (skill.readOnly || skill.readonly || skill.configurable === false)), category: String(category && category.name || '').trim() }; }) }; }), archived: archived.map(function(skill) { return { name: String(skill && skill.name || '').trim(), id: String(skill && skill.id || skill && skill.name || '').trim(), title: String(skill && skill.title || skill && skill.name || skill && skill.id || '').trim(), description: String(skill && skill.description || '').trim(), enabled: skill && skill.enabled !== false, toggleable: skill && skill.toggleable !== false, source: String(skill && skill.source || 'local').trim() || 'local', origin: String(skill && skill.origin || '').trim(), createdBy: String(skill && skill.createdBy || '').trim(), patchCount: Number(skill && skill.patchCount || 0), modified: Boolean(skill && skill.modified), builtin: Boolean(skill && skill.builtin), configurable: skill && skill.configurable !== false, configScope: String(skill && skill.configScope || '').trim(), skillKind: String(skill && skill.skillKind || '').trim(), profileId: String(skill && skill.profileId || '').trim(), readOnly: Boolean(skill && (skill.readOnly || skill.readonly || skill.configurable === false)) }; }) }; } function pageAiSkillListEntries() { var result = []; pageAiNormalizeArray(pageUiState.pageAiSkills.categories).forEach(function(category) { pageAiNormalizeArray(category.skills).forEach(function(skill) { result.push({ category: category.name, name: skill.name, id: skill.id || skill.name, title: skill.title || skill.name, description: skill.description, enabled: skill.enabled !== false, toggleable: skill.toggleable !== false, source: skill.source || 'local', origin: skill.origin || '', createdBy: skill.createdBy || '', patchCount: Number(skill.patchCount || 0), modified: Boolean(skill.modified), builtin: Boolean(skill.builtin), configurable: skill.configurable !== false, configScope: skill.configScope || '', skillKind: skill.skillKind || '', profileId: skill.profileId || '', readOnly: Boolean(skill.readOnly || skill.readonly || skill.configurable === false) }); }); }); return result.concat(pageAiNormalizeArray(pageUiState.pageAiSkills.archived)); } function pageAiSetActiveProfile(profileName) { var next = String(profileName || '').trim() || 'mnoteai'; pageUiState.pageAiActiveProfileName = next; document.documentElement.setAttribute('data-mnote-page-ai-profile', next); document.documentElement.setAttribute('data-mnote-page-ai-tool-model', pageAiMnoteToolModel()); } function pageAiSetRunStatus(status, runId) { pageUiState.pageAiRunStatus = status || 'idle'; pageUiState.pageAiCurrentRunId = runId || pageUiState.pageAiCurrentRunId || ''; document.documentElement.setAttribute('data-mnote-page-ai-run-status', pageUiState.pageAiRunStatus); if (pageUiState.pageAiCurrentRunId) { document.documentElement.setAttribute('data-mnote-page-ai-run-id', pageUiState.pageAiCurrentRunId); } } function pageAiApplyRuntimeState(runtime) { if (!runtime || typeof runtime !== 'object') return; var status = String(runtime.status || '').trim(); var runId = String(runtime.runId || runtime.run_id || '').trim(); if (status) pageAiSetRunStatus(status, runId); var queueLength = Number(runtime.queueLength || runtime.queue_length || 0); if (Number.isFinite(queueLength)) pageUiState.pageAiQueueLength = Math.max(0, queueLength); var toolName = String(runtime.lastToolName || runtime.last_tool_name || '').trim(); if (toolName) { pageUiState.pageAiLastToolCall = { event: String(runtime.lastEvent || runtime.last_event || ''), name: toolName, runId: runId, traceId: String(runtime.traceId || runtime.trace_id || ''), auditId: String(runtime.lastAuditId || runtime.last_audit_id || '') }; } } function pageAiApplyQueuedRun(payload) { if (!payload || payload.queued !== true) return false; var queueId = String(payload.queueId || payload.queue_id || '').trim(); var queueLength = Number(payload.queueLength || payload.queue_length || 0); pageUiState.pageAiQueueLength = Number.isFinite(queueLength) ? Math.max(0, queueLength) : 1; if (queueId) { pageUiState.pageAiQueuedItems = pageUiState.pageAiQueuedItems.filter(function(item) { return item.queueId !== queueId; }).concat([{ queueId: queueId, sessionId: String(payload.sessionId || payload.session_id || pageUiState.pageAiActiveSessionId || ''), traceId: String(payload.traceId || payload.trace_id || ''), queuedAt: Number(payload.queuedAt || payload.queued_at || Date.now()) }]); } pageAiSetRunStatus('queued', pageUiState.pageAiCurrentRunId); return true; } function pageAiPreviewValue(value) { if (value == null || value === '') return ''; if (typeof value === 'string') return value.length > 120 ? value.slice(0, 120) + '…' : value; try { var text = JSON.stringify(value); return text.length > 120 ? text.slice(0, 120) + '…' : text; } catch (_) { return String(value); } } function pageAiNormalizeToolName(name) { return String(name || '').trim().replace(/_/g, '.'); } function pageAiParentRelativePath(path) { var normalized = String(path || '').trim().replace(/\\/g, '/').replace(/^\/+/, ''); if (!normalized || normalized.indexOf('/') < 0) return ''; return normalized.split('/').slice(0, -1).join('/'); } function pageAiChangedFileRelativePath(file) { return String(file && (file.path || file.relativePath || file.relative_path || file.filePath || file.file_path || '') || '').trim(); } function pageAiDispatchReceiptRefresh(receipt, changedFiles, fallbackAudit, runId, traceId) { var refresh = receipt && typeof receipt === 'object' && receipt.refresh && typeof receipt.refresh === 'object' ? receipt.refresh : {}; var files = pageAiNormalizeArray( receipt && typeof receipt === 'object' && receipt.changedFiles !== undefined ? receipt.changedFiles : changedFiles ); if (files.length) { var changedPaths = files.map(function(file) { var relativePath = pageAiChangedFileRelativePath(file); return { relativePath: relativePath, changeType: String(file && (file.changeType || file.change_type || 'modified') || 'modified') }; }).filter(function(item) { return item.relativePath; }); var affectedParents = changedPaths.map(function(item) { return { relativePath: pageAiParentRelativePath(item.relativePath), reason: 'agent-run-receipt' }; }).filter(function(item, index, list) { return index === list.findIndex(function(candidate) { return candidate.relativePath === item.relativePath; }); }); if (changedPaths.length) { document.documentElement.setAttribute('data-mnote-page-ai-receipt-filetree-refresh', 'true'); window.dispatchEvent(new CustomEvent('tree:local-folder-watch-batch', { detail: { payload: { schema: 'mnote.local_folder.watch_batch.v1', source: 'agent_run_receipt', runId: runId, rootUri: String((receipt && receipt.rootUri) || (fallbackAudit && fallbackAudit.rootUri) || currentRootUri() || ''), changedPaths: changedPaths, affectedParents: affectedParents } } })); } } if (refresh.touchesCurrentFile === true) { var documentId = String(refresh.currentDocumentId || receipt && receipt.documentId || currentDocumentId() || '').trim(); document.documentElement.setAttribute('data-mnote-page-ai-receipt-current-refresh', 'true'); window.dispatchEvent(new CustomEvent('mnote:page-ai-tool-write-completed', { detail: { toolName: 'agent.run_receipt', normalizedToolName: 'agent.run_receipt', documentId: documentId, workspaceId: String(receipt && receipt.workspaceId || resolveWorkspaceId(document.body) || ''), rootUri: String(receipt && receipt.rootUri || fallbackAudit && fallbackAudit.rootUri || currentRootUri() || ''), changedFiles: files, receipt: receipt || null, runId: runId, traceId: traceId, toolCallId: runId + ':agent.run_receipt' } })); } } function pageAiToolEventDeepFindString(value, keys, depth) { if (!value || typeof value !== 'object' || depth > 5) return ''; for (var index = 0; index < keys.length; index += 1) { var key = keys[index]; if (Object.prototype.hasOwnProperty.call(value, key) && typeof value[key] === 'string' && value[key].trim()) { return value[key].trim(); } } if (Array.isArray(value)) { for (var arrayIndex = 0; arrayIndex < value.length; arrayIndex += 1) { var fromArray = pageAiToolEventDeepFindString(value[arrayIndex], keys, depth + 1); if (fromArray) return fromArray; } return ''; } var preferred = ['audit', 'args', 'arguments', 'input', 'result', 'summary', 'output', 'upstream']; for (var prefIndex = 0; prefIndex < preferred.length; prefIndex += 1) { var child = value[preferred[prefIndex]]; var fromPreferred = pageAiToolEventDeepFindString(child, keys, depth + 1); if (fromPreferred) return fromPreferred; } var objectKeys = Object.keys(value); for (var objectIndex = 0; objectIndex < objectKeys.length; objectIndex += 1) { var fromObject = pageAiToolEventDeepFindString(value[objectKeys[objectIndex]], keys, depth + 1); if (fromObject) return fromObject; } return ''; } function pageAiNotifyToolWriteCompleted(toolName, toolEvent, runId, runTraceId) { var normalizedTool = pageAiNormalizeToolName(toolName); var writesCurrentPage = [ 'mnote.page.save', 'mnote.page.update.title', 'mnote.page.update.options', 'mnote.doc.apply.block.ops', 'mnote.block.replace', 'mnote.block.insert.after', 'mnote.block.delete', 'mnote.block.move.after' ].indexOf(normalizedTool) >= 0 || [ 'mnote.page.update_title', 'mnote.page.update_options', 'mnote.doc.apply_block_ops', 'mnote.block.replace', 'mnote.block.insert_after', 'mnote.block.delete', 'mnote.block.move_after' ].indexOf(String(toolName || '').trim()) >= 0; if (!writesCurrentPage) return; var documentId = pageAiToolEventDeepFindString(toolEvent, ['documentId', 'document_id'], 0) || currentDocumentId(); var workspaceId = pageAiToolEventDeepFindString(toolEvent, ['workspaceId', 'workspace_id'], 0) || resolveWorkspaceId(document.body); try { window.dispatchEvent(new CustomEvent('mnote:page-ai-tool-write-completed', { detail: { toolName: toolName, normalizedToolName: normalizedTool, documentId: documentId, workspaceId: workspaceId, runId: String(toolEvent && (toolEvent.run_id || toolEvent.runId) || runId || ''), traceId: String(toolEvent && (toolEvent.trace_id || toolEvent.traceId) || runTraceId || ''), toolCallId: String(toolEvent && (toolEvent.tool_call_id || toolEvent.toolCallId || toolEvent.id) || '') } })); } catch (error) { console.warn('mnote 页面 AI 写入刷新事件派发失败', error); } } function pageAiApplyToolEvent(eventName, payloadText, runId, runTraceId) { var toolEvent = null; try { toolEvent = JSON.parse(payloadText || 'null'); } catch (_) { toolEvent = {}; } var rawToolName = toolEvent && (toolEvent.name || toolEvent.tool || toolEvent.toolName || ''); var toolName = String(rawToolName || eventName); var toolCallId = String(toolEvent && (toolEvent.tool_call_id || toolEvent.toolCallId || toolEvent.id) || (runId + ':' + toolName)); var eventStatus = String(toolEvent && toolEvent.status || '').trim(); var status = eventName === 'tool.completed' ? (toolEvent && toolEvent.error ? 'failed' : 'completed') : (eventName === 'tool.failed' ? 'failed' : (eventStatus || 'running')); if (status === 'in_progress' || status === 'pending') status = 'running'; var traceId = String(toolEvent && (toolEvent.trace_id || toolEvent.traceId) || runTraceId || ''); var auditId = String(toolEvent && (toolEvent.audit_id || toolEvent.auditId) || ''); var argsSummary = pageAiPreviewValue(toolEvent && (toolEvent.arguments || toolEvent.args || toolEvent.input)); var resultSource = toolEvent && (toolEvent.summary || toolEvent.result || toolEvent.output); if (!resultSource && status === 'failed') { resultSource = [toolEvent && toolEvent.code, toolEvent && toolEvent.error].filter(Boolean).join(' '); } var resultSummary = pageAiPreviewValue(resultSource); pageUiState.pageAiLastToolCall = { event: eventName, name: toolName, runId: String(toolEvent && (toolEvent.run_id || toolEvent.runId) || runId), traceId: traceId, auditId: auditId }; var rawLocations = toolEvent && toolEvent.locations; var locations = Array.isArray(rawLocations) ? rawLocations.filter(function(l) { return typeof l === 'string' || (typeof l === 'object' && l && l.path); }).map(function(l) { return typeof l === 'string' ? l : l.path; }) : []; var existing = pageUiState.pageAiMessages.find(function(item) { return item.role === 'tool' && item.toolCallId === toolCallId; }); if (!rawToolName && existing && existing.toolName) toolName = existing.toolName; if (!existing) { existing = { role: 'tool', content: toolName, toolCallId: toolCallId, toolName: toolName, toolKind: String(toolEvent && toolEvent.kind || ''), status: status, argsSummary: '', resultSummary: '', locations: locations, traceId: traceId, auditId: auditId }; pageUiState.pageAiMessages.push(existing); } existing.content = toolName; existing.toolName = toolName; existing.toolKind = String(toolEvent && toolEvent.kind || existing.toolKind || ''); existing.status = status; existing.traceId = traceId || existing.traceId || ''; existing.auditId = auditId || existing.auditId || ''; if (argsSummary) existing.argsSummary = argsSummary; if (resultSummary) existing.resultSummary = resultSummary; if (locations.length) existing.locations = locations; if (status === 'completed') { pageAiNotifyToolWriteCompleted(toolName, toolEvent, runId, runTraceId); } } async function pageAiCancelQueuedRun(queueId) { queueId = String(queueId || '').trim(); if (!queueId) 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), { method: 'DELETE', headers: { 'accept': 'application/json' } }); var payload = await response.json().catch(function(){ return null; }); if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'queue_cancel_failed_' + response.status)); pageUiState.pageAiQueuedItems = pageUiState.pageAiQueuedItems.filter(function(entry) { return entry.queueId !== queueId; }); var queueLength = Number(payload && (payload.queueLength || payload.queue_length || 0)); pageUiState.pageAiQueueLength = Number.isFinite(queueLength) ? Math.max(0, queueLength) : pageUiState.pageAiQueuedItems.length; pageUiState.pageAiMessages.push({ role: 'assistant', content: '已取消一条 AI 队列项。' }); } catch (error) { pageUiState.pageAiMessages.push({ role: 'assistant', content: '取消 AI 队列项失败:' + (error instanceof Error ? error.message : String(error)) }); } renderPageAiConversation(); renderPageAiControls(); } function pageAiSetContextScope(scope) { var next = String(scope || '').trim() || 'page'; pageUiState.pageAiContextScope = next; document.documentElement.setAttribute('data-mnote-page-ai-context-scope', next); } function pageAiHermesSettingsUrl() { var configured = String(window.__mnoteHermesSettingsUrl || '').trim(); return configured || ''; } function pageAiOpenHermesSettings() { var url = pageAiHermesSettingsUrl(); if (url) { window.open(url, '_blank', 'noopener,noreferrer'); return; } pageUiState.pageAiProfileError = '未配置 Hermes 设置入口:请设置 MNOTE_WEB_HERMES_UPSTREAM_URL。'; renderPageAiControls(); } function pageAiNormalizeTools(payload) { var upstream = pageAiUnwrapUpstream(payload) || {}; var tools = pageAiNormalizeArray(upstream.tools || upstream); return tools.map(function(tool) { var name = String(tool && (tool.name || tool.toolName || tool.tool) || '').trim(); if (!name) return null; return { name: name, description: String(tool && tool.description || '').trim(), scope: String(tool && (tool.scope || tool.requiredScope || '') || '').trim(), kind: String(tool && (tool.kind || tool.mode || '') || '').trim(), status: String(tool && (tool.status || tool.permission || 'available') || '').trim(), enabled: tool && tool.enabled !== false, unavailableReason: String(tool && (tool.unavailableReason || tool.reason || '') || '').trim() }; }).filter(Boolean); } function pageAiErrorMessage(payload, fallback) { if (!payload || typeof payload !== 'object') return fallback; return String(payload.message || payload.error || payload.code || fallback || '').trim() || fallback; } function pageAiNormalizeGatewayHealth(payload) { var upstream = pageAiUnwrapUpstream(payload) || payload || {}; return { ok: Boolean(upstream.ok), profile: upstream.profile || null, gateway: upstream.gateway || null, suggestions: pageAiNormalizeArray(upstream.suggestions).map(function(item) { return String(item || '').trim(); }).filter(Boolean) }; } function pageAiSetDraftForSection(section, value) { pageUiState.pageAiProfileMemoryDrafts[section] = String(value == null ? '' : value); } function pageAiApplyProfileMemory(payload) { var upstream = pageAiUnwrapUpstream(payload) || {}; pageUiState.pageAiProfileMemory = { memory: String(upstream.memory || ''), user: String(upstream.user || ''), soul: String(upstream.soul || '') }; pageUiState.pageAiProfileMemoryDrafts = { memory: pageUiState.pageAiProfileMemory.memory, user: pageUiState.pageAiProfileMemory.user, soul: pageUiState.pageAiProfileMemory.soul }; } async function pageAiLoadTools() { try { var response = await fetch('/api/hermes/client/tools?scope=mnote&profile=' + encodeURIComponent(pageAiCurrentProfile()), { headers: { 'accept': 'application/json' } }); var payload = await response.json().catch(function(){ return null; }); if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'tools_failed_' + response.status)); pageUiState.pageAiTools = pageAiNormalizeTools(payload); pageUiState.pageAiToolsError = ''; } catch (error) { pageUiState.pageAiToolsError = error instanceof Error ? error.message : String(error); } renderPageAiControls(); } async function pageAiLoadGatewayHealth() { try { var response = await fetch('/api/hermes/client/gateway/health?profile=' + encodeURIComponent(pageAiCurrentProfile()), { headers: { 'accept': 'application/json' } }); var payload = await response.json().catch(function(){ return null; }); if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'gateway_health_failed_' + response.status)); pageUiState.pageAiGatewayHealth = pageAiNormalizeGatewayHealth(payload); pageUiState.pageAiGatewayHealthError = ''; } catch (error) { pageUiState.pageAiGatewayHealthError = error instanceof Error ? error.message : String(error); } renderPageAiControls(); } async function pageAiStopRun() { var runId = String(pageUiState.pageAiCurrentRunId || '').trim(); if (!runId || pageUiState.pageAiRunStatus === 'idle' || pageUiState.pageAiRunStatus === 'completed') return; pageUiState.pageAiStoppedRunIds[runId] = true; try { var response = await fetch('/api/hermes/client/runs/' + encodeURIComponent(runId) + '/abort', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ sessionId: pageUiState.pageAiActiveSessionId, profile: pageAiRunProfile(), acpRuntime: pageUiState.pageAiAcpRuntime || 'reasonix', reason: 'page_ai_user_stop' }) }); var payload = await response.json().catch(function(){ return null; }); if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'run_abort_failed_' + response.status)); pageAiApplyRuntimeState(payload && payload.runtime); pageAiSetRunStatus('aborted', runId); pageUiState.pageAiMessages.push({ role: 'assistant', content: '已请求停止当前 AI run。' }); } catch (error) { pageAiSetRunStatus('failed', runId); pageUiState.pageAiMessages.push({ role: 'assistant', content: '停止 AI run 失败:' + (error instanceof Error ? error.message : String(error)) }); } renderPageAiControls(); renderPageAiConversation(); } function pageAiLoadSkillCatalog(runtime, profile) { var params = new URLSearchParams(); params.set('runtime', runtime); if (runtime === 'mnote') { params.set('agentId', pageUiState.pageAiAgentId || 'reasonix'); } else if (runtime === 'reasonix') { params.set('runtime', 'reasonix'); } else if (runtime === 'hermes' && profile) { params.set('profileId', profile); } return fetch('/api/hermes/client/skills?' + params.toString(), { headers: { 'accept': 'application/json' } }).then(function(response) { return response.json().catch(function(){ return null; }).then(function(payload) { if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'skills_failed_' + response.status)); return pageAiNormalizeSkills(payload); }); }); } async function pageAiLoadProfiles() { try { var response = await fetch('/api/ai/agent-profiles?agentId=hermes', { headers: { 'accept': 'application/json' } }); var payload = await response.json().catch(function(){ return null; }); if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'profiles_failed_' + response.status)); var profiles = pageAiNormalizeProfiles(payload); pageUiState.pageAiProfiles = profiles.length ? profiles : [{ profileId: 'shared_lite', name: 'shared_lite', alias: 'Lite', kind: 'shared', readonly: true, canManageSkills: false }]; try { var runtimeResponse = await fetch('/api/hermes/client/profiles', { headers: { 'accept': 'application/json' } }); var runtimePayload = await runtimeResponse.json().catch(function(){ return null; }); var acpRuntimes = Array.isArray(runtimePayload && runtimePayload.acpRuntimes) ? runtimePayload.acpRuntimes : []; pageUiState.pageAiAcpRuntimes = pageAiNormalizeAcpRuntimes(acpRuntimes); } catch (_) { pageUiState.pageAiAcpRuntimes = pageAiNormalizeAcpRuntimes(pageUiState.pageAiAcpRuntimes); } var current = pageAiCurrentProfile(); var hasCurrent = pageUiState.pageAiProfiles.some(function(profile) { return pageAiProfileValue(profile) === current; }); var personal = pageUiState.pageAiProfiles.find(function(profile) { return profile.kind === 'personal'; }); var active = pageAiProfileValue(pageUiState.pageAiProfiles.find(function(profile) { return profile.active; })); pageAiSetActiveProfile(hasCurrent && current !== 'default' ? current : (active || pageAiProfileValue(personal) || pageAiProfileValue(pageUiState.pageAiProfiles[0]) || current)); pageUiState.pageAiProfileError = ''; void pageAiLoadGatewayHealth(); } catch (error) { pageUiState.pageAiProfileError = error instanceof Error ? error.message : String(error); if (!pageUiState.pageAiProfiles.length) pageUiState.pageAiProfiles = [{ profileId: 'shared_lite', name: 'shared_lite', alias: 'Lite', kind: 'shared', readonly: true, canManageSkills: false }]; pageUiState.pageAiAcpRuntimes = pageAiNormalizeAcpRuntimes(pageUiState.pageAiAcpRuntimes); pageAiSetActiveProfile(pageAiCurrentProfile()); } renderPageAiProviderButtons(); renderPageAiControls(); } async function pageAiSwitchProfile(profileName) { var next = String(profileName || '').trim(); if (!next) return; var previousSkillSource = pageAiCurrentSkillSource(); pageAiSetActiveProfile(next); pageAiPersistRawAiPreference('ai.agent.hermes.profile_id', next); if (previousSkillSource.indexOf('hermes:') === 0) { pageUiState.pageAiActiveSkillSource = 'hermes:' + next; pageAiPersistAiPreference('skills.active_source', pageUiState.pageAiActiveSkillSource); } pageUiState.pageAiSkills = { categories: [], archived: [] }; pageUiState.pageAiSkillCatalogs = Object.assign({}, pageUiState.pageAiSkillCatalogs || {}, { hermes: { categories: [], archived: [] } }); pageUiState.pageAiSkillError = ''; renderPageAiProviderButtons(); renderPageAiControls(); try { pageUiState.pageAiSessions = pageUiState.pageAiSessions.filter(function(session) { return session && session.profile === next; }); pageUiState.pageAiActiveSessionId = ''; pageUiState.pageAiMessages = []; pageAiPersistSessions(); await pageAiEnsureHermesSession(true); await pageAiLoadProfileMemory(); await pageAiLoadSkills(); await pageAiLoadTools(); await pageAiLoadGatewayHealth(); renderPageAiControls(); } catch (error) { pageUiState.pageAiProfileError = error instanceof Error ? error.message : String(error); } renderPageAiConversation(); } async function pageAiLoadProfileMemory() { try { var response = await fetch('/api/hermes/client/profile-memory?profileId=' + encodeURIComponent(pageAiCurrentProfile()), { headers: { 'accept': 'application/json' } }); var payload = await response.json().catch(function(){ return null; }); if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'profile_memory_failed_' + response.status)); pageAiApplyProfileMemory(payload); pageUiState.pageAiProfileMemoryError = ''; } catch (error) { pageUiState.pageAiProfileMemoryError = error instanceof Error ? error.message : String(error); } renderPageAiControls(); renderPageAiConversation(); } async function pageAiSaveProfileMemory(section) { var normalized = String(section || '').trim(); if (['memory', 'user', 'soul'].indexOf(normalized) < 0) return; var content = String(pageUiState.pageAiProfileMemoryDrafts[normalized] || ''); try { var response = await fetch('/api/hermes/client/profile-memory', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ profileId: pageAiCurrentProfile(), section: normalized, content: content }) }); var payload = await response.json().catch(function(){ return null; }); if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'profile_memory_save_failed_' + response.status)); pageUiState.pageAiProfileMemory[normalized] = content; pageUiState.pageAiProfileMemoryError = ''; document.documentElement.setAttribute('data-mnote-page-ai-memory-saved', normalized); } catch (error) { pageUiState.pageAiProfileMemoryError = error instanceof Error ? error.message : String(error); } renderPageAiControls(); renderPageAiConversation(); } async function pageAiLoadSkills() { var requestedSource = pageAiCurrentSkillSource(); var sourceParts = pageAiSkillSourceParts(requestedSource); var loadSeq = (Number(pageUiState.pageAiSkillLoadSeq || 0) || 0) + 1; pageUiState.pageAiSkillLoadSeq = loadSeq; try { var catalog = await pageAiLoadSkillCatalog(sourceParts.group, sourceParts.profile || pageAiCurrentProfile()); if (pageUiState.pageAiSkillLoadSeq !== loadSeq || pageAiCurrentSkillSource() !== requestedSource) return; pageUiState.pageAiSkillCatalogs = Object.assign({}, pageUiState.pageAiSkillCatalogs || {}, { [sourceParts.group]: catalog, [requestedSource]: catalog }); pageUiState.pageAiSkills = catalog; pageUiState.pageAiSkillError = ''; } catch (error) { if (pageUiState.pageAiSkillLoadSeq !== loadSeq || pageAiCurrentSkillSource() !== requestedSource) return; pageUiState.pageAiSkillError = error instanceof Error ? error.message : String(error); pageUiState.pageAiSkills = { categories: [], archived: [] }; pageUiState.pageAiSkillCatalogs = pageUiState.pageAiSkillCatalogs || { mnote: { categories: [], archived: [] }, reasonix: { categories: [], archived: [] }, hermes: { categories: [], archived: [] } }; } renderPageAiControls(); renderPageAiConversation(); } async function pageAiToggleSkill(skillName, enabled, group, profile) { var name = String(skillName || '').trim(); if (!name) return; var skillGroup = String(group || 'hermes').trim() || 'hermes'; var skillProfile = String(profile || pageAiCurrentProfile() || '').trim(); if (skillGroup === 'reasonix') { pageAiSetSkillPreference(skillGroup, name, Boolean(enabled), skillProfile); document.documentElement.setAttribute('data-mnote-page-ai-skill-toggled', skillGroup + ':' + name); renderPageAiControls(); return; } if (skillGroup === 'mnote') { try { var mnoteResponse = await fetch('/api/hermes/client/skills/toggle', { method: 'PUT', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ skillKind: 'mnote_builtin', name: name, enabled: Boolean(enabled) }) }); var mnotePayload = await mnoteResponse.json().catch(function(){ return null; }); if (!mnoteResponse.ok) throw new Error(pageAiErrorMessage(mnotePayload, 'mnote_skill_toggle_failed_' + mnoteResponse.status)); pageAiSetSkillPreference(skillGroup, name, Boolean(enabled), skillProfile); pageAiNormalizeArray(pageUiState.pageAiSkillCatalogs && pageUiState.pageAiSkillCatalogs.mnote && pageUiState.pageAiSkillCatalogs.mnote.categories).forEach(function(category) { pageAiNormalizeArray(category.skills).forEach(function(skill) { if (skill.id === name || skill.name === name) skill.enabled = Boolean(enabled); }); }); document.documentElement.setAttribute('data-mnote-page-ai-skill-toggled', skillGroup + ':' + name); } catch (error) { pageUiState.pageAiSkillError = error instanceof Error ? error.message : String(error); } renderPageAiControls(); return; } var previous = null; pageAiToggleableSkillEntries('hermes', skillProfile || pageAiCurrentProfile()).forEach(function(skill) { if (skill.id === name || skill.name === name) previous = skill.enabled !== false; }); try { var response = await fetch('/api/hermes/client/skills/toggle', { method: 'PUT', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ profile: skillProfile || pageAiCurrentProfile(), profileId: skillProfile || pageAiCurrentProfile(), skillKind: 'hermes_profile', name: name, enabled: Boolean(enabled) }) }); var payload = await response.json().catch(function(){ return null; }); if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'skill_toggle_failed_' + response.status)); var hermesCatalog = pageUiState.pageAiSkillCatalogs && pageUiState.pageAiSkillCatalogs.hermes ? pageUiState.pageAiSkillCatalogs.hermes : pageUiState.pageAiSkills; pageAiNormalizeArray(hermesCatalog.categories).forEach(function(category) { pageAiNormalizeArray(category.skills).forEach(function(skill) { if (skill.name === name || skill.id === name) skill.enabled = Boolean(enabled); }); }); pageUiState.pageAiSkills = hermesCatalog; pageAiSetSkillPreference('hermes', name, Boolean(enabled), skillProfile || pageAiCurrentProfile()); document.documentElement.setAttribute('data-mnote-page-ai-skill-toggled', name); pageUiState.pageAiSkillError = ''; } catch (error) { pageUiState.pageAiSkillError = error instanceof Error ? error.message : String(error); if (previous != null) { var rollbackCatalog = pageUiState.pageAiSkillCatalogs && pageUiState.pageAiSkillCatalogs.hermes ? pageUiState.pageAiSkillCatalogs.hermes : pageUiState.pageAiSkills; pageAiNormalizeArray(rollbackCatalog.categories).forEach(function(category) { pageAiNormalizeArray(category.skills).forEach(function(skill) { if (skill.name === name || skill.id === name) skill.enabled = previous; }); }); } } renderPageAiControls(); renderPageAiConversation(); } async function pageAiToggleTool(toolName, enabled) { var name = String(toolName || '').trim(); if (!name) return; var previous = null; pageAiNormalizeArray(pageUiState.pageAiTools).forEach(function(tool) { if (tool.name === name && previous == null) previous = tool.enabled !== false; }); try { var response = await fetch('/api/hermes/client/tools/toggle', { method: 'PUT', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ profile: pageAiCurrentProfile(), name: name, enabled: Boolean(enabled) }) }); var payload = await response.json().catch(function(){ return null; }); if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'tool_toggle_failed_' + response.status)); pageAiNormalizeArray(pageUiState.pageAiTools).forEach(function(tool) { if (tool.name === name) { tool.enabled = Boolean(enabled); tool.status = Boolean(enabled) ? 'available' : 'disabled'; tool.unavailableReason = Boolean(enabled) ? '' : '当前 Hermes profile 已关闭该 mnote tool'; } }); document.documentElement.setAttribute('data-mnote-page-ai-tool-toggled', name); pageUiState.pageAiToolsError = ''; } catch (error) { pageUiState.pageAiToolsError = error instanceof Error ? error.message : String(error); if (previous != null) { pageAiNormalizeArray(pageUiState.pageAiTools).forEach(function(tool) { if (tool.name === name) tool.enabled = previous; }); } } renderPageAiControls(); } function openPageAiDrawer() { pageUiState.pageAiAgentId = pageAiCurrentAgentId(); pageAiEnsureContextRefState(); pageAiLoadSessions(); renderPageAiSuggestions(); renderPageAiConversation(); renderPageAiProviderButtons(); renderPageAiControls(); var drawer = ensurePageAiDrawer(); drawer.hidden = false; pageUiState.pageAiOpen = true; updatePageAiTriggerState(); Promise.all([ pageAiLoadProfiles(), pageAiLoadProfileMemory(), pageAiLoadSkills(), pageAiLoadTools(), pageAiLoadGatewayHealth(), pageAiLoadAiPreferences(), pageAiLoadAllowedRoots(), pageAiLoadBackendSessions() ]).then(function() { renderPageAiControls(); }).catch(function() {}).then(function() { return pageAiEnsureHermesSession(); }).then(function() { return pageAiRestoreHermesSession(); }).then(function() { renderPageAiControls(); }).catch(function(error) { pageUiState.pageAiMessages.push({ role: 'assistant', content: '当前 AI 不可用:' + (error instanceof Error ? error.message : String(error)) }); renderPageAiConversation(); renderPageAiControls(); }); } function closePageAiDrawer() { var drawer = document.querySelector('[data-testid="wolai-page-ai-drawer"]'); if (drawer instanceof HTMLElement) drawer.hidden = true; pageUiState.pageAiOpen = false; updatePageAiTriggerState(); } function isPageAiDrawerOpen() { var drawer = document.querySelector('[data-testid="wolai-page-ai-drawer"]'); return drawer instanceof HTMLElement && !drawer.hidden; } async function streamPageAiResponse(response, onEvent) { if (!response.body || typeof response.body.getReader !== 'function') return; var reader = response.body.getReader(); var decoder = new TextDecoder(); var buffer = ''; while (true) { var chunk = await reader.read(); if (chunk.done) break; buffer += decoder.decode(chunk.value, { stream: true }); var frames = buffer.split('\n\n'); buffer = frames.pop() || ''; frames.forEach(function(frame) { var eventName = ''; var dataLines = []; frame.split('\n').forEach(function(line) { if (line.startsWith('event:')) eventName = line.slice(6).trim(); if (line.startsWith('data:')) dataLines.push(line.slice(5).trim()); }); var payloadText = dataLines.join('\n'); if (!eventName && payloadText) { try { var parsed = JSON.parse(payloadText); eventName = parsed && parsed.event ? String(parsed.event) : ''; } catch (_) {} } if (eventName) onEvent(eventName, payloadText); }); } } function pageAiDecodeDeltaText(payloadText) { try { var payload = JSON.parse(payloadText || 'null'); return String((payload && (payload.text || payload.delta)) || ''); } catch (_) { return String(payloadText || ''); } } function pageAiEnsureStreamingAssistantMessage(runId) { var id = String(runId || pageUiState.pageAiCurrentRunId || ''); var existing = pageUiState.pageAiMessages.find(function(item) { return item.role === 'assistant' && item.streaming === true && item.runId === id; }); if (existing) return existing; existing = { role: 'assistant', content: '', runId: id, streaming: true }; pageUiState.pageAiMessages.push(existing); return existing; } function pageAiAppendStreamingAssistantDelta(runId, deltaText) { var delta = String(deltaText || ''); if (!delta) return ''; var message = pageAiEnsureStreamingAssistantMessage(runId); message.content = String(message.content || '') + delta; pageAiSyncCurrentSessionMessages(); renderPageAiConversation(); return message.content; } function pageAiFinishStreamingAssistantMessage(runId, finalText, promptText) { var id = String(runId || pageUiState.pageAiCurrentRunId || ''); var message = pageUiState.pageAiMessages.find(function(item) { return item.role === 'assistant' && item.streaming === true && item.runId === id; }); var text = String(finalText || (message && message.content) || ''); var content = humanizePageAiResponse(text, promptText); if (message) { message.content = content; message.streaming = false; } else if (content) { pageUiState.pageAiMessages.push({ role: 'assistant', content: content, runId: id }); } pageAiSyncCurrentSessionMessages(); pageAiPersistSessions(); } function pageAiLooksLikeBlockEdit(prompt) { var text = searchText(prompt); return ['新增', '添加', '插入', '删除', '删掉', '修改', '替换', '改成', '移动', '移到', 'move', 'replace', 'delete', 'insert'].some(function(word) { return text.indexOf(word) >= 0; }); } async function pageAiTryBlockEditWorkflow(prompt, scopedContext, runTargetSnapshot) { if (currentSourceKind() === 'local_folder') return false; if (!pageAiLooksLikeBlockEdit(prompt)) return false; var runId = 'page-ai-fast-' + Date.now().toString(36); var traceId = 'page-ai-fast-' + Date.now().toString(36); pageAiSetRunStatus('running', runId); renderPageAiControls(); var started = Date.now(); var response = await fetch('/api/page-ai/block-edit-workflow', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ workspaceId: resolveWorkspaceId(document.body), documentId: currentDocumentId(), sourceKind: currentSourceKind(), rootUri: currentRootUri(), sessionId: pageUiState.pageAiActiveSessionId, runId: runId, profile: pageAiCurrentProfile(), model: pageAiMnoteToolModel(), message: prompt, pageContext: scopedContext.pageContext, editorTarget: scopedContext.editorTarget, runTargetSnapshot: runTargetSnapshot || null, selectedBlockId: scopedContext.selectedBlockId, selectedText: scopedContext.selectedText, traceId: traceId }) }); var payload = await response.json().catch(function() { return null; }); if (!response.ok || !payload || payload.ok !== true) { var code = payload && payload.code ? String(payload.code) : ''; if (code === 'page_ai_workflow_not_block_edit') return false; pageUiState.pageAiMessages.push({ role: 'tool', toolName: 'mnote.page_ai.block_edit_workflow', status: 'failed', toolCallId: runId, resultSummary: pageAiErrorMessage(payload, 'fast_path_failed_' + response.status) }); renderPageAiConversation(); pageAiSetRunStatus('failed', runId); renderPageAiControls(); return true; } pageUiState.pageAiMessages.push({ role: 'tool', toolName: 'mnote.page_ai.block_edit_workflow', status: 'completed', toolCallId: runId, resultSummary: 'operations=' + String(Array.isArray(payload.operations) ? payload.operations.length : 0) + ' · ' + String(Date.now() - started) + 'ms' }); pageUiState.pageAiMessages.push({ role: 'assistant', content: payload.message || '已通过页面块编辑快路径完成写入。' }); pageAiSetRunStatus('completed', runId); try { window.dispatchEvent(new CustomEvent('mnote:page-ai-tool-write-completed', { detail: { toolName: 'mnote.doc.apply_block_ops', normalizedToolName: 'mnote.doc.apply.block.ops', documentId: currentDocumentId(), workspaceId: resolveWorkspaceId(document.body), runId: runId, traceId: traceId, toolCallId: runId } })); } catch (_) {} var currentSession = pageAiCurrentSession(); if (currentSession) { currentSession.messages = pageUiState.pageAiMessages.slice(); currentSession.updatedAt = Date.now(); } pageAiPersistSessions(); renderPageAiConversation(); renderPageAiControls(); return true; } async function sendPageAiMessage(text) { var allowQueue = ['running', 'tool_calling'].indexOf(pageUiState.pageAiRunStatus) >= 0; if (pageUiState.pageAiBusy && !allowQueue) return; var prompt = searchText(text); if (!prompt) return; if (!allowQueue) pageUiState.pageAiBusy = true; var currentSession = null; try { await pageAiEnsureHermesSession(); if (!allowQueue) pageAiSetRunStatus('queued'); renderPageAiControls(); var contextSnapshot = currentPageAiContextSnapshot(); var scopedContext = pageAiScopedPageContext(contextSnapshot); scopedContext.editorTarget = currentPageAiScopedEditorTarget(); assertPageAiTargetInCurrentWorkspace(scopedContext.editorTarget); await assertPageAiTargetWritable(scopedContext.editorTarget); var runTargetSnapshot = pageAiBuildRunTargetSnapshot(scopedContext, prompt); if (currentSourceKind() === 'local_folder' && !pageAiNormalizeArray(pageUiState.pageAiAllowedRoots).length) { await pageAiLoadAllowedRoots(); } var contextRefs = pageAiBuildContextRefs(scopedContext, runTargetSnapshot); var allowedRoots = pageAiBuildAllowedRoots(); var agentTargetPackage = pageAiBuildAgentTargetPackage(scopedContext, runTargetSnapshot); if (scopedContext.pageContext && scopedContext.pageContext.aiContext) { scopedContext.pageContext.aiContext.runTargetSnapshot = runTargetSnapshot; scopedContext.pageContext.aiContext.agentTargetPackage = agentTargetPackage; } var requestPageContext = pageAiPageContextForRefs(scopedContext.pageContext, contextRefs); pageUiState.pageAiMessages.push({ role: 'user', content: prompt }); currentSession = pageAiCurrentSession(); var runProfile = pageAiRunProfile(); if (currentSession) { if (currentSession.title === '新会话') { currentSession.title = prompt.length > 18 ? prompt.slice(0, 18) + '…' : prompt; } currentSession.messages = pageUiState.pageAiMessages.slice(); currentSession.updatedAt = Date.now(); } pageAiPersistSessions(); renderPageAiConversation(); if (!allowQueue && await pageAiTryBlockEditWorkflow(prompt, scopedContext, runTargetSnapshot)) { return; } var response = await fetch('/api/hermes/client/runs', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ workspaceId: resolveWorkspaceId(document.body), documentId: currentDocumentId(), sourceKind: currentSourceKind(), rootUri: currentRootUri(), agentId: pageAiCurrentAgentId(), sessionId: pageUiState.pageAiActiveSessionId, acpSessionId: currentSession && currentSession.acpSessionId ? currentSession.acpSessionId : '', profile: runProfile, profileId: pageUiState.pageAiAcpRuntime === 'hermes' ? runProfile : '', acpRuntime: pageUiState.pageAiAcpRuntime || 'reasonix', contextScope: pageUiState.pageAiContextScope, contextRefs: contextRefs, allowedRoots: allowedRoots, skillPreferences: { mnote: pageAiSkillPreferenceTable('mnote', ''), reasonix: pageAiSkillPreferenceTable('reasonix', ''), hermes: pageAiSkillPreferenceTable('hermes', runProfile) }, message: prompt, model: pageAiMnoteToolModel(), pageContext: requestPageContext, editorTarget: scopedContext.editorTarget, runTargetSnapshot: runTargetSnapshot, targetPackage: agentTargetPackage, selectedBlockId: scopedContext.selectedBlockId, selectedText: scopedContext.selectedText || '', traceId: 'page-ai-run-' + Date.now().toString(36) }) }); if (!response.ok) { var errorPayload = await response.json().catch(function(){ return null; }); throw new Error(pageAiErrorMessage(errorPayload, 'page_ai_failed_' + response.status)); } var runPayload = await response.json().catch(function(){ return null; }); if (pageAiApplyQueuedRun(runPayload)) { pageUiState.pageAiMessages.push({ role: 'assistant', content: '已加入 AI 队列,前一条 run 完成后继续处理。' }); if (currentSession) { currentSession.messages = pageUiState.pageAiMessages.slice(); currentSession.updatedAt = Date.now(); } pageAiPersistSessions(); renderPageAiConversation(); renderPageAiControls(); return; } if (allowQueue) { throw new Error('hermes_queue_expected_queued_response'); } var upstream = runPayload && runPayload.upstream ? runPayload.upstream : runPayload; var runId = upstream && (upstream.run_id || upstream.runId); if (!runId) throw new Error('hermes_run_missing_run_id'); var runTraceId = String((upstream && (upstream.trace_id || upstream.traceId)) || (runPayload && (runPayload.trace_id || runPayload.traceId)) || ''); pageAiSetRunStatus('running', runId); pageAiSetRunTargetSnapshot(Object.assign({}, runTargetSnapshot, { runId: runId, sessionId: pageUiState.pageAiActiveSessionId, traceId: runTraceId })); renderPageAiControls(); var eventResponse = await fetch('/api/hermes/client/events/' + encodeURIComponent(runId), { headers: { 'accept': 'text/event-stream' } }); if (!eventResponse.ok) { var eventError = await eventResponse.json().catch(function(){ return null; }); throw new Error(pageAiErrorMessage(eventError, 'hermes_events_failed_' + eventResponse.status)); } var assistantText = ''; await streamPageAiResponse(eventResponse, function(eventName, payloadText) { if (eventName === 'message.delta') { if (pageUiState.pageAiStoppedRunIds[runId]) return; assistantText = pageAiAppendStreamingAssistantDelta(runId, pageAiDecodeDeltaText(payloadText)); } if (eventName === 'thought.delta') { try { var thoughtPayload = JSON.parse(payloadText || 'null'); var thoughtText = String((thoughtPayload && (thoughtPayload.delta || thoughtPayload.text)) || ''); if (thoughtText) { var msgs = pageUiState.pageAiMessages; var lastThought = msgs.length > 0 && msgs[msgs.length - 1].kind === 'thought' ? msgs[msgs.length - 1] : null; if (lastThought) { lastThought.content += thoughtText; } else { msgs.push({ role: 'assistant', kind: 'thought', content: thoughtText }); } pageAiSyncCurrentSessionMessages(); pageAiPersistSessions(); renderPageAiConversation(); } } catch (_) {} } if (eventName === 'usage.updated') { try { var usagePayload = JSON.parse(payloadText || 'null') || {}; var sessionForUsage = pageAiCurrentSession(); if (sessionForUsage) { sessionForUsage.usage = { source: 'usage_update', used: Number(usagePayload.used ?? usagePayload.contextUsed ?? 0), size: Number(usagePayload.size ?? usagePayload.contextSize ?? 0) }; pageAiPersistSessions(); } } catch (_) {} renderPageAiControls(); } if (eventName === 'permission.requested' || eventName === 'permission.denied' || eventName === 'permission.allowed') { pageAiApplyPermissionEvent(eventName, payloadText); pageAiSyncCurrentSessionMessages(); pageAiPersistSessions(); renderPageAiConversation(); renderPageAiControls(); } if (eventName === 'run.completed') { try { var completed = JSON.parse(payloadText || 'null'); if (completed && completed.output) assistantText = String(completed.output || ''); if (completed && completed.usage) { var completedSession = pageAiCurrentSession(); if (completedSession) completedSession.usage = completed.usage; } var agentAudit = completed && completed.agentAudit && typeof completed.agentAudit === 'object' ? completed.agentAudit : null; var agentRunReceipt = agentAudit && agentAudit.agentRunReceipt && typeof agentAudit.agentRunReceipt === 'object' ? agentAudit.agentRunReceipt : null; var changedFiles = pageAiNormalizeArray(agentAudit && agentAudit.changedFiles).map(function(file) { if (!file || typeof file !== 'object') return file; return Object.assign({ actorId: String(agentAudit && agentAudit.actorId || ''), actorType: String(agentAudit && agentAudit.actorType || ''), agentKind: String(agentAudit && agentAudit.agentKind || ''), runId: runId }, file); }); if (changedFiles.length) { pageUiState.pageAiMessages.push({ role: 'tool', content: 'agent.changed_files', toolCallId: runId + ':agent.changed_files', toolName: 'agent.changed_files', toolKind: 'audit', status: 'completed', argsSummary: [agentAudit.rootUri, agentAudit.actorId, agentAudit.agentKind].map(function(value) { return String(value || '').trim(); }).filter(Boolean).join(' · '), resultSummary: String(agentAudit.diffSummary || changedFiles.length + ' changed file(s)'), changedFiles: changedFiles, traceId: runTraceId, auditId: String(agentAudit.eventId || '') }); } if (agentRunReceipt || changedFiles.length) { try { pageAiDispatchReceiptRefresh(agentRunReceipt, changedFiles, agentAudit, runId, runTraceId); } catch (_) {} } } catch (_) {} pageAiSetRunStatus('completed', runId); } if (eventName === 'run.failed') { try { var failed = JSON.parse(payloadText || 'null'); assistantText = String((failed && (failed.message || failed.code || failed.error)) || 'AI run failed'); } catch (_) {} pageAiSetRunStatus('failed', runId); } if (eventName === 'run.aborted' || eventName === 'run.cancelled' || eventName === 'run.canceled') { pageUiState.pageAiStoppedRunIds[runId] = true; pageAiSetRunStatus('aborted', runId); } if (eventName === 'session.info.updated') { try { var infoPayload = JSON.parse(payloadText || 'null') || {}; var newTitle = String(infoPayload.title || '').trim(); var acpSessionId = String(infoPayload.acpSessionId || infoPayload.acp_session_id || '').trim(); var sessionForInfo = pageAiCurrentSession(); if (acpSessionId && sessionForInfo) { sessionForInfo.acpSessionId = acpSessionId; sessionForInfo.updatedAt = Date.now(); pageAiPersistSessions(); } if (newTitle) { var sessionForTitle = sessionForInfo || pageAiCurrentSession(); if (sessionForTitle) { sessionForTitle.title = newTitle; sessionForTitle.updatedAt = Date.now(); pageAiPersistSessions(); renderPageAiControls(); } } } catch (_) {} } if (eventName === 'plan.updated') { try { var planPayload = JSON.parse(payloadText || 'null') || {}; var planEntries = Array.isArray(planPayload.entries) ? planPayload.entries : []; if (planEntries.length) { var planMsgs = pageUiState.pageAiMessages; var existingPlan = planMsgs.length > 0 && planMsgs[planMsgs.length - 1].kind === 'plan' ? planMsgs[planMsgs.length - 1] : null; if (existingPlan) { existingPlan.entries = planEntries; existingPlan.updatedAt = Date.now(); } else { planMsgs.push({ role: 'system', kind: 'plan', entries: planEntries, createdAt: Date.now(), updatedAt: Date.now() }); } pageAiSyncCurrentSessionMessages(); pageAiPersistSessions(); renderPageAiConversation(); } } catch (_) {} } if (eventName === 'tool.started' || eventName === 'tool.completed' || eventName === 'tool.failed') { pageAiApplyToolEvent(eventName, payloadText, runId, runTraceId); pageAiSyncCurrentSessionMessages(); pageAiPersistSessions(); renderPageAiConversation(); pageAiSetRunStatus('tool_calling', runId); renderPageAiControls(); } }); if (!pageUiState.pageAiStoppedRunIds[runId]) { pageAiFinishStreamingAssistantMessage(runId, assistantText, prompt); } currentSession = pageAiCurrentSession(); if (currentSession) { currentSession.messages = pageUiState.pageAiMessages.slice(); currentSession.updatedAt = Date.now(); } pageAiPersistSessions(); } catch (error) { pageAiSetRunStatus('failed'); pageUiState.pageAiMessages.push({ role: 'assistant', content: 'AI 当前请求失败:' + (error instanceof Error ? error.message : String(error)) }); currentSession = pageAiCurrentSession(); if (currentSession) { currentSession.messages = pageUiState.pageAiMessages.slice(); currentSession.updatedAt = Date.now(); } pageAiPersistSessions(); } finally { if (!allowQueue) pageUiState.pageAiBusy = false; renderPageAiConversation(); renderPageAiControls(); } } function handlePageAiClick(event, helpers) { var closestAction = helpers && helpers.closestAction; if (typeof closestAction !== 'function') return false; var pageAiClose = closestAction(event.target, '[data-page-ai-action="close"]'); if (pageAiClose) { event.preventDefault(); closePageAiDrawer(); return true; } var pageAiSettings = closestAction(event.target, '[data-page-ai-action="open-hermes-settings"]'); if (pageAiSettings) { event.preventDefault(); pageAiOpenHermesSettings(); return true; } var pageAiStop = closestAction(event.target, '[data-page-ai-action="stop-run"]'); if (pageAiStop) { event.preventDefault(); void pageAiStopRun(); return true; } var pageAiRotate = closestAction(event.target, '[data-page-ai-action="rotate"]'); if (pageAiRotate) { event.preventDefault(); pageUiState.pageAiSuggestionIndex += 1; renderPageAiSuggestions(); return true; } var pageAiIntent = closestAction(event.target, '[data-page-ai-intent]'); if (pageAiIntent) { event.preventDefault(); var intentName = pageAiIntent.getAttribute('data-page-ai-intent') || ''; if (intentName === 'create-summary') void sendPageAiMessage('请通过 Hermes 调用 mnote.artifact.create_summary,为当前页面创建或更新 AI Summary。'); if (intentName === 'create-ai-note') void sendPageAiMessage('请通过 Hermes 调用 mnote.artifact.create_ai_note,基于当前页面创建一篇新的 AI Note。'); return true; } var pageAiTab = closestAction(event.target, '[data-page-ai-tab]'); if (pageAiTab) { event.preventDefault(); pageUiState.pageAiPage = pageAiTab.getAttribute('data-page-ai-tab') || 'chat'; if (pageUiState.pageAiPage === 'runtime') void pageAiLoadGatewayHealth(); if (pageUiState.pageAiPage === 'skills') void pageAiLoadSkills(); renderPageAiControls(); renderPageAiConversation(); return true; } var pageAiProvider = closestAction(event.target, '[data-page-ai-provider]'); if (pageAiProvider) { event.preventDefault(); pageUiState.pageAiProvider = pageAiProvider.getAttribute('data-page-ai-provider') || 'hermes'; renderPageAiProviderButtons(); return true; } var pageAiAgent = closestAction(event.target, '[data-page-ai-agent-id]'); if (pageAiAgent) { event.preventDefault(); var nextAgentId = pageAiAgent.getAttribute('data-page-ai-agent-id') || 'reasonix'; var nextProfileId = pageAiAgent.getAttribute('data-page-ai-profile-id') || ''; pageAiSetAgentId(nextAgentId); if (nextProfileId) { pageUiState.pageAiAgentPopoverOpen = false; void pageAiSwitchProfile(nextProfileId); renderPageAiControls(); } return true; } var pageAiAgentButton = closestAction(event.target, '[data-page-ai-agent-button]'); if (pageAiAgentButton) { event.preventDefault(); pageAiSetAgentPopoverOpen(pageAiAgentButton.getAttribute('aria-expanded') !== 'true'); return true; } var pageAiContextButton = closestAction(event.target, '[data-page-ai-context-button]'); if (pageAiContextButton) { event.preventDefault(); pageAiSetContextPopoverOpen(pageAiContextButton.getAttribute('aria-expanded') !== 'true'); return true; } var pageAiTargetButton = closestAction(event.target, '[data-page-ai-target-button]'); if (pageAiTargetButton) { event.preventDefault(); pageAiSetTargetPopoverOpen(pageAiTargetButton.getAttribute('aria-expanded') !== 'true'); return true; } var pageAiAction = closestAction(event.target, '[data-page-ai-action]'); if (pageAiAction) { event.preventDefault(); var action = pageAiAction.getAttribute('data-page-ai-action') || ''; if (action === 'close-agent-popover') pageAiSetAgentPopoverOpen(false); if (action === 'close-context-popover') pageAiSetContextPopoverOpen(false); if (action === 'close-target-popover') pageAiSetTargetPopoverOpen(false); if (action === 'new-session') { pageUiState.pageAiPage = 'chat'; pageAiStartNewSession(); } if (action === 'history') { pageAiLoadSessions(); pageUiState.pageAiPage = pageUiState.pageAiPage === 'history' ? 'chat' : 'history'; renderPageAiControls(); renderPageAiConversation(); if (pageUiState.pageAiPage === 'history') { void pageAiLoadBackendSessions().catch(function(error) { pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error); renderPageAiControls(); }); } } if (action === 'send') { var input = ensurePageAiDrawer().querySelector('[data-page-ai-input]'); if (input instanceof HTMLTextAreaElement) { var message = input.value; input.value = ''; void sendPageAiMessage(message); } } if (action === 'cancel-queued-run') { void pageAiCancelQueuedRun(pageAiAction.getAttribute('data-page-ai-queue-id')); } return true; } var pageAiTargetOption = closestAction(event.target, '[data-page-ai-target-option]'); if (pageAiTargetOption) { event.preventDefault(); pageAiSelectTarget(pageAiTargetOption.getAttribute('data-page-ai-target-option') || ''); return true; } var pageAiContextRef = closestAction(event.target, '[data-page-ai-context-ref]'); if (pageAiContextRef) { event.preventDefault(); pageAiToggleContextRef(pageAiContextRef.getAttribute('data-page-ai-context-ref') || ''); return true; } var pageAiMemorySave = closestAction(event.target, '[data-page-ai-memory-save]'); if (pageAiMemorySave) { event.preventDefault(); void pageAiSaveProfileMemory(pageAiMemorySave.getAttribute('data-page-ai-memory-save') || ''); return true; } var pageAiSkillToggle = closestAction(event.target, '[data-page-ai-skill-toggle]'); if (pageAiSkillToggle) { event.preventDefault(); void pageAiToggleSkill( pageAiSkillToggle.getAttribute('data-page-ai-skill-toggle') || '', pageAiSkillToggle.getAttribute('aria-pressed') !== 'true', pageAiSkillToggle.getAttribute('data-page-ai-skill-group') || '', pageAiSkillToggle.getAttribute('data-page-ai-skill-profile') || '' ); return true; } var pageAiSkillGroupToggle = closestAction(event.target, '[data-page-ai-skill-group-toggle]'); if (pageAiSkillGroupToggle) { event.preventDefault(); pageAiToggleSkillGroup(pageAiSkillGroupToggle.getAttribute('data-page-ai-skill-group-toggle') || ''); return true; } var pageAiToolToggle = closestAction(event.target, '[data-page-ai-tool-toggle]'); if (pageAiToolToggle) { event.preventDefault(); void pageAiToggleTool(pageAiToolToggle.getAttribute('data-page-ai-tool-toggle') || '', pageAiToolToggle.getAttribute('aria-pressed') !== 'true'); return true; } var pageAiSessionResume = closestAction(event.target, '[data-page-ai-session-resume]'); if (pageAiSessionResume) { event.preventDefault(); void pageAiResumeBackendSession(pageAiSessionResume.getAttribute('data-page-ai-session-resume') || '').catch(function(error) { pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error); renderPageAiControls(); }); return true; } var pageAiSessionRename = closestAction(event.target, '[data-page-ai-session-rename]'); if (pageAiSessionRename) { event.preventDefault(); void pageAiRenameBackendSession(pageAiSessionRename.getAttribute('data-page-ai-session-rename') || '').catch(function(error) { pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error); renderPageAiControls(); }); return true; } var pageAiSessionDelete = closestAction(event.target, '[data-page-ai-session-delete]'); if (pageAiSessionDelete) { event.preventDefault(); void pageAiDeleteBackendSession(pageAiSessionDelete.getAttribute('data-page-ai-session-delete') || '').catch(function(error) { pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error); renderPageAiControls(); }); return true; } 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'); return true; } var pageAiOpenLocationAction = closestAction(event.target, '[data-page-ai-open-location]'); if (pageAiOpenLocationAction) { event.preventDefault(); var loc = String(pageAiOpenLocationAction.getAttribute('data-page-ai-open-location') || '').trim(); if (loc) pageAiOpenLocation(loc); return true; } var pageAiSession = closestAction(event.target, '[data-page-ai-session]'); if (pageAiSession) { event.preventDefault(); pageAiSetActiveSession(pageAiSession.getAttribute('data-page-ai-session') || ''); return true; } var pageAiSuggestion = closestAction(event.target, '[data-page-ai-suggestion]'); if (pageAiSuggestion) { event.preventDefault(); var text = pageAiSuggestion.getAttribute('data-page-ai-suggestion') || ''; var inputNode = ensurePageAiDrawer().querySelector('[data-page-ai-input]'); if (inputNode instanceof HTMLTextAreaElement) { inputNode.value = text; inputNode.focus(); } return true; } return false; } function handlePageAiKeyDown(event, helpers) { var closestAction = helpers && helpers.closestAction; if (typeof closestAction !== 'function') return false; if (event.key === 'Enter' && !event.shiftKey) { var aiInput = closestAction(event.target, '[data-page-ai-input]'); if (aiInput instanceof HTMLTextAreaElement) { event.preventDefault(); var text = aiInput.value; aiInput.value = ''; void sendPageAiMessage(text); return true; } } return false; } function handlePageAiInput(event, helpers) { var closestAction = helpers && helpers.closestAction; if (typeof closestAction !== 'function') return false; var skillSearch = closestAction(event.target, '[data-page-ai-skill-search]'); if (skillSearch instanceof HTMLInputElement) { pageUiState.pageAiSkillQuery = skillSearch.value; renderPageAiControls(); return true; } var sessionSearch = closestAction(event.target, '[data-page-ai-session-search]'); if (sessionSearch instanceof HTMLInputElement) { var sessionQuery = sessionSearch.value; window.clearTimeout(pageUiState.pageAiSessionSearchTimer || 0); pageUiState.pageAiSessionSearchTimer = window.setTimeout(function() { void pageAiSearchBackendSessions(sessionQuery).catch(function(error) { pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error); renderPageAiControls(); }); }, 200); return true; } var memoryEditor = closestAction(event.target, '[data-page-ai-memory-editor]'); if (memoryEditor instanceof HTMLTextAreaElement) { var section = memoryEditor.getAttribute('data-page-ai-memory-editor') || ''; if (['memory', 'user', 'soul'].indexOf(section) >= 0) pageUiState.pageAiProfileMemoryDrafts[section] = memoryEditor.value; return true; } return false; } function handlePageAiChange(event, helpers) { var closestAction = helpers && helpers.closestAction; if (typeof closestAction !== 'function') return false; var pageAiAcpRuntimeSelect = closestAction(event.target, '[data-page-ai-acp-runtime]'); if (pageAiAcpRuntimeSelect instanceof HTMLSelectElement) { var next = String(pageAiAcpRuntimeSelect.value || 'reasonix').trim() || 'reasonix'; pageUiState.pageAiAcpRuntime = next; pageUiState.pageAiSkills = { categories: [], archived: [] }; pageUiState.pageAiSkillError = ''; pageAiPersistSessions(); void pageAiLoadProfiles(); if (next !== 'reasonix') void pageAiLoadProfileMemory(); void pageAiLoadSkills(); void pageAiLoadBackendSessions().catch(function(error) { pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error); }); renderPageAiControls(); renderPageAiProviderButtons(); return true; } var pageAiProfileSelect = closestAction(event.target, '[data-page-ai-profile-select]'); if (pageAiProfileSelect instanceof HTMLSelectElement) { void pageAiSwitchProfile(pageAiProfileSelect.value); return true; } var pageAiSkillSourceSelect = closestAction(event.target, '[data-page-ai-skill-source-select]'); if (pageAiSkillSourceSelect instanceof HTMLSelectElement) { pageAiSetSkillSource(pageAiSkillSourceSelect.value); return true; } var pageAiSessionAgentFilter = closestAction(event.target, '[data-page-ai-session-agent-filter]'); if (pageAiSessionAgentFilter instanceof HTMLSelectElement) { pageUiState.pageAiSessionAgentFilter = String(pageAiSessionAgentFilter.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); return true; } var pageAiReasonixMemory = closestAction(event.target, '[data-page-ai-reasonix-memory]'); if (pageAiReasonixMemory instanceof HTMLInputElement) { pageAiSetReasonixMemoryEnabled(pageAiReasonixMemory.checked); return true; } var pageAiContextSelect = closestAction(event.target, '[data-page-ai-context-scope]'); if (pageAiContextSelect instanceof HTMLSelectElement) { pageAiSetContextScope(pageAiContextSelect.value); renderPageAiControls(); return true; } return false; } function installPageAiDelegates() { if (pageAiDelegatesInstalled) return; pageAiDelegatesInstalled = true; var helpers = { closestAction: closestAction }; document.addEventListener('click', function(event) { handlePageAiClick(event, helpers); }); document.addEventListener('keydown', function(event) { handlePageAiKeyDown(event, helpers); }); document.addEventListener('input', function(event) { handlePageAiInput(event, helpers); }); document.addEventListener('change', function(event) { handlePageAiChange(event, helpers); }); } return { ensurePageAiStateFacade: (...args) => ensurePageAiStateFacade(...args), installPageAiDelegates: (...args) => installPageAiDelegates(...args), handlePageAiClick: (...args) => handlePageAiClick(...args), handlePageAiKeyDown: (...args) => handlePageAiKeyDown(...args), handlePageAiInput: (...args) => handlePageAiInput(...args), handlePageAiChange: (...args) => handlePageAiChange(...args), openPageAiDrawer: (...args) => openPageAiDrawer(...args), closePageAiDrawer: (...args) => closePageAiDrawer(...args), isPageAiDrawerOpen: (...args) => isPageAiDrawerOpen(...args), ensurePageAiDrawer: (...args) => ensurePageAiDrawer(...args), sendPageAiMessage: (...args) => sendPageAiMessage(...args), pageAiOpenHermesSettings: (...args) => pageAiOpenHermesSettings(...args), pageAiStopRun: (...args) => pageAiStopRun(...args), pageAiLoadGatewayHealth: (...args) => pageAiLoadGatewayHealth(...args), renderPageAiControls: (...args) => renderPageAiControls(...args), renderPageAiConversation: (...args) => renderPageAiConversation(...args), renderPageAiProviderButtons: (...args) => renderPageAiProviderButtons(...args), renderPageAiSuggestions: (...args) => renderPageAiSuggestions(...args), pageAiSaveProfileMemory: (...args) => pageAiSaveProfileMemory(...args), pageAiToggleSkill: (...args) => pageAiToggleSkill(...args), pageAiToggleTool: (...args) => pageAiToggleTool(...args), pageAiResumeBackendSession: (...args) => pageAiResumeBackendSession(...args), pageAiRenameBackendSession: (...args) => pageAiRenameBackendSession(...args), pageAiDeleteBackendSession: (...args) => pageAiDeleteBackendSession(...args), pageAiResolvePermission: (...args) => pageAiResolvePermission(...args), pageAiOpenLocation: (...args) => pageAiOpenLocation(...args), pageAiSetActiveSession: (...args) => pageAiSetActiveSession(...args), pageAiStartNewSession: (...args) => pageAiStartNewSession(...args), pageAiLoadSessions: (...args) => pageAiLoadSessions(...args), pageAiLoadBackendSessions: (...args) => pageAiLoadBackendSessions(...args), pageAiCancelQueuedRun: (...args) => pageAiCancelQueuedRun(...args), pageAiSearchBackendSessions: (...args) => pageAiSearchBackendSessions(...args), pageAiSetTargetPopoverOpen: (...args) => pageAiSetTargetPopoverOpen(...args), pageAiSelectTarget: (...args) => pageAiSelectTarget(...args), pageAiPersistSessions: (...args) => pageAiPersistSessions(...args), pageAiLoadProfiles: (...args) => pageAiLoadProfiles(...args), pageAiLoadProfileMemory: (...args) => pageAiLoadProfileMemory(...args), pageAiLoadSkills: (...args) => pageAiLoadSkills(...args), pageAiSwitchProfile: (...args) => pageAiSwitchProfile(...args), pageAiToggleSkillGroup: (...args) => pageAiToggleSkillGroup(...args), pageAiSetReasonixMemoryEnabled: (...args) => pageAiSetReasonixMemoryEnabled(...args), pageAiSetHideHermesBuiltinSkills: (...args) => pageAiSetHideHermesBuiltinSkills(...args), pageAiSetSkillSource: (...args) => pageAiSetSkillSource(...args), pageAiSetContextScope: (...args) => pageAiSetContextScope(...args), pageAiSetAgentId: (...args) => pageAiSetAgentId(...args), pageAiToggleContextRef: (...args) => pageAiToggleContextRef(...args), pageAiSetContextPopoverOpen: (...args) => pageAiSetContextPopoverOpen(...args), pageAiSetAgentPopoverOpen: (...args) => pageAiSetAgentPopoverOpen(...args), pageAiLoadAllowedRoots: (...args) => pageAiLoadAllowedRoots(...args), updatePageAiTriggerState: (...args) => updatePageAiTriggerState(...args) }; }