supervisor-settings.ts
1 import type { AppSettings } from '@/types' 2 3 export type AutonomyRuntimeScope = 'chat' | 'task' | 'both' 4 5 export const DEFAULT_SUPERVISOR_ENABLED = true 6 export const DEFAULT_SUPERVISOR_RUNTIME_SCOPE: AutonomyRuntimeScope = 'both' 7 export const DEFAULT_SUPERVISOR_NO_PROGRESS_LIMIT = 2 8 export const DEFAULT_SUPERVISOR_REPEATED_TOOL_LIMIT = 3 9 export const DEFAULT_REFLECTION_ENABLED = true 10 export const DEFAULT_REFLECTION_AUTO_WRITE_MEMORY = true 11 12 export const SUPERVISOR_NO_PROGRESS_LIMIT_MIN = 1 13 export const SUPERVISOR_NO_PROGRESS_LIMIT_MAX = 8 14 export const SUPERVISOR_REPEATED_TOOL_LIMIT_MIN = 2 15 export const SUPERVISOR_REPEATED_TOOL_LIMIT_MAX = 8 16 17 function parseIntSetting(value: unknown, fallback: number, min: number, max: number): number { 18 const parsed = typeof value === 'number' 19 ? value 20 : typeof value === 'string' 21 ? Number.parseInt(value, 10) 22 : Number.NaN 23 if (!Number.isFinite(parsed)) return fallback 24 return Math.max(min, Math.min(max, Math.trunc(parsed))) 25 } 26 27 function parseBoolSetting(value: unknown, fallback: boolean): boolean { 28 if (typeof value === 'boolean') return value 29 if (typeof value === 'string') { 30 const normalized = value.trim().toLowerCase() 31 if (['1', 'true', 'yes', 'on'].includes(normalized)) return true 32 if (['0', 'false', 'no', 'off'].includes(normalized)) return false 33 } 34 return fallback 35 } 36 37 export interface NormalizedSupervisorSettings { 38 supervisorEnabled: boolean 39 supervisorRuntimeScope: AutonomyRuntimeScope 40 supervisorNoProgressLimit: number 41 supervisorRepeatedToolLimit: number 42 reflectionEnabled: boolean 43 reflectionAutoWriteMemory: boolean 44 } 45 46 export function normalizeSupervisorSettings( 47 settings: Partial<AppSettings> | NormalizedSupervisorSettings | Record<string, unknown> | null | undefined, 48 ): NormalizedSupervisorSettings { 49 const current = settings || {} 50 const runtimeScope = current.supervisorRuntimeScope === 'chat' 51 || current.supervisorRuntimeScope === 'task' 52 || current.supervisorRuntimeScope === 'both' 53 ? current.supervisorRuntimeScope 54 : DEFAULT_SUPERVISOR_RUNTIME_SCOPE 55 return { 56 supervisorEnabled: parseBoolSetting(current.supervisorEnabled, DEFAULT_SUPERVISOR_ENABLED), 57 supervisorRuntimeScope: runtimeScope, 58 supervisorNoProgressLimit: parseIntSetting( 59 current.supervisorNoProgressLimit, 60 DEFAULT_SUPERVISOR_NO_PROGRESS_LIMIT, 61 SUPERVISOR_NO_PROGRESS_LIMIT_MIN, 62 SUPERVISOR_NO_PROGRESS_LIMIT_MAX, 63 ), 64 supervisorRepeatedToolLimit: parseIntSetting( 65 current.supervisorRepeatedToolLimit, 66 DEFAULT_SUPERVISOR_REPEATED_TOOL_LIMIT, 67 SUPERVISOR_REPEATED_TOOL_LIMIT_MIN, 68 SUPERVISOR_REPEATED_TOOL_LIMIT_MAX, 69 ), 70 reflectionEnabled: parseBoolSetting(current.reflectionEnabled, DEFAULT_REFLECTION_ENABLED), 71 reflectionAutoWriteMemory: parseBoolSetting(current.reflectionAutoWriteMemory, DEFAULT_REFLECTION_AUTO_WRITE_MEMORY), 72 } 73 } 74 75 export function runtimeScopeIncludes( 76 runtimeScope: AutonomyRuntimeScope, 77 surface: 'chat' | 'task', 78 ): boolean { 79 return runtimeScope === 'both' || runtimeScope === surface 80 }