Files
mnote/rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js
T
lix-2026 1882db7681 收口 MNote P0 P1 P2 审查尾项
- 归档 OnlyOffice live bridge、Page AI、mindmap、design governance 与相关 bug 条目
- 补齐 MinerU OCR 后端 runtime 合同与 smoke/test 基线
- 收口 ChatOnly/Doubao、ObjectIdentity、Page Aggregate compat 与 runtime owner 文档口径

验证:
- cargo test --manifest-path rust/Cargo.toml -p mnote-web local_ocr -- --test-threads=1
- cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice_bridge -- --test-threads=1
- git diff --check
- git diff --cached --check
- codegraph index . --force && codegraph status .
- codegraph sync . && codegraph status .
2026-06-01 09:29:12 +08:00

2500 lines
114 KiB
JavaScript

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)
};
}