Seed starter world for world mind

This commit is contained in:
lpf
2026-03-16 21:18:45 +08:00
parent 7f2ecfab58
commit af9ba13408
3 changed files with 120 additions and 4 deletions

View File

@@ -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" {

View File

@@ -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,

View File

@@ -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))
}
}