/ src / lib / autonomy / supervisor-settings.ts
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  }