import { createSidebarPageAiMarkdownRuntime } from './sidebar-page-ai-markdown-runtime.js';
import { createSidebarPageAiPermissionRuntime } from './sidebar-page-ai-permission-runtime.js';
import { createSidebarPageAiProfileRuntime } from './sidebar-page-ai-profile-runtime.js';
import { createSidebarPageAiRenderRuntime } from './sidebar-page-ai-render-runtime.js';
import { createSidebarPageAiSessionRuntime } from './sidebar-page-ai-session-runtime.js';
import { createSidebarPageAiSkillRuntime } from './sidebar-page-ai-skill-runtime.js';
import { createSidebarPageAiTargetRuntime } from './sidebar-page-ai-target-runtime.js';

export function createSidebarPageAiRuntime(context) {
  const {
    buildLocalFileOpenUrl,
    currentDocumentId,
    currentPageAggregate,
    currentPageOptions,
    currentRootUri,
    currentSourceKind,
    escapeHtml,
    cssEscape,
    openLocalResourceInActiveTab,
    pageUiState,
    resolveWorkspaceId,
    searchText,
  } = context;
  var PAGE_AI_SESSION_STORAGE_VERSION = 3;
  // Page AI 请求合同：agentId 只负责路由；contextRefs 是用户勾选的上下文地址；
  // allowedRoots 只展示 SQLite directory_grants 解析结果，服务端仍会按当前用户重算；
  // runTargetSnapshot 必须来自 OpenEditorsSnapshot，避免运行中 UI 切换导致目标漂移。
  var PAGE_AI_AGENT_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;

  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: [],
      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: '',
      pageAiSessionSearchQuery: '',
      pageAiSessionSearchResults: [],
      pageAiSessionSearchTimer: 0,
      pageAiSessionAgentFilter: 'all',
      pageAiSelectedSessionIds: {},
      pageAiSessionError: '',
      pageAiPermissionRequests: []
    };
    Object.keys(defaults).forEach(function(key) {
      if (typeof state[key] === 'undefined') {
        state[key] = clonePageAiDefaultValue(defaults[key]);
      }
    });
    if (!state.pageAiSkillCatalogs || typeof state.pageAiSkillCatalogs !== 'object') {
      state.pageAiSkillCatalogs = clonePageAiDefaultValue(defaults.pageAiSkillCatalogs);
    }
    if (!state.pageAiProfileMemory || typeof state.pageAiProfileMemory !== 'object') {
      state.pageAiProfileMemory = clonePageAiDefaultValue(defaults.pageAiProfileMemory);
    }
    if (!state.pageAiProfileMemoryDrafts || typeof state.pageAiProfileMemoryDrafts !== 'object') {
      state.pageAiProfileMemoryDrafts = clonePageAiDefaultValue(defaults.pageAiProfileMemoryDrafts);
    }
    if (!state.pageAiSkills || typeof state.pageAiSkills !== 'object') {
      state.pageAiSkills = clonePageAiDefaultValue(defaults.pageAiSkills);
    }
    return state;
  }

  ensurePageAiStateFacade(pageUiState);

  function closestAction(target, selector) {
    var node = target && target.nodeType === Node.TEXT_NODE ? target.parentElement : target;
    return node && typeof node.closest === 'function' ? node.closest(selector) : null;
  }

  const pageAiMarkdown = createSidebarPageAiMarkdownRuntime({ escapeHtml });
  const textFromUnknown = (...args) => pageAiMarkdown.textFromUnknown(...args);
  const renderPageAiMarkdown = (...args) => pageAiMarkdown.renderPageAiMarkdown(...args);
  let pageAiRenderRuntime = null;

  function pageAiRenderApi() {
    if (!pageAiRenderRuntime) throw new Error('page_ai_render_runtime_not_ready');
    return pageAiRenderRuntime;
  }

  function ensurePageAiDrawer(...args) { return pageAiRenderApi().ensurePageAiDrawer(...args); }
  function humanizePageAiResponse(...args) { return pageAiRenderApi().humanizePageAiResponse(...args); }
  function pageAiContextButtonSummary(...args) { return pageAiRenderApi().pageAiContextButtonSummary(...args); }
  function pageAiContextRefDetail(...args) { return pageAiRenderApi().pageAiContextRefDetail(...args); }
  function pageAiContextRefDisabled(...args) { return pageAiRenderApi().pageAiContextRefDisabled(...args); }
  function pageAiContextRefLabel(...args) { return pageAiRenderApi().pageAiContextRefLabel(...args); }
  function pageAiContextScopeLabel(...args) { return pageAiRenderApi().pageAiContextScopeLabel(...args); }
  function pageAiCurrentAgentSelectionLabel(...args) { return pageAiRenderApi().pageAiCurrentAgentSelectionLabel(...args); }
  function pageAiCurrentModelLabel(...args) { return pageAiRenderApi().pageAiCurrentModelLabel(...args); }
  function pageAiFilteredSkillEntries(...args) { return pageAiRenderApi().pageAiFilteredSkillEntries(...args); }
  function pageAiFormatChangedFiles(...args) { return pageAiRenderApi().pageAiFormatChangedFiles(...args); }
  function pageAiMemoryFileLabel(...args) { return pageAiRenderApi().pageAiMemoryFileLabel(...args); }
  function pageAiRenderAgentProfileOption(...args) { return pageAiRenderApi().pageAiRenderAgentProfileOption(...args); }
  function pageAiRunStatusLabel(...args) { return pageAiRenderApi().pageAiRunStatusLabel(...args); }
  function pageAiSuggestions(...args) { return pageAiRenderApi().pageAiSuggestions(...args); }
  function pageAiTargetDetail(...args) { return pageAiRenderApi().pageAiTargetDetail(...args); }
  function pageAiTargetLabel(...args) { return pageAiRenderApi().pageAiTargetLabel(...args); }
  function renderPageAiControls(...args) { return pageAiRenderApi().renderPageAiControls(...args); }
  function renderPageAiConversation(...args) { return pageAiRenderApi().renderPageAiConversation(...args); }
  function renderPageAiProviderButtons(...args) { return pageAiRenderApi().renderPageAiProviderButtons(...args); }
  function renderPageAiSuggestions(...args) { return pageAiRenderApi().renderPageAiSuggestions(...args); }

  function readLocalEditorBlocks() {
    var editor = document.querySelector('[data-testid="mnote-leptos-tiptap-island-editor-root"] .editor-surface .ProseMirror');
    if (!(editor instanceof HTMLElement)) return [];
    return Array.from(editor.children).filter(function(node) {
      return node instanceof HTMLElement;
    }).map(function(node, index) {
      var tag = String(node.tagName || '').toUpperCase();
      var headingMatch = tag.match(/^H([1-6])$/);
      var type = headingMatch ? 'heading' : 'paragraph';
      var text = searchText(node.textContent || '');
      var id = searchText(node.getAttribute('data-id') || node.id || 'local-block-' + index);
      var props = headingMatch ? { level: Number(headingMatch[1]) } : {};
      return { id: id, type: type, props: props, content: text };
    }).filter(function(block) {
      return block.content || block.type === 'heading';
    });
  }

  function buildPageAiLocalSubtree(blocks, title) {
    var documentId = currentDocumentId() || 'current-page';
    var rootNodeId = 'page:' + documentId;
    var headingCounters = [0, 0, 0, 0, 0, 0];
    var headingStack = [];
    var nodes = [{
      id: rootNodeId,
      nodeId: rootNodeId,
      nodeType: 'page',
      blockId: null,
      blockType: 'page',
      title: title || '',
      parentNodeId: null,
      headingLevel: null
    }];
    var outline = [];
    var evidence = [];
    blocks.forEach(function(block, index) {
      var level = block.type === 'heading' ? Math.max(1, Math.min(6, Number(block.props && block.props.level || 1))) : null;
      if (level != null) {
        while (headingStack.length && headingStack[headingStack.length - 1].level >= level) headingStack.pop();
      }
      var parentNodeId = headingStack.length ? headingStack[headingStack.length - 1].nodeId : rootNodeId;
      var nodeId = 'local-node:' + String(block.id || index);
      var titleText = block.content || (block.type === 'heading' ? '未命名标题' : '');
      nodes.push({
        id: nodeId,
        nodeId: nodeId,
        nodeType: 'block',
        blockId: block.id,
        blockType: block.type,
        title: titleText,
        parentNodeId: parentNodeId,
        headingLevel: level
      });
      if (level != null) {
        headingCounters[level - 1] += 1;
        for (var i = level; i < headingCounters.length; i += 1) headingCounters[i] = 0;
        var numbering = headingCounters.slice(0, level).filter(function(count) { return count > 0; }).join('.');
        outline.push({
          id: 'local-outline:' + String(block.id || index),
          nodeId: nodeId,
          anchorBlockId: block.id,
          level: level,
          title: titleText,
          numbering: numbering
        });
        headingStack.push({ level: level, nodeId: nodeId });
      }
      if (titleText) {
        evidence.push({
          id: 'local-evidence:' + String(block.id || index),
          nodeId: nodeId,
          anchorBlockId: block.id,
          text: titleText,
          kind: block.type
        });
      }
    });
    return {
      projectionId: 'local-editor-dom:' + documentId,
      rootNode: {
        id: rootNodeId,
        documentId: documentId,
        title: title || '',
        nodeType: 'page'
      },
      subtree: {
        rootNodeId: rootNodeId,
        nodes: nodes
      },
      outline: outline,
      evidence: evidence,
      stats: {
        nodeCount: nodes.length,
        headingCount: outline.length,
        evidenceCount: evidence.length
      },
      source: 'local'
    };
  }

  function currentPageAiContextSnapshot() {
    var aggregate = currentPageAggregate();
    var body = aggregate.body || {};
    var serverSubtree = aggregate.tree && aggregate.tree.pageSubtree ? aggregate.tree.pageSubtree : null;
    return {
      aggregate: aggregate,
      body: body,
      subtree: serverSubtree,
      pageSubtreeSource: serverSubtree ? 'server' : 'none'
    };
  }

  function 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 pageAiFilteredHistoryRows = (...args) => pageAiProfileRuntime.pageAiFilteredHistoryRows(...args);
  const pageAiTimestamp = (...args) => pageAiProfileRuntime.pageAiTimestamp(...args);
  const pageAiUsageSummary = (...args) => pageAiProfileRuntime.pageAiUsageSummary(...args);
  const pageAiPermissionRuntime = createSidebarPageAiPermissionRuntime({
    documentRef: document,
    pageAiPreviewValue,
    pageUiState,
    renderPageAiConversation,
  });
  const pageAiPermissionMessage = (...args) => pageAiPermissionRuntime.pageAiPermissionMessage(...args);
  const pageAiApplyPermissionEvent = (...args) => pageAiPermissionRuntime.pageAiApplyPermissionEvent(...args);
  const pageAiResolvePermission = (...args) => pageAiPermissionRuntime.pageAiResolvePermission(...args);
  const pageAiHidePermissionDialog = (...args) => pageAiPermissionRuntime.pageAiHidePermissionDialog(...args);
  const pageAiShowPermissionDialog = (...args) => pageAiPermissionRuntime.pageAiShowPermissionDialog(...args);
  const pageAiSessionRuntime = createSidebarPageAiSessionRuntime({
    currentDocumentId,
    currentRootUri,
    currentSourceKind,
    documentRef: document,
    pageAiApplyRuntimeState,
    pageAiCurrentAgentId,
    pageAiCurrentProfile,
    pageAiErrorMessage,
    pageAiNormalizeAgentId,
    pageAiNormalizeArray,
    pageAiNormalizeChatOnlyProfileId,
    pageAiPermissionMessage,
    pageAiPreviewValue,
    pageAiRunProfile,
    pageAiSetActiveProfile,
    pageAiTimestamp,
    pageUiState,
    renderPageAiControls,
    renderPageAiConversation,
    resolveWorkspaceId,
    sessionStorageVersion: PAGE_AI_SESSION_STORAGE_VERSION,
    windowRef: window,
  });
  const pageAiStorageKey = (...args) => pageAiSessionRuntime.pageAiStorageKey(...args);
  const pageAiBackendSessionQuery = (...args) => pageAiSessionRuntime.pageAiBackendSessionQuery(...args);
  const pageAiNewSession = (...args) => pageAiSessionRuntime.pageAiNewSession(...args);
  const pageAiNormalizeSessions = (...args) => pageAiSessionRuntime.pageAiNormalizeSessions(...args);
  const pageAiNormalizeBackendSessionRow = (...args) => pageAiSessionRuntime.pageAiNormalizeBackendSessionRow(...args);
  const pageAiMergeSessions = (...args) => pageAiSessionRuntime.pageAiMergeSessions(...args);
  const pageAiSessionStorageLabel = (...args) => pageAiSessionRuntime.pageAiSessionStorageLabel(...args);
  const pageAiLoadSessions = (...args) => pageAiSessionRuntime.pageAiLoadSessions(...args);
  const pageAiLoadBackendSessions = (...args) => pageAiSessionRuntime.pageAiLoadBackendSessions(...args);
  const pageAiMessageFromRuntimeEvent = (...args) => pageAiSessionRuntime.pageAiMessageFromRuntimeEvent(...args);
  const pageAiApplyBackendSessionDetail = (...args) => pageAiSessionRuntime.pageAiApplyBackendSessionDetail(...args);
  const pageAiLoadBackendSessionDetail = (...args) => pageAiSessionRuntime.pageAiLoadBackendSessionDetail(...args);
  const pageAiSearchBackendSessions = (...args) => pageAiSessionRuntime.pageAiSearchBackendSessions(...args);
  const pageAiPersistSessions = (...args) => pageAiSessionRuntime.pageAiPersistSessions(...args);
  const pageAiEnsureHermesSession = (...args) => pageAiSessionRuntime.pageAiEnsureHermesSession(...args);
  const pageAiRestoreHermesSession = (...args) => pageAiSessionRuntime.pageAiRestoreHermesSession(...args);
  const pageAiCurrentSession = (...args) => pageAiSessionRuntime.pageAiCurrentSession(...args);
  const pageAiSyncCurrentSessionMessages = (...args) => pageAiSessionRuntime.pageAiSyncCurrentSessionMessages(...args);
  const pageAiSetActiveSession = (...args) => pageAiSessionRuntime.pageAiSetActiveSession(...args);
  const pageAiStartNewSession = (...args) => pageAiSessionRuntime.pageAiStartNewSession(...args);
  const pageAiRenameBackendSession = (...args) => pageAiSessionRuntime.pageAiRenameBackendSession(...args);
  const pageAiDeleteBackendSession = (...args) => pageAiSessionRuntime.pageAiDeleteBackendSession(...args);
  const 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,
    pageAiSessionStorageLabel,
    pageAiSessionSelected,
    pageAiSelectedSessionCount,
    pageAiSkillEnabled,
    pageAiSkillGroupCollapsed,
    pageAiSkillListEntries,
    pageAiSkillOriginLabel,
    pageAiSkillSourceOptions,
    pageAiSkillSourceParts,
    pageAiToggleableSkillEntries,
    pageAiUsageSummary,
    pageUiState,
    renderPageAiMarkdown,
    searchText,
  });

  function pageAiSetAgentId(agentId) {
    var next = pageAiNormalizeAgentId(agentId);
    var record = pageAiAgentRecord(next);
    pageUiState.pageAiAgentId = next;
    pageUiState.pageAiAcpRuntime = record.acpRuntime || 'reasonix';
    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 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 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);
    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 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;
    var item = pageUiState.pageAiQueuedItems.find(function(entry) {
      return entry.queueId === queueId;
    });
    var sessionId = String(item && item.sessionId || pageUiState.pageAiActiveSessionId || '').trim();
    if (!sessionId) return;
    try {
      var response = await fetch('/api/hermes/client/sessions/' + encodeURIComponent(sessionId) + '/queue/' + encodeURIComponent(queueId), {
        method: 'DELETE',
        headers: { 'accept': 'application/json' }
      });
      var payload = await response.json().catch(function(){ return null; });
      if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'queue_cancel_failed_' + response.status));
      pageUiState.pageAiQueuedItems = pageUiState.pageAiQueuedItems.filter(function(entry) {
        return entry.queueId !== queueId;
      });
      var queueLength = Number(payload && (payload.queueLength || payload.queue_length || 0));
      pageUiState.pageAiQueueLength = Number.isFinite(queueLength) ? Math.max(0, queueLength) : pageUiState.pageAiQueuedItems.length;
      pageUiState.pageAiMessages.push({ role: 'assistant', content: '已取消一条 AI 队列项。' });
    } catch (error) {
      pageUiState.pageAiMessages.push({
        role: 'assistant',
        content: '取消 AI 队列项失败：' + (error instanceof Error ? error.message : String(error))
      });
    }
    renderPageAiConversation();
    renderPageAiControls();
  }

  function pageAiSetContextScope(scope) {
    var next = String(scope || '').trim() || 'page';
    pageUiState.pageAiContextScope = next;
    document.documentElement.setAttribute('data-mnote-page-ai-context-scope', next);
  }


  function pageAiHermesSettingsUrl() {
    var configured = String(window.__mnoteHermesSettingsUrl || '').trim();
    return configured || '';
  }

  function pageAiOpenHermesSettings() {
    var url = pageAiHermesSettingsUrl();
    if (url) {
      window.open(url, '_blank', 'noopener,noreferrer');
      return;
    }
    pageUiState.pageAiProfileError = '未配置 Hermes 设置入口：请设置 MNOTE_WEB_HERMES_UPSTREAM_URL。';
    renderPageAiControls();
  }

  function pageAiNormalizeTools(payload) {
    var upstream = pageAiUnwrapUpstream(payload) || {};
    var tools = pageAiNormalizeArray(upstream.tools || upstream);
    return tools.map(function(tool) {
      var name = String(tool && (tool.name || tool.toolName || tool.tool) || '').trim();
      if (!name) return null;
      return {
        name: name,
        description: String(tool && tool.description || '').trim(),
        scope: String(tool && (tool.scope || tool.requiredScope || '') || '').trim(),
        kind: String(tool && (tool.kind || tool.mode || '') || '').trim(),
        status: String(tool && (tool.status || tool.permission || 'available') || '').trim(),
        enabled: tool && tool.enabled !== false,
        unavailableReason: String(tool && (tool.unavailableReason || tool.reason || '') || '').trim()
      };
    }).filter(Boolean);
  }

  function pageAiErrorMessage(payload, fallback) {
    if (!payload || typeof payload !== 'object') return fallback;
    return String(payload.message || payload.error || payload.code || fallback || '').trim() || fallback;
  }

  function pageAiNormalizeGatewayHealth(payload) {
    var upstream = pageAiUnwrapUpstream(payload) || payload || {};
    return {
      ok: Boolean(upstream.ok),
      profile: upstream.profile || null,
      gateway: upstream.gateway || null,
      suggestions: pageAiNormalizeArray(upstream.suggestions).map(function(item) {
        return String(item || '').trim();
      }).filter(Boolean)
    };
  }

  function pageAiSetDraftForSection(section, value) {
    pageUiState.pageAiProfileMemoryDrafts[section] = String(value == null ? '' : value);
  }

  function pageAiApplyProfileMemory(payload) {
    var upstream = pageAiUnwrapUpstream(payload) || {};
    pageUiState.pageAiProfileMemory = {
      memory: String(upstream.memory || ''),
      user: String(upstream.user || ''),
      soul: String(upstream.soul || '')
    };
    pageUiState.pageAiProfileMemoryDrafts = {
      memory: pageUiState.pageAiProfileMemory.memory,
      user: pageUiState.pageAiProfileMemory.user,
      soul: pageUiState.pageAiProfileMemory.soul
    };
  }

  async function pageAiLoadTools() {
    try {
      var 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 {
      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() {
    pageUiState.pageAiAgentId = pageAiCurrentAgentId();
    pageAiEnsureContextRefState();
    pageAiLoadSessions();
    renderPageAiSuggestions();
    renderPageAiConversation();
    renderPageAiProviderButtons();
    renderPageAiControls();
    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();
    });
    void pageAiLoadBackendSessions().then(function() {
      return pageAiCheckAndResumeActiveRun();
    }).catch(function(error) {
      pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
      renderPageAiControls();
    });
    Promise.all([
      pageAiLoadProfiles(),
      pageAiLoadProfileMemory(),
      pageAiLoadAgentDescriptors(),
      pageAiLoadSkills(),
      pageAiLoadTools(),
      pageAiLoadGatewayHealth(),
      pageAiLoadAiPreferences(),
      pageAiLoadAllowedRoots(),
      pageAiLoadBackendSessions()
    ]).then(function() {
      renderPageAiControls();
    }).then(function() {
      return pageAiCheckAndResumeActiveRun().catch(function(error) {
        pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
        renderPageAiControls();
        return null;
      });
    }).then(function() {
      return pageAiRestoreHermesSession();
    }).then(function() {
      renderPageAiControls();
    }).catch(function(error) {
      pageUiState.pageAiMessages.push({
        role: 'assistant',
        content: '当前 AI 不可用：' + (error instanceof Error ? error.message : String(error))
      });
      renderPageAiConversation();
      renderPageAiControls();
    });
  }

  function closePageAiDrawer() {
    var drawer = document.querySelector('[data-testid="wolai-page-ai-drawer"]');
    if (drawer instanceof HTMLElement) drawer.hidden = true;
    pageUiState.pageAiOpen = false;
    updatePageAiTriggerState();
  }

  function isPageAiDrawerOpen() {
    var drawer = document.querySelector('[data-testid="wolai-page-ai-drawer"]');
    return drawer instanceof HTMLElement && !drawer.hidden;
  }

  async function streamPageAiResponse(response, onEvent) {
    if (!response.body || typeof response.body.getReader !== 'function') return;
    var reader = response.body.getReader();
    var decoder = new TextDecoder();
    var buffer = '';
    while (true) {
      var chunk = await reader.read();
      if (chunk.done) break;
      buffer += decoder.decode(chunk.value, { stream: true });
      var frames = buffer.split('\n\n');
      buffer = frames.pop() || '';
      frames.forEach(function(frame) {
        var eventName = '';
        var dataLines = [];
        frame.split('\n').forEach(function(line) {
          if (line.startsWith('event:')) eventName = line.slice(6).trim();
          if (line.startsWith('data:')) dataLines.push(line.slice(5).trim());
        });
        var payloadText = dataLines.join('\n');
        if (!eventName && payloadText) {
          try {
            var parsed = JSON.parse(payloadText);
            eventName = parsed && parsed.event ? String(parsed.event) : '';
          } catch (_) {}
        }
        if (eventName) onEvent(eventName, payloadText);
      });
    }
  }

  function pageAiDecodeDeltaText(payloadText) {
    try {
      var payload = JSON.parse(payloadText || 'null');
      return String((payload && (payload.text || payload.delta)) || '');
    } catch (_) {
      return String(payloadText || '');
    }
  }

  function 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 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 : {};
    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;
      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) {
    var allowQueue = ['running', 'tool_calling'].indexOf(pageUiState.pageAiRunStatus) >= 0;
    if (pageUiState.pageAiBusy && !allowQueue) return;
    var prompt = searchText(text);
    if (!prompt) return;
    if (!allowQueue) pageUiState.pageAiBusy = true;
    var currentSession = null;
    try {
      await pageAiEnsureHermesSession();
      if (!allowQueue) pageAiSetRunStatus('queued');
      renderPageAiControls();
      var contextSnapshot = currentPageAiContextSnapshot();
      var scopedContext = pageAiScopedPageContext(contextSnapshot);
      scopedContext.editorTarget = currentPageAiScopedEditorTarget();
      assertPageAiTargetInCurrentWorkspace(scopedContext.editorTarget);
      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 response = await fetch('/api/hermes/client/runs', {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({
          workspaceId: resolveWorkspaceId(document.body),
          documentId: currentDocumentId(),
          sourceKind: currentSourceKind(),
          rootUri: currentRootUri(),
          agentId: pageAiCurrentAgentId(),
          sessionId: pageUiState.pageAiActiveSessionId,
          acpSessionId: currentSession && currentSession.acpSessionId ? currentSession.acpSessionId : '',
          profile: runProfile,
          profileId: pageUiState.pageAiAcpRuntime === 'hermes' ? runProfile : '',
          acpRuntime: pageUiState.pageAiAcpRuntime || 'reasonix',
          contextScope: pageUiState.pageAiContextScope,
          contextRefs: contextRefs,
          allowedRoots: allowedRoots,
          skillPreferences: {
            mnote: pageAiSkillPreferenceTable('mnote', ''),
            reasonix: pageAiSkillPreferenceTable('reasonix', ''),
            hermes: pageAiSkillPreferenceTable('hermes', runProfile)
          },
          message: prompt,
          model: pageAiMnoteToolModel(),
          pageContext: requestPageContext,
          editorTarget: scopedContext.editorTarget,
          runTargetSnapshot: runTargetSnapshot,
          targetPackage: agentTargetPackage,
          selectedBlockId: scopedContext.selectedBlockId,
          selectedText: scopedContext.selectedText || '',
          traceId: 'page-ai-run-' + Date.now().toString(36)
        })
      });
      if (!response.ok) {
        var errorPayload = await response.json().catch(function(){ return null; });
        throw new Error(pageAiErrorMessage(errorPayload, 'page_ai_failed_' + response.status));
      }
      var runPayload = await response.json().catch(function(){ return null; });
      if (pageAiApplyQueuedRun(runPayload)) {
        pageUiState.pageAiMessages.push({
          role: 'assistant',
          content: '已加入 AI 队列，前一条 run 完成后继续处理。'
        });
        if (currentSession) {
          currentSession.messages = pageUiState.pageAiMessages.slice();
          currentSession.updatedAt = Date.now();
        }
        pageAiPersistSessions();
        renderPageAiConversation();
        renderPageAiControls();
        return;
      }
      if (allowQueue) {
        throw new Error('hermes_queue_expected_queued_response');
      }
      var upstream = runPayload && runPayload.upstream ? runPayload.upstream : runPayload;
      var runId = upstream && (upstream.run_id || upstream.runId);
      if (!runId) throw new Error('hermes_run_missing_run_id');
      var runTraceId = String((upstream && (upstream.trace_id || upstream.traceId)) || (runPayload && (runPayload.trace_id || runPayload.traceId)) || '');
      pageAiSetRunStatus('running', runId);
      pageAiSetRunTargetSnapshot(Object.assign({}, runTargetSnapshot, {
        runId: runId,
        sessionId: pageUiState.pageAiActiveSessionId,
        traceId: runTraceId
      }));
      renderPageAiControls();
      var eventResponse = await fetch('/api/hermes/client/events/' + encodeURIComponent(runId), {
        headers: { 'accept': 'text/event-stream' }
      });
      if (!eventResponse.ok) {
        var eventError = await eventResponse.json().catch(function(){ return null; });
        throw new Error(pageAiErrorMessage(eventError, 'hermes_events_failed_' + eventResponse.status));
      }
      var assistantText = '';
      var autoCitations = [];
      await streamPageAiResponse(eventResponse, function(eventName, payloadText) {
        pageAiTrackStreamingRunEvent(runId, eventName, payloadText);
        if (eventName === 'message.delta') {
          if (pageUiState.pageAiStoppedRunIds[runId]) return;
          assistantText = pageAiAppendStreamingAssistantDelta(runId, pageAiDecodeDeltaText(payloadText));
        }
        if (eventName === 'thought.delta') {
          try {
            var thoughtPayload = JSON.parse(payloadText || 'null');
            var thoughtText = String((thoughtPayload && (thoughtPayload.delta || thoughtPayload.text)) || '');
            if (thoughtText) {
              var msgs = pageUiState.pageAiMessages;
              var lastThought = msgs.length > 0 && msgs[msgs.length - 1].kind === 'thought' ? msgs[msgs.length - 1] : null;
              if (lastThought) {
                lastThought.content += thoughtText;
              } else {
                msgs.push({ role: 'assistant', kind: 'thought', content: thoughtText });
              }
              pageAiSyncCurrentSessionMessages();
              pageAiPersistSessions();
              renderPageAiConversation();
            }
          } catch (_) {}
        }
        if (eventName === 'usage.updated') {
          try {
            var usagePayload = JSON.parse(payloadText || 'null') || {};
            var sessionForUsage = pageAiCurrentSession();
            if (sessionForUsage) {
              sessionForUsage.usage = {
                source: 'usage_update',
                used: Number(usagePayload.used ?? usagePayload.contextUsed ?? 0),
                size: Number(usagePayload.size ?? usagePayload.contextSize ?? 0)
              };
              pageAiPersistSessions();
            }
          } catch (_) {}
          renderPageAiControls();
        }
        if (eventName === 'permission.requested' || eventName === 'permission.denied' || eventName === 'permission.allowed') {
          pageAiApplyPermissionEvent(eventName, payloadText);
          pageAiSyncCurrentSessionMessages();
          pageAiPersistSessions();
          renderPageAiConversation();
          renderPageAiControls();
        }
        if (eventName === 'run.completed') {
          try {
            var completed = JSON.parse(payloadText || 'null');
            if (completed && completed.output) assistantText = String(completed.output || '');
            if (completed && completed.usage) {
              var completedSession = pageAiCurrentSession();
              if (completedSession) completedSession.usage = completed.usage;
            }
            var agentAudit = completed && completed.agentAudit && typeof completed.agentAudit === 'object' ? completed.agentAudit : null;
            var agentRunReceipt = agentAudit && agentAudit.agentRunReceipt && typeof agentAudit.agentRunReceipt === 'object'
              ? agentAudit.agentRunReceipt
              : null;
            var changedFiles = pageAiNormalizeArray(agentAudit && agentAudit.changedFiles).map(function(file) {
              if (!file || typeof file !== 'object') return file;
              return Object.assign({
                actorId: String(agentAudit && agentAudit.actorId || ''),
                actorType: String(agentAudit && agentAudit.actorType || ''),
                agentKind: String(agentAudit && agentAudit.agentKind || ''),
                runId: runId
              }, file);
            });
            if (changedFiles.length) {
              pageUiState.pageAiMessages.push({
                role: 'tool',
                content: 'agent.changed_files',
                toolCallId: runId + ':agent.changed_files',
                toolName: 'agent.changed_files',
                toolKind: 'audit',
                status: 'completed',
                argsSummary: [agentAudit.rootUri, agentAudit.actorId, agentAudit.agentKind].map(function(value) {
                  return String(value || '').trim();
                }).filter(Boolean).join(' · '),
                resultSummary: String(agentAudit.diffSummary || changedFiles.length + ' changed file(s)'),
                changedFiles: changedFiles,
                traceId: runTraceId,
                auditId: String(agentAudit.eventId || '')
              });
            }
            if (agentRunReceipt || changedFiles.length) {
              try {
                pageAiDispatchReceiptRefresh(agentRunReceipt, changedFiles, agentAudit, runId, runTraceId);
              } catch (_) {}
            }
          } catch (_) {}
          pageAiSetRunStatus('completed', runId);
        }
        if (eventName === 'run.failed') {
          try {
            var failed = JSON.parse(payloadText || 'null');
            assistantText = String((failed && (failed.message || failed.code || failed.error)) || 'AI run failed');
          } catch (_) {}
          pageAiSetRunStatus('failed', runId);
        }
        if (eventName === 'run.aborted' || eventName === 'run.cancelled' || eventName === 'run.canceled') {
          pageUiState.pageAiStoppedRunIds[runId] = true;
          pageAiSetRunStatus('aborted', runId);
        }
        if (eventName === 'session.info.updated') {
          try {
            var infoPayload = JSON.parse(payloadText || 'null') || {};
            var newTitle = String(infoPayload.title || '').trim();
            var acpSessionId = String(infoPayload.acpSessionId || infoPayload.acp_session_id || '').trim();
            var sessionForInfo = pageAiCurrentSession();
            if (acpSessionId && sessionForInfo) {
              sessionForInfo.acpSessionId = acpSessionId;
              sessionForInfo.updatedAt = Date.now();
              pageAiPersistSessions();
            }
            if (newTitle) {
              var sessionForTitle = sessionForInfo || pageAiCurrentSession();
              if (sessionForTitle) {
                sessionForTitle.title = newTitle;
                sessionForTitle.updatedAt = Date.now();
                pageAiPersistSessions();
                renderPageAiControls();
              }
            }
          } catch (_) {}
        }
        if (eventName === 'plan.updated') {
          try {
            var planPayload = JSON.parse(payloadText || 'null') || {};
            var planEntries = Array.isArray(planPayload.entries) ? planPayload.entries : [];
            if (planEntries.length) {
              var planMsgs = pageUiState.pageAiMessages;
              var existingPlan = planMsgs.length > 0 && planMsgs[planMsgs.length - 1].kind === 'plan' ? planMsgs[planMsgs.length - 1] : null;
              if (existingPlan) {
                existingPlan.entries = planEntries;
                existingPlan.updatedAt = Date.now();
              } else {
                planMsgs.push({ role: 'system', kind: 'plan', entries: planEntries, createdAt: Date.now(), updatedAt: Date.now() });
              }
              pageAiSyncCurrentSessionMessages();
              pageAiPersistSessions();
              renderPageAiConversation();
            }
          } catch (_) {}
        }
        if (eventName === 'tool.started' || eventName === 'tool.completed' || eventName === 'tool.failed') {
          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;
      renderPageAiConversation();
      renderPageAiControls();
    }
  }

  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 pageAiSettings = closestAction(event.target, '[data-page-ai-action="open-hermes-settings"]');
    if (pageAiSettings) {
      event.preventDefault();
      pageAiOpenHermesSettings();
      return true;
    }
    var pageAiStop = closestAction(event.target, '[data-page-ai-action="stop-run"]');
    if (pageAiStop) {
      event.preventDefault();
      void pageAiStopRun();
      return true;
    }
    var pageAiRotate = closestAction(event.target, '[data-page-ai-action="rotate"]');
    if (pageAiRotate) {
      event.preventDefault();
      pageUiState.pageAiSuggestionIndex += 1;
      renderPageAiSuggestions();
      return true;
    }
    var pageAiIntent = closestAction(event.target, '[data-page-ai-intent]');
    if (pageAiIntent) {
      event.preventDefault();
      var intentName = pageAiIntent.getAttribute('data-page-ai-intent') || '';
      if (intentName === 'create-summary') void sendPageAiMessage('请通过 Hermes 调用 mnote.artifact.create_summary，为当前页面创建或更新 AI Summary。');
      if (intentName === 'create-ai-note') void sendPageAiMessage('请通过 Hermes 调用 mnote.artifact.create_ai_note，基于当前页面创建一篇新的 AI Note。');
      return true;
    }
    var pageAiTab = closestAction(event.target, '[data-page-ai-tab]');
    if (pageAiTab) {
      event.preventDefault();
      pageUiState.pageAiPage = pageAiTab.getAttribute('data-page-ai-tab') || 'chat';
      if (pageUiState.pageAiPage === 'runtime') void pageAiLoadGatewayHealth();
      if (pageUiState.pageAiPage === 'skills') void pageAiLoadSkills();
      renderPageAiControls();
      renderPageAiConversation();
      return true;
    }
    var pageAiProvider = closestAction(event.target, '[data-page-ai-provider]');
    if (pageAiProvider) {
      event.preventDefault();
      pageUiState.pageAiProvider = pageAiProvider.getAttribute('data-page-ai-provider') || 'hermes';
      renderPageAiProviderButtons();
      return true;
    }
    var pageAiAgent = closestAction(event.target, '[data-page-ai-agent-id]');
    if (pageAiAgent) {
      event.preventDefault();
      var nextAgentId = pageAiAgent.getAttribute('data-page-ai-agent-id') || 'reasonix';
      var nextProfileId = pageAiAgent.getAttribute('data-page-ai-profile-id') || '';
      pageAiSetAgentId(nextAgentId);
      if (nextProfileId) {
        pageUiState.pageAiAgentPopoverOpen = false;
        void pageAiSwitchProfile(nextProfileId);
        renderPageAiControls();
      }
      return true;
    }
    var pageAiAgentButton = closestAction(event.target, '[data-page-ai-agent-button]');
    if (pageAiAgentButton) {
      event.preventDefault();
      pageAiSetAgentPopoverOpen(pageAiAgentButton.getAttribute('aria-expanded') !== 'true');
      return true;
    }
    var pageAiContextButton = closestAction(event.target, '[data-page-ai-context-button]');
    if (pageAiContextButton) {
      event.preventDefault();
      pageAiSetContextPopoverOpen(pageAiContextButton.getAttribute('aria-expanded') !== 'true');
      return true;
    }
    var pageAiTargetButton = closestAction(event.target, '[data-page-ai-target-button]');
    if (pageAiTargetButton) {
      event.preventDefault();
      pageAiSetTargetPopoverOpen(pageAiTargetButton.getAttribute('aria-expanded') !== 'true');
      return true;
    }
    var pageAiAction = closestAction(event.target, '[data-page-ai-action]');
    if (pageAiAction) {
      event.preventDefault();
      var action = pageAiAction.getAttribute('data-page-ai-action') || '';
      if (action === 'close-agent-popover') pageAiSetAgentPopoverOpen(false);
      if (action === 'close-context-popover') pageAiSetContextPopoverOpen(false);
      if (action === 'close-target-popover') pageAiSetTargetPopoverOpen(false);
      if (action === 'new-session') {
        pageUiState.pageAiPage = 'chat';
        pageAiStartNewSession();
      }
      if (action === 'history') {
        pageAiLoadSessions();
        pageUiState.pageAiPage = pageUiState.pageAiPage === 'history' ? 'chat' : 'history';
        renderPageAiControls();
        renderPageAiConversation();
        if (pageUiState.pageAiPage === 'history') {
          void pageAiLoadBackendSessions().catch(function(error) {
            pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
            renderPageAiControls();
          });
        }
      }
      if (action === 'send') {
        var input = ensurePageAiDrawer().querySelector('[data-page-ai-input]');
        if (input instanceof HTMLTextAreaElement) {
          var message = input.value;
          input.value = '';
          void sendPageAiMessage(message);
        }
      }
      if (action === 'cancel-queued-run') {
        void pageAiCancelQueuedRun(pageAiAction.getAttribute('data-page-ai-queue-id'));
      }
      return true;
    }
    var pageAiTargetOption = closestAction(event.target, '[data-page-ai-target-option]');
    if (pageAiTargetOption) {
      event.preventDefault();
      pageAiSelectTarget(pageAiTargetOption.getAttribute('data-page-ai-target-option') || '');
      return true;
    }
    var pageAiContextRef = closestAction(event.target, '[data-page-ai-context-ref]');
    if (pageAiContextRef) {
      event.preventDefault();
      pageAiToggleContextRef(pageAiContextRef.getAttribute('data-page-ai-context-ref') || '');
      return true;
    }
    var pageAiMemorySave = closestAction(event.target, '[data-page-ai-memory-save]');
    if (pageAiMemorySave) {
      event.preventDefault();
      void pageAiSaveProfileMemory(pageAiMemorySave.getAttribute('data-page-ai-memory-save') || '');
      return true;
    }
    var pageAiSkillToggle = closestAction(event.target, '[data-page-ai-skill-toggle]');
    if (pageAiSkillToggle) {
      event.preventDefault();
      void pageAiToggleSkill(
        pageAiSkillToggle.getAttribute('data-page-ai-skill-toggle') || '',
        pageAiSkillToggle.getAttribute('aria-pressed') !== 'true',
        pageAiSkillToggle.getAttribute('data-page-ai-skill-group') || '',
        pageAiSkillToggle.getAttribute('data-page-ai-skill-profile') || ''
      );
      return true;
    }
    var pageAiSkillGroupToggle = closestAction(event.target, '[data-page-ai-skill-group-toggle]');
    if (pageAiSkillGroupToggle) {
      event.preventDefault();
      pageAiToggleSkillGroup(pageAiSkillGroupToggle.getAttribute('data-page-ai-skill-group-toggle') || '');
      return true;
    }
    var pageAiToolToggle = closestAction(event.target, '[data-page-ai-tool-toggle]');
    if (pageAiToolToggle) {
      event.preventDefault();
      void pageAiToggleTool(pageAiToolToggle.getAttribute('data-page-ai-tool-toggle') || '', pageAiToolToggle.getAttribute('aria-pressed') !== 'true');
      return true;
    }
    var 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 pageAiSessionDelete = closestAction(event.target, '[data-page-ai-session-delete]');
    if (pageAiSessionDelete) {
      event.preventDefault();
      void pageAiDeleteBackendSession(pageAiSessionDelete.getAttribute('data-page-ai-session-delete') || '').catch(function(error) {
        pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
        renderPageAiControls();
      });
      return true;
    }
    var pageAiPermissionAction = closestAction(event.target, '[data-page-ai-permission-action]');
    if (pageAiPermissionAction) {
      event.preventDefault();
      pageAiResolvePermission(pageAiPermissionAction.getAttribute('data-page-ai-permission-id') || '', pageAiPermissionAction.getAttribute('data-page-ai-permission-action') || 'deny');
      return true;
    }
    var pageAiOpenLocationAction = closestAction(event.target, '[data-page-ai-open-location]');
    if (pageAiOpenLocationAction) {
      event.preventDefault();
      var loc = String(pageAiOpenLocationAction.getAttribute('data-page-ai-open-location') || '').trim();
      if (loc) pageAiOpenLocation(loc);
      return true;
    }
    var pageAiSession = closestAction(event.target, '[data-page-ai-session]');
    if (pageAiSession) {
      event.preventDefault();
      pageAiSetActiveSession(pageAiSession.getAttribute('data-page-ai-session') || '');
      return true;
    }
    var pageAiSuggestion = closestAction(event.target, '[data-page-ai-suggestion]');
    if (pageAiSuggestion) {
      event.preventDefault();
      var text = pageAiSuggestion.getAttribute('data-page-ai-suggestion') || '';
      var inputNode = ensurePageAiDrawer().querySelector('[data-page-ai-input]');
      if (inputNode instanceof HTMLTextAreaElement) {
        inputNode.value = text;
        inputNode.focus();
      }
      return true;
    }
    return false;
  }

  function handlePageAiKeyDown(event, helpers) {
    var closestAction = helpers && helpers.closestAction;
    if (typeof closestAction !== 'function') return false;
    if (event.key === 'Enter' && !event.shiftKey) {
      var aiInput = closestAction(event.target, '[data-page-ai-input]');
      if (aiInput instanceof HTMLTextAreaElement) {
        event.preventDefault();
        var text = aiInput.value;
        aiInput.value = '';
        void sendPageAiMessage(text);
        return true;
      }
    }
    return false;
  }

  function handlePageAiInput(event, helpers) {
    var closestAction = helpers && helpers.closestAction;
    if (typeof closestAction !== 'function') return false;
    var skillSearch = closestAction(event.target, '[data-page-ai-skill-search]');
    if (skillSearch instanceof HTMLInputElement) {
      pageUiState.pageAiSkillQuery = skillSearch.value;
      renderPageAiControls();
      return true;
    }
    var sessionSearch = closestAction(event.target, '[data-page-ai-session-search]');
    if (sessionSearch instanceof HTMLInputElement) {
      var sessionQuery = sessionSearch.value;
      window.clearTimeout(pageUiState.pageAiSessionSearchTimer || 0);
      pageUiState.pageAiSessionSearchTimer = window.setTimeout(function() {
        void pageAiSearchBackendSessions(sessionQuery).catch(function(error) {
          pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
          renderPageAiControls();
        });
      }, 200);
      return true;
    }
    var memoryEditor = closestAction(event.target, '[data-page-ai-memory-editor]');
    if (memoryEditor instanceof HTMLTextAreaElement) {
      var section = memoryEditor.getAttribute('data-page-ai-memory-editor') || '';
      if (['memory', 'user', 'soul'].indexOf(section) >= 0) pageUiState.pageAiProfileMemoryDrafts[section] = memoryEditor.value;
      return true;
    }
    return false;
  }

  function handlePageAiChange(event, helpers) {
    var closestAction = helpers && helpers.closestAction;
    if (typeof closestAction !== 'function') return false;
    var 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 pageAiSkillSourceSelect = closestAction(event.target, '[data-page-ai-skill-source-select]');
    if (pageAiSkillSourceSelect instanceof HTMLSelectElement) {
      pageAiSetSkillSource(pageAiSkillSourceSelect.value);
      return true;
    }
    var pageAiSessionAgentFilter = closestAction(event.target, '[data-page-ai-session-agent-filter]');
    if (pageAiSessionAgentFilter instanceof HTMLSelectElement) {
      pageUiState.pageAiSessionAgentFilter = String(pageAiSessionAgentFilter.value || 'all').trim() || 'all';
      renderPageAiControls();
      renderPageAiConversation();
      return true;
    }
    var pageAiHideHermesBuiltin = closestAction(event.target, '[data-page-ai-hide-hermes-builtin]');
    if (pageAiHideHermesBuiltin instanceof HTMLInputElement) {
      pageAiSetHideHermesBuiltinSkills(pageAiHideHermesBuiltin.checked);
      return true;
    }
    var pageAiReasonixMemory = closestAction(event.target, '[data-page-ai-reasonix-memory]');
    if (pageAiReasonixMemory instanceof HTMLInputElement) {
      pageAiSetReasonixMemoryEnabled(pageAiReasonixMemory.checked);
      return true;
    }
    var pageAiContextSelect = closestAction(event.target, '[data-page-ai-context-scope]');
    if (pageAiContextSelect instanceof HTMLSelectElement) {
      pageAiSetContextScope(pageAiContextSelect.value);
      renderPageAiControls();
      return true;
    }
    return false;
  }

  function installPageAiDelegates() {
    if (pageAiDelegatesInstalled) return;
    pageAiDelegatesInstalled = true;
    var helpers = { closestAction: closestAction };
    document.addEventListener('click', function(event) {
      handlePageAiClick(event, helpers);
    });
    document.addEventListener('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),
    pageAiDeleteBackendSession: (...args) => pageAiDeleteBackendSession(...args),
    pageAiResolvePermission: (...args) => pageAiResolvePermission(...args),
    pageAiOpenLocation: (...args) => pageAiOpenLocation(...args),
    pageAiSetActiveSession: (...args) => pageAiSetActiveSession(...args),
    pageAiStartNewSession: (...args) => pageAiStartNewSession(...args),
    pageAiLoadSessions: (...args) => pageAiLoadSessions(...args),
    pageAiLoadBackendSessions: (...args) => pageAiLoadBackendSessions(...args),
    pageAiCancelQueuedRun: (...args) => pageAiCancelQueuedRun(...args),
    pageAiSearchBackendSessions: (...args) => pageAiSearchBackendSessions(...args),
    pageAiSetTargetPopoverOpen: (...args) => pageAiSetTargetPopoverOpen(...args),
    pageAiSelectTarget: (...args) => pageAiSelectTarget(...args),
    pageAiPersistSessions: (...args) => pageAiPersistSessions(...args),
    pageAiLoadProfiles: (...args) => pageAiLoadProfiles(...args),
    pageAiLoadProfileMemory: (...args) => pageAiLoadProfileMemory(...args),
    pageAiLoadSkills: (...args) => pageAiLoadSkills(...args),
    pageAiSwitchProfile: (...args) => pageAiSwitchProfile(...args),
    pageAiToggleSkillGroup: (...args) => pageAiToggleSkillGroup(...args),
    pageAiSetReasonixMemoryEnabled: (...args) => pageAiSetReasonixMemoryEnabled(...args),
    pageAiSetHideHermesBuiltinSkills: (...args) => pageAiSetHideHermesBuiltinSkills(...args),
    pageAiSetSkillSource: (...args) => pageAiSetSkillSource(...args),
    pageAiSetContextScope: (...args) => pageAiSetContextScope(...args),
    pageAiSetAgentId: (...args) => pageAiSetAgentId(...args),
    pageAiToggleContextRef: (...args) => pageAiToggleContextRef(...args),
    pageAiSetContextPopoverOpen: (...args) => pageAiSetContextPopoverOpen(...args),
    pageAiSetAgentPopoverOpen: (...args) => pageAiSetAgentPopoverOpen(...args),
    pageAiLoadAllowedRoots: (...args) => pageAiLoadAllowedRoots(...args),
    updatePageAiTriggerState: (...args) => updatePageAiTriggerState(...args)
  };
}
