(function () {
    window.AgentFrontendModules = window.AgentFrontendModules || {};

    window.AgentFrontendModules.createSessionHistoryModule = function createSessionHistoryModule(scope) {
        return (function () {
            with (scope) {
    function _formatCompactNumber(value) {
        const n = Number(value) || 0;
        return n >= 1000 ? (n / 1000).toFixed(1) + 'k' : String(n);
    }

    function _normalizePolicyBudgetMode(value) {
        return String(value || 'budget').toLowerCase() === 'dynamic' ? 'dynamic' : 'budget';
    }

    function _updatePolicyBudgetModeUi() {
        const mode = _normalizePolicyBudgetMode(policyBudgetModeSelect ? policyBudgetModeSelect.value : appSettings.policy_budget_mode);
        const dynamic = mode === 'dynamic';
        [
            policyMaxStepsInput,
            policyMaxToolCallsInput,
            policyMaxTokensInput,
            policyMaxRuntimeInput,
        ].forEach((input) => {
            if (input) input.disabled = dynamic;
        });
        if (policyBudgetModeNote) {
            policyBudgetModeNote.textContent = dynamic
                ? 'Dynamisch actief: run/calls/steps/tokens/runtime zijn onbeperkt.'
                : 'Budget actief: de limieten hieronder worden afgedwongen.';
        }
    }

    function _renderBudgetCounter(run_id = activeRunId) {
        const run = run_id && runs.has(run_id) ? runs.get(run_id) : null;
        const tokens = run ? (run._tokenTotal || 0) : 0;
        const stepsDone = run ? (run._stepsDone || 0) : 0;
        const toolsDone = run ? (run._toolsDone || 0) : 0;
        const dynamicBudget = _normalizePolicyBudgetMode(appSettings.policy_budget_mode) === 'dynamic';
        const maxSteps = Number(appSettings.policy_max_steps_per_run || 0);
        const maxTools = Number(appSettings.policy_max_tool_calls_per_run || 0);

        const isActive = run && (tokens > 0 || stepsDone > 0 || toolsDone > 0);
        if (tokenCounterEl) tokenCounterEl.style.display = isActive ? '' : 'none';
        if (budgetCounterEl) budgetCounterEl.style.display = isActive ? '' : 'none';

        if (tokenCount) tokenCount.textContent = _formatCompactNumber(tokens);
        if (budgetCount) {
            const stepsText = dynamicBudget ? `${stepsDone}/∞ st` : (maxSteps > 0 ? `${stepsDone}/${maxSteps} st` : `${stepsDone} st`);
            const toolsText = dynamicBudget ? `${toolsDone}/∞ tl` : (maxTools > 0 ? `${toolsDone}/${maxTools} tl` : `${toolsDone} tl`);
            budgetCount.textContent = `${stepsText} · ${toolsText}`;
        }
    }

    function _createRunLane(run_id, goal) {
        if (!runsContainer) return;
        // Remove empty hint
        const hint = runsContainer.querySelector('.runs-empty-hint');
        if (hint) hint.remove();

        const CLEAR_ICON    = '<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M9 6V4h6v2"/></svg>';

        const lane = document.createElement('div');
        lane.className = 'run-lane';
        lane.id = `lane-${run_id}`;
        lane.addEventListener('mousedown', () => _setActiveRun(run_id));

        // Header
        const header = document.createElement('div');
        header.className = 'lane-header';

        const goalBox = document.createElement('div');
        goalBox.className = 'lane-goal-box';
        const runIdEl = document.createElement('span');
        runIdEl.className = 'lane-run-id';
        runIdEl.textContent = `#${run_id}`;
        const goalEl = document.createElement('div');
        goalEl.className = 'lane-goal';
        goalEl.textContent = goal;
        goalEl.title = goal;
        goalBox.appendChild(runIdEl);
        goalBox.appendChild(goalEl);

        const controls = document.createElement('div');
        controls.className = 'lane-controls';

        const clearBtn = document.createElement('button');
        clearBtn.className = 'lane-btn lane-clear-btn';
        clearBtn.title = 'Clean up';
        clearBtn.innerHTML = CLEAR_ICON;
        clearBtn.addEventListener('click', () => {
            _discardRunLane(run_id);
        });
        controls.appendChild(clearBtn);

        header.appendChild(goalBox);
        header.appendChild(controls);
        lane.appendChild(header);

        // Agent strip
        const agentsEl = document.createElement('div');
        agentsEl.className = 'lane-agents';
        AGENT_ORDER.forEach((agent, idx) => {
            const card = document.createElement('div');
            card.className = 'agent-card idle';
            card.dataset.agent = agent;
            const name = agent.charAt(0).toUpperCase() + agent.slice(1);
            card.innerHTML = `<div class="agent-icon">${AGENT_ICONS[agent] || '\ud83e\udd16'}</div><div class="agent-info"><span class="agent-name">${agent === 'reportout' ? 'Report Out' : name}</span><span class="agent-state-label">idle</span></div>`;
            if (agent === 'reportout') {
                card.title = 'Click to reopen final report';
                card.addEventListener('click', () => {
                    const run = runs.get(run_id);
                    if (run && run._reportContent != null) {
                        showReportModal(run._reportGoal || run.goal, run._reportContent, run._outputFiles || []);
                    }
                });
            }
            agentsEl.appendChild(card);
            if (idx < AGENT_ORDER.length - 1) {
                const conn = document.createElement('span');
                conn.className = 'agent-connector';
                conn.dataset.from = agent;
                conn.dataset.to = AGENT_ORDER[idx + 1];
                conn.textContent = '';
                agentsEl.appendChild(conn);
            }
        });
        lane.appendChild(agentsEl);

        // Plan list
        const planEl = document.createElement('div');
        planEl.className = 'lane-plan';
        planEl.style.display = 'none';
        const planList = document.createElement('ol');
        planList.className = 'plan-list';
        planEl.appendChild(planList);
        lane.appendChild(planEl);

        // Step progress bar
        const progressWrap = document.createElement('div');
        progressWrap.className = 'lane-progress-wrap hidden';
        const progressLabel = document.createElement('span');
        progressLabel.className = 'lane-progress-label';
        const progressTrack = document.createElement('div');
        progressTrack.className = 'lane-progress-track';
        const progressFill = document.createElement('div');
        progressFill.className = 'lane-progress-fill';
        progressTrack.appendChild(progressFill);
        progressWrap.appendChild(progressLabel);
        progressWrap.appendChild(progressTrack);
        lane.appendChild(progressWrap);

        // Log
        const logPane = document.createElement('div');
        logPane.className = 'lane-log scroll-area';
        lane.appendChild(logPane);

        runsContainer.appendChild(lane);
        runs.set(run_id, {
            goal, lane, logPane, agentsEl, planEl, planList, progressWrap, progressLabel, progressFill,

            _sessionId: null,
            _chatBubbles: [],
            totalSteps: 0,
            _activePlanStepIndex: -1,
            _streamEntry: null, _streamBody: null,
            _reportContent: null, _reportGoal: null,
            _outputFiles: [],
            _tokenTotal: 0,
            _stepsDone: 0,
            _toolsDone: 0,
            _lastAgent: '',
            _lastAgentLabel: '',
            _lastAgentState: '',
            _llmActive: false,
            _llmLastActiveAt: 0,
            _killRequested: false,
        });
        // Add flow tab and switch to this lane
        _addRunTab(run_id, goal);
        _setActiveRun(run_id);
        _updateLaneLayout();
        _refreshWorkflowIndicator();
    }

    // --- Agent panel helpers ---
    const AGENT_ORDER = ['planner','researcher','curator','executor','validator','reportout'];

    function setAgentState(agent, state, msg, runId = '') {
        const targetRunId = runId || _currentHandlingRunId || activeRunId;
        const run = runs.get(targetRunId);
        if (!run || !run.agentsEl) return;
        if (agent === 'all') {
            AGENT_ORDER.forEach(a => {
                const card = run.agentsEl.querySelector(`[data-agent="${a}"]`);
                if (card) {
                    card.className = 'agent-card ' + state;
                    const lbl = card.querySelector('.agent-state-label');
                    if (lbl) lbl.textContent = state === 'skip' ? 'skipped' : state;
                }
            });
            return;
        }
        const card = run.agentsEl.querySelector(`[data-agent="${agent}"]`);
        if (!card) return;
        card.className = `agent-card ${state}`;
        const lbl = card.querySelector('.agent-state-label');
        if (lbl) lbl.textContent = state === 'skip' ? 'skipped' : state;
        if (msg) card.title = msg;
    }

    function flashConnector(fromAgent, toAgent, runId = '') {
        const targetRunId = runId || _currentHandlingRunId || activeRunId;
        const run = runs.get(targetRunId);
        if (!run || !run.agentsEl) return;
        run.agentsEl.querySelectorAll('.agent-connector').forEach(c => {
            if (c.dataset.from === fromAgent && c.dataset.to === toAgent) {
                c.classList.remove('flash');
                void c.offsetWidth;
                c.classList.add('flash');
                setTimeout(() => c.classList.remove('flash'), 800);
            }
        });
    }

    function escapeHtml(str) {
        return (str || '')
            .replaceAll('&', '&amp;')
            .replaceAll('<', '&lt;')
            .replaceAll('>', '&gt;')
            .replaceAll('"', '&quot;')
            .replaceAll("'", '&#39;');
    }

    function _linkifyUrls(html) {
        let out = html.replace(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/gi,
            '<a href="$2" target="_blank" rel="noopener">$1</a>');
        out = out.replace(/(?<!["=a-z])(https?:\/\/[^\s<>"']+)/gi, (match) => {
            const href = match.replace(/&amp;/g, '&');
            return `<a href="${href}" target="_blank" rel="noopener">${match}</a>`;
        });
        return out;
    }

    function _createPlanStepItem(step) {
        const li = document.createElement('li');
        const badge = document.createElement('span');
        badge.className = 'plan-step-badge';
        const copy = document.createElement('div');
        copy.className = 'plan-step-copy';
        const text = document.createElement('span');
        text.className = 'plan-step-text';
        const detail = document.createElement('div');
        detail.className = 'plan-step-detail';
        detail.style.display = 'none';
        text.textContent = typeof step === 'string' ? step : (step.step || JSON.stringify(step));
        copy.appendChild(text);
        copy.appendChild(detail);
        li.appendChild(badge);
        li.appendChild(copy);
        _setPlanStepState(li, 'pending');
        return li;
    }

    function _setPlanStepState(stepEl, state) {
        if (!stepEl) return;
        const normalized = ['pending', 'active', 'retrying', 'plan_b', 'completed', 'issue', 'skipped'].includes(state) ? state : 'pending';
        const badge = stepEl.querySelector('.plan-step-badge');
        const labels = {
            pending: 'To do',
            active: 'Doing',
            retrying: 'Retry',
            plan_b: 'Plan B',
            completed: 'Done',
            issue: 'Issue',
            skipped: 'Skip',
        };
        const classes = {
            pending: '',
            active: 'step-active',
            retrying: 'step-active',
            plan_b: 'step-active',
            completed: 'step-completed',
            issue: 'step-issue',
            skipped: 'step-issue',
        };
        stepEl.dataset.state = normalized;
        stepEl.className = classes[normalized] || '';
        if (badge) badge.textContent = labels[normalized];
    }

    function _setPlanStepDetail(stepEl, detailText) {
        if (!stepEl) return;
        const detailEl = stepEl.querySelector('.plan-step-detail');
        if (!detailEl) return;
        const text = String(detailText || '').trim();
        detailEl.textContent = text;
        detailEl.style.display = text ? '' : 'none';
        if (text) {
            stepEl.title = text;
        } else {
            stepEl.removeAttribute('title');
        }
    }

    function _getPlanStepAt(planList, index) {
        if (!planList || index < 0) return null;
        return planList.querySelectorAll('li')[index] || null;
    }

    function _applyTodoProgress(runId, data) {
        const targetRunId = runId || _currentHandlingRunId || activeRunId;
        if (!targetRunId || !runs.has(targetRunId)) return;
        const run = runs.get(targetRunId);
        const items = Array.isArray(data.items) ? data.items : [];
        if (typeof data.total === 'number' && data.total > 0) {
            run.totalSteps = data.total;
            totalSteps = data.total;
        }
        items.forEach((item, index) => {
            let stepEl = _getPlanStepAt(run.planList, index);
            if (!stepEl) {
                run.planList.appendChild(_createPlanStepItem(item.step || `Step ${index + 1}`));
                stepEl = _getPlanStepAt(run.planList, index);
            }
            const textEl = stepEl ? stepEl.querySelector('.plan-step-text') : null;
            if (textEl && item && item.step) textEl.textContent = item.step;
            _setPlanStepState(stepEl, item.state || 'pending');
            const detailParts = [];
            if (item.approach) detailParts.push(item.approach);
            if (item.evaluation) detailParts.push(item.evaluation);
            else if (item.result_preview && ['completed', 'issue', 'skipped'].includes(String(item.state || ''))) detailParts.push(item.result_preview);
            _setPlanStepDetail(stepEl, detailParts.join(' • '));
        });
        if (items.length) run.planEl.style.display = 'block';
        if (typeof data.current_step_index === 'number') {
            run._activePlanStepIndex = Number(data.current_step_index) || 0;
            if (targetRunId === activeRunId) _renderBudgetCounter(targetRunId);
        }
        if (data.goal_evaluation && ['goal_evaluation', 'goal_final'].includes(String(data.event || ''))) {
            const level = String(data.goal_status || '').toLowerCase() === 'complete' ? 'success' : 'info';
            appendLog(`Goal check: ${data.goal_evaluation}`, level, 'Planner');
        }
    }

    // ── Toast Notifications ──────────────────────────────────────────────────
    const _toastIcons = { error: '❌', warning: '⚠️', success: '✅', info: 'ℹ️' };
    function showToast(type, message, duration = 5000) {
        const container = document.getElementById('toast-container');
        if (!container) return;
        const el = document.createElement('div');
        el.className = `toast toast-${type}`;
        el.innerHTML = `<span class="toast-icon">${_toastIcons[type] || ''}</span>`
            + `<span class="toast-body">${escapeHtml(message)}</span>`
            + `<button class="toast-close">&times;</button>`;
        container.appendChild(el);
        const dismiss = () => {
            el.classList.add('toast-out');
            setTimeout(() => el.remove(), 250);
        };
        el.querySelector('.toast-close').addEventListener('click', dismiss);
        if (duration > 0) setTimeout(dismiss, duration);
    }

    function renderReportBody(text) {
        const escaped = escapeHtml(text || '');
        const withInline = _linkifyUrls(escaped
            .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
            .replace(/`([^`]+)`/g, '<code>$1</code>'))
            .replace(/\[\U0001f4ce\s*([^:\]]+):\s*([^\]]+)\]/g,
                '<span class="citation-badge" title="Past session: $2">\U0001f4ce $1: $2</span>');

        const lines = withInline.split(/\r?\n/);
        const html = [];
        let inUl = false;
        let inOl = false;

        const closeLists = () => {
            if (inUl) {
                html.push('</ul>');
                inUl = false;
            }
            if (inOl) {
                html.push('</ol>');
                inOl = false;
            }
        };

        for (let i = 0; i < lines.length; i++) {
            const rawLine = lines[i];
            const line = rawLine.trim();
            if (!line) {
                closeLists();
                continue;
            }

            // Markdown table
            if (line.includes('|') && i + 1 < lines.length) {
                const sep = lines[i + 1].trim();
                if (/^[|\s:-]+$/.test(sep) && sep.includes('---')) {
                    closeLists();
                    const heads = line.split('|').map(c => c.trim()).filter(Boolean);
                    const isCalendarTable = heads.length > 0 && heads[0] === '#' && heads.includes('_event_id');
                    const isEmailTable = heads.length > 0 && heads[0] === '#' && heads.includes('_message_id') && !isCalendarTable;
                    const hiddenCols = (isEmailTable || isCalendarTable)
                        ? heads.reduce((acc, h, hi) => h.startsWith('_') ? [...acc, hi] : acc, [])
                        : [];
                    const emailMessageIdColIdx = isEmailTable ? heads.findIndex(h => h === '_message_id') : -1;
                    const emailProviderColIdx = isEmailTable ? heads.findIndex(h => h === '_provider') : -1;
                    const eventIdColIdx = isCalendarTable ? heads.findIndex(h => h === '_event_id') : -1;
                    const providerColIdx = isCalendarTable ? heads.findIndex(h => h === '_provider') : -1;
                    const startIsoColIdx = isCalendarTable ? heads.findIndex(h => h === '_start_iso') : -1;
                    const endIsoColIdx = isCalendarTable ? heads.findIndex(h => h === '_end_iso') : -1;
                    const tableClass = isEmailTable ? ' email-table' : isCalendarTable ? ' calendar-table' : '';
                    let t = `<div class="chat-table-wrap"><table class="chat-table${tableClass}"><thead><tr>`;
                    for (let hi = 0; hi < heads.length; hi++) {
                        if (hiddenCols.includes(hi)) continue;
                        t += `<th>${heads[hi]}</th>`;
                    }
                    t += '</tr></thead><tbody>';
                    i += 2;
                    while (i < lines.length) {
                        const row = lines[i].trim();
                        if (!row || !row.includes('|')) break;
                        const cells = row.split('|').map(c => c.trim()).filter(Boolean);
                        const idx = (isEmailTable || isCalendarTable) && cells.length > 0 ? cells[0].trim() : '';
                        let rowAttr = '';
                        if (isEmailTable && /^\d+$/.test(idx)) {
                            rowAttr = ` data-email-index="${idx}"`;
                            if (emailMessageIdColIdx >= 0 && cells[emailMessageIdColIdx] && cells[emailMessageIdColIdx] !== '-') {
                                rowAttr += ` data-message-id="${cells[emailMessageIdColIdx].replace(/"/g, '&quot;')}"`;
                            }
                            if (emailProviderColIdx >= 0 && cells[emailProviderColIdx] && cells[emailProviderColIdx] !== '-') {
                                rowAttr += ` data-provider="${cells[emailProviderColIdx].replace(/"/g, '&quot;')}"`;
                            }
                        } else if (isCalendarTable && /^\d+$/.test(idx)) {
                            rowAttr = ` data-calendar-index="${idx}"`;
                            if (eventIdColIdx >= 0 && cells[eventIdColIdx]) {
                                rowAttr += ` data-event-id="${cells[eventIdColIdx].replace(/"/g, '&quot;')}"`;
                            }
                            if (providerColIdx >= 0 && cells[providerColIdx]) {
                                rowAttr += ` data-provider="${cells[providerColIdx].replace(/"/g, '&quot;')}"`;
                            }
                            if (startIsoColIdx >= 0 && cells[startIsoColIdx]) {
                                rowAttr += ` data-start-iso="${cells[startIsoColIdx].replace(/"/g, '&quot;')}"`;
                            }
                            if (endIsoColIdx >= 0 && cells[endIsoColIdx]) {
                                rowAttr += ` data-end-iso="${cells[endIsoColIdx].replace(/"/g, '&quot;')}"`;
                            }
                        }
                        t += `<tr${rowAttr}>`;
                        for (let ci = 0; ci < cells.length; ci++) {
                            if (hiddenCols.includes(ci)) continue;
                            const c = cells[ci];
                            const statusClass = isEmailTable
                                ? (c === 'Unread' || c === 'Ongelezen' ? ' email-status--unread'
                                : c === 'Read' || c === 'Gelezen' ? ' email-status--read'
                                : c === 'Replied' || c === 'Beantwoord' ? ' email-status--replied'
                                : '')
                                : '';
                            if (statusClass) {
                                t += `<td><span class="email-status${statusClass}">${c}</span></td>`;
                            } else {
                                t += `<td>${c}</td>`;
                            }
                        }
                        t += '</tr>';
                        i++;
                    }
                    t += '</tbody></table></div>';
                    html.push(t);
                    i--;
                    continue;
                }
            }

            const boldHeading = line.match(/^\*\*(.+?)\*\*$/);
            if (boldHeading) {
                closeLists();
                html.push(`<h4>${boldHeading[1]}</h4>`);
                continue;
            }

            const bullet = line.match(/^[-*]\s+(.+)/);
            if (bullet) {
                if (inOl) {
                    html.push('</ol>');
                    inOl = false;
                }
                if (!inUl) {
                    html.push('<ul>');
                    inUl = true;
                }
                html.push(`<li>${bullet[1]}</li>`);
                continue;
            }

            const numbered = line.match(/^(\d+)[.)-]\s+(.+)/);
            if (numbered) {
                if (inUl) {
                    html.push('</ul>');
                    inUl = false;
                }
                if (!inOl) {
                    html.push('<ol>');
                    inOl = true;
                }
                html.push(`<li>${numbered[2]}</li>`);
                continue;
            }

            closeLists();
            html.push(`<p>${line}</p>`);
        }

        closeLists();
        return html.join('') || `<p>${withInline}</p>`;
    }

    function formatFinalReport(content) {
        const raw = (content || '').trim();
        if (!raw) {
            return '<div class="report-empty">No final report received.</div>';
        }

        const extractGoalKicker = (text) => {
            const lines = String(text || '').split(/\r?\n/);
            while (lines.length && !lines[0].trim()) lines.shift();
            const first = String(lines[0] || '').trim();
            if (!/^goal (achieved|completed|not achieved|partially achieved)\.?$/i.test(first)) {
                return { kicker: '', body: text };
            }
            lines.shift();
            while (lines.length && !lines[0].trim()) lines.shift();
            return {
                kicker: first,
                body: lines.join('\n').trim(),
            };
        };

        const renderAnswerWithKicker = (text) => {
            const { kicker, body } = extractGoalKicker(text);
            const kickerClass = /not achieved/i.test(kicker)
                ? 'report-kicker report-kicker--warn'
                : 'report-kicker report-kicker--ok';
            const parts = [];
            if (kicker) {
                parts.push(`<div class="${kickerClass}">${escapeHtml(kicker)}</div>`);
            }
            if (body) {
                parts.push(`<div class="report-answer">${renderReportBody(body)}</div>`);
            }
            return parts.join('') || '<div class="report-answer"></div>';
        };

        // Detect section headers: Answer, Notes (and legacy: Details, Executive Summary, etc.)
        const headerRe = /^\s*(?:#{1,4}\s*)?(?:\d+[\.)]\s*)?(Executive Summary|What Was Done|Final Outcome|Open Issues\s*\/\s*Risks|Open Issues\s*&\s*Risks|Open Issues|Risks|Recommended Next Actions|Answer|Details|Notes)\s*:?\s*$/i;
        const lines = raw.split(/\r?\n/);
        const sections = [];
        let currentTitle = 'Answer';  // default to Answer if no header found
        let buffer = [];

        const flush = () => {
            const body = buffer.join('\n').trim();
            if (body) sections.push({ title: currentTitle, body });
            buffer = [];
        };

        for (const line of lines) {
            const m = line.match(headerRe);
            if (m) {
                flush();
                currentTitle = m[1].replace(/\s+/g, ' ').trim();
            } else {
                buffer.push(line);
            }
        }
        flush();

        if (sections.length === 0) {
            return renderAnswerWithKicker(raw);
        }

        return sections.map(s => {
            const key = s.title.toLowerCase();
            // "Answer" section: prominent, no box, no header shown
            if (key === 'answer') {
                return renderAnswerWithKicker(s.body);
            }
            // "Notes" section: subtle, collapsible-style
            if (key === 'notes') {
                return `<details class="report-notes"><summary>Notes</summary><div class="report-body">${renderReportBody(s.body)}</div></details>`;
            }
            // Any other legacy headers: show as small sub-section
            return `<div class="report-section report-minor"><h4>${escapeHtml(s.title)}</h4><div class="report-body">${renderReportBody(s.body)}</div></div>`;
        }).join('');
    }

    /* ── Agent flow suggest bubble with Yes / No ───────────────────── */
    function loadSessions() {
        authFetchJson(`${BACKEND_URL}/api/sessions`)
            .then(data => renderSessions(data.sessions || []))
            .catch(() => {
                sessionsContainer.innerHTML = '<div class="sessions-empty">Could not reach backend.</div>';
            });
    }

    const _pendingCompletedSessionHydration = new Set();

    function _reconcileCompletedSessionRuns(sessions) {
        (Array.isArray(sessions) ? sessions : []).forEach((sessionSummary) => {
            const sessionId = String(sessionSummary?.id || '').trim();
            if (!sessionId || !sessionSummary?.completed) return;

            const runId = _findRunIdBySessionId(sessionId, false);
            if (!runId || !runs.has(runId) || _pendingCompletedSessionHydration.has(sessionId)) return;

            const run = runs.get(runId);
            if (run && String(run._reportContent || '').trim()) return;

            _pendingCompletedSessionHydration.add(sessionId);
            _fetchSession(sessionId)
                .then((fullSession) => {
                    const targetRun = runs.get(runId);
                    if (!targetRun || !fullSession || !String(fullSession.final_report || '').trim()) return;

                    targetRun._reportContent = fullSession.final_report || '';
                    targetRun._reportGoal = fullSession.goal || '';
                    targetRun._outputFiles = fullSession.output_files || [];
                    targetRun.lane.classList.add('lane-done');
                    targetRun._llmActive = false;
                    targetRun._killRequested = false;
                    _updateRunTabState(runId, 'done');
                    _refreshWorkflowIndicator();

                    if (runId === activeRunId) {
                        _setActiveRun(runId);
                        _renderSessionChatHistory(fullSession);
                    }
                })
                .catch(() => {})
                .finally(() => {
                    _pendingCompletedSessionHydration.delete(sessionId);
                });
        });
    }

    // Tracks which history session is actively being viewed/replayed (used by email reader etc.)
    let _viewedHistorySessionId = '';
    function _setViewedHistorySession(sessionId) {
        _viewedHistorySessionId = String(sessionId || '');
    }

    // Activate a history session as the active chat (opt-in continuation)
    async function _activateContinueSession(id) {
        const targetSessionId = String(id || '').trim();
        if (!targetSessionId) return;

        currentSessionId = targetSessionId;
        _preferredConversationSessionId = targetSessionId;
        _freshChatRequested = false;
        _persistSession(targetSessionId);
        _setChatSessionPill(targetSessionId);
        _setViewedHistorySession('');

        let targetRunId = _findRunIdBySessionId(targetSessionId, false);
        if (targetRunId) {
            _setActiveRun(targetRunId);
        } else {
            targetRunId = _ensureChatLane(targetSessionId);
        }
        _currentHandlingRunId = targetRunId || '';

        const targetRun = targetRunId && runs.has(targetRunId) ? runs.get(targetRunId) : null;
        const shouldHydrateHistory = !targetRun || !Array.isArray(targetRun._chatBubbles) || targetRun._chatBubbles.length === 0;

        if (shouldHydrateHistory) {
            try {
                const session = await _fetchSession(targetSessionId);
                if (targetRun && Array.isArray(targetRun._chatBubbles)) {
                    targetRun._chatBubbles = [];
                }
                _renderSessionChatHistory(session);
            } catch (err) {
                alert(`Could not load session: ${err.message}`);
            }
        }

        if (goalInput) goalInput.focus();
    }



    function renderSessions(sessions) {
        sessionsContainer.innerHTML = '';

        // ── Scheduled Flows section ──────────────────────────────────
        const schedules = _loadSchedules();
        if (schedules.length > 0) {
            const schedSection = document.createElement('div');
            schedSection.className = 'sessions-schedule-section';
            const schedTitle = document.createElement('div');
            schedTitle.className = 'sessions-section-title';
            schedTitle.textContent = `Scheduled Flows (${schedules.length})`;
            schedSection.appendChild(schedTitle);

            schedules.forEach(sch => {
                const card = document.createElement('div');
                card.className = 'session-card schedule-card-inline' + (sch.enabled ? '' : ' schedule-disabled');

                const header = document.createElement('div');
                header.className = 'session-card-header';

                const goalEl = document.createElement('div');
                goalEl.className = 'session-card-goal';
                goalEl.textContent = sch.goal || '(no goal)';

                const statusBadge = document.createElement('span');
                statusBadge.className = `session-badge ${sch.enabled ? 'schedule-active' : 'schedule-paused'}`;
                statusBadge.textContent = sch.enabled ? '⏱ Active' : '⏸ Paused';

                header.appendChild(goalEl);
                header.appendChild(statusBadge);

                const meta = document.createElement('div');
                meta.className = 'session-card-meta';
                const parts = [`Every ${_formatInterval(sch.interval, sch.unit)}`];
                if (sch.runCount > 0) parts.push(`${sch.runCount} run${sch.runCount !== 1 ? 's' : ''}`);
                if (sch.lastRun) parts.push(`last: ${new Date(sch.lastRun).toLocaleString()}`);
                if (sch.enabled && sch.nextRun) {
                    const remaining = Math.max(0, sch.nextRun - Date.now());
                    if (remaining < 60000) parts.push('next: <1 min');
                    else if (remaining < 3600000) parts.push(`next: ${Math.ceil(remaining / 60000)} min`);
                    else parts.push(`next: ${new Date(sch.nextRun).toLocaleTimeString()}`);
                }
                meta.textContent = parts.join(' · ');

                const actions = document.createElement('div');
                actions.className = 'session-card-actions';

                const toggleBtn = document.createElement('button');
                toggleBtn.className = 'btn-xs' + (sch.enabled ? '' : ' primary');
                toggleBtn.textContent = sch.enabled ? 'Pause' : 'Resume';
                toggleBtn.addEventListener('click', () => {
                    toggleSchedule(sch.id, !sch.enabled);
                    loadSessions();
                });

                const runNowBtn = document.createElement('button');
                runNowBtn.className = 'btn-xs primary';
                runNowBtn.textContent = 'Run Now';
                runNowBtn.addEventListener('click', () => {
                    _appendChatBubble('system', `Manual trigger: "${sch.goal}"`);
                    currentSessionId = null;
                    _setChatSessionPill('');
                    sendStart(sch.goal, sch.model || appSettings.main_model || '', sch.context || '');
                });

                const delBtn = document.createElement('button');
                delBtn.className = 'btn-xs danger';
                delBtn.textContent = 'Delete';
                delBtn.addEventListener('click', () => {
                    if (confirm(`Delete schedule "${sch.goal}"?`)) {
                        removeSchedule(sch.id);
                        loadSessions();
                    }
                });

                actions.appendChild(toggleBtn);
                actions.appendChild(runNowBtn);
                actions.appendChild(delBtn);

                card.appendChild(header);
                card.appendChild(meta);
                card.appendChild(actions);
                schedSection.appendChild(card);
            });

            sessionsContainer.appendChild(schedSection);
        }

        // ── Session history ──────────────────────────────────────────
        if (sessions.length === 0 && schedules.length === 0) {
            sessionsContainer.innerHTML = '<div class="sessions-empty">No history found.<br>Run a goal to create one.</div>';
            return;
        }
        if (sessions.length > 0 && schedules.length > 0) {
            const histTitle = document.createElement('div');
            histTitle.className = 'sessions-section-title';
            histTitle.textContent = `History (${sessions.length})`;
            sessionsContainer.appendChild(histTitle);
        }
        sessions.forEach(s => {
            const card = document.createElement('div');
            card.className = 'session-card session-history-card';
            card.dataset.sessionId = s.id || '';

            const header = document.createElement('div');
            header.className = 'session-card-header';

            const goal = document.createElement('div');
            goal.className = 'session-card-goal';
            goal.textContent = s.goal || '(no goal)';

            const badge = document.createElement('span');
            badge.className = `session-badge ${s.completed ? 'done' : 'partial'}`;
            badge.textContent = s.completed ? '✓ Done' : 'Partial';

            header.appendChild(goal);
            header.appendChild(badge);

            const actions = document.createElement('div');
            actions.className = 'session-card-actions';

            const continueBtn = document.createElement('button');
            continueBtn.className = 'btn-xs';
            continueBtn.textContent = 'Continue';
            continueBtn.title = 'Continue this chat';
            continueBtn.addEventListener('click', () => _activateContinueSession(s.id));

            const schedBtn = document.createElement('button');
            schedBtn.className = 'btn-xs';
            schedBtn.textContent = 'Schedule';
            schedBtn.addEventListener('click', () => {
                _showSchedulePopover(schedBtn, s.goal || '', s.model || appSettings.main_model || '', '');
            });

            const noLearnBtn = document.createElement('button');
            noLearnBtn.className = 'btn-xs' + (s.no_learn ? ' active' : '');
            noLearnBtn.textContent = s.no_learn ? 'Private ✓' : 'Private';
            noLearnBtn.title = s.no_learn ? 'This session is excluded from memory' : 'Exclude this session from memory';
            noLearnBtn.addEventListener('click', async () => {
                const newVal = !s.no_learn;
                try {
                    const resp = await authFetch(`${BACKEND_URL}/api/sessions/${s.id}/no-learn`, {
                        method: 'PATCH',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify({ no_learn: newVal }),
                    });
                    if (resp.ok) {
                        s.no_learn = newVal;
                        noLearnBtn.className = 'btn-xs' + (newVal ? ' active' : '');
                        noLearnBtn.textContent = newVal ? 'Private ✓' : 'Private';
                        noLearnBtn.title = newVal ? 'This session is excluded from memory' : 'Exclude this session from memory';
                    }
                } catch (_e) { /* silent */ }
            });

            const delBtn = document.createElement('button');
            delBtn.className = 'btn-xs danger';
            delBtn.textContent = 'Delete';
            delBtn.addEventListener('click', () => deleteSession(s.id));

            actions.appendChild(continueBtn);
            actions.appendChild(schedBtn);
            actions.appendChild(noLearnBtn);
            actions.appendChild(delBtn);

            card.appendChild(header);
            card.appendChild(actions);
            sessionsContainer.appendChild(card);
        });

        _reconcileCompletedSessionRuns(sessions);
    }

    // Helper: append a log entry directly to a specific run's logPane
    function _laneLog(run, message, type, agentLabel) {
        const tagMap = { info: 'INFO', success: 'DONE', error: 'ERR', memory: 'MEM' };
        const entry = document.createElement('div');
        entry.className = `log-entry ${type}`;
        const ts = document.createElement('span');
        ts.className = 'log-ts';
        ts.textContent = new Date().toLocaleTimeString('nl-NL', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
        entry.appendChild(ts);
        if (agentLabel) {
            const ab = document.createElement('span');
            ab.className = 'log-agent';
            ab.textContent = agentLabel;
            entry.appendChild(ab);
        }
        const tag = document.createElement('span');
        tag.className = 'log-tag';
        tag.textContent = tagMap[type] || type;
        const body = document.createElement('span');
        body.className = 'log-body';
        body.textContent = message;
        entry.appendChild(tag);
        entry.appendChild(body);
        run.logPane.appendChild(entry);
        run.logPane.scrollTop = run.logPane.scrollHeight;
    }

    function _fetchSession(id) {
        return authFetch(`${BACKEND_URL}/api/sessions/${id}`)
            .then(r => {
                if (!r.ok) throw new Error(`HTTP ${r.status}`);
                return r.json();
            });
    }

    function replaySessionLane(id, prefetchedSession) {
        const histId = 'hist_' + id.slice(0, 8);
        // Remove existing replay lane for this session if already open
        const existingLane = document.getElementById(`lane-${histId}`);
        if (existingLane) { existingLane.remove(); runs.delete(histId); }

        function _buildLane(session) {
            _createRunLane(histId, `[Session] ${session.goal || ''}`);
            const run = runs.get(histId);
            if (!run) return;
            run._sessionId = id;
            _currentHandlingRunId = histId;
            _setActiveRun(histId);
            _setViewedHistorySession(id);

            // Mark all agents as done
            AGENT_ORDER.forEach(agent => {
                const card = run.agentsEl.querySelector(`[data-agent="${agent}"]`);
                if (!card) return;
                card.className = 'agent-card done';
                const lbl = card.querySelector('.agent-state-label');
                if (lbl) lbl.textContent = 'done';
            });

            const steps = session.steps || [];

            // Populate plan list
            if (steps.length > 0) {
                run.planEl.style.display = '';
                steps.forEach(s => {
                    const li = _createPlanStepItem(s.step);
                    _setPlanStepState(li, String(s.verdict || '').toUpperCase().startsWith('ISSUE') ? 'issue' : 'completed');
                    run.planList.appendChild(li);
                });
            }

            // Populate log
            const dateStr = session.created_at ? new Date(session.created_at).toLocaleString('nl-NL') : '';
            _laneLog(run, `📂 ${dateStr} · model: ${session.model || ''}`, 'info');
            steps.forEach((s, i) => {
                _laneLog(run, `Step ${i + 1}: ${s.step}`, 'info', 'Plan');
                if (s.result) _laneLog(run, s.result, s.verdict === 'FAILED' ? 'error' : 'success', 'Result');
            });

            // Progress bar
            run.progressWrap.classList.remove('hidden');
            run.progressLabel.textContent = `${steps.length} / ${steps.length} steps`;
            run.progressFill.style.width = '100%';

            // Store report
            if (session.final_report) {
                run._reportContent = session.final_report;
                run._reportGoal    = session.goal || '';
                run._outputFiles   = session.output_files || [];
                _laneLog(run, '── Final report available · click Report or the Report Out card ──', 'info');
            }

            // Mark lane as done (history replay, not active)
            run.lane.classList.add('lane-done');
            run._llmActive = false;
            _updateRunTabState(histId, 'done');
            _refreshWorkflowIndicator();

            // Keep viewport stable; do not auto-scroll lanes into view.
        }

        if (prefetchedSession) {
            _buildLane(prefetchedSession);
        } else {
            _fetchSession(id)
                .then(session => _buildLane(session))
                .catch(err => alert(`Could not load session: ${err.message}`));
        }
    }

    function deleteSession(id) {
        authFetch(`${BACKEND_URL}/api/sessions/${id}`, { method: 'DELETE' })
            .then(() => loadSessions());
    }



    // --- Helpers ---
    function setStatus(text, state, spinner = false) {
        statusChip.className = `status-chip ${state}`;
        if (spinner) {
            statusText.innerHTML = `<span class="spinner"></span>${text}`;
        } else {
            statusText.textContent = text;
        }
    }

    function updateProgressBar(current, total) {
        const run = runs.get(_currentHandlingRunId);
        if (!run) return;
        if (run.totalSteps === 0 && total > 0) run.totalSteps = total;
        if (run.progressWrap && run.progressFill && run.progressLabel) {
            if (total > 0) {
                run.progressWrap.classList.remove('hidden');
                const pct = Math.round((current / total) * 100);
                run.progressFill.style.width = pct + '%';
                run.progressLabel.textContent = `Step ${current} / ${total}`;
            } else {
                run.progressWrap.classList.add('hidden');
            }
        }
    }

    function _logTimestamp() {
        const ts = document.createElement('span');
        ts.className = 'log-ts';
        const now = new Date();
        ts.textContent = now.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
        return ts;
    }

    function appendMemoryRecallLog(rawText, count) {
        const entry = document.createElement('div');
        entry.className = 'log-entry memory';

        const tag = document.createElement('span');
        tag.className = 'log-tag';
        tag.textContent = 'MEM';

        const details = document.createElement('details');
        details.className = 'log-details';
        const summary = document.createElement('summary');
        summary.textContent = `Memory recall: ${count} past session${count !== 1 ? 's' : ''}`;
        const full = document.createElement('span');
        full.className = 'log-body';
        full.textContent = rawText || 'No recalled memory context available.';
        details.appendChild(summary);
        details.appendChild(full);

        entry.appendChild(_logTimestamp());
        entry.appendChild(tag);
        entry.appendChild(details);

        const container = _getRunContainer(_currentHandlingRunId);
        container.appendChild(entry);
        container.scrollTop = container.scrollHeight;
    }

    function appendLog(message, type, agent = null, skipMirror = false) {
        const tagMap = { info: 'INFO', success: 'DONE', error: 'ERR', memory: 'MEM', curated: 'CTX', research: 'RES', handoff: '\u2192', tool: 'TOOL', hitl: 'HITL' };
        const entry = document.createElement('div');
        entry.className = `log-entry ${type}`;

        const tag = document.createElement('span');
        tag.className = 'log-tag';
        tag.textContent = tagMap[type] || type;

        entry.appendChild(_logTimestamp());

        if (agent) {
            const agentBadge = document.createElement('span');
            agentBadge.className = 'log-agent';
            agentBadge.textContent = agent;
            entry.appendChild(agentBadge);
        }

        if (type === 'curated' && message.length > 80) {
            // Collapsible for long context briefings
            const details = document.createElement('details');
            details.className = 'log-details';
            const summary = document.createElement('summary');
            summary.textContent = message.substring(0, 72) + '…';
            const full = document.createElement('span');
            full.className = 'log-body';
            full.textContent = message;
            details.appendChild(summary);
            details.appendChild(full);
            entry.appendChild(tag);
            entry.appendChild(details);
        } else {
            const body = document.createElement('span');
            body.className = 'log-body';
            body.textContent = message;
            entry.appendChild(tag);
            entry.appendChild(body);
        }

        const container = _getRunContainer(_currentHandlingRunId);
        container.appendChild(entry);
        container.scrollTop = container.scrollHeight;
        if (!skipMirror) _mirrorLogToDialog(message, type, agent);
    }

    function updateMemory(memory) {
        const items = Array.isArray(memory) ? memory : [];
        memoryContainer.innerHTML = '';

        let totalCount = items.length;
        if (recalledMemoryRaw) {
            totalCount += 1;
            const recalled = document.createElement('details');
            recalled.className = 'memory-item memory-history-item';

            const summary = document.createElement('summary');
            summary.className = 'memory-history-summary';
            summary.innerHTML = '<span class="memory-step-badge">Recall</span><span class="memory-item-step">Loaded memory from relevant past sessions</span>';

            const body = document.createElement('div');
            body.className = 'memory-item-body';
            body.textContent = recalledMemoryRaw;

            recalled.appendChild(summary);
            recalled.appendChild(body);
            memoryContainer.appendChild(recalled);
        }

        if (items.length === 0) {
            if (memoryCount) memoryCount.textContent = totalCount > 0 ? String(totalCount) : '';
            if (!recalledMemoryRaw) {
                memoryContainer.innerHTML = '<div class="sessions-empty">No execution history in this run yet.</div>';
            }
            return;
        }

        items.forEach((item, index) => {
            const step = String(item.step || '').trim() || `Step ${index + 1}`;
            const result = String(item.result || '').trim();
            const preview = result.length > 160 ? `${result.slice(0, 160)}…` : (result || '(no result)');

            const card = document.createElement('details');
            card.className = 'memory-item memory-history-item';

            const summary = document.createElement('summary');
            summary.className = 'memory-history-summary';

            const badge = document.createElement('span');
            badge.className = 'memory-step-badge';
            badge.textContent = `Step ${index + 1}`;

            const title = document.createElement('span');
            title.className = 'memory-item-step';
            title.textContent = step;

            const snippet = document.createElement('span');
            snippet.className = 'memory-history-preview';
            snippet.textContent = preview;

            summary.appendChild(badge);
            summary.appendChild(title);
            summary.appendChild(snippet);

            const body = document.createElement('div');
            body.className = 'memory-item-body';
            body.textContent = result || '(no result)';

            card.appendChild(summary);
            card.appendChild(body);
            memoryContainer.appendChild(card);
        });

        if (memoryCount) memoryCount.textContent = String(totalCount);
        memoryContainer.scrollTop = memoryContainer.scrollHeight;
    }

    // --- Clarifying question ---
    function showClarifyPrompt(question) {
        const entry = document.createElement('div');
        entry.className = 'log-entry clarify';
        entry.id = 'clarify-entry';
        const tag = document.createElement('span');
        tag.className = 'log-tag';
        tag.textContent = 'QUESTION';
        const body = document.createElement('div');
        body.className = 'log-body clarify-body';
                body.innerHTML = `<div class="clarify-question">${escapeHtml(question)}</div>
<div class="clarify-form" id="clarify-form">
    <textarea id="clarify-input" class="form-control" rows="2" placeholder="Type your answer here…"></textarea>
    <button class="btn btn-primary" id="clarify-send-btn" style="margin-top:6px;padding:6px 14px;font-size:0.85rem;">Send</button>
</div>`;
        entry.appendChild(_logTimestamp());
        entry.appendChild(tag);
        entry.appendChild(body);
        logContainer.appendChild(entry);
        logContainer.scrollTop = logContainer.scrollHeight;

        const sendBtn = document.getElementById('clarify-send-btn');
        const input   = document.getElementById('clarify-input');
        input.focus();

        function submitClarification() {
            const answer = input.value.trim();
            if (!answer) return;
            // Show user reply in log
            const replyEntry = document.createElement('div');
            replyEntry.className = 'log-entry clarify-reply';
            const rtag = document.createElement('span');
            rtag.className = 'log-tag';
            rtag.textContent = 'YOU';
            const rbody = document.createElement('span');
            rbody.className = 'log-body';
            rbody.textContent = answer;
            replyEntry.appendChild(_logTimestamp());
            replyEntry.appendChild(rtag);
            replyEntry.appendChild(rbody);
            logContainer.appendChild(replyEntry);
            logContainer.scrollTop = logContainer.scrollHeight;
            // Remove the form
            document.getElementById('clarify-form').remove();
            // Send to backend
            ws.send(JSON.stringify({ action: 'clarification_response', answer }));
        }

        sendBtn.addEventListener('click', submitClarification);
        input.addEventListener('keydown', e => {
            if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); submitClarification(); }
        });
    }

                return {
                    formatCompactNumber: _formatCompactNumber,
                    normalizePolicyBudgetMode: _normalizePolicyBudgetMode,
                    updatePolicyBudgetModeUi: _updatePolicyBudgetModeUi,
                    renderBudgetCounter: _renderBudgetCounter,
                    createPlanStepItem: _createPlanStepItem,
                    setPlanStepState: _setPlanStepState,
                    setPlanStepDetail: _setPlanStepDetail,
                    getPlanStepAt: _getPlanStepAt,
                    applyTodoProgress: _applyTodoProgress,
                    createRunLane: _createRunLane,
                    showToast,
                    renderReportBody,
                    formatFinalReport,
                    loadSessions,
                    getViewedHistorySessionId: () => _viewedHistorySessionId,
                    setViewedHistorySession: _setViewedHistorySession,
                    activateContinueSession: _activateContinueSession,
                    renderSessions,
                    laneLog: _laneLog,
                    fetchSession: _fetchSession,
                    replaySessionLane,
                    deleteSession,
                    setAgentState,
                    flashConnector,
                    setStatus,
                    updateProgressBar,
                    logTimestamp: _logTimestamp,
                    appendMemoryRecallLog,
                    appendLog,
                    updateMemory,
                    showClarifyPrompt,
                };
            }
        })();
    };
})();
