export function createSidebarPageAiSessionRuntime(context) {
  const {
    currentDocumentId,
    currentRootUri,
    currentSourceKind,
    documentRef,
    pageAiApplyRuntimeState,
    pageAiCurrentAgentId,
    pageAiCurrentProfile,
    pageAiErrorMessage,
    pageAiNormalizeAgentId,
    pageAiNormalizeArray,
    pageAiNormalizeChatOnlyProfileId,
    pageAiPermissionMessage,
    pageAiPreviewValue,
    pageAiRunProfile,
    pageAiSetActiveProfile,
    pageAiTimestamp,
    pageUiState,
    renderPageAiControls,
    renderPageAiConversation,
    resolveWorkspaceId,
    sessionStorageVersion,
    windowRef,
  } = context;
  const doc = documentRef || document;
  const win = windowRef || window;

  function pageAiStorageKey() {
    return 'hermes_page_ai_session:' + currentDocumentId();
  }

  function pageAiBackendSessionQuery(extra) {
    var params = new URLSearchParams();
    params.set('source', 'acp');
    params.set('workspaceId', resolveWorkspaceId(doc.body));
    params.set('documentId', currentDocumentId());
    params.set('profile', pageAiRunProfile());
    params.set('sourceKind', currentSourceKind());
    if (currentRootUri()) params.set('rootUri', currentRootUri());
    Object.keys(extra || {}).forEach(function(key) {
      var value = extra[key];
      if (value !== undefined && value !== null && String(value).trim() !== '') {
        params.set(key, String(value));
      }
    });
    return params.toString();
  }

  function pageAiNewSession(title) {
    var now = Date.now();
    var agentId = pageAiCurrentAgentId();
    var profile = agentId === 'chat_only' ? pageAiNormalizeChatOnlyProfileId(pageAiCurrentProfile()) : pageAiCurrentProfile();
    return {
      id: 'sess_' + now + '_' + Math.random().toString(16).slice(2, 8),
      title: title || '新会话',
      agentId: agentId,
      profileId: pageUiState.pageAiAcpRuntime === 'hermes' ? profile : '',
      profile: profile,
      acpRuntime: pageUiState.pageAiAcpRuntime || 'reasonix',
      createdAt: now,
      updatedAt: now,
      source: 'draft',
      usage: null,
      status: 'draft',
      messages: []
    };
  }

  function pageAiNormalizeSessions(sessions) {
    return (Array.isArray(sessions) ? sessions : [])
      .slice(0, 20)
      .map(function(session) {
        var sessionStorage = String(session && (session.sessionStorage || session.session_storage) || '').trim();
        var persistence = String(session && session.persistence || '').trim();
        var agentId = pageAiNormalizeAgentId(session && (session.agentId || session.agent_id));
        var profileId = String(session && (session.profileId || session.profile_id || '') || '').trim();
        var profile = String(session && session.profile || pageAiCurrentProfile()).trim() || 'default';
        if (!profileId && agentId !== 'reasonix') profileId = profile;
        if (agentId === 'chat_only') {
          profileId = pageAiNormalizeChatOnlyProfileId(profileId || profile);
          profile = profileId;
        }
        return {
          id: String(session && session.id || '').trim() || pageAiNewSession().id,
          title: String(session && session.title || '').trim() || '新会话',
          agentId: agentId,
          profileId: profileId,
          profile: profile,
          acpRuntime: String(session && session.acpRuntime || 'reasonix').trim(),
          createdAt: pageAiTimestamp(session && session.createdAt),
          updatedAt: pageAiTimestamp(session && session.updatedAt),
          source: String(session && session.source || 'local').trim() || 'local',
          persistence: persistence,
          sessionStorage: sessionStorage,
          permissionLevel: String(session && (session.permissionLevel || session.permission_level) || '').trim(),
          shareId: String(session && (session.shareId || session.share_id) || '').trim(),
          acpSessionId: String(session && (session.acpSessionId || session.acp_session_id) || '').trim(),
          runId: String(session && (session.runId || session.run_id) || '').trim(),
          status: String(session && session.status || '').trim(),
          usage: session && session.usage && typeof session.usage === 'object' ? session.usage : null,
          preview: String(session && session.preview || '').trim(),
          messages: Array.isArray(session && session.messages) ? session.messages.slice(-300) : []
        };
      })
      .sort(function(a, b) {
        return Number(b.updatedAt || 0) - Number(a.updatedAt || 0);
      });
  }

  function pageAiNormalizeBackendSessionRow(row) {
    if (!row || typeof row !== 'object') return null;
    var payload = row.payload && typeof row.payload === 'object' ? row.payload : {};
    var sessionId = String(row.sessionId || row.session_id || '').trim();
    if (!sessionId) return null;
    var title = String(row.title || payload.title || payload.message || '').trim();
    if (title.length > 28) title = title.slice(0, 28) + '…';
    var persistence = String(row.persistence || payload.persistence || '').trim();
    var sessionStorage = String(row.sessionStorage || row.session_storage || payload.sessionStorage || '').trim();
    var status = String(row.status || (row.runtime && row.runtime.status) || '').trim();
    var hasConversationSignal = String(payload.message || payload.input || row.snippet || '').trim()
      || pageAiNormalizeArray(row.messages).length > 0;
    if (status === 'session.created' && !hasConversationSignal) return null;
    var agentId = pageAiNormalizeAgentId(row.agentId || row.agent_id || payload.agentId || payload.agent_id);
    var profileId = String(row.profileId || row.profile_id || payload.profileId || payload.profile_id || '').trim();
    var profile = String(row.profile || payload.profile || pageAiRunProfile()).trim() || 'default';
    if (!profileId && agentId !== 'reasonix') profileId = profile;
    if (agentId === 'chat_only') {
      profileId = pageAiNormalizeChatOnlyProfileId(profileId || profile);
      profile = profileId;
    }
    if (!sessionStorage && persistence === 'convex_acp_runtime_store') sessionStorage = 'cloud';
    if (!sessionStorage && persistence === 'local_ai_session_jsonl') sessionStorage = String(row.shareId || payload.shareId || '').trim() ? 'local_shared' : 'local_private';
    return {
      id: sessionId,
      title: title || '当前页问答',
      agentId: agentId,
      profileId: profileId,
      profile: profile,
      acpRuntime: String(row.acpRuntime || row.acp_runtime || payload.acpRuntime || 'reasonix').trim(),
      createdAt: pageAiTimestamp(row.createdAt || row.created_at),
      updatedAt: pageAiTimestamp(row.updatedAt || row.updated_at),
      source: sessionStorage === 'local_private' || sessionStorage === 'local_shared' ? 'local' : 'acp',
      persistence: persistence,
      sessionStorage: sessionStorage,
      permissionLevel: String(row.permissionLevel || row.permission_level || payload.permissionLevel || '').trim(),
      shareId: String(row.shareId || row.share_id || payload.shareId || '').trim(),
      acpSessionId: String(row.acpSessionId || row.acp_session_id || payload.acpSessionId || payload.acp_session_id || '').trim(),
      runId: String(row.runId || row.run_id || '').trim(),
      status: status,
      usage: row.usage && typeof row.usage === 'object' ? row.usage : null,
      preview: String(payload.message || row.snippet || '').trim(),
      messages: []
    };
  }

  function pageAiMergeSessions(localSessions, backendSessions) {
    var byId = {};
    pageAiNormalizeSessions(localSessions).forEach(function(session) {
      byId[session.id] = session;
    });
    pageAiNormalizeSessions(backendSessions).forEach(function(session) {
      var existing = byId[session.id];
      byId[session.id] = Object.assign({}, existing || {}, session, {
        messages: session.messages.length ? session.messages : (existing && existing.messages || [])
      });
    });
    return pageAiNormalizeSessions(Object.keys(byId).map(function(id) { return byId[id]; }));
  }

  function pageAiDedupeSessions(sessions) {
    var byId = {};
    var ordered = [];
    pageAiNormalizeSessions(sessions).forEach(function(session) {
      var id = String(session && session.id || '').trim();
      if (!id || byId[id]) return;
      byId[id] = true;
      ordered.push(session);
    });
    return ordered;
  }

  function pageAiSessionStorageLabel(session) {
    var storage = String(session && (session.sessionStorage || session.session_storage) || '').trim();
    var persistence = String(session && session.persistence || '').trim();
    if (storage === 'local_shared') return '共享会话';
    if (storage === 'local_private') return '本地私有';
    if (storage === 'sqlite_control_plane' || persistence === 'sqlite_acp_runtime_store') return '账号会话';
    if (storage === 'cloud' || persistence === 'convex_acp_runtime_store') return '云端会话';
    if (persistence === 'local_ai_session_jsonl') return '本地私有';
    return String(session && session.source || '').trim() === 'acp' ? '云端会话' : '本地私有';
  }

  function pageAiLoadSessions() {
    if (pageUiState.pageAiSessions.length && pageUiState.pageAiActiveSessionId) return;
    try {
      var raw = win.localStorage.getItem(pageAiStorageKey());
      var parsed = raw ? JSON.parse(raw) : null;
      var activeProfile = String(parsed && parsed.activeProfileName || '').trim();
      var activeAcpRuntime = String(parsed && parsed.activeAcpRuntime || '').trim();
      var storageVersion = Number(parsed && parsed.version || 0);
      if (storageVersion >= sessionStorageVersion && activeAcpRuntime) pageUiState.pageAiAcpRuntime = activeAcpRuntime;
      if (activeProfile) pageAiSetActiveProfile(activeProfile);
    } catch (_) {}
    var fresh = pageAiNewSession();
    pageUiState.pageAiSessions = [fresh];
    pageUiState.pageAiActiveSessionId = fresh.id;
    pageUiState.pageAiMessages = [];
  }

  async function pageAiLoadBackendSessions() {
    var response = await fetch('/api/hermes/client/sessions?' + pageAiBackendSessionQuery({ limit: 50 }), {
      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, 'session_list_failed_' + response.status));
    }
    var backendSessions = pageAiNormalizeArray(payload.sessions).map(pageAiNormalizeBackendSessionRow).filter(Boolean);
    var draftSessions = pageAiNormalizeArray(pageUiState.pageAiSessions).filter(function(session) {
      var id = String(session && session.id || '').trim();
      return id && !id.startsWith('mnote_')
        && (id === pageUiState.pageAiActiveSessionId || (Array.isArray(session.messages) && session.messages.length > 0));
    });
    if (!backendSessions.length) {
      if (!draftSessions.length && !pageUiState.pageAiActiveSessionId) {
        var fresh = pageAiNewSession();
        pageUiState.pageAiSessions = [fresh];
        pageUiState.pageAiActiveSessionId = fresh.id;
        pageUiState.pageAiMessages = [];
      }
      pageUiState.pageAiSessionError = '';
      renderPageAiConversation();
      renderPageAiControls();
      return [];
    }
    pageUiState.pageAiSessions = pageAiDedupeSessions(backendSessions.concat(draftSessions));
    if (!pageUiState.pageAiActiveSessionId || !pageUiState.pageAiSessions.find(function(session) { return session.id === pageUiState.pageAiActiveSessionId; })) {
      pageUiState.pageAiActiveSessionId = pageUiState.pageAiSessions[0].id;
    }
    var active = pageAiCurrentSession();
    if (active) {
      if (active.profile) pageAiSetActiveProfile(active.profile);
      if (!pageUiState.pageAiAcpRuntime && active.acpRuntime) pageUiState.pageAiAcpRuntime = active.acpRuntime;
      pageUiState.pageAiMessages = Array.isArray(active.messages) ? active.messages.slice() : pageUiState.pageAiMessages;
    }
    pageUiState.pageAiSessionError = '';
    pageAiPersistSessions();
    renderPageAiConversation();
    renderPageAiControls();
    return backendSessions;
  }

  function pageAiMessageFromRuntimeEvent(event) {
    if (!event || typeof event !== 'object') return null;
    var eventType = String(event.eventType || event.event_type || event.event || '').trim();
    var payload = event.payload && typeof event.payload === 'object' ? event.payload : event;
    if (eventType === 'message.delta') {
      var delta = String(payload.delta || payload.text || payload.output_text || '');
      return delta ? { role: 'assistant', content: delta } : null;
    }
    if (eventType === 'thought.delta') {
      var thought = String(payload.delta || payload.text || '').trim();
      return thought ? { role: 'assistant', kind: 'thought', content: thought } : null;
    }
    if (eventType === 'tool.started' || eventType === 'tool.completed' || eventType === 'tool.failed') {
      var toolName = String(payload.toolName || payload.tool || payload.name || eventType).trim();
      var rawLocations = payload.locations;
      return {
        role: 'tool',
        content: toolName,
        toolName: toolName,
        toolCallId: String(payload.toolCallId || payload.tool_call_id || payload.id || ''),
        toolKind: String(payload.kind || ''),
        status: eventType === 'tool.completed' ? 'completed' : (eventType === 'tool.failed' ? 'failed' : 'running'),
        argsSummary: pageAiPreviewValue(payload.args || payload.arguments || payload.input),
        resultSummary: pageAiPreviewValue(payload.summary || payload.result || payload.output || payload.error),
        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; }) : [],
        traceId: String(payload.traceId || payload.trace_id || ''),
        auditId: String(payload.auditId || payload.audit_id || '')
      };
    }
    if (eventType === 'permission.requested' || eventType === 'permission.denied' || eventType === 'permission.allowed') {
      return pageAiPermissionMessage(payload, eventType);
    }
    if (eventType === 'run.completed') {
      var output = String(payload.output || payload.text || '').trim();
      return output ? { role: 'assistant', content: output } : null;
    }
    return null;
  }

  function pageAiApplyBackendSessionDetail(payload) {
    var sessionPayload = payload && payload.session ? payload.session : {};
    var runs = pageAiNormalizeArray(sessionPayload.runs);
    var latest = runs.length ? pageAiNormalizeBackendSessionRow(runs[0]) : null;
    var events = pageAiNormalizeArray(payload && payload.events);
    var storedMessages = pageAiNormalizeArray(sessionPayload.messages).map(function(message) {
      return {
        role: message.role === 'user' ? 'user' : (message.role === 'tool' ? 'tool' : 'assistant'),
        content: String(message.content || '')
      };
    }).filter(function(message) { return message.content; });
    var eventsByRunId = {};
    events.forEach(function(event) {
      var runId = String(event && (event.runId || event.run_id) || '').trim();
      if (!runId) return;
      if (!eventsByRunId[runId]) eventsByRunId[runId] = [];
      eventsByRunId[runId].push(event);
    });
    var messages = [];
    if (runs.length) {
      runs.slice().reverse().forEach(function(run) {
        var runPayload = run && run.payload && typeof run.payload === 'object' ? run.payload : {};
        var userMessage = String(runPayload.message || runPayload.input || '').trim();
        if (userMessage) messages.push({ role: 'user', content: userMessage });
        var runId = String(run && (run.runId || run.run_id) || '').trim();
        var assistantDelta = '';
        var completedOutput = '';
        function flushAssistantDelta() {
          var content = assistantDelta.trim();
          if (content) messages.push({ role: 'assistant', content: content });
          assistantDelta = '';
        }
        pageAiNormalizeArray(eventsByRunId[runId]).forEach(function(event) {
          var message = pageAiMessageFromRuntimeEvent(event);
          if (!message) return;
          if (message.role === 'assistant' && !message.kind) {
            var eventType = String(event && (event.eventType || event.event_type || event.event) || '').trim();
            if (eventType === 'run.completed') {
              completedOutput = String(message.content || '').trim();
              return;
            }
            assistantDelta += String(message.content || '');
            return;
          }
          flushAssistantDelta();
          messages.push(message);
        });
        flushAssistantDelta();
        if (completedOutput && !messages.some(function(message) {
          return message.role === 'assistant' && String(message.content || '').trim() === completedOutput;
        })) {
          messages.push({ role: 'assistant', content: completedOutput });
        }
      });
    }
    if (!messages.length) messages = storedMessages;
    var acpSessionId = '';
    events.forEach(function(event) {
      var eventType = String(event && (event.eventType || event.event_type || event.event) || '').trim();
      var payload = event && event.payload && typeof event.payload === 'object' ? event.payload : event;
      if (eventType === 'session.info.updated') {
        var nextAcpSessionId = String(payload && (payload.acpSessionId || payload.acp_session_id) || '').trim();
        if (nextAcpSessionId) acpSessionId = nextAcpSessionId;
      }
    });
    var current = pageAiCurrentSession();
    if (latest && current) {
      Object.assign(current, latest);
    }
    if (current) {
      current.messages = messages.slice(-300);
      current.updatedAt = Math.max(Number(current.updatedAt || 0), Date.now());
      if (acpSessionId) current.acpSessionId = acpSessionId;
      if (latest && latest.usage) current.usage = latest.usage;
    }
    pageAiApplyRuntimeState(payload && payload.runtime);
    pageUiState.pageAiMessages = messages.slice(-300);
    pageAiPersistSessions();
  }

  async function pageAiLoadBackendSessionDetail(sessionId) {
    sessionId = String(sessionId || pageUiState.pageAiActiveSessionId || '').trim();
    if (!sessionId) return null;
    var response = await fetch('/api/hermes/client/sessions/' + encodeURIComponent(sessionId) + '?' + pageAiBackendSessionQuery({ limit: 200 }), {
      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, 'session_detail_failed_' + response.status));
    }
    pageAiApplyBackendSessionDetail(payload);
    renderPageAiConversation();
    renderPageAiControls();
    return payload;
  }

  async function pageAiSearchBackendSessions(query) {
    var q = String(query || '').trim();
    pageUiState.pageAiSessionSearchQuery = q;
    if (!q) {
      pageUiState.pageAiSessionSearchResults = [];
      renderPageAiConversation();
      return [];
    }
    var response = await fetch('/api/hermes/client/sessions/search?' + pageAiBackendSessionQuery({ q: q, limit: 20 }), {
      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, 'session_search_failed_' + response.status));
    }
    pageUiState.pageAiSessionSearchResults = pageAiNormalizeArray(payload.results).map(function(row) {
      var normalized = pageAiNormalizeBackendSessionRow(row) || {};
      normalized.snippet = String(row && row.snippet || normalized.preview || '').trim();
      return normalized;
    }).filter(function(row) { return row.id; });
    renderPageAiConversation();
    return pageUiState.pageAiSessionSearchResults;
  }

  function pageAiPersistSessions() {
    doc.documentElement.setAttribute('data-mnote-page-ai-session-owner', 'hermes');
    doc.documentElement.setAttribute('data-mnote-page-ai-session-key', pageAiStorageKey());
    try {
      pageAiSyncCurrentSessionMessages();
      win.localStorage.setItem(pageAiStorageKey(), JSON.stringify({
        version: sessionStorageVersion,
        activeSessionId: pageUiState.pageAiActiveSessionId,
        activeProfileName: pageAiCurrentProfile(),
        activeAcpRuntime: pageUiState.pageAiAcpRuntime || 'reasonix'
      }));
    } catch (_) {}
  }

  async function pageAiEnsureHermesSession(forceCreate) {
    pageAiLoadSessions();
    var current = pageAiCurrentSession();
    var runProfile = pageAiRunProfile();
    if (!forceCreate && current && String(current.id || '').startsWith('mnote_') && current.profile === runProfile) return current;
    var response = await fetch('/api/hermes/client/sessions', {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({
        workspaceId: resolveWorkspaceId(doc.body),
        documentId: currentDocumentId(),
        sourceKind: currentSourceKind(),
        rootUri: currentRootUri(),
        traceId: 'page-ai-' + Date.now().toString(36),
        profile: runProfile,
        agentId: pageAiCurrentAgentId(),
        profileId: pageUiState.pageAiAcpRuntime === 'hermes' ? runProfile : '',
        title: current && current.title ? current.title : '当前页问答'
      })
    });
    var payload = await response.json().catch(function(){ return null; });
    if (!response.ok || !payload || payload.ok !== true) {
      throw new Error(pageAiErrorMessage(payload, 'hermes_session_failed_' + response.status));
    }
    var session = {
      id: String(payload.sessionId || '').trim(),
      title: String(payload.title || '当前页问答'),
      agentId: pageAiCurrentAgentId(),
      profileId: pageUiState.pageAiAcpRuntime === 'hermes' ? runProfile : '',
      profile: String(payload.profile || runProfile).trim() || 'default',
      acpRuntime: pageUiState.pageAiAcpRuntime || 'reasonix',
      persistence: String(payload.persistence || '').trim(),
      sessionStorage: String(payload.sessionStorage || '').trim(),
      permissionLevel: String(payload.permissionLevel || '').trim(),
      shareId: String(payload.shareId || '').trim(),
      acpSessionId: String(payload.acpSessionId || payload.acp_session_id || '').trim(),
      createdAt: Date.now(),
      updatedAt: Date.now(),
      messages: pageUiState.pageAiMessages.slice()
    };
    var previousSessionId = String(current && current.id || '').trim();
    var retainedSessions = pageAiNormalizeArray(pageUiState.pageAiSessions).filter(function(item) {
      var itemId = String(item && item.id || '').trim();
      return itemId && itemId !== session.id && itemId !== previousSessionId;
    });
    pageUiState.pageAiSessions = pageAiNormalizeSessions([session].concat(retainedSessions));
    pageUiState.pageAiActiveSessionId = session.id;
    pageAiPersistSessions();
    renderPageAiConversation();
    return session;
  }

  async function pageAiRestoreHermesSession() {
    var current = pageAiCurrentSession();
    if (!current || !String(current.id || '').startsWith('mnote_')) return;
    var response = await fetch('/api/hermes/client/sessions/' + encodeURIComponent(current.id) + '/resume?' + pageAiBackendSessionQuery({}), {
      method: 'POST',
      headers: { 'accept': 'application/json' }
    });
    if (!response.ok) return;
    var payload = await response.json().catch(function(){ return null; });
    if (payload && payload.persistence === 'convex_acp_runtime_store') {
      pageAiApplyBackendSessionDetail(payload);
      renderPageAiConversation();
      renderPageAiControls();
      return;
    }
    var session = payload && (payload.session || (payload.upstream && payload.upstream.session) || payload);
    var messages = session && Array.isArray(session.messages) ? session.messages : [];
    pageAiApplyRuntimeState(payload && payload.runtime);
    if (session && (session.profile || session.profileName)) {
      current.profile = String(session.profile || session.profileName || current.profile || pageAiCurrentProfile());
    }
    if (!messages.length) {
      renderPageAiControls();
      return;
    }
    pageUiState.pageAiMessages = messages.slice(-300).map(function(message) {
      return {
        role: message.role === 'user' ? 'user' : (message.role === 'tool' ? 'tool' : 'assistant'),
        content: String(message.content || '')
      };
    });
    current.messages = pageUiState.pageAiMessages.slice();
    current.updatedAt = Date.now();
    renderPageAiConversation();
    renderPageAiControls();
  }

  function pageAiCurrentSession() {
    return pageUiState.pageAiSessions.find(function(session) {
      return session.id === pageUiState.pageAiActiveSessionId;
    }) || null;
  }

  function pageAiSelectedSessionIds() {
    if (!pageUiState.pageAiSelectedSessionIds || typeof pageUiState.pageAiSelectedSessionIds !== 'object') {
      pageUiState.pageAiSelectedSessionIds = {};
    }
    return pageUiState.pageAiSelectedSessionIds;
  }

  function pageAiSessionSelected(sessionId) {
    sessionId = String(sessionId || '').trim();
    return Boolean(sessionId && pageAiSelectedSessionIds()[sessionId]);
  }

  function pageAiSelectedSessionList() {
    var selected = pageAiSelectedSessionIds();
    return Object.keys(selected).filter(function(sessionId) {
      return selected[sessionId] === true;
    });
  }

  function pageAiSelectedSessionCount() {
    return pageAiSelectedSessionList().length;
  }

  function pageAiToggleSessionSelection(sessionId, selected) {
    sessionId = String(sessionId || '').trim();
    if (!sessionId) return;
    var selectedMap = Object.assign({}, pageAiSelectedSessionIds());
    if (selected === false) {
      delete selectedMap[sessionId];
    } else {
      selectedMap[sessionId] = true;
    }
    pageUiState.pageAiSelectedSessionIds = selectedMap;
    renderPageAiConversation();
    renderPageAiControls();
  }

  function pageAiClearSessionSelection() {
    pageUiState.pageAiSelectedSessionIds = {};
    renderPageAiConversation();
    renderPageAiControls();
  }

  function pageAiSyncCurrentSessionMessages() {
    var session = pageAiCurrentSession();
    if (!session) return;
    var runProfile = pageAiRunProfile();
    session.messages = Array.isArray(pageUiState.pageAiMessages) ? pageUiState.pageAiMessages.slice(-300) : [];
    session.agentId = pageAiCurrentAgentId();
    session.profileId = pageUiState.pageAiAcpRuntime === 'hermes' ? runProfile : '';
    session.profile = runProfile;
    session.acpRuntime = pageUiState.pageAiAcpRuntime || 'reasonix';
    session.updatedAt = Date.now();
  }

  function pageAiSetActiveSession(sessionId) {
    var session = pageUiState.pageAiSessions.find(function(item) { return item.id === sessionId; });
    if (!session) return;
    pageUiState.pageAiActiveSessionId = session.id;
    if (session.agentId) pageUiState.pageAiAgentId = pageAiNormalizeAgentId(session.agentId);
    if (session.acpRuntime) pageUiState.pageAiAcpRuntime = session.acpRuntime;
    if (session.profileId || session.profile) pageAiSetActiveProfile(session.profileId || session.profile);
    pageUiState.pageAiMessages = Array.isArray(session.messages) ? session.messages.slice() : [];
    pageUiState.pageAiPage = 'chat';
    pageAiPersistSessions();
    renderPageAiControls();
    renderPageAiConversation();
    if (session.source === 'acp' || String(session.id || '').startsWith('mnote_')) {
      void pageAiLoadBackendSessionDetail(session.id).catch(function(error) {
        pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
        renderPageAiControls();
      });
    }
  }

  function pageAiStartNewSession() {
    var session = pageAiNewSession();
    pageUiState.pageAiSessions = pageAiNormalizeSessions([session].concat(pageUiState.pageAiSessions));
    pageUiState.pageAiActiveSessionId = session.id;
    pageUiState.pageAiMessages = [];
    pageUiState.pageAiPage = 'chat';
    pageAiPersistSessions();
    renderPageAiControls();
    renderPageAiConversation();
  }

  async function pageAiRenameBackendSession(sessionId) {
    var session = pageUiState.pageAiSessions.find(function(item) { return item.id === sessionId; });
    if (!session) return;
    var title = win.prompt('重命名 AI 会话', session.title || '当前页问答');
    if (title === null) return;
    title = String(title || '').trim();
    if (!title) return;
    var response = await fetch('/api/hermes/client/sessions/' + encodeURIComponent(sessionId) + '/rename?' + pageAiBackendSessionQuery({}), {
      method: 'POST',
      headers: { 'content-type': 'application/json', 'accept': 'application/json' },
      body: JSON.stringify({ title: title })
    });
    var payload = await response.json().catch(function(){ return null; });
    if (!response.ok || !payload || payload.ok !== true) {
      throw new Error(pageAiErrorMessage(payload, 'session_rename_failed_' + response.status));
    }
    session.title = String((payload.result && payload.result.title) || title);
    session.updatedAt = Date.now();
    pageAiPersistSessions();
    renderPageAiConversation();
    renderPageAiControls();
  }

  function pageAiSessionRequiresBackendDelete(session) {
    var id = String(session && session.id || '').trim();
    var source = String(session && session.source || '').trim();
    return Boolean(id && (
      id.startsWith('mnote_')
      || source === 'acp'
      || String(session && session.persistence || '').trim()
      || String(session && (session.sessionStorage || session.session_storage) || '').trim()
    ));
  }

  function pageAiFindSessionById(sessionId) {
    sessionId = String(sessionId || '').trim();
    if (!sessionId) return null;
    return pageAiNormalizeArray(pageUiState.pageAiSessions).find(function(item) {
      return String(item && item.id || '').trim() === sessionId;
    }) || pageAiNormalizeArray(pageUiState.pageAiSessionSearchResults).find(function(item) {
      return String(item && item.id || '').trim() === sessionId;
    }) || null;
  }

  function pageAiApplyDeletedSessionIds(sessionIds) {
    var deleted = {};
    pageAiNormalizeArray(sessionIds).forEach(function(sessionId) {
      sessionId = String(sessionId || '').trim();
      if (sessionId) deleted[sessionId] = true;
    });
    pageUiState.pageAiSessions = pageAiNormalizeArray(pageUiState.pageAiSessions).filter(function(item) {
      return !deleted[String(item && item.id || '').trim()];
    });
    pageUiState.pageAiSessionSearchResults = pageAiNormalizeArray(pageUiState.pageAiSessionSearchResults).filter(function(item) {
      return !deleted[String(item && item.id || '').trim()];
    });
    var selected = Object.assign({}, pageAiSelectedSessionIds());
    Object.keys(deleted).forEach(function(sessionId) { delete selected[sessionId]; });
    pageUiState.pageAiSelectedSessionIds = selected;
    if (deleted[pageUiState.pageAiActiveSessionId]) {
      var next = pageUiState.pageAiSessions[0] || pageAiNewSession();
      if (!pageUiState.pageAiSessions.length) pageUiState.pageAiSessions = [next];
      pageUiState.pageAiActiveSessionId = next.id;
      pageUiState.pageAiMessages = Array.isArray(next.messages) ? next.messages.slice() : [];
    }
  }

  async function pageAiDeleteBackendSessions(sessionIds) {
    var ids = pageAiNormalizeArray(sessionIds).map(function(sessionId) {
      return String(sessionId || '').trim();
    }).filter(Boolean);
    if (!ids.length) return;
    var sessions = ids.map(pageAiFindSessionById).filter(Boolean);
    if (!sessions.length) return;
    var confirmText = sessions.length === 1
      ? '确定删除 AI 会话“' + (sessions[0].title || sessions[0].id) + '”吗？'
      : '确定删除选中的 ' + String(sessions.length) + ' 个 AI 会话吗？';
    if (!win.confirm(confirmText)) return;
    for (var index = 0; index < sessions.length; index += 1) {
      var session = sessions[index];
      if (!pageAiSessionRequiresBackendDelete(session)) continue;
      var response = await fetch('/api/hermes/client/sessions/' + encodeURIComponent(session.id) + '?' + pageAiBackendSessionQuery({}), {
        method: 'DELETE',
        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, 'session_delete_failed_' + response.status));
      }
    }
    pageAiApplyDeletedSessionIds(sessions.map(function(session) { return session.id; }));
    pageAiPersistSessions();
    renderPageAiConversation();
    renderPageAiControls();
  }

  async function pageAiDeleteBackendSession(sessionId) {
    return pageAiDeleteBackendSessions([sessionId]);
  }

  async function pageAiDeleteSelectedBackendSessions() {
    return pageAiDeleteBackendSessions(pageAiSelectedSessionList());
  }

  async function pageAiResumeBackendSession(sessionId) {
    sessionId = String(sessionId || pageUiState.pageAiActiveSessionId || '').trim();
    if (!sessionId) return;
    pageAiSetActiveSession(sessionId);
    var response = await fetch('/api/hermes/client/sessions/' + encodeURIComponent(sessionId) + '/resume?' + pageAiBackendSessionQuery({}), {
      method: 'POST',
      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, 'session_resume_failed_' + response.status));
    }
    pageAiApplyBackendSessionDetail(payload);
    pageUiState.pageAiPage = 'chat';
    renderPageAiConversation();
    renderPageAiControls();
  }

  async function pageAiCheckActiveRun(sessionId) {
    sessionId = String(sessionId || pageUiState.pageAiActiveSessionId || '').trim();
    if (!sessionId) return null;
    var response = await fetch('/api/page-ai/sessions/' + encodeURIComponent(sessionId) + '/active-run?' + pageAiBackendSessionQuery({}), {
      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, 'active_run_failed_' + response.status));
    }
    if (!payload.active || !payload.run) return null;
    var run = payload.run;
    var hostRunId = String(run.hostRunId || run.runId || '').trim();
    var status = String(run.status || '').trim();
    var current = pageAiCurrentSession();
    if (current && current.id === sessionId) {
      current.runId = hostRunId;
      current.status = status || current.status || 'running';
      current.updatedAt = Date.now();
    }
    pageAiApplyRuntimeState(Object.assign({}, run.runtime || {}, {
      status: status || 'running',
      runId: hostRunId
    }));
    doc.documentElement.setAttribute('data-mnote-page-ai-active-run-checked', 'true');
    if (hostRunId) doc.documentElement.setAttribute('data-mnote-page-ai-active-host-run-id', hostRunId);
    pageAiPersistSessions();
    renderPageAiControls();
    return run;
  }

  return {
    pageAiStorageKey,
    pageAiBackendSessionQuery,
    pageAiNewSession,
    pageAiNormalizeSessions,
    pageAiNormalizeBackendSessionRow,
    pageAiMergeSessions,
    pageAiSessionStorageLabel,
    pageAiLoadSessions,
    pageAiLoadBackendSessions,
    pageAiMessageFromRuntimeEvent,
    pageAiApplyBackendSessionDetail,
    pageAiLoadBackendSessionDetail,
    pageAiSearchBackendSessions,
    pageAiPersistSessions,
    pageAiEnsureHermesSession,
    pageAiRestoreHermesSession,
    pageAiCurrentSession,
    pageAiSessionSelected,
    pageAiToggleSessionSelection,
    pageAiClearSessionSelection,
    pageAiSelectedSessionCount,
    pageAiSyncCurrentSessionMessages,
    pageAiSetActiveSession,
    pageAiStartNewSession,
    pageAiRenameBackendSession,
    pageAiDeleteBackendSession,
    pageAiDeleteSelectedBackendSessions,
    pageAiResumeBackendSession,
    pageAiCheckActiveRun
  };
}
