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