import { existsSync } from 'fs';
import { clearComposerAttachmentsScript, clearComposerInputScript, extractComposerDraftImagesScript, } from './composer-images.js';
import { generationStopDomAction } from './generation-stop-dom.js';
import { focusEditorAuxiliaryTab } from './editor-tab-focus-dom.js';
import { questionnaireDomAction } from './questionnaire-dom.js';
import { newChatDomAction } from './new-chat-dom.js';
import { pickNewChatFromSidebarDiff, readSidebarRepoAgentsDom, } from './sidebar-repo-agents-dom.js';
import { readActiveChatDom } from './read-active-chat-dom.js';
import { focusSidebarAgentDom } from './focus-sidebar-agent-dom.js';
import { queueRemoveDomAction } from './queue-remove-dom.js';
import { isPathUnderUploadDir } from './image-upload-store.js';
import { activityTracker } from './activity-tracker.js';
const RETRY_DELAY_MS = 400;
export class CommandExecutor {
    queuedMediaHints;
    selectors;
    client = null;
    activeComposerId = '';
    activeChatTitle = '';
    constructor(selectors, queuedMediaHints) {
        this.queuedMediaHints = queuedMediaHints;
        this.selectors = selectors;
    }
    setClient(client) {
        this.client = client;
    }
    /** From bridge state — skip redundant sidebar focus when chat already open. */
    setActiveChat(activeComposerId, activeChatTitle) {
        this.activeComposerId = activeComposerId?.trim() ?? '';
        this.activeChatTitle = activeChatTitle?.trim() ?? '';
    }
    shouldSkipFocus(agentId, chatTitle) {
        const id = agentId.trim();
        if (!id)
            return false;
        if (this.activeComposerId && id === this.activeComposerId)
            return true;
        const title = chatTitle?.trim();
        if (title &&
            this.activeChatTitle &&
            title.toLowerCase() === this.activeChatTitle.toLowerCase()) {
            return true;
        }
        return false;
    }
    async ensureAgentFocused(agentId, commandId, chatTitle) {
        if (this.shouldSkipFocus(agentId, chatTitle))
            return null;
        const focused = await this.focusAgent(agentId, commandId, chatTitle);
        if (!focused.ok)
            return focused;
        await sleep(200);
        return null;
    }
    async execute(cmd) {
        activityTracker.touch();
        try {
            switch (cmd.type) {
                case 'send_message': {
                    if (cmd.agentId) {
                        const focusErr = await this.ensureAgentFocused(cmd.agentId, cmd.id, cmd.tabTitle);
                        if (focusErr)
                            return focusErr;
                    }
                    return await this.sendMessage(cmd);
                }
                case 'focus_agent':
                    return await this.focusAgent(cmd.agentId ?? '', cmd.id, cmd.tabTitle, cmd.repoName);
                case 'switch_tab':
                    return await this.switchTab(cmd);
                case 'new_chat':
                    return await this.newChat(cmd);
                case 'approve':
                    return await this.clickSelector(cmd, cmd.selectorPath);
                case 'reject':
                    return await this.clickSelector(cmd, cmd.selectorPath);
                case 'approve_all':
                    return await this.approveAll(cmd);
                case 'start_multitasking':
                    return await this.clickByLabel(cmd, 'Start Multitasking');
                case 'remove_queued': {
                    const client = this.client;
                    if (!client)
                        return { id: cmd.id, ok: false, error: 'CDP not connected' };
                    if (cmd.agentId) {
                        const focusErr = await this.ensureAgentFocused(cmd.agentId, cmd.id, cmd.tabTitle);
                        const hasTarget = Boolean(cmd.queueItemId?.trim()) ||
                            Boolean(cmd.selectorPath?.trim()) ||
                            Boolean(cmd.text?.trim());
                        if (focusErr && !hasTarget)
                            return focusErr;
                    }
                    const removed = (await client.callFunction(queueRemoveDomAction, 'remove', {
                        text: cmd.text?.trim(),
                        selectorPath: cmd.selectorPath,
                        queueItemId: cmd.queueItemId?.trim(),
                    }));
                    if (!removed?.ok) {
                        return {
                            id: cmd.id,
                            ok: false,
                            error: removed?.reason ?? 'Queue item not found in Cursor',
                        };
                    }
                    if (cmd.text?.trim()) {
                        this.queuedMediaHints?.forget(cmd.text);
                    }
                    await sleep(200);
                    const restoreText = cmd.text?.trim();
                    if (restoreText) {
                        await this.setComposerInputText(restoreText);
                    }
                    const composerDraft = await this.readComposerDraft();
                    return {
                        id: cmd.id,
                        ok: true,
                        composerDraft: restoreText || composerDraft || undefined,
                    };
                }
                case 'stop_agent':
                    return await this.stopAgent(cmd);
                case 'click_selector':
                    return await this.clickSelector(cmd, cmd.selectorPath);
                default:
                    return { id: cmd.id, ok: false, error: `Unknown command: ${cmd.type}` };
            }
        }
        catch (err) {
            return { id: cmd.id, ok: false, error: err.message };
        }
    }
    parseImagePaths(raw) {
        if (!raw?.trim())
            return [];
        return raw
            .split(',')
            .map((p) => p.trim())
            .filter((p) => {
            if (!p.startsWith('/'))
                return false;
            if (isPathUnderUploadDir(p))
                return true;
            return existsSync(p);
        });
    }
    async sendMessage(cmd) {
        const client = this.client;
        const text = cmd.text?.trim() ?? '';
        const imagePaths = this.parseImagePaths(cmd.imagePaths);
        if (!client)
            return { id: cmd.id, ok: false, error: 'No client' };
        if (!text && imagePaths.length === 0) {
            return { id: cmd.id, ok: false, error: 'No text or images' };
        }
        const inputStrategies = this.selectors.chatInput.strategies;
        const submitStrategies = this.selectors.submitButton.strategies;
        const wantQueue = cmd.queue === true || cmd.queue === 'true' || cmd.queue === '1';
        const prep = (await client.evaluate(`
      (() => {
        const inputSels = ${JSON.stringify(inputStrategies)};
        const scopes = [
          document.querySelector('[class*="agent-panel"]'),
          document.querySelector('[class*="composer-panel"]'),
          document.querySelector('.composer-bar'),
          document.querySelector('[class*="composer-bar"]'),
          document,
        ].filter(Boolean);
        let input = null;
        for (const sel of inputSels) {
          try {
            for (const scope of scopes) {
              const el = scope.querySelector(sel);
              if (el) {
                input = el;
                break;
              }
            }
            if (input) break;
          } catch {}
        }
        if (!input) return { ok: false, error: 'Chat input not found' };

        input.scrollIntoView({ block: 'center' });
        input.focus();
        (input).click();

        return { ok: true, agentBusy: false };
      })()
    `));
        if (!prep?.ok) {
            return { id: cmd.id, ok: false, error: prep?.error ?? 'Chat input not found' };
        }
        const agentBusy = (await client.callFunction(generationStopDomAction, 'has')) ===
            true;
        const shouldQueue = wantQueue || agentBusy;
        const savedDraft = await this.readComposerDraft();
        const savedDraftImages = await this.readComposerDraftImages();
        const sentFromConnect = Boolean(text || imagePaths.length > 0);
        try {
            await this.clearComposerInput();
            await this.clearComposerAttachments();
            if (imagePaths.length > 0) {
                try {
                    await client.attachComposerImages(imagePaths);
                    await sleep(shouldQueue ? 550 : 400);
                }
                catch (err) {
                    return {
                        id: cmd.id,
                        ok: false,
                        error: `Attach images: ${err.message}`,
                    };
                }
            }
            if (text) {
                await client.typeText(text);
                await sleep(shouldQueue ? 200 : 150);
            }
            else {
                await sleep(shouldQueue ? 120 : 80);
            }
            if (shouldQueue) {
                await client.pressKey('Enter', 'Enter', 13);
                await sleep(450);
                if (text && imagePaths.length > 0) {
                    this.queuedMediaHints?.note(text, imagePaths);
                }
                return { id: cmd.id, ok: true };
            }
            const clicked = await client.evaluate(`
      (() => {
        const submitSels = ${JSON.stringify(submitStrategies)};
        const scopes = [
          document.querySelector('[class*="agent-panel"]'),
          document.querySelector('[class*="composer-panel"]'),
          document.querySelector('.composer-bar'),
          document.querySelector('[class*="composer-bar"]'),
          document,
        ].filter(Boolean);
        for (const scope of scopes) {
          for (const sel of submitSels) {
            try {
              const btn = scope.querySelector(sel);
              if (!btn || btn.getAttribute('data-state') === 'stop') continue;
              const disabled =
                btn.hasAttribute('disabled') || btn.getAttribute('aria-disabled') === 'true';
              if (disabled) continue;
              btn.scrollIntoView({ block: 'center' });
              (btn).click();
              return true;
            } catch {}
          }
        }
        return false;
      })()
    `);
            if (!clicked)
                await client.pressKey('Enter', 'Enter', 13);
            return { id: cmd.id, ok: true };
        }
        finally {
            if (!shouldQueue) {
                await sleep(350);
                await this.restoreComposerDraft(savedDraft, savedDraftImages);
            }
            else {
                // After queue: re-attaching sent images to composer can steal them from queued rows.
                const restoreText = savedDraft.trim();
                const restoreImages = sentFromConnect && imagePaths.length > 0 ? [] : savedDraftImages;
                if (restoreText || restoreImages.length > 0) {
                    await sleep(150);
                    await this.restoreComposerDraft(restoreText, restoreImages);
                }
            }
        }
    }
    async focusAgent(agentId, commandId = 'focus', chatTitle, repoName) {
        const client = this.client;
        if (!client || !agentId) {
            return { id: commandId, ok: false, error: 'No client or agentId' };
        }
        const tabSelectors = this.selectors.chatTabList.strategies;
        const editorFocused = (await client.callFunction(focusEditorAuxiliaryTab, { title: chatTitle ?? '', agentId })) === true;
        if (editorFocused) {
            return { id: commandId, ok: true };
        }
        try {
            const sidebarFocus = (await client.callFunction(focusSidebarAgentDom, {
                title: chatTitle ?? '',
                agentId,
                repoName: repoName ?? '',
            }, 25_000));
            if (sidebarFocus?.ok) {
                await sleep(350);
                return { id: commandId, ok: true };
            }
            if (sidebarFocus?.reason === 'row-not-found') {
                console.warn('[focus] sidebar row not found:', chatTitle ?? agentId);
            }
        }
        catch (err) {
            console.warn('[focus] sidebar callFunction:', err.message);
        }
        const ok = await client.evaluate(`
      (() => {
        const wantId = ${JSON.stringify(agentId)};
        const wantTitle = ${JSON.stringify(chatTitle ?? '')};
        const selectors = ${JSON.stringify(tabSelectors)};
        const stripEditorSuffix = (s) =>
          (s || '').replace(/,\\s*Chat Editors:\\s*Editor Group\\s*\\d+\\s*$/i, '').trim();
        const norm = (s) =>
          stripEditorSuffix(s)
            .replace(/\\s+/g, ' ')
            .replace(/\\d+\\s*(?:s|m|h|d|w)\\b/gi, '')
            .replace(/\\d+[smhdw]$/i, '')
            .trim()
            .toLowerCase();
        const titleNorm = wantTitle ? norm(wantTitle) : '';
        const wantNorm = wantId.startsWith('title:') ? wantId.slice(6) : norm(wantId);
        const tryClick = (el) => {
          if (!el) return false;
          el.scrollIntoView({ block: 'nearest', behavior: 'instant' });
          el.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true }));
          el.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, cancelable: true }));
          el.click();
          return true;
        };
        const matchBtn = (btn) => {
          const label = btn.querySelector('.ui-sidebar-menu-button-label');
          let t = (label?.textContent || btn.textContent || '').trim().replace(/\\s+/g, ' ');
          t = t.replace(/\\d+\\s*(?:s|m|h|d|w)\\b/gi, '').replace(/\\d+[smhdw]$/i, '').trim();
          const cid = btn.getAttribute('data-composer-id')
            || btn.closest('li')?.getAttribute('data-composer-id')
            || btn.closest('[data-composer-id]')?.getAttribute('data-composer-id')
            || '';
          if (cid && cid === wantId) return true;
          const tNorm = norm(t);
          if (titleNorm && tNorm === titleNorm) return true;
          if (titleNorm && tNorm.length >= 8 && titleNorm.includes(tNorm)) return true;
          if (titleNorm && tNorm.length >= 8 && tNorm.includes(titleNorm)) return true;
          return tNorm === wantNorm;
        };
        for (const btn of document.querySelectorAll(
          '.glass-sidebar-agent-list-container li.ui-sidebar-menu-item > div.glass-sidebar-agent-menu-btn'
        )) {
          if (matchBtn(btn)) return tryClick(btn);
        }
        for (const sel of selectors) {
          for (const cell of document.querySelectorAll(sel)) {
            const cid = cell.getAttribute('data-composer-id')
              || cell.closest('[data-composer-id]')?.getAttribute('data-composer-id');
            if (cid === wantId) return tryClick(cell);
          }
        }
        for (const cell of document.querySelectorAll('[data-composer-id]')) {
          if (cell.getAttribute('data-composer-id') === wantId) return tryClick(cell);
        }
        return false;
      })()
    `);
        return ok
            ? { id: commandId, ok: true }
            : {
                id: commandId,
                ok: false,
                error: chatTitle
                    ? `Чат «${chatTitle.slice(0, 48)}» не открыт в Cursor — откройте его в сайдбаре`
                    : 'Agent tab not open in Cursor — open it once or start a new chat',
            };
    }
    /** Прокрутка к низу ленты — там смонтированы последние user/assistant узлы. */
    async scrollChatToBottom() {
        const client = this.client;
        if (!client)
            return;
        const containerSelectors = this.selectors.chatContainer.strategies;
        try {
            await client.evaluate(`
        (() => {
          const sels = ${JSON.stringify(containerSelectors)};
          function findFirst(sels) {
            for (const sel of sels) {
              try {
                const el = document.querySelector(sel);
                if (el) return el;
              } catch { /* skip */ }
            }
            return null;
          }
          const anchor = findFirst(sels);
          if (!anchor) return false;
          let scrollEl = anchor;
          for (let i = 0; i < 14 && scrollEl; i++) {
            const st = getComputedStyle(scrollEl);
            const oy = st.overflowY;
            if (
              (oy === 'auto' || oy === 'scroll' || oy === 'overlay') &&
              scrollEl.scrollHeight > scrollEl.clientHeight + 8
            ) {
              break;
            }
            scrollEl = scrollEl.parentElement;
          }
          if (!scrollEl || scrollEl === document.body) return false;
          scrollEl.scrollTop = scrollEl.scrollHeight;
          scrollEl.dispatchEvent(new Event('scroll', { bubbles: true }));
          return true;
        })()
      `);
        }
        catch {
            /* non-fatal */
        }
    }
    async switchTab(cmd) {
        const client = this.client;
        const title = cmd.tabTitle?.trim();
        if (!client || !title)
            return { id: cmd.id, ok: false, error: 'No client or tab title' };
        const tabSelectors = this.selectors.chatTabList.strategies;
        const ok = await client.evaluate(`
      (() => {
        const selectors = ${JSON.stringify(tabSelectors)};
        const want = ${JSON.stringify(title)};
        for (const sel of selectors) {
          for (const cell of document.querySelectorAll(sel)) {
            const t = (cell.getAttribute('aria-label') || cell.getAttribute('title') || cell.textContent || '').trim();
            if (t.includes(want) || want.includes(t)) {
              (cell as HTMLElement).click();
              return true;
            }
          }
        }
        return false;
      })()
    `);
        return ok ? { id: cmd.id, ok: true } : { id: cmd.id, ok: false, error: 'Tab not found' };
    }
    async clickSelector(cmd, selectorPath) {
        const client = this.client;
        if (!client || !selectorPath) {
            return { id: cmd.id, ok: false, error: 'No client or selector' };
        }
        const path = selectorPath;
        const isQuestionnaire = path.includes('composer-questionnaire') ||
            path.includes('ui-tray-option') ||
            path.includes('ui-tray-actions') ||
            Boolean(cmd.questionnaireAction?.trim());
        if (isQuestionnaire) {
            const freeformText = cmd.questionnaireFreeform?.trim();
            if (cmd.questionnaireAction === 'freeform_text' && freeformText) {
                const filled = (await client.callFunction(questionnaireDomAction, 'freeform_text', {
                    letter: cmd.questionnaireLetter,
                    label: cmd.questionnaireLabel,
                    selectorPath: path,
                    text: freeformText,
                })) === true;
                if (filled) {
                    await sleep(200);
                    return { id: cmd.id, ok: true };
                }
            }
            const action = cmd.questionnaireAction === 'skip' || cmd.questionnaireAction === 'continue'
                ? cmd.questionnaireAction
                : 'option';
            if (freeformText && action === 'continue') {
                await client.callFunction(questionnaireDomAction, 'freeform_text', {
                    letter: cmd.questionnaireLetter,
                    label: cmd.questionnaireLabel,
                    selectorPath: path,
                    text: freeformText,
                });
                await sleep(action === 'continue' ? 250 : 150);
            }
            const domOk = (await client.callFunction(questionnaireDomAction, action, {
                letter: cmd.questionnaireLetter,
                label: cmd.questionnaireLabel,
                selectorPath: path,
            })) === true;
            if (domOk) {
                await sleep(200);
                return { id: cmd.id, ok: true };
            }
        }
        try {
            await client.click(path);
            return { id: cmd.id, ok: true };
        }
        catch (err) {
            return { id: cmd.id, ok: false, error: err.message };
        }
    }
    async clickByLabel(cmd, label) {
        const client = this.client;
        if (!client)
            return { id: cmd.id, ok: false, error: 'No CDP client' };
        const ok = await client.evaluate(`
      (() => {
        const want = ${JSON.stringify(label)}.toLowerCase();
        for (const el of document.querySelectorAll('button, a, [role="button"], span, div')) {
          const t = (el.textContent || '').trim().toLowerCase();
          if (t.includes(want)) {
            (el as HTMLElement).click();
            return true;
          }
        }
        return false;
      })()
    `);
        return ok
            ? { id: cmd.id, ok: true }
            : { id: cmd.id, ok: false, error: `${label} not found` };
    }
    async approveAll(cmd) {
        const client = this.client;
        if (!client)
            return { id: cmd.id, ok: false, error: 'No CDP client' };
        const patterns = this.selectors.approveButton.textMatch ?? ['Accept All'];
        const ok = await client.evaluate(`
      (() => {
        for (const btn of document.querySelectorAll('button')) {
          const t = (btn.textContent || '').toLowerCase();
          for (const pat of ${JSON.stringify(patterns)}) {
            if (t.includes(pat.toLowerCase())) {
              btn.click();
              return true;
            }
          }
        }
        return false;
      })()
    `);
        return ok
            ? { id: cmd.id, ok: true }
            : { id: cmd.id, ok: false, error: 'Accept All not found' };
    }
    async stopAgent(cmd) {
        const client = this.client;
        if (!client)
            return { id: cmd.id, ok: false, error: 'No CDP client' };
        if (cmd.agentId) {
            const focusErr = await this.ensureAgentFocused(cmd.agentId, cmd.id, cmd.tabTitle);
            if (focusErr)
                return focusErr;
        }
        const ok = (await client.callFunction(generationStopDomAction, 'click')) === true;
        if (!ok) {
            return { id: cmd.id, ok: false, error: 'Stop button not found (agent not running?)' };
        }
        await sleep(150);
        let composerDraft = await this.readComposerDraft();
        let composerDraftImages = await this.readComposerDraftImages();
        if (!composerDraft && composerDraftImages.length === 0) {
            await sleep(120);
            composerDraft = await this.readComposerDraft();
            composerDraftImages = await this.readComposerDraftImages();
        }
        if (composerDraft.trim() || composerDraftImages.length > 0) {
            await this.clearComposerInput();
            await this.clearComposerAttachments();
        }
        return {
            id: cmd.id,
            ok: true,
            composerDraft: composerDraft || undefined,
            composerDraftImages: composerDraftImages.length ? composerDraftImages : undefined,
        };
    }
    async readComposerDraftImages() {
        const client = this.client;
        if (!client)
            return [];
        const paths = await client.evaluate(extractComposerDraftImagesScript());
        return Array.isArray(paths) ? paths.filter((p) => typeof p === 'string') : [];
    }
    async clearComposerAttachments() {
        const client = this.client;
        if (!client)
            return;
        try {
            await client.evaluate(clearComposerAttachmentsScript());
            await sleep(120);
        }
        catch {
            /* best-effort */
        }
    }
    /** Empty Cursor composer (DOM + keyboard). */
    async clearComposerInput() {
        const client = this.client;
        if (!client)
            return;
        const inputStrategies = this.selectors.chatInput.strategies;
        try {
            await client.evaluate(clearComposerInputScript(inputStrategies));
        }
        catch {
            /* fall through to keyboard */
        }
        if (!(await this.focusComposerInput()))
            return;
        await sleep(80);
        // CDP modifiers: 4=Meta (⌘ on Mac), 2=Ctrl — Lexical often ignores Ctrl+A on macOS.
        await client.pressKey('a', 'KeyA', 65, 4);
        await sleep(40);
        let left = (await this.readComposerDraft())?.trim() ?? '';
        if (left) {
            await client.pressKey('a', 'KeyA', 65, 2);
            await sleep(40);
        }
        await client.pressKey('Backspace', 'Backspace', 8);
        await sleep(80);
        left = (await this.readComposerDraft())?.trim() ?? '';
        if (left) {
            try {
                await client.evaluate(clearComposerInputScript(inputStrategies));
            }
            catch {
                /* best-effort */
            }
        }
    }
    /** Replace composer text (after unqueue / restore). */
    async setComposerInputText(text) {
        const draft = text.trim();
        if (!draft)
            return;
        await this.clearComposerInput();
        const client = this.client;
        if (!client || !(await this.focusComposerInput()))
            return;
        await sleep(80);
        await client.typeText(draft);
        await sleep(120);
    }
    /** Put saved Cursor draft back after send from Connect (avoids merging with phone text). */
    async restoreComposerDraft(text, imagePaths) {
        const draft = text.trim();
        const paths = imagePaths.filter((p) => p.trim().length > 0);
        if (!draft && paths.length === 0)
            return;
        const client = this.client;
        if (!client)
            return;
        if (!(await this.focusComposerInput()))
            return;
        await sleep(80);
        if (draft) {
            await client.typeText(draft);
            await sleep(120);
        }
        if (paths.length > 0) {
            try {
                await client.attachComposerImages(paths);
                await sleep(250);
            }
            catch {
                /* best-effort */
            }
        }
    }
    async focusComposerInput() {
        const client = this.client;
        if (!client)
            return false;
        const inputStrategies = this.selectors.chatInput.strategies;
        const focused = (await client.evaluate(`
      (() => {
        const inputSels = ${JSON.stringify(inputStrategies)};
        const scopes = [
          document.querySelector('[class*="agent-panel"]'),
          document.querySelector('[class*="composer-panel"]'),
          document.querySelector('.composer-bar'),
          document.querySelector('[class*="composer-bar"]'),
          document,
        ].filter(Boolean);
        let input = null;
        for (const sel of inputSels) {
          try {
            for (const scope of scopes) {
              const el = scope.querySelector(sel);
              if (el) {
                input = el;
                break;
              }
            }
            if (input) break;
          } catch {}
        }
        if (!input) return false;
        input.scrollIntoView({ block: 'center' });
        input.focus();
        (input).click();
        return true;
      })()
    `));
        return focused === true;
    }
    async readComposerDraft() {
        const client = this.client;
        if (!client)
            return '';
        const inputStrategies = this.selectors.chatInput.strategies;
        const raw = await client.evaluate(`
      (() => {
        const inputSels = ${JSON.stringify(inputStrategies)};
        const scopes = [
          document.querySelector('[class*="agent-panel"]'),
          document.querySelector('[class*="composer-panel"]'),
          document.querySelector('.composer-bar'),
          document.querySelector('[class*="composer-bar"]'),
          document,
        ].filter(Boolean);
        let input = null;
        for (const sel of inputSels) {
          try {
            for (const scope of scopes) {
              const el = scope.querySelector(sel);
              if (el) {
                input = el;
                break;
              }
            }
            if (input) break;
          } catch {}
        }
        if (!input) return '';
        return (input.innerText || input.textContent || '')
          .replace(/\\u200b/g, ' ')
          .replace(/\\s+/g, ' ')
          .trim();
      })()
    `);
        return typeof raw === 'string' ? raw : '';
    }
    async newChat(cmd) {
        const client = this.client;
        if (!client)
            return { id: cmd.id, ok: false, error: 'No CDP client' };
        const repoName = cmd.repoName?.trim() || cmd.tabTitle?.trim() || '';
        const beforeSnap = (await client.callFunction(readSidebarRepoAgentsDom, { repoName }));
        const beforeAgents = beforeSnap.agents ?? [];
        const selectorPath = cmd.newChatSelectorPath?.trim() || cmd.selectorPath?.trim() || '';
        const clicked = (await client.callFunction(newChatDomAction, {
            repoName,
            selectorPath,
            globalButtonSelectors: this.selectors.newChatButton.strategies,
        }));
        if (!clicked?.ok) {
            return {
                id: cmd.id,
                ok: false,
                error: clicked?.reason ?? 'New chat button not found',
            };
        }
        let resolved = {};
        for (const ms of [450, 950, 1600, 2400, 3600]) {
            await sleep(ms);
            const afterSnap = (await client.callFunction(readSidebarRepoAgentsDom, { repoName }));
            const afterAgents = afterSnap.agents ?? [];
            const fromDiff = pickNewChatFromSidebarDiff(beforeAgents, afterAgents);
            if (fromDiff.composerId) {
                resolved = fromDiff;
                break;
            }
            const active = (await client.callFunction(readActiveChatDom));
            if (active?.composerId) {
                const knownBefore = beforeAgents.some((a) => a.composerId === active.composerId);
                if (!knownBefore) {
                    resolved = { composerId: active.composerId, title: active.title };
                    break;
                }
            }
        }
        if (!resolved.composerId) {
            console.warn(`[new_chat] clicked (${clicked.method ?? 'dom'}) but composer id not resolved yet` +
                (repoName ? ` repo=«${repoName}»` : ''));
            return { id: cmd.id, ok: true };
        }
        return {
            id: cmd.id,
            ok: true,
            activeAgentId: resolved.composerId,
            ...(resolved.title ? { activeChatTitle: resolved.title } : {}),
        };
    }
}
function sleep(ms) {
    return new Promise((r) => setTimeout(r, ms));
}
