// Runtime settings for the autonomy stack. The env vars stay the default, the // table is the override, so nothing changes behaviour until somebody deliberately // writes a row. Kept deliberately tiny -- this is a control plane, not a config // system, and every key here can move real money or stop the pipeline. const EXECUTION_MODES = ['shadow', 'paper']; const KEYS = { executionMode: 'execution_mode', killSwitch: 'execution_kill_switch', }; function readSetting(db, key) { try { const row = db.prepare('SELECT value FROM autonomy_settings WHERE key = ?').get(key); return row ? row.value : null; } catch (error) { // A missing table means an older schema, which should behave like "no override" // rather than taking the worker down with it. console.error(`[settings] could not read ${key}:`, error.message); return null; } } function writeSetting(db, key, value, updatedBy = 'admin') { db.prepare(` INSERT INTO autonomy_settings(key, value, updated_by) VALUES (?, ?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_by = excluded.updated_by, updated_at = datetime('now') `).run(key, String(value), updatedBy); } // Truthy strings people actually type, rather than only accepting 'true' function isOn(value) { return ['1', 'true', 'on', 'yes', 'engaged'].includes(String(value || '').trim().toLowerCase()); } function getExecutionControls(db, env = process.env) { const stored = readSetting(db, KEYS.executionMode); const fallback = env.AUTONOMY_EXECUTION_MODE || 'shadow'; const mode = EXECUTION_MODES.includes(String(stored)) ? String(stored) : fallback; return { mode: EXECUTION_MODES.includes(mode) ? mode : 'shadow', modeSource: EXECUTION_MODES.includes(String(stored)) ? 'settings' : 'env', killSwitch: isOn(readSetting(db, KEYS.killSwitch)), }; } function setExecutionMode(db, mode, updatedBy) { if (!EXECUTION_MODES.includes(mode)) { throw new Error(`unsupported execution mode: ${mode} (expected ${EXECUTION_MODES.join(' or ')})`); } writeSetting(db, KEYS.executionMode, mode, updatedBy); return getExecutionControls(db); } function setKillSwitch(db, engaged, updatedBy) { writeSetting(db, KEYS.killSwitch, engaged ? 'true' : 'false', updatedBy); return getExecutionControls(db); } module.exports = { EXECUTION_MODES, KEYS, isOn, readSetting, writeSetting, getExecutionControls, setExecutionMode, setKillSwitch, };