Fix build blockers and rewrite README with authorized-use warning.

Restore server/agent compile fixes, wire remote actions end-to-end, harden run.bat, and document AetherForge with a severity-ranked audit in PROBLEMS.md.
This commit is contained in:
drjones
2026-05-28 07:36:59 -07:00
parent 830c755235
commit a9aeaefb1b
12 changed files with 615 additions and 186 deletions

View File

@@ -23,13 +23,18 @@ import (
// AIHandler manages AI autonomy endpoints.
type AIHandler struct {
db *db.Database
engines map[string]*ollama.Engine // agentID -> engine (each agent can have its own Ollama config)
reports []ollama.Report // recent tool execution reports
engines map[string]*agentEngine // agentID -> engine wrapper
reports []ollama.Report // recent tool execution reports
activity map[string]AIActivityEntry
onEvent func(AIActivityEntry)
mu sync.RWMutex
}
type agentEngine struct {
engine *ollama.Engine
lastUsed time.Time
}
// AIActivityEntry summarizes recent AI cycles per agent.
type AIActivityEntry struct {
AgentID string `json:"agent_id"`
@@ -44,12 +49,34 @@ type AIActivityEntry struct {
// NewAIHandler creates a new AI handler.
func NewAIHandler(database *db.Database) *AIHandler {
return &AIHandler{
h := &AIHandler{
db: database,
engines: make(map[string]*ollama.Engine),
engines: make(map[string]*agentEngine),
reports: make([]ollama.Report, 0, 1000),
activity: make(map[string]AIActivityEntry),
}
go h.runCleanupLoop()
return h
}
func (h *AIHandler) runCleanupLoop() {
ticker := time.NewTicker(10 * time.Minute)
defer ticker.Stop()
for range ticker.C {
h.mu.Lock()
now := time.Now()
for id, eng := range h.engines {
if now.Sub(eng.lastUsed) > 1*time.Hour {
delete(h.engines, id)
}
}
for id, act := range h.activity {
if now.Sub(act.LastReportAt) > 24*time.Hour && now.Sub(act.LastDecideAt) > 24*time.Hour {
delete(h.activity, id)
}
}
h.mu.Unlock()
}
}
func (h *AIHandler) SetEventBroadcaster(fn func(AIActivityEntry)) {
@@ -71,7 +98,10 @@ func (h *AIHandler) SetEngineForAgent(agentID, ollamaEndpoint, model string) {
model = "llama3.2"
}
h.engines[agentID] = ollama.NewEngine(ollamaEndpoint, model)
h.engines[agentID] = &agentEngine{
engine: ollama.NewEngine(ollamaEndpoint, model),
lastUsed: time.Now(),
}
log.Printf("[AI] Engine set for agent %s (endpoint=%s, model=%s)", agentID, ollamaEndpoint, model)
}
@@ -86,7 +116,15 @@ func (h *AIHandler) RemoveEngine(agentID string) {
func (h *AIHandler) GetEngine(agentID string) *ollama.Engine {
h.mu.RLock()
defer h.mu.RUnlock()
return h.engines[agentID]
if entry, ok := h.engines[agentID]; ok {
h.mu.RUnlock() // Briefly unlock to update timestamp
h.mu.Lock()
entry.lastUsed = time.Now()
h.mu.Unlock()
h.mu.RLock()
return entry.engine
}
return nil
}
// HandleDecide handles POST /api/v1/agent/decide

View File

@@ -203,6 +203,8 @@ func sanitizeFilename(name string) string {
// Trim spaces and dots
name = strings.TrimSpace(name)
name = strings.Trim(name, ".")
// Prevent explicit traversal sequences
name = strings.ReplaceAll(name, "..", "")
// Limit length
if len(name) > 100 {
name = name[:100]

View File

@@ -189,6 +189,7 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
h.mu.Lock()
delete(h.agents, agentID)
delete(h.agentConfigs, agentID)
delete(h.agentLogs, agentID)
h.mu.Unlock()
if h.aiHandler != nil {
h.aiHandler.RemoveEngine(agentID)
@@ -629,3 +630,15 @@ func (h *WSHub) BroadcastPoolStatus(status interface{}) {
func (h *WSHub) BroadcastAIActivity(entry interface{}) {
h.broadcastDashboard(Message{Type: "ai_activity", Payload: mustMarshal(entry)})
}
// BroadcastServerLog streams a server log line to connected dashboards.
func (h *WSHub) BroadcastServerLog(line string) {
line = strings.TrimSpace(line)
if line == "" {
return
}
h.broadcastDashboard(Message{
Type: "server_log",
Payload: mustMarshal(map[string]string{"line": line}),
})
}