/** * Pure functions for consolidation UI — shared between hub.js (browser) and test runner (Node). * These operate on plain data (no DOM dependency) for testability. */ /** * Given a settings response, populate a form-field map with daemon config values. * @param {object} settings - GET /api/v1/settings response * @param {object} form - { field_id: { value?, checked? } } map * @returns {string} mode - 'daemon' | 'hosted' | 'off' */ export function populateConsolSettingsForm(settings, form) { if (!settings || !settings.daemon) return 'off'; const d = settings.daemon; let mode = 'off'; if (d.enabled) mode = 'daemon'; else if (settings.hosted_delegating || (settings.vault_path_display || '').toLowerCase() === 'canister') mode = 'hosted'; if (form['consol-interval']) form['consol-interval'].value = d.interval_minutes ?? 120; if (form['consol-idle-only']) form['consol-idle-only'].checked = d.idle_only !== false; if (form['consol-idle-threshold']) form['consol-idle-threshold'].value = d.idle_threshold_minutes ?? 15; if (form['consol-run-on-start']) form['consol-run-on-start'].checked = Boolean(d.run_on_start); if (form['pass-consolidate']) form['pass-consolidate'].checked = d.passes?.consolidate !== false; if (form['pass-verify']) form['pass-verify'].checked = d.passes?.verify !== false; if (form['pass-discover']) form['pass-discover'].checked = Boolean(d.passes?.discover); if (form['consol-llm-provider']) form['consol-llm-provider'].value = d.llm?.provider || ''; if (form['consol-llm-model']) form['consol-llm-model'].value = d.llm?.model || ''; if (form['consol-llm-base-url']) form['consol-llm-base-url'].value = d.llm?.base_url || ''; if (form['consol-lookback-hours']) form['consol-lookback-hours'].value = d.lookback_hours ?? 24; if (form['consol-max-events']) form['consol-max-events'].value = d.max_events_per_pass ?? 200; if (form['consol-max-topics']) form['consol-max-topics'].value = d.max_topics_per_pass ?? 10; if (form['consol-llm-max-tokens']) form['consol-llm-max-tokens'].value = d.llm?.max_tokens ?? 1024; if (form['consol-cost-cap']) form['consol-cost-cap'].value = d.max_cost_per_day_usd != null ? d.max_cost_per_day_usd : ''; if (form['consol-hosted-interval'] != null && d.interval_minutes != null) { const v = String(d.interval_minutes); const allowed = ['30', '60', '120', '360', '720', '1440', '10080']; form['consol-hosted-interval'].value = allowed.includes(v) ? v : '120'; } return mode; } /** * Build a consolidation settings payload from a form-field map. * @param {object} form - { field_id: { value?, checked? } } * @param {string} mode - 'daemon' | 'hosted' | 'off' * @returns {object} payload matching POST /api/v1/settings/consolidation schema */ export function buildConsolSettingsPayload(form, mode) { const intervalRaw = mode === 'hosted' && form['consol-hosted-interval'] != null ? form['consol-hosted-interval'].value : form['consol-interval']?.value; const llm = { provider: form['consol-llm-provider']?.value || '', model: form['consol-llm-model']?.value || '', base_url: form['consol-llm-base-url']?.value || '', }; const maxTokClamped = Math.max( 64, Math.min(8192, Math.floor(Number(form['consol-llm-max-tokens']?.value) || 1024)), ); if (mode === 'daemon') { llm.max_tokens = maxTokClamped; } const payload = { mode, enabled: mode === 'daemon', interval_minutes: Math.max(1, Math.floor(Number(intervalRaw) || 120)), idle_only: Boolean(form['consol-idle-only']?.checked), idle_threshold_minutes: Math.max(1, Math.floor(Number(form['consol-idle-threshold']?.value) || 15)), run_on_start: Boolean(form['consol-run-on-start']?.checked), passes: { consolidate: Boolean(form['pass-consolidate']?.checked), verify: Boolean(form['pass-verify']?.checked), discover: Boolean(form['pass-discover']?.checked), }, llm, max_cost_per_day_usd: form['consol-cost-cap']?.value === '' ? null : Number(form['consol-cost-cap']?.value) || 0, }; if (mode === 'daemon' || mode === 'hosted') { payload.lookback_hours = Math.max( 1, Math.min(8760, Math.floor(Number(form['consol-lookback-hours']?.value) || 24)), ); payload.max_events_per_pass = Math.max( 1, Math.min(10000, Math.floor(Number(form['consol-max-events']?.value) || 200)), ); payload.max_topics_per_pass = Math.max( 1, Math.min(500, Math.floor(Number(form['consol-max-topics']?.value) || 10)), ); } if (mode === 'hosted') { payload.llm = { ...llm, max_tokens: maxTokClamped }; } return payload; } /** * Render consolidation history events into a container. * @param {Array} events - consolidation-type memory events * @param {{ innerHTML: string }} container - DOM-like object with innerHTML * @returns {number} row count rendered */ export function renderConsolidationHistory(events, container) { if (!container) return 0; if (!events || events.length === 0) { container.innerHTML = '
No consolidation history found.
'; return 0; } let html = '| Date | Topics | Events Merged | Cost | Status |
|---|---|---|---|---|
| ${esc(date)} | ${esc(String(topics))} | ${esc(String(merged))} | ${esc(cost)} | ${esc(status)} |