5221 lines
248 KiB
JavaScript
5221 lines
248 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 = 4;
|
||
// Page AI 请求合同:agentId 只负责路由;contextRefs 是用户勾选的上下文地址;
|
||
// allowedRoots 只展示 SQLite directory_grants 解析结果,服务端仍会按当前用户重算;
|
||
// runTargetSnapshot 必须来自 OpenEditorsSnapshot,避免运行中 UI 切换导致目标漂移。
|
||
var PAGE_AI_AGENT_FALLBACK_REGISTRY = [
|
||
{ id: 'hermes', label: 'Hermes', acpRuntime: 'hermes', canWriteFiles: true },
|
||
{ id: 'reasonix', label: 'Reasonix', acpRuntime: 'reasonix', canWriteFiles: true },
|
||
{ id: 'chat_only', label: 'Chat-only', acpRuntime: 'hermes', canWriteFiles: false }
|
||
];
|
||
var PAGE_AI_DESCRIPTOR_SCHEMA = 'mnote.ai_agent_descriptor.v1';
|
||
var PAGE_AI_CHAT_ONLY_PROFILE_REGISTRY = [
|
||
{ profileId: 'shared_deepseek_chat', baseProfile: 'deepseek-chat', label: 'DeepSeek' },
|
||
{ profileId: 'shared_doubao_chat', baseProfile: 'doubao-chat', label: '豆包' },
|
||
{ profileId: 'shared_gemini_chat', baseProfile: 'gemini-chat', label: 'Gemini' },
|
||
{ profileId: 'shared_api_deepseek_flash_chat', baseProfile: 'api-deepseek-flash-chat', label: 'DeepSeek Flash', providerKind: 'api-chat' },
|
||
{ profileId: 'shared_api_deepseek_pro_chat', baseProfile: 'api-deepseek-pro-chat', label: 'DeepSeek Pro', providerKind: 'api-chat' },
|
||
{ profileId: 'shared_api_gpt_chat', baseProfile: 'api-gpt-chat', label: 'GPT', providerKind: 'api-chat' },
|
||
{ profileId: 'shared_api_kimi_chat', baseProfile: 'api-kimi-chat', label: 'Kimi', providerKind: 'api-chat' },
|
||
{ profileId: 'shared_api_gemini_chat', baseProfile: 'api-gemini-chat', label: 'Gemini API', providerKind: 'api-chat' },
|
||
{ profileId: 'shared_api_grok_chat', baseProfile: 'api-grok-chat', label: 'Grok API', providerKind: 'api-chat' }
|
||
];
|
||
var PAGE_AI_CONTEXT_REF_REGISTRY = [
|
||
{ id: 'current_page', label: '当前页' },
|
||
{ id: 'selection', label: '选区' },
|
||
{ id: 'active_editor', label: '打开资源' },
|
||
{ id: 'file', label: '文件' },
|
||
{ id: 'folder', label: '文件夹' },
|
||
{ id: 'changed_files', label: '最近修改' }
|
||
];
|
||
var pageAiDelegatesInstalled = false;
|
||
var PAGE_AI_DRAWER_WIDTH_STORAGE_KEY = 'mnote.page_ai.drawer_width';
|
||
var PAGE_AI_DRAWER_DEFAULT_WIDTH = 440;
|
||
var PAGE_AI_DRAWER_MIN_WIDTH = 340;
|
||
var PAGE_AI_DRAWER_MAX_WIDTH = 760;
|
||
var PAGE_AI_OPENCODE_MODEL = { providerID: 'opencodego', modelID: 'deepseek-v4-flash' };
|
||
|
||
function clonePageAiDefaultValue(value) {
|
||
if (Array.isArray(value)) return value.slice();
|
||
if (value && typeof value === 'object') return Object.assign({}, value);
|
||
return value;
|
||
}
|
||
|
||
function pageAiClampDrawerWidth(width) {
|
||
var viewportMax = Math.max(PAGE_AI_DRAWER_MIN_WIDTH, Math.min(PAGE_AI_DRAWER_MAX_WIDTH, window.innerWidth - 24));
|
||
var numeric = Number(width);
|
||
if (!Number.isFinite(numeric) || numeric <= 0) numeric = PAGE_AI_DRAWER_DEFAULT_WIDTH;
|
||
return Math.max(PAGE_AI_DRAWER_MIN_WIDTH, Math.min(viewportMax, Math.round(numeric)));
|
||
}
|
||
|
||
function pageAiStoredDrawerWidth() {
|
||
try {
|
||
return pageAiClampDrawerWidth(window.localStorage ? window.localStorage.getItem(PAGE_AI_DRAWER_WIDTH_STORAGE_KEY) : PAGE_AI_DRAWER_DEFAULT_WIDTH);
|
||
} catch (_error) {
|
||
return pageAiClampDrawerWidth(PAGE_AI_DRAWER_DEFAULT_WIDTH);
|
||
}
|
||
}
|
||
|
||
function pageAiApplyDrawerWidth(drawer, width) {
|
||
var target = drawer instanceof HTMLElement ? drawer : document.querySelector('[data-testid="wolai-page-ai-drawer"]');
|
||
if (!(target instanceof HTMLElement)) return;
|
||
var next = pageAiClampDrawerWidth(width == null ? pageAiStoredDrawerWidth() : width);
|
||
target.style.setProperty('--mnote-page-ai-width', next + 'px');
|
||
}
|
||
|
||
function handlePageAiPointerDown(event, helpers) {
|
||
var closestAction = helpers && helpers.closestAction;
|
||
if (typeof closestAction !== 'function') return false;
|
||
var handle = closestAction(event.target, '[data-page-ai-resize-handle]');
|
||
if (!(handle instanceof HTMLElement)) return false;
|
||
var drawer = handle.closest('[data-testid="wolai-page-ai-drawer"]');
|
||
var panel = drawer instanceof HTMLElement ? drawer.querySelector('.wolai-page-ai-panel') : null;
|
||
if (!(drawer instanceof HTMLElement) || !(panel instanceof HTMLElement)) return false;
|
||
event.preventDefault();
|
||
var pointerId = event.pointerId;
|
||
var startX = Number(event.clientX || 0);
|
||
var startWidth = panel.getBoundingClientRect().width || pageAiStoredDrawerWidth();
|
||
drawer.setAttribute('data-page-ai-resizing', 'true');
|
||
document.documentElement.setAttribute('data-mnote-page-ai-resizing', 'true');
|
||
try {
|
||
handle.setPointerCapture(pointerId);
|
||
} catch (_error) {}
|
||
function move(nextEvent) {
|
||
var nextWidth = startWidth + (startX - Number(nextEvent.clientX || 0));
|
||
pageAiApplyDrawerWidth(drawer, nextWidth);
|
||
}
|
||
function finish(nextEvent) {
|
||
move(nextEvent);
|
||
var value = drawer.style.getPropertyValue('--mnote-page-ai-width').replace('px', '').trim();
|
||
try {
|
||
if (window.localStorage) window.localStorage.setItem(PAGE_AI_DRAWER_WIDTH_STORAGE_KEY, String(pageAiClampDrawerWidth(value)));
|
||
} catch (_error) {}
|
||
drawer.removeAttribute('data-page-ai-resizing');
|
||
document.documentElement.removeAttribute('data-mnote-page-ai-resizing');
|
||
window.removeEventListener('pointermove', move, true);
|
||
window.removeEventListener('pointerup', finish, true);
|
||
window.removeEventListener('pointercancel', finish, true);
|
||
try {
|
||
handle.releasePointerCapture(pointerId);
|
||
} catch (_error) {}
|
||
}
|
||
window.addEventListener('pointermove', move, true);
|
||
window.addEventListener('pointerup', finish, true);
|
||
window.addEventListener('pointercancel', finish, true);
|
||
return true;
|
||
}
|
||
|
||
function ensurePageAiStateFacade(state) {
|
||
var defaults = {
|
||
pageAiOpen: false,
|
||
pageAiBusy: false,
|
||
pageAiMessages: [],
|
||
pageAiSuggestionIndex: 0,
|
||
pageAiProvider: 'hermes',
|
||
pageAiPage: 'chat',
|
||
pageAiRunStatus: 'idle',
|
||
pageAiCurrentRunId: '',
|
||
pageAiAcpRuntime: 'reasonix',
|
||
pageAiAcpRuntimes: [],
|
||
pageAiQueueLength: 0,
|
||
pageAiQueuedItems: [],
|
||
pageAiClientQueuedPrompts: [],
|
||
pageAiStoppedRunIds: {},
|
||
pageAiAbortController: null,
|
||
pageAiContextScope: 'page',
|
||
pageAiContextPopoverOpen: false,
|
||
pageAiTargetPopoverOpen: false,
|
||
pageAiSelectedTargetId: '',
|
||
pageAiSelectedContextRefs: null,
|
||
pageAiCurrentRunTargetSnapshot: null,
|
||
pageAiAllowedRoots: [],
|
||
pageAiAllowedRootsError: '',
|
||
pageAiAgentId: 'reasonix',
|
||
pageAiAgentPopoverOpen: false,
|
||
pageAiAgentDescriptors: [],
|
||
pageAiAgentDescriptorError: '',
|
||
pageAiAgentDescriptorsLoaded: false,
|
||
pageAiTools: [],
|
||
pageAiToolsError: '',
|
||
pageAiGatewayHealth: null,
|
||
pageAiGatewayHealthError: '',
|
||
pageAiLastToolCall: null,
|
||
pageAiProfiles: [],
|
||
pageAiActiveProfileName: 'mnoteai',
|
||
pageAiProfileError: '',
|
||
pageAiProfileMemory: { memory: '', user: '', soul: '' },
|
||
pageAiProfileMemoryDrafts: { memory: '', user: '', soul: '' },
|
||
pageAiProfileMemoryError: '',
|
||
pageAiSkills: { categories: [], archived: [] },
|
||
pageAiSkillCatalogs: {
|
||
mnote: { categories: [], archived: [] },
|
||
reasonix: { categories: [], archived: [] },
|
||
hermes: { categories: [], archived: [] }
|
||
},
|
||
pageAiSkillPreferences: {},
|
||
pageAiCollapsedSkillGroups: {},
|
||
pageAiSkillQuery: '',
|
||
pageAiSkillError: '',
|
||
pageAiSkillLoadSeq: 0,
|
||
pageAiActiveSkillSource: 'mnote',
|
||
pageAiSessions: [],
|
||
pageAiActiveSessionId: '',
|
||
pageAiBoardWorkerId: 'mnote-page-ai-zcode',
|
||
pageAiBoardWorkflowId: 'builtin-mnote-page-ai-chat',
|
||
pageAiBoardModelOverride: 'zcode-default',
|
||
pageAiBoardRunDetails: {},
|
||
pageAiBoardActiveDetailRunId: '',
|
||
pageAiOpencodeStatus: null,
|
||
pageAiOpencodeStatusError: '',
|
||
pageAiOpencodeChangedFiles: [],
|
||
pageAiOpencodeDiffError: '',
|
||
pageAiOpencodeSessionId: '',
|
||
pageAiOpencodeBaseUrl: 'http://127.0.0.1:4096',
|
||
pageAiOpencodeIframeUrl: '',
|
||
pageAiOpencodeBindingKey: '',
|
||
pageAiOpencodeBindingInFlight: false,
|
||
pageAiOpencodeEventSource: null,
|
||
pageAiOpencodeEventSessionId: '',
|
||
pageAiOpencodeEventTimer: 0,
|
||
pageAiSessionSearchQuery: '',
|
||
pageAiSessionSearchResults: [],
|
||
pageAiSessionSearchTimer: 0,
|
||
pageAiSessionAgentFilter: 'all',
|
||
pageAiSessionStatusFilter: 'all',
|
||
pageAiSelectedSessionIds: {},
|
||
pageAiSessionError: '',
|
||
pageAiRuntimeStatus: null,
|
||
pageAiRuntimeStatusError: '',
|
||
pageAiPermissionRequests: []
|
||
};
|
||
Object.keys(defaults).forEach(function(key) {
|
||
if (typeof state[key] === 'undefined') {
|
||
state[key] = clonePageAiDefaultValue(defaults[key]);
|
||
}
|
||
});
|
||
if (!state.pageAiSkillCatalogs || typeof state.pageAiSkillCatalogs !== 'object') {
|
||
state.pageAiSkillCatalogs = clonePageAiDefaultValue(defaults.pageAiSkillCatalogs);
|
||
}
|
||
if (!state.pageAiProfileMemory || typeof state.pageAiProfileMemory !== 'object') {
|
||
state.pageAiProfileMemory = clonePageAiDefaultValue(defaults.pageAiProfileMemory);
|
||
}
|
||
if (!state.pageAiProfileMemoryDrafts || typeof state.pageAiProfileMemoryDrafts !== 'object') {
|
||
state.pageAiProfileMemoryDrafts = clonePageAiDefaultValue(defaults.pageAiProfileMemoryDrafts);
|
||
}
|
||
if (!state.pageAiSkills || typeof state.pageAiSkills !== 'object') {
|
||
state.pageAiSkills = clonePageAiDefaultValue(defaults.pageAiSkills);
|
||
}
|
||
if (!Array.isArray(state.pageAiClientQueuedPrompts)) {
|
||
state.pageAiClientQueuedPrompts = [];
|
||
}
|
||
return state;
|
||
}
|
||
|
||
ensurePageAiStateFacade(pageUiState);
|
||
|
||
function closestAction(target, selector) {
|
||
var node = target && target.nodeType === Node.TEXT_NODE ? target.parentElement : target;
|
||
return node && typeof node.closest === 'function' ? node.closest(selector) : null;
|
||
}
|
||
|
||
const pageAiMarkdown = createSidebarPageAiMarkdownRuntime({ escapeHtml });
|
||
const textFromUnknown = (...args) => pageAiMarkdown.textFromUnknown(...args);
|
||
const renderPageAiMarkdown = (...args) => pageAiMarkdown.renderPageAiMarkdown(...args);
|
||
let pageAiRenderRuntime = null;
|
||
|
||
function pageAiRenderApi() {
|
||
if (!pageAiRenderRuntime) throw new Error('page_ai_render_runtime_not_ready');
|
||
return pageAiRenderRuntime;
|
||
}
|
||
|
||
function ensurePageAiDrawer(...args) { return pageAiRenderApi().ensurePageAiDrawer(...args); }
|
||
function humanizePageAiResponse(...args) { return pageAiRenderApi().humanizePageAiResponse(...args); }
|
||
function pageAiContextButtonSummary(...args) { return pageAiRenderApi().pageAiContextButtonSummary(...args); }
|
||
function pageAiContextRefDetail(...args) { return pageAiRenderApi().pageAiContextRefDetail(...args); }
|
||
function pageAiContextRefDisabled(...args) { return pageAiRenderApi().pageAiContextRefDisabled(...args); }
|
||
function pageAiContextRefLabel(...args) { return pageAiRenderApi().pageAiContextRefLabel(...args); }
|
||
function pageAiContextScopeLabel(...args) { return pageAiRenderApi().pageAiContextScopeLabel(...args); }
|
||
function pageAiCurrentAgentSelectionLabel(...args) { return pageAiRenderApi().pageAiCurrentAgentSelectionLabel(...args); }
|
||
function pageAiCurrentModelLabel(...args) { return pageAiRenderApi().pageAiCurrentModelLabel(...args); }
|
||
function pageAiFilteredSkillEntries(...args) { return pageAiRenderApi().pageAiFilteredSkillEntries(...args); }
|
||
function pageAiFormatChangedFiles(...args) { return pageAiRenderApi().pageAiFormatChangedFiles(...args); }
|
||
function pageAiMemoryFileLabel(...args) { return pageAiRenderApi().pageAiMemoryFileLabel(...args); }
|
||
function pageAiRenderAgentProfileOption(...args) { return pageAiRenderApi().pageAiRenderAgentProfileOption(...args); }
|
||
function pageAiRunStatusLabel(...args) { return pageAiRenderApi().pageAiRunStatusLabel(...args); }
|
||
function pageAiSuggestions(...args) { return pageAiRenderApi().pageAiSuggestions(...args); }
|
||
function pageAiTargetDetail(...args) { return pageAiRenderApi().pageAiTargetDetail(...args); }
|
||
function pageAiTargetLabel(...args) { return pageAiRenderApi().pageAiTargetLabel(...args); }
|
||
function renderPageAiControls(...args) { return pageAiRenderApi().renderPageAiControls(...args); }
|
||
function renderPageAiConversation(...args) { return pageAiRenderApi().renderPageAiConversation(...args); }
|
||
function renderPageAiProviderButtons(...args) { return pageAiRenderApi().renderPageAiProviderButtons(...args); }
|
||
function renderPageAiSuggestions(...args) { return pageAiRenderApi().renderPageAiSuggestions(...args); }
|
||
|
||
function readLocalEditorBlocks() {
|
||
var editor = document.querySelector('[data-testid="mnote-leptos-tiptap-island-editor-root"] .editor-surface .ProseMirror');
|
||
if (!(editor instanceof HTMLElement)) return [];
|
||
return Array.from(editor.children).filter(function(node) {
|
||
return node instanceof HTMLElement;
|
||
}).map(function(node, index) {
|
||
var tag = String(node.tagName || '').toUpperCase();
|
||
var headingMatch = tag.match(/^H([1-6])$/);
|
||
var type = headingMatch ? 'heading' : 'paragraph';
|
||
var text = searchText(node.textContent || '');
|
||
var id = searchText(node.getAttribute('data-id') || node.id || 'local-block-' + index);
|
||
var props = headingMatch ? { level: Number(headingMatch[1]) } : {};
|
||
return { id: id, type: type, props: props, content: text };
|
||
}).filter(function(block) {
|
||
return block.content || block.type === 'heading';
|
||
});
|
||
}
|
||
|
||
function buildPageAiLocalSubtree(blocks, title) {
|
||
var documentId = currentDocumentId() || 'current-page';
|
||
var rootNodeId = 'page:' + documentId;
|
||
var headingCounters = [0, 0, 0, 0, 0, 0];
|
||
var headingStack = [];
|
||
var nodes = [{
|
||
id: rootNodeId,
|
||
nodeId: rootNodeId,
|
||
nodeType: 'page',
|
||
blockId: null,
|
||
blockType: 'page',
|
||
title: title || '',
|
||
parentNodeId: null,
|
||
headingLevel: null
|
||
}];
|
||
var outline = [];
|
||
var evidence = [];
|
||
blocks.forEach(function(block, index) {
|
||
var level = block.type === 'heading' ? Math.max(1, Math.min(6, Number(block.props && block.props.level || 1))) : null;
|
||
if (level != null) {
|
||
while (headingStack.length && headingStack[headingStack.length - 1].level >= level) headingStack.pop();
|
||
}
|
||
var parentNodeId = headingStack.length ? headingStack[headingStack.length - 1].nodeId : rootNodeId;
|
||
var nodeId = 'local-node:' + String(block.id || index);
|
||
var titleText = block.content || (block.type === 'heading' ? '未命名标题' : '');
|
||
nodes.push({
|
||
id: nodeId,
|
||
nodeId: nodeId,
|
||
nodeType: 'block',
|
||
blockId: block.id,
|
||
blockType: block.type,
|
||
title: titleText,
|
||
parentNodeId: parentNodeId,
|
||
headingLevel: level
|
||
});
|
||
if (level != null) {
|
||
headingCounters[level - 1] += 1;
|
||
for (var i = level; i < headingCounters.length; i += 1) headingCounters[i] = 0;
|
||
var numbering = headingCounters.slice(0, level).filter(function(count) { return count > 0; }).join('.');
|
||
outline.push({
|
||
id: 'local-outline:' + String(block.id || index),
|
||
nodeId: nodeId,
|
||
anchorBlockId: block.id,
|
||
level: level,
|
||
title: titleText,
|
||
numbering: numbering
|
||
});
|
||
headingStack.push({ level: level, nodeId: nodeId });
|
||
}
|
||
if (titleText) {
|
||
evidence.push({
|
||
id: 'local-evidence:' + String(block.id || index),
|
||
nodeId: nodeId,
|
||
anchorBlockId: block.id,
|
||
text: titleText,
|
||
kind: block.type
|
||
});
|
||
}
|
||
});
|
||
return {
|
||
projectionId: 'local-editor-dom:' + documentId,
|
||
rootNode: {
|
||
id: rootNodeId,
|
||
documentId: documentId,
|
||
title: title || '',
|
||
nodeType: 'page'
|
||
},
|
||
subtree: {
|
||
rootNodeId: rootNodeId,
|
||
nodes: nodes
|
||
},
|
||
outline: outline,
|
||
evidence: evidence,
|
||
stats: {
|
||
nodeCount: nodes.length,
|
||
headingCount: outline.length,
|
||
evidenceCount: evidence.length
|
||
},
|
||
source: 'local'
|
||
};
|
||
}
|
||
|
||
function currentPageAiContextSnapshot() {
|
||
var aggregate = currentPageAggregate();
|
||
var body = aggregate.body || {};
|
||
var serverSubtree = aggregate.tree && aggregate.tree.pageSubtree ? aggregate.tree.pageSubtree : null;
|
||
return {
|
||
aggregate: aggregate,
|
||
body: body,
|
||
subtree: serverSubtree,
|
||
pageSubtreeSource: serverSubtree ? 'server' : 'none'
|
||
};
|
||
}
|
||
|
||
function pageAiNormalizeAgentDescriptor(descriptor) {
|
||
if (!descriptor || typeof descriptor !== 'object') return null;
|
||
var agentId = String(descriptor.agentId || descriptor.id || '').trim();
|
||
if (!agentId) return null;
|
||
return Object.assign({}, descriptor, {
|
||
id: agentId,
|
||
agentId: agentId,
|
||
label: String(descriptor.displayName || descriptor.label || agentId).trim() || agentId,
|
||
acpRuntime: String(descriptor.acpRuntime || descriptor.acp_runtime || '').trim() || (agentId === 'reasonix' ? 'reasonix' : 'hermes'),
|
||
canWriteFiles: descriptor.canWriteFiles !== false,
|
||
capabilities: pageAiNormalizeArray(descriptor.capabilities),
|
||
fields: pageAiNormalizeArray(descriptor.fields),
|
||
tools: pageAiNormalizeArray(descriptor.tools),
|
||
capabilityPacks: pageAiNormalizeArray(descriptor.capabilityPacks),
|
||
schema: String(descriptor.schema || PAGE_AI_DESCRIPTOR_SCHEMA).trim()
|
||
});
|
||
}
|
||
|
||
function pageAiAgentRegistry() {
|
||
var descriptors = pageAiNormalizeArray(pageUiState.pageAiAgentDescriptors)
|
||
.map(pageAiNormalizeAgentDescriptor)
|
||
.filter(Boolean);
|
||
return descriptors.length ? descriptors : PAGE_AI_AGENT_FALLBACK_REGISTRY;
|
||
}
|
||
|
||
function pageAiAgentDescriptor(agentId) {
|
||
var normalized = String(agentId || '').trim();
|
||
if (!normalized) normalized = pageAiCurrentAgentId();
|
||
return pageAiAgentRegistry().find(function(agent) { return agent.id === normalized || agent.agentId === normalized; }) || null;
|
||
}
|
||
|
||
function pageAiNormalizeAgentId(agentId) {
|
||
var value = String(agentId || '').trim();
|
||
return pageAiAgentRegistry().some(function(agent) { return agent.id === value; }) ? value : 'reasonix';
|
||
}
|
||
|
||
function pageAiAgentRecord(agentId) {
|
||
var normalized = pageAiNormalizeAgentId(agentId);
|
||
return pageAiAgentRegistry().find(function(agent) { return agent.id === normalized; }) || pageAiAgentRegistry()[1] || PAGE_AI_AGENT_FALLBACK_REGISTRY[1];
|
||
}
|
||
|
||
function pageAiCurrentAgentId() {
|
||
var explicit = String(pageUiState.pageAiAgentId || '').trim();
|
||
if (explicit) return pageAiNormalizeAgentId(explicit);
|
||
var runtime = String(pageUiState.pageAiAcpRuntime || 'reasonix').trim();
|
||
if (runtime === 'hermes') return 'hermes';
|
||
return 'reasonix';
|
||
}
|
||
|
||
const pageAiProfileRuntime = createSidebarPageAiProfileRuntime({
|
||
chatOnlyProfileRegistry: PAGE_AI_CHAT_ONLY_PROFILE_REGISTRY,
|
||
documentRef: document,
|
||
pageAiAgentRecord,
|
||
pageAiCurrentAgentId,
|
||
pageAiNormalizeAgentId,
|
||
pageUiState,
|
||
});
|
||
const pageAiProviderLabel = (...args) => pageAiProfileRuntime.pageAiProviderLabel(...args);
|
||
const pageAiNormalizeArray = (...args) => pageAiProfileRuntime.pageAiNormalizeArray(...args);
|
||
const pageAiDefaultAcpRuntimes = (...args) => pageAiProfileRuntime.pageAiDefaultAcpRuntimes(...args);
|
||
const pageAiNormalizeAcpRuntimes = (...args) => pageAiProfileRuntime.pageAiNormalizeAcpRuntimes(...args);
|
||
const pageAiUnwrapUpstream = (...args) => pageAiProfileRuntime.pageAiUnwrapUpstream(...args);
|
||
const pageAiProfileValue = (...args) => pageAiProfileRuntime.pageAiProfileValue(...args);
|
||
const pageAiCurrentProfile = (...args) => pageAiProfileRuntime.pageAiCurrentProfile(...args);
|
||
const pageAiRunProfile = (...args) => pageAiProfileRuntime.pageAiRunProfile(...args);
|
||
const pageAiMnoteToolModel = (...args) => pageAiProfileRuntime.pageAiMnoteToolModel(...args);
|
||
const pageAiCurrentProfileRecord = (...args) => pageAiProfileRuntime.pageAiCurrentProfileRecord(...args);
|
||
const pageAiChatOnlyProfileSpec = (...args) => pageAiProfileRuntime.pageAiChatOnlyProfileSpec(...args);
|
||
const pageAiDefaultChatOnlyProfileSpec = (...args) => pageAiProfileRuntime.pageAiDefaultChatOnlyProfileSpec(...args);
|
||
const pageAiNormalizeChatOnlyProfileId = (...args) => pageAiProfileRuntime.pageAiNormalizeChatOnlyProfileId(...args);
|
||
const pageAiProfileDisplayLabel = (...args) => pageAiProfileRuntime.pageAiProfileDisplayLabel(...args);
|
||
const pageAiProfileRecordById = (...args) => pageAiProfileRuntime.pageAiProfileRecordById(...args);
|
||
const pageAiSessionAgentFilterValue = (...args) => pageAiProfileRuntime.pageAiSessionAgentFilterValue(...args);
|
||
const pageAiSessionAgentLabel = (...args) => pageAiProfileRuntime.pageAiSessionAgentLabel(...args);
|
||
const pageAiSessionPreviewText = (...args) => pageAiProfileRuntime.pageAiSessionPreviewText(...args);
|
||
const pageAiSessionAgentFilterOptions = (...args) => pageAiProfileRuntime.pageAiSessionAgentFilterOptions(...args);
|
||
const pageAiSessionStatusFilterValue = (...args) => pageAiProfileRuntime.pageAiSessionStatusFilterValue(...args);
|
||
const pageAiSessionStatusLabel = (...args) => pageAiProfileRuntime.pageAiSessionStatusLabel(...args);
|
||
const pageAiSessionStatusFilterOptions = (...args) => pageAiProfileRuntime.pageAiSessionStatusFilterOptions(...args);
|
||
const pageAiFilteredHistoryRows = (...args) => pageAiProfileRuntime.pageAiFilteredHistoryRows(...args);
|
||
const pageAiTimestamp = (...args) => pageAiProfileRuntime.pageAiTimestamp(...args);
|
||
const pageAiUsageSummary = (...args) => pageAiProfileRuntime.pageAiUsageSummary(...args);
|
||
const pageAiPermissionRuntime = createSidebarPageAiPermissionRuntime({
|
||
documentRef: document,
|
||
pageAiPreviewValue,
|
||
pageUiState,
|
||
renderPageAiConversation,
|
||
});
|
||
const pageAiPermissionMessage = (...args) => pageAiPermissionRuntime.pageAiPermissionMessage(...args);
|
||
const pageAiApplyPermissionEvent = (...args) => pageAiPermissionRuntime.pageAiApplyPermissionEvent(...args);
|
||
const pageAiResolvePermission = (...args) => pageAiPermissionRuntime.pageAiResolvePermission(...args);
|
||
const pageAiRestorePermissionRequests = (...args) => pageAiPermissionRuntime.pageAiRestorePermissionRequests(...args);
|
||
const pageAiHidePermissionDialog = (...args) => pageAiPermissionRuntime.pageAiHidePermissionDialog(...args);
|
||
const pageAiShowPermissionDialog = (...args) => pageAiPermissionRuntime.pageAiShowPermissionDialog(...args);
|
||
const pageAiSessionRuntime = createSidebarPageAiSessionRuntime({
|
||
currentDocumentId,
|
||
currentRootUri,
|
||
currentSourceKind,
|
||
documentRef: document,
|
||
pageAiApplyRuntimeState,
|
||
pageAiCurrentAgentId,
|
||
pageAiCurrentProfile,
|
||
pageAiErrorMessage,
|
||
pageAiNormalizeAgentId,
|
||
pageAiNormalizeArray,
|
||
pageAiNormalizeChatOnlyProfileId,
|
||
pageAiPermissionMessage,
|
||
pageAiPreviewValue,
|
||
pageAiRunProfile,
|
||
pageAiSetActiveProfile,
|
||
pageAiTimestamp,
|
||
pageUiState,
|
||
renderPageAiControls,
|
||
renderPageAiConversation,
|
||
resolveWorkspaceId,
|
||
sessionStorageVersion: PAGE_AI_SESSION_STORAGE_VERSION,
|
||
windowRef: window,
|
||
});
|
||
const pageAiStorageKey = (...args) => pageAiSessionRuntime.pageAiStorageKey(...args);
|
||
const pageAiBackendSessionQuery = (...args) => pageAiSessionRuntime.pageAiBackendSessionQuery(...args);
|
||
const pageAiNewSession = (...args) => pageAiSessionRuntime.pageAiNewSession(...args);
|
||
const pageAiNormalizeSessions = (...args) => pageAiSessionRuntime.pageAiNormalizeSessions(...args);
|
||
const pageAiNormalizeBackendSessionRow = (...args) => pageAiSessionRuntime.pageAiNormalizeBackendSessionRow(...args);
|
||
const pageAiMergeSessions = (...args) => pageAiSessionRuntime.pageAiMergeSessions(...args);
|
||
const pageAiSessionStorageLabel = (...args) => pageAiSessionRuntime.pageAiSessionStorageLabel(...args);
|
||
const pageAiLoadSessions = (...args) => pageAiSessionRuntime.pageAiLoadSessions(...args);
|
||
const pageAiLoadBackendSessions = (...args) => pageAiSessionRuntime.pageAiLoadBackendSessions(...args);
|
||
const pageAiMessageFromRuntimeEvent = (...args) => pageAiSessionRuntime.pageAiMessageFromRuntimeEvent(...args);
|
||
const pageAiApplyBackendSessionDetail = (...args) => pageAiSessionRuntime.pageAiApplyBackendSessionDetail(...args);
|
||
const pageAiLoadBackendSessionDetail = (...args) => pageAiSessionRuntime.pageAiLoadBackendSessionDetail(...args);
|
||
const pageAiSearchBackendSessions = (...args) => pageAiSessionRuntime.pageAiSearchBackendSessions(...args);
|
||
const pageAiPersistSessions = (...args) => pageAiSessionRuntime.pageAiPersistSessions(...args);
|
||
const pageAiEnsureHermesSession = (...args) => pageAiSessionRuntime.pageAiEnsureHermesSession(...args);
|
||
const pageAiRestoreHermesSession = (...args) => pageAiSessionRuntime.pageAiRestoreHermesSession(...args);
|
||
const pageAiCurrentSession = (...args) => pageAiSessionRuntime.pageAiCurrentSession(...args);
|
||
const pageAiSyncCurrentSessionMessages = (...args) => pageAiSessionRuntime.pageAiSyncCurrentSessionMessages(...args);
|
||
const pageAiSetActiveSession = (...args) => pageAiSessionRuntime.pageAiSetActiveSession(...args);
|
||
const pageAiStartNewSession = (...args) => pageAiSessionRuntime.pageAiStartNewSession(...args);
|
||
const pageAiRenameBackendSession = (...args) => pageAiSessionRuntime.pageAiRenameBackendSession(...args);
|
||
const pageAiExportBackendSession = (...args) => pageAiSessionRuntime.pageAiExportBackendSession(...args);
|
||
const pageAiDeleteBackendSession = (...args) => pageAiSessionRuntime.pageAiDeleteBackendSession(...args);
|
||
const pageAiDeleteSelectedBackendSessions = (...args) => pageAiSessionRuntime.pageAiDeleteSelectedBackendSessions(...args);
|
||
const pageAiResumeBackendSession = (...args) => pageAiSessionRuntime.pageAiResumeBackendSession(...args);
|
||
const pageAiSessionSelected = (...args) => pageAiSessionRuntime.pageAiSessionSelected(...args);
|
||
const pageAiToggleSessionSelection = (...args) => pageAiSessionRuntime.pageAiToggleSessionSelection(...args);
|
||
const pageAiClearSessionSelection = (...args) => pageAiSessionRuntime.pageAiClearSessionSelection(...args);
|
||
const pageAiSelectedSessionCount = (...args) => pageAiSessionRuntime.pageAiSelectedSessionCount(...args);
|
||
const pageAiCheckActiveRun = (...args) => pageAiSessionRuntime.pageAiCheckActiveRun(...args);
|
||
const pageAiSkillRuntime = createSidebarPageAiSkillRuntime({
|
||
pageAiCurrentAgentId,
|
||
pageAiCurrentProfile,
|
||
pageAiLoadSkills,
|
||
pageAiNormalizeArray,
|
||
pageAiPersistAiPreference,
|
||
pageAiPersistRawAiPreference,
|
||
pageAiProfileValue,
|
||
pageUiState,
|
||
renderPageAiControls,
|
||
});
|
||
const pageAiSkillSourceOptions = (...args) => pageAiSkillRuntime.pageAiSkillSourceOptions(...args);
|
||
const pageAiDefaultSkillSource = (...args) => pageAiSkillRuntime.pageAiDefaultSkillSource(...args);
|
||
const pageAiNormalizeSkillSource = (...args) => pageAiSkillRuntime.pageAiNormalizeSkillSource(...args);
|
||
const pageAiCurrentSkillSource = (...args) => pageAiSkillRuntime.pageAiCurrentSkillSource(...args);
|
||
const pageAiSetSkillSource = (...args) => pageAiSkillRuntime.pageAiSetSkillSource(...args);
|
||
const pageAiSkillSourceParts = (...args) => pageAiSkillRuntime.pageAiSkillSourceParts(...args);
|
||
const pageAiCurrentSkillSourceLabel = (...args) => pageAiSkillRuntime.pageAiCurrentSkillSourceLabel(...args);
|
||
const pageAiSkillOriginLabel = (...args) => pageAiSkillRuntime.pageAiSkillOriginLabel(...args);
|
||
const pageAiSkillPreferenceKey = (...args) => pageAiSkillRuntime.pageAiSkillPreferenceKey(...args);
|
||
const pageAiSkillPreferenceTable = (...args) => pageAiSkillRuntime.pageAiSkillPreferenceTable(...args);
|
||
const pageAiHermesHideBuiltinPreferenceKey = (...args) => pageAiSkillRuntime.pageAiHermesHideBuiltinPreferenceKey(...args);
|
||
const pageAiHideHermesBuiltinSkills = (...args) => pageAiSkillRuntime.pageAiHideHermesBuiltinSkills(...args);
|
||
const pageAiReasonixMemoryEnabled = (...args) => pageAiSkillRuntime.pageAiReasonixMemoryEnabled(...args);
|
||
const pageAiSetReasonixMemoryEnabled = (...args) => pageAiSkillRuntime.pageAiSetReasonixMemoryEnabled(...args);
|
||
const pageAiSetHideHermesBuiltinSkills = (...args) => pageAiSkillRuntime.pageAiSetHideHermesBuiltinSkills(...args);
|
||
const pageAiSkillIsBuiltin = (...args) => pageAiSkillRuntime.pageAiSkillIsBuiltin(...args);
|
||
const pageAiToggleableSkillEntries = (...args) => pageAiSkillRuntime.pageAiToggleableSkillEntries(...args);
|
||
const pageAiAllSkillEntries = (...args) => pageAiSkillRuntime.pageAiAllSkillEntries(...args);
|
||
const pageAiSkillGroupCollapsed = (...args) => pageAiSkillRuntime.pageAiSkillGroupCollapsed(...args);
|
||
const pageAiToggleSkillGroup = (...args) => pageAiSkillRuntime.pageAiToggleSkillGroup(...args);
|
||
const pageAiSetSkillPreference = (...args) => pageAiSkillRuntime.pageAiSetSkillPreference(...args);
|
||
const pageAiSkillEnabled = (...args) => pageAiSkillRuntime.pageAiSkillEnabled(...args);
|
||
|
||
function pageAiEnsureContextRefState() {
|
||
if (!pageUiState.pageAiSelectedContextRefs || typeof pageUiState.pageAiSelectedContextRefs !== 'object') {
|
||
pageUiState.pageAiSelectedContextRefs = {
|
||
current_page: true,
|
||
selection: false,
|
||
active_editor: true,
|
||
file: false,
|
||
folder: false,
|
||
changed_files: false
|
||
};
|
||
}
|
||
return pageUiState.pageAiSelectedContextRefs;
|
||
}
|
||
|
||
const pageAiTargetRuntime = createSidebarPageAiTargetRuntime({
|
||
currentDocumentId,
|
||
currentPageOptions,
|
||
currentRootUri,
|
||
currentSourceKind,
|
||
documentRef: document,
|
||
escapeHtml,
|
||
pageAiEnsureContextRefState,
|
||
pageAiNormalizeArray,
|
||
pageUiState,
|
||
resolveWorkspaceId,
|
||
searchText,
|
||
});
|
||
const currentPageAiOpenEditorsSnapshot = (...args) => pageAiTargetRuntime.currentPageAiOpenEditorsSnapshot(...args);
|
||
const currentPageAiEditorTarget = (...args) => pageAiTargetRuntime.currentPageAiEditorTarget(...args);
|
||
const currentPageAiPageEditorTarget = (...args) => pageAiTargetRuntime.currentPageAiPageEditorTarget(...args);
|
||
const currentPageAiScopedEditorTarget = (...args) => pageAiTargetRuntime.currentPageAiScopedEditorTarget(...args);
|
||
const pageAiCloneJson = (...args) => pageAiTargetRuntime.pageAiCloneJson(...args);
|
||
const pageAiEditorTargetCandidates = (...args) => pageAiTargetRuntime.pageAiEditorTargetCandidates(...args);
|
||
const pageAiFallbackEditorTarget = (...args) => pageAiTargetRuntime.pageAiFallbackEditorTarget(...args);
|
||
const pageAiResourceKindForTarget = (...args) => pageAiTargetRuntime.pageAiResourceKindForTarget(...args);
|
||
const pageAiTargetFromOpenEditor = (...args) => pageAiTargetRuntime.pageAiTargetFromOpenEditor(...args);
|
||
const pageAiTargetId = (...args) => pageAiTargetRuntime.pageAiTargetId(...args);
|
||
const localMarkdownRelativePathFromPageAiDocumentId = (...args) => pageAiTargetRuntime.localMarkdownRelativePathFromPageAiDocumentId(...args);
|
||
const localMarkdownDocumentIdFromPageAiRelativePath = (...args) => pageAiTargetRuntime.localMarkdownDocumentIdFromPageAiRelativePath(...args);
|
||
const pageAiWorkspacePathForDocument = (...args) => pageAiTargetRuntime.pageAiWorkspacePathForDocument(...args);
|
||
const pageAiWorkspacePathForTarget = (...args) => pageAiTargetRuntime.pageAiWorkspacePathForTarget(...args);
|
||
const pageAiBuildAllowedRoots = (...args) => pageAiTargetRuntime.pageAiBuildAllowedRoots(...args);
|
||
const pageAiBuildContextRefs = (...args) => pageAiTargetRuntime.pageAiBuildContextRefs(...args);
|
||
const pageAiBuildRunTargetSnapshot = (...args) => pageAiTargetRuntime.pageAiBuildRunTargetSnapshot(...args);
|
||
const pageAiPageContextForRefs = (...args) => pageAiTargetRuntime.pageAiPageContextForRefs(...args);
|
||
const pageAiSetRunTargetSnapshot = (...args) => pageAiTargetRuntime.pageAiSetRunTargetSnapshot(...args);
|
||
const assertPageAiTargetInCurrentWorkspace = (...args) => pageAiTargetRuntime.assertPageAiTargetInCurrentWorkspace(...args);
|
||
const pageAiBuildAgentTargetPackage = (...args) => pageAiTargetRuntime.pageAiBuildAgentTargetPackage(...args);
|
||
const pageAiBlockingDirtyState = (...args) => pageAiTargetRuntime.pageAiBlockingDirtyState(...args);
|
||
const fetchPageAiTargetBufferState = (...args) => pageAiTargetRuntime.fetchPageAiTargetBufferState(...args);
|
||
const assertPageAiTargetWritable = (...args) => pageAiTargetRuntime.assertPageAiTargetWritable(...args);
|
||
const currentPageAiSelectedText = (...args) => pageAiTargetRuntime.currentPageAiSelectedText(...args);
|
||
const pageAiProjectionBlocks = (...args) => pageAiTargetRuntime.pageAiProjectionBlocks(...args);
|
||
const pageAiBlockText = (...args) => pageAiTargetRuntime.pageAiBlockText(...args);
|
||
const pageAiSelectedBlockIdsFromSelection = (...args) => pageAiTargetRuntime.pageAiSelectedBlockIdsFromSelection(...args);
|
||
const pageAiBlocksToPageXml = (...args) => pageAiTargetRuntime.pageAiBlocksToPageXml(...args);
|
||
const buildPageAiContext = (...args) => pageAiTargetRuntime.buildPageAiContext(...args);
|
||
const pageAiScopedPageContext = (...args) => pageAiTargetRuntime.pageAiScopedPageContext(...args);
|
||
pageAiRenderRuntime = createSidebarPageAiRenderRuntime({
|
||
contextRefRegistry: PAGE_AI_CONTEXT_REF_REGISTRY,
|
||
cssEscape,
|
||
currentDocumentId,
|
||
currentPageAiEditorTarget,
|
||
currentPageAiSelectedText,
|
||
documentRef: document,
|
||
escapeHtml,
|
||
localMarkdownRelativePathFromPageAiDocumentId,
|
||
pageAiAgentDescriptor,
|
||
pageAiAgentRecord,
|
||
pageAiAllSkillEntries,
|
||
pageAiBuildAllowedRoots,
|
||
pageAiChatOnlyProfileEntries,
|
||
pageAiChatOnlyProfileSpec,
|
||
pageAiCurrentAgentId,
|
||
pageAiCurrentProfile,
|
||
pageAiCurrentProfileRecord,
|
||
pageAiCurrentSession,
|
||
pageAiCurrentSkillSource,
|
||
pageAiCurrentSkillSourceLabel,
|
||
pageAiDefaultChatOnlyProfileSpec,
|
||
pageAiDescriptorFieldOptions,
|
||
pageAiDescriptorFieldValue,
|
||
pageAiEditorTargetCandidates,
|
||
pageAiEnsureContextRefState,
|
||
pageAiFilteredHistoryRows,
|
||
pageAiHermesProfileEntries,
|
||
pageAiHermesSettingsUrl,
|
||
pageAiHideHermesBuiltinSkills,
|
||
pageAiMnoteToolModel,
|
||
pageAiNormalizeAcpRuntimes,
|
||
pageAiNormalizeArray,
|
||
pageAiNormalizeSkillSource,
|
||
pageAiProfileDisplayLabel,
|
||
pageAiProfileValue,
|
||
pageAiProviderLabel,
|
||
pageAiReasonixMemoryEnabled,
|
||
pageAiRunProfile,
|
||
pageAiSessionAgentFilterOptions,
|
||
pageAiSessionAgentFilterValue,
|
||
pageAiSessionAgentLabel,
|
||
pageAiSessionPreviewText,
|
||
pageAiSessionStatusFilterOptions,
|
||
pageAiSessionStatusFilterValue,
|
||
pageAiSessionStatusLabel,
|
||
pageAiSessionStorageLabel,
|
||
pageAiSessionSelected,
|
||
pageAiSelectedSessionCount,
|
||
pageAiSkillEnabled,
|
||
pageAiSkillGroupCollapsed,
|
||
pageAiSkillListEntries,
|
||
pageAiSkillOriginLabel,
|
||
pageAiSkillSourceOptions,
|
||
pageAiSkillSourceParts,
|
||
pageAiToggleableSkillEntries,
|
||
pageAiUsageSummary,
|
||
pageAiBoardFirstEnabled,
|
||
pageAiBoardWorkerOptions,
|
||
pageAiBoardWorkflowOptions,
|
||
pageAiBoardSelectedWorkerId,
|
||
pageAiBoardSelectedWorkflowId,
|
||
pageAiBoardWorkerLabel,
|
||
pageAiBoardWorkflowLabel,
|
||
pageAiBoardModelOptions,
|
||
pageAiBoardSelectedModelOverride,
|
||
pageAiBoardModelLabel,
|
||
pageUiState,
|
||
renderPageAiMarkdown,
|
||
searchText,
|
||
});
|
||
|
||
function pageAiSetAgentId(agentId) {
|
||
var next = pageAiNormalizeAgentId(agentId);
|
||
var record = pageAiAgentRecord(next);
|
||
pageUiState.pageAiAgentId = next;
|
||
pageUiState.pageAiAcpRuntime = record.acpRuntime || 'reasonix';
|
||
if (record.tools) pageUiState.pageAiTools = record.tools;
|
||
pageUiState.pageAiAgentPopoverOpen = next === 'hermes';
|
||
if (next === 'hermes') pageAiSetActiveProfile(pageAiCurrentProfile());
|
||
if (next === 'chat_only') pageAiSetActiveProfile(pageAiNormalizeChatOnlyProfileId(pageAiCurrentProfile()));
|
||
document.documentElement.setAttribute('data-mnote-page-ai-agent-id', next);
|
||
pageAiPersistAiPreference('default_agent_id', next);
|
||
void pageAiLoadSkills();
|
||
renderPageAiProviderButtons();
|
||
renderPageAiControls();
|
||
}
|
||
|
||
function pageAiToggleContextRef(kind) {
|
||
var normalized = String(kind || '').trim();
|
||
if (!PAGE_AI_CONTEXT_REF_REGISTRY.some(function(ref) { return ref.id === normalized; })) return;
|
||
var selected = pageAiEnsureContextRefState();
|
||
selected[normalized] = !selected[normalized];
|
||
if (!Object.keys(selected).some(function(key) { return selected[key]; })) selected.current_page = true;
|
||
pageAiPersistAiPreference('context_refs.default_selected', selected);
|
||
renderPageAiControls();
|
||
}
|
||
|
||
function pageAiSetContextPopoverOpen(open) {
|
||
pageUiState.pageAiContextPopoverOpen = Boolean(open);
|
||
if (open) {
|
||
pageUiState.pageAiAgentPopoverOpen = false;
|
||
pageUiState.pageAiTargetPopoverOpen = false;
|
||
}
|
||
renderPageAiControls();
|
||
}
|
||
|
||
function pageAiSetAgentPopoverOpen(open) {
|
||
pageUiState.pageAiAgentPopoverOpen = Boolean(open);
|
||
if (open) {
|
||
pageUiState.pageAiContextPopoverOpen = false;
|
||
pageUiState.pageAiTargetPopoverOpen = false;
|
||
}
|
||
renderPageAiControls();
|
||
}
|
||
|
||
function pageAiSetTargetPopoverOpen(open) {
|
||
pageUiState.pageAiTargetPopoverOpen = Boolean(open);
|
||
if (open) {
|
||
pageUiState.pageAiAgentPopoverOpen = false;
|
||
pageUiState.pageAiContextPopoverOpen = false;
|
||
}
|
||
renderPageAiControls();
|
||
}
|
||
|
||
function pageAiSelectTarget(targetId) {
|
||
var normalized = String(targetId || '').trim();
|
||
var candidates = pageAiEditorTargetCandidates();
|
||
var target = candidates.find(function(candidate) { return candidate.targetId === normalized; }) || null;
|
||
if (!target) return;
|
||
pageUiState.pageAiSelectedTargetId = target.targetId;
|
||
pageUiState.pageAiTargetPopoverOpen = false;
|
||
renderPageAiControls();
|
||
}
|
||
|
||
|
||
function pageAiPersistAiPreference(key, value) {
|
||
pageAiPersistRawAiPreference('ai.common.' + key, value);
|
||
}
|
||
|
||
function pageAiPersistRawAiPreference(key, value) {
|
||
try {
|
||
var workspaceId = resolveWorkspaceId(document.body);
|
||
var body = {
|
||
workspaceId: workspaceId,
|
||
sourceKind: currentSourceKind(),
|
||
rootUri: currentRootUri(),
|
||
documentId: currentDocumentId(),
|
||
updates: {}
|
||
};
|
||
body.updates[key] = value;
|
||
void fetch('/api/ui/preferences', {
|
||
method: 'PUT',
|
||
headers: { 'content-type': 'application/json', accept: 'application/json' },
|
||
body: JSON.stringify(body)
|
||
}).catch(function() {});
|
||
} catch (_) {}
|
||
}
|
||
|
||
function pageAiDescriptorPreferenceValues() {
|
||
return pageUiState.pageAiSkillPreferences && typeof pageUiState.pageAiSkillPreferences === 'object'
|
||
? pageUiState.pageAiSkillPreferences
|
||
: {};
|
||
}
|
||
|
||
function pageAiDescriptorFieldValue(field) {
|
||
var key = String(field && field.key || '').trim();
|
||
var preferences = pageAiDescriptorPreferenceValues();
|
||
if (key && Object.prototype.hasOwnProperty.call(preferences, key)) return preferences[key];
|
||
return field && Object.prototype.hasOwnProperty.call(field, 'default') ? field.default : '';
|
||
}
|
||
|
||
function pageAiDescriptorFieldOptions(field) {
|
||
var source = String(field && field.optionsSource || '').trim();
|
||
if (source === 'hermes_profiles') {
|
||
return pageAiHermesProfileEntries().map(function(profile) {
|
||
var value = pageAiProfileValue(profile);
|
||
return {
|
||
value: value,
|
||
label: pageAiProfileDisplayLabel(profile, value)
|
||
};
|
||
}).filter(function(option) { return option.value; });
|
||
}
|
||
if (source === 'chat_only_profiles') {
|
||
return pageAiChatOnlyProfileEntries().map(function(profile) {
|
||
var value = pageAiProfileValue(profile);
|
||
return {
|
||
value: value,
|
||
label: profile.menuLabel || pageAiProfileDisplayLabel(profile, value)
|
||
};
|
||
}).filter(function(option) { return option.value; });
|
||
}
|
||
return pageAiNormalizeArray(field && field.options).map(function(option) {
|
||
if (option && typeof option === 'object') {
|
||
var value = String(option.value || option.id || option.key || '').trim();
|
||
return {
|
||
value: value,
|
||
label: String(option.label || option.title || value).trim() || value
|
||
};
|
||
}
|
||
var value = String(option || '').trim();
|
||
return { value: value, label: value };
|
||
}).filter(function(option) { return option.value; });
|
||
}
|
||
|
||
function pageAiSetDescriptorFieldValue(key, value) {
|
||
var normalizedKey = String(key || '').trim();
|
||
if (!normalizedKey) return;
|
||
pageUiState.pageAiSkillPreferences = Object.assign({}, pageAiDescriptorPreferenceValues(), {
|
||
[normalizedKey]: value
|
||
});
|
||
pageAiPersistRawAiPreference(normalizedKey, value);
|
||
if (normalizedKey === 'ai.agent.hermes.profile_id') {
|
||
pageAiSetActiveProfile(value);
|
||
void pageAiLoadAgentDescriptors();
|
||
void pageAiLoadSkills();
|
||
void pageAiLoadTools();
|
||
}
|
||
if (normalizedKey === 'ai.agent.chat_only.model_id' && pageAiCurrentAgentId() === 'chat_only') {
|
||
pageAiSetActiveProfile(pageAiNormalizeChatOnlyProfileId(value));
|
||
}
|
||
renderPageAiControls();
|
||
}
|
||
|
||
function pageAiReasonixPreference(key, fallback) {
|
||
var preferences = pageAiDescriptorPreferenceValues();
|
||
var value = preferences[key];
|
||
if (value == null || String(value).trim() === '') return fallback;
|
||
return String(value).trim();
|
||
}
|
||
|
||
function pageAiReasonixControlState() {
|
||
return {
|
||
modelId: pageAiReasonixPreference('ai.agent.reasonix.model_id', 'deepseek-flash'),
|
||
approvalMode: pageAiReasonixPreference('ai.agent.reasonix.approval_mode', 'ask'),
|
||
planMode: pageAiReasonixPreference('ai.agent.reasonix.plan_mode', 'manual')
|
||
};
|
||
}
|
||
|
||
function pageAiApplyReasonixPlanMode(prompt) {
|
||
if (pageAiCurrentAgentId() !== 'reasonix') return prompt;
|
||
var control = pageAiReasonixControlState();
|
||
if (control.planMode !== 'auto') return prompt;
|
||
return '请按 Reasonix 自动计划模式处理:复杂或会写文件的任务先给出简短计划,并等待我批准后再执行。\n\n' + prompt;
|
||
}
|
||
|
||
function pageAiBoardFirstEnabled() {
|
||
try {
|
||
return window.localStorage ? window.localStorage.getItem('mnote.page_ai.legacy_provider_mode') !== '1' : true;
|
||
} catch (_error) {
|
||
return true;
|
||
}
|
||
}
|
||
|
||
function pageAiOpencodeHostEnabled() {
|
||
try {
|
||
return window.localStorage ? window.localStorage.getItem('mnote.page_ai.opencode_host') !== '0' : true;
|
||
} catch (_error) {
|
||
return true;
|
||
}
|
||
}
|
||
|
||
function pageAiOpencodeStatusLabel(status) {
|
||
var node = status && typeof status === 'object' ? status : {};
|
||
var state = String(node.status || node.state || node.runtimeStatus || '').trim();
|
||
var ok = node.ok === true || node.ready === true || state === 'ready' || state === 'running' || state === 'ok';
|
||
if (ok) return state ? '可用 · ' + state : '可用';
|
||
if (state) return state;
|
||
return '未知';
|
||
}
|
||
|
||
function pageAiOpencodeSessionIdFromStatus(status) {
|
||
var node = status && typeof status === 'object' ? status : {};
|
||
var session = node.session && typeof node.session === 'object' ? node.session : {};
|
||
return String(node.sessionId || node.activeSessionId || node.currentSessionId || session.id || '').trim();
|
||
}
|
||
|
||
function pageAiOpencodeData(payload) {
|
||
return payload && payload.data && typeof payload.data === 'object' ? payload.data : payload;
|
||
}
|
||
|
||
function pageAiOpencodeSessionArray(payload) {
|
||
var data = pageAiOpencodeData(payload);
|
||
return pageAiNormalizeArray(data && data.data)
|
||
.concat(pageAiNormalizeArray(data && data.sessions))
|
||
.concat(Array.isArray(data) ? data : []);
|
||
}
|
||
|
||
function pageAiOpencodeCollectChangedFileCandidates(value, output, depth) {
|
||
if (value == null || depth > 5) return;
|
||
if (typeof value === 'string') {
|
||
if (/\.(md|markdown|txt|json|ya?ml|toml|rs|js|ts|tsx|jsx|css|html)$/i.test(value) || value.indexOf('/') !== -1) {
|
||
output.push(value);
|
||
}
|
||
return;
|
||
}
|
||
if (Array.isArray(value)) {
|
||
value.forEach(function(item) { pageAiOpencodeCollectChangedFileCandidates(item, output, depth + 1); });
|
||
return;
|
||
}
|
||
if (typeof value !== 'object') return;
|
||
var directPath = value.path || value.file || value.filename || value.relativePath || value.relative_path || value.absolutePath || value.absolute_path;
|
||
if (directPath) output.push(value);
|
||
['changedFiles', 'changed_files', 'files', 'diff', 'diffs', 'changes', 'changed', 'edited', 'created', 'deleted', 'data', 'properties'].forEach(function(key) {
|
||
if (Object.prototype.hasOwnProperty.call(value, key)) pageAiOpencodeCollectChangedFileCandidates(value[key], output, depth + 1);
|
||
});
|
||
}
|
||
|
||
function pageAiOpencodeNormalizeChangedFiles(payload) {
|
||
var source = [];
|
||
pageAiOpencodeCollectChangedFileCandidates(payload, source, 0);
|
||
var seen = {};
|
||
return source.map(function(item) {
|
||
var path = typeof item === 'string'
|
||
? item
|
||
: String(item && (item.path || item.file || item.filename || item.relativePath || item.relative_path || item.absolutePath || item.absolute_path) || '').trim();
|
||
var status = typeof item === 'object' && item ? String(item.status || item.change || item.kind || item.type || '').trim() : '';
|
||
return { path: path, status: status };
|
||
}).filter(function(item) {
|
||
if (!item.path || seen[item.path]) return false;
|
||
seen[item.path] = true;
|
||
return true;
|
||
});
|
||
}
|
||
|
||
function pageAiOpencodeCurrentPageLabel() {
|
||
var aggregate = currentPageAggregate();
|
||
var meta = aggregate && aggregate.meta && typeof aggregate.meta === 'object' ? aggregate.meta : {};
|
||
var target = currentPageAiEditorTarget() || {};
|
||
return String(meta.title || target.title || document.title || currentDocumentId() || '当前页').trim();
|
||
}
|
||
|
||
function pageAiOpencodePrimaryPath() {
|
||
var target = currentPageAiEditorTarget() || {};
|
||
var workspacePath = target && target.workspacePath && typeof target.workspacePath === 'object' ? target.workspacePath : {};
|
||
return String(workspacePath.relativePath || workspacePath.path || target.relativePath || localMarkdownRelativePathFromPageAiDocumentId(currentDocumentId()) || '').trim();
|
||
}
|
||
|
||
function pageAiOpencodeWritableLabel() {
|
||
var roots = pageAiNormalizeArray(pageUiState.pageAiAllowedRoots && pageUiState.pageAiAllowedRoots.length ? pageUiState.pageAiAllowedRoots : pageAiBuildAllowedRoots());
|
||
if (!roots.length) return '授权待同步';
|
||
var writable = roots.some(function(root) {
|
||
return /write|writable|rw/i.test(String(root && (root.permission || root.access || root.mode || '') || '')) || root && root.writable === true;
|
||
});
|
||
return writable ? '可写' : '只读';
|
||
}
|
||
|
||
function pageAiFilePathFromRootUri(rootUri, relativePath) {
|
||
var root = String(rootUri || '').trim();
|
||
var relative = String(relativePath || '').trim().replace(/\\/g, '/').replace(/^\/+/, '');
|
||
if (!root || !relative || !root.startsWith('file://')) return '';
|
||
try {
|
||
var pathname = new URL(root).pathname || '';
|
||
return decodeURIComponent(pathname).replace(/\/+$/, '') + '/' + relative;
|
||
} catch (_error) {
|
||
return '';
|
||
}
|
||
}
|
||
|
||
function pageAiBuildOpencodeContextPayload() {
|
||
var selectedText = currentPageAiSelectedText();
|
||
var target = currentPageAiEditorTarget() || {};
|
||
var workspacePath = target && target.workspacePath && typeof target.workspacePath === 'object' ? target.workspacePath : {};
|
||
var pagePath = pageAiOpencodePrimaryPath();
|
||
var rootUri = String(workspacePath.rootUri || target.rootUri || currentRootUri() || '').trim();
|
||
return {
|
||
pageTitle: pageAiOpencodeCurrentPageLabel(),
|
||
pagePath: pagePath,
|
||
pageAbsolutePath: pageAiFilePathFromRootUri(rootUri, pagePath),
|
||
rootUri: rootUri,
|
||
selection: selectedText,
|
||
allowedRoots: pageAiNormalizeArray(pageUiState.pageAiAllowedRoots && pageUiState.pageAiAllowedRoots.length ? pageUiState.pageAiAllowedRoots : pageAiBuildAllowedRoots()),
|
||
pageId: String(currentDocumentId() || target.documentId || target.id || ''),
|
||
workspaceId: String(target.workspaceId || ''),
|
||
};
|
||
}
|
||
|
||
function pageAiOpencodeContextTextForInsert() {
|
||
var context = pageAiBuildOpencodeContextPayload();
|
||
var lines = ['MNote 当前页上下文'];
|
||
if (context.pageTitle) lines.push('标题:' + context.pageTitle);
|
||
if (context.pagePath) lines.push('文件:' + context.pagePath);
|
||
if (context.selection) lines.push('选区:\n' + context.selection);
|
||
var roots = pageAiNormalizeArray(context.allowedRoots).map(function(root) {
|
||
return String(root && (root.path || root.root || root.uri || root.label) || '').trim();
|
||
}).filter(Boolean).slice(0, 4);
|
||
if (roots.length) lines.push('允许目录:' + roots.join(', '));
|
||
return lines.join('\n');
|
||
}
|
||
|
||
function pageAiPostOpencodeBridgeMessage(type, payload) {
|
||
var iframe = document.querySelector('[data-page-ai-opencode-iframe]');
|
||
if (!(iframe instanceof HTMLIFrameElement) || !iframe.contentWindow) return false;
|
||
iframe.contentWindow.postMessage({ source: 'mnote-page-ai', target: 'opencode-webui', type: type, payload: payload || {} }, window.location.origin);
|
||
return true;
|
||
}
|
||
|
||
function pageAiInstallOpencodeBridge() {
|
||
if (pageUiState.pageAiOpencodeBridgeInstalled) return;
|
||
pageUiState.pageAiOpencodeBridgeInstalled = true;
|
||
window.addEventListener('message', function(event) {
|
||
if (event.origin !== window.location.origin) return;
|
||
var message = event.data && typeof event.data === 'object' ? event.data : null;
|
||
if (!message || message.source !== 'mnote-opencode-bridge') return;
|
||
var payload = message.payload && typeof message.payload === 'object' ? message.payload : {};
|
||
if (message.type === 'session-ready') {
|
||
pageUiState.pageAiOpencodeBridgeReady = true;
|
||
pageAiRenderOpencodeHostChrome();
|
||
return;
|
||
}
|
||
if (message.type === 'open-file') {
|
||
pageAiOpenOpencodeChangedFile(payload.path || payload.href || payload.text || '');
|
||
return;
|
||
}
|
||
if (message.type === 'refresh-file') {
|
||
pageAiRefreshOpencodeCurrentPage('page-ai-opencode-bridge');
|
||
}
|
||
});
|
||
}
|
||
|
||
function pageAiOpencodeMessageInfo(message) {
|
||
return message && message.info && typeof message.info === 'object' ? message.info : message || {};
|
||
}
|
||
|
||
function pageAiOpencodePartText(part) {
|
||
if (!part || typeof part !== 'object') return '';
|
||
if (part.text) return String(part.text);
|
||
if (part.content) return String(part.content);
|
||
if (part.summary) return String(part.summary);
|
||
if (part.type === 'patch') return '修改文件:' + pageAiNormalizeArray(part.files).join(', ');
|
||
if (part.type === 'file') return String(part.filename || part.url || 'file');
|
||
if (part.type === 'tool') return String(part.tool || part.name || 'tool');
|
||
return '';
|
||
}
|
||
|
||
function pageAiOpencodeIsContextEnvelope(message, parts) {
|
||
var info = pageAiOpencodeMessageInfo(message);
|
||
var text = String(info.text || '').trim() || pageAiNormalizeArray(parts).map(pageAiOpencodePartText).join('\n').trim();
|
||
return text.indexOf('MNote context envelope') === 0;
|
||
}
|
||
|
||
function pageAiOpencodeNormalizeMessages(payload) {
|
||
var data = pageAiOpencodeData(payload);
|
||
var items = pageAiNormalizeArray(data && data.data)
|
||
.concat(pageAiNormalizeArray(data && data.messages))
|
||
.concat(Array.isArray(data) ? data : []);
|
||
return items.map(function(item) {
|
||
var info = pageAiOpencodeMessageInfo(item);
|
||
var parts = pageAiNormalizeArray(item && item.parts).concat(pageAiNormalizeArray(info.content));
|
||
var type = String(info.role || info.type || item && item.type || '').toLowerCase();
|
||
var role = type.indexOf('assistant') !== -1 ? 'assistant' : type.indexOf('user') !== -1 ? 'user' : type || 'event';
|
||
return {
|
||
id: String(info.id || item && item.id || Math.random()).trim(),
|
||
role: role,
|
||
type: type,
|
||
agent: String(info.agent || ''),
|
||
model: info.model || null,
|
||
finish: String(info.finish || ''),
|
||
error: info.error || null,
|
||
cost: info.cost,
|
||
tokens: info.tokens || null,
|
||
parts: parts,
|
||
hiddenContext: pageAiOpencodeIsContextEnvelope(item, parts),
|
||
text: String(info.text || '').trim(),
|
||
};
|
||
}).filter(function(message) {
|
||
return !message.hiddenContext && (message.text || message.parts.length || message.error || message.type);
|
||
});
|
||
}
|
||
|
||
function pageAiOpencodeNormalizePermissions(payload) {
|
||
var data = pageAiOpencodeData(payload);
|
||
return pageAiNormalizeArray(data && data.data)
|
||
.concat(pageAiNormalizeArray(data && data.permissions))
|
||
.concat(Array.isArray(data) ? data : []);
|
||
}
|
||
|
||
function pageAiOpencodeNormalizeTodo(payload) {
|
||
var data = pageAiOpencodeData(payload);
|
||
return pageAiNormalizeArray(data && data.data).concat(pageAiNormalizeArray(data && data.todo)).concat(Array.isArray(data) ? data : []);
|
||
}
|
||
|
||
function pageAiOpencodeCompact(value, max) {
|
||
var text = typeof value === 'string' ? value : JSON.stringify(value || {});
|
||
max = max || 260;
|
||
return text.length > max ? text.slice(0, max - 1) + '…' : text;
|
||
}
|
||
|
||
function pageAiRenderOpencodePart(part) {
|
||
if (!part || typeof part !== 'object') return '';
|
||
var type = String(part.type || 'part');
|
||
if (type === 'text') return '<div class="wolai-page-ai-opencode-message-body">' + escapeHtml(pageAiOpencodePartText(part)).replace(/\n/g, '<br>') + '</div>';
|
||
if (type === 'reasoning') return '<details class="wolai-page-ai-opencode-part"><summary>reasoning</summary><pre>' + escapeHtml(pageAiOpencodePartText(part)) + '</pre></details>';
|
||
if (type === 'tool') {
|
||
var state = part.state && typeof part.state === 'object' ? part.state : {};
|
||
var status = String(state.status || state.type || part.status || 'pending');
|
||
return '<details class="wolai-page-ai-opencode-part wolai-page-ai-opencode-tool" open><summary>' + escapeHtml(part.tool || 'tool') + '<span>' + escapeHtml(status) + '</span></summary><pre>' + escapeHtml(pageAiOpencodeCompact(state.output || state.input || state.error || state)) + '</pre></details>';
|
||
}
|
||
if (type === 'patch') {
|
||
var files = pageAiNormalizeArray(part.files);
|
||
return '<div class="wolai-page-ai-opencode-part wolai-page-ai-opencode-patch"><strong>patch</strong>' + files.map(function(file) {
|
||
var path = String(file || '').trim();
|
||
return '<button type="button" data-page-ai-opencode-open-file="' + escapeHtml(path) + '">' + escapeHtml(path.split('/').pop() || path) + '</button>';
|
||
}).join('') + '</div>';
|
||
}
|
||
if (type === 'file') {
|
||
var filePath = String(part.filename || part.url || '').trim();
|
||
return '<div class="wolai-page-ai-opencode-part"><strong>file</strong><button type="button" data-page-ai-opencode-open-file="' + escapeHtml(filePath) + '">' + escapeHtml(filePath || 'file') + '</button></div>';
|
||
}
|
||
if (type === 'step-start' || type === 'step-finish' || type === 'snapshot' || type === 'agent' || type === 'subtask' || type === 'retry' || type === 'compaction') {
|
||
return '<details class="wolai-page-ai-opencode-part"><summary>' + escapeHtml(type) + '</summary><pre>' + escapeHtml(pageAiOpencodeCompact(part, 420)) + '</pre></details>';
|
||
}
|
||
return '<details class="wolai-page-ai-opencode-part"><summary>' + escapeHtml(type) + '</summary><pre>' + escapeHtml(pageAiOpencodeCompact(part, 420)) + '</pre></details>';
|
||
}
|
||
|
||
function pageAiRenderOpencodeMessages(drawer) {
|
||
var node = drawer.querySelector('[data-page-ai-opencode-messages]');
|
||
if (!(node instanceof HTMLElement)) return;
|
||
var messages = pageAiNormalizeArray(pageUiState.pageAiOpencodeMessages);
|
||
if (!messages.length) {
|
||
node.innerHTML = '<div class="wolai-page-ai-opencode-empty">输入消息后由 opencode 原生 runtime 回复;tool、reasoning、patch 会在这里显示。</div>';
|
||
return;
|
||
}
|
||
node.innerHTML = messages.map(function(message) {
|
||
var model = message.model && typeof message.model === 'object' ? String(message.model.providerID || message.model.provider || '') + '/' + String(message.model.id || message.model.modelID || '') : '';
|
||
var parts = pageAiNormalizeArray(message.parts).map(pageAiRenderOpencodePart).join('') || (message.text ? '<div class="wolai-page-ai-opencode-message-body">' + escapeHtml(message.text).replace(/\n/g, '<br>') + '</div>' : '');
|
||
var error = message.error ? '<pre class="wolai-page-ai-opencode-error">' + escapeHtml(pageAiOpencodeCompact(message.error, 500)) + '</pre>' : '';
|
||
return '<article class="wolai-page-ai-opencode-message" data-role="' + escapeHtml(message.role) + '">' +
|
||
'<div class="wolai-page-ai-opencode-message-role"><strong>' + escapeHtml(message.role === 'assistant' ? 'opencode' : message.role === 'user' ? '你' : message.type || 'event') + '</strong><span>' + escapeHtml([message.agent, model, message.finish].filter(Boolean).join(' · ')) + '</span></div>' +
|
||
parts + error +
|
||
'</article>';
|
||
}).join('');
|
||
node.scrollTop = node.scrollHeight;
|
||
}
|
||
|
||
function pageAiRenderOpencodePermissions(drawer) {
|
||
var node = drawer.querySelector('[data-page-ai-opencode-permissions]');
|
||
if (!(node instanceof HTMLElement)) return;
|
||
var permissions = pageAiNormalizeArray(pageUiState.pageAiOpencodePermissions);
|
||
node.innerHTML = permissions.map(function(item) {
|
||
var id = String(item && item.id || '').trim();
|
||
var action = String(item && item.action || 'permission').trim();
|
||
var resources = pageAiNormalizeArray(item && item.resources).join(', ');
|
||
return '<div class="wolai-page-ai-opencode-permission">' +
|
||
'<strong>' + escapeHtml(action) + '</strong>' +
|
||
'<span>' + escapeHtml(resources || '等待审批') + '</span>' +
|
||
'<button type="button" data-page-ai-opencode-permission="once" data-page-ai-opencode-permission-id="' + escapeHtml(id) + '">允许一次</button>' +
|
||
'<button type="button" data-page-ai-opencode-permission="always" data-page-ai-opencode-permission-id="' + escapeHtml(id) + '">总是允许</button>' +
|
||
'<button type="button" data-page-ai-opencode-permission="reject" data-page-ai-opencode-permission-id="' + escapeHtml(id) + '">拒绝</button>' +
|
||
'</div>';
|
||
}).join('');
|
||
}
|
||
|
||
function pageAiRenderOpencodeSessions(drawer) {
|
||
var node = drawer.querySelector('[data-page-ai-opencode-sessions]');
|
||
if (!(node instanceof HTMLElement)) return;
|
||
var sessions = pageAiNormalizeArray(pageUiState.pageAiOpencodeSessions).slice(0, 12);
|
||
node.innerHTML = sessions.length ? sessions.map(function(session) {
|
||
var id = String(session && session.id || '').trim();
|
||
var active = id && id === String(pageUiState.pageAiOpencodeSessionId || '');
|
||
var title = String(session && (session.title || session.name || session.id) || 'session').trim();
|
||
var model = session && session.model && typeof session.model === 'object' ? String(session.model.providerID || '') + '/' + String(session.model.id || session.model.modelID || '') : '';
|
||
return '<button type="button" class="wolai-page-ai-opencode-session-row" data-active="' + (active ? 'true' : 'false') + '" data-page-ai-opencode-session-select="' + escapeHtml(id) + '"><strong>' + escapeHtml(title) + '</strong><span>' + escapeHtml(model || id) + '</span></button>';
|
||
}).join('') : '<span class="wolai-page-ai-opencode-empty">暂无 session 列表</span>';
|
||
}
|
||
|
||
function pageAiRenderOpencodeTodo(drawer) {
|
||
var node = drawer.querySelector('[data-page-ai-opencode-todo]');
|
||
if (!(node instanceof HTMLElement)) return;
|
||
var todo = pageAiNormalizeArray(pageUiState.pageAiOpencodeTodo);
|
||
node.innerHTML = todo.length ? todo.map(function(item) {
|
||
return '<li data-state="' + escapeHtml(String(item && (item.status || item.state) || '')) + '">' + escapeHtml(String(item && (item.content || item.text || item.title) || item || '')) + '</li>';
|
||
}).join('') : '';
|
||
}
|
||
|
||
function pageAiRenderOpencodeHostChrome() {
|
||
var drawer = document.querySelector('[data-testid="wolai-page-ai-drawer"]');
|
||
if (!(drawer instanceof HTMLElement) || drawer.getAttribute('data-page-ai-opencode-host') !== 'true') return;
|
||
var status = pageUiState.pageAiOpencodeStatus || {};
|
||
var session = status.session && typeof status.session === 'object' ? status.session : status;
|
||
var sessionId = String(pageUiState.pageAiOpencodeSessionId || pageAiOpencodeSessionIdFromStatus(status) || '').trim();
|
||
var primaryPath = pageAiOpencodePrimaryPath();
|
||
var titleNode = drawer.querySelector('[data-page-ai-opencode-current-page]');
|
||
if (titleNode instanceof HTMLElement) titleNode.textContent = pageAiOpencodeCurrentPageLabel();
|
||
var pathNode = drawer.querySelector('[data-page-ai-opencode-current-path]');
|
||
if (pathNode instanceof HTMLElement) pathNode.textContent = primaryPath || '未定位真实 Markdown 路径';
|
||
var selectionNode = drawer.querySelector('[data-page-ai-opencode-selection]');
|
||
if (selectionNode instanceof HTMLElement) selectionNode.textContent = currentPageAiSelectedText() ? '已选中 ' + currentPageAiSelectedText().length + ' 字' : '无选区';
|
||
var writableNode = drawer.querySelector('[data-page-ai-opencode-writable]');
|
||
if (writableNode instanceof HTMLElement) writableNode.textContent = pageAiOpencodeWritableLabel();
|
||
var statusNode = drawer.querySelector('[data-page-ai-opencode-runtime-status]');
|
||
if (statusNode instanceof HTMLElement) {
|
||
statusNode.textContent = pageUiState.pageAiOpencodeStatusError || pageAiOpencodeStatusLabel(status);
|
||
statusNode.setAttribute('data-state', pageUiState.pageAiOpencodeStatusError ? 'error' : pageAiOpencodeStatusLabel(status));
|
||
}
|
||
var sessionNode = drawer.querySelector('[data-page-ai-opencode-session]');
|
||
if (sessionNode instanceof HTMLElement) sessionNode.textContent = sessionId ? 'session ' + sessionId : '未绑定 session';
|
||
var modelNode = drawer.querySelector('[data-page-ai-opencode-model]');
|
||
if (modelNode instanceof HTMLElement) {
|
||
var model = session.model && typeof session.model === 'object' ? String(session.model.providerID || '') + '/' + String(session.model.id || session.model.modelID || '') : '';
|
||
modelNode.textContent = model || 'model 未知';
|
||
}
|
||
var chipsNode = drawer.querySelector('[data-page-ai-opencode-changed-files]');
|
||
if (chipsNode instanceof HTMLElement) {
|
||
var changedFiles = pageAiNormalizeArray(pageUiState.pageAiOpencodeChangedFiles);
|
||
chipsNode.innerHTML = changedFiles.length ? changedFiles.map(function(file) {
|
||
var path = String(file && file.path || '').trim();
|
||
var label = path.split('/').filter(Boolean).pop() || path;
|
||
var statusText = String(file && file.status || '').trim();
|
||
return '<button type="button" class="wolai-page-ai-opencode-chip" data-page-ai-opencode-open-file="' + escapeHtml(path) + '" data-page-ai-open-file="' + escapeHtml(path) + '" data-page-ai-changed-file-chip="true" title="' + escapeHtml(path) + '">' + escapeHtml(label) + (statusText ? '<span>' + escapeHtml(statusText) + '</span>' : '') + '</button>';
|
||
}).join('') : '<span class="wolai-page-ai-opencode-empty">' + escapeHtml(pageUiState.pageAiOpencodeDiffError || '暂无 changed files') + '</span>';
|
||
}
|
||
var iframe = drawer.querySelector('[data-page-ai-opencode-iframe]');
|
||
if (iframe instanceof HTMLIFrameElement) {
|
||
var nextUrl = String(pageUiState.pageAiOpencodeIframeUrl || '').trim();
|
||
if (nextUrl && iframe.getAttribute('src') !== nextUrl) iframe.setAttribute('src', nextUrl);
|
||
iframe.hidden = !nextUrl;
|
||
}
|
||
var fallbackNode = drawer.querySelector('[data-page-ai-opencode-iframe-fallback]');
|
||
if (fallbackNode instanceof HTMLElement) {
|
||
var hasUrl = Boolean(String(pageUiState.pageAiOpencodeIframeUrl || '').trim());
|
||
fallbackNode.hidden = hasUrl && !pageUiState.pageAiOpencodeStatusError;
|
||
fallbackNode.textContent = pageUiState.pageAiOpencodeStatusError
|
||
? 'opencode WebUI 暂不可用:' + pageUiState.pageAiOpencodeStatusError
|
||
: '正在连接 opencode WebUI…';
|
||
}
|
||
}
|
||
|
||
function pageAiEnsureOpencodeHostDrawer() {
|
||
var drawer = ensurePageAiDrawer();
|
||
if (drawer.getAttribute('data-page-ai-opencode-host') !== 'true') {
|
||
drawer.setAttribute('data-page-ai-opencode-host', 'true');
|
||
drawer.setAttribute('data-mnote-acp-runtime', 'opencode');
|
||
drawer.innerHTML = '' +
|
||
'<div class="wolai-page-ai-resize-handle" data-page-ai-resize-handle title="拖动调整页面 AI 宽度" aria-hidden="true"></div>' +
|
||
'<div class="wolai-page-ai-panel wolai-page-ai-opencode-panel" role="dialog" aria-modal="false">' +
|
||
'<div class="wolai-page-ai-header wolai-page-ai-opencode-header">' +
|
||
'<div class="wolai-page-ai-header-copy">' +
|
||
'<h2 class="wolai-page-ai-title">opencode</h2>' +
|
||
'<div class="wolai-page-ai-subtitle"><span data-page-ai-opencode-runtime-status>未知</span><span data-page-ai-opencode-session>未绑定 session</span></div>' +
|
||
'</div>' +
|
||
'<div class="wolai-page-ai-header-actions">' +
|
||
'<button type="button" class="wolai-page-ai-icon" data-page-ai-action="opencode-new-session" aria-label="新建 opencode session" title="新建 session"><span class="material-symbols-outlined" data-icon="add" aria-hidden="true"></span></button>' +
|
||
'<button type="button" class="wolai-page-ai-icon" data-page-ai-action="opencode-insert-context" aria-label="把当前页上下文插入 opencode" title="插入当前页上下文"><span class="material-symbols-outlined" data-icon="post_add" aria-hidden="true"></span></button>' +
|
||
'<button type="button" class="wolai-page-ai-icon" data-page-ai-action="opencode-refresh-status" aria-label="刷新 opencode 状态" title="刷新 opencode 状态"><span class="material-symbols-outlined" data-icon="sync" aria-hidden="true"></span></button>' +
|
||
'<button type="button" class="wolai-page-ai-icon" data-page-ai-action="opencode-refresh-current-page" data-page-ai-refresh-file="current" aria-label="刷新当前页" title="刷新当前页"><span class="material-symbols-outlined" data-icon="refresh" aria-hidden="true"></span></button>' +
|
||
'<button type="button" class="wolai-page-ai-icon" data-page-ai-action="close" aria-label="关闭页面 AI"><span class="material-symbols-outlined" data-icon="close" aria-hidden="true"></span></button>' +
|
||
'</div>' +
|
||
'</div>' +
|
||
'<div class="wolai-page-ai-opencode-chrome" data-page-ai-opencode-chrome>' +
|
||
'<div class="wolai-page-ai-opencode-row"><span>当前页</span><strong data-page-ai-opencode-current-page>当前页</strong></div>' +
|
||
'<div class="wolai-page-ai-opencode-row"><span>路径</span><code data-page-ai-opencode-current-path>未定位真实 Markdown 路径</code></div>' +
|
||
'<div class="wolai-page-ai-opencode-badges"><span data-page-ai-opencode-selection>无选区</span><span data-page-ai-opencode-writable>授权待同步</span></div>' +
|
||
'<div class="wolai-page-ai-opencode-files" data-page-ai-opencode-changed-files><span class="wolai-page-ai-opencode-empty">暂无 changed files</span></div>' +
|
||
'</div>' +
|
||
'<div class="wolai-page-ai-opencode-frame-wrap" data-page-ai-opencode-frame-wrap>' +
|
||
'<div class="wolai-page-ai-opencode-iframe-fallback" data-page-ai-opencode-iframe-fallback>正在连接 opencode WebUI…</div>' +
|
||
'<iframe class="wolai-page-ai-opencode-iframe" data-page-ai-opencode-iframe title="opencode WebUI" allow="clipboard-read; clipboard-write" referrerpolicy="same-origin" hidden></iframe>' +
|
||
'</div>' +
|
||
'</div>';
|
||
}
|
||
pageAiInstallOpencodeBridge();
|
||
pageAiRenderOpencodeHostChrome();
|
||
void pageAiEnsureOpencodeSessionBinding(false);
|
||
return drawer;
|
||
}
|
||
|
||
async function pageAiEnsureOpencodeSessionBinding(force) {
|
||
if (pageUiState.pageAiOpencodeBindingInFlight) return;
|
||
var binding = pageUiState.pageAiOpencodeStatus && typeof pageUiState.pageAiOpencodeStatus === 'object' ? pageUiState.pageAiOpencodeStatus : {};
|
||
var context = pageAiBuildOpencodeContextPayload();
|
||
var pathSignature = JSON.stringify([context.pageTitle, context.pagePath, context.pageAbsolutePath, context.rootUri, context.selection.length, JSON.stringify(context.allowedRoots).slice(0, 512)]);
|
||
if (!force && pageUiState.pageAiOpencodeSessionId && pageUiState.pageAiOpencodeBindingKey === pathSignature) return;
|
||
pageUiState.pageAiOpencodeBindingInFlight = true;
|
||
pageUiState.pageAiOpencodeBindingKey = pathSignature;
|
||
try {
|
||
var response = await fetch('/api/page-ai/opencode/session', {
|
||
method: 'POST',
|
||
headers: { 'content-type': 'application/json', accept: 'application/json' },
|
||
cache: 'no-store',
|
||
body: JSON.stringify(Object.assign({ sessionId: force ? pageUiState.pageAiOpencodeSessionId || undefined : undefined, forceNew: force && !pageUiState.pageAiOpencodeSessionId }, context)),
|
||
});
|
||
var payload = await response.json().catch(function() { return null; });
|
||
if (!response.ok) throw new Error(payload && payload.error ? String(payload.error) : 'HTTP ' + response.status);
|
||
pageUiState.pageAiOpencodeStatus = payload.session || binding;
|
||
pageUiState.pageAiOpencodeStatusError = '';
|
||
pageUiState.pageAiOpencodeSessionId = String(payload.sessionId || '').trim();
|
||
pageUiState.pageAiOpencodeIframeUrl = String(payload.iframeUrl || pageUiState.pageAiOpencodeIframeUrl || '').trim();
|
||
pageAiStartOpencodeEvents();
|
||
void pageAiRefreshOpencodeProjection();
|
||
pageAiRenderOpencodeHostChrome();
|
||
} catch (error) {
|
||
pageUiState.pageAiOpencodeStatusError = error instanceof Error ? error.message : String(error);
|
||
pageUiState.pageAiOpencodeSending = false;
|
||
pageAiRenderOpencodeHostChrome();
|
||
} finally {
|
||
pageUiState.pageAiOpencodeBindingInFlight = false;
|
||
}
|
||
}
|
||
|
||
async function pageAiLoadOpencodeStatus() {
|
||
try {
|
||
var response = await fetch('/api/page-ai/opencode/status', { headers: { accept: 'application/json' }, cache: 'no-store' });
|
||
var payload = await response.json().catch(function() { return null; });
|
||
if (!response.ok) throw new Error(payload && payload.error ? String(payload.error) : 'HTTP ' + response.status);
|
||
pageUiState.pageAiOpencodeStatus = payload || {};
|
||
pageUiState.pageAiOpencodeStatusError = '';
|
||
pageUiState.pageAiOpencodeBaseUrl = String(payload && payload.baseUrl || pageUiState.pageAiOpencodeBaseUrl || 'http://127.0.0.1:4096').trim();
|
||
pageUiState.pageAiOpencodeSessionId = pageAiOpencodeSessionIdFromStatus(payload) || pageUiState.pageAiOpencodeSessionId;
|
||
pageAiRenderOpencodeHostChrome();
|
||
await pageAiRefreshOpencodeProjection();
|
||
} catch (error) {
|
||
pageUiState.pageAiOpencodeStatusError = error instanceof Error ? error.message : String(error);
|
||
pageUiState.pageAiOpencodeSending = false;
|
||
pageAiRenderOpencodeHostChrome();
|
||
}
|
||
}
|
||
|
||
async function pageAiLoadOpencodeSessions() {
|
||
try {
|
||
var response = await fetch('/api/page-ai/opencode/sessions', { headers: { accept: 'application/json' }, cache: 'no-store' });
|
||
var payload = await response.json().catch(function() { return null; });
|
||
if (!response.ok) throw new Error(payload && payload.error ? String(payload.error) : 'HTTP ' + response.status);
|
||
pageUiState.pageAiOpencodeSessions = pageAiOpencodeSessionArray(payload && payload.sessions || payload);
|
||
pageAiRenderOpencodeHostChrome();
|
||
} catch (error) {
|
||
pageUiState.pageAiOpencodeStatusError = error instanceof Error ? error.message : String(error);
|
||
pageUiState.pageAiOpencodeSending = false;
|
||
pageAiRenderOpencodeHostChrome();
|
||
}
|
||
}
|
||
|
||
async function pageAiLoadOpencodeMessages() {
|
||
var sessionId = String(pageUiState.pageAiOpencodeSessionId || '').trim();
|
||
if (!sessionId) return;
|
||
var response = await fetch('/api/page-ai/opencode/messages?sessionId=' + encodeURIComponent(sessionId), { headers: { accept: 'application/json' }, cache: 'no-store' });
|
||
var payload = await response.json().catch(function() { return null; });
|
||
if (!response.ok) throw new Error(payload && payload.error ? String(payload.error) : 'HTTP ' + response.status);
|
||
pageUiState.pageAiOpencodeMessages = pageAiOpencodeNormalizeMessages(payload);
|
||
pageAiRenderOpencodeHostChrome();
|
||
}
|
||
|
||
async function pageAiLoadOpencodePermissions() {
|
||
var sessionId = String(pageUiState.pageAiOpencodeSessionId || '').trim();
|
||
if (!sessionId) return;
|
||
var response = await fetch('/api/page-ai/opencode/permissions?sessionId=' + encodeURIComponent(sessionId), { headers: { accept: 'application/json' }, cache: 'no-store' });
|
||
var payload = await response.json().catch(function() { return null; });
|
||
if (!response.ok) throw new Error(payload && payload.error ? String(payload.error) : 'HTTP ' + response.status);
|
||
pageUiState.pageAiOpencodePermissions = pageAiOpencodeNormalizePermissions(payload);
|
||
pageAiRenderOpencodeHostChrome();
|
||
}
|
||
|
||
async function pageAiLoadOpencodeTodo() {
|
||
var sessionId = String(pageUiState.pageAiOpencodeSessionId || '').trim();
|
||
if (!sessionId) return;
|
||
var response = await fetch('/api/page-ai/opencode/todo?sessionId=' + encodeURIComponent(sessionId), { headers: { accept: 'application/json' }, cache: 'no-store' });
|
||
var payload = await response.json().catch(function() { return null; });
|
||
if (response.ok) pageUiState.pageAiOpencodeTodo = pageAiOpencodeNormalizeTodo(payload);
|
||
pageAiRenderOpencodeHostChrome();
|
||
}
|
||
|
||
async function pageAiLoadOpencodeDiff(sessionId) {
|
||
var id = String(sessionId || pageUiState.pageAiOpencodeSessionId || '').trim();
|
||
if (!id) return;
|
||
try {
|
||
var response = await fetch('/api/page-ai/opencode/diff?sessionId=' + encodeURIComponent(id), { headers: { accept: 'application/json' }, cache: 'no-store' });
|
||
var payload = await response.json().catch(function() { return null; });
|
||
if (!response.ok) throw new Error(payload && payload.error ? String(payload.error) : 'HTTP ' + response.status);
|
||
pageUiState.pageAiOpencodeChangedFiles = pageAiOpencodeNormalizeChangedFiles(payload);
|
||
pageUiState.pageAiOpencodeDiffError = '';
|
||
pageAiRenderOpencodeHostChrome();
|
||
if (pageAiOpencodeChangedFilesHitCurrentPage(pageUiState.pageAiOpencodeChangedFiles)) pageAiRefreshOpencodeCurrentPage('page-ai-opencode-event');
|
||
} catch (error) {
|
||
pageUiState.pageAiOpencodeDiffError = error instanceof Error ? error.message : String(error);
|
||
pageAiRenderOpencodeHostChrome();
|
||
}
|
||
}
|
||
|
||
async function pageAiRefreshOpencodeProjection() {
|
||
if (!pageUiState.pageAiOpencodeSessionId) return;
|
||
await Promise.allSettled([
|
||
pageAiLoadOpencodeMessages(),
|
||
pageAiLoadOpencodePermissions(),
|
||
pageAiLoadOpencodeTodo(),
|
||
pageAiLoadOpencodeDiff(pageUiState.pageAiOpencodeSessionId),
|
||
pageAiLoadOpencodeSessions(),
|
||
]);
|
||
}
|
||
|
||
function pageAiOpencodeChangedFilesHitCurrentPage(files) {
|
||
var currentPath = String(pageAiOpencodePrimaryPath() || '').trim();
|
||
if (!currentPath) return false;
|
||
var normalizedCurrent = currentPath.replace(/\\/g, '/');
|
||
return pageAiNormalizeArray(files).some(function(file) {
|
||
var path = String(file && file.path || '').trim().replace(/\\/g, '/');
|
||
return path && (path === normalizedCurrent || path.endsWith('/' + normalizedCurrent.split('/').pop()));
|
||
});
|
||
}
|
||
|
||
function pageAiOpencodeEventMatchesSession(payload, sessionId) {
|
||
var id = String(sessionId || '').trim();
|
||
if (!id) return false;
|
||
var node = payload && typeof payload === 'object' ? (payload.properties || payload.data || payload) : {};
|
||
if (String(node.sessionID || node.sessionId || '').trim() === id) return true;
|
||
var text = '';
|
||
try { text = JSON.stringify(payload || {}); } catch (_error) { text = ''; }
|
||
return text.indexOf(id) !== -1;
|
||
}
|
||
|
||
function pageAiScheduleOpencodeRefresh(reason) {
|
||
if (!pageUiState.pageAiOpencodeSessionId) return;
|
||
if (pageUiState.pageAiOpencodeEventTimer) window.clearTimeout(pageUiState.pageAiOpencodeEventTimer);
|
||
pageUiState.pageAiOpencodeEventTimer = window.setTimeout(function() {
|
||
pageUiState.pageAiOpencodeEventTimer = 0;
|
||
void pageAiRefreshOpencodeProjection();
|
||
}, reason === 'open' ? 250 : 700);
|
||
}
|
||
|
||
function pageAiStartOpencodeEvents() {
|
||
var sessionId = String(pageUiState.pageAiOpencodeSessionId || '').trim();
|
||
if (!sessionId || !('EventSource' in window)) return;
|
||
var existing = pageUiState.pageAiOpencodeEventSource;
|
||
if (existing && pageUiState.pageAiOpencodeEventSessionId === sessionId) return;
|
||
if (existing && typeof existing.close === 'function') existing.close();
|
||
var source = new EventSource('/api/page-ai/opencode/events');
|
||
pageUiState.pageAiOpencodeEventSource = source;
|
||
pageUiState.pageAiOpencodeEventSessionId = sessionId;
|
||
var handleEvent = function(event) {
|
||
var payload = null;
|
||
try { payload = event && event.data ? JSON.parse(event.data) : null; } catch (_error) { payload = event && event.data ? { raw: String(event.data) } : null; }
|
||
var eventType = String(event && event.type || payload && (payload.type || payload.event) || 'message');
|
||
var changedFiles = pageAiOpencodeNormalizeChangedFiles(payload);
|
||
if (changedFiles.length) {
|
||
pageUiState.pageAiOpencodeChangedFiles = changedFiles;
|
||
pageUiState.pageAiOpencodeDiffError = '';
|
||
if (pageAiOpencodeChangedFilesHitCurrentPage(changedFiles)) pageAiRefreshOpencodeCurrentPage('page-ai-opencode-event');
|
||
pageAiRenderOpencodeHostChrome();
|
||
}
|
||
if (pageAiOpencodeEventMatchesSession(payload, sessionId) || /diff|file|permission|session|message/.test(eventType)) pageAiScheduleOpencodeRefresh('event');
|
||
};
|
||
source.onopen = function() { pageAiScheduleOpencodeRefresh('open'); };
|
||
source.onmessage = handleEvent;
|
||
['message.updated', 'message.part.updated', 'message.part.delta', 'session.diff', 'session.error', 'session.updated', 'session.next.text.delta', 'session.next.tool.called', 'session.next.tool.success', 'permission.v2.asked', 'permission.v2.replied', 'file.edited'].forEach(function(type) {
|
||
source.addEventListener(type, handleEvent);
|
||
});
|
||
source.onerror = function() {
|
||
pageUiState.pageAiOpencodeDiffError = pageUiState.pageAiOpencodeDiffError || 'opencode event 暂不可用,保留手动刷新';
|
||
pageAiRenderOpencodeHostChrome();
|
||
};
|
||
}
|
||
|
||
async function pageAiSendOpencodeMessage(text) {
|
||
var sessionId = String(pageUiState.pageAiOpencodeSessionId || '').trim();
|
||
if (!sessionId) await pageAiEnsureOpencodeSessionBinding(true);
|
||
sessionId = String(pageUiState.pageAiOpencodeSessionId || '').trim();
|
||
if (!sessionId) throw new Error('opencode session 未就绪');
|
||
pageUiState.pageAiOpencodeMessages = pageAiNormalizeArray(pageUiState.pageAiOpencodeMessages);
|
||
pageUiState.pageAiOpencodeSending = true;
|
||
pageAiRenderOpencodeHostChrome();
|
||
var response = await fetch('/api/page-ai/opencode/prompt', {
|
||
method: 'POST',
|
||
headers: { 'content-type': 'application/json', accept: 'application/json' },
|
||
cache: 'no-store',
|
||
body: JSON.stringify({ sessionId: sessionId, text: text, model: PAGE_AI_OPENCODE_MODEL })
|
||
});
|
||
var payload = await response.json().catch(function() { return null; });
|
||
if (!response.ok) throw new Error(payload && payload.error ? String(payload.error) : 'HTTP ' + response.status);
|
||
pageUiState.pageAiOpencodeSending = false;
|
||
pageAiScheduleOpencodeRefresh('prompt');
|
||
}
|
||
|
||
async function pageAiReplyOpencodePermission(id, response) {
|
||
var sessionId = String(pageUiState.pageAiOpencodeSessionId || '').trim();
|
||
if (!sessionId || !id) return;
|
||
await fetch('/api/page-ai/opencode/permission/reply', {
|
||
method: 'POST',
|
||
headers: { 'content-type': 'application/json', accept: 'application/json' },
|
||
cache: 'no-store',
|
||
body: JSON.stringify({ sessionId: sessionId, requestId: id, response: response })
|
||
});
|
||
void pageAiLoadOpencodePermissions();
|
||
}
|
||
|
||
async function pageAiAbortOpencodeRun() {
|
||
var sessionId = String(pageUiState.pageAiOpencodeSessionId || '').trim();
|
||
if (!sessionId) return;
|
||
await fetch('/api/page-ai/opencode/abort', {
|
||
method: 'POST',
|
||
headers: { 'content-type': 'application/json', accept: 'application/json' },
|
||
cache: 'no-store',
|
||
body: JSON.stringify({ sessionId: sessionId })
|
||
});
|
||
pageAiScheduleOpencodeRefresh('abort');
|
||
}
|
||
|
||
function pageAiSelectOpencodeSession(sessionId) {
|
||
var id = String(sessionId || '').trim();
|
||
if (!id) return;
|
||
pageUiState.pageAiOpencodeSessionId = id;
|
||
pageUiState.pageAiOpencodeMessages = [];
|
||
pageUiState.pageAiOpencodeLocalMessages = [];
|
||
pageAiStartOpencodeEvents();
|
||
void pageAiRefreshOpencodeProjection();
|
||
pageAiRenderOpencodeHostChrome();
|
||
}
|
||
|
||
function pageAiOpenOpencodeChangedFile(path) {
|
||
var targetPath = String(path || '').trim();
|
||
if (!targetPath || !window.__mnoteDocumentPaneRuntime || typeof window.__mnoteDocumentPaneRuntime.openResourceInActiveTab !== 'function') return false;
|
||
void window.__mnoteDocumentPaneRuntime.openResourceInActiveTab({ path: targetPath });
|
||
return true;
|
||
}
|
||
|
||
function pageAiRefreshOpencodeCurrentPage(reason) {
|
||
if (window.__mnoteDocumentPaneRuntime && typeof window.__mnoteDocumentPaneRuntime.refreshPrimaryDocument === 'function') {
|
||
void window.__mnoteDocumentPaneRuntime.refreshPrimaryDocument({ reason: reason || 'page-ai-opencode-host' });
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
function pageAiApplyBoardFirstShell() {
|
||
if (!pageAiBoardFirstEnabled()) return;
|
||
var drawer = document.querySelector('[data-testid="wolai-page-ai-drawer"]');
|
||
if (!(drawer instanceof HTMLElement)) return;
|
||
if (drawer.getAttribute('data-page-ai-opencode-host') === 'true') return;
|
||
drawer.setAttribute('data-page-ai-board-first', 'true');
|
||
var subtitle = drawer.querySelector('.wolai-page-ai-subtitle');
|
||
if (subtitle instanceof HTMLElement) {
|
||
subtitle.innerHTML = '<span>Agent Board</span><span>ZCode 默认 developer</span><span>local-first 文件编辑</span>';
|
||
}
|
||
var runtimeStrip = drawer.querySelector('[data-page-ai-runtime-strip]');
|
||
if (runtimeStrip instanceof HTMLElement) {
|
||
runtimeStrip.setAttribute('aria-label', '打开 Agent Board 运行状态');
|
||
}
|
||
var runtimeBadge = drawer.querySelector('[data-page-ai-runtime-badge]');
|
||
if (runtimeBadge instanceof HTMLElement) runtimeBadge.textContent = 'Agent Board · ' + pageAiBoardWorkflowLabel();
|
||
var agentChip = drawer.querySelector('.wolai-page-ai-agent-chip');
|
||
if (agentChip instanceof HTMLElement) {
|
||
agentChip.textContent = pageAiBoardWorkerLabel() + ' / ' + pageAiBoardModelLabel();
|
||
agentChip.setAttribute('title', '当前 worker/model:' + pageAiBoardWorkerLabel() + ' / ' + pageAiBoardModelLabel());
|
||
}
|
||
drawer.querySelectorAll('[data-page-ai-tab="agent"], [data-page-ai-tab="reasonix-settings"], [data-page-ai-tab="hermes-settings"]').forEach(function(node) {
|
||
if (node instanceof HTMLElement) node.hidden = true;
|
||
});
|
||
drawer.querySelectorAll('[data-page-ai-panel="agent"], [data-page-ai-panel="reasonix-settings"], [data-page-ai-panel="hermes-settings"]').forEach(function(node) {
|
||
if (node instanceof HTMLElement) node.setAttribute('data-page-ai-legacy-provider-panel', 'true');
|
||
});
|
||
}
|
||
|
||
function pageAiBoardDefaultWorkerId() {
|
||
return 'mnote-page-ai-zcode';
|
||
}
|
||
|
||
function pageAiBoardDefaultWorkflowId() {
|
||
return 'builtin-mnote-page-ai-chat';
|
||
}
|
||
|
||
function pageAiBoardDefaultModelOverride() {
|
||
return 'zcode-default';
|
||
}
|
||
|
||
function pageAiBoardDefaultModelOptions() {
|
||
return [
|
||
{ id: 'zcode-default', label: '默认', default: true },
|
||
{ id: 'zcode-fast', label: '快速' },
|
||
{ id: 'zcode-strong', label: '强力' }
|
||
];
|
||
}
|
||
|
||
function pageAiBoardSelectedWorkerId() {
|
||
return String(pageUiState.pageAiBoardWorkerId || pageAiBoardDefaultWorkerId()).trim() || pageAiBoardDefaultWorkerId();
|
||
}
|
||
|
||
function pageAiBoardSelectedWorkflowId() {
|
||
return String(pageUiState.pageAiBoardWorkflowId || pageAiBoardDefaultWorkflowId()).trim() || pageAiBoardDefaultWorkflowId();
|
||
}
|
||
|
||
function pageAiBoardModelOptions(workerId) {
|
||
var selectedWorkerId = String(workerId || pageAiBoardSelectedWorkerId()).trim() || pageAiBoardDefaultWorkerId();
|
||
var worker = pageAiBoardWorkerOptions().find(function(item) { return String(item.id || '') === selectedWorkerId; }) || {};
|
||
var options = pageAiNormalizeArray(worker.modelOptions || worker.models || worker.allowedModels)
|
||
.map(function(option) {
|
||
if (typeof option === 'string') return { id: option, label: option };
|
||
return {
|
||
id: String(option && (option.id || option.value || option.modelOverride || option.model) || '').trim(),
|
||
label: String(option && (option.label || option.name || option.id || option.value || option.modelOverride || option.model) || '').trim(),
|
||
default: Boolean(option && option.default)
|
||
};
|
||
})
|
||
.filter(function(option) { return option.id; });
|
||
if (!options.length && selectedWorkerId === pageAiBoardDefaultWorkerId()) options = pageAiBoardDefaultModelOptions();
|
||
return options;
|
||
}
|
||
|
||
function pageAiBoardSelectedModelOverride() {
|
||
var options = pageAiBoardModelOptions();
|
||
var fallback = (options.find(function(option) { return option.default; }) || options[0] || { id: pageAiBoardDefaultModelOverride() }).id;
|
||
var selected = String(pageUiState.pageAiBoardModelOverride || fallback || pageAiBoardDefaultModelOverride()).trim();
|
||
if (!options.length) return selected || pageAiBoardDefaultModelOverride();
|
||
return options.some(function(option) { return option.id === selected; }) ? selected : fallback;
|
||
}
|
||
|
||
function pageAiBoardModelLabel(modelOverride) {
|
||
var id = String(modelOverride || pageAiBoardSelectedModelOverride()).trim();
|
||
var option = pageAiBoardModelOptions().find(function(item) { return item.id === id; });
|
||
return String(option && (option.label || option.id) || id || '默认').trim();
|
||
}
|
||
|
||
function pageAiBoardWorkerOptions() {
|
||
var byId = {};
|
||
var fallback = {
|
||
id: pageAiBoardDefaultWorkerId(),
|
||
name: 'MNote 页面 AI · ZCode',
|
||
surface: 'mnote-page-ai',
|
||
role: 'developer',
|
||
agentType: 'zcode',
|
||
agentModel: 'default',
|
||
capabilities: ['text', 'repo-edit', 'terminal', 'mnote-capability-envelope', 'local-markdown-edit'],
|
||
modelOptions: pageAiBoardDefaultModelOptions()
|
||
};
|
||
pageAiNormalizeArray(pageUiState.pageAiBoardWorkers).concat([fallback]).forEach(function(worker) {
|
||
var id = String(worker && (worker.id || worker.workerPresetId || worker.presetId) || '').trim();
|
||
var surface = String(worker && (worker.surface || worker.productSurface) || '').trim();
|
||
if (!id || id !== pageAiBoardDefaultWorkerId()) return;
|
||
if (surface && surface !== 'mnote-page-ai') return;
|
||
byId[id] = Object.assign({}, fallback, worker, { id: id, surface: 'mnote-page-ai' });
|
||
});
|
||
return Object.keys(byId).map(function(id) { return byId[id]; });
|
||
}
|
||
|
||
function pageAiBoardWorkflowOptions() {
|
||
var byId = {};
|
||
var fallback = { id: pageAiBoardDefaultWorkflowId(), name: 'MNote 页面 AI', surface: 'mnote-page-ai' };
|
||
pageAiNormalizeArray(pageUiState.pageAiBoardWorkflows).concat([fallback]).forEach(function(workflow) {
|
||
var id = String(workflow && (workflow.id || workflow.workflowId) || '').trim();
|
||
var surface = String(workflow && (workflow.surface || workflow.productSurface) || '').trim();
|
||
if (!id || id !== pageAiBoardDefaultWorkflowId()) return;
|
||
if (surface && surface !== 'mnote-page-ai') return;
|
||
byId[id] = Object.assign({}, fallback, workflow, { id: id, surface: 'mnote-page-ai' });
|
||
});
|
||
return Object.keys(byId).map(function(id) { return byId[id]; });
|
||
}
|
||
|
||
function pageAiBoardWorkerLabel(workerId) {
|
||
var id = String(workerId || pageAiBoardSelectedWorkerId()).trim();
|
||
var worker = pageAiBoardWorkerOptions().find(function(item) { return String(item.id || '') === id; });
|
||
return String(worker && (worker.name || worker.id) || id || 'Board worker').trim();
|
||
}
|
||
|
||
function pageAiBoardWorkflowLabel(workflowId) {
|
||
var id = String(workflowId || pageAiBoardSelectedWorkflowId()).trim();
|
||
var workflow = pageAiBoardWorkflowOptions().find(function(item) { return String(item.id || '') === id; });
|
||
return String(workflow && (workflow.name || workflow.id) || id || 'Board workflow').trim();
|
||
}
|
||
|
||
async function pageAiLoadBoardCatalog() {
|
||
if (!pageAiBoardFirstEnabled()) return;
|
||
try {
|
||
var workersResponse = await fetch('/api/page-ai/board/workers', { headers: { accept: 'application/json' }, cache: 'no-store' });
|
||
var workersPayload = await workersResponse.json().catch(function() { return null; });
|
||
if (workersResponse.ok) {
|
||
var workers = pageAiNormalizeArray(workersPayload && workersPayload.workers);
|
||
if (!workers.length) workers = pageAiNormalizeArray(workersPayload && workersPayload.workers && workersPayload.workers.workers);
|
||
pageUiState.pageAiBoardWorkers = workers;
|
||
}
|
||
var workflowsResponse = await fetch('/api/page-ai/board/workflows', { headers: { accept: 'application/json' }, cache: 'no-store' });
|
||
var workflowsPayload = await workflowsResponse.json().catch(function() { return null; });
|
||
if (workflowsResponse.ok) {
|
||
var workflows = pageAiNormalizeArray(workflowsPayload && workflowsPayload.workflows);
|
||
pageUiState.pageAiBoardWorkflows = workflows;
|
||
}
|
||
pageUiState.pageAiBoardCatalogError = '';
|
||
} catch (error) {
|
||
pageUiState.pageAiBoardCatalogError = error instanceof Error ? error.message : String(error);
|
||
}
|
||
renderPageAiControls();
|
||
pageAiApplyBoardFirstShell();
|
||
}
|
||
|
||
function pageAiExtractFinalAnswer(summary) {
|
||
var text = String(summary || '').trim();
|
||
if (!text) return '';
|
||
var match = text.match(/(?:^|\n)##\s*FINAL_ANSWER\s*\n([\s\S]*?)(?=\n##\s+|$)/i);
|
||
if (match && match[1].trim()) return match[1].trim();
|
||
return text
|
||
.replace(/^##\s*Completed\s*/i, '')
|
||
.replace(/\n##\s*Comments[\s\S]*$/i, '')
|
||
.replace(/\n##\s*Remaining[\s\S]*$/i, '')
|
||
.trim();
|
||
}
|
||
|
||
function pageAiFileUriToPath(rootUri) {
|
||
var value = String(rootUri || '').trim();
|
||
if (!value.indexOf('file://') === 0 && value.slice(0, 7) !== 'file://') return '';
|
||
try {
|
||
return decodeURIComponent(value.replace(/^file:\/\//, ''));
|
||
} catch (_error) {
|
||
return value.replace(/^file:\/\//, '');
|
||
}
|
||
}
|
||
|
||
function pageAiJoinPath(rootPath, relativePath) {
|
||
var root = String(rootPath || '').replace(/\/+$/, '');
|
||
var rel = String(relativePath || '').replace(/\\/g, '/').replace(/^\/+/, '');
|
||
if (!root || !rel) return root || rel;
|
||
return root + '/' + rel;
|
||
}
|
||
|
||
function pageAiBoardCapabilities() {
|
||
return [
|
||
'mnote.current_page.read',
|
||
'mnote.selection.read',
|
||
'mnote.open_resources.snapshot',
|
||
'mnote.allowed_roots.describe',
|
||
'mnote.lightrag.query',
|
||
'mnote.reference.open',
|
||
'mnote.page_aggregate.snapshot',
|
||
'mnote.local_file.receipt'
|
||
];
|
||
}
|
||
|
||
function pageAiBuildBoardEnvelope(prompt, scopedContext, runTargetSnapshot, contextRefs, allowedRoots, agentTargetPackage) {
|
||
var workspacePath = agentTargetPackage && agentTargetPackage.workspacePath && typeof agentTargetPackage.workspacePath === 'object'
|
||
? agentTargetPackage.workspacePath
|
||
: (runTargetSnapshot && runTargetSnapshot.workspacePath && typeof runTargetSnapshot.workspacePath === 'object' ? runTargetSnapshot.workspacePath : {});
|
||
var relativePath = String(workspacePath.relativePath || agentTargetPackage && agentTargetPackage.relativePath || runTargetSnapshot && runTargetSnapshot.relativePath || '').trim();
|
||
var rootPath = pageAiFileUriToPath(currentRootUri());
|
||
var absolutePath = String(workspacePath.absolutePath || workspacePath.path || '').trim() || pageAiJoinPath(rootPath, relativePath);
|
||
var selectedText = String(scopedContext && scopedContext.selectedText || '').trim();
|
||
var writeIntent = /\b(edit|write|append|replace|fix|rewrite|update)\b|编辑|修改|追加|替换|修复|改写|更新/.test(String(prompt || '').toLowerCase());
|
||
return {
|
||
schema: 'mnote.page_ai.board_task.v1',
|
||
intent: writeIntent ? 'edit' : 'ask',
|
||
message: prompt,
|
||
workspaceId: resolveWorkspaceId(document.body),
|
||
sourceKind: currentSourceKind(),
|
||
rootUri: currentRootUri(),
|
||
documentId: currentDocumentId(),
|
||
pageTitle: String((currentPageAggregate().meta || {}).title || document.title || '').trim(),
|
||
board: {
|
||
workflowId: pageAiBoardSelectedWorkflowId(),
|
||
workflowName: pageAiBoardWorkflowLabel(),
|
||
workerPresetId: pageAiBoardSelectedWorkerId(),
|
||
workerName: pageAiBoardWorkerLabel(),
|
||
modelOverride: pageAiBoardSelectedModelOverride(),
|
||
modelLabel: pageAiBoardModelLabel()
|
||
},
|
||
primaryTarget: {
|
||
kind: 'markdown_file',
|
||
relativePath: relativePath,
|
||
absolutePath: absolutePath,
|
||
documentId: currentDocumentId(),
|
||
workspacePath: workspacePath
|
||
},
|
||
selection: {
|
||
text: selectedText,
|
||
range: null
|
||
},
|
||
contextRefs: contextRefs,
|
||
allowedRoots: allowedRoots,
|
||
capabilities: pageAiBoardCapabilities(),
|
||
modelOverride: pageAiBoardSelectedModelOverride(),
|
||
pageContext: pageAiPageContextForRefs(scopedContext.pageContext, contextRefs),
|
||
runTargetSnapshot: runTargetSnapshot,
|
||
targetPackage: agentTargetPackage,
|
||
writePolicy: {
|
||
allowFileWrite: allowedRoots.some(function(root) { return String(root.permission || '').indexOf('write') >= 0; }),
|
||
requireHumanApproval: false,
|
||
forbidMnoteInternalStateWrite: true
|
||
}
|
||
};
|
||
}
|
||
|
||
function pageAiBoardReceiptSummary(payload) {
|
||
var receipt = payload && payload.receipt && typeof payload.receipt === 'object' ? payload.receipt : null;
|
||
var board = payload && payload.board && typeof payload.board === 'object' ? payload.board : null;
|
||
if (receipt && String(receipt.summary || '').trim()) return String(receipt.summary || '').trim();
|
||
var nodeRuns = Array.isArray(board && board.nodeRuns) ? board.nodeRuns : [];
|
||
for (var i = nodeRuns.length - 1; i >= 0; i -= 1) {
|
||
if (String(nodeRuns[i] && nodeRuns[i].outputSummary || '').trim()) return String(nodeRuns[i].outputSummary).trim();
|
||
}
|
||
var events = Array.isArray(board && board.events) ? board.events : [];
|
||
if (events.length) return String(events[events.length - 1].message || '').trim();
|
||
return '';
|
||
}
|
||
|
||
function pageAiBoardRunStatus(payload) {
|
||
var board = payload && payload.board && typeof payload.board === 'object' ? payload.board : null;
|
||
var run = board && board.run && typeof board.run === 'object' ? board.run : (board && board.id ? board : null);
|
||
return String(run && run.status || '').trim();
|
||
}
|
||
|
||
function pageAiBoardRunId(payload) {
|
||
var board = payload && payload.board && typeof payload.board === 'object' ? payload.board : null;
|
||
var run = board && board.run && typeof board.run === 'object' ? board.run : (board && board.id ? board : null);
|
||
return String(run && run.id || payload && payload.runId || payload && payload.id || '').trim();
|
||
}
|
||
|
||
function pageAiBoardReceiptObject(payload) {
|
||
var receipt = payload && payload.receipt && typeof payload.receipt === 'object' ? payload.receipt : null;
|
||
if (receipt && receipt.receipt && typeof receipt.receipt === 'object') return receipt.receipt;
|
||
return receipt;
|
||
}
|
||
|
||
function pageAiBoardFinalAnswer(payload) {
|
||
var receipt = pageAiBoardReceiptObject(payload);
|
||
var candidates = [
|
||
receipt && receipt.finalAnswer,
|
||
receipt && receipt.summary,
|
||
receipt && receipt.outputSummary,
|
||
pageAiBoardReceiptSummary(payload)
|
||
];
|
||
for (var i = 0; i < candidates.length; i += 1) {
|
||
var text = pageAiExtractFinalAnswer(candidates[i]);
|
||
if (text) return text;
|
||
}
|
||
return '';
|
||
}
|
||
|
||
function pageAiBoardChangedFiles(payload) {
|
||
var receipt = pageAiBoardReceiptObject(payload) || {};
|
||
var changed = pageAiNormalizeArray(receipt.changedFiles || receipt.changed_files || receipt.filesChanged);
|
||
return changed.map(function(file) {
|
||
if (typeof file === 'string') return { path: file };
|
||
return {
|
||
path: String(file && (file.path || file.absolutePath || file.relativePath || file.file) || '').trim(),
|
||
status: String(file && (file.status || file.changeType || file.kind) || '').trim()
|
||
};
|
||
}).filter(function(file) { return file.path; });
|
||
}
|
||
|
||
function pageAiBoardTimeline(payload) {
|
||
var board = payload && payload.board && typeof payload.board === 'object' ? payload.board : null;
|
||
var events = pageAiNormalizeArray(board && board.events);
|
||
if (!events.length && board && board.run && Array.isArray(board.run.events)) events = board.run.events;
|
||
return pageAiNormalizeArray(events).map(function(event) {
|
||
var timestamp = String(event && (event.timestamp || event.createdAt || event.created_at || event.time) || '').trim();
|
||
var type = String(event && (event.type || event.eventType || event.event || event.status) || '').trim();
|
||
var message = String(event && (event.message || event.summary || event.title || '') || '').trim();
|
||
return { timestamp: timestamp, type: type, message: message };
|
||
}).filter(function(event) { return event.type || event.message; });
|
||
}
|
||
|
||
function pageAiBoardVerification(payload) {
|
||
var receipt = pageAiBoardReceiptObject(payload) || {};
|
||
return pageAiNormalizeArray(receipt.verification || receipt.checks).map(function(item) {
|
||
if (typeof item === 'string') return { command: item, status: '' };
|
||
return {
|
||
command: String(item && (item.command || item.name || item.label) || '').trim(),
|
||
status: String(item && (item.status || item.result) || '').trim(),
|
||
output: String(item && (item.output || item.summary) || '').trim()
|
||
};
|
||
}).filter(function(item) { return item.command || item.status || item.output; });
|
||
}
|
||
|
||
function pageAiBuildBoardRunDetail(runId, payload) {
|
||
var status = pageAiBoardRunStatus(payload);
|
||
return {
|
||
schema: 'mnote.page_ai.board_run_detail.v1',
|
||
runId: runId,
|
||
status: status === 'complete' ? 'completed' : (status || 'completed'),
|
||
workflowId: pageAiBoardSelectedWorkflowId(),
|
||
workflowName: pageAiBoardWorkflowLabel(),
|
||
workerPresetId: pageAiBoardSelectedWorkerId(),
|
||
workerName: pageAiBoardWorkerLabel(),
|
||
modelOverride: pageAiBoardSelectedModelOverride(),
|
||
modelLabel: pageAiBoardModelLabel(),
|
||
finalAnswer: pageAiBoardFinalAnswer(payload),
|
||
changedFiles: pageAiBoardChangedFiles(payload),
|
||
verification: pageAiBoardVerification(payload),
|
||
timeline: pageAiBoardTimeline(payload),
|
||
receipt: pageAiBoardReceiptObject(payload) || null,
|
||
payload: payload || null,
|
||
updatedAt: Date.now()
|
||
};
|
||
}
|
||
|
||
function pageAiStoreBoardRunDetail(runId, payload) {
|
||
if (!runId) return null;
|
||
if (!pageUiState.pageAiBoardRunDetails || typeof pageUiState.pageAiBoardRunDetails !== 'object') {
|
||
pageUiState.pageAiBoardRunDetails = {};
|
||
}
|
||
var detail = pageAiBuildBoardRunDetail(runId, payload);
|
||
pageUiState.pageAiBoardRunDetails[runId] = detail;
|
||
var current = pageAiCurrentSession();
|
||
if (current) {
|
||
current.boardRuns = Object.assign({}, current.boardRuns || {}, { [runId]: detail });
|
||
current.boardRunId = runId;
|
||
current.workflowId = detail.workflowId;
|
||
current.workerPresetId = detail.workerPresetId;
|
||
current.modelOverride = detail.modelOverride;
|
||
current.receiptId = String(detail.receipt && (detail.receipt.id || detail.receipt.receiptId) || runId || '').trim();
|
||
}
|
||
return detail;
|
||
}
|
||
|
||
async function pageAiPollBoardRun(runId) {
|
||
var lastPayload = null;
|
||
for (var attempt = 0; attempt < 360; attempt += 1) {
|
||
var response = await fetch('/api/page-ai/board/runs/' + encodeURIComponent(runId), { headers: { accept: 'application/json' }, cache: 'no-store' });
|
||
var payload = await response.json().catch(function() { return null; });
|
||
if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'board_run_status_failed_' + response.status));
|
||
lastPayload = payload;
|
||
var status = pageAiBoardRunStatus(payload);
|
||
if (status) pageAiSetRunStatus(status === 'complete' ? 'completed' : status);
|
||
renderPageAiControls();
|
||
if (['complete', 'failed', 'blocked', 'cancelled'].indexOf(status) >= 0) return payload;
|
||
await new Promise(function(resolve) { window.setTimeout(resolve, attempt < 8 ? 1000 : 2500); });
|
||
}
|
||
return lastPayload;
|
||
}
|
||
|
||
async function sendPageAiBoardMessage(text) {
|
||
if (pageUiState.pageAiBusy) return;
|
||
var prompt = searchText(text);
|
||
if (!prompt) return;
|
||
pageUiState.pageAiBusy = true;
|
||
pageAiSetRunStatus('preparing');
|
||
var currentSession = pageAiCurrentSession();
|
||
try {
|
||
var contextSnapshot = currentPageAiContextSnapshot();
|
||
var scopedContext = pageAiScopedPageContext(contextSnapshot);
|
||
scopedContext.editorTarget = currentPageAiScopedEditorTarget();
|
||
assertPageAiTargetInCurrentWorkspace(scopedContext.editorTarget);
|
||
if (currentSourceKind() === 'local_folder' && !pageAiNormalizeArray(pageUiState.pageAiAllowedRoots).length) {
|
||
await pageAiLoadAllowedRoots();
|
||
}
|
||
await assertPageAiTargetWritable(scopedContext.editorTarget);
|
||
var runTargetSnapshot = pageAiBuildRunTargetSnapshot(scopedContext, prompt);
|
||
var contextRefs = pageAiBuildContextRefs(scopedContext, runTargetSnapshot);
|
||
var allowedRoots = pageAiBuildAllowedRoots();
|
||
var agentTargetPackage = pageAiBuildAgentTargetPackage(scopedContext, runTargetSnapshot);
|
||
assertPageAiLocalWritePermission(prompt, agentTargetPackage);
|
||
var envelope = pageAiBuildBoardEnvelope(prompt, scopedContext, runTargetSnapshot, contextRefs, allowedRoots, agentTargetPackage);
|
||
pageUiState.pageAiMessages.push({ role: 'user', content: prompt });
|
||
pageUiState.pageAiMessages.push({ role: 'assistant', kind: 'status', content: '正在处理…', streaming: true });
|
||
if (currentSession) {
|
||
if (currentSession.title === '新会话') currentSession.title = prompt.length > 18 ? prompt.slice(0, 18) + '…' : prompt;
|
||
currentSession.schema = 'mnote.page_ai.session.v2';
|
||
currentSession.source = 'board';
|
||
currentSession.workerPresetId = pageAiBoardSelectedWorkerId();
|
||
currentSession.workflowId = pageAiBoardSelectedWorkflowId();
|
||
currentSession.modelOverride = pageAiBoardSelectedModelOverride();
|
||
currentSession.messages = pageUiState.pageAiMessages.slice();
|
||
currentSession.updatedAt = Date.now();
|
||
}
|
||
pageAiPersistSessions();
|
||
renderPageAiConversation();
|
||
renderPageAiControls();
|
||
pageAiApplyBoardFirstShell();
|
||
var response = await fetch('/api/page-ai/board/runs', {
|
||
method: 'POST',
|
||
headers: { 'content-type': 'application/json', accept: 'application/json' },
|
||
body: JSON.stringify({
|
||
workflowId: pageAiBoardSelectedWorkflowId(),
|
||
workerPresetId: pageAiBoardSelectedWorkerId(),
|
||
modelOverride: pageAiBoardSelectedModelOverride(),
|
||
message: prompt,
|
||
envelope: envelope,
|
||
autoRun: true,
|
||
priority: 'high'
|
||
})
|
||
});
|
||
var payload = await response.json().catch(function() { return null; });
|
||
if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'board_run_failed_' + response.status));
|
||
var runId = pageAiBoardRunId(payload);
|
||
pageUiState.pageAiCurrentRunId = runId;
|
||
if (runId) {
|
||
pageUiState.pageAiMessages = pageUiState.pageAiMessages.filter(function(message) { return !(message && message.kind === 'status' && message.streaming); });
|
||
pageUiState.pageAiMessages.push({ role: 'assistant', kind: 'status', content: 'Agent Board 正在处理…', streaming: true, boardRunId: runId });
|
||
}
|
||
pageAiSetRunStatus('running');
|
||
renderPageAiConversation();
|
||
pageAiApplyBoardFirstShell();
|
||
var finalPayload = runId ? await pageAiPollBoardRun(runId) : payload;
|
||
var detail = pageAiStoreBoardRunDetail(runId, finalPayload);
|
||
var finalAnswer = detail && detail.finalAnswer || 'Agent Board run 已结束,但没有返回 final answer。';
|
||
pageUiState.pageAiMessages = pageUiState.pageAiMessages.filter(function(message) { return !(message && message.kind === 'status' && message.streaming); });
|
||
pageUiState.pageAiMessages.push({
|
||
role: 'assistant',
|
||
content: finalAnswer,
|
||
boardRunId: runId,
|
||
workflowId: pageAiBoardSelectedWorkflowId(),
|
||
workerPresetId: pageAiBoardSelectedWorkerId(),
|
||
modelOverride: pageAiBoardSelectedModelOverride(),
|
||
retryPrompt: prompt,
|
||
receiptId: String(detail && detail.receipt && (detail.receipt.id || detail.receipt.receiptId) || runId || '').trim()
|
||
});
|
||
if (currentSession) {
|
||
currentSession.schema = 'mnote.page_ai.session.v2';
|
||
currentSession.source = 'board';
|
||
currentSession.messages = pageUiState.pageAiMessages.slice();
|
||
currentSession.updatedAt = Date.now();
|
||
currentSession.boardRunId = runId;
|
||
currentSession.workflowId = pageAiBoardSelectedWorkflowId();
|
||
currentSession.workerPresetId = pageAiBoardSelectedWorkerId();
|
||
currentSession.modelOverride = pageAiBoardSelectedModelOverride();
|
||
}
|
||
pageAiPersistSessions();
|
||
pageAiSetRunStatus(pageAiBoardRunStatus(finalPayload) === 'complete' ? 'completed' : pageAiBoardRunStatus(finalPayload) || 'completed');
|
||
renderPageAiConversation();
|
||
pageAiApplyBoardFirstShell();
|
||
} catch (error) {
|
||
pageAiSetRunStatus('failed');
|
||
pageUiState.pageAiMessages.push({ role: 'assistant', content: 'Agent Board 处理失败:' + (error instanceof Error ? error.message : String(error)) });
|
||
renderPageAiConversation();
|
||
pageAiApplyBoardFirstShell();
|
||
} finally {
|
||
pageUiState.pageAiBusy = false;
|
||
renderPageAiControls();
|
||
pageAiApplyBoardFirstShell();
|
||
}
|
||
}
|
||
|
||
function pageAiApplyAgentDescriptorsPayload(payload) {
|
||
var descriptors = pageAiNormalizeArray(payload && payload.descriptors)
|
||
.map(pageAiNormalizeAgentDescriptor)
|
||
.filter(function(descriptor) { return descriptor.schema === PAGE_AI_DESCRIPTOR_SCHEMA; });
|
||
if (!descriptors.length) return false;
|
||
pageUiState.pageAiAgentDescriptors = descriptors;
|
||
pageUiState.pageAiAgentDescriptorsLoaded = true;
|
||
pageUiState.pageAiAgentDescriptorError = '';
|
||
if (payload && payload.preferenceValues && typeof payload.preferenceValues === 'object') {
|
||
pageUiState.pageAiSkillPreferences = Object.assign(
|
||
{},
|
||
pageAiDescriptorPreferenceValues(),
|
||
payload.preferenceValues
|
||
);
|
||
}
|
||
var current = pageAiCurrentAgentId();
|
||
var record = pageAiAgentRecord(current);
|
||
pageUiState.pageAiAgentId = record.id;
|
||
pageUiState.pageAiAcpRuntime = record.acpRuntime || pageUiState.pageAiAcpRuntime || 'reasonix';
|
||
if (record.tools) pageUiState.pageAiTools = record.tools;
|
||
return true;
|
||
}
|
||
|
||
async function pageAiLoadAgentDescriptors() {
|
||
try {
|
||
var url = new URL('/api/page-ai/agents/descriptors', window.location.origin);
|
||
url.searchParams.set('profile', pageAiCurrentProfile());
|
||
var response = await fetch(url.toString(), {
|
||
headers: { accept: 'application/json' },
|
||
cache: 'no-store'
|
||
});
|
||
var payload = await response.json().catch(function() { return null; });
|
||
if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'agent_descriptors_failed_' + response.status));
|
||
if (!pageAiApplyAgentDescriptorsPayload(payload)) throw new Error('agent_descriptors_empty');
|
||
} catch (error) {
|
||
pageUiState.pageAiAgentDescriptorError = error instanceof Error ? error.message : String(error);
|
||
pageUiState.pageAiAgentDescriptorsLoaded = false;
|
||
}
|
||
renderPageAiProviderButtons();
|
||
renderPageAiControls();
|
||
return pageUiState.pageAiAgentDescriptors;
|
||
}
|
||
|
||
async function pageAiLoadAiPreferences() {
|
||
try {
|
||
var url = new URL('/api/ui/preferences/effective', window.location.origin);
|
||
url.searchParams.set('workspaceId', resolveWorkspaceId(document.body));
|
||
url.searchParams.set('sourceKind', currentSourceKind());
|
||
url.searchParams.set('rootUri', currentRootUri());
|
||
url.searchParams.set('documentId', currentDocumentId());
|
||
var response = await fetch(url.toString(), { headers: { accept: 'application/json' }, cache: 'no-store' });
|
||
var payload = await response.json().catch(function() { return null; });
|
||
if (!response.ok || !payload || !payload.result) return;
|
||
var preferences = payload.result.aiPreferences && typeof payload.result.aiPreferences === 'object'
|
||
? payload.result.aiPreferences
|
||
: {};
|
||
pageUiState.pageAiSkillPreferences = Object.assign({}, preferences);
|
||
var defaultAgent = String(preferences['ai.common.default_agent_id'] || '').trim();
|
||
if (defaultAgent) {
|
||
var agent = pageAiAgentRecord(defaultAgent);
|
||
pageUiState.pageAiAgentId = agent.id;
|
||
pageUiState.pageAiAcpRuntime = agent.acpRuntime || pageUiState.pageAiAcpRuntime || 'reasonix';
|
||
if (agent.tools) pageUiState.pageAiTools = agent.tools;
|
||
}
|
||
var hermesProfile = String(preferences['ai.agent.hermes.profile_id'] || '').trim();
|
||
if (hermesProfile) pageAiSetActiveProfile(hermesProfile);
|
||
var skillSource = String(preferences['ai.common.skills.active_source'] || '').trim();
|
||
if (skillSource) pageUiState.pageAiActiveSkillSource = skillSource;
|
||
var selected = preferences['ai.common.context_refs.default_selected'];
|
||
if (selected && typeof selected === 'object' && !Array.isArray(selected)) {
|
||
pageUiState.pageAiSelectedContextRefs = Object.assign({}, pageAiEnsureContextRefState(), selected);
|
||
}
|
||
var collapsedSkillGroups = preferences['ai.common.skills.groups.collapsed'];
|
||
if (collapsedSkillGroups && typeof collapsedSkillGroups === 'object' && !Array.isArray(collapsedSkillGroups)) {
|
||
pageUiState.pageAiCollapsedSkillGroups = Object.assign({}, collapsedSkillGroups);
|
||
}
|
||
} catch (_) {}
|
||
renderPageAiControls();
|
||
}
|
||
|
||
function pageAiNormalizeAccessGrant(grant) {
|
||
if (!grant || typeof grant !== 'object') return null;
|
||
var rootUri = String(grant.rootUri || grant.root_uri || '').trim();
|
||
if (!rootUri) return null;
|
||
var permission = String(grant.permission || grant.access || '').trim() || 'read';
|
||
return {
|
||
id: String(grant.id || '').trim(),
|
||
rootUri: rootUri,
|
||
rootPath: String(grant.rootPath || grant.root_path || '').trim(),
|
||
permission: permission === 'read_write' ? 'write' : permission,
|
||
recursive: grant.recursive !== false,
|
||
source: String(grant.source || 'sqlite_directory_grant').trim() || 'sqlite_directory_grant',
|
||
status: String(grant.status || 'active').trim() || 'active'
|
||
};
|
||
}
|
||
|
||
async function pageAiLoadAllowedRoots() {
|
||
if (currentSourceKind() !== 'local_folder') {
|
||
pageUiState.pageAiAllowedRoots = [];
|
||
renderPageAiControls();
|
||
return [];
|
||
}
|
||
try {
|
||
var response = await fetch('/api/user/access-policy', {
|
||
headers: { accept: 'application/json' },
|
||
cache: 'no-store'
|
||
});
|
||
var payload = await response.json().catch(function() { return null; });
|
||
if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'access_policy_failed_' + response.status));
|
||
var grants = pageAiNormalizeArray(payload && (payload.grants || payload.policy && payload.policy.grants));
|
||
var currentRoot = String(currentRootUri() || '').trim();
|
||
pageUiState.pageAiAllowedRoots = grants.map(pageAiNormalizeAccessGrant).filter(Boolean).filter(function(grant) {
|
||
return grant.status === 'active' && (!currentRoot || grant.rootUri === currentRoot);
|
||
});
|
||
pageUiState.pageAiAllowedRootsError = '';
|
||
} catch (error) {
|
||
pageUiState.pageAiAllowedRoots = [];
|
||
pageUiState.pageAiAllowedRootsError = error instanceof Error ? error.message : String(error);
|
||
}
|
||
renderPageAiControls();
|
||
return pageUiState.pageAiAllowedRoots;
|
||
}
|
||
|
||
function updatePageAiTriggerState() {
|
||
var trigger = document.querySelector('[data-testid="wolai-floating-ai"]');
|
||
if (!(trigger instanceof HTMLElement)) return;
|
||
trigger.setAttribute('data-state', pageUiState.pageAiOpen ? 'open' : 'closed');
|
||
trigger.setAttribute('aria-expanded', pageUiState.pageAiOpen ? 'true' : 'false');
|
||
}
|
||
|
||
|
||
|
||
function pageAiOpenLocation(loc) {
|
||
var path = String(loc || '').trim();
|
||
if (!path) return;
|
||
openLocalResourceInActiveTab({ path: path }).then(function(opened) {
|
||
if (!opened) {
|
||
var href = buildLocalFileOpenUrl(path, false);
|
||
if (href) window.open(href, '_blank', 'noopener,noreferrer');
|
||
}
|
||
});
|
||
}
|
||
|
||
function pageAiEvidenceRangeFromParam(value) {
|
||
var text = String(value || '').trim();
|
||
if (!text) return null;
|
||
var match = text.match(/^(\d+)[-:,](\d+)$/);
|
||
if (!match) return text;
|
||
return { start: Number(match[1]), end: Number(match[2]) };
|
||
}
|
||
|
||
function pageAiEvidenceBboxFromParam(value) {
|
||
var text = String(value || '').trim();
|
||
if (!text) return null;
|
||
var parts = text.split(',').map(function(part) { return Number(part.trim()); });
|
||
if (parts.length < 4 || parts.slice(0, 4).some(function(part) { return !Number.isFinite(part); })) return text;
|
||
return { x0: parts[0], y0: parts[1], x1: parts[2], y1: parts[3] };
|
||
}
|
||
|
||
function pageAiDocumentIdFromUrl(url) {
|
||
var prefix = '/documents/';
|
||
if (!url.pathname.startsWith(prefix)) return '';
|
||
try {
|
||
var raw = url.pathname.slice(prefix.length).split('/')[0] || '';
|
||
var decoded = decodeURIComponent(raw);
|
||
if (decoded.indexOf('%') >= 0 || decoded.indexOf('\uFFFD') >= 0) return '';
|
||
return decoded;
|
||
} catch (_error) {
|
||
return '';
|
||
}
|
||
}
|
||
|
||
function pageAiCitationResourcePath(url, rootUri) {
|
||
var explicitPath = String(url.searchParams.get('resourcePath') || '').trim();
|
||
if (explicitPath) return explicitPath;
|
||
var raw = String(url.searchParams.get('resourceTab') || '').trim();
|
||
if (!raw) {
|
||
try {
|
||
var decodedHash = decodeURIComponent(String(url.hash || ''));
|
||
var marker = '#resource-tab-';
|
||
var markerIndex = decodedHash.indexOf(marker);
|
||
if (markerIndex >= 0) raw = decodedHash.slice(markerIndex + marker.length).replace(/^#+/, '').trim();
|
||
} catch (_error) {}
|
||
}
|
||
if (raw.indexOf('::') >= 0) raw = raw.slice(raw.indexOf('::') + 2);
|
||
if (!raw.startsWith('resource:file:')) return '';
|
||
var rest = raw.slice('resource:file:'.length);
|
||
var prefix = String(rootUri || '').trim() + ':';
|
||
if (!prefix.trim() || !rest.startsWith(prefix)) return '';
|
||
return rest.slice(prefix.length).replace(/^\/+/, '');
|
||
}
|
||
|
||
function pageAiSameMnoteOrigin(url) {
|
||
try {
|
||
var current = new URL(window.location.origin);
|
||
if (url.origin === current.origin) return true;
|
||
if (url.hostname === 'mnote.local') return true;
|
||
var localNames = ['localhost', '127.0.0.1', '::1', 'mnote.local'];
|
||
function defaultPort(protocol) {
|
||
return protocol === 'https:' ? '443' : protocol === 'http:' ? '80' : '';
|
||
}
|
||
return localNames.indexOf(url.hostname) >= 0 &&
|
||
localNames.indexOf(current.hostname) >= 0 &&
|
||
String(url.port || defaultPort(url.protocol)) === String(current.port || defaultPort(current.protocol));
|
||
} catch (_error) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
function pageAiPortableCitationUrl(url) {
|
||
try {
|
||
if (url.pathname === '/api/local-folder/files/open') {
|
||
return Boolean(String(url.searchParams.get('rootUri') || '').trim()) &&
|
||
Boolean(String(url.searchParams.get('path') || '').trim());
|
||
}
|
||
if (!url.pathname.startsWith('/documents/')) return false;
|
||
var resourceTab = String(url.searchParams.get('resourceTab') || '').trim();
|
||
return String(url.searchParams.get('sourceKind') || '').trim() === 'local_folder' ||
|
||
Boolean(String(url.searchParams.get('rootUri') || '').trim()) ||
|
||
Boolean(String(url.searchParams.get('resourcePath') || '').trim()) ||
|
||
resourceTab.startsWith('resource:file:');
|
||
} catch (_error) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
function pageAiOpenCitationUrl(href) {
|
||
var url;
|
||
try {
|
||
url = new URL(String(href || ''), window.location.origin);
|
||
} catch (_error) {
|
||
return false;
|
||
}
|
||
var unwrappedHref = pageAiUnwrapSearchCitationUrl(url);
|
||
if (unwrappedHref) return pageAiOpenCitationUrl(unwrappedHref);
|
||
if (!pageAiSameMnoteOrigin(url) && !pageAiPortableCitationUrl(url)) return false;
|
||
if (url.pathname === '/api/local-folder/files/open') {
|
||
var fileRootUri = String(url.searchParams.get('rootUri') || currentRootUri() || '').trim();
|
||
var filePath = String(url.searchParams.get('path') || '').trim();
|
||
if (!filePath) return false;
|
||
void openLocalResourceInActiveTab({
|
||
path: filePath,
|
||
rootUri: fileRootUri,
|
||
documentId: currentDocumentId() || '',
|
||
workspaceId: resolveWorkspaceId(document.body) || '',
|
||
sourceKind: currentSourceKind() || 'local_folder',
|
||
page: url.searchParams.get('page') || undefined,
|
||
bbox: pageAiEvidenceBboxFromParam(url.searchParams.get('bbox')),
|
||
sourceMapPath: String(url.searchParams.get('sourceMapPath') || '').trim(),
|
||
blockId: String(url.searchParams.get('blockId') || '').trim(),
|
||
evidenceText: String(url.searchParams.get('evidenceText') || '').trim(),
|
||
lineRange: pageAiEvidenceRangeFromParam(url.searchParams.get('lineRange')),
|
||
charRange: pageAiEvidenceRangeFromParam(url.searchParams.get('charRange')),
|
||
openTarget: 'active-tab',
|
||
paneRole: 'primary'
|
||
}).then(function(opened) {
|
||
if (!opened) window.open(url.toString(), '_blank', 'noopener,noreferrer');
|
||
});
|
||
return true;
|
||
}
|
||
if (!url.pathname.startsWith('/documents/')) return false;
|
||
var rootUri = String(url.searchParams.get('rootUri') || currentRootUri() || '').trim();
|
||
var resourcePath = pageAiCitationResourcePath(url, rootUri);
|
||
var documentId = pageAiDocumentIdFromUrl(url) || currentDocumentId() || '';
|
||
var workspaceId = String(url.searchParams.get('workspaceId') || resolveWorkspaceId(document.body) || '').trim();
|
||
if (resourcePath) {
|
||
void openLocalResourceInActiveTab({
|
||
path: resourcePath,
|
||
rootUri: rootUri,
|
||
documentId: documentId,
|
||
workspaceId: workspaceId,
|
||
sourceKind: String(url.searchParams.get('sourceKind') || currentSourceKind() || 'local_folder').trim(),
|
||
page: url.searchParams.get('page') || undefined,
|
||
bbox: pageAiEvidenceBboxFromParam(url.searchParams.get('bbox')),
|
||
sourceMapPath: String(url.searchParams.get('sourceMapPath') || '').trim(),
|
||
blockId: String(url.searchParams.get('blockId') || '').trim(),
|
||
evidenceText: String(url.searchParams.get('evidenceText') || '').trim(),
|
||
lineRange: pageAiEvidenceRangeFromParam(url.searchParams.get('lineRange')),
|
||
charRange: pageAiEvidenceRangeFromParam(url.searchParams.get('charRange')),
|
||
openTarget: 'active-tab',
|
||
paneRole: 'primary'
|
||
}).then(function(opened) {
|
||
if (!opened) window.location.assign(url.pathname + url.search + url.hash);
|
||
});
|
||
return true;
|
||
}
|
||
if (typeof window.__mnoteDocumentPaneRuntime?.openPrimaryDocument === 'function') {
|
||
void window.__mnoteDocumentPaneRuntime.openPrimaryDocument({
|
||
documentId: documentId,
|
||
workspaceId: workspaceId,
|
||
sourceKind: String(url.searchParams.get('sourceKind') || currentSourceKind() || '').trim(),
|
||
rootUri: rootUri,
|
||
url: url
|
||
});
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
function pageAiChatOnlyProfileEntries() {
|
||
var profiles = pageAiNormalizeArray(pageUiState.pageAiProfiles);
|
||
return PAGE_AI_CHAT_ONLY_PROFILE_REGISTRY.map(function(spec) {
|
||
var profile = profiles.find(function(candidate) {
|
||
var candidateSpec = pageAiChatOnlyProfileSpec(candidate);
|
||
return candidateSpec && candidateSpec.profileId === spec.profileId;
|
||
});
|
||
return Object.assign({}, profile || {
|
||
profileId: spec.profileId,
|
||
name: spec.profileId,
|
||
alias: spec.label,
|
||
kind: 'shared',
|
||
baseProfile: spec.baseProfile,
|
||
providerKind: spec.providerKind || '',
|
||
readonly: true
|
||
}, { menuLabel: spec.label });
|
||
});
|
||
}
|
||
|
||
function pageAiHermesProfileEntries() {
|
||
var profiles = pageAiNormalizeArray(pageUiState.pageAiProfiles).filter(function(profile) {
|
||
return !pageAiChatOnlyProfileSpec(profile);
|
||
});
|
||
if (profiles.length) return profiles;
|
||
return [{ profileId: pageAiCurrentProfile(), name: pageAiCurrentProfile(), alias: pageAiCurrentProfile(), kind: 'personal' }];
|
||
}
|
||
|
||
|
||
function pageAiNormalizeProfiles(payload) {
|
||
var upstream = pageAiUnwrapUpstream(payload);
|
||
var profiles = pageAiNormalizeArray(upstream && upstream.profiles ? upstream.profiles : upstream);
|
||
return profiles.map(function(profile) {
|
||
var profileId = String(profile && (profile.profileId || profile.id || profile.name) || '').trim();
|
||
var kind = String(profile && profile.kind || profile && profile.profileKind || '').trim();
|
||
var canManageSkills = profile && profile.canManageSkills !== false;
|
||
return {
|
||
profileId: profileId || pageAiProfileValue(profile) || 'default',
|
||
name: profileId || pageAiProfileValue(profile) || 'default',
|
||
active: Boolean(profile && profile.active),
|
||
model: String(profile && profile.model || '').trim(),
|
||
gateway: String(profile && profile.gateway || '').trim(),
|
||
alias: String(profile && (profile.displayName || profile.alias) || '').trim(),
|
||
kind: kind,
|
||
ownerUserId: String(profile && profile.ownerUserId || '').trim(),
|
||
baseProfile: String(profile && profile.baseProfile || '').trim(),
|
||
isolatedProfile: String(profile && profile.isolatedProfile || '').trim(),
|
||
providerKind: String(profile && profile.providerKind || profile && profile.provider_kind || '').trim(),
|
||
canRun: profile ? profile.canRun !== false : true,
|
||
canManageSkills: canManageSkills,
|
||
canManageConfig: profile ? profile.canManageConfig !== false : canManageSkills,
|
||
readonly: profile ? (profile.readonly === true || !canManageSkills) : false,
|
||
grantRole: String(profile && profile.grantRole || '').trim()
|
||
};
|
||
});
|
||
}
|
||
|
||
function pageAiNormalizeSkills(payload) {
|
||
var upstream = pageAiUnwrapUpstream(payload);
|
||
var categories = pageAiNormalizeArray(upstream && upstream.categories ? upstream.categories : []);
|
||
var archived = pageAiNormalizeArray(upstream && upstream.archived ? upstream.archived : []);
|
||
return {
|
||
categories: categories.map(function(category) {
|
||
var skillRows = pageAiNormalizeArray(category && (category.skills || category.capabilities));
|
||
return {
|
||
name: String(category && category.name || '').trim() || 'misc',
|
||
description: String(category && category.description || '').trim(),
|
||
title: String(category && category.title || '').trim(),
|
||
capabilities: pageAiNormalizeArray(category && category.capabilities),
|
||
skills: skillRows.map(function(skill) {
|
||
return {
|
||
name: String(skill && skill.name || '').trim(),
|
||
id: String(skill && skill.id || skill && skill.name || '').trim(),
|
||
title: String(skill && skill.title || skill && skill.name || skill && skill.id || '').trim(),
|
||
description: String(skill && skill.description || '').trim(),
|
||
enabled: skill && skill.enabled !== false,
|
||
toggleable: skill && skill.toggleable !== false,
|
||
source: String(skill && skill.source || 'local').trim() || 'local',
|
||
origin: String(skill && skill.origin || '').trim(),
|
||
createdBy: String(skill && skill.createdBy || '').trim(),
|
||
patchCount: Number(skill && skill.patchCount || 0),
|
||
modified: Boolean(skill && skill.modified),
|
||
builtin: Boolean(skill && skill.builtin),
|
||
configurable: skill && skill.configurable !== false,
|
||
configScope: String(skill && skill.configScope || '').trim(),
|
||
skillKind: String(skill && skill.skillKind || '').trim(),
|
||
profileId: String(skill && skill.profileId || '').trim(),
|
||
toolNames: pageAiNormalizeArray(skill && skill.toolNames),
|
||
tools: pageAiNormalizeArray(skill && skill.tools),
|
||
toolCount: Number(skill && skill.toolCount || 0),
|
||
disabledToolCount: Number(skill && skill.disabledToolCount || 0),
|
||
status: String(skill && skill.status || '').trim(),
|
||
capabilityId: String(skill && skill.capabilityId || '').trim(),
|
||
capabilityKind: String(skill && skill.capabilityKind || '').trim(),
|
||
uiKind: String(skill && skill.uiKind || '').trim(),
|
||
requiresContextRefs: pageAiNormalizeArray(skill && skill.requiresContextRefs),
|
||
readOnly: Boolean(skill && (skill.readOnly || skill.readonly || skill.configurable === false)),
|
||
category: String(skill && skill.category || category && category.name || '').trim(),
|
||
categoryTitle: String(skill && skill.categoryTitle || category && category.title || category && category.name || '').trim()
|
||
};
|
||
})
|
||
};
|
||
}),
|
||
archived: archived.map(function(skill) {
|
||
return {
|
||
name: String(skill && skill.name || '').trim(),
|
||
id: String(skill && skill.id || skill && skill.name || '').trim(),
|
||
title: String(skill && skill.title || skill && skill.name || skill && skill.id || '').trim(),
|
||
description: String(skill && skill.description || '').trim(),
|
||
enabled: skill && skill.enabled !== false,
|
||
toggleable: skill && skill.toggleable !== false,
|
||
source: String(skill && skill.source || 'local').trim() || 'local',
|
||
origin: String(skill && skill.origin || '').trim(),
|
||
createdBy: String(skill && skill.createdBy || '').trim(),
|
||
patchCount: Number(skill && skill.patchCount || 0),
|
||
modified: Boolean(skill && skill.modified),
|
||
builtin: Boolean(skill && skill.builtin),
|
||
configurable: skill && skill.configurable !== false,
|
||
configScope: String(skill && skill.configScope || '').trim(),
|
||
skillKind: String(skill && skill.skillKind || '').trim(),
|
||
profileId: String(skill && skill.profileId || '').trim(),
|
||
toolNames: pageAiNormalizeArray(skill && skill.toolNames),
|
||
tools: pageAiNormalizeArray(skill && skill.tools),
|
||
toolCount: Number(skill && skill.toolCount || 0),
|
||
disabledToolCount: Number(skill && skill.disabledToolCount || 0),
|
||
status: String(skill && skill.status || '').trim(),
|
||
capabilityId: String(skill && skill.capabilityId || '').trim(),
|
||
capabilityKind: String(skill && skill.capabilityKind || '').trim(),
|
||
uiKind: String(skill && skill.uiKind || '').trim(),
|
||
requiresContextRefs: pageAiNormalizeArray(skill && skill.requiresContextRefs),
|
||
categoryTitle: String(skill && skill.categoryTitle || '').trim(),
|
||
readOnly: Boolean(skill && (skill.readOnly || skill.readonly || skill.configurable === false))
|
||
};
|
||
})
|
||
};
|
||
}
|
||
|
||
function pageAiSkillListEntries() {
|
||
var result = [];
|
||
pageAiNormalizeArray(pageUiState.pageAiSkills.categories).forEach(function(category) {
|
||
pageAiNormalizeArray(category.skills).forEach(function(skill) {
|
||
result.push({
|
||
category: category.name,
|
||
name: skill.name,
|
||
id: skill.id || skill.name,
|
||
title: skill.title || skill.name,
|
||
description: skill.description,
|
||
enabled: skill.enabled !== false,
|
||
toggleable: skill.toggleable !== false,
|
||
source: skill.source || 'local',
|
||
origin: skill.origin || '',
|
||
createdBy: skill.createdBy || '',
|
||
patchCount: Number(skill.patchCount || 0),
|
||
modified: Boolean(skill.modified),
|
||
builtin: Boolean(skill.builtin),
|
||
configurable: skill.configurable !== false,
|
||
configScope: skill.configScope || '',
|
||
skillKind: skill.skillKind || '',
|
||
profileId: skill.profileId || '',
|
||
toolNames: skill.toolNames || [],
|
||
tools: skill.tools || [],
|
||
toolCount: Number(skill.toolCount || 0),
|
||
disabledToolCount: Number(skill.disabledToolCount || 0),
|
||
status: skill.status || '',
|
||
capabilityId: skill.capabilityId || '',
|
||
capabilityKind: skill.capabilityKind || '',
|
||
uiKind: skill.uiKind || '',
|
||
requiresContextRefs: skill.requiresContextRefs || [],
|
||
categoryTitle: skill.categoryTitle || category.title || category.name || '',
|
||
readOnly: Boolean(skill.readOnly || skill.readonly || skill.configurable === false)
|
||
});
|
||
});
|
||
});
|
||
return result.concat(pageAiNormalizeArray(pageUiState.pageAiSkills.archived));
|
||
}
|
||
|
||
function pageAiSetActiveProfile(profileName) {
|
||
var next = String(profileName || '').trim() || 'mnoteai';
|
||
pageUiState.pageAiActiveProfileName = next;
|
||
document.documentElement.setAttribute('data-mnote-page-ai-profile', next);
|
||
document.documentElement.setAttribute('data-mnote-page-ai-tool-model', pageAiMnoteToolModel());
|
||
}
|
||
|
||
function pageAiSetRunStatus(status, runId) {
|
||
pageUiState.pageAiRunStatus = status || 'idle';
|
||
pageUiState.pageAiCurrentRunId = runId || pageUiState.pageAiCurrentRunId || '';
|
||
document.documentElement.setAttribute('data-mnote-page-ai-run-status', pageUiState.pageAiRunStatus);
|
||
if (pageUiState.pageAiCurrentRunId) {
|
||
document.documentElement.setAttribute('data-mnote-page-ai-run-id', pageUiState.pageAiCurrentRunId);
|
||
}
|
||
pageAiUpdateActiveRunSnapshotFromStatus(pageUiState.pageAiRunStatus, pageUiState.pageAiCurrentRunId);
|
||
}
|
||
|
||
function pageAiApplyRuntimeState(runtime) {
|
||
if (!runtime || typeof runtime !== 'object') return;
|
||
var status = String(runtime.status || '').trim();
|
||
var runId = String(runtime.runId || runtime.run_id || '').trim();
|
||
if (status) pageAiSetRunStatus(status, runId);
|
||
var queueLength = Number(runtime.queueLength || runtime.queue_length || 0);
|
||
if (Number.isFinite(queueLength)) pageUiState.pageAiQueueLength = Math.max(0, queueLength);
|
||
var toolName = String(runtime.lastToolName || runtime.last_tool_name || '').trim();
|
||
if (toolName) {
|
||
pageUiState.pageAiLastToolCall = {
|
||
event: String(runtime.lastEvent || runtime.last_event || ''),
|
||
name: toolName,
|
||
runId: runId,
|
||
traceId: String(runtime.traceId || runtime.trace_id || ''),
|
||
auditId: String(runtime.lastAuditId || runtime.last_audit_id || '')
|
||
};
|
||
}
|
||
}
|
||
|
||
function pageAiApplyRuntimeStatus(payload) {
|
||
if (!payload || typeof payload !== 'object') return null;
|
||
pageUiState.pageAiRuntimeStatus = payload;
|
||
pageUiState.pageAiRuntimeStatusError = '';
|
||
var activeRun = payload.activeRun && typeof payload.activeRun === 'object' ? payload.activeRun : null;
|
||
if (activeRun && activeRun.hostRunId) {
|
||
pageAiSetRunStatus(String(activeRun.status || 'running'), String(activeRun.hostRunId || ''));
|
||
} else if (pageAiRunStatusIsTerminal(pageUiState.pageAiRunStatus)) {
|
||
pageAiUpdateActiveRunSnapshotFromStatus(pageUiState.pageAiRunStatus, pageUiState.pageAiCurrentRunId);
|
||
} else if (!activeRun && ['running', 'tool_calling', 'queued', 'pending', 'acp_pending'].indexOf(pageUiState.pageAiRunStatus) >= 0) {
|
||
pageAiSetRunStatus('idle', pageUiState.pageAiCurrentRunId);
|
||
}
|
||
var queue = payload.session && payload.session.queue && typeof payload.session.queue === 'object' ? payload.session.queue : null;
|
||
if (queue) {
|
||
var queuedCount = Number(queue.queuedCount || 0);
|
||
pageUiState.pageAiQueueLength = Number.isFinite(queuedCount) ? Math.max(0, queuedCount) : 0;
|
||
if (Array.isArray(queue.items)) {
|
||
pageUiState.pageAiQueuedItems = queue.items.map(function(item) {
|
||
return {
|
||
queueId: String(item.queueId || ''),
|
||
sessionId: String(item.sessionId || payload.mnoteSessionId || ''),
|
||
traceId: String(item.traceId || ''),
|
||
preview: String(item.preview || ''),
|
||
queuedAt: Number(item.queuedAt || Date.now())
|
||
};
|
||
}).filter(function(item) { return item.queueId; });
|
||
}
|
||
}
|
||
var acpSessionId = payload.acp && typeof payload.acp === 'object' ? String(payload.acp.acpSessionId || '') : '';
|
||
var current = pageAiCurrentSession();
|
||
if (current && acpSessionId) {
|
||
current.acpSessionId = acpSessionId;
|
||
current.runtimeMode = String(payload.mode || '');
|
||
current.updatedAt = Date.now();
|
||
pageAiPersistSessions();
|
||
}
|
||
document.documentElement.setAttribute('data-mnote-page-ai-runtime-status-loaded', 'true');
|
||
document.documentElement.setAttribute('data-mnote-page-ai-runtime', String(payload.runtime || ''));
|
||
document.documentElement.setAttribute('data-mnote-page-ai-runtime-mode', String(payload.mode || ''));
|
||
return payload;
|
||
}
|
||
|
||
async function pageAiLoadRuntimeStatus() {
|
||
var params = new URLSearchParams();
|
||
var sessionId = String(pageUiState.pageAiActiveSessionId || '').trim();
|
||
if (sessionId) params.set('sessionId', sessionId);
|
||
var workspaceId = String(resolveWorkspaceId(document.body) || '').trim();
|
||
if (workspaceId) params.set('workspaceId', workspaceId);
|
||
var documentId = String(currentDocumentId() || '').trim();
|
||
if (documentId) params.set('documentId', documentId);
|
||
var profile = String(pageAiCurrentProfile() || '').trim();
|
||
if (profile) params.set('profile', profile);
|
||
var response = await fetch('/api/page-ai/runtime/status?' + params.toString(), {
|
||
headers: { 'accept': 'application/json' }
|
||
});
|
||
var payload = await response.json().catch(function(){ return null; });
|
||
if (!response.ok || !payload || payload.ok !== true) {
|
||
throw new Error(pageAiErrorMessage(payload, 'page_ai_runtime_status_failed_' + response.status));
|
||
}
|
||
return pageAiApplyRuntimeStatus(payload);
|
||
}
|
||
|
||
function pageAiActiveRunSnapshotKey() {
|
||
return pageAiStorageKey() + ':active-run';
|
||
}
|
||
|
||
function pageAiReadActiveRunSnapshot() {
|
||
try {
|
||
var raw = window.localStorage ? window.localStorage.getItem(pageAiActiveRunSnapshotKey()) : '';
|
||
return raw ? JSON.parse(raw) : null;
|
||
} catch (_) {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
function pageAiWriteActiveRunSnapshot(snapshot) {
|
||
if (!snapshot || !snapshot.hostRunId) return;
|
||
try {
|
||
window.localStorage.setItem(pageAiActiveRunSnapshotKey(), JSON.stringify(Object.assign({
|
||
schema: 'mnote.page_ai_active_run_snapshot.v1',
|
||
createdAt: Date.now()
|
||
}, snapshot, {
|
||
updatedAt: Date.now()
|
||
})));
|
||
document.documentElement.setAttribute('data-mnote-page-ai-active-run-snapshot', 'true');
|
||
} catch (_) {}
|
||
}
|
||
|
||
function pageAiClearActiveRunSnapshot(runId) {
|
||
var existing = pageAiReadActiveRunSnapshot();
|
||
if (runId && existing && existing.hostRunId && existing.hostRunId !== runId) return;
|
||
try {
|
||
if (window.localStorage) window.localStorage.removeItem(pageAiActiveRunSnapshotKey());
|
||
document.documentElement.removeAttribute('data-mnote-page-ai-active-run-snapshot');
|
||
} catch (_) {}
|
||
}
|
||
|
||
function pageAiRunStatusIsTerminal(status) {
|
||
return ['completed', 'failed', 'aborted', 'cancelled', 'canceled'].indexOf(String(status || '').trim()) >= 0;
|
||
}
|
||
|
||
function pageAiUpdateActiveRunSnapshotFromStatus(status, runId) {
|
||
var id = String(runId || '').trim();
|
||
if (!id) return;
|
||
if (pageAiRunStatusIsTerminal(status)) {
|
||
pageAiClearActiveRunSnapshot(id);
|
||
return;
|
||
}
|
||
if (['running', 'tool_calling', 'queued', 'pending', 'interrupted', 'acp_pending'].indexOf(String(status || '').trim()) < 0) return;
|
||
var previous = pageAiReadActiveRunSnapshot();
|
||
var lastSeq = previous && previous.hostRunId === id ? String(previous.lastSeq || '000000000000000000') : '000000000000000000';
|
||
pageAiWriteActiveRunSnapshot({
|
||
hostRunId: id,
|
||
sessionId: pageUiState.pageAiActiveSessionId || '',
|
||
lastSeq: lastSeq,
|
||
status: status || 'running'
|
||
});
|
||
}
|
||
|
||
async function pageAiCheckAndResumeActiveRun() {
|
||
var snapshot = pageAiReadActiveRunSnapshot();
|
||
var sessionId = snapshot && snapshot.sessionId ? String(snapshot.sessionId || '').trim() : '';
|
||
var run = await pageAiCheckActiveRun(sessionId || undefined);
|
||
if (run && sessionId && pageUiState.pageAiActiveSessionId !== sessionId) {
|
||
pageAiSetActiveSession(sessionId);
|
||
}
|
||
if (run) await pageAiResumeActiveRunJournal(run);
|
||
if (!run && snapshot && snapshot.hostRunId) {
|
||
pageAiStopStreamingAssistantMessage(snapshot.hostRunId);
|
||
pageAiClearActiveRunSnapshot(snapshot.hostRunId);
|
||
pageUiState.pageAiBusy = false;
|
||
pageAiSetRunStatus('idle', snapshot.hostRunId);
|
||
}
|
||
return run;
|
||
}
|
||
|
||
function pageAiApplyQueuedRun(payload) {
|
||
if (!payload || payload.queued !== true) return false;
|
||
var queueId = String(payload.queueId || payload.queue_id || '').trim();
|
||
var queueLength = Number(payload.queueLength || payload.queue_length || 0);
|
||
pageUiState.pageAiQueueLength = Number.isFinite(queueLength) ? Math.max(0, queueLength) : 1;
|
||
if (queueId) {
|
||
pageUiState.pageAiQueuedItems = pageUiState.pageAiQueuedItems.filter(function(item) {
|
||
return item.queueId !== queueId;
|
||
}).concat([{
|
||
queueId: queueId,
|
||
sessionId: String(payload.sessionId || payload.session_id || pageUiState.pageAiActiveSessionId || ''),
|
||
traceId: String(payload.traceId || payload.trace_id || ''),
|
||
queuedAt: Number(payload.queuedAt || payload.queued_at || Date.now())
|
||
}]);
|
||
}
|
||
pageAiSetRunStatus('queued', pageUiState.pageAiCurrentRunId);
|
||
return true;
|
||
}
|
||
|
||
function pageAiRefreshClientQueueState() {
|
||
var clientItems = pageAiNormalizeArray(pageUiState.pageAiClientQueuedPrompts);
|
||
var serverItems = pageAiNormalizeArray(pageUiState.pageAiQueuedItems)
|
||
.filter(function(item) { return String(item.queueId || '').indexOf('client_') !== 0; });
|
||
pageUiState.pageAiQueuedItems = serverItems.concat(clientItems.map(function(item) {
|
||
return {
|
||
queueId: item.queueId,
|
||
sessionId: item.sessionId || pageUiState.pageAiActiveSessionId || '',
|
||
traceId: item.traceId || '',
|
||
preview: item.preview || item.prompt || '',
|
||
queuedAt: item.queuedAt || Date.now()
|
||
};
|
||
}));
|
||
pageUiState.pageAiQueueLength = pageUiState.pageAiQueuedItems.length;
|
||
}
|
||
|
||
function pageAiEnqueueClientPrompt(prompt) {
|
||
var queueId = 'client_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2, 8);
|
||
pageUiState.pageAiClientQueuedPrompts.push({
|
||
queueId: queueId,
|
||
prompt: prompt,
|
||
preview: String(prompt || '').slice(0, 120),
|
||
sessionId: pageUiState.pageAiActiveSessionId || '',
|
||
queuedAt: Date.now()
|
||
});
|
||
pageAiRefreshClientQueueState();
|
||
pageAiSetRunStatus('queued', pageUiState.pageAiCurrentRunId);
|
||
return queueId;
|
||
}
|
||
|
||
function pageAiRemoveClientQueuedPrompt(queueId) {
|
||
var before = pageUiState.pageAiClientQueuedPrompts.length;
|
||
pageUiState.pageAiClientQueuedPrompts = pageUiState.pageAiClientQueuedPrompts.filter(function(item) {
|
||
return item.queueId !== queueId;
|
||
});
|
||
pageAiRefreshClientQueueState();
|
||
return before !== pageUiState.pageAiClientQueuedPrompts.length;
|
||
}
|
||
|
||
function pageAiStartNextClientQueuedPrompt() {
|
||
if (pageUiState.pageAiBusy) return false;
|
||
if (['running', 'tool_calling', 'pending', 'acp_pending'].indexOf(pageUiState.pageAiRunStatus) >= 0) return false;
|
||
var next = pageUiState.pageAiClientQueuedPrompts.shift();
|
||
if (!next) {
|
||
pageAiRefreshClientQueueState();
|
||
return false;
|
||
}
|
||
pageAiRefreshClientQueueState();
|
||
window.setTimeout(function() {
|
||
void sendPageAiMessage(next.prompt);
|
||
}, 0);
|
||
return true;
|
||
}
|
||
|
||
function pageAiUnwrapSearchCitationUrl(url) {
|
||
try {
|
||
var raw = '';
|
||
['wd', 'q', 'query'].some(function(key) {
|
||
raw = String(url.searchParams.get(key) || '').trim();
|
||
return Boolean(raw);
|
||
});
|
||
if (!raw) return '';
|
||
if (/^documents\//i.test(raw)) raw = '/' + raw;
|
||
if (!/^\/documents\//i.test(raw) && !/^https?:\/\/[^/]+\/documents\//i.test(raw) && !/^mnote:\/\/open/i.test(raw)) return '';
|
||
if (/^mnote:\/\/open/i.test(raw)) return raw;
|
||
var nested = new URL(raw, window.location.origin);
|
||
if (!nested.pathname.startsWith('/documents/')) return '';
|
||
['sourceKind', 'rootUri', 'workspaceId', 'resourceTab', 'resourcePath', 'page', 'bbox', 'sourceMapPath', 'blockId', 'evidenceText', 'lineRange', 'charRange'].forEach(function(key) {
|
||
if (nested.searchParams.get(key)) return;
|
||
var value = String(url.searchParams.get(key) || '').trim();
|
||
if (value) nested.searchParams.set(key, value);
|
||
});
|
||
return nested.pathname + nested.search;
|
||
} catch (_error) {
|
||
return '';
|
||
}
|
||
}
|
||
|
||
function pageAiPreviewValue(value) {
|
||
if (value == null || value === '') return '';
|
||
if (typeof value === 'string') return value.length > 120 ? value.slice(0, 120) + '…' : value;
|
||
try {
|
||
var text = JSON.stringify(value);
|
||
return text.length > 120 ? text.slice(0, 120) + '…' : text;
|
||
} catch (_) {
|
||
return String(value);
|
||
}
|
||
}
|
||
|
||
function pageAiNormalizeToolName(name) {
|
||
return String(name || '').trim().replace(/_/g, '.');
|
||
}
|
||
|
||
|
||
function pageAiParentRelativePath(path) {
|
||
var normalized = String(path || '').trim().replace(/\\/g, '/').replace(/^\/+/, '');
|
||
if (!normalized || normalized.indexOf('/') < 0) return '';
|
||
return normalized.split('/').slice(0, -1).join('/');
|
||
}
|
||
|
||
function pageAiChangedFileRelativePath(file) {
|
||
return String(file && (file.path || file.relativePath || file.relative_path || file.filePath || file.file_path || '') || '').trim();
|
||
}
|
||
|
||
function pageAiDispatchReceiptRefresh(receipt, changedFiles, fallbackAudit, runId, traceId) {
|
||
var refresh = receipt && typeof receipt === 'object' && receipt.refresh && typeof receipt.refresh === 'object'
|
||
? receipt.refresh
|
||
: {};
|
||
var files = pageAiNormalizeArray(
|
||
receipt && typeof receipt === 'object' && receipt.changedFiles !== undefined
|
||
? receipt.changedFiles
|
||
: changedFiles
|
||
);
|
||
if (files.length) {
|
||
var changedPaths = files.map(function(file) {
|
||
var relativePath = pageAiChangedFileRelativePath(file);
|
||
return {
|
||
relativePath: relativePath,
|
||
changeType: String(file && (file.changeType || file.change_type || 'modified') || 'modified')
|
||
};
|
||
}).filter(function(item) { return item.relativePath; });
|
||
var affectedParents = changedPaths.map(function(item) {
|
||
return { relativePath: pageAiParentRelativePath(item.relativePath), reason: 'agent-run-receipt' };
|
||
}).filter(function(item, index, list) {
|
||
return index === list.findIndex(function(candidate) { return candidate.relativePath === item.relativePath; });
|
||
});
|
||
if (changedPaths.length) {
|
||
var rootUri = String((receipt && receipt.rootUri) || (fallbackAudit && fallbackAudit.rootUri) || currentRootUri() || '');
|
||
var syntheticPayload = {
|
||
schema: 'mnote.local_folder.watch_batch.v1',
|
||
source: 'agent_run_receipt',
|
||
runId: runId,
|
||
rootUri: rootUri,
|
||
changedPaths: changedPaths,
|
||
affectedParents: affectedParents
|
||
};
|
||
document.documentElement.setAttribute('data-mnote-page-ai-receipt-filetree-refresh', 'true');
|
||
if (window.__mnoteLocalFolderEventBus && typeof window.__mnoteLocalFolderEventBus.emitSyntheticWatchBatch === 'function') {
|
||
window.__mnoteLocalFolderEventBus.emitSyntheticWatchBatch({
|
||
source: 'synthetic_page_ai_receipt',
|
||
reason: 'agent_run_receipt',
|
||
rootUri: rootUri,
|
||
workspaceId: resolveWorkspaceId(document.body),
|
||
payload: syntheticPayload
|
||
});
|
||
} else {
|
||
window.dispatchEvent(new CustomEvent('tree:local-folder-watch-batch', {
|
||
detail: { payload: syntheticPayload }
|
||
}));
|
||
}
|
||
}
|
||
}
|
||
if (refresh.touchesCurrentFile === true) {
|
||
var documentId = String(refresh.currentDocumentId || receipt && receipt.documentId || currentDocumentId() || '').trim();
|
||
document.documentElement.setAttribute('data-mnote-page-ai-receipt-current-refresh', 'true');
|
||
window.dispatchEvent(new CustomEvent('mnote:page-ai-tool-write-completed', {
|
||
detail: {
|
||
toolName: 'agent.run_receipt',
|
||
normalizedToolName: 'agent.run_receipt',
|
||
documentId: documentId,
|
||
workspaceId: String(receipt && receipt.workspaceId || resolveWorkspaceId(document.body) || ''),
|
||
rootUri: String(receipt && receipt.rootUri || fallbackAudit && fallbackAudit.rootUri || currentRootUri() || ''),
|
||
changedFiles: files,
|
||
receipt: receipt || null,
|
||
runId: runId,
|
||
traceId: traceId,
|
||
toolCallId: runId + ':agent.run_receipt'
|
||
}
|
||
}));
|
||
}
|
||
}
|
||
|
||
function pageAiToolEventDeepFindString(value, keys, depth) {
|
||
if (!value || typeof value !== 'object' || depth > 5) return '';
|
||
for (var index = 0; index < keys.length; index += 1) {
|
||
var key = keys[index];
|
||
if (Object.prototype.hasOwnProperty.call(value, key) && typeof value[key] === 'string' && value[key].trim()) {
|
||
return value[key].trim();
|
||
}
|
||
}
|
||
if (Array.isArray(value)) {
|
||
for (var arrayIndex = 0; arrayIndex < value.length; arrayIndex += 1) {
|
||
var fromArray = pageAiToolEventDeepFindString(value[arrayIndex], keys, depth + 1);
|
||
if (fromArray) return fromArray;
|
||
}
|
||
return '';
|
||
}
|
||
var preferred = ['audit', 'args', 'arguments', 'input', 'result', 'summary', 'output', 'upstream'];
|
||
for (var prefIndex = 0; prefIndex < preferred.length; prefIndex += 1) {
|
||
var child = value[preferred[prefIndex]];
|
||
var fromPreferred = pageAiToolEventDeepFindString(child, keys, depth + 1);
|
||
if (fromPreferred) return fromPreferred;
|
||
}
|
||
var objectKeys = Object.keys(value);
|
||
for (var objectIndex = 0; objectIndex < objectKeys.length; objectIndex += 1) {
|
||
var fromObject = pageAiToolEventDeepFindString(value[objectKeys[objectIndex]], keys, depth + 1);
|
||
if (fromObject) return fromObject;
|
||
}
|
||
return '';
|
||
}
|
||
|
||
function pageAiNotifyToolWriteCompleted(toolName, toolEvent, runId, runTraceId) {
|
||
var normalizedTool = pageAiNormalizeToolName(toolName);
|
||
var writesCurrentPage = [
|
||
'mnote.page.save',
|
||
'mnote.page.update.title',
|
||
'mnote.page.update.options',
|
||
'mnote.doc.apply.block.ops',
|
||
'mnote.block.replace',
|
||
'mnote.block.insert.after',
|
||
'mnote.block.delete',
|
||
'mnote.block.move.after'
|
||
].indexOf(normalizedTool) >= 0 || [
|
||
'mnote.page.update_title',
|
||
'mnote.page.update_options',
|
||
'mnote.doc.apply_block_ops',
|
||
'mnote.block.replace',
|
||
'mnote.block.insert_after',
|
||
'mnote.block.delete',
|
||
'mnote.block.move_after'
|
||
].indexOf(String(toolName || '').trim()) >= 0;
|
||
if (!writesCurrentPage) return;
|
||
var documentId = pageAiToolEventDeepFindString(toolEvent, ['documentId', 'document_id'], 0) || currentDocumentId();
|
||
var workspaceId = pageAiToolEventDeepFindString(toolEvent, ['workspaceId', 'workspace_id'], 0) || resolveWorkspaceId(document.body);
|
||
try {
|
||
window.dispatchEvent(new CustomEvent('mnote:page-ai-tool-write-completed', {
|
||
detail: {
|
||
toolName: toolName,
|
||
normalizedToolName: normalizedTool,
|
||
documentId: documentId,
|
||
workspaceId: workspaceId,
|
||
runId: String(toolEvent && (toolEvent.run_id || toolEvent.runId) || runId || ''),
|
||
traceId: String(toolEvent && (toolEvent.trace_id || toolEvent.traceId) || runTraceId || ''),
|
||
toolCallId: String(toolEvent && (toolEvent.tool_call_id || toolEvent.toolCallId || toolEvent.id) || '')
|
||
}
|
||
}));
|
||
} catch (error) {
|
||
console.warn('mnote 页面 AI 写入刷新事件派发失败', error);
|
||
}
|
||
}
|
||
|
||
function pageAiApplyToolEvent(eventName, payloadText, runId, runTraceId) {
|
||
var toolEvent = null;
|
||
try {
|
||
toolEvent = JSON.parse(payloadText || 'null');
|
||
} catch (_) {
|
||
toolEvent = {};
|
||
}
|
||
var rawToolName = toolEvent && (toolEvent.name || toolEvent.tool || toolEvent.toolName || '');
|
||
var toolName = String(rawToolName || eventName);
|
||
var toolCallId = String(toolEvent && (toolEvent.tool_call_id || toolEvent.toolCallId || toolEvent.id) || (runId + ':' + toolName));
|
||
var eventStatus = String(toolEvent && toolEvent.status || '').trim();
|
||
var status = eventName === 'tool.completed'
|
||
? (toolEvent && toolEvent.error ? 'failed' : 'completed')
|
||
: (eventName === 'tool.failed' ? 'failed' : (eventStatus || 'running'));
|
||
if (status === 'in_progress' || status === 'pending') status = 'running';
|
||
var traceId = String(toolEvent && (toolEvent.trace_id || toolEvent.traceId) || runTraceId || '');
|
||
var auditId = String(toolEvent && (toolEvent.audit_id || toolEvent.auditId) || '');
|
||
var argsSummary = pageAiPreviewValue(toolEvent && (toolEvent.arguments || toolEvent.args || toolEvent.input));
|
||
var resultSource = toolEvent && (toolEvent.summary || toolEvent.result || toolEvent.output);
|
||
if (!resultSource && status === 'failed') {
|
||
resultSource = [toolEvent && toolEvent.code, toolEvent && toolEvent.error].filter(Boolean).join(' ');
|
||
}
|
||
var resultSummary = pageAiPreviewValue(resultSource);
|
||
pageUiState.pageAiLastToolCall = {
|
||
event: eventName,
|
||
name: toolName,
|
||
runId: String(toolEvent && (toolEvent.run_id || toolEvent.runId) || runId),
|
||
traceId: traceId,
|
||
auditId: auditId
|
||
};
|
||
var rawLocations = toolEvent && toolEvent.locations;
|
||
var locations = Array.isArray(rawLocations) ? rawLocations.filter(function(l) { return typeof l === 'string' || (typeof l === 'object' && l && l.path); }).map(function(l) { return typeof l === 'string' ? l : l.path; }) : [];
|
||
var existing = pageUiState.pageAiMessages.find(function(item) {
|
||
return item.role === 'tool' && item.toolCallId === toolCallId;
|
||
});
|
||
if (!rawToolName && existing && existing.toolName) toolName = existing.toolName;
|
||
if (!existing) {
|
||
existing = {
|
||
role: 'tool',
|
||
content: toolName,
|
||
toolCallId: toolCallId,
|
||
toolName: toolName,
|
||
toolKind: String(toolEvent && toolEvent.kind || ''),
|
||
status: status,
|
||
argsSummary: '',
|
||
resultSummary: '',
|
||
locations: locations,
|
||
traceId: traceId,
|
||
auditId: auditId
|
||
};
|
||
pageUiState.pageAiMessages.push(existing);
|
||
}
|
||
existing.content = toolName;
|
||
existing.toolName = toolName;
|
||
existing.toolKind = String(toolEvent && toolEvent.kind || existing.toolKind || '');
|
||
existing.status = status;
|
||
existing.traceId = traceId || existing.traceId || '';
|
||
existing.auditId = auditId || existing.auditId || '';
|
||
if (argsSummary) existing.argsSummary = argsSummary;
|
||
if (resultSummary) existing.resultSummary = resultSummary;
|
||
if (locations.length) existing.locations = locations;
|
||
existing.rawOutput = toolEvent && toolEvent.output;
|
||
existing.rawResult = resultSource;
|
||
if (status === 'completed') {
|
||
pageAiNotifyToolWriteCompleted(toolName, toolEvent, runId, runTraceId);
|
||
}
|
||
return existing;
|
||
}
|
||
|
||
async function pageAiCancelQueuedRun(queueId) {
|
||
queueId = String(queueId || '').trim();
|
||
if (!queueId) return;
|
||
if (queueId.indexOf('client_') === 0) {
|
||
if (pageAiRemoveClientQueuedPrompt(queueId)) {
|
||
pageUiState.pageAiMessages.push({ role: 'assistant', content: '已取消一条 AI 队列项。' });
|
||
}
|
||
renderPageAiConversation();
|
||
renderPageAiControls();
|
||
return;
|
||
}
|
||
var item = pageUiState.pageAiQueuedItems.find(function(entry) {
|
||
return entry.queueId === queueId;
|
||
});
|
||
var sessionId = String(item && item.sessionId || pageUiState.pageAiActiveSessionId || '').trim();
|
||
if (!sessionId) return;
|
||
try {
|
||
var response = await fetch('/api/page-ai/sessions/' + encodeURIComponent(sessionId) + '/queue/' + encodeURIComponent(queueId), {
|
||
method: 'DELETE',
|
||
headers: { 'accept': 'application/json' }
|
||
});
|
||
var payload = await response.json().catch(function(){ return null; });
|
||
if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'queue_cancel_failed_' + response.status));
|
||
pageUiState.pageAiQueuedItems = pageUiState.pageAiQueuedItems.filter(function(entry) {
|
||
return entry.queueId !== queueId;
|
||
});
|
||
var queueLength = Number(payload && (payload.queueLength || payload.queue_length || 0));
|
||
pageUiState.pageAiQueueLength = Number.isFinite(queueLength) ? Math.max(0, queueLength) : pageUiState.pageAiQueuedItems.length;
|
||
pageUiState.pageAiMessages.push({ role: 'assistant', content: '已取消一条 AI 队列项。' });
|
||
} catch (error) {
|
||
pageUiState.pageAiMessages.push({
|
||
role: 'assistant',
|
||
content: '取消 AI 队列项失败:' + (error instanceof Error ? error.message : String(error))
|
||
});
|
||
}
|
||
renderPageAiConversation();
|
||
renderPageAiControls();
|
||
}
|
||
|
||
async function pageAiResetRuntime() {
|
||
var sessionId = String(pageUiState.pageAiActiveSessionId || '').trim();
|
||
if (!sessionId) return;
|
||
try {
|
||
var response = await fetch('/api/page-ai/runtime/reset', {
|
||
method: 'POST',
|
||
headers: { 'content-type': 'application/json' },
|
||
body: JSON.stringify({
|
||
sessionId: sessionId,
|
||
runtime: pageUiState.pageAiAcpRuntime || '',
|
||
profile: pageAiCurrentProfile()
|
||
})
|
||
});
|
||
var payload = await response.json().catch(function(){ return null; });
|
||
if (!response.ok || !payload || payload.ok !== true) {
|
||
throw new Error(pageAiErrorMessage(payload, 'runtime_reset_failed_' + response.status));
|
||
}
|
||
pageUiState.pageAiRuntimeStatus = null;
|
||
pageUiState.pageAiRuntimeStatusError = '';
|
||
pageUiState.pageAiMessages.push({
|
||
role: 'assistant',
|
||
content: '已重置 runtime;MNote 会话历史保留,外部 provider 会话未删除。'
|
||
});
|
||
await pageAiLoadRuntimeStatus().catch(function(error) {
|
||
pageUiState.pageAiRuntimeStatusError = error instanceof Error ? error.message : String(error);
|
||
return null;
|
||
});
|
||
} catch (error) {
|
||
pageUiState.pageAiRuntimeStatusError = error instanceof Error ? error.message : String(error);
|
||
}
|
||
renderPageAiConversation();
|
||
renderPageAiControls();
|
||
}
|
||
|
||
function pageAiSetContextScope(scope) {
|
||
var next = String(scope || '').trim() || 'page';
|
||
pageUiState.pageAiContextScope = next;
|
||
document.documentElement.setAttribute('data-mnote-page-ai-context-scope', next);
|
||
}
|
||
|
||
|
||
function pageAiHermesSettingsUrl() {
|
||
var configured = String(window.__mnoteHermesSettingsUrl || '').trim();
|
||
return configured || '';
|
||
}
|
||
|
||
function pageAiOpenHermesSettings() {
|
||
var url = pageAiHermesSettingsUrl();
|
||
if (url) {
|
||
window.open(url, '_blank', 'noopener,noreferrer');
|
||
return;
|
||
}
|
||
pageUiState.pageAiProfileError = '未配置 Hermes 设置入口:请设置 MNOTE_WEB_HERMES_UPSTREAM_URL。';
|
||
renderPageAiControls();
|
||
}
|
||
|
||
function pageAiNormalizeTools(payload) {
|
||
var upstream = pageAiUnwrapUpstream(payload) || {};
|
||
var tools = pageAiNormalizeArray(upstream.tools || upstream);
|
||
return tools.map(function(tool) {
|
||
var name = String(tool && (tool.name || tool.toolName || tool.tool) || '').trim();
|
||
if (!name) return null;
|
||
return {
|
||
name: name,
|
||
description: String(tool && tool.description || '').trim(),
|
||
scope: String(tool && (tool.scope || tool.requiredScope || '') || '').trim(),
|
||
kind: String(tool && (tool.kind || tool.mode || '') || '').trim(),
|
||
status: String(tool && (tool.status || tool.permission || 'available') || '').trim(),
|
||
enabled: tool && tool.enabled !== false,
|
||
unavailableReason: String(tool && (tool.unavailableReason || tool.reason || '') || '').trim()
|
||
};
|
||
}).filter(Boolean);
|
||
}
|
||
|
||
function pageAiErrorMessage(payload, fallback) {
|
||
if (!payload || typeof payload !== 'object') return fallback;
|
||
return String(payload.message || payload.error || payload.code || fallback || '').trim() || fallback;
|
||
}
|
||
|
||
function pageAiNormalizeGatewayHealth(payload) {
|
||
var upstream = pageAiUnwrapUpstream(payload) || payload || {};
|
||
return {
|
||
ok: Boolean(upstream.ok),
|
||
profile: upstream.profile || null,
|
||
gateway: upstream.gateway || null,
|
||
suggestions: pageAiNormalizeArray(upstream.suggestions).map(function(item) {
|
||
return String(item || '').trim();
|
||
}).filter(Boolean)
|
||
};
|
||
}
|
||
|
||
function pageAiSetDraftForSection(section, value) {
|
||
pageUiState.pageAiProfileMemoryDrafts[section] = String(value == null ? '' : value);
|
||
}
|
||
|
||
function pageAiApplyProfileMemory(payload) {
|
||
var upstream = pageAiUnwrapUpstream(payload) || {};
|
||
pageUiState.pageAiProfileMemory = {
|
||
memory: String(upstream.memory || ''),
|
||
user: String(upstream.user || ''),
|
||
soul: String(upstream.soul || '')
|
||
};
|
||
pageUiState.pageAiProfileMemoryDrafts = {
|
||
memory: pageUiState.pageAiProfileMemory.memory,
|
||
user: pageUiState.pageAiProfileMemory.user,
|
||
soul: pageUiState.pageAiProfileMemory.soul
|
||
};
|
||
}
|
||
|
||
async function pageAiLoadTools() {
|
||
try {
|
||
var descriptor = pageAiAgentDescriptor(pageAiCurrentAgentId());
|
||
if (descriptor && pageUiState.pageAiAgentDescriptorsLoaded) {
|
||
pageUiState.pageAiTools = pageAiNormalizeArray(descriptor.tools);
|
||
pageUiState.pageAiToolsError = '';
|
||
renderPageAiControls();
|
||
return;
|
||
}
|
||
var response = await fetch('/api/hermes/client/tools?scope=mnote&profile=' + encodeURIComponent(pageAiCurrentProfile()), {
|
||
headers: { 'accept': 'application/json' }
|
||
});
|
||
var payload = await response.json().catch(function(){ return null; });
|
||
if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'tools_failed_' + response.status));
|
||
pageUiState.pageAiTools = pageAiNormalizeTools(payload);
|
||
pageUiState.pageAiToolsError = '';
|
||
} catch (error) {
|
||
pageUiState.pageAiToolsError = error instanceof Error ? error.message : String(error);
|
||
}
|
||
renderPageAiControls();
|
||
}
|
||
|
||
async function pageAiLoadGatewayHealth() {
|
||
try {
|
||
var response = await fetch('/api/hermes/client/gateway/health?profile=' + encodeURIComponent(pageAiCurrentProfile()), {
|
||
headers: { 'accept': 'application/json' }
|
||
});
|
||
var payload = await response.json().catch(function(){ return null; });
|
||
if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'gateway_health_failed_' + response.status));
|
||
pageUiState.pageAiGatewayHealth = pageAiNormalizeGatewayHealth(payload);
|
||
pageUiState.pageAiGatewayHealthError = '';
|
||
} catch (error) {
|
||
pageUiState.pageAiGatewayHealthError = error instanceof Error ? error.message : String(error);
|
||
}
|
||
renderPageAiControls();
|
||
}
|
||
|
||
async function pageAiStopRun() {
|
||
var runId = String(pageUiState.pageAiCurrentRunId || '').trim();
|
||
if (!runId || pageUiState.pageAiRunStatus === 'idle' || pageUiState.pageAiRunStatus === 'completed') return;
|
||
pageUiState.pageAiStoppedRunIds[runId] = true;
|
||
try {
|
||
if (pageAiBoardFirstEnabled()) {
|
||
var boardResponse = await fetch('/api/page-ai/board/runs/' + encodeURIComponent(runId) + '/cancel', {
|
||
method: 'POST',
|
||
headers: { accept: 'application/json' }
|
||
});
|
||
var boardPayload = await boardResponse.json().catch(function(){ return null; });
|
||
if (!boardResponse.ok) throw new Error(pageAiErrorMessage(boardPayload, 'board_run_cancel_failed_' + boardResponse.status));
|
||
pageAiSetRunStatus('cancelled', runId);
|
||
pageUiState.pageAiMessages.push({ role: 'assistant', content: '已请求停止当前 Board run。', boardRunId: runId });
|
||
return;
|
||
}
|
||
var response = await fetch('/api/hermes/client/runs/' + encodeURIComponent(runId) + '/abort', {
|
||
method: 'POST',
|
||
headers: { 'content-type': 'application/json' },
|
||
body: JSON.stringify({
|
||
sessionId: pageUiState.pageAiActiveSessionId,
|
||
profile: pageAiRunProfile(),
|
||
acpRuntime: pageUiState.pageAiAcpRuntime || 'reasonix',
|
||
reason: 'page_ai_user_stop'
|
||
})
|
||
});
|
||
var payload = await response.json().catch(function(){ return null; });
|
||
if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'run_abort_failed_' + response.status));
|
||
pageAiApplyRuntimeState(payload && payload.runtime);
|
||
pageAiSetRunStatus('aborted', runId);
|
||
pageUiState.pageAiMessages.push({ role: 'assistant', content: '已请求停止当前 AI run。' });
|
||
} catch (error) {
|
||
pageAiSetRunStatus('failed', runId);
|
||
pageUiState.pageAiMessages.push({
|
||
role: 'assistant',
|
||
content: '停止 AI run 失败:' + (error instanceof Error ? error.message : String(error))
|
||
});
|
||
}
|
||
renderPageAiControls();
|
||
renderPageAiConversation();
|
||
}
|
||
|
||
|
||
function pageAiLoadSkillCatalog(runtime, profile) {
|
||
var params = new URLSearchParams();
|
||
params.set('runtime', runtime);
|
||
var endpoint = '/api/hermes/client/skills';
|
||
if (runtime === 'mnote') {
|
||
endpoint = '/api/hermes/client/capabilities';
|
||
params.set('agentId', 'reasonix');
|
||
if (profile) params.set('profile', profile);
|
||
} else if (runtime === 'reasonix') {
|
||
params.set('runtime', 'reasonix');
|
||
} else if (runtime === 'hermes' && profile) {
|
||
params.set('profileId', profile);
|
||
}
|
||
return fetch(endpoint + '?' + params.toString(), {
|
||
headers: { 'accept': 'application/json' }
|
||
}).then(function(response) {
|
||
return response.json().catch(function(){ return null; }).then(function(payload) {
|
||
if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'capabilities_failed_' + response.status));
|
||
return pageAiNormalizeSkills(payload);
|
||
});
|
||
});
|
||
}
|
||
|
||
async function pageAiLoadProfiles() {
|
||
try {
|
||
var response = await fetch('/api/ai/agent-profiles?agentId=hermes', {
|
||
headers: { 'accept': 'application/json' }
|
||
});
|
||
var payload = await response.json().catch(function(){ return null; });
|
||
if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'profiles_failed_' + response.status));
|
||
var profiles = pageAiNormalizeProfiles(payload);
|
||
pageUiState.pageAiProfiles = profiles.length ? profiles : [{ profileId: 'shared_lite', name: 'shared_lite', alias: 'Lite', kind: 'shared', readonly: true, canManageSkills: false }];
|
||
try {
|
||
var runtimeResponse = await fetch('/api/hermes/client/profiles', { headers: { 'accept': 'application/json' } });
|
||
var runtimePayload = await runtimeResponse.json().catch(function(){ return null; });
|
||
var acpRuntimes = Array.isArray(runtimePayload && runtimePayload.acpRuntimes) ? runtimePayload.acpRuntimes : [];
|
||
pageUiState.pageAiAcpRuntimes = pageAiNormalizeAcpRuntimes(acpRuntimes);
|
||
} catch (_) {
|
||
pageUiState.pageAiAcpRuntimes = pageAiNormalizeAcpRuntimes(pageUiState.pageAiAcpRuntimes);
|
||
}
|
||
var current = pageAiCurrentProfile();
|
||
var hasCurrent = pageUiState.pageAiProfiles.some(function(profile) { return pageAiProfileValue(profile) === current; });
|
||
var personal = pageUiState.pageAiProfiles.find(function(profile) { return profile.kind === 'personal'; });
|
||
var active = pageAiProfileValue(pageUiState.pageAiProfiles.find(function(profile) { return profile.active; }));
|
||
pageAiSetActiveProfile(hasCurrent && current !== 'default' ? current : (active || pageAiProfileValue(personal) || pageAiProfileValue(pageUiState.pageAiProfiles[0]) || current));
|
||
pageUiState.pageAiProfileError = '';
|
||
void pageAiLoadAgentDescriptors();
|
||
void pageAiLoadGatewayHealth();
|
||
} catch (error) {
|
||
pageUiState.pageAiProfileError = error instanceof Error ? error.message : String(error);
|
||
if (!pageUiState.pageAiProfiles.length) pageUiState.pageAiProfiles = [{ profileId: 'shared_lite', name: 'shared_lite', alias: 'Lite', kind: 'shared', readonly: true, canManageSkills: false }];
|
||
pageUiState.pageAiAcpRuntimes = pageAiNormalizeAcpRuntimes(pageUiState.pageAiAcpRuntimes);
|
||
pageAiSetActiveProfile(pageAiCurrentProfile());
|
||
void pageAiLoadAgentDescriptors();
|
||
}
|
||
renderPageAiProviderButtons();
|
||
renderPageAiControls();
|
||
}
|
||
|
||
async function pageAiSwitchProfile(profileName) {
|
||
var next = String(profileName || '').trim();
|
||
if (!next) return;
|
||
var previousSkillSource = pageAiCurrentSkillSource();
|
||
pageAiSetActiveProfile(next);
|
||
pageAiPersistRawAiPreference('ai.agent.hermes.profile_id', next);
|
||
if (previousSkillSource.indexOf('hermes:') === 0) {
|
||
pageUiState.pageAiActiveSkillSource = 'hermes:' + next;
|
||
pageAiPersistAiPreference('skills.active_source', pageUiState.pageAiActiveSkillSource);
|
||
}
|
||
pageUiState.pageAiSkills = { categories: [], archived: [] };
|
||
pageUiState.pageAiSkillCatalogs = Object.assign({}, pageUiState.pageAiSkillCatalogs || {}, {
|
||
hermes: { categories: [], archived: [] }
|
||
});
|
||
pageUiState.pageAiSkillError = '';
|
||
renderPageAiProviderButtons();
|
||
renderPageAiControls();
|
||
try {
|
||
pageUiState.pageAiSessions = pageUiState.pageAiSessions.filter(function(session) {
|
||
return session && session.profile === next;
|
||
});
|
||
pageUiState.pageAiActiveSessionId = '';
|
||
pageUiState.pageAiMessages = [];
|
||
pageAiPersistSessions();
|
||
await pageAiEnsureHermesSession(true);
|
||
await pageAiLoadProfileMemory();
|
||
await pageAiLoadAgentDescriptors();
|
||
await pageAiLoadSkills();
|
||
await pageAiLoadTools();
|
||
await pageAiLoadGatewayHealth();
|
||
renderPageAiControls();
|
||
} catch (error) {
|
||
pageUiState.pageAiProfileError = error instanceof Error ? error.message : String(error);
|
||
}
|
||
renderPageAiConversation();
|
||
}
|
||
|
||
async function pageAiLoadProfileMemory() {
|
||
try {
|
||
var response = await fetch('/api/hermes/client/profile-memory?profileId=' + encodeURIComponent(pageAiCurrentProfile()), {
|
||
headers: { 'accept': 'application/json' }
|
||
});
|
||
var payload = await response.json().catch(function(){ return null; });
|
||
if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'profile_memory_failed_' + response.status));
|
||
pageAiApplyProfileMemory(payload);
|
||
pageUiState.pageAiProfileMemoryError = '';
|
||
} catch (error) {
|
||
pageUiState.pageAiProfileMemoryError = error instanceof Error ? error.message : String(error);
|
||
}
|
||
renderPageAiControls();
|
||
renderPageAiConversation();
|
||
}
|
||
|
||
async function pageAiSaveProfileMemory(section) {
|
||
var normalized = String(section || '').trim();
|
||
if (['memory', 'user', 'soul'].indexOf(normalized) < 0) return;
|
||
var content = String(pageUiState.pageAiProfileMemoryDrafts[normalized] || '');
|
||
try {
|
||
var response = await fetch('/api/hermes/client/profile-memory', {
|
||
method: 'POST',
|
||
headers: { 'content-type': 'application/json' },
|
||
body: JSON.stringify({
|
||
profileId: pageAiCurrentProfile(),
|
||
section: normalized,
|
||
content: content
|
||
})
|
||
});
|
||
var payload = await response.json().catch(function(){ return null; });
|
||
if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'profile_memory_save_failed_' + response.status));
|
||
pageUiState.pageAiProfileMemory[normalized] = content;
|
||
pageUiState.pageAiProfileMemoryError = '';
|
||
document.documentElement.setAttribute('data-mnote-page-ai-memory-saved', normalized);
|
||
} catch (error) {
|
||
pageUiState.pageAiProfileMemoryError = error instanceof Error ? error.message : String(error);
|
||
}
|
||
renderPageAiControls();
|
||
renderPageAiConversation();
|
||
}
|
||
|
||
async function pageAiLoadSkills() {
|
||
var requestedSource = pageAiCurrentSkillSource();
|
||
var sourceParts = pageAiSkillSourceParts(requestedSource);
|
||
var loadSeq = (Number(pageUiState.pageAiSkillLoadSeq || 0) || 0) + 1;
|
||
pageUiState.pageAiSkillLoadSeq = loadSeq;
|
||
try {
|
||
var catalog = await pageAiLoadSkillCatalog(sourceParts.group, sourceParts.profile || pageAiCurrentProfile());
|
||
if (pageUiState.pageAiSkillLoadSeq !== loadSeq
|
||
|| pageAiCurrentSkillSource() !== requestedSource) return;
|
||
pageUiState.pageAiSkillCatalogs = Object.assign({}, pageUiState.pageAiSkillCatalogs || {}, {
|
||
[sourceParts.group]: catalog,
|
||
[requestedSource]: catalog
|
||
});
|
||
pageUiState.pageAiSkills = catalog;
|
||
pageUiState.pageAiSkillError = '';
|
||
} catch (error) {
|
||
if (pageUiState.pageAiSkillLoadSeq !== loadSeq
|
||
|| pageAiCurrentSkillSource() !== requestedSource) return;
|
||
pageUiState.pageAiSkillError = error instanceof Error ? error.message : String(error);
|
||
pageUiState.pageAiSkills = { categories: [], archived: [] };
|
||
pageUiState.pageAiSkillCatalogs = pageUiState.pageAiSkillCatalogs || { mnote: { categories: [], archived: [] }, reasonix: { categories: [], archived: [] }, hermes: { categories: [], archived: [] } };
|
||
}
|
||
renderPageAiControls();
|
||
renderPageAiConversation();
|
||
}
|
||
|
||
async function pageAiToggleSkill(skillName, enabled, group, profile) {
|
||
var name = String(skillName || '').trim();
|
||
if (!name) return;
|
||
var skillGroup = String(group || 'hermes').trim() || 'hermes';
|
||
var skillProfile = String(profile || pageAiCurrentProfile() || '').trim();
|
||
if (skillGroup === 'reasonix') {
|
||
pageAiSetSkillPreference(skillGroup, name, Boolean(enabled), skillProfile);
|
||
document.documentElement.setAttribute('data-mnote-page-ai-skill-toggled', skillGroup + ':' + name);
|
||
renderPageAiControls();
|
||
return;
|
||
}
|
||
if (skillGroup === 'mnote') {
|
||
try {
|
||
var mnoteResponse = await fetch('/api/hermes/client/capabilities/toggle', {
|
||
method: 'PUT',
|
||
headers: { 'content-type': 'application/json' },
|
||
body: JSON.stringify({
|
||
runtime: 'mnote',
|
||
profile: skillProfile || pageAiCurrentProfile(),
|
||
id: name,
|
||
enabled: Boolean(enabled)
|
||
})
|
||
});
|
||
var mnotePayload = await mnoteResponse.json().catch(function(){ return null; });
|
||
if (!mnoteResponse.ok) throw new Error(pageAiErrorMessage(mnotePayload, 'mnote_capability_toggle_failed_' + mnoteResponse.status));
|
||
pageAiSetSkillPreference(skillGroup, name, Boolean(enabled), skillProfile);
|
||
pageAiNormalizeArray(pageUiState.pageAiSkillCatalogs && pageUiState.pageAiSkillCatalogs.mnote && pageUiState.pageAiSkillCatalogs.mnote.categories).forEach(function(category) {
|
||
pageAiNormalizeArray(category.skills).forEach(function(skill) {
|
||
if (skill.id === name || skill.name === name) {
|
||
skill.enabled = Boolean(enabled);
|
||
skill.status = enabled ? 'available' : 'disabled';
|
||
pageAiNormalizeArray(skill.tools).forEach(function(tool) {
|
||
tool.enabled = Boolean(enabled);
|
||
tool.status = enabled ? 'available' : 'disabled';
|
||
});
|
||
skill.disabledToolCount = enabled ? 0 : Number(skill.toolCount || pageAiNormalizeArray(skill.tools).length || 0);
|
||
}
|
||
});
|
||
});
|
||
document.documentElement.setAttribute('data-mnote-page-ai-skill-toggled', skillGroup + ':' + name);
|
||
} catch (error) {
|
||
pageUiState.pageAiSkillError = error instanceof Error ? error.message : String(error);
|
||
}
|
||
renderPageAiControls();
|
||
return;
|
||
}
|
||
var previous = null;
|
||
pageAiToggleableSkillEntries('hermes', skillProfile || pageAiCurrentProfile()).forEach(function(skill) {
|
||
if (skill.id === name || skill.name === name) previous = skill.enabled !== false;
|
||
});
|
||
try {
|
||
var response = await fetch('/api/hermes/client/skills/toggle', {
|
||
method: 'PUT',
|
||
headers: { 'content-type': 'application/json' },
|
||
body: JSON.stringify({
|
||
profile: skillProfile || pageAiCurrentProfile(),
|
||
profileId: skillProfile || pageAiCurrentProfile(),
|
||
skillKind: 'hermes_profile',
|
||
name: name,
|
||
enabled: Boolean(enabled)
|
||
})
|
||
});
|
||
var payload = await response.json().catch(function(){ return null; });
|
||
if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'skill_toggle_failed_' + response.status));
|
||
var hermesCatalog = pageUiState.pageAiSkillCatalogs && pageUiState.pageAiSkillCatalogs.hermes
|
||
? pageUiState.pageAiSkillCatalogs.hermes
|
||
: pageUiState.pageAiSkills;
|
||
pageAiNormalizeArray(hermesCatalog.categories).forEach(function(category) {
|
||
pageAiNormalizeArray(category.skills).forEach(function(skill) {
|
||
if (skill.name === name || skill.id === name) skill.enabled = Boolean(enabled);
|
||
});
|
||
});
|
||
pageUiState.pageAiSkills = hermesCatalog;
|
||
pageAiSetSkillPreference('hermes', name, Boolean(enabled), skillProfile || pageAiCurrentProfile());
|
||
document.documentElement.setAttribute('data-mnote-page-ai-skill-toggled', name);
|
||
pageUiState.pageAiSkillError = '';
|
||
} catch (error) {
|
||
pageUiState.pageAiSkillError = error instanceof Error ? error.message : String(error);
|
||
if (previous != null) {
|
||
var rollbackCatalog = pageUiState.pageAiSkillCatalogs && pageUiState.pageAiSkillCatalogs.hermes
|
||
? pageUiState.pageAiSkillCatalogs.hermes
|
||
: pageUiState.pageAiSkills;
|
||
pageAiNormalizeArray(rollbackCatalog.categories).forEach(function(category) {
|
||
pageAiNormalizeArray(category.skills).forEach(function(skill) {
|
||
if (skill.name === name || skill.id === name) skill.enabled = previous;
|
||
});
|
||
});
|
||
}
|
||
}
|
||
renderPageAiControls();
|
||
renderPageAiConversation();
|
||
}
|
||
|
||
async function pageAiToggleTool(toolName, enabled) {
|
||
var name = String(toolName || '').trim();
|
||
if (!name) return;
|
||
var previous = null;
|
||
pageAiNormalizeArray(pageUiState.pageAiTools).forEach(function(tool) {
|
||
if (tool.name === name && previous == null) previous = tool.enabled !== false;
|
||
});
|
||
try {
|
||
var response = await fetch('/api/hermes/client/tools/toggle', {
|
||
method: 'PUT',
|
||
headers: { 'content-type': 'application/json' },
|
||
body: JSON.stringify({
|
||
profile: pageAiCurrentProfile(),
|
||
name: name,
|
||
enabled: Boolean(enabled)
|
||
})
|
||
});
|
||
var payload = await response.json().catch(function(){ return null; });
|
||
if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'tool_toggle_failed_' + response.status));
|
||
pageAiNormalizeArray(pageUiState.pageAiTools).forEach(function(tool) {
|
||
if (tool.name === name) {
|
||
tool.enabled = Boolean(enabled);
|
||
tool.status = Boolean(enabled) ? 'available' : 'disabled';
|
||
tool.unavailableReason = Boolean(enabled) ? '' : '当前 Hermes profile 已关闭该 mnote tool';
|
||
}
|
||
});
|
||
document.documentElement.setAttribute('data-mnote-page-ai-tool-toggled', name);
|
||
pageUiState.pageAiToolsError = '';
|
||
} catch (error) {
|
||
pageUiState.pageAiToolsError = error instanceof Error ? error.message : String(error);
|
||
if (previous != null) {
|
||
pageAiNormalizeArray(pageUiState.pageAiTools).forEach(function(tool) {
|
||
if (tool.name === name) tool.enabled = previous;
|
||
});
|
||
}
|
||
}
|
||
renderPageAiControls();
|
||
}
|
||
|
||
|
||
function openPageAiDrawer() {
|
||
if (pageAiOpencodeHostEnabled()) {
|
||
pageAiEnsureContextRefState();
|
||
void pageAiLoadAllowedRoots().then(function() {
|
||
pageAiRenderOpencodeHostChrome();
|
||
}).catch(function() {
|
||
pageAiRenderOpencodeHostChrome();
|
||
});
|
||
var opencodeDrawer = pageAiEnsureOpencodeHostDrawer();
|
||
pageAiApplyDrawerWidth(opencodeDrawer);
|
||
opencodeDrawer.hidden = false;
|
||
pageUiState.pageAiOpen = true;
|
||
updatePageAiTriggerState();
|
||
void pageAiLoadOpencodeStatus();
|
||
return;
|
||
}
|
||
pageUiState.pageAiAgentId = pageAiCurrentAgentId();
|
||
pageAiEnsureContextRefState();
|
||
pageAiLoadSessions();
|
||
pageAiRestorePermissionRequests();
|
||
renderPageAiSuggestions();
|
||
renderPageAiConversation();
|
||
renderPageAiProviderButtons();
|
||
renderPageAiControls();
|
||
pageAiApplyBoardFirstShell();
|
||
var drawer = ensurePageAiDrawer();
|
||
pageAiApplyDrawerWidth(drawer);
|
||
drawer.hidden = false;
|
||
pageUiState.pageAiOpen = true;
|
||
updatePageAiTriggerState();
|
||
void pageAiCheckAndResumeActiveRun().catch(function(error) {
|
||
pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
|
||
renderPageAiControls();
|
||
}).then(function() {
|
||
return pageAiLoadRuntimeStatus().catch(function(error) {
|
||
pageUiState.pageAiRuntimeStatusError = error instanceof Error ? error.message : String(error);
|
||
renderPageAiControls();
|
||
pageAiApplyBoardFirstShell();
|
||
return null;
|
||
});
|
||
});
|
||
void pageAiLoadBackendSessions().then(function() {
|
||
return pageAiCheckAndResumeActiveRun();
|
||
}).catch(function(error) {
|
||
pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
|
||
renderPageAiControls();
|
||
pageAiApplyBoardFirstShell();
|
||
});
|
||
Promise.all([
|
||
pageAiLoadProfiles(),
|
||
pageAiLoadProfileMemory(),
|
||
pageAiLoadAgentDescriptors(),
|
||
pageAiLoadSkills(),
|
||
pageAiLoadTools(),
|
||
pageAiLoadGatewayHealth(),
|
||
pageAiLoadAiPreferences(),
|
||
pageAiLoadBoardCatalog(),
|
||
pageAiLoadAllowedRoots(),
|
||
pageAiLoadBackendSessions(),
|
||
pageAiLoadRuntimeStatus()
|
||
]).then(function() {
|
||
renderPageAiControls();
|
||
pageAiApplyBoardFirstShell();
|
||
}).then(function() {
|
||
return pageAiCheckAndResumeActiveRun().catch(function(error) {
|
||
pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
|
||
renderPageAiControls();
|
||
pageAiApplyBoardFirstShell();
|
||
return null;
|
||
});
|
||
}).then(function() {
|
||
if (!pageAiBoardFirstEnabled()) return pageAiRestoreHermesSession();
|
||
return null;
|
||
}).then(function() {
|
||
renderPageAiControls();
|
||
pageAiApplyBoardFirstShell();
|
||
}).catch(function(error) {
|
||
pageUiState.pageAiMessages.push({
|
||
role: 'assistant',
|
||
content: '当前 AI 不可用:' + (error instanceof Error ? error.message : String(error))
|
||
});
|
||
renderPageAiConversation();
|
||
renderPageAiControls();
|
||
pageAiApplyBoardFirstShell();
|
||
});
|
||
}
|
||
|
||
function closePageAiDrawer() {
|
||
var drawer = document.querySelector('[data-testid="wolai-page-ai-drawer"]');
|
||
if (drawer instanceof HTMLElement) drawer.hidden = true;
|
||
pageUiState.pageAiOpen = false;
|
||
updatePageAiTriggerState();
|
||
}
|
||
|
||
function isPageAiDrawerOpen() {
|
||
var drawer = document.querySelector('[data-testid="wolai-page-ai-drawer"]');
|
||
return drawer instanceof HTMLElement && !drawer.hidden;
|
||
}
|
||
|
||
async function streamPageAiResponse(response, onEvent) {
|
||
if (!response.body || typeof response.body.getReader !== 'function') return;
|
||
var reader = response.body.getReader();
|
||
var decoder = new TextDecoder();
|
||
var buffer = '';
|
||
while (true) {
|
||
var chunk = await reader.read();
|
||
if (chunk.done) break;
|
||
buffer += decoder.decode(chunk.value, { stream: true });
|
||
var frames = buffer.split('\n\n');
|
||
buffer = frames.pop() || '';
|
||
frames.forEach(function(frame) {
|
||
var eventName = '';
|
||
var dataLines = [];
|
||
frame.split('\n').forEach(function(line) {
|
||
if (line.startsWith('event:')) eventName = line.slice(6).trim();
|
||
if (line.startsWith('data:')) dataLines.push(line.slice(5).trim());
|
||
});
|
||
var payloadText = dataLines.join('\n');
|
||
if (!eventName && payloadText) {
|
||
try {
|
||
var parsed = JSON.parse(payloadText);
|
||
eventName = parsed && parsed.event ? String(parsed.event) : '';
|
||
} catch (_) {}
|
||
}
|
||
if (eventName) onEvent(eventName, payloadText);
|
||
});
|
||
}
|
||
}
|
||
|
||
function pageAiDecodeDeltaText(payloadText) {
|
||
try {
|
||
var payload = JSON.parse(payloadText || 'null');
|
||
return String((payload && (payload.text || payload.delta)) || '');
|
||
} catch (_) {
|
||
return String(payloadText || '');
|
||
}
|
||
}
|
||
|
||
function pageAiEventSeqFromPayloadText(payloadText) {
|
||
try {
|
||
var payload = JSON.parse(payloadText || 'null');
|
||
return String((payload && payload.seq) || '').trim();
|
||
} catch (_) {
|
||
return '';
|
||
}
|
||
}
|
||
|
||
function pageAiTrackStreamingRunEvent(runId, eventName, payloadText) {
|
||
var seq = pageAiEventSeqFromPayloadText(payloadText);
|
||
if (!seq) return;
|
||
pageAiWriteActiveRunSnapshot({
|
||
hostRunId: runId,
|
||
sessionId: pageUiState.pageAiActiveSessionId || '',
|
||
lastSeq: seq,
|
||
status: pageAiRunStatusIsTerminal(eventName) ? pageUiState.pageAiRunStatus : (pageUiState.pageAiRunStatus || 'running')
|
||
});
|
||
document.documentElement.setAttribute('data-mnote-page-ai-active-run-last-seq', seq);
|
||
}
|
||
|
||
function pageAiEnsureStreamingAssistantMessage(runId) {
|
||
var id = String(runId || pageUiState.pageAiCurrentRunId || '');
|
||
var existing = pageUiState.pageAiMessages.find(function(item) {
|
||
return item.role === 'assistant' && item.streaming === true && item.runId === id;
|
||
});
|
||
if (existing) return existing;
|
||
existing = {
|
||
role: 'assistant',
|
||
content: '',
|
||
runId: id,
|
||
streaming: true
|
||
};
|
||
pageUiState.pageAiMessages.push(existing);
|
||
return existing;
|
||
}
|
||
|
||
function pageAiAppendStreamingAssistantDelta(runId, deltaText) {
|
||
var delta = String(deltaText || '');
|
||
if (!delta) return '';
|
||
var message = pageAiEnsureStreamingAssistantMessage(runId);
|
||
message.content = String(message.content || '') + delta;
|
||
pageAiSyncCurrentSessionMessages();
|
||
renderPageAiConversation();
|
||
return message.content;
|
||
}
|
||
|
||
function pageAiAppendCitationSection(content, citations) {
|
||
var text = String(content || '');
|
||
var unique = [];
|
||
pageAiNormalizeArray(citations).forEach(function(value) {
|
||
var citation = String(value || '').trim();
|
||
if (!citation || unique.indexOf(citation) >= 0) return;
|
||
unique.push(citation);
|
||
});
|
||
var missing = unique.filter(function(citation) {
|
||
return text.indexOf(citation) < 0;
|
||
});
|
||
if (!missing.length) return text;
|
||
return text.replace(/\s+$/g, '') + '\n\n**引用**\n' + missing.map(function(citation) {
|
||
return '- ' + citation;
|
||
}).join('\n');
|
||
}
|
||
|
||
function pageAiPromptNeedsKnowledgeRagCitations(prompt) {
|
||
var text = searchText(prompt);
|
||
if (!text) return false;
|
||
var asksCitation = ['链接', '引用', '来源', '出处', '证据', '定位', 'link', 'citation', 'source'].some(function(word) {
|
||
return text.indexOf(word) >= 0;
|
||
});
|
||
if (!asksCitation) return false;
|
||
return ['lightrag', '资料库', '知识库', 'rag', '文献', '论文', '保护基'].some(function(word) {
|
||
return text.indexOf(word) >= 0;
|
||
});
|
||
}
|
||
|
||
function pageAiKnowledgeRagFallbackQuery(prompt) {
|
||
var text = searchText(prompt);
|
||
if (!text) return '';
|
||
var afterColon = text.split(/[::]/).slice(1).join(':').trim();
|
||
var candidate = afterColon || text;
|
||
candidate = candidate
|
||
.replace(/^(请|请用|帮我|帮忙|用)?(资料库|知识库|lightrag|rag)?(搜索|检索|查找|查询|回答|说明|解释|总结)?/i, '')
|
||
.replace(/(请)?(给出|给我|附上|提供)?(链接|引用|来源|出处|证据|定位).*$/i, '')
|
||
.trim();
|
||
var stopIndex = candidate.search(/(在|的|是|有哪些|有什么|如何|怎么|用于|用途|作用|资料|文献|论文)/);
|
||
if (stopIndex > 0) candidate = candidate.slice(0, stopIndex).trim();
|
||
var cjkMatch = candidate.match(/[A-Za-z0-9\u4e00-\u9fff·α-ωΑ-Ω\-]{2,24}/);
|
||
if (cjkMatch && cjkMatch[0]) return cjkMatch[0];
|
||
return text;
|
||
}
|
||
|
||
function pageAiCleanDegradedCitationNotes(content) {
|
||
return String(content || '')
|
||
.split('\n')
|
||
.filter(function(line) {
|
||
var text = String(line || '');
|
||
return text.indexOf('来源定位降级') < 0 && text.toLowerCase().indexOf('locator degraded') < 0;
|
||
})
|
||
.join('\n')
|
||
.replace(/\n{3,}/g, '\n\n')
|
||
.trim();
|
||
}
|
||
|
||
function pageAiCitationPrecisionLabel(value) {
|
||
var precision = searchText(value || '').toLowerCase();
|
||
if (precision === 'bbox') return '精确定位';
|
||
if (precision === 'paragraph') return '段落级';
|
||
if (precision === 'file') return '文件级';
|
||
return '';
|
||
}
|
||
|
||
function pageAiCitationSourceTitle(citation) {
|
||
if (!citation || typeof citation !== 'object') return '';
|
||
var path = searchText(citation.sourceRootRelativePath || citation.sourcePath || citation.filePath || citation.lightRagFilePath || '');
|
||
if (path) {
|
||
var parts = path.split('/').filter(Boolean);
|
||
return parts[parts.length - 1] || path;
|
||
}
|
||
var markdown = searchText(citation.citationMarkdown || '');
|
||
var labelMatch = markdown.match(/^\[([^\]]+)\]/);
|
||
return labelMatch ? labelMatch[1] : '';
|
||
}
|
||
|
||
function pageAiCitationDisplayMarkdown(value) {
|
||
if (typeof value === 'string') return value.trim();
|
||
if (!value || typeof value !== 'object') return '';
|
||
var markdown = searchText(value.citationMarkdown || '');
|
||
var citationId = searchText(value.citationLabel || (value.citationId ? '[' + value.citationId + ']' : ''));
|
||
var title = pageAiCitationSourceTitle(value);
|
||
var headingPath = pageAiNormalizeArray(value.headingPath).map(function(item) {
|
||
return searchText(item);
|
||
}).filter(Boolean).join(' > ');
|
||
var precisionLabel = pageAiCitationPrecisionLabel(value.locatorPrecision);
|
||
var quote = searchText(value.displayQuote || value.quote || '');
|
||
if (quote.length > 120) quote = quote.slice(0, 117) + '...';
|
||
var head = markdown || title;
|
||
if (citationId && head.indexOf(citationId) < 0) head = citationId + ' ' + head;
|
||
var details = [];
|
||
if (precisionLabel) details.push(precisionLabel);
|
||
if (headingPath) details.push(headingPath);
|
||
if (quote) details.push(quote);
|
||
return [head].concat(details).filter(Boolean).join(' · ').trim();
|
||
}
|
||
|
||
function pageAiFilterCitationCards(values) {
|
||
var cards = pageAiNormalizeArray(values);
|
||
var hasPreciseCitation = cards.some(function(value) {
|
||
if (typeof value === 'string') return value.indexOf('来源定位降级') < 0;
|
||
return value && typeof value === 'object' && searchText(value.citationMarkdown || '') && value.locatorDegraded !== true;
|
||
});
|
||
var seen = {};
|
||
return cards.map(pageAiCitationDisplayMarkdown).filter(function(card, index) {
|
||
var source = cards[index];
|
||
if (!card || seen[card]) return false;
|
||
if (hasPreciseCitation) {
|
||
if (typeof source === 'string' && source.indexOf('来源定位降级') >= 0) return false;
|
||
if (source && typeof source === 'object' && source.locatorDegraded === true) return false;
|
||
}
|
||
seen[card] = true;
|
||
return true;
|
||
}).slice(0, 5);
|
||
}
|
||
|
||
function pageAiCollectKnowledgeRagApiCitations(payload) {
|
||
var structuredCitations = pageAiNormalizeArray(payload && payload.citations);
|
||
if (structuredCitations.length) {
|
||
return pageAiFilterCitationCards(structuredCitations);
|
||
}
|
||
var references = pageAiNormalizeArray(payload && payload.references);
|
||
return pageAiFilterCitationCards(references);
|
||
}
|
||
|
||
async function pageAiAppendKnowledgeRagFallbackCitations(runId, promptText) {
|
||
if (!pageAiPromptNeedsKnowledgeRagCitations(promptText)) return;
|
||
var rootUri = currentRootUri();
|
||
if (!rootUri) return;
|
||
var fallbackQuery = pageAiKnowledgeRagFallbackQuery(promptText);
|
||
if (!fallbackQuery) return;
|
||
try {
|
||
var response = await fetch('/api/knowledge-rag/search', {
|
||
method: 'POST',
|
||
headers: { 'content-type': 'application/json', 'accept': 'application/json' },
|
||
body: JSON.stringify({
|
||
workspaceId: resolveWorkspaceId(document.body) || undefined,
|
||
rootUri: rootUri,
|
||
query: fallbackQuery,
|
||
mode: 'mix',
|
||
topK: 12,
|
||
chunkTopK: 24,
|
||
includeChunkContent: true
|
||
})
|
||
});
|
||
var payload = await response.json().catch(function() { return null; });
|
||
if (!response.ok) return;
|
||
var citations = pageAiCollectKnowledgeRagApiCitations(payload);
|
||
if (!citations.length) return;
|
||
var message = pageUiState.pageAiMessages.find(function(item) {
|
||
return item.role === 'assistant' && item.runId === runId;
|
||
}) || pageUiState.pageAiMessages.filter(function(item) { return item.role === 'assistant'; }).slice(-1)[0];
|
||
if (!message) return;
|
||
var hasPrecise = citations.some(function(citation) { return citation.indexOf('来源定位降级') < 0; });
|
||
var baseContent = hasPrecise ? pageAiCleanDegradedCitationNotes(message.content) : String(message.content || '');
|
||
message.content = pageAiAppendCitationSection(baseContent, citations);
|
||
pageAiSyncCurrentSessionMessages();
|
||
pageAiPersistSessions();
|
||
renderPageAiConversation();
|
||
} catch (error) {
|
||
console.warn('MNote Page AI 自动追加 LightRAG 引用失败', error);
|
||
}
|
||
}
|
||
|
||
function pageAiFinishStreamingAssistantMessage(runId, finalText, promptText, citations) {
|
||
var id = String(runId || pageUiState.pageAiCurrentRunId || '');
|
||
var message = pageUiState.pageAiMessages.find(function(item) {
|
||
return item.role === 'assistant' && item.streaming === true && item.runId === id;
|
||
});
|
||
var text = String(finalText || (message && message.content) || '');
|
||
var hasPreciseCitation = pageAiNormalizeArray(citations).some(function(citation) {
|
||
return String(citation || '').indexOf('来源定位降级') < 0;
|
||
});
|
||
var contentBase = humanizePageAiResponse(text, promptText);
|
||
if (hasPreciseCitation) contentBase = pageAiCleanDegradedCitationNotes(contentBase);
|
||
var content = pageAiAppendCitationSection(contentBase, citations);
|
||
if (message) {
|
||
message.content = content;
|
||
message.streaming = false;
|
||
} else if (content) {
|
||
pageUiState.pageAiMessages.push({
|
||
role: 'assistant',
|
||
content: content,
|
||
runId: id
|
||
});
|
||
}
|
||
pageAiSyncCurrentSessionMessages();
|
||
pageAiPersistSessions();
|
||
void pageAiAppendKnowledgeRagFallbackCitations(id, promptText);
|
||
}
|
||
|
||
function pageAiStopStreamingAssistantMessage(runId) {
|
||
var id = String(runId || pageUiState.pageAiCurrentRunId || '');
|
||
var changed = false;
|
||
pageUiState.pageAiMessages.forEach(function(message) {
|
||
if (message && message.role === 'assistant' && message.streaming === true && (!id || message.runId === id)) {
|
||
message.streaming = false;
|
||
changed = true;
|
||
}
|
||
});
|
||
if (!changed) return;
|
||
pageAiSyncCurrentSessionMessages();
|
||
pageAiPersistSessions();
|
||
renderPageAiConversation();
|
||
}
|
||
|
||
function pageAiToolOutputText(value) {
|
||
var parts = [];
|
||
function visit(node) {
|
||
if (node == null) return;
|
||
if (typeof node === 'string') {
|
||
parts.push(node);
|
||
return;
|
||
}
|
||
if (typeof node === 'number' || typeof node === 'boolean') return;
|
||
if (Array.isArray(node)) {
|
||
node.forEach(visit);
|
||
return;
|
||
}
|
||
if (typeof node !== 'object') return;
|
||
if (typeof node.text === 'string') parts.push(node.text);
|
||
if (typeof node.content === 'string') parts.push(node.content);
|
||
if (node.content && typeof node.content === 'object') visit(node.content);
|
||
if (node.output && typeof node.output === 'object') visit(node.output);
|
||
if (node.result && typeof node.result === 'object') visit(node.result);
|
||
}
|
||
visit(value);
|
||
return parts.join('\n');
|
||
}
|
||
|
||
function pageAiParseJsonMaybe(value) {
|
||
if (value && typeof value === 'object') return value;
|
||
var text = String(value || '').trim();
|
||
if (!text || (text[0] !== '{' && text[0] !== '[')) return null;
|
||
try {
|
||
return JSON.parse(text);
|
||
} catch (_error) {
|
||
var firstLine = text.split('\n')[0].trim();
|
||
if (firstLine && firstLine !== text && (firstLine[0] === '{' || firstLine[0] === '[')) {
|
||
try {
|
||
return JSON.parse(firstLine);
|
||
} catch (_lineError) {}
|
||
}
|
||
return null;
|
||
}
|
||
}
|
||
|
||
function pageAiCollectCitationMarkdowns(value) {
|
||
var citations = [];
|
||
var seen = {};
|
||
function add(value) {
|
||
var citation = String(value || '').trim();
|
||
if (!citation || seen[citation]) return;
|
||
seen[citation] = true;
|
||
citations.push(citation);
|
||
}
|
||
function visit(node) {
|
||
if (node == null) return;
|
||
if (typeof node === 'string') {
|
||
var parsed = pageAiParseJsonMaybe(node);
|
||
if (parsed) visit(parsed);
|
||
return;
|
||
}
|
||
if (Array.isArray(node)) {
|
||
node.forEach(visit);
|
||
return;
|
||
}
|
||
if (typeof node !== 'object') return;
|
||
if (typeof node.citationMarkdown === 'string') add(pageAiCitationDisplayMarkdown(node));
|
||
if (Array.isArray(node.citationMarkdowns)) {
|
||
node.citationMarkdowns.forEach(function(value) {
|
||
if (typeof value === 'string') add(value);
|
||
else if (value && typeof value === 'object') add(pageAiCitationDisplayMarkdown(value));
|
||
else visit(value);
|
||
});
|
||
}
|
||
Object.keys(node).forEach(function(key) {
|
||
visit(node[key]);
|
||
});
|
||
}
|
||
visit(value);
|
||
return citations;
|
||
}
|
||
|
||
function pageAiCollectKnowledgeRagCitations(toolItem, toolEvent) {
|
||
var toolName = String((toolItem && toolItem.toolName) || (toolEvent && (toolEvent.name || toolEvent.tool || toolEvent.toolName)) || '');
|
||
var sources = [
|
||
toolEvent,
|
||
toolItem,
|
||
toolEvent && toolEvent.output,
|
||
toolEvent && toolEvent.result,
|
||
toolEvent && toolEvent.summary,
|
||
toolItem && toolItem.rawOutput,
|
||
toolItem && toolItem.rawResult,
|
||
toolItem && toolItem.resultSummary
|
||
];
|
||
var citations = [];
|
||
var outputText = '';
|
||
sources.forEach(function(source) {
|
||
citations = citations.concat(pageAiCollectCitationMarkdowns(source));
|
||
var sourceText = pageAiToolOutputText(source);
|
||
if (sourceText) outputText += '\n' + sourceText;
|
||
var parsed = pageAiParseJsonMaybe(sourceText);
|
||
if (parsed) citations = citations.concat(pageAiCollectCitationMarkdowns(parsed));
|
||
});
|
||
var looksLikeKnowledgeRag = toolName.indexOf('knowledge_rag') >= 0 ||
|
||
toolName.indexOf('knowledge.rag') >= 0 ||
|
||
outputText.indexOf('mnote.knowledge_rag') >= 0 ||
|
||
outputText.indexOf('uiCitations') >= 0;
|
||
if (!looksLikeKnowledgeRag && citations.length === 0) return [];
|
||
var hasPreciseCitation = citations.some(function(value) {
|
||
return String(value || '').indexOf('来源定位降级') < 0;
|
||
});
|
||
var seen = {};
|
||
return citations.filter(function(value) {
|
||
var citation = String(value || '').trim();
|
||
if (!citation || seen[citation]) return false;
|
||
if (hasPreciseCitation && citation.indexOf('来源定位降级') >= 0) return false;
|
||
seen[citation] = true;
|
||
return true;
|
||
});
|
||
}
|
||
|
||
function pageAiJournalPayloadText(event) {
|
||
try {
|
||
return JSON.stringify(event && event.payload && typeof event.payload === 'object' ? event.payload : {});
|
||
} catch (_) {
|
||
return '{}';
|
||
}
|
||
}
|
||
|
||
function pageAiLastUserPrompt() {
|
||
var messages = pageAiNormalizeArray(pageUiState.pageAiMessages);
|
||
for (var index = messages.length - 1; index >= 0; index -= 1) {
|
||
if (messages[index] && messages[index].role === 'user') return String(messages[index].content || '');
|
||
}
|
||
return '';
|
||
}
|
||
|
||
function pageAiApplyRunJournalEvent(event, runId, runTraceId) {
|
||
if (!event || typeof event !== 'object') return;
|
||
var eventName = String(event.kind || event.eventType || event.event || '').trim();
|
||
var payload = event.payload && typeof event.payload === 'object' ? event.payload : {};
|
||
if (payload.source === 'adapter_replay' || payload.replay === true) {
|
||
document.documentElement.setAttribute('data-mnote-page-ai-adapter-replay-seen', 'true');
|
||
return;
|
||
}
|
||
var payloadText = pageAiJournalPayloadText(event);
|
||
var prompt = pageAiLastUserPrompt();
|
||
if (eventName === 'message.delta') {
|
||
pageAiAppendStreamingAssistantDelta(runId, pageAiDecodeDeltaText(payloadText));
|
||
} else if (eventName === 'run.completed') {
|
||
pageAiFinishStreamingAssistantMessage(runId, String(payload.output || ''), prompt, []);
|
||
pageAiSetRunStatus('completed', runId);
|
||
} else if (eventName === 'run.failed') {
|
||
pageAiFinishStreamingAssistantMessage(runId, String(payload.message || payload.code || payload.error || 'AI run failed'), prompt, []);
|
||
pageAiSetRunStatus('failed', runId);
|
||
} else if (eventName === 'run.aborted' || eventName === 'run.cancelled' || eventName === 'run.canceled') {
|
||
pageUiState.pageAiStoppedRunIds[runId] = true;
|
||
pageAiStopStreamingAssistantMessage(runId);
|
||
pageAiSetRunStatus('aborted', runId);
|
||
} else if (eventName === 'tool.started' || eventName === 'tool.completed' || eventName === 'tool.failed') {
|
||
var toolItem = pageAiApplyToolEvent(eventName, payloadText, runId, runTraceId);
|
||
if (eventName === 'tool.completed') {
|
||
var citations = pageAiCollectKnowledgeRagCitations(toolItem, payload);
|
||
if (citations.length) pageAiFinishStreamingAssistantMessage(runId, '', prompt, citations);
|
||
}
|
||
pageAiSetRunStatus('tool_calling', runId);
|
||
} else if (eventName === 'plan.updated') {
|
||
var planEntries = Array.isArray(payload.entries) ? payload.entries : [];
|
||
if (planEntries.length) {
|
||
pageUiState.pageAiMessages.push({ role: 'system', kind: 'plan', entries: planEntries, createdAt: Date.now(), updatedAt: Date.now() });
|
||
}
|
||
}
|
||
var seq = String(event.seq || '').trim();
|
||
if (seq) {
|
||
pageAiWriteActiveRunSnapshot({
|
||
hostRunId: runId,
|
||
sessionId: pageUiState.pageAiActiveSessionId || String(event.sessionId || ''),
|
||
lastSeq: seq,
|
||
status: pageUiState.pageAiRunStatus || 'running'
|
||
});
|
||
document.documentElement.setAttribute('data-mnote-page-ai-active-run-last-seq', seq);
|
||
}
|
||
pageAiSyncCurrentSessionMessages();
|
||
pageAiPersistSessions();
|
||
renderPageAiConversation();
|
||
renderPageAiControls();
|
||
}
|
||
|
||
async function pageAiResumeActiveRunJournal(run) {
|
||
if (!run || typeof run !== 'object') return;
|
||
var runId = String(run.hostRunId || run.runId || '').trim();
|
||
if (!runId) return;
|
||
var snapshot = pageAiReadActiveRunSnapshot();
|
||
var afterSeq = snapshot && snapshot.hostRunId === runId ? String(snapshot.lastSeq || '000000000000000000') : '000000000000000000';
|
||
var response = await fetch('/api/page-ai/runs/' + encodeURIComponent(runId) + '/events?afterSeq=' + encodeURIComponent(afterSeq), {
|
||
headers: { 'accept': 'application/json' }
|
||
});
|
||
var payload = await response.json().catch(function(){ return null; });
|
||
if (!response.ok || !payload || payload.ok !== true) {
|
||
throw new Error(pageAiErrorMessage(payload, 'run_journal_failed_' + response.status));
|
||
}
|
||
var runTraceId = String((payload.run && payload.run.traceId) || (run && run.traceId) || '');
|
||
pageAiNormalizeArray(payload.events).forEach(function(event) {
|
||
pageAiApplyRunJournalEvent(event, runId, runTraceId);
|
||
});
|
||
var status = String((payload.run && payload.run.status) || run.status || '').trim();
|
||
if (pageAiRunStatusIsTerminal(status)) pageAiClearActiveRunSnapshot(runId);
|
||
document.documentElement.setAttribute('data-mnote-page-ai-run-journal-resumed', runId);
|
||
}
|
||
|
||
function pageAiLooksLikeBlockEdit(prompt) {
|
||
var text = searchText(prompt);
|
||
return ['新增', '添加', '插入', '删除', '删掉', '修改', '替换', '改成', '移动', '移到', 'move', 'replace', 'delete', 'insert'].some(function(word) {
|
||
return text.indexOf(word) >= 0;
|
||
});
|
||
}
|
||
|
||
function assertPageAiLocalWritePermission(prompt, targetPackage) {
|
||
if (currentSourceKind() !== 'local_folder') return;
|
||
if (!pageAiLooksLikeBlockEdit(prompt)) return;
|
||
var target = pageAiNormalizeArray(targetPackage && targetPackage.targets)[0] || null;
|
||
var permission = String(target && target.policy && target.policy.permission || '').trim();
|
||
if (permission === 'read_write') return;
|
||
var error = new Error('当前本地工作区只读授权,不能让 AI 写入目标文件。请切换到有写权限的工作区或调整授权后重试。');
|
||
error.code = 'page_ai_target_readonly';
|
||
throw error;
|
||
}
|
||
|
||
async function pageAiTryBlockEditWorkflow(prompt, scopedContext, runTargetSnapshot) {
|
||
if (currentSourceKind() === 'local_folder') return false;
|
||
if (!pageAiLooksLikeBlockEdit(prompt)) return false;
|
||
var runId = 'page-ai-fast-' + Date.now().toString(36);
|
||
var traceId = 'page-ai-fast-' + Date.now().toString(36);
|
||
pageAiSetRunStatus('running', runId);
|
||
renderPageAiControls();
|
||
var started = Date.now();
|
||
var response = await fetch('/api/page-ai/block-edit-workflow', {
|
||
method: 'POST',
|
||
headers: { 'content-type': 'application/json' },
|
||
body: JSON.stringify({
|
||
workspaceId: resolveWorkspaceId(document.body),
|
||
documentId: currentDocumentId(),
|
||
sourceKind: currentSourceKind(),
|
||
rootUri: currentRootUri(),
|
||
sessionId: pageUiState.pageAiActiveSessionId,
|
||
runId: runId,
|
||
profile: pageAiCurrentProfile(),
|
||
model: pageAiMnoteToolModel(),
|
||
message: prompt,
|
||
pageContext: scopedContext.pageContext,
|
||
editorTarget: scopedContext.editorTarget,
|
||
runTargetSnapshot: runTargetSnapshot || null,
|
||
selectedBlockId: scopedContext.selectedBlockId,
|
||
selectedText: scopedContext.selectedText,
|
||
traceId: traceId
|
||
})
|
||
});
|
||
var payload = await response.json().catch(function() { return null; });
|
||
if (!response.ok || !payload || payload.ok !== true) {
|
||
var code = payload && payload.code ? String(payload.code) : '';
|
||
if (code === 'page_ai_workflow_not_block_edit') return false;
|
||
pageUiState.pageAiMessages.push({
|
||
role: 'tool',
|
||
toolName: 'mnote.page_ai.block_edit_workflow',
|
||
status: 'failed',
|
||
toolCallId: runId,
|
||
resultSummary: pageAiErrorMessage(payload, 'fast_path_failed_' + response.status)
|
||
});
|
||
renderPageAiConversation();
|
||
pageAiSetRunStatus('failed', runId);
|
||
renderPageAiControls();
|
||
return true;
|
||
}
|
||
pageUiState.pageAiMessages.push({
|
||
role: 'tool',
|
||
toolName: 'mnote.page_ai.block_edit_workflow',
|
||
status: 'completed',
|
||
toolCallId: runId,
|
||
resultSummary: 'operations=' + String(Array.isArray(payload.operations) ? payload.operations.length : 0) + ' · ' + String(Date.now() - started) + 'ms'
|
||
});
|
||
pageUiState.pageAiMessages.push({
|
||
role: 'assistant',
|
||
content: payload.message || '已通过页面块编辑快路径完成写入。'
|
||
});
|
||
pageAiSetRunStatus('completed', runId);
|
||
try {
|
||
window.dispatchEvent(new CustomEvent('mnote:page-ai-tool-write-completed', {
|
||
detail: {
|
||
toolName: 'mnote.doc.apply_block_ops',
|
||
normalizedToolName: 'mnote.doc.apply.block.ops',
|
||
documentId: currentDocumentId(),
|
||
workspaceId: resolveWorkspaceId(document.body),
|
||
runId: runId,
|
||
traceId: traceId,
|
||
toolCallId: runId
|
||
}
|
||
}));
|
||
} catch (_) {}
|
||
var currentSession = pageAiCurrentSession();
|
||
if (currentSession) {
|
||
currentSession.messages = pageUiState.pageAiMessages.slice();
|
||
currentSession.updatedAt = Date.now();
|
||
}
|
||
pageAiPersistSessions();
|
||
renderPageAiConversation();
|
||
renderPageAiControls();
|
||
return true;
|
||
}
|
||
|
||
async function sendPageAiMessage(text) {
|
||
if (pageAiBoardFirstEnabled()) {
|
||
await sendPageAiBoardMessage(text);
|
||
return;
|
||
}
|
||
var allowQueue = pageUiState.pageAiBusy
|
||
|| ['queued', 'running', 'tool_calling', 'pending', 'acp_pending'].indexOf(pageUiState.pageAiRunStatus) >= 0;
|
||
if (pageUiState.pageAiBusy && !allowQueue) return;
|
||
var prompt = searchText(text);
|
||
if (!prompt) return;
|
||
if (allowQueue) {
|
||
pageAiEnqueueClientPrompt(prompt);
|
||
pageUiState.pageAiMessages.push({
|
||
role: 'assistant',
|
||
content: '已加入 AI 队列,前一条 run 完成后继续处理。'
|
||
});
|
||
var queuedSession = pageAiCurrentSession();
|
||
if (queuedSession) {
|
||
queuedSession.messages = pageUiState.pageAiMessages.slice();
|
||
queuedSession.updatedAt = Date.now();
|
||
}
|
||
pageAiPersistSessions();
|
||
renderPageAiConversation();
|
||
renderPageAiControls();
|
||
return;
|
||
}
|
||
if (!allowQueue) pageUiState.pageAiBusy = true;
|
||
var currentSession = null;
|
||
try {
|
||
await pageAiEnsureHermesSession();
|
||
if (!allowQueue) pageAiSetRunStatus('queued');
|
||
renderPageAiControls();
|
||
var contextSnapshot = currentPageAiContextSnapshot();
|
||
var scopedContext = pageAiScopedPageContext(contextSnapshot);
|
||
scopedContext.editorTarget = currentPageAiScopedEditorTarget();
|
||
assertPageAiTargetInCurrentWorkspace(scopedContext.editorTarget);
|
||
if (currentSourceKind() === 'local_folder' && !pageAiNormalizeArray(pageUiState.pageAiAllowedRoots).length) {
|
||
await pageAiLoadAllowedRoots();
|
||
}
|
||
await assertPageAiTargetWritable(scopedContext.editorTarget);
|
||
var runTargetSnapshot = pageAiBuildRunTargetSnapshot(scopedContext, prompt);
|
||
var contextRefs = pageAiBuildContextRefs(scopedContext, runTargetSnapshot);
|
||
var allowedRoots = pageAiBuildAllowedRoots();
|
||
var agentTargetPackage = pageAiBuildAgentTargetPackage(scopedContext, runTargetSnapshot);
|
||
assertPageAiLocalWritePermission(prompt, agentTargetPackage);
|
||
if (scopedContext.pageContext && scopedContext.pageContext.aiContext) {
|
||
scopedContext.pageContext.aiContext.runTargetSnapshot = runTargetSnapshot;
|
||
scopedContext.pageContext.aiContext.agentTargetPackage = agentTargetPackage;
|
||
}
|
||
var requestPageContext = pageAiPageContextForRefs(scopedContext.pageContext, contextRefs);
|
||
pageUiState.pageAiMessages.push({ role: 'user', content: prompt });
|
||
currentSession = pageAiCurrentSession();
|
||
var runProfile = pageAiRunProfile();
|
||
if (currentSession) {
|
||
if (currentSession.title === '新会话') {
|
||
currentSession.title = prompt.length > 18 ? prompt.slice(0, 18) + '…' : prompt;
|
||
}
|
||
currentSession.messages = pageUiState.pageAiMessages.slice();
|
||
currentSession.updatedAt = Date.now();
|
||
}
|
||
pageAiPersistSessions();
|
||
renderPageAiConversation();
|
||
if (!allowQueue && await pageAiTryBlockEditWorkflow(prompt, scopedContext, runTargetSnapshot)) {
|
||
return;
|
||
}
|
||
var reasonixSettings = pageAiCurrentAgentId() === 'reasonix' ? pageAiReasonixControlState() : null;
|
||
var promptForRun = pageAiApplyReasonixPlanMode(prompt);
|
||
var response = await fetch('/api/hermes/client/runs', {
|
||
method: 'POST',
|
||
headers: { 'content-type': 'application/json' },
|
||
body: JSON.stringify({
|
||
workspaceId: resolveWorkspaceId(document.body),
|
||
documentId: currentDocumentId(),
|
||
sourceKind: currentSourceKind(),
|
||
rootUri: currentRootUri(),
|
||
agentId: pageAiCurrentAgentId(),
|
||
sessionId: pageUiState.pageAiActiveSessionId,
|
||
acpSessionId: currentSession && currentSession.acpSessionId ? currentSession.acpSessionId : '',
|
||
profile: runProfile,
|
||
profileId: pageUiState.pageAiAcpRuntime === 'hermes' ? runProfile : '',
|
||
acpRuntime: pageUiState.pageAiAcpRuntime || 'reasonix',
|
||
contextScope: pageUiState.pageAiContextScope,
|
||
contextRefs: contextRefs,
|
||
allowedRoots: allowedRoots,
|
||
reasonixSettings: reasonixSettings,
|
||
skillPreferences: {
|
||
mnote: pageAiSkillPreferenceTable('mnote', ''),
|
||
reasonix: pageAiSkillPreferenceTable('reasonix', ''),
|
||
hermes: pageAiSkillPreferenceTable('hermes', runProfile)
|
||
},
|
||
message: promptForRun,
|
||
model: reasonixSettings && reasonixSettings.modelId ? reasonixSettings.modelId : pageAiMnoteToolModel(),
|
||
pageContext: requestPageContext,
|
||
editorTarget: scopedContext.editorTarget,
|
||
runTargetSnapshot: runTargetSnapshot,
|
||
targetPackage: agentTargetPackage,
|
||
selectedBlockId: scopedContext.selectedBlockId,
|
||
selectedText: scopedContext.selectedText || '',
|
||
traceId: 'page-ai-run-' + Date.now().toString(36)
|
||
})
|
||
});
|
||
if (!response.ok) {
|
||
var errorPayload = await response.json().catch(function(){ return null; });
|
||
throw new Error(pageAiErrorMessage(errorPayload, 'page_ai_failed_' + response.status));
|
||
}
|
||
var runPayload = await response.json().catch(function(){ return null; });
|
||
if (pageAiApplyQueuedRun(runPayload)) {
|
||
pageUiState.pageAiMessages.push({
|
||
role: 'assistant',
|
||
content: '已加入 AI 队列,前一条 run 完成后继续处理。'
|
||
});
|
||
if (currentSession) {
|
||
currentSession.messages = pageUiState.pageAiMessages.slice();
|
||
currentSession.updatedAt = Date.now();
|
||
}
|
||
pageAiPersistSessions();
|
||
renderPageAiConversation();
|
||
renderPageAiControls();
|
||
void pageAiLoadRuntimeStatus().catch(function(error) {
|
||
pageUiState.pageAiRuntimeStatusError = error instanceof Error ? error.message : String(error);
|
||
renderPageAiControls();
|
||
});
|
||
return;
|
||
}
|
||
if (allowQueue) {
|
||
throw new Error('hermes_queue_expected_queued_response');
|
||
}
|
||
var upstream = runPayload && runPayload.upstream ? runPayload.upstream : runPayload;
|
||
var runId = upstream && (upstream.run_id || upstream.runId);
|
||
if (!runId) throw new Error('hermes_run_missing_run_id');
|
||
var runTraceId = String((upstream && (upstream.trace_id || upstream.traceId)) || (runPayload && (runPayload.trace_id || runPayload.traceId)) || '');
|
||
pageAiSetRunStatus('running', runId);
|
||
pageAiSetRunTargetSnapshot(Object.assign({}, runTargetSnapshot, {
|
||
runId: runId,
|
||
sessionId: pageUiState.pageAiActiveSessionId,
|
||
traceId: runTraceId
|
||
}));
|
||
renderPageAiControls();
|
||
var eventResponse = await fetch('/api/hermes/client/events/' + encodeURIComponent(runId), {
|
||
headers: { 'accept': 'text/event-stream' }
|
||
});
|
||
if (!eventResponse.ok) {
|
||
var eventError = await eventResponse.json().catch(function(){ return null; });
|
||
throw new Error(pageAiErrorMessage(eventError, 'hermes_events_failed_' + eventResponse.status));
|
||
}
|
||
var assistantText = '';
|
||
var autoCitations = [];
|
||
await streamPageAiResponse(eventResponse, function(eventName, payloadText) {
|
||
pageAiTrackStreamingRunEvent(runId, eventName, payloadText);
|
||
var streamPayload = null;
|
||
try {
|
||
streamPayload = JSON.parse(payloadText || 'null');
|
||
} catch (_) {}
|
||
if (streamPayload && (streamPayload.source === 'adapter_replay' || streamPayload.replay === true)) {
|
||
document.documentElement.setAttribute('data-mnote-page-ai-adapter-replay-seen', 'true');
|
||
return;
|
||
}
|
||
if (eventName === 'message.delta') {
|
||
if (pageUiState.pageAiStoppedRunIds[runId]) return;
|
||
assistantText = pageAiAppendStreamingAssistantDelta(runId, pageAiDecodeDeltaText(payloadText));
|
||
}
|
||
if (eventName === 'thought.delta') {
|
||
try {
|
||
var thoughtPayload = JSON.parse(payloadText || 'null');
|
||
var thoughtText = String((thoughtPayload && (thoughtPayload.delta || thoughtPayload.text)) || '');
|
||
if (thoughtText) {
|
||
var msgs = pageUiState.pageAiMessages;
|
||
var lastThought = msgs.length > 0 && msgs[msgs.length - 1].kind === 'thought' ? msgs[msgs.length - 1] : null;
|
||
if (lastThought) {
|
||
lastThought.content += thoughtText;
|
||
} else {
|
||
msgs.push({ role: 'assistant', kind: 'thought', content: thoughtText });
|
||
}
|
||
pageAiSyncCurrentSessionMessages();
|
||
pageAiPersistSessions();
|
||
renderPageAiConversation();
|
||
}
|
||
} catch (_) {}
|
||
}
|
||
if (eventName === 'usage.updated') {
|
||
try {
|
||
var usagePayload = JSON.parse(payloadText || 'null') || {};
|
||
var sessionForUsage = pageAiCurrentSession();
|
||
if (sessionForUsage) {
|
||
sessionForUsage.usage = {
|
||
source: 'usage_update',
|
||
used: Number(usagePayload.used ?? usagePayload.contextUsed ?? 0),
|
||
size: Number(usagePayload.size ?? usagePayload.contextSize ?? 0)
|
||
};
|
||
pageAiPersistSessions();
|
||
}
|
||
} catch (_) {}
|
||
renderPageAiControls();
|
||
}
|
||
if (eventName === 'permission.requested' || eventName === 'permission.denied' || eventName === 'permission.allowed') {
|
||
pageAiApplyPermissionEvent(eventName, payloadText);
|
||
pageAiSyncCurrentSessionMessages();
|
||
pageAiPersistSessions();
|
||
renderPageAiConversation();
|
||
renderPageAiControls();
|
||
}
|
||
if (eventName === 'run.completed') {
|
||
try {
|
||
var completed = JSON.parse(payloadText || 'null');
|
||
if (completed && completed.output) assistantText = String(completed.output || '');
|
||
if (completed && completed.usage) {
|
||
var completedSession = pageAiCurrentSession();
|
||
if (completedSession) completedSession.usage = completed.usage;
|
||
}
|
||
var agentAudit = completed && completed.agentAudit && typeof completed.agentAudit === 'object' ? completed.agentAudit : null;
|
||
var agentRunReceipt = agentAudit && agentAudit.agentRunReceipt && typeof agentAudit.agentRunReceipt === 'object'
|
||
? agentAudit.agentRunReceipt
|
||
: null;
|
||
var changedFiles = pageAiNormalizeArray(agentAudit && agentAudit.changedFiles).map(function(file) {
|
||
if (!file || typeof file !== 'object') return file;
|
||
return Object.assign({
|
||
actorId: String(agentAudit && agentAudit.actorId || ''),
|
||
actorType: String(agentAudit && agentAudit.actorType || ''),
|
||
agentKind: String(agentAudit && agentAudit.agentKind || ''),
|
||
runId: runId
|
||
}, file);
|
||
});
|
||
if (changedFiles.length) {
|
||
pageUiState.pageAiMessages.push({
|
||
role: 'tool',
|
||
content: 'agent.changed_files',
|
||
toolCallId: runId + ':agent.changed_files',
|
||
toolName: 'agent.changed_files',
|
||
toolKind: 'audit',
|
||
status: 'completed',
|
||
argsSummary: [agentAudit.rootUri, agentAudit.actorId, agentAudit.agentKind].map(function(value) {
|
||
return String(value || '').trim();
|
||
}).filter(Boolean).join(' · '),
|
||
resultSummary: String(agentAudit.diffSummary || changedFiles.length + ' changed file(s)'),
|
||
changedFiles: changedFiles,
|
||
traceId: runTraceId,
|
||
auditId: String(agentAudit.eventId || '')
|
||
});
|
||
}
|
||
if (agentRunReceipt || changedFiles.length) {
|
||
try {
|
||
pageAiDispatchReceiptRefresh(agentRunReceipt, changedFiles, agentAudit, runId, runTraceId);
|
||
} catch (_) {}
|
||
}
|
||
} catch (_) {}
|
||
pageAiSetRunStatus('completed', runId);
|
||
}
|
||
if (eventName === 'run.failed') {
|
||
try {
|
||
var failed = JSON.parse(payloadText || 'null');
|
||
assistantText = String((failed && (failed.message || failed.code || failed.error)) || 'AI run failed');
|
||
} catch (_) {}
|
||
pageAiSetRunStatus('failed', runId);
|
||
}
|
||
if (eventName === 'run.aborted' || eventName === 'run.cancelled' || eventName === 'run.canceled') {
|
||
pageUiState.pageAiStoppedRunIds[runId] = true;
|
||
pageAiStopStreamingAssistantMessage(runId);
|
||
pageAiSetRunStatus('aborted', runId);
|
||
}
|
||
if (eventName === 'session.info.updated') {
|
||
try {
|
||
var infoPayload = JSON.parse(payloadText || 'null') || {};
|
||
var newTitle = String(infoPayload.title || '').trim();
|
||
var acpSessionId = String(infoPayload.acpSessionId || infoPayload.acp_session_id || '').trim();
|
||
var sessionForInfo = pageAiCurrentSession();
|
||
if (acpSessionId && sessionForInfo) {
|
||
sessionForInfo.acpSessionId = acpSessionId;
|
||
sessionForInfo.updatedAt = Date.now();
|
||
pageAiPersistSessions();
|
||
}
|
||
if (newTitle) {
|
||
var sessionForTitle = sessionForInfo || pageAiCurrentSession();
|
||
if (sessionForTitle) {
|
||
sessionForTitle.title = newTitle;
|
||
sessionForTitle.updatedAt = Date.now();
|
||
pageAiPersistSessions();
|
||
renderPageAiControls();
|
||
}
|
||
}
|
||
} catch (_) {}
|
||
}
|
||
if (eventName === 'plan.updated') {
|
||
try {
|
||
var planPayload = JSON.parse(payloadText || 'null') || {};
|
||
var planEntries = Array.isArray(planPayload.entries) ? planPayload.entries : [];
|
||
if (planEntries.length) {
|
||
var planMsgs = pageUiState.pageAiMessages;
|
||
var existingPlan = planMsgs.length > 0 && planMsgs[planMsgs.length - 1].kind === 'plan' ? planMsgs[planMsgs.length - 1] : null;
|
||
if (existingPlan) {
|
||
existingPlan.entries = planEntries;
|
||
existingPlan.updatedAt = Date.now();
|
||
} else {
|
||
planMsgs.push({ role: 'system', kind: 'plan', entries: planEntries, createdAt: Date.now(), updatedAt: Date.now() });
|
||
}
|
||
pageAiSyncCurrentSessionMessages();
|
||
pageAiPersistSessions();
|
||
renderPageAiConversation();
|
||
}
|
||
} catch (_) {}
|
||
}
|
||
if (eventName === 'tool.started' || eventName === 'tool.completed' || eventName === 'tool.failed') {
|
||
var toolEventPayload = null;
|
||
try {
|
||
toolEventPayload = JSON.parse(payloadText || 'null');
|
||
} catch (_) {}
|
||
var toolItem = pageAiApplyToolEvent(eventName, payloadText, runId, runTraceId);
|
||
if (eventName === 'tool.completed') {
|
||
var completedCitations = pageAiCollectKnowledgeRagCitations(toolItem, toolEventPayload);
|
||
if (completedCitations.length) autoCitations = completedCitations;
|
||
}
|
||
pageAiSyncCurrentSessionMessages();
|
||
pageAiPersistSessions();
|
||
renderPageAiConversation();
|
||
pageAiSetRunStatus('tool_calling', runId);
|
||
renderPageAiControls();
|
||
}
|
||
});
|
||
if (!pageUiState.pageAiStoppedRunIds[runId]) {
|
||
pageAiFinishStreamingAssistantMessage(runId, assistantText, prompt, autoCitations);
|
||
}
|
||
currentSession = pageAiCurrentSession();
|
||
if (currentSession) {
|
||
currentSession.messages = pageUiState.pageAiMessages.slice();
|
||
currentSession.updatedAt = Date.now();
|
||
}
|
||
pageAiPersistSessions();
|
||
} catch (error) {
|
||
pageAiSetRunStatus('failed');
|
||
pageUiState.pageAiMessages.push({
|
||
role: 'assistant',
|
||
content: 'AI 当前请求失败:' + (error instanceof Error ? error.message : String(error))
|
||
});
|
||
currentSession = pageAiCurrentSession();
|
||
if (currentSession) {
|
||
currentSession.messages = pageUiState.pageAiMessages.slice();
|
||
currentSession.updatedAt = Date.now();
|
||
}
|
||
pageAiPersistSessions();
|
||
} finally {
|
||
if (!allowQueue) pageUiState.pageAiBusy = false;
|
||
await pageAiLoadRuntimeStatus().catch(function(error) {
|
||
pageUiState.pageAiRuntimeStatusError = error instanceof Error ? error.message : String(error);
|
||
return null;
|
||
});
|
||
renderPageAiConversation();
|
||
renderPageAiControls();
|
||
pageAiStartNextClientQueuedPrompt();
|
||
}
|
||
}
|
||
|
||
function handlePageAiClick(event, helpers) {
|
||
var closestAction = helpers && helpers.closestAction;
|
||
if (typeof closestAction !== 'function') return false;
|
||
var pageAiCitationLink = closestAction(event.target, 'a[data-page-ai-citation-link="true"]');
|
||
if (pageAiCitationLink && !(event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)) {
|
||
var href = pageAiCitationLink.getAttribute('href') || '';
|
||
if (pageAiOpenCitationUrl(href)) {
|
||
event.preventDefault();
|
||
return true;
|
||
}
|
||
}
|
||
var pageAiClose = closestAction(event.target, '[data-page-ai-action="close"]');
|
||
if (pageAiClose) {
|
||
event.preventDefault();
|
||
closePageAiDrawer();
|
||
return true;
|
||
}
|
||
var pageAiOpencodeFile = closestAction(event.target, '[data-page-ai-opencode-open-file]');
|
||
if (pageAiOpencodeFile) {
|
||
event.preventDefault();
|
||
pageAiOpenOpencodeChangedFile(pageAiOpencodeFile.getAttribute('data-page-ai-opencode-open-file') || '');
|
||
return true;
|
||
}
|
||
var pageAiSettings = closestAction(event.target, '[data-page-ai-action="open-hermes-settings"]');
|
||
if (pageAiSettings) {
|
||
event.preventDefault();
|
||
pageAiOpenHermesSettings();
|
||
return true;
|
||
}
|
||
var pageAiStop = closestAction(event.target, '[data-page-ai-action="stop-run"]');
|
||
if (pageAiStop) {
|
||
event.preventDefault();
|
||
void pageAiStopRun();
|
||
return true;
|
||
}
|
||
var pageAiReset = closestAction(event.target, '[data-page-ai-action="reset-runtime"]');
|
||
if (pageAiReset) {
|
||
event.preventDefault();
|
||
void pageAiResetRuntime();
|
||
return true;
|
||
}
|
||
var pageAiStopAllJobs = closestAction(event.target, '[data-page-ai-action="stop-all-jobs"]');
|
||
if (pageAiStopAllJobs) {
|
||
event.preventDefault();
|
||
pageUiState.pageAiMessages.push({ role: 'assistant', content: '当前没有可停止的后台 job;长任务 job 将在 runtime status 中独立显示。' });
|
||
renderPageAiConversation();
|
||
renderPageAiControls();
|
||
return true;
|
||
}
|
||
var pageAiRotate = closestAction(event.target, '[data-page-ai-action="rotate"]');
|
||
if (pageAiRotate) {
|
||
event.preventDefault();
|
||
pageUiState.pageAiSuggestionIndex += 1;
|
||
renderPageAiSuggestions();
|
||
return true;
|
||
}
|
||
var pageAiIntent = closestAction(event.target, '[data-page-ai-intent]');
|
||
if (pageAiIntent) {
|
||
event.preventDefault();
|
||
var intentName = pageAiIntent.getAttribute('data-page-ai-intent') || '';
|
||
if (intentName === 'create-summary') void sendPageAiMessage('请通过 Hermes 调用 mnote.artifact.create_summary,为当前页面创建或更新 AI Summary。');
|
||
if (intentName === 'create-ai-note') void sendPageAiMessage('请通过 Hermes 调用 mnote.artifact.create_ai_note,基于当前页面创建一篇新的 AI Note。');
|
||
return true;
|
||
}
|
||
var pageAiTab = closestAction(event.target, '[data-page-ai-tab]');
|
||
if (pageAiTab) {
|
||
event.preventDefault();
|
||
pageUiState.pageAiPage = pageAiTab.getAttribute('data-page-ai-tab') || 'chat';
|
||
if (pageUiState.pageAiPage === 'runtime') pageUiState.pageAiPage = 'status';
|
||
if (pageUiState.pageAiPage === 'chat-only-settings') pageUiState.pageAiPage = 'agent';
|
||
if (pageUiState.pageAiPage === 'status') {
|
||
void pageAiLoadGatewayHealth();
|
||
void pageAiLoadRuntimeStatus().catch(function(error) {
|
||
pageUiState.pageAiRuntimeStatusError = error instanceof Error ? error.message : String(error);
|
||
renderPageAiControls();
|
||
});
|
||
}
|
||
if (pageUiState.pageAiPage === 'skills') void pageAiLoadSkills();
|
||
renderPageAiControls();
|
||
renderPageAiConversation();
|
||
return true;
|
||
}
|
||
var pageAiProvider = closestAction(event.target, '[data-page-ai-provider]');
|
||
if (pageAiProvider) {
|
||
event.preventDefault();
|
||
pageUiState.pageAiProvider = pageAiProvider.getAttribute('data-page-ai-provider') || 'hermes';
|
||
renderPageAiProviderButtons();
|
||
return true;
|
||
}
|
||
var pageAiAgent = closestAction(event.target, '[data-page-ai-agent-id]');
|
||
if (pageAiAgent) {
|
||
event.preventDefault();
|
||
var nextAgentId = pageAiAgent.getAttribute('data-page-ai-agent-id') || 'reasonix';
|
||
var nextProfileId = pageAiAgent.getAttribute('data-page-ai-profile-id') || '';
|
||
pageAiSetAgentId(nextAgentId);
|
||
if (nextProfileId) {
|
||
pageUiState.pageAiAgentPopoverOpen = false;
|
||
void pageAiSwitchProfile(nextProfileId);
|
||
renderPageAiControls();
|
||
}
|
||
return true;
|
||
}
|
||
var pageAiAgentButton = closestAction(event.target, '[data-page-ai-agent-button]');
|
||
if (pageAiAgentButton) {
|
||
event.preventDefault();
|
||
pageAiSetAgentPopoverOpen(pageAiAgentButton.getAttribute('aria-expanded') !== 'true');
|
||
return true;
|
||
}
|
||
var pageAiContextButton = closestAction(event.target, '[data-page-ai-context-button]');
|
||
if (pageAiContextButton) {
|
||
event.preventDefault();
|
||
pageAiSetContextPopoverOpen(pageAiContextButton.getAttribute('aria-expanded') !== 'true');
|
||
return true;
|
||
}
|
||
var pageAiTargetButton = closestAction(event.target, '[data-page-ai-target-button]');
|
||
if (pageAiTargetButton) {
|
||
event.preventDefault();
|
||
pageAiSetTargetPopoverOpen(pageAiTargetButton.getAttribute('aria-expanded') !== 'true');
|
||
return true;
|
||
}
|
||
var pageAiAction = closestAction(event.target, '[data-page-ai-action]');
|
||
if (pageAiAction) {
|
||
event.preventDefault();
|
||
var action = pageAiAction.getAttribute('data-page-ai-action') || '';
|
||
if (action === 'close-agent-popover') pageAiSetAgentPopoverOpen(false);
|
||
if (action === 'close-context-popover') pageAiSetContextPopoverOpen(false);
|
||
if (action === 'close-target-popover') pageAiSetTargetPopoverOpen(false);
|
||
if (action === 'new-session') {
|
||
pageUiState.pageAiPage = 'chat';
|
||
pageAiStartNewSession();
|
||
}
|
||
if (action === 'history') {
|
||
pageAiLoadSessions();
|
||
pageUiState.pageAiPage = pageUiState.pageAiPage === 'history' ? 'chat' : 'history';
|
||
renderPageAiControls();
|
||
renderPageAiConversation();
|
||
if (pageUiState.pageAiPage === 'history') {
|
||
void pageAiLoadBackendSessions().catch(function(error) {
|
||
pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
|
||
renderPageAiControls();
|
||
});
|
||
}
|
||
}
|
||
if (action === 'send') {
|
||
var input = ensurePageAiDrawer().querySelector('[data-page-ai-input]');
|
||
if (input instanceof HTMLTextAreaElement) {
|
||
var message = input.value;
|
||
input.value = '';
|
||
void sendPageAiMessage(message);
|
||
}
|
||
}
|
||
if (action === 'opencode-send') {
|
||
var opencodeInput = ensurePageAiDrawer().querySelector('[data-page-ai-opencode-input]');
|
||
var opencodeText = opencodeInput instanceof HTMLTextAreaElement ? opencodeInput.value.trim() : '';
|
||
if (opencodeText) {
|
||
opencodeInput.value = '';
|
||
void pageAiSendOpencodeMessage(opencodeText).catch(function(error) {
|
||
pageUiState.pageAiOpencodeStatusError = error instanceof Error ? error.message : String(error);
|
||
pageUiState.pageAiOpencodeSending = false;
|
||
pageAiRenderOpencodeHostChrome();
|
||
});
|
||
}
|
||
}
|
||
if (action === 'cancel-queued-run') {
|
||
void pageAiCancelQueuedRun(pageAiAction.getAttribute('data-page-ai-queue-id'));
|
||
}
|
||
if (action === 'opencode-refresh-status') {
|
||
void pageAiLoadOpencodeStatus();
|
||
}
|
||
if (action === 'opencode-new-session') {
|
||
pageUiState.pageAiOpencodeSessionId = '';
|
||
pageUiState.pageAiOpencodeBindingKey = '';
|
||
pageUiState.pageAiOpencodeMessages = [];
|
||
pageUiState.pageAiOpencodeLocalMessages = [];
|
||
void pageAiEnsureOpencodeSessionBinding(true);
|
||
}
|
||
if (action === 'opencode-abort') {
|
||
void pageAiAbortOpencodeRun();
|
||
}
|
||
if (action === 'opencode-refresh-current-page') {
|
||
pageAiRefreshOpencodeCurrentPage();
|
||
}
|
||
if (action === 'opencode-insert-context') {
|
||
pageAiPostOpencodeBridgeMessage('insert-text', { text: pageAiOpencodeContextTextForInsert() });
|
||
}
|
||
return true;
|
||
}
|
||
var pageAiSessionSelect = closestAction(event.target, '[data-page-ai-opencode-session-select]');
|
||
if (pageAiSessionSelect) {
|
||
event.preventDefault();
|
||
pageAiSelectOpencodeSession(pageAiSessionSelect.getAttribute('data-page-ai-opencode-session-select') || '');
|
||
return true;
|
||
}
|
||
var pageAiPermission = closestAction(event.target, '[data-page-ai-opencode-permission]');
|
||
if (pageAiPermission) {
|
||
event.preventDefault();
|
||
void pageAiReplyOpencodePermission(pageAiPermission.getAttribute('data-page-ai-opencode-permission-id') || '', pageAiPermission.getAttribute('data-page-ai-opencode-permission') || 'once');
|
||
return true;
|
||
}
|
||
var pageAiTargetOption = closestAction(event.target, '[data-page-ai-target-option]');
|
||
if (pageAiTargetOption) {
|
||
event.preventDefault();
|
||
pageAiSelectTarget(pageAiTargetOption.getAttribute('data-page-ai-target-option') || '');
|
||
return true;
|
||
}
|
||
var pageAiContextRef = closestAction(event.target, '[data-page-ai-context-ref]');
|
||
if (pageAiContextRef) {
|
||
event.preventDefault();
|
||
pageAiToggleContextRef(pageAiContextRef.getAttribute('data-page-ai-context-ref') || '');
|
||
return true;
|
||
}
|
||
var pageAiMemorySave = closestAction(event.target, '[data-page-ai-memory-save]');
|
||
if (pageAiMemorySave) {
|
||
event.preventDefault();
|
||
void pageAiSaveProfileMemory(pageAiMemorySave.getAttribute('data-page-ai-memory-save') || '');
|
||
return true;
|
||
}
|
||
var pageAiSkillToggle = closestAction(event.target, '[data-page-ai-skill-toggle]');
|
||
if (pageAiSkillToggle) {
|
||
event.preventDefault();
|
||
void pageAiToggleSkill(
|
||
pageAiSkillToggle.getAttribute('data-page-ai-skill-toggle') || '',
|
||
pageAiSkillToggle.getAttribute('aria-pressed') !== 'true',
|
||
pageAiSkillToggle.getAttribute('data-page-ai-skill-group') || '',
|
||
pageAiSkillToggle.getAttribute('data-page-ai-skill-profile') || ''
|
||
);
|
||
return true;
|
||
}
|
||
var pageAiSkillGroupToggle = closestAction(event.target, '[data-page-ai-skill-group-toggle]');
|
||
if (pageAiSkillGroupToggle) {
|
||
event.preventDefault();
|
||
pageAiToggleSkillGroup(pageAiSkillGroupToggle.getAttribute('data-page-ai-skill-group-toggle') || '');
|
||
return true;
|
||
}
|
||
var pageAiToolToggle = closestAction(event.target, '[data-page-ai-tool-toggle]');
|
||
if (pageAiToolToggle) {
|
||
event.preventDefault();
|
||
void pageAiToggleTool(pageAiToolToggle.getAttribute('data-page-ai-tool-toggle') || '', pageAiToolToggle.getAttribute('aria-pressed') !== 'true');
|
||
return true;
|
||
}
|
||
var pageAiSessionSelect = closestAction(event.target, '[data-page-ai-session-select]');
|
||
if (pageAiSessionSelect) {
|
||
event.preventDefault();
|
||
pageAiToggleSessionSelection(
|
||
pageAiSessionSelect.getAttribute('data-page-ai-session-select') || '',
|
||
pageAiSessionSelect.getAttribute('aria-checked') !== 'true'
|
||
);
|
||
return true;
|
||
}
|
||
var pageAiClearSelectedSessions = closestAction(event.target, '[data-page-ai-session-clear-selection]');
|
||
if (pageAiClearSelectedSessions) {
|
||
event.preventDefault();
|
||
pageAiClearSessionSelection();
|
||
return true;
|
||
}
|
||
var pageAiDeleteSelectedSessions = closestAction(event.target, '[data-page-ai-session-delete-selected]');
|
||
if (pageAiDeleteSelectedSessions) {
|
||
event.preventDefault();
|
||
void pageAiDeleteSelectedBackendSessions().catch(function(error) {
|
||
pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
|
||
renderPageAiControls();
|
||
});
|
||
return true;
|
||
}
|
||
var pageAiSessionResume = closestAction(event.target, '[data-page-ai-session-resume]');
|
||
if (pageAiSessionResume) {
|
||
event.preventDefault();
|
||
void pageAiResumeBackendSession(pageAiSessionResume.getAttribute('data-page-ai-session-resume') || '').catch(function(error) {
|
||
pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
|
||
renderPageAiControls();
|
||
});
|
||
return true;
|
||
}
|
||
var pageAiSessionRename = closestAction(event.target, '[data-page-ai-session-rename]');
|
||
if (pageAiSessionRename) {
|
||
event.preventDefault();
|
||
void pageAiRenameBackendSession(pageAiSessionRename.getAttribute('data-page-ai-session-rename') || '').catch(function(error) {
|
||
pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
|
||
renderPageAiControls();
|
||
});
|
||
return true;
|
||
}
|
||
var pageAiSessionExport = closestAction(event.target, '[data-page-ai-session-export]');
|
||
if (pageAiSessionExport) {
|
||
event.preventDefault();
|
||
void pageAiExportBackendSession(pageAiSessionExport.getAttribute('data-page-ai-session-export') || '').catch(function(error) {
|
||
pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
|
||
renderPageAiControls();
|
||
});
|
||
return true;
|
||
}
|
||
var pageAiSessionDelete = closestAction(event.target, '[data-page-ai-session-delete]');
|
||
if (pageAiSessionDelete) {
|
||
event.preventDefault();
|
||
void pageAiDeleteBackendSession(pageAiSessionDelete.getAttribute('data-page-ai-session-delete') || '').catch(function(error) {
|
||
pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
|
||
renderPageAiControls();
|
||
});
|
||
return true;
|
||
}
|
||
var pageAiPermissionAction = closestAction(event.target, '[data-page-ai-permission-action]');
|
||
if (pageAiPermissionAction) {
|
||
event.preventDefault();
|
||
pageAiResolvePermission(
|
||
pageAiPermissionAction.getAttribute('data-page-ai-permission-id') || '',
|
||
pageAiPermissionAction.getAttribute('data-page-ai-permission-action') || 'deny',
|
||
pageAiPermissionAction.getAttribute('data-page-ai-permission-option-id') || ''
|
||
);
|
||
return true;
|
||
}
|
||
var pageAiPlanAction = closestAction(event.target, '[data-page-ai-plan-action]');
|
||
if (pageAiPlanAction) {
|
||
event.preventDefault();
|
||
var action = String(pageAiPlanAction.getAttribute('data-page-ai-plan-action') || '').trim();
|
||
var inputNode = ensurePageAiDrawer().querySelector('[data-page-ai-input]');
|
||
if (action === 'approve') {
|
||
void sendPageAiMessage('批准,开始执行。');
|
||
return true;
|
||
}
|
||
if (action === 'cancel') {
|
||
void sendPageAiMessage('取消执行。');
|
||
return true;
|
||
}
|
||
if (inputNode instanceof HTMLTextAreaElement) {
|
||
inputNode.value = action === 'revise' ? '请修改计划:' : '';
|
||
inputNode.focus();
|
||
}
|
||
return true;
|
||
}
|
||
var pageAiOpenLocationAction = closestAction(event.target, '[data-page-ai-open-location]');
|
||
if (pageAiOpenLocationAction) {
|
||
event.preventDefault();
|
||
var loc = String(pageAiOpenLocationAction.getAttribute('data-page-ai-open-location') || '').trim();
|
||
if (loc) pageAiOpenLocation(loc);
|
||
return true;
|
||
}
|
||
var pageAiBoardRunDetail = closestAction(event.target, '[data-page-ai-board-run-detail]');
|
||
if (pageAiBoardRunDetail) {
|
||
event.preventDefault();
|
||
pageUiState.pageAiBoardActiveDetailRunId = pageAiBoardRunDetail.getAttribute('data-page-ai-board-run-detail') || '';
|
||
renderPageAiConversation();
|
||
return true;
|
||
}
|
||
var pageAiCopyMessage = closestAction(event.target, '[data-page-ai-copy-message]');
|
||
if (pageAiCopyMessage) {
|
||
event.preventDefault();
|
||
var runId = pageAiCopyMessage.getAttribute('data-page-ai-copy-message') || '';
|
||
var message = pageAiNormalizeArray(pageUiState.pageAiMessages).find(function(item) { return String(item && item.boardRunId || '') === runId; });
|
||
if (navigator.clipboard && message) void navigator.clipboard.writeText(String(message.content || ''));
|
||
return true;
|
||
}
|
||
var pageAiRetryMessage = closestAction(event.target, '[data-page-ai-retry-message]');
|
||
if (pageAiRetryMessage) {
|
||
event.preventDefault();
|
||
var retryRunId = pageAiRetryMessage.getAttribute('data-page-ai-retry-message') || '';
|
||
var retryMessage = pageAiNormalizeArray(pageUiState.pageAiMessages).find(function(item) { return String(item && item.boardRunId || '') === retryRunId; });
|
||
var retryPrompt = String(retryMessage && retryMessage.retryPrompt || '').trim();
|
||
if (retryPrompt) void sendPageAiMessage(retryPrompt);
|
||
return true;
|
||
}
|
||
var pageAiSession = closestAction(event.target, '[data-page-ai-session]');
|
||
if (pageAiSession) {
|
||
event.preventDefault();
|
||
pageAiSetActiveSession(pageAiSession.getAttribute('data-page-ai-session') || '');
|
||
return true;
|
||
}
|
||
var pageAiSuggestion = closestAction(event.target, '[data-page-ai-suggestion]');
|
||
if (pageAiSuggestion) {
|
||
event.preventDefault();
|
||
var text = pageAiSuggestion.getAttribute('data-page-ai-suggestion') || '';
|
||
var inputNode = ensurePageAiDrawer().querySelector('[data-page-ai-input]');
|
||
if (inputNode instanceof HTMLTextAreaElement) {
|
||
inputNode.value = text;
|
||
inputNode.focus();
|
||
}
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
function handlePageAiKeyDown(event, helpers) {
|
||
var closestAction = helpers && helpers.closestAction;
|
||
if (typeof closestAction !== 'function') return false;
|
||
if (event.key === 'Enter' && !event.shiftKey) {
|
||
var aiInput = closestAction(event.target, '[data-page-ai-input]');
|
||
if (aiInput instanceof HTMLTextAreaElement) {
|
||
event.preventDefault();
|
||
var text = aiInput.value;
|
||
aiInput.value = '';
|
||
void sendPageAiMessage(text);
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
function handlePageAiInput(event, helpers) {
|
||
var closestAction = helpers && helpers.closestAction;
|
||
if (typeof closestAction !== 'function') return false;
|
||
var skillSearch = closestAction(event.target, '[data-page-ai-skill-search]');
|
||
if (skillSearch instanceof HTMLInputElement) {
|
||
pageUiState.pageAiSkillQuery = skillSearch.value;
|
||
renderPageAiControls();
|
||
return true;
|
||
}
|
||
var sessionSearch = closestAction(event.target, '[data-page-ai-session-search]');
|
||
if (sessionSearch instanceof HTMLInputElement) {
|
||
var sessionQuery = sessionSearch.value;
|
||
window.clearTimeout(pageUiState.pageAiSessionSearchTimer || 0);
|
||
pageUiState.pageAiSessionSearchTimer = window.setTimeout(function() {
|
||
void pageAiSearchBackendSessions(sessionQuery).catch(function(error) {
|
||
pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
|
||
renderPageAiControls();
|
||
});
|
||
}, 200);
|
||
return true;
|
||
}
|
||
var memoryEditor = closestAction(event.target, '[data-page-ai-memory-editor]');
|
||
if (memoryEditor instanceof HTMLTextAreaElement) {
|
||
var section = memoryEditor.getAttribute('data-page-ai-memory-editor') || '';
|
||
if (['memory', 'user', 'soul'].indexOf(section) >= 0) pageUiState.pageAiProfileMemoryDrafts[section] = memoryEditor.value;
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
function handlePageAiChange(event, helpers) {
|
||
var closestAction = helpers && helpers.closestAction;
|
||
if (typeof closestAction !== 'function') return false;
|
||
var pageAiDescriptorField = closestAction(event.target, '[data-page-ai-descriptor-field]');
|
||
if (pageAiDescriptorField instanceof HTMLInputElement || pageAiDescriptorField instanceof HTMLSelectElement) {
|
||
var key = pageAiDescriptorField.getAttribute('data-page-ai-descriptor-field') || '';
|
||
var kind = pageAiDescriptorField.getAttribute('data-page-ai-descriptor-kind') || '';
|
||
var value = kind === 'boolean' && pageAiDescriptorField instanceof HTMLInputElement
|
||
? pageAiDescriptorField.checked
|
||
: pageAiDescriptorField.value;
|
||
pageAiSetDescriptorFieldValue(key, value);
|
||
return true;
|
||
}
|
||
var pageAiAcpRuntimeSelect = closestAction(event.target, '[data-page-ai-acp-runtime]');
|
||
if (pageAiAcpRuntimeSelect instanceof HTMLSelectElement) {
|
||
var next = String(pageAiAcpRuntimeSelect.value || 'reasonix').trim() || 'reasonix';
|
||
pageUiState.pageAiAcpRuntime = next;
|
||
pageUiState.pageAiSkills = { categories: [], archived: [] };
|
||
pageUiState.pageAiSkillError = '';
|
||
pageAiPersistSessions();
|
||
void pageAiLoadProfiles();
|
||
if (next !== 'reasonix') void pageAiLoadProfileMemory();
|
||
void pageAiLoadSkills();
|
||
void pageAiLoadBackendSessions().catch(function(error) {
|
||
pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
|
||
});
|
||
renderPageAiControls();
|
||
renderPageAiProviderButtons();
|
||
return true;
|
||
}
|
||
var pageAiProfileSelect = closestAction(event.target, '[data-page-ai-profile-select]');
|
||
if (pageAiProfileSelect instanceof HTMLSelectElement) {
|
||
void pageAiSwitchProfile(pageAiProfileSelect.value);
|
||
return true;
|
||
}
|
||
var pageAiBoardWorker = closestAction(event.target, '[data-page-ai-board-worker]');
|
||
if (pageAiBoardWorker instanceof HTMLSelectElement) {
|
||
pageUiState.pageAiBoardWorkerId = String(pageAiBoardWorker.value || pageAiBoardDefaultWorkerId()).trim() || pageAiBoardDefaultWorkerId();
|
||
pageUiState.pageAiBoardModelOverride = pageAiBoardSelectedModelOverride();
|
||
pageAiPersistAiPreference('board.worker_preset_id', pageUiState.pageAiBoardWorkerId);
|
||
pageAiPersistAiPreference('board.model_override', pageUiState.pageAiBoardModelOverride);
|
||
renderPageAiControls();
|
||
pageAiApplyBoardFirstShell();
|
||
return true;
|
||
}
|
||
var pageAiBoardModel = closestAction(event.target, '[data-page-ai-board-model]');
|
||
if (pageAiBoardModel instanceof HTMLSelectElement) {
|
||
pageUiState.pageAiBoardModelOverride = String(pageAiBoardModel.value || pageAiBoardSelectedModelOverride()).trim() || pageAiBoardSelectedModelOverride();
|
||
pageAiPersistAiPreference('board.model_override', pageUiState.pageAiBoardModelOverride);
|
||
var current = pageAiCurrentSession();
|
||
if (current) {
|
||
current.modelOverride = pageUiState.pageAiBoardModelOverride;
|
||
current.updatedAt = Date.now();
|
||
}
|
||
pageAiPersistSessions();
|
||
renderPageAiControls();
|
||
pageAiApplyBoardFirstShell();
|
||
return true;
|
||
}
|
||
var pageAiBoardWorkflow = closestAction(event.target, '[data-page-ai-board-workflow]');
|
||
if (pageAiBoardWorkflow instanceof HTMLSelectElement) {
|
||
pageUiState.pageAiBoardWorkflowId = String(pageAiBoardWorkflow.value || pageAiBoardDefaultWorkflowId()).trim() || pageAiBoardDefaultWorkflowId();
|
||
pageAiPersistAiPreference('board.workflow_id', pageUiState.pageAiBoardWorkflowId);
|
||
renderPageAiControls();
|
||
pageAiApplyBoardFirstShell();
|
||
return true;
|
||
}
|
||
var pageAiSkillSourceSelect = closestAction(event.target, '[data-page-ai-skill-source-select]');
|
||
if (pageAiSkillSourceSelect instanceof HTMLSelectElement) {
|
||
pageAiSetSkillSource(pageAiSkillSourceSelect.value);
|
||
return true;
|
||
}
|
||
var pageAiSessionAgentFilter = closestAction(event.target, '[data-page-ai-session-agent-filter]');
|
||
if (pageAiSessionAgentFilter instanceof HTMLSelectElement) {
|
||
pageUiState.pageAiSessionAgentFilter = String(pageAiSessionAgentFilter.value || 'all').trim() || 'all';
|
||
renderPageAiControls();
|
||
renderPageAiConversation();
|
||
return true;
|
||
}
|
||
var pageAiSessionStatusFilter = closestAction(event.target, '[data-page-ai-session-status-filter]');
|
||
if (pageAiSessionStatusFilter instanceof HTMLSelectElement) {
|
||
pageUiState.pageAiSessionStatusFilter = String(pageAiSessionStatusFilter.value || 'all').trim() || 'all';
|
||
renderPageAiControls();
|
||
renderPageAiConversation();
|
||
return true;
|
||
}
|
||
var pageAiHideHermesBuiltin = closestAction(event.target, '[data-page-ai-hide-hermes-builtin]');
|
||
if (pageAiHideHermesBuiltin instanceof HTMLInputElement) {
|
||
pageAiSetHideHermesBuiltinSkills(pageAiHideHermesBuiltin.checked);
|
||
return true;
|
||
}
|
||
var pageAiReasonixMemory = closestAction(event.target, '[data-page-ai-reasonix-memory]');
|
||
if (pageAiReasonixMemory instanceof HTMLInputElement) {
|
||
pageAiSetReasonixMemoryEnabled(pageAiReasonixMemory.checked);
|
||
return true;
|
||
}
|
||
var pageAiContextSelect = closestAction(event.target, '[data-page-ai-context-scope]');
|
||
if (pageAiContextSelect instanceof HTMLSelectElement) {
|
||
pageAiSetContextScope(pageAiContextSelect.value);
|
||
renderPageAiControls();
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
function installPageAiDelegates() {
|
||
if (pageAiDelegatesInstalled) return;
|
||
pageAiDelegatesInstalled = true;
|
||
var helpers = { closestAction: closestAction };
|
||
document.addEventListener('click', function(event) {
|
||
handlePageAiClick(event, helpers);
|
||
});
|
||
document.addEventListener('submit', function(event) {
|
||
var form = event.target && event.target.closest ? event.target.closest('[data-page-ai-opencode-composer]') : null;
|
||
if (!form) return;
|
||
event.preventDefault();
|
||
var input = form.querySelector('[data-page-ai-opencode-input]');
|
||
var text = input instanceof HTMLTextAreaElement ? input.value.trim() : '';
|
||
if (!text) return;
|
||
input.value = '';
|
||
void pageAiSendOpencodeMessage(text).catch(function(error) {
|
||
pageUiState.pageAiOpencodeStatusError = error instanceof Error ? error.message : String(error);
|
||
pageAiRenderOpencodeHostChrome();
|
||
});
|
||
});
|
||
document.addEventListener('pointerdown', function(event) {
|
||
handlePageAiPointerDown(event, helpers);
|
||
});
|
||
document.addEventListener('keydown', function(event) {
|
||
handlePageAiKeyDown(event, helpers);
|
||
});
|
||
document.addEventListener('input', function(event) {
|
||
handlePageAiInput(event, helpers);
|
||
});
|
||
document.addEventListener('change', function(event) {
|
||
handlePageAiChange(event, helpers);
|
||
});
|
||
document.addEventListener('visibilitychange', function() {
|
||
if (document.visibilityState !== 'visible' || !pageUiState.pageAiOpen) return;
|
||
void pageAiCheckAndResumeActiveRun().catch(function(error) {
|
||
pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
|
||
renderPageAiControls();
|
||
});
|
||
});
|
||
}
|
||
|
||
|
||
return {
|
||
ensurePageAiStateFacade: (...args) => ensurePageAiStateFacade(...args),
|
||
installPageAiDelegates: (...args) => installPageAiDelegates(...args),
|
||
handlePageAiClick: (...args) => handlePageAiClick(...args),
|
||
handlePageAiKeyDown: (...args) => handlePageAiKeyDown(...args),
|
||
handlePageAiInput: (...args) => handlePageAiInput(...args),
|
||
handlePageAiChange: (...args) => handlePageAiChange(...args),
|
||
openPageAiDrawer: (...args) => openPageAiDrawer(...args),
|
||
closePageAiDrawer: (...args) => closePageAiDrawer(...args),
|
||
isPageAiDrawerOpen: (...args) => isPageAiDrawerOpen(...args),
|
||
ensurePageAiDrawer: (...args) => ensurePageAiDrawer(...args),
|
||
sendPageAiMessage: (...args) => sendPageAiMessage(...args),
|
||
pageAiOpenHermesSettings: (...args) => pageAiOpenHermesSettings(...args),
|
||
pageAiStopRun: (...args) => pageAiStopRun(...args),
|
||
pageAiLoadGatewayHealth: (...args) => pageAiLoadGatewayHealth(...args),
|
||
renderPageAiControls: (...args) => renderPageAiControls(...args),
|
||
renderPageAiConversation: (...args) => renderPageAiConversation(...args),
|
||
renderPageAiProviderButtons: (...args) => renderPageAiProviderButtons(...args),
|
||
renderPageAiSuggestions: (...args) => renderPageAiSuggestions(...args),
|
||
pageAiSaveProfileMemory: (...args) => pageAiSaveProfileMemory(...args),
|
||
pageAiToggleSkill: (...args) => pageAiToggleSkill(...args),
|
||
pageAiToggleTool: (...args) => pageAiToggleTool(...args),
|
||
pageAiResumeBackendSession: (...args) => pageAiResumeBackendSession(...args),
|
||
pageAiRenameBackendSession: (...args) => pageAiRenameBackendSession(...args),
|
||
pageAiExportBackendSession: (...args) => pageAiExportBackendSession(...args),
|
||
pageAiDeleteBackendSession: (...args) => pageAiDeleteBackendSession(...args),
|
||
pageAiResolvePermission: (...args) => pageAiResolvePermission(...args),
|
||
pageAiOpenLocation: (...args) => pageAiOpenLocation(...args),
|
||
pageAiSetActiveSession: (...args) => pageAiSetActiveSession(...args),
|
||
pageAiStartNewSession: (...args) => pageAiStartNewSession(...args),
|
||
pageAiLoadSessions: (...args) => pageAiLoadSessions(...args),
|
||
pageAiLoadBackendSessions: (...args) => pageAiLoadBackendSessions(...args),
|
||
pageAiCancelQueuedRun: (...args) => pageAiCancelQueuedRun(...args),
|
||
pageAiSearchBackendSessions: (...args) => pageAiSearchBackendSessions(...args),
|
||
pageAiSetTargetPopoverOpen: (...args) => pageAiSetTargetPopoverOpen(...args),
|
||
pageAiSelectTarget: (...args) => pageAiSelectTarget(...args),
|
||
pageAiPersistSessions: (...args) => pageAiPersistSessions(...args),
|
||
pageAiLoadProfiles: (...args) => pageAiLoadProfiles(...args),
|
||
pageAiLoadProfileMemory: (...args) => pageAiLoadProfileMemory(...args),
|
||
pageAiLoadSkills: (...args) => pageAiLoadSkills(...args),
|
||
pageAiSwitchProfile: (...args) => pageAiSwitchProfile(...args),
|
||
pageAiToggleSkillGroup: (...args) => pageAiToggleSkillGroup(...args),
|
||
pageAiSetReasonixMemoryEnabled: (...args) => pageAiSetReasonixMemoryEnabled(...args),
|
||
pageAiSetHideHermesBuiltinSkills: (...args) => pageAiSetHideHermesBuiltinSkills(...args),
|
||
pageAiSetSkillSource: (...args) => pageAiSetSkillSource(...args),
|
||
pageAiSetContextScope: (...args) => pageAiSetContextScope(...args),
|
||
pageAiSetAgentId: (...args) => pageAiSetAgentId(...args),
|
||
pageAiToggleContextRef: (...args) => pageAiToggleContextRef(...args),
|
||
pageAiSetContextPopoverOpen: (...args) => pageAiSetContextPopoverOpen(...args),
|
||
pageAiSetAgentPopoverOpen: (...args) => pageAiSetAgentPopoverOpen(...args),
|
||
pageAiLoadAllowedRoots: (...args) => pageAiLoadAllowedRoots(...args),
|
||
pageAiEnsureOpencodeHostDrawer: (...args) => pageAiEnsureOpencodeHostDrawer(...args),
|
||
pageAiLoadOpencodeStatus: (...args) => pageAiLoadOpencodeStatus(...args),
|
||
pageAiLoadOpencodeDiff: (...args) => pageAiLoadOpencodeDiff(...args),
|
||
pageAiOpenOpencodeChangedFile: (...args) => pageAiOpenOpencodeChangedFile(...args),
|
||
pageAiRefreshOpencodeCurrentPage: (...args) => pageAiRefreshOpencodeCurrentPage(...args),
|
||
updatePageAiTriggerState: (...args) => updatePageAiTriggerState(...args)
|
||
};
|
||
}
|