agentshire 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (406) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/LICENSE +21 -0
  3. package/README.md +437 -0
  4. package/README.zh-CN.md +441 -0
  5. package/ROADMAP.md +207 -0
  6. package/THIRD_PARTY_NOTICES.md +49 -0
  7. package/VISION.md +101 -0
  8. package/index.ts +386 -0
  9. package/openclaw.plugin.json +27 -0
  10. package/package.json +100 -0
  11. package/src/bridge/AGENTS.md +136 -0
  12. package/src/bridge/ActivityStream.ts +184 -0
  13. package/src/bridge/CitizenManager.ts +280 -0
  14. package/src/bridge/DirectorBridge.ts +1076 -0
  15. package/src/bridge/EventTranslator.ts +142 -0
  16. package/src/bridge/NpcEventQueue.ts +61 -0
  17. package/src/bridge/ReconnectManager.ts +42 -0
  18. package/src/bridge/RouteManager.ts +270 -0
  19. package/src/bridge/StateTracker.ts +100 -0
  20. package/src/bridge/ToolVfxMapper.ts +94 -0
  21. package/src/bridge/__tests__/ActivityStream.test.ts +156 -0
  22. package/src/bridge/__tests__/CitizenManager.test.ts +174 -0
  23. package/src/bridge/__tests__/EventTranslator.test.ts +292 -0
  24. package/src/bridge/__tests__/NpcEventQueue.test.ts +92 -0
  25. package/src/bridge/__tests__/RouteManager.test.ts +106 -0
  26. package/src/bridge/data/route-config.json +36 -0
  27. package/src/bridge/data/route-config.ts +19 -0
  28. package/src/bridge/implicit-chat.ts +192 -0
  29. package/src/bridge/index.ts +12 -0
  30. package/src/contracts/agent-state.ts +87 -0
  31. package/src/contracts/agui.ts +212 -0
  32. package/src/contracts/chat.ts +67 -0
  33. package/src/contracts/events.ts +170 -0
  34. package/src/contracts/index.ts +71 -0
  35. package/src/contracts/media.ts +77 -0
  36. package/src/contracts/registry.ts +32 -0
  37. package/src/plugin/AGENTS.md +192 -0
  38. package/src/plugin/__tests__/editor-serve-megapack.test.ts +150 -0
  39. package/src/plugin/__tests__/hook-translator.test.ts +197 -0
  40. package/src/plugin/auto-config.ts +131 -0
  41. package/src/plugin/channel.ts +399 -0
  42. package/src/plugin/chat-asset-resolver.ts +75 -0
  43. package/src/plugin/chat-session-watcher.ts +98 -0
  44. package/src/plugin/citizen-agent-manager.ts +178 -0
  45. package/src/plugin/citizen-chat-router.ts +83 -0
  46. package/src/plugin/citizen-workshop-manager.ts +96 -0
  47. package/src/plugin/custom-asset-manager.ts +250 -0
  48. package/src/plugin/editor-serve.ts +1368 -0
  49. package/src/plugin/group-discussion.ts +223 -0
  50. package/src/plugin/hook-translator.ts +202 -0
  51. package/src/plugin/llm-agent-proxy.ts +193 -0
  52. package/src/plugin/outbound-adapter.ts +187 -0
  53. package/src/plugin/paths.ts +33 -0
  54. package/src/plugin/plan-manager.ts +594 -0
  55. package/src/plugin/runtime.ts +9 -0
  56. package/src/plugin/session-history.ts +838 -0
  57. package/src/plugin/session-log-watcher.ts +221 -0
  58. package/src/plugin/soul-prompt-template.ts +290 -0
  59. package/src/plugin/subagent-tracker.ts +205 -0
  60. package/src/plugin/tools.ts +493 -0
  61. package/src/plugin/town-session.ts +40 -0
  62. package/src/plugin/ws-server.ts +680 -0
  63. package/src/town-souls.ts +134 -0
  64. package/town-frontend/dist/assets/CustomAssetStore-oi8aIurt.js +2 -0
  65. package/town-frontend/dist/assets/GLTFLoader-BA5RqSME.js +3804 -0
  66. package/town-frontend/dist/assets/OrbitControls-ZmySp9sQ.js +2 -0
  67. package/town-frontend/dist/assets/SettingsPanel-BO52reJe.js +2 -0
  68. package/town-frontend/dist/assets/SkeletonUtils-BCVmgslc.js +2 -0
  69. package/town-frontend/dist/assets/SkillLearnCard-Dk38iDpy.js +6 -0
  70. package/town-frontend/dist/assets/TopicSetupPanel-DLaLHB_Z.js +2 -0
  71. package/town-frontend/dist/assets/Trap-Bold-CT0JBE39.woff2 +0 -0
  72. package/town-frontend/dist/assets/Trap-SemiBold-R4_-Ld0j.woff2 +0 -0
  73. package/town-frontend/dist/assets/WeatherSystem-Cb3BvHes.js +550 -0
  74. package/town-frontend/dist/assets/avatars/char-female-a.webp +0 -0
  75. package/town-frontend/dist/assets/avatars/char-female-b.webp +0 -0
  76. package/town-frontend/dist/assets/avatars/char-female-c.webp +0 -0
  77. package/town-frontend/dist/assets/avatars/char-female-d.webp +0 -0
  78. package/town-frontend/dist/assets/avatars/char-female-e.webp +0 -0
  79. package/town-frontend/dist/assets/avatars/char-female-f.webp +0 -0
  80. package/town-frontend/dist/assets/avatars/char-male-a.webp +0 -0
  81. package/town-frontend/dist/assets/avatars/char-male-b.webp +0 -0
  82. package/town-frontend/dist/assets/avatars/char-male-c.webp +0 -0
  83. package/town-frontend/dist/assets/avatars/char-male-d.webp +0 -0
  84. package/town-frontend/dist/assets/avatars/char-male-e.webp +0 -0
  85. package/town-frontend/dist/assets/avatars/char-male-f.webp +0 -0
  86. package/town-frontend/dist/assets/avatars/char-pet-beaver.webp +0 -0
  87. package/town-frontend/dist/assets/avatars/char-pet-bee.webp +0 -0
  88. package/town-frontend/dist/assets/avatars/char-pet-bunny.webp +0 -0
  89. package/town-frontend/dist/assets/avatars/char-pet-cat.webp +0 -0
  90. package/town-frontend/dist/assets/avatars/char-pet-caterpillar.webp +0 -0
  91. package/town-frontend/dist/assets/avatars/char-pet-chick.webp +0 -0
  92. package/town-frontend/dist/assets/avatars/char-pet-cow.webp +0 -0
  93. package/town-frontend/dist/assets/avatars/char-pet-crab.webp +0 -0
  94. package/town-frontend/dist/assets/avatars/char-pet-deer.webp +0 -0
  95. package/town-frontend/dist/assets/avatars/char-pet-dog.webp +0 -0
  96. package/town-frontend/dist/assets/avatars/char-pet-elephant.webp +0 -0
  97. package/town-frontend/dist/assets/avatars/char-pet-fish.webp +0 -0
  98. package/town-frontend/dist/assets/avatars/char-pet-fox.webp +0 -0
  99. package/town-frontend/dist/assets/avatars/char-pet-giraffe.webp +0 -0
  100. package/town-frontend/dist/assets/avatars/char-pet-hog.webp +0 -0
  101. package/town-frontend/dist/assets/avatars/char-pet-koala.webp +0 -0
  102. package/town-frontend/dist/assets/avatars/char-pet-lion.webp +0 -0
  103. package/town-frontend/dist/assets/avatars/char-pet-monkey.webp +0 -0
  104. package/town-frontend/dist/assets/avatars/char-pet-panda.webp +0 -0
  105. package/town-frontend/dist/assets/avatars/char-pet-parrot.webp +0 -0
  106. package/town-frontend/dist/assets/avatars/char-pet-penguin.webp +0 -0
  107. package/town-frontend/dist/assets/avatars/char-pet-pig.webp +0 -0
  108. package/town-frontend/dist/assets/avatars/char-pet-polar.webp +0 -0
  109. package/town-frontend/dist/assets/avatars/char-pet-tiger.webp +0 -0
  110. package/town-frontend/dist/assets/character-catalog-DSmLtlNC.js +2 -0
  111. package/town-frontend/dist/assets/citizenEditor-DubGSJOQ.js +296 -0
  112. package/town-frontend/dist/assets/command-parser-BUd15Bmv.js +12 -0
  113. package/town-frontend/dist/assets/editor-B5QO0OtX.js +258 -0
  114. package/town-frontend/dist/assets/editor-Bk8g1NCD.css +1 -0
  115. package/town-frontend/dist/assets/index-BWfrufil.js +2 -0
  116. package/town-frontend/dist/assets/index-faS20RJk.js +6 -0
  117. package/town-frontend/dist/assets/logo-DJI6EtST.png +0 -0
  118. package/town-frontend/dist/assets/logo-title-AdKPZX5E.png +0 -0
  119. package/town-frontend/dist/assets/main-CqsN43aT.js +224 -0
  120. package/town-frontend/dist/assets/main-D7neuy3w.css +1 -0
  121. package/town-frontend/dist/assets/models/buildings/base.bin +0 -0
  122. package/town-frontend/dist/assets/models/buildings/base.gltf +136 -0
  123. package/town-frontend/dist/assets/models/buildings/bench.bin +0 -0
  124. package/town-frontend/dist/assets/models/buildings/bench.gltf +136 -0
  125. package/town-frontend/dist/assets/models/buildings/box_A.bin +0 -0
  126. package/town-frontend/dist/assets/models/buildings/box_A.gltf +136 -0
  127. package/town-frontend/dist/assets/models/buildings/box_B.bin +0 -0
  128. package/town-frontend/dist/assets/models/buildings/box_B.gltf +136 -0
  129. package/town-frontend/dist/assets/models/buildings/building_A.bin +0 -0
  130. package/town-frontend/dist/assets/models/buildings/building_A.gltf +136 -0
  131. package/town-frontend/dist/assets/models/buildings/building_A_withoutBase.bin +0 -0
  132. package/town-frontend/dist/assets/models/buildings/building_A_withoutBase.gltf +136 -0
  133. package/town-frontend/dist/assets/models/buildings/building_B.bin +0 -0
  134. package/town-frontend/dist/assets/models/buildings/building_B.gltf +136 -0
  135. package/town-frontend/dist/assets/models/buildings/building_B_withoutBase.bin +0 -0
  136. package/town-frontend/dist/assets/models/buildings/building_B_withoutBase.gltf +136 -0
  137. package/town-frontend/dist/assets/models/buildings/building_C.bin +0 -0
  138. package/town-frontend/dist/assets/models/buildings/building_C.gltf +136 -0
  139. package/town-frontend/dist/assets/models/buildings/building_C_withoutBase.bin +0 -0
  140. package/town-frontend/dist/assets/models/buildings/building_C_withoutBase.gltf +136 -0
  141. package/town-frontend/dist/assets/models/buildings/building_D.bin +0 -0
  142. package/town-frontend/dist/assets/models/buildings/building_D.gltf +136 -0
  143. package/town-frontend/dist/assets/models/buildings/building_D_withoutBase.bin +0 -0
  144. package/town-frontend/dist/assets/models/buildings/building_D_withoutBase.gltf +136 -0
  145. package/town-frontend/dist/assets/models/buildings/building_E.bin +0 -0
  146. package/town-frontend/dist/assets/models/buildings/building_E.gltf +136 -0
  147. package/town-frontend/dist/assets/models/buildings/building_E_withoutBase.bin +0 -0
  148. package/town-frontend/dist/assets/models/buildings/building_E_withoutBase.gltf +136 -0
  149. package/town-frontend/dist/assets/models/buildings/building_F.bin +0 -0
  150. package/town-frontend/dist/assets/models/buildings/building_F.gltf +136 -0
  151. package/town-frontend/dist/assets/models/buildings/building_F_withoutBase.bin +0 -0
  152. package/town-frontend/dist/assets/models/buildings/building_F_withoutBase.gltf +136 -0
  153. package/town-frontend/dist/assets/models/buildings/building_G.bin +0 -0
  154. package/town-frontend/dist/assets/models/buildings/building_G.gltf +136 -0
  155. package/town-frontend/dist/assets/models/buildings/building_G_withoutBase.bin +0 -0
  156. package/town-frontend/dist/assets/models/buildings/building_G_withoutBase.gltf +136 -0
  157. package/town-frontend/dist/assets/models/buildings/building_H.bin +0 -0
  158. package/town-frontend/dist/assets/models/buildings/building_H.gltf +136 -0
  159. package/town-frontend/dist/assets/models/buildings/building_H_withoutBase.bin +0 -0
  160. package/town-frontend/dist/assets/models/buildings/building_H_withoutBase.gltf +136 -0
  161. package/town-frontend/dist/assets/models/buildings/bush.bin +0 -0
  162. package/town-frontend/dist/assets/models/buildings/bush.gltf +136 -0
  163. package/town-frontend/dist/assets/models/buildings/car_hatchback.bin +0 -0
  164. package/town-frontend/dist/assets/models/buildings/car_hatchback.gltf +442 -0
  165. package/town-frontend/dist/assets/models/buildings/car_police.bin +0 -0
  166. package/town-frontend/dist/assets/models/buildings/car_police.gltf +442 -0
  167. package/town-frontend/dist/assets/models/buildings/car_sedan.bin +0 -0
  168. package/town-frontend/dist/assets/models/buildings/car_sedan.gltf +442 -0
  169. package/town-frontend/dist/assets/models/buildings/car_stationwagon.bin +0 -0
  170. package/town-frontend/dist/assets/models/buildings/car_stationwagon.gltf +442 -0
  171. package/town-frontend/dist/assets/models/buildings/car_taxi.bin +0 -0
  172. package/town-frontend/dist/assets/models/buildings/car_taxi.gltf +442 -0
  173. package/town-frontend/dist/assets/models/buildings/citybits_texture.png +0 -0
  174. package/town-frontend/dist/assets/models/buildings/dumpster.bin +0 -0
  175. package/town-frontend/dist/assets/models/buildings/dumpster.gltf +136 -0
  176. package/town-frontend/dist/assets/models/buildings/firehydrant.bin +0 -0
  177. package/town-frontend/dist/assets/models/buildings/firehydrant.gltf +136 -0
  178. package/town-frontend/dist/assets/models/buildings/road_corner.bin +0 -0
  179. package/town-frontend/dist/assets/models/buildings/road_corner.gltf +136 -0
  180. package/town-frontend/dist/assets/models/buildings/road_corner_curved.bin +0 -0
  181. package/town-frontend/dist/assets/models/buildings/road_corner_curved.gltf +136 -0
  182. package/town-frontend/dist/assets/models/buildings/road_junction.bin +0 -0
  183. package/town-frontend/dist/assets/models/buildings/road_junction.gltf +136 -0
  184. package/town-frontend/dist/assets/models/buildings/road_straight.bin +0 -0
  185. package/town-frontend/dist/assets/models/buildings/road_straight.gltf +136 -0
  186. package/town-frontend/dist/assets/models/buildings/road_straight_crossing.bin +0 -0
  187. package/town-frontend/dist/assets/models/buildings/road_straight_crossing.gltf +136 -0
  188. package/town-frontend/dist/assets/models/buildings/road_tsplit.bin +0 -0
  189. package/town-frontend/dist/assets/models/buildings/road_tsplit.gltf +136 -0
  190. package/town-frontend/dist/assets/models/buildings/streetlight.bin +0 -0
  191. package/town-frontend/dist/assets/models/buildings/streetlight.gltf +136 -0
  192. package/town-frontend/dist/assets/models/buildings/trafficlight_A.bin +0 -0
  193. package/town-frontend/dist/assets/models/buildings/trafficlight_A.gltf +136 -0
  194. package/town-frontend/dist/assets/models/buildings/trafficlight_B.bin +0 -0
  195. package/town-frontend/dist/assets/models/buildings/trafficlight_B.gltf +136 -0
  196. package/town-frontend/dist/assets/models/buildings/trafficlight_C.bin +0 -0
  197. package/town-frontend/dist/assets/models/buildings/trafficlight_C.gltf +136 -0
  198. package/town-frontend/dist/assets/models/buildings/trash_A.bin +0 -0
  199. package/town-frontend/dist/assets/models/buildings/trash_A.gltf +136 -0
  200. package/town-frontend/dist/assets/models/buildings/trash_B.bin +0 -0
  201. package/town-frontend/dist/assets/models/buildings/trash_B.gltf +136 -0
  202. package/town-frontend/dist/assets/models/buildings/watertower.bin +0 -0
  203. package/town-frontend/dist/assets/models/buildings/watertower.gltf +136 -0
  204. package/town-frontend/dist/assets/models/characters/Textures/colormap.png +0 -0
  205. package/town-frontend/dist/assets/models/characters/character-female-a.glb +0 -0
  206. package/town-frontend/dist/assets/models/characters/character-female-b.glb +0 -0
  207. package/town-frontend/dist/assets/models/characters/character-female-c.glb +0 -0
  208. package/town-frontend/dist/assets/models/characters/character-female-d.glb +0 -0
  209. package/town-frontend/dist/assets/models/characters/character-female-e.glb +0 -0
  210. package/town-frontend/dist/assets/models/characters/character-female-f.glb +0 -0
  211. package/town-frontend/dist/assets/models/characters/character-male-a.glb +0 -0
  212. package/town-frontend/dist/assets/models/characters/character-male-b.glb +0 -0
  213. package/town-frontend/dist/assets/models/characters/character-male-c.glb +0 -0
  214. package/town-frontend/dist/assets/models/characters/character-male-d.glb +0 -0
  215. package/town-frontend/dist/assets/models/characters/character-male-e.glb +0 -0
  216. package/town-frontend/dist/assets/models/characters/character-male-f.glb +0 -0
  217. package/town-frontend/dist/assets/models/characters/character-pet-beaver.glb +0 -0
  218. package/town-frontend/dist/assets/models/characters/character-pet-bee.glb +0 -0
  219. package/town-frontend/dist/assets/models/characters/character-pet-bunny.glb +0 -0
  220. package/town-frontend/dist/assets/models/characters/character-pet-cat.glb +0 -0
  221. package/town-frontend/dist/assets/models/characters/character-pet-caterpillar.glb +0 -0
  222. package/town-frontend/dist/assets/models/characters/character-pet-chick.glb +0 -0
  223. package/town-frontend/dist/assets/models/characters/character-pet-cow.glb +0 -0
  224. package/town-frontend/dist/assets/models/characters/character-pet-crab.glb +0 -0
  225. package/town-frontend/dist/assets/models/characters/character-pet-deer.glb +0 -0
  226. package/town-frontend/dist/assets/models/characters/character-pet-dog.glb +0 -0
  227. package/town-frontend/dist/assets/models/characters/character-pet-elephant.glb +0 -0
  228. package/town-frontend/dist/assets/models/characters/character-pet-fish.glb +0 -0
  229. package/town-frontend/dist/assets/models/characters/character-pet-fox.glb +0 -0
  230. package/town-frontend/dist/assets/models/characters/character-pet-giraffe.glb +0 -0
  231. package/town-frontend/dist/assets/models/characters/character-pet-hog.glb +0 -0
  232. package/town-frontend/dist/assets/models/characters/character-pet-koala.glb +0 -0
  233. package/town-frontend/dist/assets/models/characters/character-pet-lion.glb +0 -0
  234. package/town-frontend/dist/assets/models/characters/character-pet-monkey.glb +0 -0
  235. package/town-frontend/dist/assets/models/characters/character-pet-panda.glb +0 -0
  236. package/town-frontend/dist/assets/models/characters/character-pet-parrot.glb +0 -0
  237. package/town-frontend/dist/assets/models/characters/character-pet-penguin.glb +0 -0
  238. package/town-frontend/dist/assets/models/characters/character-pet-pig.glb +0 -0
  239. package/town-frontend/dist/assets/models/characters/character-pet-polar.glb +0 -0
  240. package/town-frontend/dist/assets/models/characters/character-pet-tiger.glb +0 -0
  241. package/town-frontend/dist/assets/models/characters/colormap.png +0 -0
  242. package/town-frontend/dist/assets/models/furniture/armchair.bin +0 -0
  243. package/town-frontend/dist/assets/models/furniture/armchair.gltf +136 -0
  244. package/town-frontend/dist/assets/models/furniture/armchair_pillows.bin +0 -0
  245. package/town-frontend/dist/assets/models/furniture/armchair_pillows.gltf +136 -0
  246. package/town-frontend/dist/assets/models/furniture/bed_double_A.bin +0 -0
  247. package/town-frontend/dist/assets/models/furniture/bed_double_A.gltf +136 -0
  248. package/town-frontend/dist/assets/models/furniture/bed_double_B.bin +0 -0
  249. package/town-frontend/dist/assets/models/furniture/bed_double_B.gltf +136 -0
  250. package/town-frontend/dist/assets/models/furniture/bed_single_A.bin +0 -0
  251. package/town-frontend/dist/assets/models/furniture/bed_single_A.gltf +136 -0
  252. package/town-frontend/dist/assets/models/furniture/bed_single_B.bin +0 -0
  253. package/town-frontend/dist/assets/models/furniture/bed_single_B.gltf +136 -0
  254. package/town-frontend/dist/assets/models/furniture/book_set.bin +0 -0
  255. package/town-frontend/dist/assets/models/furniture/book_set.gltf +136 -0
  256. package/town-frontend/dist/assets/models/furniture/book_single.bin +0 -0
  257. package/town-frontend/dist/assets/models/furniture/book_single.gltf +136 -0
  258. package/town-frontend/dist/assets/models/furniture/cabinet_medium.bin +0 -0
  259. package/town-frontend/dist/assets/models/furniture/cabinet_medium.gltf +136 -0
  260. package/town-frontend/dist/assets/models/furniture/cabinet_medium_decorated.bin +0 -0
  261. package/town-frontend/dist/assets/models/furniture/cabinet_medium_decorated.gltf +136 -0
  262. package/town-frontend/dist/assets/models/furniture/cabinet_small.bin +0 -0
  263. package/town-frontend/dist/assets/models/furniture/cabinet_small.gltf +136 -0
  264. package/town-frontend/dist/assets/models/furniture/cabinet_small_decorated.bin +0 -0
  265. package/town-frontend/dist/assets/models/furniture/cabinet_small_decorated.gltf +136 -0
  266. package/town-frontend/dist/assets/models/furniture/cactus_medium_A.bin +0 -0
  267. package/town-frontend/dist/assets/models/furniture/cactus_medium_A.gltf +136 -0
  268. package/town-frontend/dist/assets/models/furniture/cactus_medium_B.bin +0 -0
  269. package/town-frontend/dist/assets/models/furniture/cactus_medium_B.gltf +136 -0
  270. package/town-frontend/dist/assets/models/furniture/cactus_small_A.bin +0 -0
  271. package/town-frontend/dist/assets/models/furniture/cactus_small_A.gltf +136 -0
  272. package/town-frontend/dist/assets/models/furniture/cactus_small_B.bin +0 -0
  273. package/town-frontend/dist/assets/models/furniture/cactus_small_B.gltf +136 -0
  274. package/town-frontend/dist/assets/models/furniture/chair_A.bin +0 -0
  275. package/town-frontend/dist/assets/models/furniture/chair_A.gltf +136 -0
  276. package/town-frontend/dist/assets/models/furniture/chair_A_wood.bin +0 -0
  277. package/town-frontend/dist/assets/models/furniture/chair_A_wood.gltf +136 -0
  278. package/town-frontend/dist/assets/models/furniture/chair_B.bin +0 -0
  279. package/town-frontend/dist/assets/models/furniture/chair_B.gltf +136 -0
  280. package/town-frontend/dist/assets/models/furniture/chair_B_wood.bin +0 -0
  281. package/town-frontend/dist/assets/models/furniture/chair_B_wood.gltf +136 -0
  282. package/town-frontend/dist/assets/models/furniture/chair_C.bin +0 -0
  283. package/town-frontend/dist/assets/models/furniture/chair_C.gltf +136 -0
  284. package/town-frontend/dist/assets/models/furniture/chair_stool.bin +0 -0
  285. package/town-frontend/dist/assets/models/furniture/chair_stool.gltf +136 -0
  286. package/town-frontend/dist/assets/models/furniture/chair_stool_wood.bin +0 -0
  287. package/town-frontend/dist/assets/models/furniture/chair_stool_wood.gltf +136 -0
  288. package/town-frontend/dist/assets/models/furniture/couch.bin +0 -0
  289. package/town-frontend/dist/assets/models/furniture/couch.gltf +136 -0
  290. package/town-frontend/dist/assets/models/furniture/couch_pillows.bin +0 -0
  291. package/town-frontend/dist/assets/models/furniture/couch_pillows.gltf +136 -0
  292. package/town-frontend/dist/assets/models/furniture/furniturebits_texture.png +0 -0
  293. package/town-frontend/dist/assets/models/furniture/lamp_standing.bin +0 -0
  294. package/town-frontend/dist/assets/models/furniture/lamp_standing.gltf +136 -0
  295. package/town-frontend/dist/assets/models/furniture/lamp_table.bin +0 -0
  296. package/town-frontend/dist/assets/models/furniture/lamp_table.gltf +136 -0
  297. package/town-frontend/dist/assets/models/furniture/pictureframe_large_A.bin +0 -0
  298. package/town-frontend/dist/assets/models/furniture/pictureframe_large_A.gltf +136 -0
  299. package/town-frontend/dist/assets/models/furniture/pictureframe_large_B.bin +0 -0
  300. package/town-frontend/dist/assets/models/furniture/pictureframe_large_B.gltf +136 -0
  301. package/town-frontend/dist/assets/models/furniture/pictureframe_medium.bin +0 -0
  302. package/town-frontend/dist/assets/models/furniture/pictureframe_medium.gltf +136 -0
  303. package/town-frontend/dist/assets/models/furniture/pictureframe_small_A.bin +0 -0
  304. package/town-frontend/dist/assets/models/furniture/pictureframe_small_A.gltf +136 -0
  305. package/town-frontend/dist/assets/models/furniture/pictureframe_small_B.bin +0 -0
  306. package/town-frontend/dist/assets/models/furniture/pictureframe_small_B.gltf +136 -0
  307. package/town-frontend/dist/assets/models/furniture/pictureframe_small_C.bin +0 -0
  308. package/town-frontend/dist/assets/models/furniture/pictureframe_small_C.gltf +136 -0
  309. package/town-frontend/dist/assets/models/furniture/pictureframe_standing_A.bin +0 -0
  310. package/town-frontend/dist/assets/models/furniture/pictureframe_standing_A.gltf +136 -0
  311. package/town-frontend/dist/assets/models/furniture/pictureframe_standing_B.bin +0 -0
  312. package/town-frontend/dist/assets/models/furniture/pictureframe_standing_B.gltf +136 -0
  313. package/town-frontend/dist/assets/models/furniture/pillow_A.bin +0 -0
  314. package/town-frontend/dist/assets/models/furniture/pillow_A.gltf +136 -0
  315. package/town-frontend/dist/assets/models/furniture/pillow_B.bin +0 -0
  316. package/town-frontend/dist/assets/models/furniture/pillow_B.gltf +136 -0
  317. package/town-frontend/dist/assets/models/furniture/rug_oval_A.bin +0 -0
  318. package/town-frontend/dist/assets/models/furniture/rug_oval_A.gltf +136 -0
  319. package/town-frontend/dist/assets/models/furniture/rug_oval_B.bin +0 -0
  320. package/town-frontend/dist/assets/models/furniture/rug_oval_B.gltf +136 -0
  321. package/town-frontend/dist/assets/models/furniture/rug_rectangle_A.bin +0 -0
  322. package/town-frontend/dist/assets/models/furniture/rug_rectangle_A.gltf +136 -0
  323. package/town-frontend/dist/assets/models/furniture/rug_rectangle_B.bin +0 -0
  324. package/town-frontend/dist/assets/models/furniture/rug_rectangle_B.gltf +136 -0
  325. package/town-frontend/dist/assets/models/furniture/rug_rectangle_stripes_A.bin +0 -0
  326. package/town-frontend/dist/assets/models/furniture/rug_rectangle_stripes_A.gltf +136 -0
  327. package/town-frontend/dist/assets/models/furniture/rug_rectangle_stripes_B.bin +0 -0
  328. package/town-frontend/dist/assets/models/furniture/rug_rectangle_stripes_B.gltf +136 -0
  329. package/town-frontend/dist/assets/models/furniture/shelf_A_big.bin +0 -0
  330. package/town-frontend/dist/assets/models/furniture/shelf_A_big.gltf +136 -0
  331. package/town-frontend/dist/assets/models/furniture/shelf_A_small.bin +0 -0
  332. package/town-frontend/dist/assets/models/furniture/shelf_A_small.gltf +136 -0
  333. package/town-frontend/dist/assets/models/furniture/shelf_B_large.bin +0 -0
  334. package/town-frontend/dist/assets/models/furniture/shelf_B_large.gltf +136 -0
  335. package/town-frontend/dist/assets/models/furniture/shelf_B_large_decorated.bin +0 -0
  336. package/town-frontend/dist/assets/models/furniture/shelf_B_large_decorated.gltf +136 -0
  337. package/town-frontend/dist/assets/models/furniture/shelf_B_small.bin +0 -0
  338. package/town-frontend/dist/assets/models/furniture/shelf_B_small.gltf +136 -0
  339. package/town-frontend/dist/assets/models/furniture/shelf_B_small_decorated.bin +0 -0
  340. package/town-frontend/dist/assets/models/furniture/shelf_B_small_decorated.gltf +136 -0
  341. package/town-frontend/dist/assets/models/furniture/table_low.bin +0 -0
  342. package/town-frontend/dist/assets/models/furniture/table_low.gltf +136 -0
  343. package/town-frontend/dist/assets/models/furniture/table_medium.bin +0 -0
  344. package/town-frontend/dist/assets/models/furniture/table_medium.gltf +136 -0
  345. package/town-frontend/dist/assets/models/furniture/table_medium_long.bin +0 -0
  346. package/town-frontend/dist/assets/models/furniture/table_medium_long.gltf +136 -0
  347. package/town-frontend/dist/assets/models/furniture/table_small.bin +0 -0
  348. package/town-frontend/dist/assets/models/furniture/table_small.gltf +136 -0
  349. package/town-frontend/dist/assets/models/props/bench.bin +0 -0
  350. package/town-frontend/dist/assets/models/props/bench.gltf +136 -0
  351. package/town-frontend/dist/assets/models/props/bush.bin +0 -0
  352. package/town-frontend/dist/assets/models/props/bush.gltf +136 -0
  353. package/town-frontend/dist/assets/models/props/capybara.glb +0 -0
  354. package/town-frontend/dist/assets/models/props/car_hatchback.bin +0 -0
  355. package/town-frontend/dist/assets/models/props/car_hatchback.gltf +442 -0
  356. package/town-frontend/dist/assets/models/props/car_sedan.bin +0 -0
  357. package/town-frontend/dist/assets/models/props/car_sedan.gltf +442 -0
  358. package/town-frontend/dist/assets/models/props/car_taxi.bin +0 -0
  359. package/town-frontend/dist/assets/models/props/car_taxi.gltf +442 -0
  360. package/town-frontend/dist/assets/models/props/citybits_texture.png +0 -0
  361. package/town-frontend/dist/assets/models/props/dumpster.bin +0 -0
  362. package/town-frontend/dist/assets/models/props/dumpster.gltf +136 -0
  363. package/town-frontend/dist/assets/models/props/firehydrant.bin +0 -0
  364. package/town-frontend/dist/assets/models/props/firehydrant.gltf +136 -0
  365. package/town-frontend/dist/assets/models/props/streetlight.bin +0 -0
  366. package/town-frontend/dist/assets/models/props/streetlight.gltf +136 -0
  367. package/town-frontend/dist/assets/models/props/trafficlight_A.bin +0 -0
  368. package/town-frontend/dist/assets/models/props/trafficlight_A.gltf +136 -0
  369. package/town-frontend/dist/assets/models/props/trash_A.bin +0 -0
  370. package/town-frontend/dist/assets/models/props/trash_A.gltf +136 -0
  371. package/town-frontend/dist/assets/models/props/watertower.bin +0 -0
  372. package/town-frontend/dist/assets/models/props/watertower.gltf +136 -0
  373. package/town-frontend/dist/assets/models/stage-deco/Flowers_1_D.glb +0 -0
  374. package/town-frontend/dist/assets/models/stage-deco/Flowers_2_B.glb +0 -0
  375. package/town-frontend/dist/assets/models/stage-deco/Grass_A_1.glb +0 -0
  376. package/town-frontend/dist/assets/models/stage-deco/Park_GrassHill_A.glb +0 -0
  377. package/town-frontend/dist/assets/models/stage-deco/Pebles_1_A_2.glb +0 -0
  378. package/town-frontend/dist/assets/music/bgm_day.mp3 +0 -0
  379. package/town-frontend/dist/assets/music/bgm_dusk.mp3 +0 -0
  380. package/town-frontend/dist/assets/music/bgm_night.mp3 +0 -0
  381. package/town-frontend/dist/assets/music/bgm_work.mp3 +0 -0
  382. package/town-frontend/dist/assets/office-whiteboard-idle-CyEwBrq_.webp +0 -0
  383. package/town-frontend/dist/assets/preview-B6hYEQij.js +2 -0
  384. package/town-frontend/dist/assets/town-BKesnERP.js +8888 -0
  385. package/town-frontend/dist/assets/town-BnswKsjF.css +1 -0
  386. package/town-frontend/dist/citizen-editor.html +166 -0
  387. package/town-frontend/dist/editor.html +227 -0
  388. package/town-frontend/dist/index.html +22 -0
  389. package/town-frontend/dist/preview.html +56 -0
  390. package/town-frontend/dist/town.html +165 -0
  391. package/town-frontend/dist/viewer.html +91 -0
  392. package/town-souls/CHEN.md +130 -0
  393. package/town-souls/CHENGZI.md +92 -0
  394. package/town-souls/CITIZEN_tpl.md +16 -0
  395. package/town-souls/DIANDIAN.md +92 -0
  396. package/town-souls/HAITANG.md +94 -0
  397. package/town-souls/QIQI.md +119 -0
  398. package/town-souls/SOUL.md +141 -0
  399. package/town-souls/SOUL_tpl.md +135 -0
  400. package/town-souls/XIAOLIE.md +107 -0
  401. package/town-souls/YAN.md +107 -0
  402. package/town-workspace/IDENTITY.md +5 -0
  403. package/town-workspace/SOUL.md +141 -0
  404. package/town-workspace/project-workflow.md +81 -0
  405. package/town-workspace/town-defaults.json +92 -0
  406. package/town-workspace/town-guide.md +45 -0
@@ -0,0 +1,594 @@
1
+ /**
2
+ * PlanManager — server-side plan state for multi-agent project workflows.
3
+ *
4
+ * The steward calls `create_plan` to register a structured plan, then
5
+ * `next_step` after each batch completes. Sub-agent completions are
6
+ * auto-matched to plan steps via label/displayName.
7
+ */
8
+ import { isLabelBusy } from './subagent-tracker.js'
9
+ import { getActiveTownSessionId } from './ws-server.js'
10
+ import { existsSync, readdirSync, readFileSync } from 'node:fs'
11
+ import { join } from 'node:path'
12
+
13
+ export interface CitizenRosterEntry {
14
+ specialty: string
15
+ role: string
16
+ bio: string
17
+ soulFilePath: string
18
+ }
19
+
20
+ export interface PlanAgent {
21
+ name: string
22
+ task: string
23
+ files?: string[]
24
+ status: 'pending' | 'running' | 'completed' | 'failed'
25
+ }
26
+
27
+ export interface PlanStep {
28
+ id: string
29
+ description: string
30
+ agents: PlanAgent[]
31
+ /** Batch number (1-based). Steps with the same batch run concurrently. */
32
+ batch: number
33
+ }
34
+
35
+ export interface ProjectPlan {
36
+ name: string
37
+ type: string
38
+ projectDir: string
39
+ citizenRoster: Map<string, CitizenRosterEntry>
40
+ steps: PlanStep[]
41
+ currentStepIndex: number
42
+ createdAt: number
43
+ townSessionId: string
44
+ }
45
+
46
+ const activePlans = new Map<string, ProjectPlan>()
47
+ let lastPlanName: string | null = null
48
+
49
+ interface ActiveTask {
50
+ name: string
51
+ taskDir: string
52
+ status: 'pending' | 'completed'
53
+ createdAt: number
54
+ }
55
+
56
+ const activeTasks = new Map<string, ActiveTask>()
57
+
58
+ export function registerTask(name: string, taskDir: string): void {
59
+ activeTasks.set(name, { name, taskDir, status: 'pending', createdAt: Date.now() })
60
+ console.log(`[PlanManager] Task registered: "${name}" → ${taskDir}`)
61
+ }
62
+
63
+ export function completeTask(name: string): void {
64
+ const task = activeTasks.get(name)
65
+ if (task) {
66
+ task.status = 'completed'
67
+ console.log(`[PlanManager] Task completed: "${name}"`)
68
+ }
69
+ }
70
+
71
+ export function hasActiveTasks(): boolean {
72
+ for (const task of activeTasks.values()) {
73
+ if (task.status === 'pending') return true
74
+ }
75
+ return false
76
+ }
77
+
78
+ export function clearTasks(): void {
79
+ activeTasks.clear()
80
+ }
81
+
82
+ export function createPlan(
83
+ name: string,
84
+ type: string,
85
+ projectDir: string,
86
+ citizenRoster: Map<string, CitizenRosterEntry>,
87
+ steps: Array<{ id: string; description: string; agents: Array<{ name: string; task: string; files?: string[] }>; batch?: number }>,
88
+ ): string {
89
+ if (steps.length === 0) {
90
+ return 'Error: plan must have at least one step.'
91
+ }
92
+
93
+ for (const s of steps) {
94
+ if (s.agents.length > 1) {
95
+ const agentsWithFiles = s.agents.filter(a => a.files && a.files.length > 0)
96
+ if (agentsWithFiles.length > 0) {
97
+ const allFiles = agentsWithFiles.flatMap(a => a.files ?? [])
98
+ const seen = new Set<string>()
99
+ for (const f of allFiles) {
100
+ if (seen.has(f)) {
101
+ const conflicting = agentsWithFiles.filter(a => (a.files ?? []).includes(f)).map(a => a.name).join(' 和 ')
102
+ return `Error: step "${s.id}" — "${f}" is claimed by both ${conflicting}. ` +
103
+ `Split into non-overlapping subdirectories.`
104
+ }
105
+ seen.add(f)
106
+ }
107
+ }
108
+ }
109
+ }
110
+
111
+ const plan: ProjectPlan = {
112
+ name,
113
+ type,
114
+ projectDir,
115
+ citizenRoster,
116
+ steps: [
117
+ ...steps.map((s, i) => ({
118
+ id: s.id,
119
+ description: s.description,
120
+ batch: Number(s.batch) || (i + 1),
121
+ agents: s.agents.map(a => ({ name: a.name ?? '', task: a.task ?? '', files: a.files, status: 'pending' as const })),
122
+ })),
123
+ {
124
+ id: COMPLETE_STEP_ID,
125
+ description: '宣布完成 — 调用 mission_complete',
126
+ agents: [],
127
+ batch: steps.length + 1,
128
+ },
129
+ ],
130
+ currentStepIndex: 0,
131
+ createdAt: Date.now(),
132
+ townSessionId: getActiveTownSessionId() ?? 'unknown',
133
+ }
134
+
135
+ activePlans.set(name, plan)
136
+ lastPlanName = name
137
+
138
+ console.log(`[PlanManager] Plan created: "${name}" (${type}), ${steps.length} steps + mission_complete, projectDir: ${projectDir}, total plans: ${activePlans.size}`)
139
+ return formatPlanCreated(plan)
140
+ }
141
+
142
+ export function getNextStepInstruction(planName?: string): string {
143
+ const key = planName ?? lastPlanName
144
+ if (!key) return 'No active plan. Call create_plan first for complex multi-agent projects.'
145
+ const plan = activePlans.get(key)
146
+ if (!plan) return `No plan found with name "${key}".`
147
+ reconcileRunningAgents(plan)
148
+ return formatNextStep(plan)
149
+ }
150
+
151
+ function reconcileRunningAgents(plan: ProjectPlan): void {
152
+ for (const step of plan.steps) {
153
+ for (const agent of step.agents) {
154
+ if (agent.status === 'running' && !isLabelBusy(agent.name ?? '')) {
155
+ console.warn(`[PlanManager] ⚠️ "${agent.name}" is running in plan "${plan.name}" step "${step.id}" but not tracked by subagent-tracker — keeping status as running (tracker may not have registered yet)`)
156
+ }
157
+ }
158
+ }
159
+ }
160
+
161
+ export function onAgentCompleted(label: string, success: boolean): void {
162
+ if (activePlans.size === 0) return
163
+
164
+ const currentSession = getActiveTownSessionId()
165
+ const normalizedLabel = label.trim().toLowerCase()
166
+
167
+ for (const plan of activePlans.values()) {
168
+ if (currentSession && plan.townSessionId !== currentSession) continue
169
+ for (const step of plan.steps) {
170
+ for (const agent of step.agents) {
171
+ if ((agent.name ?? '').trim().toLowerCase() === normalizedLabel && (agent.status === 'pending' || agent.status === 'running')) {
172
+ agent.status = success ? 'completed' : 'failed'
173
+ console.log(`[PlanManager] Agent "${label}" marked ${agent.status} in plan "${plan.name}" step "${step.id}" (session: ${plan.townSessionId})`)
174
+ return
175
+ }
176
+ }
177
+ }
178
+ }
179
+ }
180
+
181
+ export function onAgentStarted(label: string): void {
182
+ if (activePlans.size === 0) return
183
+
184
+ const currentSession = getActiveTownSessionId()
185
+ const normalizedLabel = label.trim().toLowerCase()
186
+
187
+ for (const plan of activePlans.values()) {
188
+ if (currentSession && plan.townSessionId !== currentSession) continue
189
+ for (const step of plan.steps) {
190
+ for (const agent of step.agents) {
191
+ if ((agent.name ?? '').trim().toLowerCase() === normalizedLabel && agent.status === 'pending') {
192
+ agent.status = 'running'
193
+ return
194
+ }
195
+ }
196
+ }
197
+ }
198
+ }
199
+
200
+ export function clearPlan(): void {
201
+ activePlans.clear()
202
+ lastPlanName = null
203
+ activeTasks.clear()
204
+ }
205
+
206
+ export function cleanupStaleSessionPlans(currentSessionId: string): void {
207
+ let cleaned = 0
208
+ for (const [name, plan] of activePlans) {
209
+ if (plan.townSessionId !== currentSessionId) {
210
+ activePlans.delete(name)
211
+ cleaned++
212
+ }
213
+ }
214
+ for (const [name, task] of activeTasks) {
215
+ if ((task as any).townSessionId && (task as any).townSessionId !== currentSessionId) {
216
+ activeTasks.delete(name)
217
+ cleaned++
218
+ }
219
+ }
220
+ if (cleaned > 0) {
221
+ console.log(`[PlanManager] Cleaned up ${cleaned} stale plan/task entries from previous sessions`)
222
+ }
223
+ }
224
+
225
+ export function hasActivePlan(): boolean {
226
+ return activePlans.size > 0
227
+ }
228
+
229
+ export function completePlan(planName?: string): void {
230
+ const key = planName ?? lastPlanName
231
+ if (!key) return
232
+ if (activePlans.has(key)) {
233
+ activePlans.delete(key)
234
+ console.log(`[PlanManager] Plan "${key}" completed and removed`)
235
+ }
236
+ if (lastPlanName === key) {
237
+ lastPlanName = activePlans.size > 0 ? Array.from(activePlans.keys()).pop()! : null
238
+ }
239
+ }
240
+
241
+ export function isCurrentBatchDone(): boolean {
242
+ if (activePlans.size === 0) return false
243
+ for (const plan of activePlans.values()) {
244
+ if (isPlanBatchDone(plan)) return true
245
+ }
246
+ return false
247
+ }
248
+
249
+ export function getActivePlan(): ProjectPlan | null {
250
+ if (!lastPlanName) return null
251
+ return activePlans.get(lastPlanName) ?? null
252
+ }
253
+
254
+ export function getAllActivePlans(): ProjectPlan[] {
255
+ return Array.from(activePlans.values())
256
+ }
257
+
258
+ export interface WhiteboardPlanSnapshot {
259
+ name: string
260
+ type: string
261
+ steps: Array<{
262
+ id: string
263
+ description: string
264
+ agents: Array<{ name: string; status: string }>
265
+ }>
266
+ }
267
+
268
+ export function snapshotPlansForDisplay(sessionId?: string): WhiteboardPlanSnapshot[] {
269
+ const result: WhiteboardPlanSnapshot[] = []
270
+ for (const plan of activePlans.values()) {
271
+ if (sessionId && plan.townSessionId !== sessionId) continue
272
+ result.push({
273
+ name: plan.name,
274
+ type: plan.type,
275
+ steps: plan.steps
276
+ .filter(s => s.id !== COMPLETE_STEP_ID)
277
+ .map(s => ({
278
+ id: s.id,
279
+ description: s.description,
280
+ agents: s.agents.map(a => ({ name: a.name, status: a.status })),
281
+ })),
282
+ })
283
+ }
284
+ return result
285
+ }
286
+
287
+ export function isPlanFullyComplete(): boolean {
288
+ if (hasActiveTasks()) return false
289
+ if (activePlans.size === 0) return true
290
+ for (const plan of activePlans.values()) {
291
+ const allDone = plan.steps
292
+ .filter(s => s.id !== COMPLETE_STEP_ID)
293
+ .every(s => s.agents.every(a => a.status === 'completed' || a.status === 'failed'))
294
+ if (!allDone) return false
295
+ }
296
+ return true
297
+ }
298
+
299
+ function isPlanBatchDone(plan: ProjectPlan): boolean {
300
+ const startedBatches = new Set(
301
+ plan.steps
302
+ .filter(s => !isCompleteStep(s) && isStepStarted(s))
303
+ .map(s => s.batch),
304
+ )
305
+ for (const b of startedBatches) {
306
+ const batchSteps = plan.steps.filter(s => s.batch === b && !isCompleteStep(s))
307
+ if (batchSteps.every(s => isStepDone(s))) return true
308
+ }
309
+ return false
310
+ }
311
+
312
+ const COMPLETE_STEP_ID = '_complete'
313
+
314
+ function isCompleteStep(step: PlanStep): boolean {
315
+ return step.id === COMPLETE_STEP_ID
316
+ }
317
+
318
+ function isStepDone(step: PlanStep): boolean {
319
+ if (isCompleteStep(step)) return false
320
+ return step.agents.every(a => a.status === 'completed' || a.status === 'failed')
321
+ }
322
+
323
+
324
+ function isStepStarted(step: PlanStep): boolean {
325
+ return step.agents.some(a => a.status !== 'pending')
326
+ }
327
+
328
+ function readUpstreamDocs(projectDir: string): string {
329
+ try {
330
+ if (!existsSync(projectDir)) return ''
331
+ const docNames = ['MODULES.md', 'SPEC.md', 'visual.md', 'DESIGN.md', 'STYLE.md']
332
+ const parts: string[] = []
333
+ for (const name of docNames) {
334
+ const p = join(projectDir, name)
335
+ if (existsSync(p)) {
336
+ try {
337
+ const content = readFileSync(p, 'utf-8').trim()
338
+ if (content) parts.push(`### ${name}\n${content}`)
339
+ } catch {}
340
+ }
341
+ }
342
+ return parts.join('\n\n')
343
+ } catch {
344
+ return ''
345
+ }
346
+ }
347
+
348
+ function isLaterBatch(plan: ProjectPlan, stepIndex: number): boolean {
349
+ const step = plan.steps[stepIndex]
350
+ if (!step || isCompleteStep(step)) return false
351
+ const businessSteps = plan.steps.filter(s => !isCompleteStep(s))
352
+ if (businessSteps.length <= 2) return false
353
+ const minBatch = Math.min(...businessSteps.map(s => s.batch))
354
+ return step.batch > minBatch + 1
355
+ }
356
+
357
+ function isLastBusinessBatch(plan: ProjectPlan, stepIndex: number): boolean {
358
+ const step = plan.steps[stepIndex]
359
+ if (!step || isCompleteStep(step)) return false
360
+ const businessBatches = new Set(plan.steps.filter(s => !isCompleteStep(s)).map(s => s.batch))
361
+ return step.batch === Math.max(...businessBatches)
362
+ }
363
+
364
+ function buildEnrichedTask(agent: PlanAgent, plan: ProjectPlan, stepIndex: number): string {
365
+ const citizen = plan.citizenRoster.get(agent.name ?? '')
366
+ const sections: string[] = []
367
+
368
+ if (citizen) {
369
+ sections.push(
370
+ `## 角色\n你是${agent.name},${citizen.role}。\n` +
371
+ `请先阅读你的角色人设文件了解自己的性格和专长:\n${citizen.soulFilePath}`,
372
+ )
373
+ }
374
+
375
+ sections.push(
376
+ `## 项目\n项目目录: ${plan.projectDir}\n` +
377
+ `请先阅读项目说明文件了解项目背景和前序工作: ${plan.projectDir}/PROJECT.md`,
378
+ )
379
+
380
+ sections.push(`## 任务\n${agent.task}`)
381
+
382
+ if (agent.files && agent.files.length > 0) {
383
+ const fileList = agent.files
384
+ .map(f => (f.startsWith('/') ? f : `${plan.projectDir}/${f}`))
385
+ .map(f => `- ${f}`)
386
+ .join('\n')
387
+ sections.push(
388
+ `## 文件边界(强制)\n` +
389
+ `你只能创建或修改以下目录/文件中的内容:\n${fileList}\n\n` +
390
+ `【红线】严禁修改上述范围以外的任何文件。如需与其他模块对接,在自己的目录内创建新文件。`,
391
+ )
392
+ }
393
+
394
+ const upstreamDocs = readUpstreamDocs(plan.projectDir)
395
+
396
+ if (isLastBusinessBatch(plan, stepIndex) && upstreamDocs) {
397
+ sections.push(
398
+ `## 上游产出(验收依据)\n${upstreamDocs}\n\n` +
399
+ `## 验收清单(必须逐项完成)\n` +
400
+ `1. 阅读设计文档(SPEC.md / DESIGN.md),逐条核对功能是否实现\n` +
401
+ `2. 打开入口文件(index.html),确认页面能正常加载无报错\n` +
402
+ `3. 操作核心功能,确认可交互、无白屏、无控制台报错\n` +
403
+ `4. 若有视觉规范(visual.md / STYLE.md),核对配色、字体、布局是否符合\n` +
404
+ `5. 检查各模块的 import/引用路径是否正确连通\n` +
405
+ `6. 发现问题必须修复,不能只报告不动手——你是最后一道关`,
406
+ )
407
+ } else if (isLaterBatch(plan, stepIndex) && upstreamDocs) {
408
+ sections.push(
409
+ `## 上游产出(必须基于这些文档开发)\n${upstreamDocs}\n\n` +
410
+ `- 必须在骨架已有的目录结构上开发,禁止创建模块定义中未定义的新目录\n` +
411
+ `- 按模块定义中的分工建议,在对应目录下实现功能`,
412
+ )
413
+ }
414
+
415
+ sections.push(
416
+ `## 通用规则\n` +
417
+ `- 所有产出必须用 write/edit 工具写入项目目录(${plan.projectDir})中的文件,不要直接输出到对话中\n` +
418
+ `- 开始前先查看项目目录结构和 PROJECT.md\n` +
419
+ `- 新建文件、整文件重写、大段内容生成时优先使用 write;只有在小范围精确替换已有内容时才使用 edit\n` +
420
+ `- 使用 edit 前必须先 read 目标文件;edit 时统一使用 old_string/new_string,不要混用 oldText/newText 或 oldString/newText 等参数名\n` +
421
+ `- old_string 必须直接复制自刚刚 read 到的原文,必须与文件内容完全一致,包括空格、缩进和换行;如果做不到精确匹配,就改用 write 重写文件\n` +
422
+ `- 完成修改后,如有必要再 read 一次确认结果,不要凭记忆假设编辑已经成功`,
423
+ )
424
+
425
+ return sections.join('\n\n')
426
+ }
427
+
428
+ function formatPlanCreated(plan: ProjectPlan): string {
429
+ const lines: string[] = [
430
+ `Plan created: "${plan.name}" (${plan.type}), ${plan.steps.length} steps.`,
431
+ `Project directory: ${plan.projectDir}`,
432
+ '',
433
+ ]
434
+
435
+ for (let i = 0; i < plan.steps.length; i++) {
436
+ const step = plan.steps[i]
437
+ const num = circledNumber(i + 1)
438
+ if (isCompleteStep(step)) {
439
+ lines.push(`${num} ${step.description}`)
440
+ } else {
441
+ const agentNames = step.agents.map(a => a.name).join(', ')
442
+ lines.push(`${num} [batch ${step.batch}] ${step.description} — ${agentNames}`)
443
+ }
444
+ }
445
+
446
+ const firstBatch = getFirstBatchIndices(plan)
447
+ const batchDesc = firstBatch.map(i => circledNumber(i + 1)).join('')
448
+
449
+ lines.push('')
450
+ lines.push(`Start step ${batchDesc} now. After each batch completes, call next_step() for instructions.`)
451
+ lines.push('')
452
+ lines.push('IMPORTANT: When spawning agents, copy the enriched task from next_step() output as-is — it includes role persona, project path, and file boundaries.')
453
+
454
+ return lines.join('\n')
455
+ }
456
+
457
+ function formatNextStep(plan: ProjectPlan): string {
458
+ const doneSteps: number[] = []
459
+ const inProgressSteps: number[] = []
460
+ const pendingSteps: number[] = []
461
+
462
+ for (let i = 0; i < plan.steps.length; i++) {
463
+ const step = plan.steps[i]
464
+ if (isStepDone(step)) doneSteps.push(i)
465
+ else if (isStepStarted(step)) inProgressSteps.push(i)
466
+ else pendingSteps.push(i)
467
+ }
468
+
469
+ if (inProgressSteps.length > 0) {
470
+ const lines: string[] = []
471
+ for (const i of inProgressSteps) {
472
+ const step = plan.steps[i]
473
+ const done = step.agents.filter(a => a.status === 'completed' || a.status === 'failed').length
474
+ const waiting = step.agents.filter(a => a.status === 'running' || a.status === 'pending').map(a => a.name)
475
+ lines.push(`Step ${circledNumber(i + 1)} "${step.description}": ${done}/${step.agents.length} done. Waiting: ${waiting.join(', ')}`)
476
+ }
477
+ lines.push('')
478
+ lines.push('Current batch still in progress. Wait for agents to finish, then call next_step() again.')
479
+ return lines.join('\n')
480
+ }
481
+
482
+ if (pendingSteps.length === 0) {
483
+ return formatAllDone(plan)
484
+ }
485
+
486
+ const nextBatch = getNextBatchIndices(plan)
487
+ if (nextBatch.length === 0) {
488
+ return formatAllDone(plan)
489
+ }
490
+
491
+ if (nextBatch.length === 1 && isCompleteStep(plan.steps[nextBatch[0]])) {
492
+ return formatAllDone(plan)
493
+ }
494
+
495
+ const lines: string[] = []
496
+
497
+ if (doneSteps.length > 0) {
498
+ const doneLabels = doneSteps.map(i => `${circledNumber(i + 1)} ${plan.steps[i].description}`).join(', ')
499
+ lines.push(`✅ Done: ${doneLabels}`)
500
+ lines.push('')
501
+ lines.push(
502
+ `⚠️ 在 spawn 下一批之前,先更新 ${plan.projectDir}/PROJECT.md 的「变更记录」,` +
503
+ `记录刚完成的步骤产出了哪些文件、做了什么。这样下一批居民才能了解前序工作。`,
504
+ )
505
+ lines.push('')
506
+ }
507
+
508
+ lines.push(`→ Next: step ${nextBatch.map(i => circledNumber(i + 1)).join('')}`)
509
+ lines.push('')
510
+
511
+ for (const i of nextBatch) {
512
+ const step = plan.steps[i]
513
+ if (isCompleteStep(step)) continue
514
+ lines.push(` Step ${circledNumber(i + 1)} "${step.description}":`)
515
+ for (const agent of step.agents) {
516
+ const enrichedTask = buildEnrichedTask(agent, plan, i)
517
+ if (isLabelBusy(agent.name ?? '')) {
518
+ lines.push(` ⚠️ ${agent.name} is busy — spawn WITHOUT label (temp worker):`)
519
+ lines.push(` → sessions_spawn({ task: ${JSON.stringify(enrichedTask)} })`)
520
+ } else {
521
+ lines.push(` → sessions_spawn({ label: "${agent.name}", task: ${JSON.stringify(enrichedTask)} })`)
522
+ }
523
+ }
524
+ lines.push('')
525
+ }
526
+
527
+ const remainingWork = plan.steps.filter((s, i) => !isCompleteStep(s) && !doneSteps.includes(i) && !nextBatch.includes(i)).length
528
+ if (remainingWork > 0) {
529
+ lines.push(`After this batch: ${remainingWork} more step(s), then mission_complete.`)
530
+ } else {
531
+ lines.push('This is the last batch before mission_complete. After it completes, call next_step().')
532
+ }
533
+
534
+ return lines.join('\n')
535
+ }
536
+
537
+ function formatAllDone(plan: ProjectPlan): string {
538
+ const hasFailures = plan.steps.some(s => !isCompleteStep(s) && s.agents.some(a => a.status === 'failed'))
539
+
540
+ const lines: string[] = [
541
+ '✅ All steps completed!',
542
+ '',
543
+ ]
544
+
545
+ if (hasFailures) {
546
+ lines.push('⚠️ Some agents failed. Review results before completing.')
547
+ lines.push('')
548
+ }
549
+
550
+ if (['game', 'app', 'website'].includes(plan.type)) {
551
+ lines.push(
552
+ `Call mission_complete({ type: "${plan.type}", name: "${plan.name}", summary: "brief summary of what was built", files: ["${plan.projectDir}/index.html"] }) NOW.`,
553
+ )
554
+ } else if (plan.type === 'media' || plan.type === 'files') {
555
+ lines.push(
556
+ `Call mission_complete({ type: "${plan.type}", name: "${plan.name}", summary: "brief summary", files: ["path/to/output"] }) NOW.`,
557
+ )
558
+ } else {
559
+ lines.push(
560
+ `Call mission_complete({ type: "${plan.type}", name: "${plan.name}", summary: "brief summary" }) NOW.`,
561
+ )
562
+ }
563
+
564
+ return lines.join('\n')
565
+ }
566
+
567
+ function getFirstBatchIndices(plan: ProjectPlan): number[] {
568
+ const businessSteps = plan.steps.filter(s => !isCompleteStep(s))
569
+ if (businessSteps.length === 0) return []
570
+ const minBatch = Math.min(...businessSteps.map(s => s.batch))
571
+ return plan.steps
572
+ .map((s, i) => ({ s, i }))
573
+ .filter(({ s }) => !isCompleteStep(s) && s.batch === minBatch)
574
+ .map(({ i }) => i)
575
+ }
576
+
577
+ function getNextBatchIndices(plan: ProjectPlan): number[] {
578
+ const pendingSteps = plan.steps.filter(s => !isCompleteStep(s) && !isStepDone(s))
579
+ if (pendingSteps.length === 0) return []
580
+ const minPendingBatch = Math.min(...pendingSteps.map(s => s.batch))
581
+ const allLowerDone = plan.steps
582
+ .filter(s => !isCompleteStep(s) && s.batch < minPendingBatch)
583
+ .every(s => isStepDone(s))
584
+ if (!allLowerDone) return []
585
+ return plan.steps
586
+ .map((s, i) => ({ s, i }))
587
+ .filter(({ s }) => !isCompleteStep(s) && s.batch === minPendingBatch && !isStepDone(s))
588
+ .map(({ i }) => i)
589
+ }
590
+
591
+ function circledNumber(n: number): string {
592
+ const nums = ['①', '②', '③', '④', '⑤', '⑥', '⑦', '⑧', '⑨', '⑩']
593
+ return nums[n - 1] ?? `(${n})`
594
+ }
@@ -0,0 +1,9 @@
1
+ import { createPluginRuntimeStore } from "openclaw/plugin-sdk";
2
+ import type { PluginRuntime } from "openclaw/plugin-sdk";
3
+
4
+ const { setRuntime: setTownRuntime, getRuntime: getTownRuntime } =
5
+ createPluginRuntimeStore<PluginRuntime>(
6
+ "Agentshire runtime not initialized — plugin not registered",
7
+ );
8
+
9
+ export { setTownRuntime, getTownRuntime };