diff --git a/frontend-modern/src/components/Settings/AISettings.tsx b/frontend-modern/src/components/Settings/AISettings.tsx
index d7027c7..0447d60 100644
--- a/frontend-modern/src/components/Settings/AISettings.tsx
+++ b/frontend-modern/src/components/Settings/AISettings.tsx
@@ -69,6 +69,9 @@ export const AISettings: Component = () => {
const [testingProvider, setTestingProvider] = createSignal(null);
const [providerTestResult, setProviderTestResult] = createSignal<{ provider: string; success: boolean; message: string } | null>(null);
+ // Auto-fix acknowledgement state (not persisted - must acknowledge each session)
+ const [autoFixAcknowledged, setAutoFixAcknowledged] = createSignal(false);
+
const [form, setForm] = createStore({
enabled: false,
provider: 'anthropic' as AIProvider, // Legacy - kept for compatibility
@@ -80,7 +83,7 @@ export const AISettings: Component = () => {
clearApiKey: false,
autonomousMode: false,
authMethod: 'api_key' as AuthMethod,
- patrolSchedulePreset: '6hr',
+ patrolIntervalMinutes: 360, // 6 hours default
alertTriggeredAnalysis: true,
patrolAutoFix: false,
// Multi-provider credentials
@@ -104,7 +107,7 @@ export const AISettings: Component = () => {
clearApiKey: false,
autonomousMode: false,
authMethod: 'api_key',
- patrolSchedulePreset: '6hr',
+ patrolIntervalMinutes: 360, // 6 hours default
alertTriggeredAnalysis: true,
patrolAutoFix: false,
// Multi-provider - empty by default
@@ -128,7 +131,7 @@ export const AISettings: Component = () => {
clearApiKey: false,
autonomousMode: data.autonomous_mode || false,
authMethod: data.auth_method || 'api_key',
- patrolSchedulePreset: data.patrol_schedule_preset || '6hr',
+ patrolIntervalMinutes: data.patrol_interval_minutes ?? 360, // Use minutes, default to 6hr
alertTriggeredAnalysis: data.alert_triggered_analysis !== false, // default to true
patrolAutoFix: data.patrol_auto_fix || false, // default to false (observe only)
// Multi-provider - never load actual keys from server (security), just track if configured
@@ -249,8 +252,8 @@ export const AISettings: Component = () => {
}
// Include patrol settings if changed
- if (form.patrolSchedulePreset !== settings()?.patrol_schedule_preset) {
- payload.patrol_schedule_preset = form.patrolSchedulePreset;
+ if (form.patrolIntervalMinutes !== (settings()?.patrol_interval_minutes ?? 360)) {
+ payload.patrol_interval_minutes = form.patrolIntervalMinutes;
}
if (form.alertTriggeredAnalysis !== settings()?.alert_triggered_analysis) {
@@ -922,27 +925,39 @@ export const AISettings: Component = () => {
- {/* Patrol Schedule Preset */}
+ {/* Patrol Interval */}
-
+
+ {
+ const value = parseInt(e.currentTarget.value, 10);
+ if (!isNaN(value)) {
+ setForm('patrolIntervalMinutes', Math.max(0, value));
+ }
+ }}
+ min={0}
+ max={10080}
+ step={15}
+ disabled={saving()}
+ style={{ width: '120px' }}
+ />
+
+ {form.patrolIntervalMinutes === 0
+ ? 'Disabled'
+ : form.patrolIntervalMinutes >= 60
+ ? `${Math.floor(form.patrolIntervalMinutes / 60)}h ${form.patrolIntervalMinutes % 60 > 0 ? `${form.patrolIntervalMinutes % 60}m` : ''}`
+ : `${form.patrolIntervalMinutes}m`}
+
+
- How often to scan all infrastructure for potential issues
+ Set to 0 to disable scheduled patrol. Minimum 10 minutes when enabled.
@@ -983,10 +998,59 @@ export const AISettings: Component = () => {
setForm('patrolAutoFix', event.currentTarget.checked)}
- disabled={saving()}
+ onChange={(event) => {
+ // Can only enable if acknowledged
+ if (event.currentTarget.checked && !autoFixAcknowledged()) {
+ return; // Prevent enabling without acknowledgement
+ }
+ setForm('patrolAutoFix', event.currentTarget.checked);
+ }}
+ disabled={saving() || (!form.patrolAutoFix && !autoFixAcknowledged())}
/>
+
+ {/* Auto-Fix Warning & Acknowledgement */}
+
+
+
+
+
+
Before enabling Auto-Fix Mode:
+
+ - AI will execute commands without asking for approval
+ - Actions may be irreversible (e.g., restarting services, clearing caches)
+ - Recommended to test in non-production environments first
+
+
+
+
+
+
+
+ {/* Warning when enabled */}
+
+
+
+
+
+
⚠️ Auto-Fix is ENABLED
+
AI patrol will automatically attempt to fix issues without asking for approval. Review findings regularly.
+
+
+
+
diff --git a/frontend-modern/src/types/ai.ts b/frontend-modern/src/types/ai.ts
index 60adffe..68bb181 100644
--- a/frontend-modern/src/types/ai.ts
+++ b/frontend-modern/src/types/ai.ts
@@ -25,7 +25,8 @@ export interface AISettings {
auth_method: AuthMethod; // "api_key" or "oauth"
oauth_connected: boolean; // true if OAuth tokens are configured
// Patrol settings for token efficiency
- patrol_schedule_preset?: string; // "15min" | "1hr" | "6hr" | "12hr" | "daily" | "disabled"
+ patrol_schedule_preset?: string; // DEPRECATED: use patrol_interval_minutes
+ patrol_interval_minutes?: number; // Patrol interval in minutes (0 = disabled, minimum 10)
alert_triggered_analysis?: boolean; // true if AI should analyze when alerts fire
patrol_auto_fix?: boolean; // true if patrol can attempt automatic remediation
available_models?: ModelInfo[]; // DEPRECATED: use /api/ai/models endpoint
@@ -52,7 +53,8 @@ export interface AISettingsUpdateRequest {
chat_model?: string; // Model for interactive chat
patrol_model?: string; // Model for background patrol
// Patrol settings for token efficiency
- patrol_schedule_preset?: string; // "15min" | "1hr" | "6hr" | "12hr" | "daily" | "disabled"
+ patrol_schedule_preset?: string; // DEPRECATED: use patrol_interval_minutes
+ patrol_interval_minutes?: number; // Custom interval in minutes (0 = disabled, minimum 10)
alert_triggered_analysis?: boolean; // true if AI should analyze when alerts fire
patrol_auto_fix?: boolean; // true if patrol can attempt automatic remediation
// Multi-provider credentials
diff --git a/internal/api/ai_handlers.go b/internal/api/ai_handlers.go
index a5e254d..4b0ccee 100644
--- a/internal/api/ai_handlers.go
+++ b/internal/api/ai_handlers.go
@@ -118,7 +118,8 @@ type AISettingsResponse struct {
AuthMethod string `json:"auth_method"` // "api_key" or "oauth"
OAuthConnected bool `json:"oauth_connected"` // true if OAuth tokens are configured
// Patrol settings for token efficiency
- PatrolSchedulePreset string `json:"patrol_schedule_preset"` // "15min", "1hr", "6hr", "12hr", "daily", "disabled"
+ PatrolSchedulePreset string `json:"patrol_schedule_preset"` // DEPRECATED: legacy preset
+ PatrolIntervalMinutes int `json:"patrol_interval_minutes"` // Patrol interval in minutes (0 = disabled)
AlertTriggeredAnalysis bool `json:"alert_triggered_analysis"` // true if AI analyzes when alerts fire
AvailableModels []config.ModelInfo `json:"available_models"` // List of models for current provider
// Multi-provider credentials - shows which providers are configured
@@ -144,7 +145,8 @@ type AISettingsUpdateRequest struct {
CustomContext *string `json:"custom_context,omitempty"` // user-provided infrastructure context
AuthMethod *string `json:"auth_method,omitempty"` // "api_key" or "oauth"
// Patrol settings for token efficiency
- PatrolSchedulePreset *string `json:"patrol_schedule_preset,omitempty"` // "15min", "1hr", "6hr", "12hr", "daily", "disabled"
+ PatrolSchedulePreset *string `json:"patrol_schedule_preset,omitempty"` // DEPRECATED: use patrol_interval_minutes
+ PatrolIntervalMinutes *int `json:"patrol_interval_minutes,omitempty"` // Custom interval in minutes (0 = disabled, minimum 10)
AlertTriggeredAnalysis *bool `json:"alert_triggered_analysis,omitempty"` // true if AI analyzes when alerts fire
// Multi-provider credentials
AnthropicAPIKey *string `json:"anthropic_api_key,omitempty"` // Set Anthropic API key
@@ -198,6 +200,7 @@ func (h *AISettingsHandler) HandleGetAISettings(w http.ResponseWriter, r *http.R
OAuthConnected: settings.OAuthAccessToken != "",
// Patrol settings
PatrolSchedulePreset: settings.PatrolSchedulePreset,
+ PatrolIntervalMinutes: settings.PatrolIntervalMinutes,
AlertTriggeredAnalysis: settings.AlertTriggeredAnalysis,
AvailableModels: nil, // Now populated via /api/ai/models endpoint
// Multi-provider configuration
@@ -321,8 +324,25 @@ func (h *AISettingsHandler) HandleUpdateAISettings(w http.ResponseWriter, r *htt
settings.Enabled = *req.Enabled
}
- // Handle patrol schedule preset
- if req.PatrolSchedulePreset != nil {
+ // Handle patrol interval - prefer custom minutes over preset
+ if req.PatrolIntervalMinutes != nil {
+ minutes := *req.PatrolIntervalMinutes
+ if minutes < 0 {
+ http.Error(w, "patrol_interval_minutes cannot be negative", http.StatusBadRequest)
+ return
+ }
+ if minutes > 0 && minutes < 10 {
+ http.Error(w, "patrol_interval_minutes must be at least 10 minutes (or 0 to disable)", http.StatusBadRequest)
+ return
+ }
+ if minutes > 10080 { // 7 days max
+ http.Error(w, "patrol_interval_minutes cannot exceed 10080 (7 days)", http.StatusBadRequest)
+ return
+ }
+ settings.PatrolIntervalMinutes = minutes
+ settings.PatrolSchedulePreset = "" // Clear preset when using custom minutes
+ } else if req.PatrolSchedulePreset != nil {
+ // Legacy preset support
preset := strings.ToLower(strings.TrimSpace(*req.PatrolSchedulePreset))
switch preset {
case "15min", "1hr", "6hr", "12hr", "daily", "disabled":
@@ -414,6 +434,7 @@ func (h *AISettingsHandler) HandleUpdateAISettings(w http.ResponseWriter, r *htt
AuthMethod: authMethod,
OAuthConnected: settings.OAuthAccessToken != "",
PatrolSchedulePreset: settings.PatrolSchedulePreset,
+ PatrolIntervalMinutes: settings.PatrolIntervalMinutes,
AlertTriggeredAnalysis: settings.AlertTriggeredAnalysis,
AvailableModels: nil, // Now populated via /api/ai/models endpoint
// Multi-provider configuration