Files
mnote/rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js
T
2026-06-25 21:08:17 +08:00

5221 lines
248 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 = 4;
// Page AI 请求合同:agentId 只负责路由;contextRefs 是用户勾选的上下文地址;
// allowedRoots 只展示 SQLite directory_grants 解析结果,服务端仍会按当前用户重算;
// runTargetSnapshot 必须来自 OpenEditorsSnapshot,避免运行中 UI 切换导致目标漂移。
var PAGE_AI_AGENT_FALLBACK_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_DESCRIPTOR_SCHEMA = 'mnote.ai_agent_descriptor.v1';
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' },
{ profileId: 'shared_api_deepseek_flash_chat', baseProfile: 'api-deepseek-flash-chat', label: 'DeepSeek Flash', providerKind: 'api-chat' },
{ profileId: 'shared_api_deepseek_pro_chat', baseProfile: 'api-deepseek-pro-chat', label: 'DeepSeek Pro', providerKind: 'api-chat' },
{ profileId: 'shared_api_gpt_chat', baseProfile: 'api-gpt-chat', label: 'GPT', providerKind: 'api-chat' },
{ profileId: 'shared_api_kimi_chat', baseProfile: 'api-kimi-chat', label: 'Kimi', providerKind: 'api-chat' },
{ profileId: 'shared_api_gemini_chat', baseProfile: 'api-gemini-chat', label: 'Gemini API', providerKind: 'api-chat' },
{ profileId: 'shared_api_grok_chat', baseProfile: 'api-grok-chat', label: 'Grok API', providerKind: 'api-chat' }
];
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;
var PAGE_AI_DRAWER_WIDTH_STORAGE_KEY = 'mnote.page_ai.drawer_width';
var PAGE_AI_DRAWER_DEFAULT_WIDTH = 440;
var PAGE_AI_DRAWER_MIN_WIDTH = 340;
var PAGE_AI_DRAWER_MAX_WIDTH = 760;
var PAGE_AI_OPENCODE_MODEL = { providerID: 'opencodego', modelID: 'deepseek-v4-flash' };
function clonePageAiDefaultValue(value) {
if (Array.isArray(value)) return value.slice();
if (value && typeof value === 'object') return Object.assign({}, value);
return value;
}
function pageAiClampDrawerWidth(width) {
var viewportMax = Math.max(PAGE_AI_DRAWER_MIN_WIDTH, Math.min(PAGE_AI_DRAWER_MAX_WIDTH, window.innerWidth - 24));
var numeric = Number(width);
if (!Number.isFinite(numeric) || numeric <= 0) numeric = PAGE_AI_DRAWER_DEFAULT_WIDTH;
return Math.max(PAGE_AI_DRAWER_MIN_WIDTH, Math.min(viewportMax, Math.round(numeric)));
}
function pageAiStoredDrawerWidth() {
try {
return pageAiClampDrawerWidth(window.localStorage ? window.localStorage.getItem(PAGE_AI_DRAWER_WIDTH_STORAGE_KEY) : PAGE_AI_DRAWER_DEFAULT_WIDTH);
} catch (_error) {
return pageAiClampDrawerWidth(PAGE_AI_DRAWER_DEFAULT_WIDTH);
}
}
function pageAiApplyDrawerWidth(drawer, width) {
var target = drawer instanceof HTMLElement ? drawer : document.querySelector('[data-testid="wolai-page-ai-drawer"]');
if (!(target instanceof HTMLElement)) return;
var next = pageAiClampDrawerWidth(width == null ? pageAiStoredDrawerWidth() : width);
target.style.setProperty('--mnote-page-ai-width', next + 'px');
}
function handlePageAiPointerDown(event, helpers) {
var closestAction = helpers && helpers.closestAction;
if (typeof closestAction !== 'function') return false;
var handle = closestAction(event.target, '[data-page-ai-resize-handle]');
if (!(handle instanceof HTMLElement)) return false;
var drawer = handle.closest('[data-testid="wolai-page-ai-drawer"]');
var panel = drawer instanceof HTMLElement ? drawer.querySelector('.wolai-page-ai-panel') : null;
if (!(drawer instanceof HTMLElement) || !(panel instanceof HTMLElement)) return false;
event.preventDefault();
var pointerId = event.pointerId;
var startX = Number(event.clientX || 0);
var startWidth = panel.getBoundingClientRect().width || pageAiStoredDrawerWidth();
drawer.setAttribute('data-page-ai-resizing', 'true');
document.documentElement.setAttribute('data-mnote-page-ai-resizing', 'true');
try {
handle.setPointerCapture(pointerId);
} catch (_error) {}
function move(nextEvent) {
var nextWidth = startWidth + (startX - Number(nextEvent.clientX || 0));
pageAiApplyDrawerWidth(drawer, nextWidth);
}
function finish(nextEvent) {
move(nextEvent);
var value = drawer.style.getPropertyValue('--mnote-page-ai-width').replace('px', '').trim();
try {
if (window.localStorage) window.localStorage.setItem(PAGE_AI_DRAWER_WIDTH_STORAGE_KEY, String(pageAiClampDrawerWidth(value)));
} catch (_error) {}
drawer.removeAttribute('data-page-ai-resizing');
document.documentElement.removeAttribute('data-mnote-page-ai-resizing');
window.removeEventListener('pointermove', move, true);
window.removeEventListener('pointerup', finish, true);
window.removeEventListener('pointercancel', finish, true);
try {
handle.releasePointerCapture(pointerId);
} catch (_error) {}
}
window.addEventListener('pointermove', move, true);
window.addEventListener('pointerup', finish, true);
window.addEventListener('pointercancel', finish, true);
return true;
}
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: [],
pageAiClientQueuedPrompts: [],
pageAiStoppedRunIds: {},
pageAiAbortController: null,
pageAiContextScope: 'page',
pageAiContextPopoverOpen: false,
pageAiTargetPopoverOpen: false,
pageAiSelectedTargetId: '',
pageAiSelectedContextRefs: null,
pageAiCurrentRunTargetSnapshot: null,
pageAiAllowedRoots: [],
pageAiAllowedRootsError: '',
pageAiAgentId: 'reasonix',
pageAiAgentPopoverOpen: false,
pageAiAgentDescriptors: [],
pageAiAgentDescriptorError: '',
pageAiAgentDescriptorsLoaded: 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: '',
pageAiBoardWorkerId: 'mnote-page-ai-zcode',
pageAiBoardWorkflowId: 'builtin-mnote-page-ai-chat',
pageAiBoardModelOverride: 'zcode-default',
pageAiBoardRunDetails: {},
pageAiBoardActiveDetailRunId: '',
pageAiOpencodeStatus: null,
pageAiOpencodeStatusError: '',
pageAiOpencodeChangedFiles: [],
pageAiOpencodeDiffError: '',
pageAiOpencodeSessionId: '',
pageAiOpencodeBaseUrl: 'http://127.0.0.1:4096',
pageAiOpencodeIframeUrl: '',
pageAiOpencodeBindingKey: '',
pageAiOpencodeBindingInFlight: false,
pageAiOpencodeEventSource: null,
pageAiOpencodeEventSessionId: '',
pageAiOpencodeEventTimer: 0,
pageAiSessionSearchQuery: '',
pageAiSessionSearchResults: [],
pageAiSessionSearchTimer: 0,
pageAiSessionAgentFilter: 'all',
pageAiSessionStatusFilter: 'all',
pageAiSelectedSessionIds: {},
pageAiSessionError: '',
pageAiRuntimeStatus: null,
pageAiRuntimeStatusError: '',
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);
}
if (!Array.isArray(state.pageAiClientQueuedPrompts)) {
state.pageAiClientQueuedPrompts = [];
}
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 pageAiNormalizeAgentDescriptor(descriptor) {
if (!descriptor || typeof descriptor !== 'object') return null;
var agentId = String(descriptor.agentId || descriptor.id || '').trim();
if (!agentId) return null;
return Object.assign({}, descriptor, {
id: agentId,
agentId: agentId,
label: String(descriptor.displayName || descriptor.label || agentId).trim() || agentId,
acpRuntime: String(descriptor.acpRuntime || descriptor.acp_runtime || '').trim() || (agentId === 'reasonix' ? 'reasonix' : 'hermes'),
canWriteFiles: descriptor.canWriteFiles !== false,
capabilities: pageAiNormalizeArray(descriptor.capabilities),
fields: pageAiNormalizeArray(descriptor.fields),
tools: pageAiNormalizeArray(descriptor.tools),
capabilityPacks: pageAiNormalizeArray(descriptor.capabilityPacks),
schema: String(descriptor.schema || PAGE_AI_DESCRIPTOR_SCHEMA).trim()
});
}
function pageAiAgentRegistry() {
var descriptors = pageAiNormalizeArray(pageUiState.pageAiAgentDescriptors)
.map(pageAiNormalizeAgentDescriptor)
.filter(Boolean);
return descriptors.length ? descriptors : PAGE_AI_AGENT_FALLBACK_REGISTRY;
}
function pageAiAgentDescriptor(agentId) {
var normalized = String(agentId || '').trim();
if (!normalized) normalized = pageAiCurrentAgentId();
return pageAiAgentRegistry().find(function(agent) { return agent.id === normalized || agent.agentId === normalized; }) || null;
}
function pageAiNormalizeAgentId(agentId) {
var value = String(agentId || '').trim();
return pageAiAgentRegistry().some(function(agent) { return agent.id === value; }) ? value : 'reasonix';
}
function pageAiAgentRecord(agentId) {
var normalized = pageAiNormalizeAgentId(agentId);
return pageAiAgentRegistry().find(function(agent) { return agent.id === normalized; }) || pageAiAgentRegistry()[1] || PAGE_AI_AGENT_FALLBACK_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 pageAiSessionStatusFilterValue = (...args) => pageAiProfileRuntime.pageAiSessionStatusFilterValue(...args);
const pageAiSessionStatusLabel = (...args) => pageAiProfileRuntime.pageAiSessionStatusLabel(...args);
const pageAiSessionStatusFilterOptions = (...args) => pageAiProfileRuntime.pageAiSessionStatusFilterOptions(...args);
const pageAiFilteredHistoryRows = (...args) => pageAiProfileRuntime.pageAiFilteredHistoryRows(...args);
const pageAiTimestamp = (...args) => pageAiProfileRuntime.pageAiTimestamp(...args);
const pageAiUsageSummary = (...args) => pageAiProfileRuntime.pageAiUsageSummary(...args);
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 pageAiRestorePermissionRequests = (...args) => pageAiPermissionRuntime.pageAiRestorePermissionRequests(...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 pageAiExportBackendSession = (...args) => pageAiSessionRuntime.pageAiExportBackendSession(...args);
const pageAiDeleteBackendSession = (...args) => pageAiSessionRuntime.pageAiDeleteBackendSession(...args);
const pageAiDeleteSelectedBackendSessions = (...args) => pageAiSessionRuntime.pageAiDeleteSelectedBackendSessions(...args);
const pageAiResumeBackendSession = (...args) => pageAiSessionRuntime.pageAiResumeBackendSession(...args);
const pageAiSessionSelected = (...args) => pageAiSessionRuntime.pageAiSessionSelected(...args);
const pageAiToggleSessionSelection = (...args) => pageAiSessionRuntime.pageAiToggleSessionSelection(...args);
const pageAiClearSessionSelection = (...args) => pageAiSessionRuntime.pageAiClearSessionSelection(...args);
const pageAiSelectedSessionCount = (...args) => pageAiSessionRuntime.pageAiSelectedSessionCount(...args);
const pageAiCheckActiveRun = (...args) => pageAiSessionRuntime.pageAiCheckActiveRun(...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,
pageAiAgentDescriptor,
pageAiAgentRecord,
pageAiAllSkillEntries,
pageAiBuildAllowedRoots,
pageAiChatOnlyProfileEntries,
pageAiChatOnlyProfileSpec,
pageAiCurrentAgentId,
pageAiCurrentProfile,
pageAiCurrentProfileRecord,
pageAiCurrentSession,
pageAiCurrentSkillSource,
pageAiCurrentSkillSourceLabel,
pageAiDefaultChatOnlyProfileSpec,
pageAiDescriptorFieldOptions,
pageAiDescriptorFieldValue,
pageAiEditorTargetCandidates,
pageAiEnsureContextRefState,
pageAiFilteredHistoryRows,
pageAiHermesProfileEntries,
pageAiHermesSettingsUrl,
pageAiHideHermesBuiltinSkills,
pageAiMnoteToolModel,
pageAiNormalizeAcpRuntimes,
pageAiNormalizeArray,
pageAiNormalizeSkillSource,
pageAiProfileDisplayLabel,
pageAiProfileValue,
pageAiProviderLabel,
pageAiReasonixMemoryEnabled,
pageAiRunProfile,
pageAiSessionAgentFilterOptions,
pageAiSessionAgentFilterValue,
pageAiSessionAgentLabel,
pageAiSessionPreviewText,
pageAiSessionStatusFilterOptions,
pageAiSessionStatusFilterValue,
pageAiSessionStatusLabel,
pageAiSessionStorageLabel,
pageAiSessionSelected,
pageAiSelectedSessionCount,
pageAiSkillEnabled,
pageAiSkillGroupCollapsed,
pageAiSkillListEntries,
pageAiSkillOriginLabel,
pageAiSkillSourceOptions,
pageAiSkillSourceParts,
pageAiToggleableSkillEntries,
pageAiUsageSummary,
pageAiBoardFirstEnabled,
pageAiBoardWorkerOptions,
pageAiBoardWorkflowOptions,
pageAiBoardSelectedWorkerId,
pageAiBoardSelectedWorkflowId,
pageAiBoardWorkerLabel,
pageAiBoardWorkflowLabel,
pageAiBoardModelOptions,
pageAiBoardSelectedModelOverride,
pageAiBoardModelLabel,
pageUiState,
renderPageAiMarkdown,
searchText,
});
function pageAiSetAgentId(agentId) {
var next = pageAiNormalizeAgentId(agentId);
var record = pageAiAgentRecord(next);
pageUiState.pageAiAgentId = next;
pageUiState.pageAiAcpRuntime = record.acpRuntime || 'reasonix';
if (record.tools) pageUiState.pageAiTools = record.tools;
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 (_) {}
}
function pageAiDescriptorPreferenceValues() {
return pageUiState.pageAiSkillPreferences && typeof pageUiState.pageAiSkillPreferences === 'object'
? pageUiState.pageAiSkillPreferences
: {};
}
function pageAiDescriptorFieldValue(field) {
var key = String(field && field.key || '').trim();
var preferences = pageAiDescriptorPreferenceValues();
if (key && Object.prototype.hasOwnProperty.call(preferences, key)) return preferences[key];
return field && Object.prototype.hasOwnProperty.call(field, 'default') ? field.default : '';
}
function pageAiDescriptorFieldOptions(field) {
var source = String(field && field.optionsSource || '').trim();
if (source === 'hermes_profiles') {
return pageAiHermesProfileEntries().map(function(profile) {
var value = pageAiProfileValue(profile);
return {
value: value,
label: pageAiProfileDisplayLabel(profile, value)
};
}).filter(function(option) { return option.value; });
}
if (source === 'chat_only_profiles') {
return pageAiChatOnlyProfileEntries().map(function(profile) {
var value = pageAiProfileValue(profile);
return {
value: value,
label: profile.menuLabel || pageAiProfileDisplayLabel(profile, value)
};
}).filter(function(option) { return option.value; });
}
return pageAiNormalizeArray(field && field.options).map(function(option) {
if (option && typeof option === 'object') {
var value = String(option.value || option.id || option.key || '').trim();
return {
value: value,
label: String(option.label || option.title || value).trim() || value
};
}
var value = String(option || '').trim();
return { value: value, label: value };
}).filter(function(option) { return option.value; });
}
function pageAiSetDescriptorFieldValue(key, value) {
var normalizedKey = String(key || '').trim();
if (!normalizedKey) return;
pageUiState.pageAiSkillPreferences = Object.assign({}, pageAiDescriptorPreferenceValues(), {
[normalizedKey]: value
});
pageAiPersistRawAiPreference(normalizedKey, value);
if (normalizedKey === 'ai.agent.hermes.profile_id') {
pageAiSetActiveProfile(value);
void pageAiLoadAgentDescriptors();
void pageAiLoadSkills();
void pageAiLoadTools();
}
if (normalizedKey === 'ai.agent.chat_only.model_id' && pageAiCurrentAgentId() === 'chat_only') {
pageAiSetActiveProfile(pageAiNormalizeChatOnlyProfileId(value));
}
renderPageAiControls();
}
function pageAiReasonixPreference(key, fallback) {
var preferences = pageAiDescriptorPreferenceValues();
var value = preferences[key];
if (value == null || String(value).trim() === '') return fallback;
return String(value).trim();
}
function pageAiReasonixControlState() {
return {
modelId: pageAiReasonixPreference('ai.agent.reasonix.model_id', 'deepseek-flash'),
approvalMode: pageAiReasonixPreference('ai.agent.reasonix.approval_mode', 'ask'),
planMode: pageAiReasonixPreference('ai.agent.reasonix.plan_mode', 'manual')
};
}
function pageAiApplyReasonixPlanMode(prompt) {
if (pageAiCurrentAgentId() !== 'reasonix') return prompt;
var control = pageAiReasonixControlState();
if (control.planMode !== 'auto') return prompt;
return '请按 Reasonix 自动计划模式处理:复杂或会写文件的任务先给出简短计划,并等待我批准后再执行。\n\n' + prompt;
}
function pageAiBoardFirstEnabled() {
try {
return window.localStorage ? window.localStorage.getItem('mnote.page_ai.legacy_provider_mode') !== '1' : true;
} catch (_error) {
return true;
}
}
function pageAiOpencodeHostEnabled() {
try {
return window.localStorage ? window.localStorage.getItem('mnote.page_ai.opencode_host') !== '0' : true;
} catch (_error) {
return true;
}
}
function pageAiOpencodeStatusLabel(status) {
var node = status && typeof status === 'object' ? status : {};
var state = String(node.status || node.state || node.runtimeStatus || '').trim();
var ok = node.ok === true || node.ready === true || state === 'ready' || state === 'running' || state === 'ok';
if (ok) return state ? '可用 · ' + state : '可用';
if (state) return state;
return '未知';
}
function pageAiOpencodeSessionIdFromStatus(status) {
var node = status && typeof status === 'object' ? status : {};
var session = node.session && typeof node.session === 'object' ? node.session : {};
return String(node.sessionId || node.activeSessionId || node.currentSessionId || session.id || '').trim();
}
function pageAiOpencodeData(payload) {
return payload && payload.data && typeof payload.data === 'object' ? payload.data : payload;
}
function pageAiOpencodeSessionArray(payload) {
var data = pageAiOpencodeData(payload);
return pageAiNormalizeArray(data && data.data)
.concat(pageAiNormalizeArray(data && data.sessions))
.concat(Array.isArray(data) ? data : []);
}
function pageAiOpencodeCollectChangedFileCandidates(value, output, depth) {
if (value == null || depth > 5) return;
if (typeof value === 'string') {
if (/\.(md|markdown|txt|json|ya?ml|toml|rs|js|ts|tsx|jsx|css|html)$/i.test(value) || value.indexOf('/') !== -1) {
output.push(value);
}
return;
}
if (Array.isArray(value)) {
value.forEach(function(item) { pageAiOpencodeCollectChangedFileCandidates(item, output, depth + 1); });
return;
}
if (typeof value !== 'object') return;
var directPath = value.path || value.file || value.filename || value.relativePath || value.relative_path || value.absolutePath || value.absolute_path;
if (directPath) output.push(value);
['changedFiles', 'changed_files', 'files', 'diff', 'diffs', 'changes', 'changed', 'edited', 'created', 'deleted', 'data', 'properties'].forEach(function(key) {
if (Object.prototype.hasOwnProperty.call(value, key)) pageAiOpencodeCollectChangedFileCandidates(value[key], output, depth + 1);
});
}
function pageAiOpencodeNormalizeChangedFiles(payload) {
var source = [];
pageAiOpencodeCollectChangedFileCandidates(payload, source, 0);
var seen = {};
return source.map(function(item) {
var path = typeof item === 'string'
? item
: String(item && (item.path || item.file || item.filename || item.relativePath || item.relative_path || item.absolutePath || item.absolute_path) || '').trim();
var status = typeof item === 'object' && item ? String(item.status || item.change || item.kind || item.type || '').trim() : '';
return { path: path, status: status };
}).filter(function(item) {
if (!item.path || seen[item.path]) return false;
seen[item.path] = true;
return true;
});
}
function pageAiOpencodeCurrentPageLabel() {
var aggregate = currentPageAggregate();
var meta = aggregate && aggregate.meta && typeof aggregate.meta === 'object' ? aggregate.meta : {};
var target = currentPageAiEditorTarget() || {};
return String(meta.title || target.title || document.title || currentDocumentId() || '当前页').trim();
}
function pageAiOpencodePrimaryPath() {
var target = currentPageAiEditorTarget() || {};
var workspacePath = target && target.workspacePath && typeof target.workspacePath === 'object' ? target.workspacePath : {};
return String(workspacePath.relativePath || workspacePath.path || target.relativePath || localMarkdownRelativePathFromPageAiDocumentId(currentDocumentId()) || '').trim();
}
function pageAiOpencodeWritableLabel() {
var roots = pageAiNormalizeArray(pageUiState.pageAiAllowedRoots && pageUiState.pageAiAllowedRoots.length ? pageUiState.pageAiAllowedRoots : pageAiBuildAllowedRoots());
if (!roots.length) return '授权待同步';
var writable = roots.some(function(root) {
return /write|writable|rw/i.test(String(root && (root.permission || root.access || root.mode || '') || '')) || root && root.writable === true;
});
return writable ? '可写' : '只读';
}
function pageAiFilePathFromRootUri(rootUri, relativePath) {
var root = String(rootUri || '').trim();
var relative = String(relativePath || '').trim().replace(/\\/g, '/').replace(/^\/+/, '');
if (!root || !relative || !root.startsWith('file://')) return '';
try {
var pathname = new URL(root).pathname || '';
return decodeURIComponent(pathname).replace(/\/+$/, '') + '/' + relative;
} catch (_error) {
return '';
}
}
function pageAiBuildOpencodeContextPayload() {
var selectedText = currentPageAiSelectedText();
var target = currentPageAiEditorTarget() || {};
var workspacePath = target && target.workspacePath && typeof target.workspacePath === 'object' ? target.workspacePath : {};
var pagePath = pageAiOpencodePrimaryPath();
var rootUri = String(workspacePath.rootUri || target.rootUri || currentRootUri() || '').trim();
return {
pageTitle: pageAiOpencodeCurrentPageLabel(),
pagePath: pagePath,
pageAbsolutePath: pageAiFilePathFromRootUri(rootUri, pagePath),
rootUri: rootUri,
selection: selectedText,
allowedRoots: pageAiNormalizeArray(pageUiState.pageAiAllowedRoots && pageUiState.pageAiAllowedRoots.length ? pageUiState.pageAiAllowedRoots : pageAiBuildAllowedRoots()),
pageId: String(currentDocumentId() || target.documentId || target.id || ''),
workspaceId: String(target.workspaceId || ''),
};
}
function pageAiOpencodeContextTextForInsert() {
var context = pageAiBuildOpencodeContextPayload();
var lines = ['MNote 当前页上下文'];
if (context.pageTitle) lines.push('标题:' + context.pageTitle);
if (context.pagePath) lines.push('文件:' + context.pagePath);
if (context.selection) lines.push('选区:\n' + context.selection);
var roots = pageAiNormalizeArray(context.allowedRoots).map(function(root) {
return String(root && (root.path || root.root || root.uri || root.label) || '').trim();
}).filter(Boolean).slice(0, 4);
if (roots.length) lines.push('允许目录:' + roots.join(', '));
return lines.join('\n');
}
function pageAiPostOpencodeBridgeMessage(type, payload) {
var iframe = document.querySelector('[data-page-ai-opencode-iframe]');
if (!(iframe instanceof HTMLIFrameElement) || !iframe.contentWindow) return false;
iframe.contentWindow.postMessage({ source: 'mnote-page-ai', target: 'opencode-webui', type: type, payload: payload || {} }, window.location.origin);
return true;
}
function pageAiInstallOpencodeBridge() {
if (pageUiState.pageAiOpencodeBridgeInstalled) return;
pageUiState.pageAiOpencodeBridgeInstalled = true;
window.addEventListener('message', function(event) {
if (event.origin !== window.location.origin) return;
var message = event.data && typeof event.data === 'object' ? event.data : null;
if (!message || message.source !== 'mnote-opencode-bridge') return;
var payload = message.payload && typeof message.payload === 'object' ? message.payload : {};
if (message.type === 'session-ready') {
pageUiState.pageAiOpencodeBridgeReady = true;
pageAiRenderOpencodeHostChrome();
return;
}
if (message.type === 'open-file') {
pageAiOpenOpencodeChangedFile(payload.path || payload.href || payload.text || '');
return;
}
if (message.type === 'refresh-file') {
pageAiRefreshOpencodeCurrentPage('page-ai-opencode-bridge');
}
});
}
function pageAiOpencodeMessageInfo(message) {
return message && message.info && typeof message.info === 'object' ? message.info : message || {};
}
function pageAiOpencodePartText(part) {
if (!part || typeof part !== 'object') return '';
if (part.text) return String(part.text);
if (part.content) return String(part.content);
if (part.summary) return String(part.summary);
if (part.type === 'patch') return '修改文件:' + pageAiNormalizeArray(part.files).join(', ');
if (part.type === 'file') return String(part.filename || part.url || 'file');
if (part.type === 'tool') return String(part.tool || part.name || 'tool');
return '';
}
function pageAiOpencodeIsContextEnvelope(message, parts) {
var info = pageAiOpencodeMessageInfo(message);
var text = String(info.text || '').trim() || pageAiNormalizeArray(parts).map(pageAiOpencodePartText).join('\n').trim();
return text.indexOf('MNote context envelope') === 0;
}
function pageAiOpencodeNormalizeMessages(payload) {
var data = pageAiOpencodeData(payload);
var items = pageAiNormalizeArray(data && data.data)
.concat(pageAiNormalizeArray(data && data.messages))
.concat(Array.isArray(data) ? data : []);
return items.map(function(item) {
var info = pageAiOpencodeMessageInfo(item);
var parts = pageAiNormalizeArray(item && item.parts).concat(pageAiNormalizeArray(info.content));
var type = String(info.role || info.type || item && item.type || '').toLowerCase();
var role = type.indexOf('assistant') !== -1 ? 'assistant' : type.indexOf('user') !== -1 ? 'user' : type || 'event';
return {
id: String(info.id || item && item.id || Math.random()).trim(),
role: role,
type: type,
agent: String(info.agent || ''),
model: info.model || null,
finish: String(info.finish || ''),
error: info.error || null,
cost: info.cost,
tokens: info.tokens || null,
parts: parts,
hiddenContext: pageAiOpencodeIsContextEnvelope(item, parts),
text: String(info.text || '').trim(),
};
}).filter(function(message) {
return !message.hiddenContext && (message.text || message.parts.length || message.error || message.type);
});
}
function pageAiOpencodeNormalizePermissions(payload) {
var data = pageAiOpencodeData(payload);
return pageAiNormalizeArray(data && data.data)
.concat(pageAiNormalizeArray(data && data.permissions))
.concat(Array.isArray(data) ? data : []);
}
function pageAiOpencodeNormalizeTodo(payload) {
var data = pageAiOpencodeData(payload);
return pageAiNormalizeArray(data && data.data).concat(pageAiNormalizeArray(data && data.todo)).concat(Array.isArray(data) ? data : []);
}
function pageAiOpencodeCompact(value, max) {
var text = typeof value === 'string' ? value : JSON.stringify(value || {});
max = max || 260;
return text.length > max ? text.slice(0, max - 1) + '…' : text;
}
function pageAiRenderOpencodePart(part) {
if (!part || typeof part !== 'object') return '';
var type = String(part.type || 'part');
if (type === 'text') return '<div class="wolai-page-ai-opencode-message-body">' + escapeHtml(pageAiOpencodePartText(part)).replace(/\n/g, '<br>') + '</div>';
if (type === 'reasoning') return '<details class="wolai-page-ai-opencode-part"><summary>reasoning</summary><pre>' + escapeHtml(pageAiOpencodePartText(part)) + '</pre></details>';
if (type === 'tool') {
var state = part.state && typeof part.state === 'object' ? part.state : {};
var status = String(state.status || state.type || part.status || 'pending');
return '<details class="wolai-page-ai-opencode-part wolai-page-ai-opencode-tool" open><summary>' + escapeHtml(part.tool || 'tool') + '<span>' + escapeHtml(status) + '</span></summary><pre>' + escapeHtml(pageAiOpencodeCompact(state.output || state.input || state.error || state)) + '</pre></details>';
}
if (type === 'patch') {
var files = pageAiNormalizeArray(part.files);
return '<div class="wolai-page-ai-opencode-part wolai-page-ai-opencode-patch"><strong>patch</strong>' + files.map(function(file) {
var path = String(file || '').trim();
return '<button type="button" data-page-ai-opencode-open-file="' + escapeHtml(path) + '">' + escapeHtml(path.split('/').pop() || path) + '</button>';
}).join('') + '</div>';
}
if (type === 'file') {
var filePath = String(part.filename || part.url || '').trim();
return '<div class="wolai-page-ai-opencode-part"><strong>file</strong><button type="button" data-page-ai-opencode-open-file="' + escapeHtml(filePath) + '">' + escapeHtml(filePath || 'file') + '</button></div>';
}
if (type === 'step-start' || type === 'step-finish' || type === 'snapshot' || type === 'agent' || type === 'subtask' || type === 'retry' || type === 'compaction') {
return '<details class="wolai-page-ai-opencode-part"><summary>' + escapeHtml(type) + '</summary><pre>' + escapeHtml(pageAiOpencodeCompact(part, 420)) + '</pre></details>';
}
return '<details class="wolai-page-ai-opencode-part"><summary>' + escapeHtml(type) + '</summary><pre>' + escapeHtml(pageAiOpencodeCompact(part, 420)) + '</pre></details>';
}
function pageAiRenderOpencodeMessages(drawer) {
var node = drawer.querySelector('[data-page-ai-opencode-messages]');
if (!(node instanceof HTMLElement)) return;
var messages = pageAiNormalizeArray(pageUiState.pageAiOpencodeMessages);
if (!messages.length) {
node.innerHTML = '<div class="wolai-page-ai-opencode-empty">输入消息后由 opencode 原生 runtime 回复;tool、reasoning、patch 会在这里显示。</div>';
return;
}
node.innerHTML = messages.map(function(message) {
var model = message.model && typeof message.model === 'object' ? String(message.model.providerID || message.model.provider || '') + '/' + String(message.model.id || message.model.modelID || '') : '';
var parts = pageAiNormalizeArray(message.parts).map(pageAiRenderOpencodePart).join('') || (message.text ? '<div class="wolai-page-ai-opencode-message-body">' + escapeHtml(message.text).replace(/\n/g, '<br>') + '</div>' : '');
var error = message.error ? '<pre class="wolai-page-ai-opencode-error">' + escapeHtml(pageAiOpencodeCompact(message.error, 500)) + '</pre>' : '';
return '<article class="wolai-page-ai-opencode-message" data-role="' + escapeHtml(message.role) + '">' +
'<div class="wolai-page-ai-opencode-message-role"><strong>' + escapeHtml(message.role === 'assistant' ? 'opencode' : message.role === 'user' ? '你' : message.type || 'event') + '</strong><span>' + escapeHtml([message.agent, model, message.finish].filter(Boolean).join(' · ')) + '</span></div>' +
parts + error +
'</article>';
}).join('');
node.scrollTop = node.scrollHeight;
}
function pageAiRenderOpencodePermissions(drawer) {
var node = drawer.querySelector('[data-page-ai-opencode-permissions]');
if (!(node instanceof HTMLElement)) return;
var permissions = pageAiNormalizeArray(pageUiState.pageAiOpencodePermissions);
node.innerHTML = permissions.map(function(item) {
var id = String(item && item.id || '').trim();
var action = String(item && item.action || 'permission').trim();
var resources = pageAiNormalizeArray(item && item.resources).join(', ');
return '<div class="wolai-page-ai-opencode-permission">' +
'<strong>' + escapeHtml(action) + '</strong>' +
'<span>' + escapeHtml(resources || '等待审批') + '</span>' +
'<button type="button" data-page-ai-opencode-permission="once" data-page-ai-opencode-permission-id="' + escapeHtml(id) + '">允许一次</button>' +
'<button type="button" data-page-ai-opencode-permission="always" data-page-ai-opencode-permission-id="' + escapeHtml(id) + '">总是允许</button>' +
'<button type="button" data-page-ai-opencode-permission="reject" data-page-ai-opencode-permission-id="' + escapeHtml(id) + '">拒绝</button>' +
'</div>';
}).join('');
}
function pageAiRenderOpencodeSessions(drawer) {
var node = drawer.querySelector('[data-page-ai-opencode-sessions]');
if (!(node instanceof HTMLElement)) return;
var sessions = pageAiNormalizeArray(pageUiState.pageAiOpencodeSessions).slice(0, 12);
node.innerHTML = sessions.length ? sessions.map(function(session) {
var id = String(session && session.id || '').trim();
var active = id && id === String(pageUiState.pageAiOpencodeSessionId || '');
var title = String(session && (session.title || session.name || session.id) || 'session').trim();
var model = session && session.model && typeof session.model === 'object' ? String(session.model.providerID || '') + '/' + String(session.model.id || session.model.modelID || '') : '';
return '<button type="button" class="wolai-page-ai-opencode-session-row" data-active="' + (active ? 'true' : 'false') + '" data-page-ai-opencode-session-select="' + escapeHtml(id) + '"><strong>' + escapeHtml(title) + '</strong><span>' + escapeHtml(model || id) + '</span></button>';
}).join('') : '<span class="wolai-page-ai-opencode-empty">暂无 session 列表</span>';
}
function pageAiRenderOpencodeTodo(drawer) {
var node = drawer.querySelector('[data-page-ai-opencode-todo]');
if (!(node instanceof HTMLElement)) return;
var todo = pageAiNormalizeArray(pageUiState.pageAiOpencodeTodo);
node.innerHTML = todo.length ? todo.map(function(item) {
return '<li data-state="' + escapeHtml(String(item && (item.status || item.state) || '')) + '">' + escapeHtml(String(item && (item.content || item.text || item.title) || item || '')) + '</li>';
}).join('') : '';
}
function pageAiRenderOpencodeHostChrome() {
var drawer = document.querySelector('[data-testid="wolai-page-ai-drawer"]');
if (!(drawer instanceof HTMLElement) || drawer.getAttribute('data-page-ai-opencode-host') !== 'true') return;
var status = pageUiState.pageAiOpencodeStatus || {};
var session = status.session && typeof status.session === 'object' ? status.session : status;
var sessionId = String(pageUiState.pageAiOpencodeSessionId || pageAiOpencodeSessionIdFromStatus(status) || '').trim();
var primaryPath = pageAiOpencodePrimaryPath();
var titleNode = drawer.querySelector('[data-page-ai-opencode-current-page]');
if (titleNode instanceof HTMLElement) titleNode.textContent = pageAiOpencodeCurrentPageLabel();
var pathNode = drawer.querySelector('[data-page-ai-opencode-current-path]');
if (pathNode instanceof HTMLElement) pathNode.textContent = primaryPath || '未定位真实 Markdown 路径';
var selectionNode = drawer.querySelector('[data-page-ai-opencode-selection]');
if (selectionNode instanceof HTMLElement) selectionNode.textContent = currentPageAiSelectedText() ? '已选中 ' + currentPageAiSelectedText().length + ' 字' : '无选区';
var writableNode = drawer.querySelector('[data-page-ai-opencode-writable]');
if (writableNode instanceof HTMLElement) writableNode.textContent = pageAiOpencodeWritableLabel();
var statusNode = drawer.querySelector('[data-page-ai-opencode-runtime-status]');
if (statusNode instanceof HTMLElement) {
statusNode.textContent = pageUiState.pageAiOpencodeStatusError || pageAiOpencodeStatusLabel(status);
statusNode.setAttribute('data-state', pageUiState.pageAiOpencodeStatusError ? 'error' : pageAiOpencodeStatusLabel(status));
}
var sessionNode = drawer.querySelector('[data-page-ai-opencode-session]');
if (sessionNode instanceof HTMLElement) sessionNode.textContent = sessionId ? 'session ' + sessionId : '未绑定 session';
var modelNode = drawer.querySelector('[data-page-ai-opencode-model]');
if (modelNode instanceof HTMLElement) {
var model = session.model && typeof session.model === 'object' ? String(session.model.providerID || '') + '/' + String(session.model.id || session.model.modelID || '') : '';
modelNode.textContent = model || 'model 未知';
}
var chipsNode = drawer.querySelector('[data-page-ai-opencode-changed-files]');
if (chipsNode instanceof HTMLElement) {
var changedFiles = pageAiNormalizeArray(pageUiState.pageAiOpencodeChangedFiles);
chipsNode.innerHTML = changedFiles.length ? changedFiles.map(function(file) {
var path = String(file && file.path || '').trim();
var label = path.split('/').filter(Boolean).pop() || path;
var statusText = String(file && file.status || '').trim();
return '<button type="button" class="wolai-page-ai-opencode-chip" data-page-ai-opencode-open-file="' + escapeHtml(path) + '" data-page-ai-open-file="' + escapeHtml(path) + '" data-page-ai-changed-file-chip="true" title="' + escapeHtml(path) + '">' + escapeHtml(label) + (statusText ? '<span>' + escapeHtml(statusText) + '</span>' : '') + '</button>';
}).join('') : '<span class="wolai-page-ai-opencode-empty">' + escapeHtml(pageUiState.pageAiOpencodeDiffError || '暂无 changed files') + '</span>';
}
var iframe = drawer.querySelector('[data-page-ai-opencode-iframe]');
if (iframe instanceof HTMLIFrameElement) {
var nextUrl = String(pageUiState.pageAiOpencodeIframeUrl || '').trim();
if (nextUrl && iframe.getAttribute('src') !== nextUrl) iframe.setAttribute('src', nextUrl);
iframe.hidden = !nextUrl;
}
var fallbackNode = drawer.querySelector('[data-page-ai-opencode-iframe-fallback]');
if (fallbackNode instanceof HTMLElement) {
var hasUrl = Boolean(String(pageUiState.pageAiOpencodeIframeUrl || '').trim());
fallbackNode.hidden = hasUrl && !pageUiState.pageAiOpencodeStatusError;
fallbackNode.textContent = pageUiState.pageAiOpencodeStatusError
? 'opencode WebUI 暂不可用:' + pageUiState.pageAiOpencodeStatusError
: '正在连接 opencode WebUI…';
}
}
function pageAiEnsureOpencodeHostDrawer() {
var drawer = ensurePageAiDrawer();
if (drawer.getAttribute('data-page-ai-opencode-host') !== 'true') {
drawer.setAttribute('data-page-ai-opencode-host', 'true');
drawer.setAttribute('data-mnote-acp-runtime', 'opencode');
drawer.innerHTML = '' +
'<div class="wolai-page-ai-resize-handle" data-page-ai-resize-handle title="拖动调整页面 AI 宽度" aria-hidden="true"></div>' +
'<div class="wolai-page-ai-panel wolai-page-ai-opencode-panel" role="dialog" aria-modal="false">' +
'<div class="wolai-page-ai-header wolai-page-ai-opencode-header">' +
'<div class="wolai-page-ai-header-copy">' +
'<h2 class="wolai-page-ai-title">opencode</h2>' +
'<div class="wolai-page-ai-subtitle"><span data-page-ai-opencode-runtime-status>未知</span><span data-page-ai-opencode-session>未绑定 session</span></div>' +
'</div>' +
'<div class="wolai-page-ai-header-actions">' +
'<button type="button" class="wolai-page-ai-icon" data-page-ai-action="opencode-new-session" aria-label="新建 opencode session" title="新建 session"><span class="material-symbols-outlined" data-icon="add" aria-hidden="true"></span></button>' +
'<button type="button" class="wolai-page-ai-icon" data-page-ai-action="opencode-insert-context" aria-label="把当前页上下文插入 opencode" title="插入当前页上下文"><span class="material-symbols-outlined" data-icon="post_add" aria-hidden="true"></span></button>' +
'<button type="button" class="wolai-page-ai-icon" data-page-ai-action="opencode-refresh-status" aria-label="刷新 opencode 状态" title="刷新 opencode 状态"><span class="material-symbols-outlined" data-icon="sync" aria-hidden="true"></span></button>' +
'<button type="button" class="wolai-page-ai-icon" data-page-ai-action="opencode-refresh-current-page" data-page-ai-refresh-file="current" aria-label="刷新当前页" title="刷新当前页"><span class="material-symbols-outlined" data-icon="refresh" aria-hidden="true"></span></button>' +
'<button type="button" class="wolai-page-ai-icon" data-page-ai-action="close" aria-label="关闭页面 AI"><span class="material-symbols-outlined" data-icon="close" aria-hidden="true"></span></button>' +
'</div>' +
'</div>' +
'<div class="wolai-page-ai-opencode-chrome" data-page-ai-opencode-chrome>' +
'<div class="wolai-page-ai-opencode-row"><span>当前页</span><strong data-page-ai-opencode-current-page>当前页</strong></div>' +
'<div class="wolai-page-ai-opencode-row"><span>路径</span><code data-page-ai-opencode-current-path>未定位真实 Markdown 路径</code></div>' +
'<div class="wolai-page-ai-opencode-badges"><span data-page-ai-opencode-selection>无选区</span><span data-page-ai-opencode-writable>授权待同步</span></div>' +
'<div class="wolai-page-ai-opencode-files" data-page-ai-opencode-changed-files><span class="wolai-page-ai-opencode-empty">暂无 changed files</span></div>' +
'</div>' +
'<div class="wolai-page-ai-opencode-frame-wrap" data-page-ai-opencode-frame-wrap>' +
'<div class="wolai-page-ai-opencode-iframe-fallback" data-page-ai-opencode-iframe-fallback>正在连接 opencode WebUI…</div>' +
'<iframe class="wolai-page-ai-opencode-iframe" data-page-ai-opencode-iframe title="opencode WebUI" allow="clipboard-read; clipboard-write" referrerpolicy="same-origin" hidden></iframe>' +
'</div>' +
'</div>';
}
pageAiInstallOpencodeBridge();
pageAiRenderOpencodeHostChrome();
void pageAiEnsureOpencodeSessionBinding(false);
return drawer;
}
async function pageAiEnsureOpencodeSessionBinding(force) {
if (pageUiState.pageAiOpencodeBindingInFlight) return;
var binding = pageUiState.pageAiOpencodeStatus && typeof pageUiState.pageAiOpencodeStatus === 'object' ? pageUiState.pageAiOpencodeStatus : {};
var context = pageAiBuildOpencodeContextPayload();
var pathSignature = JSON.stringify([context.pageTitle, context.pagePath, context.pageAbsolutePath, context.rootUri, context.selection.length, JSON.stringify(context.allowedRoots).slice(0, 512)]);
if (!force && pageUiState.pageAiOpencodeSessionId && pageUiState.pageAiOpencodeBindingKey === pathSignature) return;
pageUiState.pageAiOpencodeBindingInFlight = true;
pageUiState.pageAiOpencodeBindingKey = pathSignature;
try {
var response = await fetch('/api/page-ai/opencode/session', {
method: 'POST',
headers: { 'content-type': 'application/json', accept: 'application/json' },
cache: 'no-store',
body: JSON.stringify(Object.assign({ sessionId: force ? pageUiState.pageAiOpencodeSessionId || undefined : undefined, forceNew: force && !pageUiState.pageAiOpencodeSessionId }, context)),
});
var payload = await response.json().catch(function() { return null; });
if (!response.ok) throw new Error(payload && payload.error ? String(payload.error) : 'HTTP ' + response.status);
pageUiState.pageAiOpencodeStatus = payload.session || binding;
pageUiState.pageAiOpencodeStatusError = '';
pageUiState.pageAiOpencodeSessionId = String(payload.sessionId || '').trim();
pageUiState.pageAiOpencodeIframeUrl = String(payload.iframeUrl || pageUiState.pageAiOpencodeIframeUrl || '').trim();
pageAiStartOpencodeEvents();
void pageAiRefreshOpencodeProjection();
pageAiRenderOpencodeHostChrome();
} catch (error) {
pageUiState.pageAiOpencodeStatusError = error instanceof Error ? error.message : String(error);
pageUiState.pageAiOpencodeSending = false;
pageAiRenderOpencodeHostChrome();
} finally {
pageUiState.pageAiOpencodeBindingInFlight = false;
}
}
async function pageAiLoadOpencodeStatus() {
try {
var response = await fetch('/api/page-ai/opencode/status', { headers: { accept: 'application/json' }, cache: 'no-store' });
var payload = await response.json().catch(function() { return null; });
if (!response.ok) throw new Error(payload && payload.error ? String(payload.error) : 'HTTP ' + response.status);
pageUiState.pageAiOpencodeStatus = payload || {};
pageUiState.pageAiOpencodeStatusError = '';
pageUiState.pageAiOpencodeBaseUrl = String(payload && payload.baseUrl || pageUiState.pageAiOpencodeBaseUrl || 'http://127.0.0.1:4096').trim();
pageUiState.pageAiOpencodeSessionId = pageAiOpencodeSessionIdFromStatus(payload) || pageUiState.pageAiOpencodeSessionId;
pageAiRenderOpencodeHostChrome();
await pageAiRefreshOpencodeProjection();
} catch (error) {
pageUiState.pageAiOpencodeStatusError = error instanceof Error ? error.message : String(error);
pageUiState.pageAiOpencodeSending = false;
pageAiRenderOpencodeHostChrome();
}
}
async function pageAiLoadOpencodeSessions() {
try {
var response = await fetch('/api/page-ai/opencode/sessions', { headers: { accept: 'application/json' }, cache: 'no-store' });
var payload = await response.json().catch(function() { return null; });
if (!response.ok) throw new Error(payload && payload.error ? String(payload.error) : 'HTTP ' + response.status);
pageUiState.pageAiOpencodeSessions = pageAiOpencodeSessionArray(payload && payload.sessions || payload);
pageAiRenderOpencodeHostChrome();
} catch (error) {
pageUiState.pageAiOpencodeStatusError = error instanceof Error ? error.message : String(error);
pageUiState.pageAiOpencodeSending = false;
pageAiRenderOpencodeHostChrome();
}
}
async function pageAiLoadOpencodeMessages() {
var sessionId = String(pageUiState.pageAiOpencodeSessionId || '').trim();
if (!sessionId) return;
var response = await fetch('/api/page-ai/opencode/messages?sessionId=' + encodeURIComponent(sessionId), { headers: { accept: 'application/json' }, cache: 'no-store' });
var payload = await response.json().catch(function() { return null; });
if (!response.ok) throw new Error(payload && payload.error ? String(payload.error) : 'HTTP ' + response.status);
pageUiState.pageAiOpencodeMessages = pageAiOpencodeNormalizeMessages(payload);
pageAiRenderOpencodeHostChrome();
}
async function pageAiLoadOpencodePermissions() {
var sessionId = String(pageUiState.pageAiOpencodeSessionId || '').trim();
if (!sessionId) return;
var response = await fetch('/api/page-ai/opencode/permissions?sessionId=' + encodeURIComponent(sessionId), { headers: { accept: 'application/json' }, cache: 'no-store' });
var payload = await response.json().catch(function() { return null; });
if (!response.ok) throw new Error(payload && payload.error ? String(payload.error) : 'HTTP ' + response.status);
pageUiState.pageAiOpencodePermissions = pageAiOpencodeNormalizePermissions(payload);
pageAiRenderOpencodeHostChrome();
}
async function pageAiLoadOpencodeTodo() {
var sessionId = String(pageUiState.pageAiOpencodeSessionId || '').trim();
if (!sessionId) return;
var response = await fetch('/api/page-ai/opencode/todo?sessionId=' + encodeURIComponent(sessionId), { headers: { accept: 'application/json' }, cache: 'no-store' });
var payload = await response.json().catch(function() { return null; });
if (response.ok) pageUiState.pageAiOpencodeTodo = pageAiOpencodeNormalizeTodo(payload);
pageAiRenderOpencodeHostChrome();
}
async function pageAiLoadOpencodeDiff(sessionId) {
var id = String(sessionId || pageUiState.pageAiOpencodeSessionId || '').trim();
if (!id) return;
try {
var response = await fetch('/api/page-ai/opencode/diff?sessionId=' + encodeURIComponent(id), { headers: { accept: 'application/json' }, cache: 'no-store' });
var payload = await response.json().catch(function() { return null; });
if (!response.ok) throw new Error(payload && payload.error ? String(payload.error) : 'HTTP ' + response.status);
pageUiState.pageAiOpencodeChangedFiles = pageAiOpencodeNormalizeChangedFiles(payload);
pageUiState.pageAiOpencodeDiffError = '';
pageAiRenderOpencodeHostChrome();
if (pageAiOpencodeChangedFilesHitCurrentPage(pageUiState.pageAiOpencodeChangedFiles)) pageAiRefreshOpencodeCurrentPage('page-ai-opencode-event');
} catch (error) {
pageUiState.pageAiOpencodeDiffError = error instanceof Error ? error.message : String(error);
pageAiRenderOpencodeHostChrome();
}
}
async function pageAiRefreshOpencodeProjection() {
if (!pageUiState.pageAiOpencodeSessionId) return;
await Promise.allSettled([
pageAiLoadOpencodeMessages(),
pageAiLoadOpencodePermissions(),
pageAiLoadOpencodeTodo(),
pageAiLoadOpencodeDiff(pageUiState.pageAiOpencodeSessionId),
pageAiLoadOpencodeSessions(),
]);
}
function pageAiOpencodeChangedFilesHitCurrentPage(files) {
var currentPath = String(pageAiOpencodePrimaryPath() || '').trim();
if (!currentPath) return false;
var normalizedCurrent = currentPath.replace(/\\/g, '/');
return pageAiNormalizeArray(files).some(function(file) {
var path = String(file && file.path || '').trim().replace(/\\/g, '/');
return path && (path === normalizedCurrent || path.endsWith('/' + normalizedCurrent.split('/').pop()));
});
}
function pageAiOpencodeEventMatchesSession(payload, sessionId) {
var id = String(sessionId || '').trim();
if (!id) return false;
var node = payload && typeof payload === 'object' ? (payload.properties || payload.data || payload) : {};
if (String(node.sessionID || node.sessionId || '').trim() === id) return true;
var text = '';
try { text = JSON.stringify(payload || {}); } catch (_error) { text = ''; }
return text.indexOf(id) !== -1;
}
function pageAiScheduleOpencodeRefresh(reason) {
if (!pageUiState.pageAiOpencodeSessionId) return;
if (pageUiState.pageAiOpencodeEventTimer) window.clearTimeout(pageUiState.pageAiOpencodeEventTimer);
pageUiState.pageAiOpencodeEventTimer = window.setTimeout(function() {
pageUiState.pageAiOpencodeEventTimer = 0;
void pageAiRefreshOpencodeProjection();
}, reason === 'open' ? 250 : 700);
}
function pageAiStartOpencodeEvents() {
var sessionId = String(pageUiState.pageAiOpencodeSessionId || '').trim();
if (!sessionId || !('EventSource' in window)) return;
var existing = pageUiState.pageAiOpencodeEventSource;
if (existing && pageUiState.pageAiOpencodeEventSessionId === sessionId) return;
if (existing && typeof existing.close === 'function') existing.close();
var source = new EventSource('/api/page-ai/opencode/events');
pageUiState.pageAiOpencodeEventSource = source;
pageUiState.pageAiOpencodeEventSessionId = sessionId;
var handleEvent = function(event) {
var payload = null;
try { payload = event && event.data ? JSON.parse(event.data) : null; } catch (_error) { payload = event && event.data ? { raw: String(event.data) } : null; }
var eventType = String(event && event.type || payload && (payload.type || payload.event) || 'message');
var changedFiles = pageAiOpencodeNormalizeChangedFiles(payload);
if (changedFiles.length) {
pageUiState.pageAiOpencodeChangedFiles = changedFiles;
pageUiState.pageAiOpencodeDiffError = '';
if (pageAiOpencodeChangedFilesHitCurrentPage(changedFiles)) pageAiRefreshOpencodeCurrentPage('page-ai-opencode-event');
pageAiRenderOpencodeHostChrome();
}
if (pageAiOpencodeEventMatchesSession(payload, sessionId) || /diff|file|permission|session|message/.test(eventType)) pageAiScheduleOpencodeRefresh('event');
};
source.onopen = function() { pageAiScheduleOpencodeRefresh('open'); };
source.onmessage = handleEvent;
['message.updated', 'message.part.updated', 'message.part.delta', 'session.diff', 'session.error', 'session.updated', 'session.next.text.delta', 'session.next.tool.called', 'session.next.tool.success', 'permission.v2.asked', 'permission.v2.replied', 'file.edited'].forEach(function(type) {
source.addEventListener(type, handleEvent);
});
source.onerror = function() {
pageUiState.pageAiOpencodeDiffError = pageUiState.pageAiOpencodeDiffError || 'opencode event 暂不可用,保留手动刷新';
pageAiRenderOpencodeHostChrome();
};
}
async function pageAiSendOpencodeMessage(text) {
var sessionId = String(pageUiState.pageAiOpencodeSessionId || '').trim();
if (!sessionId) await pageAiEnsureOpencodeSessionBinding(true);
sessionId = String(pageUiState.pageAiOpencodeSessionId || '').trim();
if (!sessionId) throw new Error('opencode session 未就绪');
pageUiState.pageAiOpencodeMessages = pageAiNormalizeArray(pageUiState.pageAiOpencodeMessages);
pageUiState.pageAiOpencodeSending = true;
pageAiRenderOpencodeHostChrome();
var response = await fetch('/api/page-ai/opencode/prompt', {
method: 'POST',
headers: { 'content-type': 'application/json', accept: 'application/json' },
cache: 'no-store',
body: JSON.stringify({ sessionId: sessionId, text: text, model: PAGE_AI_OPENCODE_MODEL })
});
var payload = await response.json().catch(function() { return null; });
if (!response.ok) throw new Error(payload && payload.error ? String(payload.error) : 'HTTP ' + response.status);
pageUiState.pageAiOpencodeSending = false;
pageAiScheduleOpencodeRefresh('prompt');
}
async function pageAiReplyOpencodePermission(id, response) {
var sessionId = String(pageUiState.pageAiOpencodeSessionId || '').trim();
if (!sessionId || !id) return;
await fetch('/api/page-ai/opencode/permission/reply', {
method: 'POST',
headers: { 'content-type': 'application/json', accept: 'application/json' },
cache: 'no-store',
body: JSON.stringify({ sessionId: sessionId, requestId: id, response: response })
});
void pageAiLoadOpencodePermissions();
}
async function pageAiAbortOpencodeRun() {
var sessionId = String(pageUiState.pageAiOpencodeSessionId || '').trim();
if (!sessionId) return;
await fetch('/api/page-ai/opencode/abort', {
method: 'POST',
headers: { 'content-type': 'application/json', accept: 'application/json' },
cache: 'no-store',
body: JSON.stringify({ sessionId: sessionId })
});
pageAiScheduleOpencodeRefresh('abort');
}
function pageAiSelectOpencodeSession(sessionId) {
var id = String(sessionId || '').trim();
if (!id) return;
pageUiState.pageAiOpencodeSessionId = id;
pageUiState.pageAiOpencodeMessages = [];
pageUiState.pageAiOpencodeLocalMessages = [];
pageAiStartOpencodeEvents();
void pageAiRefreshOpencodeProjection();
pageAiRenderOpencodeHostChrome();
}
function pageAiOpenOpencodeChangedFile(path) {
var targetPath = String(path || '').trim();
if (!targetPath || !window.__mnoteDocumentPaneRuntime || typeof window.__mnoteDocumentPaneRuntime.openResourceInActiveTab !== 'function') return false;
void window.__mnoteDocumentPaneRuntime.openResourceInActiveTab({ path: targetPath });
return true;
}
function pageAiRefreshOpencodeCurrentPage(reason) {
if (window.__mnoteDocumentPaneRuntime && typeof window.__mnoteDocumentPaneRuntime.refreshPrimaryDocument === 'function') {
void window.__mnoteDocumentPaneRuntime.refreshPrimaryDocument({ reason: reason || 'page-ai-opencode-host' });
return true;
}
return false;
}
function pageAiApplyBoardFirstShell() {
if (!pageAiBoardFirstEnabled()) return;
var drawer = document.querySelector('[data-testid="wolai-page-ai-drawer"]');
if (!(drawer instanceof HTMLElement)) return;
if (drawer.getAttribute('data-page-ai-opencode-host') === 'true') return;
drawer.setAttribute('data-page-ai-board-first', 'true');
var subtitle = drawer.querySelector('.wolai-page-ai-subtitle');
if (subtitle instanceof HTMLElement) {
subtitle.innerHTML = '<span>Agent Board</span><span>ZCode 默认 developer</span><span>local-first 文件编辑</span>';
}
var runtimeStrip = drawer.querySelector('[data-page-ai-runtime-strip]');
if (runtimeStrip instanceof HTMLElement) {
runtimeStrip.setAttribute('aria-label', '打开 Agent Board 运行状态');
}
var runtimeBadge = drawer.querySelector('[data-page-ai-runtime-badge]');
if (runtimeBadge instanceof HTMLElement) runtimeBadge.textContent = 'Agent Board · ' + pageAiBoardWorkflowLabel();
var agentChip = drawer.querySelector('.wolai-page-ai-agent-chip');
if (agentChip instanceof HTMLElement) {
agentChip.textContent = pageAiBoardWorkerLabel() + ' / ' + pageAiBoardModelLabel();
agentChip.setAttribute('title', '当前 worker/model' + pageAiBoardWorkerLabel() + ' / ' + pageAiBoardModelLabel());
}
drawer.querySelectorAll('[data-page-ai-tab="agent"], [data-page-ai-tab="reasonix-settings"], [data-page-ai-tab="hermes-settings"]').forEach(function(node) {
if (node instanceof HTMLElement) node.hidden = true;
});
drawer.querySelectorAll('[data-page-ai-panel="agent"], [data-page-ai-panel="reasonix-settings"], [data-page-ai-panel="hermes-settings"]').forEach(function(node) {
if (node instanceof HTMLElement) node.setAttribute('data-page-ai-legacy-provider-panel', 'true');
});
}
function pageAiBoardDefaultWorkerId() {
return 'mnote-page-ai-zcode';
}
function pageAiBoardDefaultWorkflowId() {
return 'builtin-mnote-page-ai-chat';
}
function pageAiBoardDefaultModelOverride() {
return 'zcode-default';
}
function pageAiBoardDefaultModelOptions() {
return [
{ id: 'zcode-default', label: '默认', default: true },
{ id: 'zcode-fast', label: '快速' },
{ id: 'zcode-strong', label: '强力' }
];
}
function pageAiBoardSelectedWorkerId() {
return String(pageUiState.pageAiBoardWorkerId || pageAiBoardDefaultWorkerId()).trim() || pageAiBoardDefaultWorkerId();
}
function pageAiBoardSelectedWorkflowId() {
return String(pageUiState.pageAiBoardWorkflowId || pageAiBoardDefaultWorkflowId()).trim() || pageAiBoardDefaultWorkflowId();
}
function pageAiBoardModelOptions(workerId) {
var selectedWorkerId = String(workerId || pageAiBoardSelectedWorkerId()).trim() || pageAiBoardDefaultWorkerId();
var worker = pageAiBoardWorkerOptions().find(function(item) { return String(item.id || '') === selectedWorkerId; }) || {};
var options = pageAiNormalizeArray(worker.modelOptions || worker.models || worker.allowedModels)
.map(function(option) {
if (typeof option === 'string') return { id: option, label: option };
return {
id: String(option && (option.id || option.value || option.modelOverride || option.model) || '').trim(),
label: String(option && (option.label || option.name || option.id || option.value || option.modelOverride || option.model) || '').trim(),
default: Boolean(option && option.default)
};
})
.filter(function(option) { return option.id; });
if (!options.length && selectedWorkerId === pageAiBoardDefaultWorkerId()) options = pageAiBoardDefaultModelOptions();
return options;
}
function pageAiBoardSelectedModelOverride() {
var options = pageAiBoardModelOptions();
var fallback = (options.find(function(option) { return option.default; }) || options[0] || { id: pageAiBoardDefaultModelOverride() }).id;
var selected = String(pageUiState.pageAiBoardModelOverride || fallback || pageAiBoardDefaultModelOverride()).trim();
if (!options.length) return selected || pageAiBoardDefaultModelOverride();
return options.some(function(option) { return option.id === selected; }) ? selected : fallback;
}
function pageAiBoardModelLabel(modelOverride) {
var id = String(modelOverride || pageAiBoardSelectedModelOverride()).trim();
var option = pageAiBoardModelOptions().find(function(item) { return item.id === id; });
return String(option && (option.label || option.id) || id || '默认').trim();
}
function pageAiBoardWorkerOptions() {
var byId = {};
var fallback = {
id: pageAiBoardDefaultWorkerId(),
name: 'MNote 页面 AI · ZCode',
surface: 'mnote-page-ai',
role: 'developer',
agentType: 'zcode',
agentModel: 'default',
capabilities: ['text', 'repo-edit', 'terminal', 'mnote-capability-envelope', 'local-markdown-edit'],
modelOptions: pageAiBoardDefaultModelOptions()
};
pageAiNormalizeArray(pageUiState.pageAiBoardWorkers).concat([fallback]).forEach(function(worker) {
var id = String(worker && (worker.id || worker.workerPresetId || worker.presetId) || '').trim();
var surface = String(worker && (worker.surface || worker.productSurface) || '').trim();
if (!id || id !== pageAiBoardDefaultWorkerId()) return;
if (surface && surface !== 'mnote-page-ai') return;
byId[id] = Object.assign({}, fallback, worker, { id: id, surface: 'mnote-page-ai' });
});
return Object.keys(byId).map(function(id) { return byId[id]; });
}
function pageAiBoardWorkflowOptions() {
var byId = {};
var fallback = { id: pageAiBoardDefaultWorkflowId(), name: 'MNote 页面 AI', surface: 'mnote-page-ai' };
pageAiNormalizeArray(pageUiState.pageAiBoardWorkflows).concat([fallback]).forEach(function(workflow) {
var id = String(workflow && (workflow.id || workflow.workflowId) || '').trim();
var surface = String(workflow && (workflow.surface || workflow.productSurface) || '').trim();
if (!id || id !== pageAiBoardDefaultWorkflowId()) return;
if (surface && surface !== 'mnote-page-ai') return;
byId[id] = Object.assign({}, fallback, workflow, { id: id, surface: 'mnote-page-ai' });
});
return Object.keys(byId).map(function(id) { return byId[id]; });
}
function pageAiBoardWorkerLabel(workerId) {
var id = String(workerId || pageAiBoardSelectedWorkerId()).trim();
var worker = pageAiBoardWorkerOptions().find(function(item) { return String(item.id || '') === id; });
return String(worker && (worker.name || worker.id) || id || 'Board worker').trim();
}
function pageAiBoardWorkflowLabel(workflowId) {
var id = String(workflowId || pageAiBoardSelectedWorkflowId()).trim();
var workflow = pageAiBoardWorkflowOptions().find(function(item) { return String(item.id || '') === id; });
return String(workflow && (workflow.name || workflow.id) || id || 'Board workflow').trim();
}
async function pageAiLoadBoardCatalog() {
if (!pageAiBoardFirstEnabled()) return;
try {
var workersResponse = await fetch('/api/page-ai/board/workers', { headers: { accept: 'application/json' }, cache: 'no-store' });
var workersPayload = await workersResponse.json().catch(function() { return null; });
if (workersResponse.ok) {
var workers = pageAiNormalizeArray(workersPayload && workersPayload.workers);
if (!workers.length) workers = pageAiNormalizeArray(workersPayload && workersPayload.workers && workersPayload.workers.workers);
pageUiState.pageAiBoardWorkers = workers;
}
var workflowsResponse = await fetch('/api/page-ai/board/workflows', { headers: { accept: 'application/json' }, cache: 'no-store' });
var workflowsPayload = await workflowsResponse.json().catch(function() { return null; });
if (workflowsResponse.ok) {
var workflows = pageAiNormalizeArray(workflowsPayload && workflowsPayload.workflows);
pageUiState.pageAiBoardWorkflows = workflows;
}
pageUiState.pageAiBoardCatalogError = '';
} catch (error) {
pageUiState.pageAiBoardCatalogError = error instanceof Error ? error.message : String(error);
}
renderPageAiControls();
pageAiApplyBoardFirstShell();
}
function pageAiExtractFinalAnswer(summary) {
var text = String(summary || '').trim();
if (!text) return '';
var match = text.match(/(?:^|\n)##\s*FINAL_ANSWER\s*\n([\s\S]*?)(?=\n##\s+|$)/i);
if (match && match[1].trim()) return match[1].trim();
return text
.replace(/^##\s*Completed\s*/i, '')
.replace(/\n##\s*Comments[\s\S]*$/i, '')
.replace(/\n##\s*Remaining[\s\S]*$/i, '')
.trim();
}
function pageAiFileUriToPath(rootUri) {
var value = String(rootUri || '').trim();
if (!value.indexOf('file://') === 0 && value.slice(0, 7) !== 'file://') return '';
try {
return decodeURIComponent(value.replace(/^file:\/\//, ''));
} catch (_error) {
return value.replace(/^file:\/\//, '');
}
}
function pageAiJoinPath(rootPath, relativePath) {
var root = String(rootPath || '').replace(/\/+$/, '');
var rel = String(relativePath || '').replace(/\\/g, '/').replace(/^\/+/, '');
if (!root || !rel) return root || rel;
return root + '/' + rel;
}
function pageAiBoardCapabilities() {
return [
'mnote.current_page.read',
'mnote.selection.read',
'mnote.open_resources.snapshot',
'mnote.allowed_roots.describe',
'mnote.lightrag.query',
'mnote.reference.open',
'mnote.page_aggregate.snapshot',
'mnote.local_file.receipt'
];
}
function pageAiBuildBoardEnvelope(prompt, scopedContext, runTargetSnapshot, contextRefs, allowedRoots, agentTargetPackage) {
var workspacePath = agentTargetPackage && agentTargetPackage.workspacePath && typeof agentTargetPackage.workspacePath === 'object'
? agentTargetPackage.workspacePath
: (runTargetSnapshot && runTargetSnapshot.workspacePath && typeof runTargetSnapshot.workspacePath === 'object' ? runTargetSnapshot.workspacePath : {});
var relativePath = String(workspacePath.relativePath || agentTargetPackage && agentTargetPackage.relativePath || runTargetSnapshot && runTargetSnapshot.relativePath || '').trim();
var rootPath = pageAiFileUriToPath(currentRootUri());
var absolutePath = String(workspacePath.absolutePath || workspacePath.path || '').trim() || pageAiJoinPath(rootPath, relativePath);
var selectedText = String(scopedContext && scopedContext.selectedText || '').trim();
var writeIntent = /\b(edit|write|append|replace|fix|rewrite|update)\b|编辑|修改|追加|替换|修复|改写|更新/.test(String(prompt || '').toLowerCase());
return {
schema: 'mnote.page_ai.board_task.v1',
intent: writeIntent ? 'edit' : 'ask',
message: prompt,
workspaceId: resolveWorkspaceId(document.body),
sourceKind: currentSourceKind(),
rootUri: currentRootUri(),
documentId: currentDocumentId(),
pageTitle: String((currentPageAggregate().meta || {}).title || document.title || '').trim(),
board: {
workflowId: pageAiBoardSelectedWorkflowId(),
workflowName: pageAiBoardWorkflowLabel(),
workerPresetId: pageAiBoardSelectedWorkerId(),
workerName: pageAiBoardWorkerLabel(),
modelOverride: pageAiBoardSelectedModelOverride(),
modelLabel: pageAiBoardModelLabel()
},
primaryTarget: {
kind: 'markdown_file',
relativePath: relativePath,
absolutePath: absolutePath,
documentId: currentDocumentId(),
workspacePath: workspacePath
},
selection: {
text: selectedText,
range: null
},
contextRefs: contextRefs,
allowedRoots: allowedRoots,
capabilities: pageAiBoardCapabilities(),
modelOverride: pageAiBoardSelectedModelOverride(),
pageContext: pageAiPageContextForRefs(scopedContext.pageContext, contextRefs),
runTargetSnapshot: runTargetSnapshot,
targetPackage: agentTargetPackage,
writePolicy: {
allowFileWrite: allowedRoots.some(function(root) { return String(root.permission || '').indexOf('write') >= 0; }),
requireHumanApproval: false,
forbidMnoteInternalStateWrite: true
}
};
}
function pageAiBoardReceiptSummary(payload) {
var receipt = payload && payload.receipt && typeof payload.receipt === 'object' ? payload.receipt : null;
var board = payload && payload.board && typeof payload.board === 'object' ? payload.board : null;
if (receipt && String(receipt.summary || '').trim()) return String(receipt.summary || '').trim();
var nodeRuns = Array.isArray(board && board.nodeRuns) ? board.nodeRuns : [];
for (var i = nodeRuns.length - 1; i >= 0; i -= 1) {
if (String(nodeRuns[i] && nodeRuns[i].outputSummary || '').trim()) return String(nodeRuns[i].outputSummary).trim();
}
var events = Array.isArray(board && board.events) ? board.events : [];
if (events.length) return String(events[events.length - 1].message || '').trim();
return '';
}
function pageAiBoardRunStatus(payload) {
var board = payload && payload.board && typeof payload.board === 'object' ? payload.board : null;
var run = board && board.run && typeof board.run === 'object' ? board.run : (board && board.id ? board : null);
return String(run && run.status || '').trim();
}
function pageAiBoardRunId(payload) {
var board = payload && payload.board && typeof payload.board === 'object' ? payload.board : null;
var run = board && board.run && typeof board.run === 'object' ? board.run : (board && board.id ? board : null);
return String(run && run.id || payload && payload.runId || payload && payload.id || '').trim();
}
function pageAiBoardReceiptObject(payload) {
var receipt = payload && payload.receipt && typeof payload.receipt === 'object' ? payload.receipt : null;
if (receipt && receipt.receipt && typeof receipt.receipt === 'object') return receipt.receipt;
return receipt;
}
function pageAiBoardFinalAnswer(payload) {
var receipt = pageAiBoardReceiptObject(payload);
var candidates = [
receipt && receipt.finalAnswer,
receipt && receipt.summary,
receipt && receipt.outputSummary,
pageAiBoardReceiptSummary(payload)
];
for (var i = 0; i < candidates.length; i += 1) {
var text = pageAiExtractFinalAnswer(candidates[i]);
if (text) return text;
}
return '';
}
function pageAiBoardChangedFiles(payload) {
var receipt = pageAiBoardReceiptObject(payload) || {};
var changed = pageAiNormalizeArray(receipt.changedFiles || receipt.changed_files || receipt.filesChanged);
return changed.map(function(file) {
if (typeof file === 'string') return { path: file };
return {
path: String(file && (file.path || file.absolutePath || file.relativePath || file.file) || '').trim(),
status: String(file && (file.status || file.changeType || file.kind) || '').trim()
};
}).filter(function(file) { return file.path; });
}
function pageAiBoardTimeline(payload) {
var board = payload && payload.board && typeof payload.board === 'object' ? payload.board : null;
var events = pageAiNormalizeArray(board && board.events);
if (!events.length && board && board.run && Array.isArray(board.run.events)) events = board.run.events;
return pageAiNormalizeArray(events).map(function(event) {
var timestamp = String(event && (event.timestamp || event.createdAt || event.created_at || event.time) || '').trim();
var type = String(event && (event.type || event.eventType || event.event || event.status) || '').trim();
var message = String(event && (event.message || event.summary || event.title || '') || '').trim();
return { timestamp: timestamp, type: type, message: message };
}).filter(function(event) { return event.type || event.message; });
}
function pageAiBoardVerification(payload) {
var receipt = pageAiBoardReceiptObject(payload) || {};
return pageAiNormalizeArray(receipt.verification || receipt.checks).map(function(item) {
if (typeof item === 'string') return { command: item, status: '' };
return {
command: String(item && (item.command || item.name || item.label) || '').trim(),
status: String(item && (item.status || item.result) || '').trim(),
output: String(item && (item.output || item.summary) || '').trim()
};
}).filter(function(item) { return item.command || item.status || item.output; });
}
function pageAiBuildBoardRunDetail(runId, payload) {
var status = pageAiBoardRunStatus(payload);
return {
schema: 'mnote.page_ai.board_run_detail.v1',
runId: runId,
status: status === 'complete' ? 'completed' : (status || 'completed'),
workflowId: pageAiBoardSelectedWorkflowId(),
workflowName: pageAiBoardWorkflowLabel(),
workerPresetId: pageAiBoardSelectedWorkerId(),
workerName: pageAiBoardWorkerLabel(),
modelOverride: pageAiBoardSelectedModelOverride(),
modelLabel: pageAiBoardModelLabel(),
finalAnswer: pageAiBoardFinalAnswer(payload),
changedFiles: pageAiBoardChangedFiles(payload),
verification: pageAiBoardVerification(payload),
timeline: pageAiBoardTimeline(payload),
receipt: pageAiBoardReceiptObject(payload) || null,
payload: payload || null,
updatedAt: Date.now()
};
}
function pageAiStoreBoardRunDetail(runId, payload) {
if (!runId) return null;
if (!pageUiState.pageAiBoardRunDetails || typeof pageUiState.pageAiBoardRunDetails !== 'object') {
pageUiState.pageAiBoardRunDetails = {};
}
var detail = pageAiBuildBoardRunDetail(runId, payload);
pageUiState.pageAiBoardRunDetails[runId] = detail;
var current = pageAiCurrentSession();
if (current) {
current.boardRuns = Object.assign({}, current.boardRuns || {}, { [runId]: detail });
current.boardRunId = runId;
current.workflowId = detail.workflowId;
current.workerPresetId = detail.workerPresetId;
current.modelOverride = detail.modelOverride;
current.receiptId = String(detail.receipt && (detail.receipt.id || detail.receipt.receiptId) || runId || '').trim();
}
return detail;
}
async function pageAiPollBoardRun(runId) {
var lastPayload = null;
for (var attempt = 0; attempt < 360; attempt += 1) {
var response = await fetch('/api/page-ai/board/runs/' + encodeURIComponent(runId), { headers: { accept: 'application/json' }, cache: 'no-store' });
var payload = await response.json().catch(function() { return null; });
if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'board_run_status_failed_' + response.status));
lastPayload = payload;
var status = pageAiBoardRunStatus(payload);
if (status) pageAiSetRunStatus(status === 'complete' ? 'completed' : status);
renderPageAiControls();
if (['complete', 'failed', 'blocked', 'cancelled'].indexOf(status) >= 0) return payload;
await new Promise(function(resolve) { window.setTimeout(resolve, attempt < 8 ? 1000 : 2500); });
}
return lastPayload;
}
async function sendPageAiBoardMessage(text) {
if (pageUiState.pageAiBusy) return;
var prompt = searchText(text);
if (!prompt) return;
pageUiState.pageAiBusy = true;
pageAiSetRunStatus('preparing');
var currentSession = pageAiCurrentSession();
try {
var contextSnapshot = currentPageAiContextSnapshot();
var scopedContext = pageAiScopedPageContext(contextSnapshot);
scopedContext.editorTarget = currentPageAiScopedEditorTarget();
assertPageAiTargetInCurrentWorkspace(scopedContext.editorTarget);
if (currentSourceKind() === 'local_folder' && !pageAiNormalizeArray(pageUiState.pageAiAllowedRoots).length) {
await pageAiLoadAllowedRoots();
}
await assertPageAiTargetWritable(scopedContext.editorTarget);
var runTargetSnapshot = pageAiBuildRunTargetSnapshot(scopedContext, prompt);
var contextRefs = pageAiBuildContextRefs(scopedContext, runTargetSnapshot);
var allowedRoots = pageAiBuildAllowedRoots();
var agentTargetPackage = pageAiBuildAgentTargetPackage(scopedContext, runTargetSnapshot);
assertPageAiLocalWritePermission(prompt, agentTargetPackage);
var envelope = pageAiBuildBoardEnvelope(prompt, scopedContext, runTargetSnapshot, contextRefs, allowedRoots, agentTargetPackage);
pageUiState.pageAiMessages.push({ role: 'user', content: prompt });
pageUiState.pageAiMessages.push({ role: 'assistant', kind: 'status', content: '正在处理…', streaming: true });
if (currentSession) {
if (currentSession.title === '新会话') currentSession.title = prompt.length > 18 ? prompt.slice(0, 18) + '…' : prompt;
currentSession.schema = 'mnote.page_ai.session.v2';
currentSession.source = 'board';
currentSession.workerPresetId = pageAiBoardSelectedWorkerId();
currentSession.workflowId = pageAiBoardSelectedWorkflowId();
currentSession.modelOverride = pageAiBoardSelectedModelOverride();
currentSession.messages = pageUiState.pageAiMessages.slice();
currentSession.updatedAt = Date.now();
}
pageAiPersistSessions();
renderPageAiConversation();
renderPageAiControls();
pageAiApplyBoardFirstShell();
var response = await fetch('/api/page-ai/board/runs', {
method: 'POST',
headers: { 'content-type': 'application/json', accept: 'application/json' },
body: JSON.stringify({
workflowId: pageAiBoardSelectedWorkflowId(),
workerPresetId: pageAiBoardSelectedWorkerId(),
modelOverride: pageAiBoardSelectedModelOverride(),
message: prompt,
envelope: envelope,
autoRun: true,
priority: 'high'
})
});
var payload = await response.json().catch(function() { return null; });
if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'board_run_failed_' + response.status));
var runId = pageAiBoardRunId(payload);
pageUiState.pageAiCurrentRunId = runId;
if (runId) {
pageUiState.pageAiMessages = pageUiState.pageAiMessages.filter(function(message) { return !(message && message.kind === 'status' && message.streaming); });
pageUiState.pageAiMessages.push({ role: 'assistant', kind: 'status', content: 'Agent Board 正在处理…', streaming: true, boardRunId: runId });
}
pageAiSetRunStatus('running');
renderPageAiConversation();
pageAiApplyBoardFirstShell();
var finalPayload = runId ? await pageAiPollBoardRun(runId) : payload;
var detail = pageAiStoreBoardRunDetail(runId, finalPayload);
var finalAnswer = detail && detail.finalAnswer || 'Agent Board run 已结束,但没有返回 final answer。';
pageUiState.pageAiMessages = pageUiState.pageAiMessages.filter(function(message) { return !(message && message.kind === 'status' && message.streaming); });
pageUiState.pageAiMessages.push({
role: 'assistant',
content: finalAnswer,
boardRunId: runId,
workflowId: pageAiBoardSelectedWorkflowId(),
workerPresetId: pageAiBoardSelectedWorkerId(),
modelOverride: pageAiBoardSelectedModelOverride(),
retryPrompt: prompt,
receiptId: String(detail && detail.receipt && (detail.receipt.id || detail.receipt.receiptId) || runId || '').trim()
});
if (currentSession) {
currentSession.schema = 'mnote.page_ai.session.v2';
currentSession.source = 'board';
currentSession.messages = pageUiState.pageAiMessages.slice();
currentSession.updatedAt = Date.now();
currentSession.boardRunId = runId;
currentSession.workflowId = pageAiBoardSelectedWorkflowId();
currentSession.workerPresetId = pageAiBoardSelectedWorkerId();
currentSession.modelOverride = pageAiBoardSelectedModelOverride();
}
pageAiPersistSessions();
pageAiSetRunStatus(pageAiBoardRunStatus(finalPayload) === 'complete' ? 'completed' : pageAiBoardRunStatus(finalPayload) || 'completed');
renderPageAiConversation();
pageAiApplyBoardFirstShell();
} catch (error) {
pageAiSetRunStatus('failed');
pageUiState.pageAiMessages.push({ role: 'assistant', content: 'Agent Board 处理失败:' + (error instanceof Error ? error.message : String(error)) });
renderPageAiConversation();
pageAiApplyBoardFirstShell();
} finally {
pageUiState.pageAiBusy = false;
renderPageAiControls();
pageAiApplyBoardFirstShell();
}
}
function pageAiApplyAgentDescriptorsPayload(payload) {
var descriptors = pageAiNormalizeArray(payload && payload.descriptors)
.map(pageAiNormalizeAgentDescriptor)
.filter(function(descriptor) { return descriptor.schema === PAGE_AI_DESCRIPTOR_SCHEMA; });
if (!descriptors.length) return false;
pageUiState.pageAiAgentDescriptors = descriptors;
pageUiState.pageAiAgentDescriptorsLoaded = true;
pageUiState.pageAiAgentDescriptorError = '';
if (payload && payload.preferenceValues && typeof payload.preferenceValues === 'object') {
pageUiState.pageAiSkillPreferences = Object.assign(
{},
pageAiDescriptorPreferenceValues(),
payload.preferenceValues
);
}
var current = pageAiCurrentAgentId();
var record = pageAiAgentRecord(current);
pageUiState.pageAiAgentId = record.id;
pageUiState.pageAiAcpRuntime = record.acpRuntime || pageUiState.pageAiAcpRuntime || 'reasonix';
if (record.tools) pageUiState.pageAiTools = record.tools;
return true;
}
async function pageAiLoadAgentDescriptors() {
try {
var url = new URL('/api/page-ai/agents/descriptors', window.location.origin);
url.searchParams.set('profile', pageAiCurrentProfile());
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) throw new Error(pageAiErrorMessage(payload, 'agent_descriptors_failed_' + response.status));
if (!pageAiApplyAgentDescriptorsPayload(payload)) throw new Error('agent_descriptors_empty');
} catch (error) {
pageUiState.pageAiAgentDescriptorError = error instanceof Error ? error.message : String(error);
pageUiState.pageAiAgentDescriptorsLoaded = false;
}
renderPageAiProviderButtons();
renderPageAiControls();
return pageUiState.pageAiAgentDescriptors;
}
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';
if (agent.tools) pageUiState.pageAiTools = agent.tools;
}
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 pageAiEvidenceRangeFromParam(value) {
var text = String(value || '').trim();
if (!text) return null;
var match = text.match(/^(\d+)[-:,](\d+)$/);
if (!match) return text;
return { start: Number(match[1]), end: Number(match[2]) };
}
function pageAiEvidenceBboxFromParam(value) {
var text = String(value || '').trim();
if (!text) return null;
var parts = text.split(',').map(function(part) { return Number(part.trim()); });
if (parts.length < 4 || parts.slice(0, 4).some(function(part) { return !Number.isFinite(part); })) return text;
return { x0: parts[0], y0: parts[1], x1: parts[2], y1: parts[3] };
}
function pageAiDocumentIdFromUrl(url) {
var prefix = '/documents/';
if (!url.pathname.startsWith(prefix)) return '';
try {
var raw = url.pathname.slice(prefix.length).split('/')[0] || '';
var decoded = decodeURIComponent(raw);
if (decoded.indexOf('%') >= 0 || decoded.indexOf('\uFFFD') >= 0) return '';
return decoded;
} catch (_error) {
return '';
}
}
function pageAiCitationResourcePath(url, rootUri) {
var explicitPath = String(url.searchParams.get('resourcePath') || '').trim();
if (explicitPath) return explicitPath;
var raw = String(url.searchParams.get('resourceTab') || '').trim();
if (!raw) {
try {
var decodedHash = decodeURIComponent(String(url.hash || ''));
var marker = '#resource-tab-';
var markerIndex = decodedHash.indexOf(marker);
if (markerIndex >= 0) raw = decodedHash.slice(markerIndex + marker.length).replace(/^#+/, '').trim();
} catch (_error) {}
}
if (raw.indexOf('::') >= 0) raw = raw.slice(raw.indexOf('::') + 2);
if (!raw.startsWith('resource:file:')) return '';
var rest = raw.slice('resource:file:'.length);
var prefix = String(rootUri || '').trim() + ':';
if (!prefix.trim() || !rest.startsWith(prefix)) return '';
return rest.slice(prefix.length).replace(/^\/+/, '');
}
function pageAiSameMnoteOrigin(url) {
try {
var current = new URL(window.location.origin);
if (url.origin === current.origin) return true;
if (url.hostname === 'mnote.local') return true;
var localNames = ['localhost', '127.0.0.1', '::1', 'mnote.local'];
function defaultPort(protocol) {
return protocol === 'https:' ? '443' : protocol === 'http:' ? '80' : '';
}
return localNames.indexOf(url.hostname) >= 0 &&
localNames.indexOf(current.hostname) >= 0 &&
String(url.port || defaultPort(url.protocol)) === String(current.port || defaultPort(current.protocol));
} catch (_error) {
return false;
}
}
function pageAiPortableCitationUrl(url) {
try {
if (url.pathname === '/api/local-folder/files/open') {
return Boolean(String(url.searchParams.get('rootUri') || '').trim()) &&
Boolean(String(url.searchParams.get('path') || '').trim());
}
if (!url.pathname.startsWith('/documents/')) return false;
var resourceTab = String(url.searchParams.get('resourceTab') || '').trim();
return String(url.searchParams.get('sourceKind') || '').trim() === 'local_folder' ||
Boolean(String(url.searchParams.get('rootUri') || '').trim()) ||
Boolean(String(url.searchParams.get('resourcePath') || '').trim()) ||
resourceTab.startsWith('resource:file:');
} catch (_error) {
return false;
}
}
function pageAiOpenCitationUrl(href) {
var url;
try {
url = new URL(String(href || ''), window.location.origin);
} catch (_error) {
return false;
}
var unwrappedHref = pageAiUnwrapSearchCitationUrl(url);
if (unwrappedHref) return pageAiOpenCitationUrl(unwrappedHref);
if (!pageAiSameMnoteOrigin(url) && !pageAiPortableCitationUrl(url)) return false;
if (url.pathname === '/api/local-folder/files/open') {
var fileRootUri = String(url.searchParams.get('rootUri') || currentRootUri() || '').trim();
var filePath = String(url.searchParams.get('path') || '').trim();
if (!filePath) return false;
void openLocalResourceInActiveTab({
path: filePath,
rootUri: fileRootUri,
documentId: currentDocumentId() || '',
workspaceId: resolveWorkspaceId(document.body) || '',
sourceKind: currentSourceKind() || 'local_folder',
page: url.searchParams.get('page') || undefined,
bbox: pageAiEvidenceBboxFromParam(url.searchParams.get('bbox')),
sourceMapPath: String(url.searchParams.get('sourceMapPath') || '').trim(),
blockId: String(url.searchParams.get('blockId') || '').trim(),
evidenceText: String(url.searchParams.get('evidenceText') || '').trim(),
lineRange: pageAiEvidenceRangeFromParam(url.searchParams.get('lineRange')),
charRange: pageAiEvidenceRangeFromParam(url.searchParams.get('charRange')),
openTarget: 'active-tab',
paneRole: 'primary'
}).then(function(opened) {
if (!opened) window.open(url.toString(), '_blank', 'noopener,noreferrer');
});
return true;
}
if (!url.pathname.startsWith('/documents/')) return false;
var rootUri = String(url.searchParams.get('rootUri') || currentRootUri() || '').trim();
var resourcePath = pageAiCitationResourcePath(url, rootUri);
var documentId = pageAiDocumentIdFromUrl(url) || currentDocumentId() || '';
var workspaceId = String(url.searchParams.get('workspaceId') || resolveWorkspaceId(document.body) || '').trim();
if (resourcePath) {
void openLocalResourceInActiveTab({
path: resourcePath,
rootUri: rootUri,
documentId: documentId,
workspaceId: workspaceId,
sourceKind: String(url.searchParams.get('sourceKind') || currentSourceKind() || 'local_folder').trim(),
page: url.searchParams.get('page') || undefined,
bbox: pageAiEvidenceBboxFromParam(url.searchParams.get('bbox')),
sourceMapPath: String(url.searchParams.get('sourceMapPath') || '').trim(),
blockId: String(url.searchParams.get('blockId') || '').trim(),
evidenceText: String(url.searchParams.get('evidenceText') || '').trim(),
lineRange: pageAiEvidenceRangeFromParam(url.searchParams.get('lineRange')),
charRange: pageAiEvidenceRangeFromParam(url.searchParams.get('charRange')),
openTarget: 'active-tab',
paneRole: 'primary'
}).then(function(opened) {
if (!opened) window.location.assign(url.pathname + url.search + url.hash);
});
return true;
}
if (typeof window.__mnoteDocumentPaneRuntime?.openPrimaryDocument === 'function') {
void window.__mnoteDocumentPaneRuntime.openPrimaryDocument({
documentId: documentId,
workspaceId: workspaceId,
sourceKind: String(url.searchParams.get('sourceKind') || currentSourceKind() || '').trim(),
rootUri: rootUri,
url: url
});
return true;
}
return false;
}
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,
providerKind: spec.providerKind || '',
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(),
providerKind: String(profile && profile.providerKind || profile && profile.provider_kind || '').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) {
var skillRows = pageAiNormalizeArray(category && (category.skills || category.capabilities));
return {
name: String(category && category.name || '').trim() || 'misc',
description: String(category && category.description || '').trim(),
title: String(category && category.title || '').trim(),
capabilities: pageAiNormalizeArray(category && category.capabilities),
skills: skillRows.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(),
toolNames: pageAiNormalizeArray(skill && skill.toolNames),
tools: pageAiNormalizeArray(skill && skill.tools),
toolCount: Number(skill && skill.toolCount || 0),
disabledToolCount: Number(skill && skill.disabledToolCount || 0),
status: String(skill && skill.status || '').trim(),
capabilityId: String(skill && skill.capabilityId || '').trim(),
capabilityKind: String(skill && skill.capabilityKind || '').trim(),
uiKind: String(skill && skill.uiKind || '').trim(),
requiresContextRefs: pageAiNormalizeArray(skill && skill.requiresContextRefs),
readOnly: Boolean(skill && (skill.readOnly || skill.readonly || skill.configurable === false)),
category: String(skill && skill.category || category && category.name || '').trim(),
categoryTitle: String(skill && skill.categoryTitle || category && category.title || 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(),
toolNames: pageAiNormalizeArray(skill && skill.toolNames),
tools: pageAiNormalizeArray(skill && skill.tools),
toolCount: Number(skill && skill.toolCount || 0),
disabledToolCount: Number(skill && skill.disabledToolCount || 0),
status: String(skill && skill.status || '').trim(),
capabilityId: String(skill && skill.capabilityId || '').trim(),
capabilityKind: String(skill && skill.capabilityKind || '').trim(),
uiKind: String(skill && skill.uiKind || '').trim(),
requiresContextRefs: pageAiNormalizeArray(skill && skill.requiresContextRefs),
categoryTitle: String(skill && skill.categoryTitle || '').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 || '',
toolNames: skill.toolNames || [],
tools: skill.tools || [],
toolCount: Number(skill.toolCount || 0),
disabledToolCount: Number(skill.disabledToolCount || 0),
status: skill.status || '',
capabilityId: skill.capabilityId || '',
capabilityKind: skill.capabilityKind || '',
uiKind: skill.uiKind || '',
requiresContextRefs: skill.requiresContextRefs || [],
categoryTitle: skill.categoryTitle || category.title || category.name || '',
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);
}
pageAiUpdateActiveRunSnapshotFromStatus(pageUiState.pageAiRunStatus, 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 pageAiApplyRuntimeStatus(payload) {
if (!payload || typeof payload !== 'object') return null;
pageUiState.pageAiRuntimeStatus = payload;
pageUiState.pageAiRuntimeStatusError = '';
var activeRun = payload.activeRun && typeof payload.activeRun === 'object' ? payload.activeRun : null;
if (activeRun && activeRun.hostRunId) {
pageAiSetRunStatus(String(activeRun.status || 'running'), String(activeRun.hostRunId || ''));
} else if (pageAiRunStatusIsTerminal(pageUiState.pageAiRunStatus)) {
pageAiUpdateActiveRunSnapshotFromStatus(pageUiState.pageAiRunStatus, pageUiState.pageAiCurrentRunId);
} else if (!activeRun && ['running', 'tool_calling', 'queued', 'pending', 'acp_pending'].indexOf(pageUiState.pageAiRunStatus) >= 0) {
pageAiSetRunStatus('idle', pageUiState.pageAiCurrentRunId);
}
var queue = payload.session && payload.session.queue && typeof payload.session.queue === 'object' ? payload.session.queue : null;
if (queue) {
var queuedCount = Number(queue.queuedCount || 0);
pageUiState.pageAiQueueLength = Number.isFinite(queuedCount) ? Math.max(0, queuedCount) : 0;
if (Array.isArray(queue.items)) {
pageUiState.pageAiQueuedItems = queue.items.map(function(item) {
return {
queueId: String(item.queueId || ''),
sessionId: String(item.sessionId || payload.mnoteSessionId || ''),
traceId: String(item.traceId || ''),
preview: String(item.preview || ''),
queuedAt: Number(item.queuedAt || Date.now())
};
}).filter(function(item) { return item.queueId; });
}
}
var acpSessionId = payload.acp && typeof payload.acp === 'object' ? String(payload.acp.acpSessionId || '') : '';
var current = pageAiCurrentSession();
if (current && acpSessionId) {
current.acpSessionId = acpSessionId;
current.runtimeMode = String(payload.mode || '');
current.updatedAt = Date.now();
pageAiPersistSessions();
}
document.documentElement.setAttribute('data-mnote-page-ai-runtime-status-loaded', 'true');
document.documentElement.setAttribute('data-mnote-page-ai-runtime', String(payload.runtime || ''));
document.documentElement.setAttribute('data-mnote-page-ai-runtime-mode', String(payload.mode || ''));
return payload;
}
async function pageAiLoadRuntimeStatus() {
var params = new URLSearchParams();
var sessionId = String(pageUiState.pageAiActiveSessionId || '').trim();
if (sessionId) params.set('sessionId', sessionId);
var workspaceId = String(resolveWorkspaceId(document.body) || '').trim();
if (workspaceId) params.set('workspaceId', workspaceId);
var documentId = String(currentDocumentId() || '').trim();
if (documentId) params.set('documentId', documentId);
var profile = String(pageAiCurrentProfile() || '').trim();
if (profile) params.set('profile', profile);
var response = await fetch('/api/page-ai/runtime/status?' + params.toString(), {
headers: { 'accept': 'application/json' }
});
var payload = await response.json().catch(function(){ return null; });
if (!response.ok || !payload || payload.ok !== true) {
throw new Error(pageAiErrorMessage(payload, 'page_ai_runtime_status_failed_' + response.status));
}
return pageAiApplyRuntimeStatus(payload);
}
function pageAiActiveRunSnapshotKey() {
return pageAiStorageKey() + ':active-run';
}
function pageAiReadActiveRunSnapshot() {
try {
var raw = window.localStorage ? window.localStorage.getItem(pageAiActiveRunSnapshotKey()) : '';
return raw ? JSON.parse(raw) : null;
} catch (_) {
return null;
}
}
function pageAiWriteActiveRunSnapshot(snapshot) {
if (!snapshot || !snapshot.hostRunId) return;
try {
window.localStorage.setItem(pageAiActiveRunSnapshotKey(), JSON.stringify(Object.assign({
schema: 'mnote.page_ai_active_run_snapshot.v1',
createdAt: Date.now()
}, snapshot, {
updatedAt: Date.now()
})));
document.documentElement.setAttribute('data-mnote-page-ai-active-run-snapshot', 'true');
} catch (_) {}
}
function pageAiClearActiveRunSnapshot(runId) {
var existing = pageAiReadActiveRunSnapshot();
if (runId && existing && existing.hostRunId && existing.hostRunId !== runId) return;
try {
if (window.localStorage) window.localStorage.removeItem(pageAiActiveRunSnapshotKey());
document.documentElement.removeAttribute('data-mnote-page-ai-active-run-snapshot');
} catch (_) {}
}
function pageAiRunStatusIsTerminal(status) {
return ['completed', 'failed', 'aborted', 'cancelled', 'canceled'].indexOf(String(status || '').trim()) >= 0;
}
function pageAiUpdateActiveRunSnapshotFromStatus(status, runId) {
var id = String(runId || '').trim();
if (!id) return;
if (pageAiRunStatusIsTerminal(status)) {
pageAiClearActiveRunSnapshot(id);
return;
}
if (['running', 'tool_calling', 'queued', 'pending', 'interrupted', 'acp_pending'].indexOf(String(status || '').trim()) < 0) return;
var previous = pageAiReadActiveRunSnapshot();
var lastSeq = previous && previous.hostRunId === id ? String(previous.lastSeq || '000000000000000000') : '000000000000000000';
pageAiWriteActiveRunSnapshot({
hostRunId: id,
sessionId: pageUiState.pageAiActiveSessionId || '',
lastSeq: lastSeq,
status: status || 'running'
});
}
async function pageAiCheckAndResumeActiveRun() {
var snapshot = pageAiReadActiveRunSnapshot();
var sessionId = snapshot && snapshot.sessionId ? String(snapshot.sessionId || '').trim() : '';
var run = await pageAiCheckActiveRun(sessionId || undefined);
if (run && sessionId && pageUiState.pageAiActiveSessionId !== sessionId) {
pageAiSetActiveSession(sessionId);
}
if (run) await pageAiResumeActiveRunJournal(run);
if (!run && snapshot && snapshot.hostRunId) {
pageAiStopStreamingAssistantMessage(snapshot.hostRunId);
pageAiClearActiveRunSnapshot(snapshot.hostRunId);
pageUiState.pageAiBusy = false;
pageAiSetRunStatus('idle', snapshot.hostRunId);
}
return run;
}
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 pageAiRefreshClientQueueState() {
var clientItems = pageAiNormalizeArray(pageUiState.pageAiClientQueuedPrompts);
var serverItems = pageAiNormalizeArray(pageUiState.pageAiQueuedItems)
.filter(function(item) { return String(item.queueId || '').indexOf('client_') !== 0; });
pageUiState.pageAiQueuedItems = serverItems.concat(clientItems.map(function(item) {
return {
queueId: item.queueId,
sessionId: item.sessionId || pageUiState.pageAiActiveSessionId || '',
traceId: item.traceId || '',
preview: item.preview || item.prompt || '',
queuedAt: item.queuedAt || Date.now()
};
}));
pageUiState.pageAiQueueLength = pageUiState.pageAiQueuedItems.length;
}
function pageAiEnqueueClientPrompt(prompt) {
var queueId = 'client_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2, 8);
pageUiState.pageAiClientQueuedPrompts.push({
queueId: queueId,
prompt: prompt,
preview: String(prompt || '').slice(0, 120),
sessionId: pageUiState.pageAiActiveSessionId || '',
queuedAt: Date.now()
});
pageAiRefreshClientQueueState();
pageAiSetRunStatus('queued', pageUiState.pageAiCurrentRunId);
return queueId;
}
function pageAiRemoveClientQueuedPrompt(queueId) {
var before = pageUiState.pageAiClientQueuedPrompts.length;
pageUiState.pageAiClientQueuedPrompts = pageUiState.pageAiClientQueuedPrompts.filter(function(item) {
return item.queueId !== queueId;
});
pageAiRefreshClientQueueState();
return before !== pageUiState.pageAiClientQueuedPrompts.length;
}
function pageAiStartNextClientQueuedPrompt() {
if (pageUiState.pageAiBusy) return false;
if (['running', 'tool_calling', 'pending', 'acp_pending'].indexOf(pageUiState.pageAiRunStatus) >= 0) return false;
var next = pageUiState.pageAiClientQueuedPrompts.shift();
if (!next) {
pageAiRefreshClientQueueState();
return false;
}
pageAiRefreshClientQueueState();
window.setTimeout(function() {
void sendPageAiMessage(next.prompt);
}, 0);
return true;
}
function pageAiUnwrapSearchCitationUrl(url) {
try {
var raw = '';
['wd', 'q', 'query'].some(function(key) {
raw = String(url.searchParams.get(key) || '').trim();
return Boolean(raw);
});
if (!raw) return '';
if (/^documents\//i.test(raw)) raw = '/' + raw;
if (!/^\/documents\//i.test(raw) && !/^https?:\/\/[^/]+\/documents\//i.test(raw) && !/^mnote:\/\/open/i.test(raw)) return '';
if (/^mnote:\/\/open/i.test(raw)) return raw;
var nested = new URL(raw, window.location.origin);
if (!nested.pathname.startsWith('/documents/')) return '';
['sourceKind', 'rootUri', 'workspaceId', 'resourceTab', 'resourcePath', 'page', 'bbox', 'sourceMapPath', 'blockId', 'evidenceText', 'lineRange', 'charRange'].forEach(function(key) {
if (nested.searchParams.get(key)) return;
var value = String(url.searchParams.get(key) || '').trim();
if (value) nested.searchParams.set(key, value);
});
return nested.pathname + nested.search;
} catch (_error) {
return '';
}
}
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) {
var rootUri = String((receipt && receipt.rootUri) || (fallbackAudit && fallbackAudit.rootUri) || currentRootUri() || '');
var syntheticPayload = {
schema: 'mnote.local_folder.watch_batch.v1',
source: 'agent_run_receipt',
runId: runId,
rootUri: rootUri,
changedPaths: changedPaths,
affectedParents: affectedParents
};
document.documentElement.setAttribute('data-mnote-page-ai-receipt-filetree-refresh', 'true');
if (window.__mnoteLocalFolderEventBus && typeof window.__mnoteLocalFolderEventBus.emitSyntheticWatchBatch === 'function') {
window.__mnoteLocalFolderEventBus.emitSyntheticWatchBatch({
source: 'synthetic_page_ai_receipt',
reason: 'agent_run_receipt',
rootUri: rootUri,
workspaceId: resolveWorkspaceId(document.body),
payload: syntheticPayload
});
} else {
window.dispatchEvent(new CustomEvent('tree:local-folder-watch-batch', {
detail: { payload: syntheticPayload }
}));
}
}
}
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;
existing.rawOutput = toolEvent && toolEvent.output;
existing.rawResult = resultSource;
if (status === 'completed') {
pageAiNotifyToolWriteCompleted(toolName, toolEvent, runId, runTraceId);
}
return existing;
}
async function pageAiCancelQueuedRun(queueId) {
queueId = String(queueId || '').trim();
if (!queueId) return;
if (queueId.indexOf('client_') === 0) {
if (pageAiRemoveClientQueuedPrompt(queueId)) {
pageUiState.pageAiMessages.push({ role: 'assistant', content: '已取消一条 AI 队列项。' });
}
renderPageAiConversation();
renderPageAiControls();
return;
}
var item = pageUiState.pageAiQueuedItems.find(function(entry) {
return entry.queueId === queueId;
});
var sessionId = String(item && item.sessionId || pageUiState.pageAiActiveSessionId || '').trim();
if (!sessionId) return;
try {
var response = await fetch('/api/page-ai/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();
}
async function pageAiResetRuntime() {
var sessionId = String(pageUiState.pageAiActiveSessionId || '').trim();
if (!sessionId) return;
try {
var response = await fetch('/api/page-ai/runtime/reset', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
sessionId: sessionId,
runtime: pageUiState.pageAiAcpRuntime || '',
profile: pageAiCurrentProfile()
})
});
var payload = await response.json().catch(function(){ return null; });
if (!response.ok || !payload || payload.ok !== true) {
throw new Error(pageAiErrorMessage(payload, 'runtime_reset_failed_' + response.status));
}
pageUiState.pageAiRuntimeStatus = null;
pageUiState.pageAiRuntimeStatusError = '';
pageUiState.pageAiMessages.push({
role: 'assistant',
content: '已重置 runtimeMNote 会话历史保留,外部 provider 会话未删除。'
});
await pageAiLoadRuntimeStatus().catch(function(error) {
pageUiState.pageAiRuntimeStatusError = error instanceof Error ? error.message : String(error);
return null;
});
} catch (error) {
pageUiState.pageAiRuntimeStatusError = error instanceof Error ? error.message : String(error);
}
renderPageAiConversation();
renderPageAiControls();
}
function pageAiSetContextScope(scope) {
var next = String(scope || '').trim() || 'page';
pageUiState.pageAiContextScope = next;
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 descriptor = pageAiAgentDescriptor(pageAiCurrentAgentId());
if (descriptor && pageUiState.pageAiAgentDescriptorsLoaded) {
pageUiState.pageAiTools = pageAiNormalizeArray(descriptor.tools);
pageUiState.pageAiToolsError = '';
renderPageAiControls();
return;
}
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 {
if (pageAiBoardFirstEnabled()) {
var boardResponse = await fetch('/api/page-ai/board/runs/' + encodeURIComponent(runId) + '/cancel', {
method: 'POST',
headers: { accept: 'application/json' }
});
var boardPayload = await boardResponse.json().catch(function(){ return null; });
if (!boardResponse.ok) throw new Error(pageAiErrorMessage(boardPayload, 'board_run_cancel_failed_' + boardResponse.status));
pageAiSetRunStatus('cancelled', runId);
pageUiState.pageAiMessages.push({ role: 'assistant', content: '已请求停止当前 Board run。', boardRunId: runId });
return;
}
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);
var endpoint = '/api/hermes/client/skills';
if (runtime === 'mnote') {
endpoint = '/api/hermes/client/capabilities';
params.set('agentId', 'reasonix');
if (profile) params.set('profile', profile);
} else if (runtime === 'reasonix') {
params.set('runtime', 'reasonix');
} else if (runtime === 'hermes' && profile) {
params.set('profileId', profile);
}
return fetch(endpoint + '?' + 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, 'capabilities_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 pageAiLoadAgentDescriptors();
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());
void pageAiLoadAgentDescriptors();
}
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 pageAiLoadAgentDescriptors();
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/capabilities/toggle', {
method: 'PUT',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
runtime: 'mnote',
profile: skillProfile || pageAiCurrentProfile(),
id: name,
enabled: Boolean(enabled)
})
});
var mnotePayload = await mnoteResponse.json().catch(function(){ return null; });
if (!mnoteResponse.ok) throw new Error(pageAiErrorMessage(mnotePayload, 'mnote_capability_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);
skill.status = enabled ? 'available' : 'disabled';
pageAiNormalizeArray(skill.tools).forEach(function(tool) {
tool.enabled = Boolean(enabled);
tool.status = enabled ? 'available' : 'disabled';
});
skill.disabledToolCount = enabled ? 0 : Number(skill.toolCount || pageAiNormalizeArray(skill.tools).length || 0);
}
});
});
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() {
if (pageAiOpencodeHostEnabled()) {
pageAiEnsureContextRefState();
void pageAiLoadAllowedRoots().then(function() {
pageAiRenderOpencodeHostChrome();
}).catch(function() {
pageAiRenderOpencodeHostChrome();
});
var opencodeDrawer = pageAiEnsureOpencodeHostDrawer();
pageAiApplyDrawerWidth(opencodeDrawer);
opencodeDrawer.hidden = false;
pageUiState.pageAiOpen = true;
updatePageAiTriggerState();
void pageAiLoadOpencodeStatus();
return;
}
pageUiState.pageAiAgentId = pageAiCurrentAgentId();
pageAiEnsureContextRefState();
pageAiLoadSessions();
pageAiRestorePermissionRequests();
renderPageAiSuggestions();
renderPageAiConversation();
renderPageAiProviderButtons();
renderPageAiControls();
pageAiApplyBoardFirstShell();
var drawer = ensurePageAiDrawer();
pageAiApplyDrawerWidth(drawer);
drawer.hidden = false;
pageUiState.pageAiOpen = true;
updatePageAiTriggerState();
void pageAiCheckAndResumeActiveRun().catch(function(error) {
pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
renderPageAiControls();
}).then(function() {
return pageAiLoadRuntimeStatus().catch(function(error) {
pageUiState.pageAiRuntimeStatusError = error instanceof Error ? error.message : String(error);
renderPageAiControls();
pageAiApplyBoardFirstShell();
return null;
});
});
void pageAiLoadBackendSessions().then(function() {
return pageAiCheckAndResumeActiveRun();
}).catch(function(error) {
pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
renderPageAiControls();
pageAiApplyBoardFirstShell();
});
Promise.all([
pageAiLoadProfiles(),
pageAiLoadProfileMemory(),
pageAiLoadAgentDescriptors(),
pageAiLoadSkills(),
pageAiLoadTools(),
pageAiLoadGatewayHealth(),
pageAiLoadAiPreferences(),
pageAiLoadBoardCatalog(),
pageAiLoadAllowedRoots(),
pageAiLoadBackendSessions(),
pageAiLoadRuntimeStatus()
]).then(function() {
renderPageAiControls();
pageAiApplyBoardFirstShell();
}).then(function() {
return pageAiCheckAndResumeActiveRun().catch(function(error) {
pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
renderPageAiControls();
pageAiApplyBoardFirstShell();
return null;
});
}).then(function() {
if (!pageAiBoardFirstEnabled()) return pageAiRestoreHermesSession();
return null;
}).then(function() {
renderPageAiControls();
pageAiApplyBoardFirstShell();
}).catch(function(error) {
pageUiState.pageAiMessages.push({
role: 'assistant',
content: '当前 AI 不可用:' + (error instanceof Error ? error.message : String(error))
});
renderPageAiConversation();
renderPageAiControls();
pageAiApplyBoardFirstShell();
});
}
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 pageAiEventSeqFromPayloadText(payloadText) {
try {
var payload = JSON.parse(payloadText || 'null');
return String((payload && payload.seq) || '').trim();
} catch (_) {
return '';
}
}
function pageAiTrackStreamingRunEvent(runId, eventName, payloadText) {
var seq = pageAiEventSeqFromPayloadText(payloadText);
if (!seq) return;
pageAiWriteActiveRunSnapshot({
hostRunId: runId,
sessionId: pageUiState.pageAiActiveSessionId || '',
lastSeq: seq,
status: pageAiRunStatusIsTerminal(eventName) ? pageUiState.pageAiRunStatus : (pageUiState.pageAiRunStatus || 'running')
});
document.documentElement.setAttribute('data-mnote-page-ai-active-run-last-seq', seq);
}
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 pageAiAppendCitationSection(content, citations) {
var text = String(content || '');
var unique = [];
pageAiNormalizeArray(citations).forEach(function(value) {
var citation = String(value || '').trim();
if (!citation || unique.indexOf(citation) >= 0) return;
unique.push(citation);
});
var missing = unique.filter(function(citation) {
return text.indexOf(citation) < 0;
});
if (!missing.length) return text;
return text.replace(/\s+$/g, '') + '\n\n**引用**\n' + missing.map(function(citation) {
return '- ' + citation;
}).join('\n');
}
function pageAiPromptNeedsKnowledgeRagCitations(prompt) {
var text = searchText(prompt);
if (!text) return false;
var asksCitation = ['链接', '引用', '来源', '出处', '证据', '定位', 'link', 'citation', 'source'].some(function(word) {
return text.indexOf(word) >= 0;
});
if (!asksCitation) return false;
return ['lightrag', '资料库', '知识库', 'rag', '文献', '论文', '保护基'].some(function(word) {
return text.indexOf(word) >= 0;
});
}
function pageAiKnowledgeRagFallbackQuery(prompt) {
var text = searchText(prompt);
if (!text) return '';
var afterColon = text.split(/[:]/).slice(1).join('').trim();
var candidate = afterColon || text;
candidate = candidate
.replace(/^(请|请用|帮我|帮忙|用)?(资料库|知识库|lightrag|rag)?(搜索|检索|查找|查询|回答|说明|解释|总结)?/i, '')
.replace(/(请)?(给出|给我|附上|提供)?(链接|引用|来源|出处|证据|定位).*$/i, '')
.trim();
var stopIndex = candidate.search(/(在|的|是|有哪些|有什么|如何|怎么|用于|用途|作用|资料|文献|论文)/);
if (stopIndex > 0) candidate = candidate.slice(0, stopIndex).trim();
var cjkMatch = candidate.match(/[A-Za-z0-9\u4e00-\u9fff·α-ωΑ-Ω\-]{2,24}/);
if (cjkMatch && cjkMatch[0]) return cjkMatch[0];
return text;
}
function pageAiCleanDegradedCitationNotes(content) {
return String(content || '')
.split('\n')
.filter(function(line) {
var text = String(line || '');
return text.indexOf('来源定位降级') < 0 && text.toLowerCase().indexOf('locator degraded') < 0;
})
.join('\n')
.replace(/\n{3,}/g, '\n\n')
.trim();
}
function pageAiCitationPrecisionLabel(value) {
var precision = searchText(value || '').toLowerCase();
if (precision === 'bbox') return '精确定位';
if (precision === 'paragraph') return '段落级';
if (precision === 'file') return '文件级';
return '';
}
function pageAiCitationSourceTitle(citation) {
if (!citation || typeof citation !== 'object') return '';
var path = searchText(citation.sourceRootRelativePath || citation.sourcePath || citation.filePath || citation.lightRagFilePath || '');
if (path) {
var parts = path.split('/').filter(Boolean);
return parts[parts.length - 1] || path;
}
var markdown = searchText(citation.citationMarkdown || '');
var labelMatch = markdown.match(/^\[([^\]]+)\]/);
return labelMatch ? labelMatch[1] : '';
}
function pageAiCitationDisplayMarkdown(value) {
if (typeof value === 'string') return value.trim();
if (!value || typeof value !== 'object') return '';
var markdown = searchText(value.citationMarkdown || '');
var citationId = searchText(value.citationLabel || (value.citationId ? '[' + value.citationId + ']' : ''));
var title = pageAiCitationSourceTitle(value);
var headingPath = pageAiNormalizeArray(value.headingPath).map(function(item) {
return searchText(item);
}).filter(Boolean).join(' > ');
var precisionLabel = pageAiCitationPrecisionLabel(value.locatorPrecision);
var quote = searchText(value.displayQuote || value.quote || '');
if (quote.length > 120) quote = quote.slice(0, 117) + '...';
var head = markdown || title;
if (citationId && head.indexOf(citationId) < 0) head = citationId + ' ' + head;
var details = [];
if (precisionLabel) details.push(precisionLabel);
if (headingPath) details.push(headingPath);
if (quote) details.push(quote);
return [head].concat(details).filter(Boolean).join(' · ').trim();
}
function pageAiFilterCitationCards(values) {
var cards = pageAiNormalizeArray(values);
var hasPreciseCitation = cards.some(function(value) {
if (typeof value === 'string') return value.indexOf('来源定位降级') < 0;
return value && typeof value === 'object' && searchText(value.citationMarkdown || '') && value.locatorDegraded !== true;
});
var seen = {};
return cards.map(pageAiCitationDisplayMarkdown).filter(function(card, index) {
var source = cards[index];
if (!card || seen[card]) return false;
if (hasPreciseCitation) {
if (typeof source === 'string' && source.indexOf('来源定位降级') >= 0) return false;
if (source && typeof source === 'object' && source.locatorDegraded === true) return false;
}
seen[card] = true;
return true;
}).slice(0, 5);
}
function pageAiCollectKnowledgeRagApiCitations(payload) {
var structuredCitations = pageAiNormalizeArray(payload && payload.citations);
if (structuredCitations.length) {
return pageAiFilterCitationCards(structuredCitations);
}
var references = pageAiNormalizeArray(payload && payload.references);
return pageAiFilterCitationCards(references);
}
async function pageAiAppendKnowledgeRagFallbackCitations(runId, promptText) {
if (!pageAiPromptNeedsKnowledgeRagCitations(promptText)) return;
var rootUri = currentRootUri();
if (!rootUri) return;
var fallbackQuery = pageAiKnowledgeRagFallbackQuery(promptText);
if (!fallbackQuery) return;
try {
var response = await fetch('/api/knowledge-rag/search', {
method: 'POST',
headers: { 'content-type': 'application/json', 'accept': 'application/json' },
body: JSON.stringify({
workspaceId: resolveWorkspaceId(document.body) || undefined,
rootUri: rootUri,
query: fallbackQuery,
mode: 'mix',
topK: 12,
chunkTopK: 24,
includeChunkContent: true
})
});
var payload = await response.json().catch(function() { return null; });
if (!response.ok) return;
var citations = pageAiCollectKnowledgeRagApiCitations(payload);
if (!citations.length) return;
var message = pageUiState.pageAiMessages.find(function(item) {
return item.role === 'assistant' && item.runId === runId;
}) || pageUiState.pageAiMessages.filter(function(item) { return item.role === 'assistant'; }).slice(-1)[0];
if (!message) return;
var hasPrecise = citations.some(function(citation) { return citation.indexOf('来源定位降级') < 0; });
var baseContent = hasPrecise ? pageAiCleanDegradedCitationNotes(message.content) : String(message.content || '');
message.content = pageAiAppendCitationSection(baseContent, citations);
pageAiSyncCurrentSessionMessages();
pageAiPersistSessions();
renderPageAiConversation();
} catch (error) {
console.warn('MNote Page AI 自动追加 LightRAG 引用失败', error);
}
}
function pageAiFinishStreamingAssistantMessage(runId, finalText, promptText, citations) {
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 hasPreciseCitation = pageAiNormalizeArray(citations).some(function(citation) {
return String(citation || '').indexOf('来源定位降级') < 0;
});
var contentBase = humanizePageAiResponse(text, promptText);
if (hasPreciseCitation) contentBase = pageAiCleanDegradedCitationNotes(contentBase);
var content = pageAiAppendCitationSection(contentBase, citations);
if (message) {
message.content = content;
message.streaming = false;
} else if (content) {
pageUiState.pageAiMessages.push({
role: 'assistant',
content: content,
runId: id
});
}
pageAiSyncCurrentSessionMessages();
pageAiPersistSessions();
void pageAiAppendKnowledgeRagFallbackCitations(id, promptText);
}
function pageAiStopStreamingAssistantMessage(runId) {
var id = String(runId || pageUiState.pageAiCurrentRunId || '');
var changed = false;
pageUiState.pageAiMessages.forEach(function(message) {
if (message && message.role === 'assistant' && message.streaming === true && (!id || message.runId === id)) {
message.streaming = false;
changed = true;
}
});
if (!changed) return;
pageAiSyncCurrentSessionMessages();
pageAiPersistSessions();
renderPageAiConversation();
}
function pageAiToolOutputText(value) {
var parts = [];
function visit(node) {
if (node == null) return;
if (typeof node === 'string') {
parts.push(node);
return;
}
if (typeof node === 'number' || typeof node === 'boolean') return;
if (Array.isArray(node)) {
node.forEach(visit);
return;
}
if (typeof node !== 'object') return;
if (typeof node.text === 'string') parts.push(node.text);
if (typeof node.content === 'string') parts.push(node.content);
if (node.content && typeof node.content === 'object') visit(node.content);
if (node.output && typeof node.output === 'object') visit(node.output);
if (node.result && typeof node.result === 'object') visit(node.result);
}
visit(value);
return parts.join('\n');
}
function pageAiParseJsonMaybe(value) {
if (value && typeof value === 'object') return value;
var text = String(value || '').trim();
if (!text || (text[0] !== '{' && text[0] !== '[')) return null;
try {
return JSON.parse(text);
} catch (_error) {
var firstLine = text.split('\n')[0].trim();
if (firstLine && firstLine !== text && (firstLine[0] === '{' || firstLine[0] === '[')) {
try {
return JSON.parse(firstLine);
} catch (_lineError) {}
}
return null;
}
}
function pageAiCollectCitationMarkdowns(value) {
var citations = [];
var seen = {};
function add(value) {
var citation = String(value || '').trim();
if (!citation || seen[citation]) return;
seen[citation] = true;
citations.push(citation);
}
function visit(node) {
if (node == null) return;
if (typeof node === 'string') {
var parsed = pageAiParseJsonMaybe(node);
if (parsed) visit(parsed);
return;
}
if (Array.isArray(node)) {
node.forEach(visit);
return;
}
if (typeof node !== 'object') return;
if (typeof node.citationMarkdown === 'string') add(pageAiCitationDisplayMarkdown(node));
if (Array.isArray(node.citationMarkdowns)) {
node.citationMarkdowns.forEach(function(value) {
if (typeof value === 'string') add(value);
else if (value && typeof value === 'object') add(pageAiCitationDisplayMarkdown(value));
else visit(value);
});
}
Object.keys(node).forEach(function(key) {
visit(node[key]);
});
}
visit(value);
return citations;
}
function pageAiCollectKnowledgeRagCitations(toolItem, toolEvent) {
var toolName = String((toolItem && toolItem.toolName) || (toolEvent && (toolEvent.name || toolEvent.tool || toolEvent.toolName)) || '');
var sources = [
toolEvent,
toolItem,
toolEvent && toolEvent.output,
toolEvent && toolEvent.result,
toolEvent && toolEvent.summary,
toolItem && toolItem.rawOutput,
toolItem && toolItem.rawResult,
toolItem && toolItem.resultSummary
];
var citations = [];
var outputText = '';
sources.forEach(function(source) {
citations = citations.concat(pageAiCollectCitationMarkdowns(source));
var sourceText = pageAiToolOutputText(source);
if (sourceText) outputText += '\n' + sourceText;
var parsed = pageAiParseJsonMaybe(sourceText);
if (parsed) citations = citations.concat(pageAiCollectCitationMarkdowns(parsed));
});
var looksLikeKnowledgeRag = toolName.indexOf('knowledge_rag') >= 0 ||
toolName.indexOf('knowledge.rag') >= 0 ||
outputText.indexOf('mnote.knowledge_rag') >= 0 ||
outputText.indexOf('uiCitations') >= 0;
if (!looksLikeKnowledgeRag && citations.length === 0) return [];
var hasPreciseCitation = citations.some(function(value) {
return String(value || '').indexOf('来源定位降级') < 0;
});
var seen = {};
return citations.filter(function(value) {
var citation = String(value || '').trim();
if (!citation || seen[citation]) return false;
if (hasPreciseCitation && citation.indexOf('来源定位降级') >= 0) return false;
seen[citation] = true;
return true;
});
}
function pageAiJournalPayloadText(event) {
try {
return JSON.stringify(event && event.payload && typeof event.payload === 'object' ? event.payload : {});
} catch (_) {
return '{}';
}
}
function pageAiLastUserPrompt() {
var messages = pageAiNormalizeArray(pageUiState.pageAiMessages);
for (var index = messages.length - 1; index >= 0; index -= 1) {
if (messages[index] && messages[index].role === 'user') return String(messages[index].content || '');
}
return '';
}
function pageAiApplyRunJournalEvent(event, runId, runTraceId) {
if (!event || typeof event !== 'object') return;
var eventName = String(event.kind || event.eventType || event.event || '').trim();
var payload = event.payload && typeof event.payload === 'object' ? event.payload : {};
if (payload.source === 'adapter_replay' || payload.replay === true) {
document.documentElement.setAttribute('data-mnote-page-ai-adapter-replay-seen', 'true');
return;
}
var payloadText = pageAiJournalPayloadText(event);
var prompt = pageAiLastUserPrompt();
if (eventName === 'message.delta') {
pageAiAppendStreamingAssistantDelta(runId, pageAiDecodeDeltaText(payloadText));
} else if (eventName === 'run.completed') {
pageAiFinishStreamingAssistantMessage(runId, String(payload.output || ''), prompt, []);
pageAiSetRunStatus('completed', runId);
} else if (eventName === 'run.failed') {
pageAiFinishStreamingAssistantMessage(runId, String(payload.message || payload.code || payload.error || 'AI run failed'), prompt, []);
pageAiSetRunStatus('failed', runId);
} else if (eventName === 'run.aborted' || eventName === 'run.cancelled' || eventName === 'run.canceled') {
pageUiState.pageAiStoppedRunIds[runId] = true;
pageAiStopStreamingAssistantMessage(runId);
pageAiSetRunStatus('aborted', runId);
} else if (eventName === 'tool.started' || eventName === 'tool.completed' || eventName === 'tool.failed') {
var toolItem = pageAiApplyToolEvent(eventName, payloadText, runId, runTraceId);
if (eventName === 'tool.completed') {
var citations = pageAiCollectKnowledgeRagCitations(toolItem, payload);
if (citations.length) pageAiFinishStreamingAssistantMessage(runId, '', prompt, citations);
}
pageAiSetRunStatus('tool_calling', runId);
} else if (eventName === 'plan.updated') {
var planEntries = Array.isArray(payload.entries) ? payload.entries : [];
if (planEntries.length) {
pageUiState.pageAiMessages.push({ role: 'system', kind: 'plan', entries: planEntries, createdAt: Date.now(), updatedAt: Date.now() });
}
}
var seq = String(event.seq || '').trim();
if (seq) {
pageAiWriteActiveRunSnapshot({
hostRunId: runId,
sessionId: pageUiState.pageAiActiveSessionId || String(event.sessionId || ''),
lastSeq: seq,
status: pageUiState.pageAiRunStatus || 'running'
});
document.documentElement.setAttribute('data-mnote-page-ai-active-run-last-seq', seq);
}
pageAiSyncCurrentSessionMessages();
pageAiPersistSessions();
renderPageAiConversation();
renderPageAiControls();
}
async function pageAiResumeActiveRunJournal(run) {
if (!run || typeof run !== 'object') return;
var runId = String(run.hostRunId || run.runId || '').trim();
if (!runId) return;
var snapshot = pageAiReadActiveRunSnapshot();
var afterSeq = snapshot && snapshot.hostRunId === runId ? String(snapshot.lastSeq || '000000000000000000') : '000000000000000000';
var response = await fetch('/api/page-ai/runs/' + encodeURIComponent(runId) + '/events?afterSeq=' + encodeURIComponent(afterSeq), {
headers: { 'accept': 'application/json' }
});
var payload = await response.json().catch(function(){ return null; });
if (!response.ok || !payload || payload.ok !== true) {
throw new Error(pageAiErrorMessage(payload, 'run_journal_failed_' + response.status));
}
var runTraceId = String((payload.run && payload.run.traceId) || (run && run.traceId) || '');
pageAiNormalizeArray(payload.events).forEach(function(event) {
pageAiApplyRunJournalEvent(event, runId, runTraceId);
});
var status = String((payload.run && payload.run.status) || run.status || '').trim();
if (pageAiRunStatusIsTerminal(status)) pageAiClearActiveRunSnapshot(runId);
document.documentElement.setAttribute('data-mnote-page-ai-run-journal-resumed', runId);
}
function pageAiLooksLikeBlockEdit(prompt) {
var text = searchText(prompt);
return ['新增', '添加', '插入', '删除', '删掉', '修改', '替换', '改成', '移动', '移到', 'move', 'replace', 'delete', 'insert'].some(function(word) {
return text.indexOf(word) >= 0;
});
}
function assertPageAiLocalWritePermission(prompt, targetPackage) {
if (currentSourceKind() !== 'local_folder') return;
if (!pageAiLooksLikeBlockEdit(prompt)) return;
var target = pageAiNormalizeArray(targetPackage && targetPackage.targets)[0] || null;
var permission = String(target && target.policy && target.policy.permission || '').trim();
if (permission === 'read_write') return;
var error = new Error('当前本地工作区只读授权,不能让 AI 写入目标文件。请切换到有写权限的工作区或调整授权后重试。');
error.code = 'page_ai_target_readonly';
throw error;
}
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) {
if (pageAiBoardFirstEnabled()) {
await sendPageAiBoardMessage(text);
return;
}
var allowQueue = pageUiState.pageAiBusy
|| ['queued', 'running', 'tool_calling', 'pending', 'acp_pending'].indexOf(pageUiState.pageAiRunStatus) >= 0;
if (pageUiState.pageAiBusy && !allowQueue) return;
var prompt = searchText(text);
if (!prompt) return;
if (allowQueue) {
pageAiEnqueueClientPrompt(prompt);
pageUiState.pageAiMessages.push({
role: 'assistant',
content: '已加入 AI 队列,前一条 run 完成后继续处理。'
});
var queuedSession = pageAiCurrentSession();
if (queuedSession) {
queuedSession.messages = pageUiState.pageAiMessages.slice();
queuedSession.updatedAt = Date.now();
}
pageAiPersistSessions();
renderPageAiConversation();
renderPageAiControls();
return;
}
if (!allowQueue) pageUiState.pageAiBusy = true;
var currentSession = null;
try {
await pageAiEnsureHermesSession();
if (!allowQueue) pageAiSetRunStatus('queued');
renderPageAiControls();
var contextSnapshot = currentPageAiContextSnapshot();
var scopedContext = pageAiScopedPageContext(contextSnapshot);
scopedContext.editorTarget = currentPageAiScopedEditorTarget();
assertPageAiTargetInCurrentWorkspace(scopedContext.editorTarget);
if (currentSourceKind() === 'local_folder' && !pageAiNormalizeArray(pageUiState.pageAiAllowedRoots).length) {
await pageAiLoadAllowedRoots();
}
await assertPageAiTargetWritable(scopedContext.editorTarget);
var runTargetSnapshot = pageAiBuildRunTargetSnapshot(scopedContext, prompt);
var contextRefs = pageAiBuildContextRefs(scopedContext, runTargetSnapshot);
var allowedRoots = pageAiBuildAllowedRoots();
var agentTargetPackage = pageAiBuildAgentTargetPackage(scopedContext, runTargetSnapshot);
assertPageAiLocalWritePermission(prompt, agentTargetPackage);
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 reasonixSettings = pageAiCurrentAgentId() === 'reasonix' ? pageAiReasonixControlState() : null;
var promptForRun = pageAiApplyReasonixPlanMode(prompt);
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,
reasonixSettings: reasonixSettings,
skillPreferences: {
mnote: pageAiSkillPreferenceTable('mnote', ''),
reasonix: pageAiSkillPreferenceTable('reasonix', ''),
hermes: pageAiSkillPreferenceTable('hermes', runProfile)
},
message: promptForRun,
model: reasonixSettings && reasonixSettings.modelId ? reasonixSettings.modelId : 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();
void pageAiLoadRuntimeStatus().catch(function(error) {
pageUiState.pageAiRuntimeStatusError = error instanceof Error ? error.message : String(error);
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 = '';
var autoCitations = [];
await streamPageAiResponse(eventResponse, function(eventName, payloadText) {
pageAiTrackStreamingRunEvent(runId, eventName, payloadText);
var streamPayload = null;
try {
streamPayload = JSON.parse(payloadText || 'null');
} catch (_) {}
if (streamPayload && (streamPayload.source === 'adapter_replay' || streamPayload.replay === true)) {
document.documentElement.setAttribute('data-mnote-page-ai-adapter-replay-seen', 'true');
return;
}
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;
pageAiStopStreamingAssistantMessage(runId);
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') {
var toolEventPayload = null;
try {
toolEventPayload = JSON.parse(payloadText || 'null');
} catch (_) {}
var toolItem = pageAiApplyToolEvent(eventName, payloadText, runId, runTraceId);
if (eventName === 'tool.completed') {
var completedCitations = pageAiCollectKnowledgeRagCitations(toolItem, toolEventPayload);
if (completedCitations.length) autoCitations = completedCitations;
}
pageAiSyncCurrentSessionMessages();
pageAiPersistSessions();
renderPageAiConversation();
pageAiSetRunStatus('tool_calling', runId);
renderPageAiControls();
}
});
if (!pageUiState.pageAiStoppedRunIds[runId]) {
pageAiFinishStreamingAssistantMessage(runId, assistantText, prompt, autoCitations);
}
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;
await pageAiLoadRuntimeStatus().catch(function(error) {
pageUiState.pageAiRuntimeStatusError = error instanceof Error ? error.message : String(error);
return null;
});
renderPageAiConversation();
renderPageAiControls();
pageAiStartNextClientQueuedPrompt();
}
}
function handlePageAiClick(event, helpers) {
var closestAction = helpers && helpers.closestAction;
if (typeof closestAction !== 'function') return false;
var pageAiCitationLink = closestAction(event.target, 'a[data-page-ai-citation-link="true"]');
if (pageAiCitationLink && !(event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)) {
var href = pageAiCitationLink.getAttribute('href') || '';
if (pageAiOpenCitationUrl(href)) {
event.preventDefault();
return true;
}
}
var pageAiClose = closestAction(event.target, '[data-page-ai-action="close"]');
if (pageAiClose) {
event.preventDefault();
closePageAiDrawer();
return true;
}
var pageAiOpencodeFile = closestAction(event.target, '[data-page-ai-opencode-open-file]');
if (pageAiOpencodeFile) {
event.preventDefault();
pageAiOpenOpencodeChangedFile(pageAiOpencodeFile.getAttribute('data-page-ai-opencode-open-file') || '');
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 pageAiReset = closestAction(event.target, '[data-page-ai-action="reset-runtime"]');
if (pageAiReset) {
event.preventDefault();
void pageAiResetRuntime();
return true;
}
var pageAiStopAllJobs = closestAction(event.target, '[data-page-ai-action="stop-all-jobs"]');
if (pageAiStopAllJobs) {
event.preventDefault();
pageUiState.pageAiMessages.push({ role: 'assistant', content: '当前没有可停止的后台 job;长任务 job 将在 runtime status 中独立显示。' });
renderPageAiConversation();
renderPageAiControls();
return true;
}
var pageAiRotate = closestAction(event.target, '[data-page-ai-action="rotate"]');
if (pageAiRotate) {
event.preventDefault();
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') pageUiState.pageAiPage = 'status';
if (pageUiState.pageAiPage === 'chat-only-settings') pageUiState.pageAiPage = 'agent';
if (pageUiState.pageAiPage === 'status') {
void pageAiLoadGatewayHealth();
void pageAiLoadRuntimeStatus().catch(function(error) {
pageUiState.pageAiRuntimeStatusError = error instanceof Error ? error.message : String(error);
renderPageAiControls();
});
}
if (pageUiState.pageAiPage === 'skills') void pageAiLoadSkills();
renderPageAiControls();
renderPageAiConversation();
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 === 'opencode-send') {
var opencodeInput = ensurePageAiDrawer().querySelector('[data-page-ai-opencode-input]');
var opencodeText = opencodeInput instanceof HTMLTextAreaElement ? opencodeInput.value.trim() : '';
if (opencodeText) {
opencodeInput.value = '';
void pageAiSendOpencodeMessage(opencodeText).catch(function(error) {
pageUiState.pageAiOpencodeStatusError = error instanceof Error ? error.message : String(error);
pageUiState.pageAiOpencodeSending = false;
pageAiRenderOpencodeHostChrome();
});
}
}
if (action === 'cancel-queued-run') {
void pageAiCancelQueuedRun(pageAiAction.getAttribute('data-page-ai-queue-id'));
}
if (action === 'opencode-refresh-status') {
void pageAiLoadOpencodeStatus();
}
if (action === 'opencode-new-session') {
pageUiState.pageAiOpencodeSessionId = '';
pageUiState.pageAiOpencodeBindingKey = '';
pageUiState.pageAiOpencodeMessages = [];
pageUiState.pageAiOpencodeLocalMessages = [];
void pageAiEnsureOpencodeSessionBinding(true);
}
if (action === 'opencode-abort') {
void pageAiAbortOpencodeRun();
}
if (action === 'opencode-refresh-current-page') {
pageAiRefreshOpencodeCurrentPage();
}
if (action === 'opencode-insert-context') {
pageAiPostOpencodeBridgeMessage('insert-text', { text: pageAiOpencodeContextTextForInsert() });
}
return true;
}
var pageAiSessionSelect = closestAction(event.target, '[data-page-ai-opencode-session-select]');
if (pageAiSessionSelect) {
event.preventDefault();
pageAiSelectOpencodeSession(pageAiSessionSelect.getAttribute('data-page-ai-opencode-session-select') || '');
return true;
}
var pageAiPermission = closestAction(event.target, '[data-page-ai-opencode-permission]');
if (pageAiPermission) {
event.preventDefault();
void pageAiReplyOpencodePermission(pageAiPermission.getAttribute('data-page-ai-opencode-permission-id') || '', pageAiPermission.getAttribute('data-page-ai-opencode-permission') || 'once');
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 pageAiSessionSelect = closestAction(event.target, '[data-page-ai-session-select]');
if (pageAiSessionSelect) {
event.preventDefault();
pageAiToggleSessionSelection(
pageAiSessionSelect.getAttribute('data-page-ai-session-select') || '',
pageAiSessionSelect.getAttribute('aria-checked') !== 'true'
);
return true;
}
var pageAiClearSelectedSessions = closestAction(event.target, '[data-page-ai-session-clear-selection]');
if (pageAiClearSelectedSessions) {
event.preventDefault();
pageAiClearSessionSelection();
return true;
}
var pageAiDeleteSelectedSessions = closestAction(event.target, '[data-page-ai-session-delete-selected]');
if (pageAiDeleteSelectedSessions) {
event.preventDefault();
void pageAiDeleteSelectedBackendSessions().catch(function(error) {
pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
renderPageAiControls();
});
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 pageAiSessionExport = closestAction(event.target, '[data-page-ai-session-export]');
if (pageAiSessionExport) {
event.preventDefault();
void pageAiExportBackendSession(pageAiSessionExport.getAttribute('data-page-ai-session-export') || '').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',
pageAiPermissionAction.getAttribute('data-page-ai-permission-option-id') || ''
);
return true;
}
var pageAiPlanAction = closestAction(event.target, '[data-page-ai-plan-action]');
if (pageAiPlanAction) {
event.preventDefault();
var action = String(pageAiPlanAction.getAttribute('data-page-ai-plan-action') || '').trim();
var inputNode = ensurePageAiDrawer().querySelector('[data-page-ai-input]');
if (action === 'approve') {
void sendPageAiMessage('批准,开始执行。');
return true;
}
if (action === 'cancel') {
void sendPageAiMessage('取消执行。');
return true;
}
if (inputNode instanceof HTMLTextAreaElement) {
inputNode.value = action === 'revise' ? '请修改计划:' : '';
inputNode.focus();
}
return true;
}
var pageAiOpenLocationAction = closestAction(event.target, '[data-page-ai-open-location]');
if (pageAiOpenLocationAction) {
event.preventDefault();
var loc = String(pageAiOpenLocationAction.getAttribute('data-page-ai-open-location') || '').trim();
if (loc) pageAiOpenLocation(loc);
return true;
}
var pageAiBoardRunDetail = closestAction(event.target, '[data-page-ai-board-run-detail]');
if (pageAiBoardRunDetail) {
event.preventDefault();
pageUiState.pageAiBoardActiveDetailRunId = pageAiBoardRunDetail.getAttribute('data-page-ai-board-run-detail') || '';
renderPageAiConversation();
return true;
}
var pageAiCopyMessage = closestAction(event.target, '[data-page-ai-copy-message]');
if (pageAiCopyMessage) {
event.preventDefault();
var runId = pageAiCopyMessage.getAttribute('data-page-ai-copy-message') || '';
var message = pageAiNormalizeArray(pageUiState.pageAiMessages).find(function(item) { return String(item && item.boardRunId || '') === runId; });
if (navigator.clipboard && message) void navigator.clipboard.writeText(String(message.content || ''));
return true;
}
var pageAiRetryMessage = closestAction(event.target, '[data-page-ai-retry-message]');
if (pageAiRetryMessage) {
event.preventDefault();
var retryRunId = pageAiRetryMessage.getAttribute('data-page-ai-retry-message') || '';
var retryMessage = pageAiNormalizeArray(pageUiState.pageAiMessages).find(function(item) { return String(item && item.boardRunId || '') === retryRunId; });
var retryPrompt = String(retryMessage && retryMessage.retryPrompt || '').trim();
if (retryPrompt) void sendPageAiMessage(retryPrompt);
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 pageAiDescriptorField = closestAction(event.target, '[data-page-ai-descriptor-field]');
if (pageAiDescriptorField instanceof HTMLInputElement || pageAiDescriptorField instanceof HTMLSelectElement) {
var key = pageAiDescriptorField.getAttribute('data-page-ai-descriptor-field') || '';
var kind = pageAiDescriptorField.getAttribute('data-page-ai-descriptor-kind') || '';
var value = kind === 'boolean' && pageAiDescriptorField instanceof HTMLInputElement
? pageAiDescriptorField.checked
: pageAiDescriptorField.value;
pageAiSetDescriptorFieldValue(key, value);
return true;
}
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 pageAiBoardWorker = closestAction(event.target, '[data-page-ai-board-worker]');
if (pageAiBoardWorker instanceof HTMLSelectElement) {
pageUiState.pageAiBoardWorkerId = String(pageAiBoardWorker.value || pageAiBoardDefaultWorkerId()).trim() || pageAiBoardDefaultWorkerId();
pageUiState.pageAiBoardModelOverride = pageAiBoardSelectedModelOverride();
pageAiPersistAiPreference('board.worker_preset_id', pageUiState.pageAiBoardWorkerId);
pageAiPersistAiPreference('board.model_override', pageUiState.pageAiBoardModelOverride);
renderPageAiControls();
pageAiApplyBoardFirstShell();
return true;
}
var pageAiBoardModel = closestAction(event.target, '[data-page-ai-board-model]');
if (pageAiBoardModel instanceof HTMLSelectElement) {
pageUiState.pageAiBoardModelOverride = String(pageAiBoardModel.value || pageAiBoardSelectedModelOverride()).trim() || pageAiBoardSelectedModelOverride();
pageAiPersistAiPreference('board.model_override', pageUiState.pageAiBoardModelOverride);
var current = pageAiCurrentSession();
if (current) {
current.modelOverride = pageUiState.pageAiBoardModelOverride;
current.updatedAt = Date.now();
}
pageAiPersistSessions();
renderPageAiControls();
pageAiApplyBoardFirstShell();
return true;
}
var pageAiBoardWorkflow = closestAction(event.target, '[data-page-ai-board-workflow]');
if (pageAiBoardWorkflow instanceof HTMLSelectElement) {
pageUiState.pageAiBoardWorkflowId = String(pageAiBoardWorkflow.value || pageAiBoardDefaultWorkflowId()).trim() || pageAiBoardDefaultWorkflowId();
pageAiPersistAiPreference('board.workflow_id', pageUiState.pageAiBoardWorkflowId);
renderPageAiControls();
pageAiApplyBoardFirstShell();
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 pageAiSessionStatusFilter = closestAction(event.target, '[data-page-ai-session-status-filter]');
if (pageAiSessionStatusFilter instanceof HTMLSelectElement) {
pageUiState.pageAiSessionStatusFilter = String(pageAiSessionStatusFilter.value || 'all').trim() || 'all';
renderPageAiControls();
renderPageAiConversation();
return true;
}
var pageAiHideHermesBuiltin = closestAction(event.target, '[data-page-ai-hide-hermes-builtin]');
if (pageAiHideHermesBuiltin instanceof HTMLInputElement) {
pageAiSetHideHermesBuiltinSkills(pageAiHideHermesBuiltin.checked);
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('submit', function(event) {
var form = event.target && event.target.closest ? event.target.closest('[data-page-ai-opencode-composer]') : null;
if (!form) return;
event.preventDefault();
var input = form.querySelector('[data-page-ai-opencode-input]');
var text = input instanceof HTMLTextAreaElement ? input.value.trim() : '';
if (!text) return;
input.value = '';
void pageAiSendOpencodeMessage(text).catch(function(error) {
pageUiState.pageAiOpencodeStatusError = error instanceof Error ? error.message : String(error);
pageAiRenderOpencodeHostChrome();
});
});
document.addEventListener('pointerdown', function(event) {
handlePageAiPointerDown(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);
});
document.addEventListener('visibilitychange', function() {
if (document.visibilityState !== 'visible' || !pageUiState.pageAiOpen) return;
void pageAiCheckAndResumeActiveRun().catch(function(error) {
pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
renderPageAiControls();
});
});
}
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),
pageAiExportBackendSession: (...args) => pageAiExportBackendSession(...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),
pageAiEnsureOpencodeHostDrawer: (...args) => pageAiEnsureOpencodeHostDrawer(...args),
pageAiLoadOpencodeStatus: (...args) => pageAiLoadOpencodeStatus(...args),
pageAiLoadOpencodeDiff: (...args) => pageAiLoadOpencodeDiff(...args),
pageAiOpenOpencodeChangedFile: (...args) => pageAiOpenOpencodeChangedFile(...args),
pageAiRefreshOpencodeCurrentPage: (...args) => pageAiRefreshOpencodeCurrentPage(...args),
updatePageAiTriggerState: (...args) => updatePageAiTriggerState(...args)
};
}