package api import ( "encoding/json" "log" fleetai "crypto-miner-server/internal/ai" "crypto-miner-server/internal/mining" "crypto-miner-server/internal/strategy" ) func (h *WSHub) contingencyOrchestrator() *mining.ContingencyOrchestrator { if h == nil { return nil } return h.contingencyOrch } func (h *WSHub) initContingencyOrchestrator() { if h == nil || h.contingencyOrch != nil { return } policy := h.serverPolicySnapshot() h.contingencyOrch = mining.NewContingencyOrchestrator(mining.OrchestratorDeps{ AIControl: policy.AIControlEnabled, Persona: policy.AIPersona, GraftFor: h.graftPolicyLookup, OnSeer: h.emitContingencySeerEvent, OnPush: h.pushContingencyBranchParams, }) } func (h *WSHub) graftPolicyLookup(agentID string) (*strategy.GraftPolicy, bool) { if graft, ok := h.GraftPolicyForAgent(agentID); ok { return graft, true } return nil, false } func (h *WSHub) emitContingencySeerEvent(agentID string, payload map[string]interface{}) { if h == nil { return } emitter := &HubSeerEmitter{Hub: h, DB: h.db} _ = emitter.EmitSeerEvent("onion_miner_log", agentID, payload) } func (h *WSHub) pushContingencyBranchParams(agentID string, params mining.BranchParamsPush) error { body, err := json.Marshal(params) if err != nil { return err } return h.SendToAgent(agentID, Message{Type: "contingency_branch_params", Payload: body}) } func (h *WSHub) handleOnionMinerLog(agentID string, raw json.RawMessage) { h.initContingencyOrchestrator() orch := h.contingencyOrchestrator() if orch == nil || !orch.Enabled() { return } hop, err := mining.ParseOnionHop(raw) if err != nil { return } emitter := &HubSeerEmitter{Hub: h, DB: h.db} var payload map[string]interface{} _ = json.Unmarshal(raw, &payload) if payload == nil { payload = map[string]interface{}{} } payload["agent_id"] = agentID _ = emitter.EmitSeerEvent("onion_miner_log", agentID, payload) push, ok := orch.ObserveHop(agentID, hop) if !ok { h.cacheContingencyDepth(agentID, hop.Depth) return } if err := h.pushContingencyBranchParams(agentID, push); err != nil { log.Printf("[contingency] push branch params to %s: %v", agentID[:min(8, len(agentID))], err) } h.cacheContingencyDepth(agentID, hop.Depth) } func (h *WSHub) cacheContingencyDepth(agentID string, depth int) { if depth <= 0 { if orch := h.contingencyOrchestrator(); orch != nil { depth = orch.Depth(agentID) } } if depth <= 0 { return } h.mu.Lock() if h.agentLiveTelemetry[agentID] == nil { h.agentLiveTelemetry[agentID] = make(map[string]interface{}) } h.agentLiveTelemetry[agentID]["contingency_depth"] = depth h.mu.Unlock() } func (h *WSHub) attachContingencyPolicy(resp map[string]interface{}) { policy := h.serverPolicySnapshot() if !policy.AIControlEnabled { return } cp := fleetai.BuildContingencyPolicy(true, policy.AIPersona) if cp != nil { resp["contingency_policy"] = cp } } func (h *WSHub) contingencyDepthForAgent(agentID string) int { if orch := h.contingencyOrchestrator(); orch != nil { if d := orch.Depth(agentID); d > 0 { return d } } h.mu.RLock() defer h.mu.RUnlock() if tel, ok := h.agentLiveTelemetry[agentID]; ok { if v, ok := tel["contingency_depth"].(int); ok { return v } if v, ok := tel["contingency_depth"].(float64); ok { return int(v) } } return 0 }