evolcore 0.0.1 → 0.0.2

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 (371) hide show
  1. package/CHANGELOG.md +813 -0
  2. package/LICENSE +21 -0
  3. package/MIGRATION-0.5.0.md +378 -0
  4. package/README.md +318 -14
  5. package/ROLE_ACCESS_CONTROL.md +174 -0
  6. package/assets/.env.template +4 -0
  7. package/assets/brand/evolcore/README.md +19 -0
  8. package/assets/brand/evolcore/evolcore-app-icon.png +0 -0
  9. package/assets/brand/evolcore/evolcore-app-icon.svg +13 -0
  10. package/assets/brand/evolcore/evolcore-brand-board.png +0 -0
  11. package/assets/brand/evolcore/evolcore-brand-board.svg +126 -0
  12. package/assets/brand/evolcore/evolcore-logo-kit.zip +0 -0
  13. package/assets/brand/evolcore/evolcore-logo-reverse.png +0 -0
  14. package/assets/brand/evolcore/evolcore-logo-reverse.svg +14 -0
  15. package/assets/brand/evolcore/evolcore-logo.png +0 -0
  16. package/assets/brand/evolcore/evolcore-logo.svg +14 -0
  17. package/assets/brand/evolcore/evolcore-mark.png +0 -0
  18. package/assets/brand/evolcore/evolcore-mark.svg +10 -0
  19. package/bin/ec-safe-output.js +161 -0
  20. package/bin/ec.js +29 -0
  21. package/dist/agents/baseagent.js +163 -0
  22. package/dist/agents/claude-runner.js +2385 -0
  23. package/dist/agents/codex-app-server-client.js +448 -0
  24. package/dist/agents/codex-runner.js +2639 -0
  25. package/dist/agents/gemini-runner.js +666 -0
  26. package/dist/agents/runner-types.js +75 -0
  27. package/dist/aun/aid/agentmd.js +216 -0
  28. package/dist/aun/aid/client.js +132 -0
  29. package/dist/aun/aid/control-aid.js +91 -0
  30. package/dist/aun/aid/identity.js +518 -0
  31. package/dist/aun/aid/index.js +4 -0
  32. package/dist/aun/aid/store.js +74 -0
  33. package/dist/aun/aid/types.js +1 -0
  34. package/dist/aun/aid/validation.js +21 -0
  35. package/dist/aun/group-identity.js +10 -0
  36. package/dist/aun/msg/group-index.js +6 -0
  37. package/dist/aun/msg/group.js +1231 -0
  38. package/dist/aun/msg/history.js +123 -0
  39. package/dist/aun/msg/index.js +5 -0
  40. package/dist/aun/msg/p2p.js +393 -0
  41. package/dist/aun/msg/payload-type.js +27 -0
  42. package/dist/aun/msg/upload.js +137 -0
  43. package/dist/aun/outbox.js +160 -0
  44. package/dist/aun/rpc/caller.js +42 -0
  45. package/dist/aun/rpc/connection.js +25 -0
  46. package/dist/aun/rpc/index.js +2 -0
  47. package/dist/aun/service-proxy.js +225 -0
  48. package/dist/aun/storage/download.js +29 -0
  49. package/dist/aun/storage/index.js +3 -0
  50. package/dist/aun/storage/manage.js +10 -0
  51. package/dist/aun/storage/upload.js +68 -0
  52. package/dist/channels/aun.js +4147 -0
  53. package/dist/channels/daemon.js +422 -0
  54. package/dist/channels/dingtalk.js +649 -0
  55. package/dist/channels/feishu.js +1789 -0
  56. package/dist/channels/qqbot.js +409 -0
  57. package/dist/channels/wechat.js +817 -0
  58. package/dist/channels/wecom.js +565 -0
  59. package/dist/cli/agent-command.js +641 -0
  60. package/dist/cli/agent.js +1059 -0
  61. package/dist/cli/aun-commands.js +1948 -0
  62. package/dist/cli/bench.js +1228 -0
  63. package/dist/cli/cli-argv.js +66 -0
  64. package/dist/cli/code-stats.js +329 -0
  65. package/dist/cli/command-log.js +82 -0
  66. package/dist/cli/config-selector.js +69 -0
  67. package/dist/cli/config.js +261 -0
  68. package/dist/cli/ctl-command.js +62 -0
  69. package/dist/cli/daemon-commands.js +2887 -0
  70. package/dist/cli/fs-command.js +1447 -0
  71. package/dist/cli/handoff-command.js +302 -0
  72. package/dist/cli/help.js +31 -0
  73. package/dist/cli/index.js +358 -0
  74. package/dist/cli/init-channel.js +1377 -0
  75. package/dist/cli/init.js +553 -0
  76. package/dist/cli/link-rules.js +240 -0
  77. package/dist/cli/model.js +590 -0
  78. package/dist/cli/net-check.js +723 -0
  79. package/dist/cli/queue-command.js +126 -0
  80. package/dist/cli/response.js +345 -0
  81. package/dist/cli/restart-monitor.js +456 -0
  82. package/dist/cli/stats.js +607 -0
  83. package/dist/cli/task-context.js +80 -0
  84. package/dist/cli/trigger-command.js +505 -0
  85. package/dist/cli/version.js +88 -0
  86. package/dist/cli/watch-logs.js +33 -0
  87. package/dist/cli/watch-msg.js +673 -0
  88. package/dist/config/boot-log.js +266 -0
  89. package/dist/config/builtin-role-templates.js +30 -0
  90. package/dist/config/builtin-roles.js +85 -0
  91. package/dist/config/config-batch-get.js +11 -0
  92. package/dist/config/config-field-policy.js +261 -0
  93. package/dist/config/config-manager.js +916 -0
  94. package/dist/config/config-operation-service.js +384 -0
  95. package/dist/config/contact-book.js +371 -0
  96. package/dist/config/gateway-config.js +858 -0
  97. package/dist/config/lifecycle.js +17 -0
  98. package/dist/config/mention-mode.js +27 -0
  99. package/dist/config/merge.js +161 -0
  100. package/dist/config/owner-policy.js +4 -0
  101. package/dist/config/peer-role-resolver.js +139 -0
  102. package/dist/config/resolved-config-op.js +483 -0
  103. package/dist/config/role-config-v4-startup.js +32 -0
  104. package/dist/config/role-config-v5-startup.js +27 -0
  105. package/dist/config/role-schema.js +105 -0
  106. package/dist/config/role-service.js +159 -0
  107. package/dist/config/role-store.js +204 -0
  108. package/dist/config/roles.js +55 -0
  109. package/dist/config/schema-registry.js +154 -0
  110. package/dist/config/snapshot.js +598 -0
  111. package/dist/config-store.js +503 -0
  112. package/dist/core/auth/agent-delegation.js +111 -0
  113. package/dist/core/auth/auth-gateway.js +166 -0
  114. package/dist/core/auth/authenticated-actor.js +6 -0
  115. package/dist/core/auth/authorization-audit.js +110 -0
  116. package/dist/core/auth/operation-authorizer.js +718 -0
  117. package/dist/core/auth/operation-catalog.js +675 -0
  118. package/dist/core/baseagent-loader.js +54 -0
  119. package/dist/core/bootstrap-service.js +175 -0
  120. package/dist/core/capability/capability-manager.js +316 -0
  121. package/dist/core/capability/providers/claude-capability-provider.js +176 -0
  122. package/dist/core/capability/providers/codex-capability-provider.js +148 -0
  123. package/dist/core/capability/providers/gemini-capability-provider.js +10 -0
  124. package/dist/core/capability/types.js +27 -0
  125. package/dist/core/causation/audit.js +103 -0
  126. package/dist/core/causation/aun-association.js +111 -0
  127. package/dist/core/causation/context.js +93 -0
  128. package/dist/core/causation/index.js +4 -0
  129. package/dist/core/causation/types.js +2 -0
  130. package/dist/core/channel-loader.js +277 -0
  131. package/dist/core/command/agent-control.js +616 -0
  132. package/dist/core/command/cli-intent-parser.js +225 -0
  133. package/dist/core/command/command-handler.js +1638 -0
  134. package/dist/core/command/menu-handler.js +3354 -0
  135. package/dist/core/command/menu-protocol.js +247 -0
  136. package/dist/core/command/role-menu.js +1524 -0
  137. package/dist/core/command/slash-gate.js +148 -0
  138. package/dist/core/command/slash-handler.js +3056 -0
  139. package/dist/core/daemon-file-cache.js +216 -0
  140. package/dist/core/event-bus.js +32 -0
  141. package/dist/core/event-catalog.js +740 -0
  142. package/dist/core/evolagent-registry.js +546 -0
  143. package/dist/core/evolagent.js +331 -0
  144. package/dist/core/handoff/dispatcher.js +229 -0
  145. package/dist/core/handoff/mutex.js +46 -0
  146. package/dist/core/handoff/runtime.js +324 -0
  147. package/dist/core/handoff/store.js +537 -0
  148. package/dist/core/handoff/types.js +12 -0
  149. package/dist/core/inference/text-inference.js +173 -0
  150. package/dist/core/interaction-registration.js +10 -0
  151. package/dist/core/interaction-router.js +278 -0
  152. package/dist/core/message/create-status.js +67 -0
  153. package/dist/core/message/im-renderer.js +657 -0
  154. package/dist/core/message/items-formatter.js +76 -0
  155. package/dist/core/message/logical-queue-bridge.js +123 -0
  156. package/dist/core/message/message-bridge.js +803 -0
  157. package/dist/core/message/message-cache.js +56 -0
  158. package/dist/core/message/message-log.js +339 -0
  159. package/dist/core/message/message-processor.js +4 -0
  160. package/dist/core/message/message-queue.js +1436 -0
  161. package/dist/core/message/message-utils.js +70 -0
  162. package/dist/core/message/peer-mode.js +105 -0
  163. package/dist/core/message/pending-hints.js +232 -0
  164. package/dist/core/message/response-depth.js +33 -0
  165. package/dist/core/message/response-engine.js +3776 -0
  166. package/dist/core/message/response-snapshot.js +83 -0
  167. package/dist/core/message/stream-debouncer.js +130 -0
  168. package/dist/core/message/stream-idle-monitor.js +124 -0
  169. package/dist/core/model/config-scope.js +162 -0
  170. package/dist/core/model/field-scope.js +78 -0
  171. package/dist/core/model/model-catalog.js +227 -0
  172. package/dist/core/model/model-diagnostics.js +182 -0
  173. package/dist/core/model/model-permission.js +90 -0
  174. package/dist/core/permission/approval-gateway.js +1017 -0
  175. package/dist/core/permission/ec-command-parser.js +347 -0
  176. package/dist/core/permission/execution-sandbox.js +16 -0
  177. package/dist/core/permission/index.js +6 -0
  178. package/dist/core/permission/mode.js +24 -0
  179. package/dist/core/permission/sandbox-runtime.js +265 -0
  180. package/dist/core/permission/tool-policy.js +987 -0
  181. package/dist/core/permission/unix-socket-policy.js +99 -0
  182. package/dist/core/protected-paths.js +330 -0
  183. package/dist/core/relation/peer-identity.js +222 -0
  184. package/dist/core/relation/peer-key.js +1 -0
  185. package/dist/core/role/runtime-policy.js +141 -0
  186. package/dist/core/session/adapters/claude-session-file-adapter.js +218 -0
  187. package/dist/core/session/adapters/codex-session-file-adapter.js +333 -0
  188. package/dist/core/session/adapters/gemini-session-file-adapter.js +181 -0
  189. package/dist/core/session/session-file-adapter.js +7 -0
  190. package/dist/core/session/session-file-health.js +45 -0
  191. package/dist/core/session/session-fs-store.js +232 -0
  192. package/dist/core/session/session-key.js +24 -0
  193. package/dist/core/session/session-manager.js +1587 -0
  194. package/dist/core/session/session-mapper.js +100 -0
  195. package/dist/core/session/session-renew.js +314 -0
  196. package/dist/core/session/session-title.js +128 -0
  197. package/dist/core/session/session-turn-coordinator.js +205 -0
  198. package/dist/core/session/session-turns.js +67 -0
  199. package/dist/core/system-channels.js +29 -0
  200. package/dist/eck/baseagent-caps.js +18 -0
  201. package/dist/eck/detect.js +47 -0
  202. package/dist/eck/group-rules-sync.js +345 -0
  203. package/dist/eck/init.js +77 -0
  204. package/dist/eck/kit-renderer.js +359 -0
  205. package/dist/eck/manifest-engine.js +446 -0
  206. package/dist/eck/message-renderer.js +199 -0
  207. package/dist/eck/rules-loader.js +28 -0
  208. package/dist/index.js +2870 -4
  209. package/dist/ipc.js +748 -0
  210. package/dist/paths.js +262 -0
  211. package/dist/product.js +18 -0
  212. package/dist/response-system/context-builder.js +71 -0
  213. package/dist/response-system/coordinator.js +117 -0
  214. package/dist/response-system/decision-executor.js +86 -0
  215. package/dist/response-system/engines/v1/index.js +21 -0
  216. package/dist/response-system/engines/v1/interactive-flow.js +27 -0
  217. package/dist/response-system/engines/v1/proactive-flow.js +137 -0
  218. package/dist/response-system/engines/v1/types.js +1 -0
  219. package/dist/response-system/extensions.js +41 -0
  220. package/dist/response-system/index.js +6 -0
  221. package/dist/response-system/modes/index.js +7 -0
  222. package/dist/response-system/modes/single-session/index.js +72 -0
  223. package/dist/response-system/queues/fifo-queue.js +44 -0
  224. package/dist/response-system/queues/index.js +6 -0
  225. package/dist/response-system/queues/lifo-queue.js +42 -0
  226. package/dist/response-system/queues/priority-queue.js +63 -0
  227. package/dist/response-system/registry.js +97 -0
  228. package/dist/response-system/resolver.js +37 -0
  229. package/dist/response-system/selector.js +23 -0
  230. package/dist/response-system/types.js +7 -0
  231. package/dist/stats/billing.js +151 -0
  232. package/dist/stats/budget.js +93 -0
  233. package/dist/stats/db.js +403 -0
  234. package/dist/stats/eck-vars.js +89 -0
  235. package/dist/stats/index.js +11 -0
  236. package/dist/stats/normalizer.js +80 -0
  237. package/dist/stats/price-resolver.js +138 -0
  238. package/dist/stats/query.js +763 -0
  239. package/dist/stats/role-budget.js +168 -0
  240. package/dist/stats/writer.js +151 -0
  241. package/dist/trigger/anomaly-store.js +258 -0
  242. package/dist/trigger/audit.js +152 -0
  243. package/dist/trigger/event-source.js +119 -0
  244. package/dist/trigger/feedback.js +685 -0
  245. package/dist/trigger/history.js +290 -0
  246. package/dist/trigger/manager.js +291 -0
  247. package/dist/trigger/parser.js +520 -0
  248. package/dist/trigger/patch.js +148 -0
  249. package/dist/trigger/scheduler.js +1600 -0
  250. package/dist/trigger/script-executor.js +155 -0
  251. package/dist/trigger/state.js +145 -0
  252. package/dist/trigger/types.js +1 -0
  253. package/dist/trigger/validation.js +621 -0
  254. package/dist/types.js +12 -0
  255. package/dist/utils/aid-bind.js +299 -0
  256. package/dist/utils/atomic-write.js +95 -0
  257. package/dist/utils/avatar-upload.js +123 -0
  258. package/dist/utils/cross-platform.js +297 -0
  259. package/dist/utils/ecweb-utils.js +73 -0
  260. package/dist/utils/error-dict.json +153 -0
  261. package/dist/utils/error-utils.js +349 -0
  262. package/dist/utils/instance-registry.js +437 -0
  263. package/dist/utils/locale.js +21 -0
  264. package/dist/utils/log-writer.js +224 -0
  265. package/dist/utils/logger.js +89 -0
  266. package/dist/utils/markdown-to-plain-text.js +20 -0
  267. package/dist/utils/media-cache.js +271 -0
  268. package/dist/utils/model-prices.jsonl +17 -0
  269. package/dist/utils/npm-ops.js +210 -0
  270. package/dist/utils/process-introspect.js +133 -0
  271. package/dist/utils/process-tree-stats.js +271 -0
  272. package/dist/utils/project-path.js +74 -0
  273. package/dist/utils/stats.js +410 -0
  274. package/dist/utils/tool-summary.js +284 -0
  275. package/dist/utils/welcome.js +268 -0
  276. package/kits/docs/GUIDE.md +20 -0
  277. package/kits/docs/INDEX.md +65 -0
  278. package/kits/docs/aun/CHEATSHEET.md +19 -0
  279. package/kits/docs/aun/SYNC_PROTOCOL.md +15 -0
  280. package/kits/docs/channels/aun.md +65 -0
  281. package/kits/docs/channels/feishu.md +56 -0
  282. package/kits/docs/context-assembly.md +366 -0
  283. package/kits/docs/eck_templates/GUIDE.template.md +22 -0
  284. package/kits/docs/eck_templates/INDEX.template.md +28 -0
  285. package/kits/docs/eck_templates/path-registry.template.md +33 -0
  286. package/kits/docs/eck_templates/runtime.template.md +19 -0
  287. package/kits/docs/evolcore/INDEX.md +66 -0
  288. package/kits/docs/evolcore/agent.md +69 -0
  289. package/kits/docs/evolcore/aid.md +49 -0
  290. package/kits/docs/evolcore/config.md +149 -0
  291. package/kits/docs/evolcore/ctl.md +46 -0
  292. package/kits/docs/evolcore/event.md +216 -0
  293. package/kits/docs/evolcore/fs-architecture.md +1215 -0
  294. package/kits/docs/evolcore/fs.md +101 -0
  295. package/kits/docs/evolcore/group-fs.md +17 -0
  296. package/kits/docs/evolcore/group-rules.md +226 -0
  297. package/kits/docs/evolcore/group.md +141 -0
  298. package/kits/docs/evolcore/model.md +47 -0
  299. package/kits/docs/evolcore/msg.md +130 -0
  300. package/kits/docs/evolcore/response.md +80 -0
  301. package/kits/docs/evolcore/rpc.md +35 -0
  302. package/kits/docs/evolcore/self-summary.md +29 -0
  303. package/kits/docs/evolcore/stats.md +70 -0
  304. package/kits/docs/evolcore/storage.md +49 -0
  305. package/kits/docs/evolcore/trigger.md +524 -0
  306. package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -0
  307. package/kits/docs/identity/PATH_OPS.md +16 -0
  308. package/kits/docs/identity/ROLE_DETAIL.md +23 -0
  309. package/kits/docs/identity/identity-tools.md +26 -0
  310. package/kits/docs/path-registry.md +43 -0
  311. package/kits/docs/prompt-loading-architecture.md +266 -0
  312. package/kits/docs/venues/aun-group.md +45 -0
  313. package/kits/docs/venues/aun-private.md +10 -0
  314. package/kits/docs/venues/client-desktop.md +10 -0
  315. package/kits/docs/venues/client-mobile.md +10 -0
  316. package/kits/docs/venues/feishu-group.md +13 -0
  317. package/kits/docs/venues/feishu-private.md +9 -0
  318. package/kits/docs/venues/group.md +25 -0
  319. package/kits/docs/venues/private.md +10 -0
  320. package/kits/eck_manifest.auxiliary.json +43 -0
  321. package/kits/eck_manifest.json +191 -0
  322. package/kits/eck_message_manifest.json +63 -0
  323. package/kits/migrations/README-role-config-v4.md +32 -0
  324. package/kits/migrations/migrate-role-config-v4.mjs +623 -0
  325. package/kits/migrations/migrate-role-config-v5.mjs +346 -0
  326. package/kits/migrations/rename-config-file.mjs +99 -0
  327. package/kits/rules/01-overview.md +142 -0
  328. package/kits/rules/02-navigation.md +76 -0
  329. package/kits/rules/03-identity.md +34 -0
  330. package/kits/rules/04-relation.md +59 -0
  331. package/kits/rules/05-venue.md +44 -0
  332. package/kits/rules/06-channel.md +59 -0
  333. package/kits/schemas/_meta.json +29 -0
  334. package/kits/schemas/agent-config.schema.1.json +177 -0
  335. package/kits/schemas/agent-config.schema.2.json +239 -0
  336. package/kits/schemas/agent-config.schema.3.json +119 -0
  337. package/kits/schemas/agent-config.schema.4.json +208 -0
  338. package/kits/schemas/agent-config.schema.5.json +326 -0
  339. package/kits/schemas/contact-book.schema.1.json +36 -0
  340. package/kits/schemas/daemon.schema.1.json +90 -0
  341. package/kits/schemas/defaults.schema.1.json +81 -0
  342. package/kits/schemas/menu-exec-schema-commands.md +208 -0
  343. package/kits/schemas/migrations/README.md +28 -0
  344. package/kits/schemas/relation-config.schema.1.json +158 -0
  345. package/kits/schemas/relation-config.schema.2.json +73 -0
  346. package/kits/schemas/relation-config.schema.3.json +50 -0
  347. package/kits/schemas/relation-config.schema.4.json +47 -0
  348. package/kits/schemas/role-config.schema.1.json +200 -0
  349. package/kits/schemas/role-registry.schema.1.json +35 -0
  350. package/kits/schemas/single-session.schema.1.json +31 -0
  351. package/kits/templates/bootstrap-welcome.md +15 -0
  352. package/kits/templates/message-fragments/handoff-request-to-target.md +13 -0
  353. package/kits/templates/message-fragments/handoff-response-to-origin.md +10 -0
  354. package/kits/templates/message-fragments/inject-default.md +2 -0
  355. package/kits/templates/message-fragments/item.md +2 -0
  356. package/kits/templates/roles/admin.json +8 -0
  357. package/kits/templates/roles/member.json +40 -0
  358. package/kits/templates/roles/owner.json +8 -0
  359. package/kits/templates/roles/visitor.json +39 -0
  360. package/kits/templates/system-fragments/baseagent.md +14 -0
  361. package/kits/templates/system-fragments/bootstrap.md +16 -0
  362. package/kits/templates/system-fragments/channel.md +48 -0
  363. package/kits/templates/system-fragments/commands.md +26 -0
  364. package/kits/templates/system-fragments/identity.md +11 -0
  365. package/kits/templates/system-fragments/relation.md +19 -0
  366. package/kits/templates/system-fragments/session.md +53 -0
  367. package/kits/templates/system-fragments/venue.md +31 -0
  368. package/package.json +50 -15
  369. package/dist/index.d.ts +0 -7
  370. package/dist/index.d.ts.map +0 -1
  371. package/dist/index.js.map +0 -1
@@ -0,0 +1,546 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { EvolAgent } from './evolagent.js';
4
+ import { logger } from '../utils/logger.js';
5
+ import { agentMdPath, agentPersonalDir } from '../paths.js';
6
+ import { loadAllAgents, ensureAgentDirSkeleton, loadAgent, validateAgentConfig, } from '../config-store.js';
7
+ import { resolveEffective } from '../config/config-manager.js';
8
+ // ── Channel Fingerprint ───────────────────────────────────────────────────
9
+ // 用于检测多 agent 之间复用同一外部凭证的冲突(appId、aid、token 等)。
10
+ // 格式:{type}:{primaryKey}
11
+ const PRIMARY_KEY_MAP = {
12
+ feishu: 'appId',
13
+ aun: '__aid__', // AUN 实例的“凭证”就是 agent 自身 aid
14
+ wechat: 'token',
15
+ wecom: 'botId',
16
+ dingtalk: 'clientId',
17
+ qqbot: 'appId',
18
+ };
19
+ export function extractFingerprint(channelType, instance, agentAid) {
20
+ const keyField = PRIMARY_KEY_MAP[channelType];
21
+ if (!keyField)
22
+ return null;
23
+ if (keyField === '__aid__')
24
+ return `${channelType}:${agentAid}`;
25
+ const value = instance[keyField];
26
+ if (!value || typeof value !== 'string')
27
+ return null;
28
+ return `${channelType}:${value}`;
29
+ }
30
+ /**
31
+ * 跨 agent 检查同一外部凭证是否被多次声明(飞书 appId、AUN aid 等)。
32
+ */
33
+ export function detectDuplicates(agents) {
34
+ const seen = new Map();
35
+ for (const agent of agents) {
36
+ for (const inst of agent.config.channels) {
37
+ const fp = extractFingerprint(inst.type, inst, agent.aid);
38
+ if (!fp)
39
+ continue;
40
+ const arr = seen.get(fp) ?? [];
41
+ arr.push({ aid: agent.aid, channelName: agent.effectiveChannelName(inst.type, inst.name) });
42
+ seen.set(fp, arr);
43
+ }
44
+ }
45
+ const out = [];
46
+ for (const [fp, arr] of seen) {
47
+ if (arr.length > 1) {
48
+ const [type] = fp.split(':');
49
+ out.push({ fingerprint: fp, channelType: type, agents: arr });
50
+ }
51
+ }
52
+ return out;
53
+ }
54
+ // ── Registry ──────────────────────────────────────────────────────────────
55
+ export class EvolAgentRegistry {
56
+ _agentsDir;
57
+ agents = new Map();
58
+ /** channel key (`<type>#<selfAID>#<name>`) → agent aid */
59
+ channelIndex = new Map();
60
+ /** 启动期被 ConfigStore 跳过的目录(命名非法 / 缺 config.json / 校验失败等) */
61
+ skipped = [];
62
+ /**
63
+ * agentsDir 参数保留作 ctor 兼容,但实际加载走 ConfigStore(基于 paths.ts)。
64
+ * globalWriter 已废弃——构造期接受但忽略,阶段 2c 删除。
65
+ */
66
+ constructor(_agentsDir, _globalWriter) {
67
+ this._agentsDir = _agentsDir;
68
+ void _globalWriter;
69
+ }
70
+ setGlobalWriter(_writer) {
71
+ void _writer; // no-op,废弃 API
72
+ }
73
+ /**
74
+ * 扫描 agents/ 目录加载所有 self-agent 配置(合并 defaults),构造 EvolAgent
75
+ * 实例并建立 channel 路由索引。
76
+ *
77
+ * `globalConfig` 参数保留作签名兼容,但被忽略——defaults 由 ConfigStore 自己加载。
78
+ */
79
+ loadAll(_globalConfig) {
80
+ void _globalConfig;
81
+ this.agents.clear();
82
+ this.channelIndex.clear();
83
+ this.skipped = [];
84
+ const { agents: rawAgents, skipped, invalidAgents = [] } = loadAllAgents({ includeInvalid: true });
85
+ this.skipped = skipped;
86
+ for (const raw of rawAgents) {
87
+ try {
88
+ const merged = resolveEffective({ self: raw.aid });
89
+ const agent = new EvolAgent(raw, merged);
90
+ ensureAgentDirSkeleton(raw.aid);
91
+ this.agents.set(agent.aid, agent);
92
+ }
93
+ catch (e) {
94
+ logger.warn(`[EvolAgentRegistry] failed to construct agent ${raw.aid}: ${e}`);
95
+ }
96
+ }
97
+ for (const { agent: raw, reason } of invalidAgents) {
98
+ this.registerErrorAgent(raw, reason);
99
+ }
100
+ this.detectAndFlagConflicts();
101
+ this.buildChannelIndex();
102
+ }
103
+ registerErrorAgent(raw, reason) {
104
+ try {
105
+ const merged = this.resolveMergedForErrorAgent(raw, reason);
106
+ const agent = new EvolAgent(raw, merged);
107
+ agent.status = 'error';
108
+ agent.error = reason;
109
+ ensureAgentDirSkeleton(raw.aid);
110
+ this.agents.set(agent.aid, agent);
111
+ logger.warn(`[EvolAgentRegistry] loaded invalid agent ${raw.aid} as error: ${reason}`);
112
+ return agent;
113
+ }
114
+ catch (e) {
115
+ logger.warn(`[EvolAgentRegistry] failed to register invalid agent ${raw.aid}: ${e}`);
116
+ return null;
117
+ }
118
+ }
119
+ resolveMergedForErrorAgent(raw, reason) {
120
+ try {
121
+ return resolveEffective({ self: raw.aid });
122
+ }
123
+ catch (e) {
124
+ const message = e instanceof Error ? e.message : String(e);
125
+ logger.warn(`[EvolAgentRegistry] fallback effective config for invalid agent ${raw.aid}: ${message}; original error: ${reason}`);
126
+ return {
127
+ ...raw,
128
+ $schema_version: raw.$schema_version ?? 1,
129
+ aid: raw.aid,
130
+ enabled: raw.enabled,
131
+ owners: raw.owners,
132
+ admins: raw.admins,
133
+ channels: Array.isArray(raw.channels) ? raw.channels : [],
134
+ active_baseagent: raw.active_baseagent,
135
+ baseagents: raw.baseagents,
136
+ projects: raw.projects,
137
+ chatmode: raw.chatmode,
138
+ };
139
+ }
140
+ }
141
+ detectAndFlagConflicts() {
142
+ const dups = detectDuplicates([...this.agents.values()].filter(a => a.status !== 'error' && a.status !== 'disabled'));
143
+ for (const d of dups) {
144
+ const owners = d.agents.map(o => `${o.aid}(${o.channelName})`).join(', ');
145
+ const msg = `Channel conflict: ${d.fingerprint} claimed by ${owners}`;
146
+ logger.error(`[EvolAgentRegistry] ${msg}`);
147
+ for (const o of d.agents) {
148
+ const a = this.agents.get(o.aid);
149
+ if (a && a.status !== 'error') {
150
+ a.status = 'error';
151
+ a.error = msg;
152
+ }
153
+ }
154
+ }
155
+ }
156
+ buildChannelIndex() {
157
+ for (const agent of this.agents.values()) {
158
+ if (agent.status === 'error' || agent.status === 'disabled')
159
+ continue;
160
+ for (const key of agent.channelInstanceNames()) {
161
+ const prev = this.channelIndex.get(key);
162
+ if (prev && prev !== agent.aid) {
163
+ logger.warn(`[EvolAgentRegistry] channel key "${key}" claimed by both ${prev} and ${agent.aid}`);
164
+ }
165
+ this.channelIndex.set(key, agent.aid);
166
+ }
167
+ }
168
+ }
169
+ resolveByChannel(channelKey) {
170
+ const aid = this.channelIndex.get(channelKey);
171
+ if (!aid)
172
+ return null;
173
+ return this.agents.get(aid) ?? null;
174
+ }
175
+ isOwner(channelKey, userId, _globalFallback) {
176
+ void _globalFallback;
177
+ const agent = this.resolveByChannel(channelKey);
178
+ return agent?.isOwner(channelKey, userId) ?? false;
179
+ }
180
+ isAdmin(channelKey, userId, _globalFallback) {
181
+ void _globalFallback;
182
+ const agent = this.resolveByChannel(channelKey);
183
+ return agent?.isAdmin(channelKey, userId) ?? false;
184
+ }
185
+ getOwner(channelKey) {
186
+ return this.resolveByChannel(channelKey)?.getOwner(channelKey);
187
+ }
188
+ setChannelOwner(channelKey, userId) {
189
+ const agent = this.resolveByChannel(channelKey);
190
+ if (!agent) {
191
+ logger.warn(`[EvolAgentRegistry] setChannelOwner: channel "${channelKey}" not found`);
192
+ return;
193
+ }
194
+ agent.setOwner(channelKey, userId);
195
+ }
196
+ getShowActivities(channelKey) {
197
+ return this.resolveByChannel(channelKey)?.getShowActivities(channelKey) ?? 'all';
198
+ }
199
+ setShowActivities(channelKey, mode) {
200
+ const agent = this.resolveByChannel(channelKey);
201
+ if (!agent) {
202
+ logger.warn(`[EvolAgentRegistry] setShowActivities: channel "${channelKey}" not found`);
203
+ return;
204
+ }
205
+ agent.setShowActivities(channelKey, mode);
206
+ }
207
+ get(aidOrName) {
208
+ return this.agents.get(aidOrName) ?? null;
209
+ }
210
+ list() {
211
+ return [...this.agents.values()].map(a => this.toInfo(a));
212
+ }
213
+ invalidateAgentDisplayCache(aid) {
214
+ this.displayNameCache.delete(aid);
215
+ this.personalNameCache.delete(aid);
216
+ this.displayNamePending.delete(aid);
217
+ }
218
+ runnableAgents() {
219
+ return [...this.agents.values()].filter(a => a.status === 'stopped');
220
+ }
221
+ getSkipped() {
222
+ return [...this.skipped];
223
+ }
224
+ loadNewAgent(aid) {
225
+ if (this.agents.has(aid)) {
226
+ logger.info(`[EvolAgentRegistry] agent ${aid} already loaded, skipping`);
227
+ return this.agents.get(aid);
228
+ }
229
+ let raw = null;
230
+ try {
231
+ raw = loadAgent(aid);
232
+ }
233
+ catch (e) {
234
+ const reason = e instanceof Error ? e.message : String(e);
235
+ logger.warn(`[EvolAgentRegistry] loadNewAgent ${aid}: ${reason}`);
236
+ return this.registerErrorAgent({
237
+ $schema_version: 1,
238
+ aid,
239
+ enabled: true,
240
+ channels: [],
241
+ }, reason);
242
+ }
243
+ if (!raw) {
244
+ logger.warn(`[EvolAgentRegistry] loadNewAgent: ${aid}/config.json not found`);
245
+ return null;
246
+ }
247
+ const errs = validateAgentConfig(raw);
248
+ if (errs.length > 0) {
249
+ const reason = errs.join('; ');
250
+ logger.warn(`[EvolAgentRegistry] loadNewAgent ${aid}: ${reason}`);
251
+ return this.registerErrorAgent(raw, reason);
252
+ }
253
+ const conflict = this.checkConflictForReload(raw, aid);
254
+ if (conflict) {
255
+ logger.warn(`[EvolAgentRegistry] loadNewAgent ${aid}: ${conflict}`);
256
+ return this.registerErrorAgent(raw, `Channel conflict: ${conflict}`);
257
+ }
258
+ const merged = resolveEffective({ self: aid });
259
+ const agent = new EvolAgent(raw, merged);
260
+ ensureAgentDirSkeleton(aid);
261
+ this.agents.set(aid, agent);
262
+ for (const key of agent.channelInstanceNames()) {
263
+ this.channelIndex.set(key, aid);
264
+ }
265
+ logger.info(`[EvolAgentRegistry] Hot-loaded agent: ${aid}`);
266
+ return agent;
267
+ }
268
+ async reload(aidOrName, hooks) {
269
+ const oldAgent = this.agents.get(aidOrName);
270
+ if (!oldAgent)
271
+ throw new Error(`Agent "${aidOrName}" not found`);
272
+ const raw = loadAgent(oldAgent.aid);
273
+ if (!raw)
274
+ throw new Error(`Agent ${oldAgent.aid}/config.json missing on reload`);
275
+ const errs = validateAgentConfig(raw);
276
+ if (errs.length > 0)
277
+ throw new Error(`Invalid config after edit: ${errs.join('; ')}`);
278
+ const merged = resolveEffective({ self: raw.aid });
279
+ if (oldAgent.status === 'disabled' && raw.enabled !== false) {
280
+ oldAgent.swapConfig(raw, merged);
281
+ const hotLoad = globalThis.__evolcore_hotLoadAgent;
282
+ if (!hotLoad)
283
+ throw new Error(`Cannot enable agent "${aidOrName}": hot-load handler not initialized`);
284
+ this.agents.delete(oldAgent.aid);
285
+ this.channelIndex.clear();
286
+ this.buildChannelIndex();
287
+ await hotLoad(oldAgent.aid);
288
+ logger.info(`[Reload] Agent "${aidOrName}" transitioned from disabled to enabled`);
289
+ return;
290
+ }
291
+ if (oldAgent.status !== 'disabled' && raw.enabled === false) {
292
+ let prepared = false;
293
+ try {
294
+ await hooks.prepareHandoffReload?.(oldAgent.aid);
295
+ prepared = true;
296
+ for (const ch of oldAgent.channelInstanceNames()) {
297
+ try {
298
+ await hooks.drainChannel(ch);
299
+ }
300
+ catch { }
301
+ try {
302
+ await hooks.disconnectChannel(ch);
303
+ }
304
+ catch { }
305
+ }
306
+ oldAgent.swapConfig(raw, merged);
307
+ oldAgent.status = 'disabled';
308
+ this.channelIndex.clear();
309
+ this.buildChannelIndex();
310
+ logger.info(`[Reload] Agent "${aidOrName}" disabled`);
311
+ return;
312
+ }
313
+ catch (error) {
314
+ if (prepared) {
315
+ try {
316
+ await hooks.completeHandoffReload?.(oldAgent.aid);
317
+ }
318
+ catch { }
319
+ }
320
+ throw error;
321
+ }
322
+ }
323
+ const conflict = this.checkConflictForReload(raw, oldAgent.aid);
324
+ if (conflict)
325
+ throw new Error(`Channel conflict: ${conflict}`);
326
+ const removedSuccessfully = [];
327
+ const addedSuccessfully = [];
328
+ let prepared = false;
329
+ try {
330
+ await hooks.prepareHandoffReload?.(oldAgent.aid);
331
+ prepared = true;
332
+ const oldChannels = new Set(oldAgent.channelInstanceNames());
333
+ const aunKey = oldAgent.effectiveChannelName('aun', 'main');
334
+ const otherKeys = raw.channels.filter(c => c.type !== 'aun').map(c => oldAgent.effectiveChannelName(c.type, c.name));
335
+ const newChannels = new Set([aunKey, ...otherKeys]);
336
+ const toRemove = [...oldChannels].filter(c => !newChannels.has(c));
337
+ const toAdd = [...newChannels].filter(c => !oldChannels.has(c));
338
+ const kept = [...oldChannels].filter(c => newChannels.has(c));
339
+ const credentialsChanged = [];
340
+ for (const ch of kept) {
341
+ const oldInst = oldAgent.findChannelInstance(ch);
342
+ const newInst = findInstanceByKey(raw, oldAgent, ch);
343
+ if (oldInst && newInst && JSON.stringify(oldInst) !== JSON.stringify(newInst)) {
344
+ credentialsChanged.push(ch);
345
+ }
346
+ }
347
+ toRemove.push(...credentialsChanged);
348
+ toAdd.push(...credentialsChanged);
349
+ for (const ch of toRemove)
350
+ await hooks.drainChannel(ch);
351
+ for (const ch of toRemove) {
352
+ await hooks.disconnectChannel(ch);
353
+ removedSuccessfully.push(ch);
354
+ }
355
+ oldAgent.swapConfig(raw, merged);
356
+ oldAgent.invalidatePersonaCache();
357
+ for (const ch of toAdd) {
358
+ await hooks.startChannel(oldAgent, ch);
359
+ addedSuccessfully.push(ch);
360
+ }
361
+ oldAgent.status = 'running';
362
+ this.channelIndex.clear();
363
+ this.buildChannelIndex();
364
+ await hooks.completeHandoffReload?.(oldAgent.aid);
365
+ prepared = false;
366
+ }
367
+ catch (err) {
368
+ logger.error(`[Reload] Failed: ${err}. Attempting rollback for "${aidOrName}".`);
369
+ for (const ch of addedSuccessfully) {
370
+ try {
371
+ await hooks.disconnectChannel(ch);
372
+ }
373
+ catch { }
374
+ }
375
+ if (prepared) {
376
+ try {
377
+ await hooks.completeHandoffReload?.(oldAgent.aid);
378
+ }
379
+ catch (resumeError) {
380
+ logger.error(`[Reload] Failed to resume Handoff dispatcher for "${aidOrName}":`, resumeError);
381
+ }
382
+ }
383
+ void removedSuccessfully;
384
+ oldAgent.status = 'error';
385
+ oldAgent.error = `Reload failed (rollback partial): ${err instanceof Error ? err.message : String(err)}`;
386
+ throw err;
387
+ }
388
+ }
389
+ async stopAgent(aidOrName, hooks) {
390
+ const agent = this.agents.get(aidOrName);
391
+ if (!agent)
392
+ throw new Error(`Agent "${aidOrName}" not found`);
393
+ if (agent.status === 'disabled')
394
+ throw new Error('Agent is disabled; use enable/disable instead');
395
+ if (agent.status === 'stopped')
396
+ return;
397
+ for (const ch of agent.channelInstanceNames()) {
398
+ try {
399
+ await hooks.disconnectChannel(ch);
400
+ }
401
+ catch { }
402
+ }
403
+ agent.status = 'stopped';
404
+ this.channelIndex.clear();
405
+ this.buildChannelIndex();
406
+ logger.info(`[Registry] Stopped agent ${aidOrName}`);
407
+ }
408
+ async startAgent(aidOrName, hooks) {
409
+ const agent = this.agents.get(aidOrName);
410
+ if (!agent)
411
+ throw new Error(`Agent "${aidOrName}" not found`);
412
+ if (agent.status === 'disabled')
413
+ throw new Error('Agent is disabled; use enable instead');
414
+ if (agent.status === 'error')
415
+ throw new Error(`Agent is in error state: ${agent.error ?? 'unknown error'}`);
416
+ if (agent.status === 'running')
417
+ return;
418
+ for (const ch of agent.channelInstanceNames()) {
419
+ await hooks.startChannel(agent, ch);
420
+ }
421
+ agent.status = 'running';
422
+ this.channelIndex.clear();
423
+ this.buildChannelIndex();
424
+ logger.info(`[Registry] Started agent ${aidOrName}`);
425
+ }
426
+ checkConflictForReload(newRaw, excludeAid) {
427
+ const newFps = new Set();
428
+ for (const inst of newRaw.channels) {
429
+ const fp = extractFingerprint(inst.type, inst, newRaw.aid);
430
+ if (fp)
431
+ newFps.add(fp);
432
+ }
433
+ for (const [aid, agent] of this.agents) {
434
+ if (aid === excludeAid)
435
+ continue;
436
+ if (agent.status === 'error' || agent.status === 'disabled')
437
+ continue;
438
+ for (const inst of agent.config.channels) {
439
+ const fp = extractFingerprint(inst.type, inst, agent.aid);
440
+ if (fp && newFps.has(fp))
441
+ return `${fp} conflicts with agent "${aid}"`;
442
+ }
443
+ }
444
+ return null;
445
+ }
446
+ displayNameCache = new Map();
447
+ displayNamePending = new Set();
448
+ resolveDisplayName(aid) {
449
+ const cached = this.displayNameCache.get(aid);
450
+ if (cached)
451
+ return cached;
452
+ try {
453
+ const mdPath = agentMdPath(aid);
454
+ if (fs.existsSync(mdPath)) {
455
+ const content = fs.readFileSync(mdPath, 'utf-8');
456
+ const fm = content.match(/^---\n([\s\S]*?)\n---/);
457
+ if (fm) {
458
+ const nm = fm[1].match(/^name:\s*["']?(.+?)["']?\s*$/m);
459
+ if (nm?.[1]) {
460
+ this.displayNameCache.set(aid, nm[1]);
461
+ return nm[1];
462
+ }
463
+ }
464
+ }
465
+ }
466
+ catch { }
467
+ if (!this.displayNamePending.has(aid)) {
468
+ this.displayNamePending.add(aid);
469
+ import('../aun/aid/index.js').then(({ agentmdGet }) => {
470
+ agentmdGet(aid).then(content => {
471
+ if (typeof content === 'string') {
472
+ const fm = content.match(/^---\n([\s\S]*?)\n---/);
473
+ if (fm) {
474
+ const nm = fm[1].match(/^name:\s*["']?(.+?)["']?\s*$/m);
475
+ if (nm?.[1])
476
+ this.displayNameCache.set(aid, nm[1]);
477
+ }
478
+ }
479
+ }).catch(() => { }).finally(() => this.displayNamePending.delete(aid));
480
+ }).catch(() => { this.displayNamePending.delete(aid); });
481
+ }
482
+ return undefined;
483
+ }
484
+ personalNameCache = new Map();
485
+ resolvePersonalName(aid) {
486
+ const cached = this.personalNameCache.get(aid);
487
+ if (cached !== undefined)
488
+ return cached;
489
+ try {
490
+ const personaPath = path.join(agentPersonalDir(aid), 'persona.md');
491
+ if (fs.existsSync(personaPath)) {
492
+ const content = fs.readFileSync(personaPath, 'utf-8');
493
+ const bold = content.match(/我[叫是]\s*\*{1,2}(.+?)\*{1,2}/);
494
+ if (bold?.[1]) {
495
+ this.personalNameCache.set(aid, bold[1]);
496
+ return bold[1];
497
+ }
498
+ const plain = content.match(/(?:我是|我叫)\s*([^\s((,,。.)]+)/);
499
+ if (plain?.[1]) {
500
+ this.personalNameCache.set(aid, plain[1]);
501
+ return plain[1];
502
+ }
503
+ }
504
+ }
505
+ catch { }
506
+ this.personalNameCache.set(aid, undefined);
507
+ return undefined;
508
+ }
509
+ toInfo(agent) {
510
+ const displayName = this.resolveDisplayName(agent.aid);
511
+ const personalName = this.resolvePersonalName(agent.aid);
512
+ const owners = Array.from(new Set(agent.config.owners ?? []));
513
+ const channels = safeInfoValue(() => agent.channelInstanceNames(), []);
514
+ const projectPath = safeInfoValue(() => agent.projectPath, '');
515
+ const baseagent = safeInfoValue(() => agent.baseagent, agent.config.active_baseagent ?? '');
516
+ const model = baseagent ? safeInfoValue(() => agent.model, undefined) : undefined;
517
+ const effort = baseagent ? safeInfoValue(() => agent.effort, undefined) : undefined;
518
+ return {
519
+ name: displayName || agent.name || agent.aid,
520
+ displayName,
521
+ personalName,
522
+ aid: agent.aid,
523
+ status: agent.status,
524
+ channels,
525
+ projectPath,
526
+ baseagent,
527
+ model,
528
+ effort,
529
+ owners,
530
+ lastActivity: agent.lastActivity,
531
+ activeSessions: agent.activeSessions,
532
+ error: agent.error,
533
+ };
534
+ }
535
+ }
536
+ function safeInfoValue(fn, fallback) {
537
+ try {
538
+ return fn();
539
+ }
540
+ catch {
541
+ return fallback;
542
+ }
543
+ }
544
+ function findInstanceByKey(raw, agent, channelKey) {
545
+ return raw.channels.find(c => agent.effectiveChannelName(c.type, c.name) === channelKey) ?? null;
546
+ }