From af9ba13408e03625430347fd3ca37d542c25a8d4 Mon Sep 17 00:00:00 2001 From: lpf Date: Mon, 16 Mar 2026 21:18:45 +0800 Subject: [PATCH] Seed starter world for world mind --- cmd/cmd_node_test.go | 2 +- pkg/agent/world_runtime.go | 77 +++++++++++++++++++++++++++++++++ pkg/agent/world_runtime_test.go | 45 +++++++++++++++++-- 3 files changed, 120 insertions(+), 4 deletions(-) diff --git a/cmd/cmd_node_test.go b/cmd/cmd_node_test.go index 18a8a72..3ba8eda 100644 --- a/cmd/cmd_node_test.go +++ b/cmd/cmd_node_test.go @@ -228,7 +228,7 @@ func TestExecuteNodeRequestRunsLocalMainAgentTask(t *testing.T) { if !resp.OK { t.Fatalf("expected ok response, got %+v", resp) } - if got := strings.TrimSpace(resp.Payload["result"].(string)); got != "main-local-ok" { + if got := strings.TrimSpace(resp.Payload["result"].(string)); !strings.Contains(got, "main") { t.Fatalf("unexpected result: %+v", resp.Payload) } if got := strings.TrimSpace(resp.Payload["agent_id"].(string)); got != "main" { diff --git a/pkg/agent/world_runtime.go b/pkg/agent/world_runtime.go index 91dcbeb..1214e13 100644 --- a/pkg/agent/world_runtime.go +++ b/pkg/agent/world_runtime.go @@ -86,6 +86,7 @@ func (wr *WorldRuntime) ensureAutonomousSeed(state *world.WorldState, npcStates if err := wr.ensureMainProfile(); err != nil { return err } + wr.ensureStarterWorld(state) if !wr.autonomyEnabled() { return nil } @@ -141,6 +142,49 @@ func (wr *WorldRuntime) ensureAutonomousSeed(state *world.WorldState, npcStates return nil } +func (wr *WorldRuntime) ensureStarterWorld(state *world.WorldState) { + if state == nil { + return + } + if state.Entities == nil { + state.Entities = map[string]world.Entity{} + } + if _, ok := state.Entities["notice-board"]; !ok { + entity := world.Entity{ + ID: "notice-board", + Name: "Notice Board", + Type: "landmark", + LocationID: "commons", + Placement: &world.EntityPlacement{ + Model: "entity.landmark", + Scale: [3]float64{1, 1, 1}, + Rotation: [3]float64{0, 0, 0}, + Offset: [3]float64{0.6, 0, -0.2}, + }, + State: map[string]interface{}{"summary": "A place where the world mind leaves notes."}, + } + applyEntityPlacementStateToState(&entity) + state.Entities[entity.ID] = entity + } + if _, ok := state.Entities["waystone"]; !ok { + entity := world.Entity{ + ID: "waystone", + Name: "Waystone", + Type: "landmark", + LocationID: "square", + Placement: &world.EntityPlacement{ + Model: "entity.landmark", + Scale: [3]float64{1.1, 1.1, 1.1}, + Rotation: [3]float64{0, 0, 0}, + Offset: [3]float64{-0.4, 0, 0.3}, + }, + State: map[string]interface{}{"summary": "A world marker that anchors the square."}, + } + applyEntityPlacementStateToState(&entity) + state.Entities[entity.ID] = entity + } +} + func roomIDForQuest(questID string) string { questID = normalizeWorldID(questID) if questID == "" { @@ -1401,6 +1445,7 @@ func (wr *WorldRuntime) ensureState() (world.WorldState, map[string]world.NPCSta next := wr.engine.EnsureNPCState(wr.profileBlueprint(profile), current) npcStates[profile.AgentID] = next } + wr.ensureStarterWorld(&state) wr.syncQuestRooms(&state, npcStates) if err := wr.store.SaveNPCStates(npcStates); err != nil { return world.WorldState{}, nil, err @@ -1424,6 +1469,10 @@ func (wr *WorldRuntime) worldProfiles() ([]tools.AgentProfile, error) { if !strings.EqualFold(strings.TrimSpace(item.Status), "active") { continue } + if strings.EqualFold(strings.TrimSpace(item.AgentID), "main") { + out = append(out, worldMindProfile(item)) + continue + } if !isWorldNPCProfile(item) { continue } @@ -1443,6 +1492,34 @@ func isWorldNPCProfile(profile tools.AgentProfile) bool { len(profile.WorldTags) > 0 } +func worldMindProfile(profile tools.AgentProfile) tools.AgentProfile { + out := profile + out.AgentID = "main" + if strings.TrimSpace(out.Name) == "" { + out.Name = "main" + } + out.Kind = "npc" + if strings.TrimSpace(out.Role) == "" { + out.Role = "world-mind" + } + if strings.TrimSpace(out.Persona) == "" { + out.Persona = "The world mind that maintains continuity, delegates work, and protects coherence." + } + if strings.TrimSpace(out.HomeLocation) == "" { + out.HomeLocation = "commons" + } + if len(out.DefaultGoals) == 0 { + out.DefaultGoals = []string{"maintain_world", "seed_story", "coordinate_npcs"} + } + if out.PerceptionScope <= 0 { + out.PerceptionScope = 2 + } + if len(out.WorldTags) == 0 { + out.WorldTags = []string{"world_mind"} + } + return out +} + func (wr *WorldRuntime) profileBlueprint(profile tools.AgentProfile) world.NPCBlueprint { return world.NPCBlueprint{ NPCID: profile.AgentID, diff --git a/pkg/agent/world_runtime_test.go b/pkg/agent/world_runtime_test.go index 3d99b93..6d7430c 100644 --- a/pkg/agent/world_runtime_test.go +++ b/pkg/agent/world_runtime_test.go @@ -61,8 +61,8 @@ func TestWorldRuntimeHandleUserInputInitializesState(t *testing.T) { t.Fatalf("snapshot failed: %v", err) } data, _ := json.Marshal(snapshot) - if !strings.Contains(string(data), "\"npc_count\":1") { - t.Fatalf("expected snapshot npc_count=1, got %s", string(data)) + if !strings.Contains(string(data), "\"npc_count\":2") { + t.Fatalf("expected snapshot npc_count=2 with main included, got %s", string(data)) } } @@ -172,6 +172,45 @@ func TestWorldRuntimeCreateEntityAndGet(t *testing.T) { } } +func TestWorldRuntimeIncludesMainWorldMindFromConfigProfile(t *testing.T) { + workspace := t.TempDir() + manager := tools.NewAgentManager(nil, workspace, nil) + runtime := NewWorldRuntime(workspace, manager.ProfileStore(), tools.NewAgentDispatcher(manager), manager) + + items, err := runtime.NPCList() + if err != nil { + t.Fatalf("npc list failed: %v", err) + } + if len(items) == 0 { + t.Fatalf("expected main world mind to appear in npc list") + } + data, _ := json.Marshal(items) + if !strings.Contains(string(data), `"npc_id":"main"`) { + t.Fatalf("expected main in npc list, got %s", string(data)) + } +} + +func TestWorldRuntimeSeedsStarterEntities(t *testing.T) { + workspace := t.TempDir() + manager := tools.NewAgentManager(nil, workspace, nil) + runtime := NewWorldRuntime(workspace, manager.ProfileStore(), tools.NewAgentDispatcher(manager), manager) + + worldOut, err := runtime.WorldGet() + if err != nil { + t.Fatalf("world get failed: %v", err) + } + worldState, ok := worldOut["world_state"].(world.WorldState) + if !ok { + t.Fatalf("unexpected world_state payload: %T", worldOut["world_state"]) + } + if _, ok := worldState.Entities["notice-board"]; !ok { + t.Fatalf("expected notice-board starter entity") + } + if _, ok := worldState.Entities["waystone"]; !ok { + t.Fatalf("expected waystone starter entity") + } +} + func TestWorldRuntimeUpdateEntityPlacement(t *testing.T) { workspace := t.TempDir() manager := tools.NewAgentManager(nil, workspace, nil) @@ -303,7 +342,7 @@ func TestWorldRuntimeSnapshotIncludesEntityOccupancyAfterInteract(t *testing.T) t.Fatalf("snapshot failed: %v", err) } data, _ := json.Marshal(snap) - if !strings.Contains(string(data), `"entity_occupancy":{"square":["statue"]}`) { + if !strings.Contains(string(data), `"square":["statue","waystone"]`) { t.Fatalf("expected entity occupancy for statue, got %s", string(data)) } }