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,205 @@
1
+ import { join } from "node:path";
2
+ import { readFileSync, existsSync } from "node:fs";
3
+ import { SessionLogWatcher } from "./session-log-watcher.js";
4
+ import { broadcastAgentEvent } from "./ws-server.js";
5
+ import type { AgentEvent } from "../contracts/events.js";
6
+ import { stateDir } from "./paths.js";
7
+
8
+ const TOWN_AGENT_ID = "town-steward";
9
+
10
+ function getSessionsDir(): string {
11
+ return join(stateDir(), "agents", TOWN_AGENT_ID, "sessions");
12
+ }
13
+
14
+ function resolveSessionFileId(childSessionKey: string): string | null {
15
+ try {
16
+ const indexPath = join(getSessionsDir(), "sessions.json");
17
+ if (!existsSync(indexPath)) return null;
18
+ const index = JSON.parse(readFileSync(indexPath, "utf-8"));
19
+ const entry = index[childSessionKey];
20
+ if (entry?.sessionId) return entry.sessionId;
21
+ } catch {}
22
+ return null;
23
+ }
24
+
25
+ interface TrackedAgent {
26
+ childSessionKey: string;
27
+ sessionId: string;
28
+ label: string;
29
+ townSessionId: string | undefined;
30
+ watcher: SessionLogWatcher;
31
+ }
32
+
33
+ const tracked = new Map<string, TrackedAgent>();
34
+
35
+ export function onSubagentSpawned(
36
+ event: Record<string, unknown>,
37
+ townSessionId: string | undefined,
38
+ onFallbackComplete?: (label: string, townSessionId: string | undefined) => void,
39
+ ): void {
40
+ const childSessionKey = event.childSessionKey as string | undefined;
41
+ if (!childSessionKey) return;
42
+
43
+ const sessionFileId = resolveSessionFileId(childSessionKey);
44
+ if (!sessionFileId) {
45
+ console.log(`[agentshire] ⚠️ Could not resolve session file for ${childSessionKey}`);
46
+ return;
47
+ }
48
+
49
+ const label = String(event.label ?? event.displayName ?? sessionFileId);
50
+ const filePath = join(getSessionsDir(), `${sessionFileId}.jsonl`);
51
+
52
+ const watcher = new SessionLogWatcher(filePath, childSessionKey, (events) => {
53
+ for (const ev of events) {
54
+ broadcastAgentEvent(ev, townSessionId);
55
+ }
56
+ }, (reason) => {
57
+ const entry = tracked.get(childSessionKey);
58
+ if (entry) {
59
+ console.log(`[agentshire] ⚠️ Watcher detected session end (${reason}) for "${entry.label}" but subagent_ended hook not received. Triggering fallback.`);
60
+ entry.watcher.markComplete();
61
+ entry.watcher.stop();
62
+ tracked.delete(childSessionKey);
63
+ onFallbackComplete?.(entry.label, entry.townSessionId);
64
+ }
65
+ });
66
+
67
+ tracked.set(childSessionKey, { childSessionKey, sessionId: sessionFileId, label, townSessionId, watcher });
68
+ watcher.start();
69
+ console.log(`[agentshire] 📖 Session watcher started: ${label} (${sessionFileId.slice(0, 8)}…)`);
70
+ }
71
+
72
+ export function onSubagentEnded(event: Record<string, unknown>): void {
73
+ const childKey = (event.targetSessionKey ?? event.childSessionKey) as string | undefined;
74
+ if (!childKey) return;
75
+
76
+ const entry = tracked.get(childKey);
77
+ if (entry) {
78
+ entry.watcher.markComplete();
79
+ entry.watcher.stop();
80
+ tracked.delete(childKey);
81
+ console.log(`[agentshire] 📖 Session watcher stopped: ${entry.label} (${entry.sessionId.slice(0, 8)}…)`);
82
+ }
83
+ }
84
+
85
+ export function getLabelForSession(event: Record<string, unknown>): string | undefined {
86
+ const childKey = (event.targetSessionKey ?? event.childSessionKey) as string | undefined;
87
+ if (!childKey) return undefined;
88
+ return tracked.get(childKey)?.label;
89
+ }
90
+
91
+ export function stopAll(): void {
92
+ for (const [key, entry] of tracked) {
93
+ entry.watcher.stop();
94
+ tracked.delete(key);
95
+ }
96
+ }
97
+
98
+ export function getTrackedCount(): number {
99
+ return tracked.size;
100
+ }
101
+
102
+ export function hasRunningSubagents(): boolean {
103
+ return tracked.size > 0;
104
+ }
105
+
106
+ export function isLabelBusy(label: string): boolean {
107
+ const normalized = label.trim().toLowerCase();
108
+ for (const entry of tracked.values()) {
109
+ if (entry.label.trim().toLowerCase() === normalized) return true;
110
+ }
111
+ return false;
112
+ }
113
+
114
+ export interface ActivityLogEntry {
115
+ name: string;
116
+ input: Record<string, unknown>;
117
+ output?: string;
118
+ success?: boolean;
119
+ }
120
+
121
+ export function getActivityLogForAgent(agentId: string): ActivityLogEntry[] {
122
+ const entry = tracked.get(agentId);
123
+ if (!entry) {
124
+ const sessionFileId = resolveSessionFileId(agentId);
125
+ if (!sessionFileId) return [];
126
+ const filePath = join(getSessionsDir(), `${sessionFileId}.jsonl`);
127
+ if (!existsSync(filePath)) return [];
128
+ return parseActivityLog(filePath);
129
+ }
130
+ const filePath = join(getSessionsDir(), `${entry.sessionId}.jsonl`);
131
+ if (!existsSync(filePath)) return [];
132
+ return parseActivityLog(filePath);
133
+ }
134
+
135
+ function parseActivityLog(filePath: string): ActivityLogEntry[] {
136
+ const entries: ActivityLogEntry[] = [];
137
+ try {
138
+ const content = readFileSync(filePath, "utf-8");
139
+ const lines = content.split("\n");
140
+ const pendingTools = new Map<string, { name: string; input: Record<string, unknown> }>();
141
+
142
+ for (const line of lines) {
143
+ const trimmed = line.trim();
144
+ if (!trimmed) continue;
145
+ let record: any;
146
+ try { record = JSON.parse(trimmed); } catch { continue; }
147
+ if (record.type !== "message") continue;
148
+ const msg = record.message;
149
+ if (!msg || !Array.isArray(msg.content)) continue;
150
+
151
+ if (msg.role === "assistant") {
152
+ for (const [id, info] of pendingTools) {
153
+ entries.push({ name: info.name, input: info.input, output: "(abandoned)", success: false });
154
+ }
155
+ pendingTools.clear();
156
+ }
157
+
158
+ for (const block of msg.content) {
159
+ if (block.type === "toolCall") {
160
+ const name = block.name ?? "unknown";
161
+ if (name === "__thinking__" || name === "__thinking_placeholder__") continue;
162
+ const args = block.arguments ?? {};
163
+ const toolId = block.id ?? `tool-${Date.now()}`;
164
+ pendingTools.set(toolId, { name, input: args });
165
+ entries.push({ name, input: args });
166
+ }
167
+
168
+ if (block.type === "text" && msg.role === "toolResult") {
169
+ const resultText = block.text ?? "";
170
+ let matchedId: string | undefined;
171
+ let matchedInfo: { name: string; input: Record<string, unknown> } | undefined;
172
+ for (const [id, info] of pendingTools) { matchedId = id; matchedInfo = info; break; }
173
+ if (matchedId) pendingTools.delete(matchedId);
174
+
175
+ if (matchedInfo) {
176
+ let last: ActivityLogEntry | undefined;
177
+ for (let j = entries.length - 1; j >= 0; j--) {
178
+ if (entries[j].name === matchedInfo.name && entries[j].success === undefined) {
179
+ last = entries[j]; break;
180
+ }
181
+ }
182
+ if (last) {
183
+ last.output = resultText.slice(0, 200);
184
+ last.success = isToolSuccess(matchedInfo.name, resultText);
185
+ }
186
+ }
187
+ }
188
+ }
189
+ }
190
+ } catch (err) {
191
+ console.warn("[agentshire] Failed to parse activity log:", (err as Error).message);
192
+ }
193
+ return entries;
194
+ }
195
+
196
+ function isToolSuccess(name: string, output: string): boolean {
197
+ if (["read", "read_file", "grep", "glob"].includes(name)) return true;
198
+ if (["write", "write_file", "edit", "edit_file"].includes(name)) return /^Successfully/.test(output);
199
+ const errorPatterns = [
200
+ /^Error:/i, /^node:/, /EADDRINUSE/, /ENOENT/, /EACCES/,
201
+ /throw\s+er;/, /command not found/i, /permission denied/i,
202
+ /^fatal:/i, /^SyntaxError:/, /^TypeError:/, /^ReferenceError:/,
203
+ ];
204
+ return !errorPatterns.some(p => p.test(output));
205
+ }
@@ -0,0 +1,493 @@
1
+ // @ts-ignore — OpenClawPluginToolFactory not re-exported from SDK barrel
2
+ import type { OpenClawPluginToolFactory } from "openclaw/plugin-sdk";
3
+ import { mkdirSync, existsSync, readFileSync, writeFileSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { broadcastAgentEvent } from "./ws-server.js";
7
+ import { createPlan, getNextStepInstruction, getActivePlan, isPlanFullyComplete, hasActiveTasks, clearTasks, completePlan } from "./plan-manager.js";
8
+ import type { CitizenRosterEntry } from "./plan-manager.js";
9
+ import { hasRunningSubagents } from "./subagent-tracker.js";
10
+ import { resolveFileData } from "./outbound-adapter.js";
11
+ import { stateDir } from "./paths.js";
12
+
13
+ function repairJson(s: string): string {
14
+ let trimmed = s.trim()
15
+ if (!trimmed) return trimmed
16
+ const opens = { '{': '}', '[': ']' } as Record<string, string>
17
+ const closes = new Set(['}', ']'])
18
+ const stack: string[] = []
19
+ let inString = false
20
+ let escape = false
21
+ for (const ch of trimmed) {
22
+ if (escape) { escape = false; continue }
23
+ if (ch === '\\') { escape = true; continue }
24
+ if (ch === '"') { inString = !inString; continue }
25
+ if (inString) continue
26
+ if (opens[ch]) stack.push(opens[ch])
27
+ else if (closes.has(ch)) { if (stack.length && stack[stack.length - 1] === ch) stack.pop() }
28
+ }
29
+ while (stack.length) trimmed += stack.pop()
30
+ return trimmed
31
+ }
32
+
33
+ function getPluginDir(): string {
34
+ return join(fileURLToPath(import.meta.url), "../../..");
35
+ }
36
+
37
+ function getStewardWorkspaceDir(): string {
38
+ return join(stateDir(), "workspace-town-steward");
39
+ }
40
+
41
+ function loadCitizenRoster(): Map<string, CitizenRosterEntry> {
42
+ const roster = new Map<string, CitizenRosterEntry>();
43
+ try {
44
+ const configPath = join(getStewardWorkspaceDir(), "town-defaults.json");
45
+ if (!existsSync(configPath)) return roster;
46
+ const config = JSON.parse(readFileSync(configPath, "utf-8"));
47
+ const pluginDir = getPluginDir();
48
+ for (const c of config.citizens ?? []) {
49
+ roster.set(c.name, {
50
+ specialty: c.specialty ?? "general",
51
+ role: c.role ?? c.specialty ?? "通用",
52
+ bio: c.bio ?? "",
53
+ soulFilePath: c.personaFile
54
+ ? (c.personaFile.startsWith("/") ? c.personaFile : join(pluginDir, c.personaFile))
55
+ : "",
56
+ });
57
+ }
58
+ } catch (e) { console.debug('[tools] roster load:', e); }
59
+ return roster;
60
+ }
61
+
62
+ /**
63
+ * Tools registered to the LLM agent, allowing AI to control the 3D town.
64
+ */
65
+ export function createTownTools(): OpenClawPluginToolFactory {
66
+ return () => [
67
+ {
68
+ name: "town_announce",
69
+ description:
70
+ "Broadcast a message to the 3D agentshire. The steward NPC will display it as a chat bubble.",
71
+ parameters: {
72
+ type: "object" as const,
73
+ properties: {
74
+ message: { type: "string", description: "The message to announce" },
75
+ },
76
+ required: ["message"],
77
+ },
78
+ async execute(_id: string, { message }: { message: string }) {
79
+ broadcastAgentEvent({
80
+ type: "text",
81
+ content: message,
82
+ });
83
+ return `Announced in town: "${message}"`;
84
+ },
85
+ },
86
+ {
87
+ name: "town_spawn_npc",
88
+ description:
89
+ "Spawn a new NPC in the 3D agentshire, representing a new agent or character.",
90
+ parameters: {
91
+ type: "object" as const,
92
+ properties: {
93
+ npcId: { type: "string", description: "Unique NPC identifier" },
94
+ name: { type: "string", description: "Display name for the NPC" },
95
+ role: {
96
+ type: "string",
97
+ description: "NPC role (e.g. developer, designer, researcher)",
98
+ },
99
+ },
100
+ required: ["npcId", "name"],
101
+ },
102
+ async execute(_id: string, { npcId, name, role }: { npcId: string; name: string; role?: string }) {
103
+ broadcastAgentEvent({
104
+ type: "sub_agent",
105
+ subtype: "started",
106
+ agentId: npcId,
107
+ agentType: role ?? "worker",
108
+ parentToolUseId: "",
109
+ task: `${name} joined the town`,
110
+ model: "unknown",
111
+ displayName: name,
112
+ });
113
+ return `NPC "${name}" (${npcId}) spawned in town`;
114
+ },
115
+ },
116
+ {
117
+ name: "town_effect",
118
+ description:
119
+ "Trigger a visual effect in the 3D Agentshire (celebration, ripple, etc.)",
120
+ parameters: {
121
+ type: "object" as const,
122
+ properties: {
123
+ effect: {
124
+ type: "string",
125
+ enum: ["celebration", "ripple", "fireworks", "glow"],
126
+ description: "Effect type",
127
+ },
128
+ },
129
+ required: ["effect"],
130
+ },
131
+ async execute(_id: string, { effect }: { effect: string }) {
132
+ broadcastAgentEvent({
133
+ type: 'fx',
134
+ effect,
135
+ params: { intensity: 1.0 },
136
+ } as any);
137
+ return `Effect "${effect}" triggered in town`;
138
+ },
139
+ },
140
+ {
141
+ name: "town_set_time",
142
+ description:
143
+ "Control the game clock in the 3D town. " +
144
+ "action=set: jump to a specific hour (0-23). " +
145
+ "action=pause: freeze time. " +
146
+ "action=resume: resume normal time flow (恢复时间).",
147
+ parameters: {
148
+ type: "object" as const,
149
+ properties: {
150
+ action: {
151
+ type: "string",
152
+ enum: ["set", "pause", "resume"],
153
+ description: "set=jump to hour, pause=freeze, resume=restore normal flow",
154
+ },
155
+ hour: { type: "number", description: "Hour of day (0-23), required when action=set" },
156
+ },
157
+ required: ["action"],
158
+ },
159
+ async execute(_id: string, { action, hour }: { action: string; hour?: number }) {
160
+ broadcastAgentEvent({
161
+ type: "world_control",
162
+ target: "time",
163
+ action,
164
+ hour: action === "set" && hour != null ? Math.max(0, Math.min(23, Math.round(hour))) : undefined,
165
+ } as any);
166
+ if (action === "set") return `Game time set to ${hour}:00`;
167
+ if (action === "pause") return `Game time paused`;
168
+ return `Game time resumed to normal flow`;
169
+ },
170
+ },
171
+ {
172
+ name: "town_set_weather",
173
+ description:
174
+ "Control the weather in the 3D town. " +
175
+ "action=set: force a specific weather type. " +
176
+ "action=reset: restore automatic weather cycle (恢复天气). " +
177
+ "Available weather types: clear, cloudy, drizzle, rain, heavyRain, storm, " +
178
+ "lightSnow, snow, blizzard, fog, sandstorm, aurora.",
179
+ parameters: {
180
+ type: "object" as const,
181
+ properties: {
182
+ action: {
183
+ type: "string",
184
+ enum: ["set", "reset"],
185
+ description: "set=force weather, reset=restore automatic weather cycle",
186
+ },
187
+ weather: {
188
+ type: "string",
189
+ enum: ["clear", "cloudy", "drizzle", "rain", "heavyRain", "storm",
190
+ "lightSnow", "snow", "blizzard", "fog", "sandstorm", "aurora"],
191
+ description: "Weather type, required when action=set",
192
+ },
193
+ },
194
+ required: ["action"],
195
+ },
196
+ async execute(_id: string, { action, weather }: { action: string; weather?: string }) {
197
+ broadcastAgentEvent({
198
+ type: "world_control",
199
+ target: "weather",
200
+ action,
201
+ weather: action === "set" ? weather : undefined,
202
+ } as any);
203
+ if (action === "reset") return `Weather restored to automatic cycle`;
204
+ return `Weather changed to "${weather}"`;
205
+ },
206
+ },
207
+ {
208
+ name: "town_status",
209
+ description: "Get current status of the 3D Agentshire (connected clients, etc.)",
210
+ parameters: { type: "object" as const, properties: {} },
211
+ async execute() {
212
+ const { getConnectedClientCount } = await import("./ws-server.js");
213
+ const count = getConnectedClientCount();
214
+ return `Town status: ${count} frontend(s) connected`;
215
+ },
216
+ },
217
+ {
218
+ name: "register_project",
219
+ description:
220
+ "Register a simple project (single-agent or steward-only). " +
221
+ "For complex multi-agent projects, use create_plan instead — it registers the project automatically.",
222
+ parameters: {
223
+ type: "object" as const,
224
+ properties: {
225
+ name: { type: "string", description: "Human-readable project name (e.g. \"飞机大战\", \"公司官网\")" },
226
+ type: {
227
+ type: "string",
228
+ enum: ["game", "app", "website", "media", "files", "operation"],
229
+ description: "Project category: game, app, website, media, files, or operation",
230
+ },
231
+ },
232
+ required: ["name", "type"],
233
+ },
234
+ async execute(_id: string, { name, type }: { name: string; type: string }) {
235
+ return `Project registered: "${name}" (${type})`;
236
+ },
237
+ },
238
+ {
239
+ name: "create_project",
240
+ description:
241
+ "Create a project directory for a multi-agent project. " +
242
+ "Call this BEFORE create_plan. Returns the absolute path to use as projectDir in create_plan. " +
243
+ "The directory is created under the steward's workspace/projects/ with a timestamp suffix to avoid conflicts.",
244
+ parameters: {
245
+ type: "object" as const,
246
+ properties: {
247
+ name: { type: "string", description: "Project name in English (e.g. 'aircraft-war', 'company-website'). Will be sanitized for filesystem." },
248
+ },
249
+ required: ["name"],
250
+ },
251
+ async execute(_id: string, { name }: { name: string }) {
252
+ const sanitized = name.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").toLowerCase();
253
+ const timestamp = Date.now().toString(36);
254
+ const dirName = `${sanitized}-${timestamp}`;
255
+ const projectsDir = join(getStewardWorkspaceDir(), "projects");
256
+ const projectDir = join(projectsDir, dirName);
257
+ try {
258
+ mkdirSync(projectDir, { recursive: true });
259
+ } catch (err) {
260
+ return `Error: Failed to create project directory: ${(err as Error).message}`;
261
+ }
262
+ return `Project directory created: ${projectDir}\n\nUse this path as projectDir when calling create_plan.`;
263
+ },
264
+ },
265
+ {
266
+ name: "create_task",
267
+ description:
268
+ "Create a task directory for a single-agent delegation. " +
269
+ "Call this BEFORE create_plan. Returns the absolute path to use as projectDir in create_plan. " +
270
+ "The directory is created under the steward's workspace/tasks/ with a timestamp suffix.",
271
+ parameters: {
272
+ type: "object" as const,
273
+ properties: {
274
+ name: { type: "string", description: "Task name in English (e.g. 'product-report', 'video-script'). Will be sanitized for filesystem." },
275
+ },
276
+ required: ["name"],
277
+ },
278
+ async execute(_id: string, { name }: { name: string }) {
279
+ const sanitized = name.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").toLowerCase();
280
+ const timestamp = Date.now().toString(36);
281
+ const dirName = `${sanitized}-${timestamp}`;
282
+ const tasksDir = join(getStewardWorkspaceDir(), "tasks");
283
+ const taskDir = join(tasksDir, dirName);
284
+ try {
285
+ mkdirSync(taskDir, { recursive: true });
286
+ } catch (err) {
287
+ return `Error: Failed to create task directory: ${(err as Error).message}`;
288
+ }
289
+ return `Task directory created: ${taskDir}\n\nUse this path as projectDir when calling create_plan.`;
290
+ },
291
+ },
292
+ {
293
+ name: "create_plan",
294
+ description:
295
+ "Create a structured execution plan for a multi-agent project or single-agent task. " +
296
+ "Call create_project or create_task FIRST to get a projectDir, then call this. " +
297
+ "Each step has agents, tasks, and a batch number. Steps with the same batch number run concurrently; lower batches run first and must all complete before the next batch starts. " +
298
+ "File boundaries (files field) are OPTIONAL — the scaffold phase will define module structure and dev task allocation in MODULES.md; " +
299
+ "the system automatically injects upstream docs (MODULES.md, SPEC.md, visual.md) into develop and integrate tasks. " +
300
+ "If you do provide files, they must be non-overlapping across agents in the same step. " +
301
+ "After spawning each batch, wait for agents to finish, then call next_step() for instructions. " +
302
+ "The final next_step() call will tell you to call mission_complete.",
303
+ parameters: {
304
+ type: "object" as const,
305
+ properties: {
306
+ name: { type: "string", description: "Project name (e.g. \"飞机大战\")" },
307
+ type: {
308
+ type: "string",
309
+ enum: ["game", "app", "website", "media", "files", "operation"],
310
+ description: "Project category",
311
+ },
312
+ projectDir: { type: "string", description: "Absolute path to project/task directory (from create_project or create_task)" },
313
+ steps: {
314
+ description: "Ordered execution steps as a JSON array. Can also be passed as a JSON string (will be auto-parsed). " +
315
+ "Each step: { id, description, batch, agents: [{ name, task }] }. " +
316
+ "batch is a positive integer (1, 2, 3...). Same batch = run concurrently. " +
317
+ "files field is optional — scaffold output (MODULES.md) defines file boundaries for develop phase.",
318
+ },
319
+ },
320
+ required: ["name", "type", "projectDir", "steps"],
321
+ },
322
+ async execute(_id: string, args: Record<string, unknown>) {
323
+ const name = String(args.name ?? "").trim();
324
+ const type = String(args.type ?? "");
325
+ const projectDir = String(args.projectDir ?? "").trim();
326
+
327
+ let steps: Array<{
328
+ id: string; description: string;
329
+ agents: Array<{ name?: string; label?: string; task: string; files?: string[] }>;
330
+ batch?: number;
331
+ }>;
332
+ const rawSteps = args.steps;
333
+ if (typeof rawSteps === 'string') {
334
+ try {
335
+ steps = JSON.parse(rawSteps);
336
+ } catch {
337
+ try {
338
+ steps = JSON.parse(repairJson(rawSteps));
339
+ } catch {
340
+ return "Error: steps is a malformed JSON string. Pass steps as a JSON array.";
341
+ }
342
+ }
343
+ } else {
344
+ steps = rawSteps as typeof steps;
345
+ }
346
+
347
+ for (const s of steps) {
348
+ s.agents = s.agents.map(a => ({
349
+ ...a,
350
+ name: (a as any).name ?? (a as any).label ?? '',
351
+ }));
352
+ }
353
+
354
+ if (!name) return "Error: name is required.";
355
+ if (!type) return "Error: type is required.";
356
+ if (!projectDir) return "Error: projectDir is required. Call create_project first.";
357
+ if (!Array.isArray(steps) || steps.length === 0) return "Error: at least one step is required.";
358
+
359
+ const roster = loadCitizenRoster();
360
+ const result = createPlan(name, type, projectDir, roster, steps as any);
361
+
362
+ if (!result.startsWith('Error:')) {
363
+ try {
364
+ const planLines = [`# ${name}`, '', `类型: ${type}`, `目录: ${projectDir}`, '', '## 计划概要', '']
365
+ for (const s of steps) {
366
+ const agentDescs = s.agents.map(a => {
367
+ const entry = roster.get(a.name ?? '')
368
+ return entry ? `${a.name}(${entry.specialty})` : a.name
369
+ }).join(', ')
370
+ planLines.push(`- ${s.id}: ${s.description} — ${agentDescs}`)
371
+ }
372
+ planLines.push('', '## 变更记录', '', '(管家在每批任务完成后更新此处)', '')
373
+ writeFileSync(join(projectDir, 'PROJECT.md'), planLines.join('\n'), 'utf-8')
374
+ } catch {}
375
+ }
376
+
377
+ return result;
378
+ },
379
+ },
380
+ {
381
+ name: "next_step",
382
+ description:
383
+ "Check the current plan progress and get instructions for the next step. " +
384
+ "Call this after each batch of agents finishes. " +
385
+ "Returns: what's done, what to do next (which agents to spawn with what tasks), " +
386
+ "and when all steps are done, tells you to call mission_complete.",
387
+ parameters: { type: "object" as const, properties: {} },
388
+ async execute() {
389
+ return getNextStepInstruction();
390
+ },
391
+ },
392
+ {
393
+ name: "mission_complete",
394
+ description:
395
+ "Unified completion handler — call this when agents finish work. " +
396
+ "Automatically checks if other agents are still running or plan has pending steps. " +
397
+ "All done → triggers departure animation + celebration + deliverable card. " +
398
+ "Still working → sends deliverable card only (no celebration). " +
399
+ "Do NOT call for simple queries or conversational responses — only for tasks that produced tangible deliverables.",
400
+ parameters: {
401
+ type: "object" as const,
402
+ properties: {
403
+ type: {
404
+ type: "string",
405
+ enum: ["game", "app", "website", "media", "files", "operation"],
406
+ description:
407
+ "Deliverable category: " +
408
+ "\"game\" for playable games, " +
409
+ "\"app\" for interactive applications/tools, " +
410
+ "\"website\" for deployed websites/pages, " +
411
+ "\"media\" for produced images/audio/video, " +
412
+ "\"files\" for documents/code deliverables, " +
413
+ "\"operation\" for tasks with no tangible output (e.g. cleanup, config).",
414
+ },
415
+ name: {
416
+ type: "string",
417
+ description: "Human-readable deliverable name (e.g. \"飞机大战\", \"Horse 2048\"). Used for the completion card display.",
418
+ },
419
+ summary: {
420
+ type: "string",
421
+ description: "Brief summary of what was accomplished.",
422
+ },
423
+ files: {
424
+ type: "array",
425
+ items: { type: "string" },
426
+ description:
427
+ "List of output file absolute paths. " +
428
+ "For game/app/website: pass the entry HTML file path (e.g. [\"/path/to/project/index.html\"]). " +
429
+ "For media/files: pass all output file paths. " +
430
+ "Do NOT pass http:// URLs — always use absolute file paths, the system will auto-convert them to servable URLs.",
431
+ },
432
+ },
433
+ required: ["type", "summary"],
434
+ },
435
+ async execute(_id: string, args: Record<string, unknown>) {
436
+ const VALID_TYPES = ["game", "app", "website", "media", "files", "operation"];
437
+ const type = String(args.type ?? "");
438
+ const summary = String(args.summary ?? "").trim();
439
+
440
+ if (!type || !VALID_TYPES.includes(type)) {
441
+ return `Error: type must be one of: ${VALID_TYPES.join(", ")}`;
442
+ }
443
+ if (!summary) {
444
+ return "Error: summary is required.";
445
+ }
446
+
447
+ const running = hasRunningSubagents();
448
+ const planDone = isPlanFullyComplete();
449
+ const routeName = (!running && planDone) ? "project_complete" : "deliver_output";
450
+
451
+ if (routeName === "project_complete") {
452
+ clearTasks();
453
+ completePlan();
454
+ }
455
+
456
+ const enrichedFiles = Array.isArray(args.files)
457
+ ? (args.files as string[]).map((f) => {
458
+ const resolved = resolveFileData(String(f));
459
+ return { path: String(f), ...resolved };
460
+ })
461
+ : [];
462
+
463
+ // For game/app/website, derive URL from the first file entry
464
+ let resolvedUrl: string | undefined;
465
+ if (["game", "app", "website"].includes(type) && enrichedFiles.length > 0) {
466
+ resolvedUrl = enrichedFiles[0].httpUrl || enrichedFiles[0].path;
467
+ }
468
+
469
+ const toolUseId = `mission-${Date.now().toString(36)}`;
470
+ broadcastAgentEvent({
471
+ type: "tool_use",
472
+ toolUseId,
473
+ name: routeName,
474
+ input: { ...args, url: resolvedUrl, _enrichedFiles: enrichedFiles },
475
+ });
476
+ broadcastAgentEvent({
477
+ type: "tool_result",
478
+ toolUseId,
479
+ name: routeName,
480
+ output: `Project marked complete (${type}): ${summary}`,
481
+ });
482
+
483
+ if (routeName === "project_complete") {
484
+ return `Mission complete — project_complete triggered. (${type}): ${summary}`;
485
+ }
486
+ const reasons: string[] = [];
487
+ if (running) reasons.push("other agents still running");
488
+ if (!planDone) reasons.push("plan has remaining steps");
489
+ return `Deliverable sent (${type}): ${summary}. Not yet complete: ${reasons.join(", ")}.`;
490
+ },
491
+ },
492
+ ];
493
+ }