package miningsurgery import ( "crypto/sha256" "encoding/hex" "encoding/json" "sync" "time" ) // SeerEvent is a read-only transcript row for the Seer page stream. type SeerEvent struct { Kind string `json:"kind"` AgentID string `json:"agent_id"` Payload json.RawMessage `json:"payload"` Timestamp string `json:"timestamp"` } // Reporter callbacks for Seer + oath ledger hooks. type Reporter struct { OnSeer func(event SeerEvent) OnOath func(agentID, actor, actionType, strain, whyHash, outcome string, payload map[string]interface{}) } // Tracker queues per-agent mining self-surgery plans (same host only). type Tracker struct { mu sync.Mutex pending map[string]Plan lastPlan map[string]Plan seerEvents []SeerEvent reporter Reporter } func NewTracker() *Tracker { return &Tracker{ pending: make(map[string]Plan), lastPlan: make(map[string]Plan), seerEvents: make([]SeerEvent, 0, 64), } } func (t *Tracker) SetReporter(r Reporter) { if t == nil { return } t.mu.Lock() t.reporter = r t.mu.Unlock() } // QueuePlan stores a composed plan for the agent's next auth pass. func (t *Tracker) QueuePlan(plan Plan) { if t == nil || plan.AgentID == "" { return } t.mu.Lock() prev, seen := t.lastPlan[plan.AgentID] if seen && plansEqual(prev, plan) { t.mu.Unlock() return } t.lastPlan[plan.AgentID] = plan t.pending[plan.AgentID] = plan reporter := t.reporter t.mu.Unlock() t.emit(plan, "pending", reporter) } // ConsumePlan returns and clears a pending plan for the agent's next auth handshake. func (t *Tracker) ConsumePlan(agentID string) (Plan, bool) { if t == nil || agentID == "" { return Plan{}, false } t.mu.Lock() defer t.mu.Unlock() plan, ok := t.pending[agentID] if ok { delete(t.pending, agentID) } return plan, ok } // RecordOutcome logs agent execution results to Seer + oath ledger. func (t *Tracker) RecordOutcome(agentID string, outcome string, results []map[string]interface{}) { if t == nil || agentID == "" { return } t.mu.Lock() plan, ok := t.lastPlan[agentID] reporter := t.reporter t.mu.Unlock() if !ok { plan = Plan{AgentID: agentID, Trigger: "self_surgery_report"} } payload := map[string]interface{}{ "agent_id": agentID, "trigger": plan.Trigger, "reason": plan.Reason, "actions": plan.Actions, "outcome": outcome, "results": results, } raw, _ := json.Marshal(payload) event := SeerEvent{ Kind: "mining_self_surgery", AgentID: agentID, Payload: raw, Timestamp: time.Now().UTC().Format(time.RFC3339), } t.mu.Lock() t.seerEvents = append(t.seerEvents, event) if len(t.seerEvents) > 500 { t.seerEvents = t.seerEvents[len(t.seerEvents)-500:] } t.mu.Unlock() if reporter.OnSeer != nil { reporter.OnSeer(event) } if reporter.OnOath != nil { reporter.OnOath(agentID, "ai_control:mining_surgery", "mining_self_surgery", "", hashWhy(plan), outcome, payload) } } func (t *Tracker) emit(plan Plan, outcome string, reporter Reporter) { raw, _ := json.Marshal(plan) event := SeerEvent{ Kind: "mining_self_surgery", AgentID: plan.AgentID, Payload: raw, Timestamp: time.Now().UTC().Format(time.RFC3339), } t.mu.Lock() t.seerEvents = append(t.seerEvents, event) if len(t.seerEvents) > 500 { t.seerEvents = t.seerEvents[len(t.seerEvents)-500:] } t.mu.Unlock() if reporter.OnSeer != nil { reporter.OnSeer(event) } if reporter.OnOath != nil { payload := map[string]interface{}{ "agent_id": plan.AgentID, "trigger": plan.Trigger, "reason": plan.Reason, "actions": plan.Actions, } reporter.OnOath(plan.AgentID, "ai_control:mining_surgery", "mining_self_surgery", "", hashWhy(plan), outcome, payload) } } func plansEqual(a, b Plan) bool { ab, _ := json.Marshal(a) bb, _ := json.Marshal(b) return string(ab) == string(bb) } func hashWhy(plan Plan) string { raw, _ := json.Marshal(plan) sum := sha256.Sum256(raw) return hex.EncodeToString(sum[:]) }