function toBool(value, defaultValue) { if (value === undefined || value === null || value === '') return defaultValue; return String(value).toLowerCase() !== 'false'; } function toNumber(value, defaultValue) { const n = Number(value); return Number.isFinite(n) ? n : defaultValue; } function parseConfig(env) { const e = env || {}; return { aiEnabled: toBool(e.AI_ENABLED, true), aiProvider: e.AI_PROVIDER || 'openai', aiModel: e.AI_MODEL || 'gpt-4o-mini', candidatePoolSize: toNumber(e.CANDIDATE_POOL_SIZE, 60), // Igual a candidatePoolSize por padrao -- nenhum candidato elegivel deve ficar sem // analise semantica por um cap artificial (missao "finalizacao para producao", secao // 13). AI_FALLBACK fica reservado para falha real de provider/timeout, nao para // economia de chamadas dentro de um pool que o cache ja deixou barato de repetir. aiMaxCandidates: toNumber(e.AI_MAX_CANDIDATES, 60), topN: Math.min(toNumber(e.TOP_N, 10), 10), cooldownDays: toNumber(e.COOLDOWN_DAYS, 1), aiConfidenceThreshold: toNumber(e.AI_CONFIDENCE_THRESHOLD, 0.6), aiMaxContextChars: toNumber(e.AI_MAX_CONTEXT_CHARS, 4000), devMode: toBool(e.DEV_MODE, true), shadowMode: toBool(e.SHADOW_MODE, true), diasSemInteracaoAlerta: toNumber(e.DIAS_SEM_INTERACAO_ALERTA, 2), horasSlaRisco: toNumber(e.HORAS_SLA_RISCO, 8), // Gate explicito de envio real -- nunca depende de editar codigo pra habilitar/desabilitar // (missao "finalizacao para producao", secao 22). Default false: so quem liga // explicitamente no Config node manda mensagem de verdade. environment: e.ENVIRONMENT || 'dev', sendEnabled: toBool(e.SEND_ENABLED, false), // So importa pra execucoes 'manual' -- protege contra alguem clicar "Execute Workflow" em // producao (SEND_ENABLED=true) e mandar o digest de novo sem querer. Runs agendadas nunca // olham esse campo. Default false: manual nunca envia de verdade sem override explicito // (missao "correcao final", secao 16/17). forceSend: toBool(e.FORCE_SEND, false), }; } module.exports = { parseConfig, toBool, toNumber };