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,1059 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import readline from 'readline';
4
+ import { resolvePaths, agentMdPath as getAgentMdPathFromPaths, aunPath as defaultAunPath } from '../paths.js';
5
+ import { loadDefaults, loadAllAgents, loadAgent, saveAgent, ensureAgentDirSkeleton } from '../config-store.js';
6
+ import { ConfigTarget, write as cfgWrite, ensureFile as cfgEnsure, read as cfgRead, initConfigManager } from '../config/config-manager.js';
7
+ import { resolveConfigCommand } from '../config/resolved-config-op.js';
8
+ import { executeResolvedConfigCommand } from '../config/config-operation-service.js';
9
+ import { ipcQuery } from '../ipc.js';
10
+ import { CONFIG_SCHEMA_VERSION } from '../types.js';
11
+ import { withLifecycleForWrite } from '../config/lifecycle.js';
12
+ import { ensureInitialRoleRegistry } from '../config/role-service.js';
13
+ import { isValidChannelName } from '../core/channel-loader.js';
14
+ import { commandExists } from '../utils/cross-platform.js';
15
+ import { getCodexAppServerAvailability, isCodexAppServerAvailable } from '../agents/codex-runner.js';
16
+ import { agentProjectRootFromDefaults, deriveAgentProjectPath } from '../utils/project-path.js';
17
+ import { AGENT_DELEGATION_TOKEN_ENV } from '../core/auth/agent-delegation.js';
18
+ function withStaticOwner(config, owner) {
19
+ const value = owner?.trim();
20
+ if (!value)
21
+ return config;
22
+ const owners = config.owners ?? [];
23
+ if (owners.includes(value))
24
+ return config;
25
+ return { ...config, owners: [...owners, value] };
26
+ }
27
+ // ==================== Helpers ====================
28
+ const BASEAGENT_CANDIDATES = ['claude', 'codex', 'gemini'];
29
+ function isBaseagentAvailable(baseagent) {
30
+ if (baseagent === 'codex')
31
+ return isCodexAppServerAvailable();
32
+ return commandExists(baseagent);
33
+ }
34
+ function detectAvailableBaseagents() {
35
+ return BASEAGENT_CANDIDATES.filter(isBaseagentAvailable);
36
+ }
37
+ function pickDefaultBaseagent(available) {
38
+ if (available.length === 0)
39
+ return null;
40
+ return available.includes('claude') ? 'claude' : available[0];
41
+ }
42
+ function buildBaseagentsBlock(chosen) {
43
+ return { [chosen]: {} };
44
+ }
45
+ const DEFAULT_CHATMODE = { private: 'interactive', group: 'proactive', nothuman: 'proactive' };
46
+ /**
47
+ * 写入新 agent 的初始配置(所有字段统一在 config.json)。
48
+ * mentionMode 不写初始值,继承 schema 出厂默认(disabled)。
49
+ */
50
+ function saveInitialBehavior(aid, baseagent) {
51
+ const behavior = {
52
+ active_baseagent: baseagent,
53
+ baseagents: buildBaseagentsBlock(baseagent),
54
+ chatmode: { ...DEFAULT_CHATMODE },
55
+ };
56
+ const sel = { self: aid };
57
+ const existingBehavior = cfgRead(ConfigTarget.Agent, sel) || {};
58
+ cfgEnsure(ConfigTarget.Agent, sel);
59
+ cfgWrite(ConfigTarget.Agent, { ...existingBehavior, ...behavior }, sel);
60
+ }
61
+ function readAgentMdIdentity(aid) {
62
+ const agentMdFilePath = getAgentMdPathFromPaths(aid);
63
+ try {
64
+ if (!fs.existsSync(agentMdFilePath))
65
+ return { name: null, description: null };
66
+ const content = fs.readFileSync(agentMdFilePath, 'utf-8');
67
+ const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
68
+ if (!fmMatch)
69
+ return { name: null, description: null };
70
+ const fm = fmMatch[1];
71
+ const nameMatch = fm.match(/^name:\s*["']?(.+?)["']?\s*$/m);
72
+ const descMatch = fm.match(/^description:\s*["']?(.+?)["']?\s*$/m);
73
+ return {
74
+ name: nameMatch?.[1]?.trim() || null,
75
+ description: descMatch?.[1]?.trim() || null,
76
+ };
77
+ }
78
+ catch {
79
+ return { name: null, description: null };
80
+ }
81
+ }
82
+ function getAgentMdPath(aid) {
83
+ return getAgentMdPathFromPaths(aid);
84
+ }
85
+ /** Normalize path separators to forward slashes for cross-platform consistent display. */
86
+ function toPosix(p) {
87
+ if (!p)
88
+ return null;
89
+ return p.replace(/\\/g, '/');
90
+ }
91
+ // ==================== agentList ====================
92
+ export async function agentList() {
93
+ const p = resolvePaths();
94
+ // Try IPC first (running process has real status)
95
+ try {
96
+ const result = await ipcQuery(p.socket, { type: 'evolagent.list' });
97
+ if (result && result.ok && result.agents) {
98
+ const agents = result.agents.map((info) => ({
99
+ aid: info.aid || info.name,
100
+ name: info.name,
101
+ personalName: info.personalName || null,
102
+ status: info.status || 'stopped',
103
+ channels: info.channels || [],
104
+ projectPath: toPosix(info.projectPath || null),
105
+ baseagent: info.baseagent || null,
106
+ model: info.model || null,
107
+ lastActivity: info.lastActivity || null,
108
+ }));
109
+ return { ok: true, agents };
110
+ }
111
+ }
112
+ catch { }
113
+ // Cold mode: read from disk
114
+ const { EvolAgentRegistry } = await import('../core/evolagent-registry.js');
115
+ const registry = new EvolAgentRegistry(p.agentsDir);
116
+ registry.loadAll();
117
+ const agents = registry.list().map((info) => ({
118
+ aid: info.aid || info.name,
119
+ name: info.name,
120
+ personalName: info.personalName || null,
121
+ status: info.status || 'stopped',
122
+ channels: info.channels || [],
123
+ projectPath: toPosix(info.projectPath || null),
124
+ baseagent: info.baseagent || null,
125
+ model: info.model || null,
126
+ lastActivity: info.lastActivity || null,
127
+ }));
128
+ return { ok: true, agents };
129
+ }
130
+ // ==================== agentShow ====================
131
+ export async function agentShow(aid) {
132
+ const p = resolvePaths();
133
+ let agentInfo = null;
134
+ let connectionInfo = null;
135
+ // Try IPC for live data
136
+ try {
137
+ const [showResp, aidsResp, statsResp] = await Promise.all([
138
+ ipcQuery(p.socket, { type: 'evolagent.show', name: aid }),
139
+ ipcQuery(p.socket, { type: 'aun-aids' }),
140
+ ipcQuery(p.socket, { type: 'aun-aid-stats' }),
141
+ ]);
142
+ if (showResp?.ok && showResp.agent) {
143
+ agentInfo = showResp.agent;
144
+ }
145
+ // Build connection info from aun-aids + aun-aid-stats (same source as watch aid)
146
+ if (aidsResp?.ok && aidsResp.aids) {
147
+ const aidEntry = aidsResp.aids.find((a) => a.aid === aid);
148
+ if (aidEntry) {
149
+ const stats = statsResp?.ok && statsResp.stats
150
+ ? statsResp.stats.find((s) => s.aid === aid)
151
+ : null;
152
+ const now = Date.now();
153
+ connectionInfo = {
154
+ status: aidEntry.status || 'unknown',
155
+ uptime_ms: (aidEntry.status === 'connected' && aidEntry.lastConnectedAt)
156
+ ? now - aidEntry.lastConnectedAt : null,
157
+ reconnect_count: aidEntry.reconnectCount ?? 0,
158
+ messages_received: stats?.messagesReceived ?? 0,
159
+ messages_sent: stats?.messagesSent ?? 0,
160
+ bytes_received: stats?.bytesReceived ?? 0,
161
+ bytes_sent: stats?.bytesSent ?? 0,
162
+ last_received_at: stats?.lastReceivedAt ? new Date(stats.lastReceivedAt).toISOString() : null,
163
+ last_sent_at: stats?.lastSentAt ? new Date(stats.lastSentAt).toISOString() : null,
164
+ unique_peer_count: stats?.uniquePeerCount ?? 0,
165
+ };
166
+ }
167
+ }
168
+ }
169
+ catch { }
170
+ // Cold mode fallback for agent info
171
+ if (!agentInfo) {
172
+ const { EvolAgentRegistry } = await import('../core/evolagent-registry.js');
173
+ const registry = new EvolAgentRegistry(p.agentsDir);
174
+ registry.loadAll();
175
+ const agent = registry.get(aid);
176
+ if (!agent) {
177
+ const allList = registry.list();
178
+ const available = allList.map((i) => i.name).join(', ');
179
+ return { ok: false, error: `Agent "${aid}" not found.${available ? ` Available: ${available}` : ''}` };
180
+ }
181
+ agentInfo = {
182
+ name: agent.name,
183
+ aid: agent.aid || agent.name,
184
+ status: agent.status,
185
+ baseagent: agent.baseagent,
186
+ model: agent.model,
187
+ effort: agent.effort,
188
+ projectPath: agent.projectPath,
189
+ channels: agent.channelInstanceNames?.() || [],
190
+ activeSessions: agent.activeSessions || 0,
191
+ lastActivity: agent.lastActivity || null,
192
+ error: agent.error,
193
+ chatmode: agent.config?.chatmode || null,
194
+ };
195
+ }
196
+ const identity = readAgentMdIdentity(aid);
197
+ const agentMdPath = getAgentMdPath(aid);
198
+ return {
199
+ ok: true,
200
+ aid,
201
+ status: agentInfo.status || 'stopped',
202
+ identity,
203
+ config: {
204
+ baseagent: agentInfo.baseagent || null,
205
+ model: agentInfo.model || null,
206
+ effort: agentInfo.effort || null,
207
+ chatmode: agentInfo.chatmode || null,
208
+ channels: agentInfo.channels || [],
209
+ },
210
+ connection: connectionInfo,
211
+ sessions: {
212
+ active: agentInfo.activeSessions || 0,
213
+ last_activity: agentInfo.lastActivity ? new Date(agentInfo.lastActivity).toISOString() : null,
214
+ },
215
+ paths: {
216
+ config: toPosix(path.join(p.agentsDir, aid, 'config.json')),
217
+ agent_md: toPosix(agentMdPath),
218
+ project: toPosix(agentInfo.projectPath || null),
219
+ data: toPosix(path.join(p.agentsDir, aid, 'data')),
220
+ },
221
+ };
222
+ }
223
+ export async function agentCreateInteractive(opts = {}) {
224
+ const p = resolvePaths();
225
+ const ownRl = !opts.rl;
226
+ const rl = opts.rl ?? readline.createInterface({
227
+ input: opts.stdin || process.stdin,
228
+ output: opts.stdout || process.stdout,
229
+ });
230
+ const ask = (q) => new Promise(r => rl.question(q, r));
231
+ try {
232
+ const { isValidAid, aidCreate } = await import('../aun/aid/index.js');
233
+ const aidPrompt = opts.suggestedName
234
+ ? `AID [${opts.suggestedName}]: `
235
+ : 'AID (e.g. mybot.agentid.pub): ';
236
+ let aid = '';
237
+ while (!aid) {
238
+ const aidInput = (await ask(aidPrompt)).trim();
239
+ const candidate = aidInput || opts.suggestedName;
240
+ if (!candidate) {
241
+ console.log(' ⚠ AID is required.');
242
+ continue;
243
+ }
244
+ if (!isValidAid(candidate)) {
245
+ console.log(` ⚠ Invalid AID "${candidate}": must be a valid multi-level domain (e.g. mybot.agentid.pub)`);
246
+ continue;
247
+ }
248
+ aid = candidate;
249
+ }
250
+ const agentDirPath = path.join(p.agentsDir, aid);
251
+ const configExists = fs.existsSync(path.join(agentDirPath, 'config.json'));
252
+ if (configExists) {
253
+ const ans = (await ask(`Agent "${aid}" already exists. Overwrite config.json? (AID 与 agent.md 保留) [y/N]: `)).trim().toLowerCase();
254
+ if (ans !== 'y' && ans !== 'yes') {
255
+ return { ok: false, error: 'Aborted.' };
256
+ }
257
+ }
258
+ console.log(`\nCreating agent: ${aid}\n`);
259
+ let aidCreated = false;
260
+ try {
261
+ const result = await aidCreate(aid);
262
+ try {
263
+ await result.client.close();
264
+ }
265
+ catch { /* ignore */ }
266
+ aidCreated = !result.alreadyExisted;
267
+ console.log(` ✓ AID ${result.alreadyExisted ? 'reused' : 'created'}: ${aid}`);
268
+ }
269
+ catch (e) {
270
+ return { ok: false, error: `AID creation failed: ${e?.message || e}` };
271
+ }
272
+ // Project path
273
+ let suggestedProjectPath = '';
274
+ try {
275
+ const defaults = loadDefaults();
276
+ const rootPath = agentProjectRootFromDefaults(defaults, p.root);
277
+ suggestedProjectPath = deriveAgentProjectPath(rootPath, aid);
278
+ }
279
+ catch {
280
+ suggestedProjectPath = deriveAgentProjectPath(agentProjectRootFromDefaults(null, p.root), aid);
281
+ }
282
+ const projectInput = (await ask(`Project path [${suggestedProjectPath}]: `)).trim();
283
+ const projectPath = projectInput || suggestedProjectPath;
284
+ if (!path.isAbsolute(projectPath)) {
285
+ return { ok: false, error: 'Project path must be an absolute path.' };
286
+ }
287
+ if (!fs.existsSync(projectPath)) {
288
+ const create = (await ask(`Project path does not exist. Create? [Y/n]: `)).trim().toLowerCase();
289
+ if (create === '' || create === 'y' || create === 'yes') {
290
+ try {
291
+ fs.mkdirSync(projectPath, { recursive: true });
292
+ }
293
+ catch (e) {
294
+ return { ok: false, error: `Failed to create project path "${projectPath}": ${e?.message || e}` };
295
+ }
296
+ console.log(` ✓ Created ${projectPath}`);
297
+ }
298
+ else {
299
+ return { ok: false, error: 'Aborted.' };
300
+ }
301
+ }
302
+ // Baseagent
303
+ const available = detectAvailableBaseagents();
304
+ if (available.length === 0) {
305
+ return { ok: false, error: `No usable baseagent detected. Install claude/gemini CLI or codex CLI with app-server.` };
306
+ }
307
+ const defaultBa = pickDefaultBaseagent(available);
308
+ let baseagent;
309
+ if (available.length === 1) {
310
+ console.log(` Baseagent: ${defaultBa}`);
311
+ baseagent = defaultBa;
312
+ }
313
+ else {
314
+ let chosen = null;
315
+ while (chosen === null) {
316
+ const input = (await ask(`Baseagent (${available.join('/')}) [${defaultBa}]: `)).trim() || defaultBa;
317
+ if (!BASEAGENT_CANDIDATES.includes(input)) {
318
+ console.log(` Invalid choice. Options: ${BASEAGENT_CANDIDATES.join('/')}`);
319
+ continue;
320
+ }
321
+ if (!available.includes(input)) {
322
+ console.log(` ${input} is not available in the current environment. Available: ${available.join('/')}`);
323
+ continue;
324
+ }
325
+ chosen = input;
326
+ }
327
+ baseagent = chosen;
328
+ }
329
+ // Owner: only prompt if daemon (daemon.json) has owners configured.
330
+ // If daemon has no owner, agent owner is meaningless and should be skipped entirely.
331
+ const { loadDaemonConfig } = await import('../config-store.js');
332
+ const daemonCfg = loadDaemonConfig();
333
+ const daemonHasOwner = daemonCfg.owners && daemonCfg.owners.length > 0;
334
+ let owner;
335
+ let skipQrBinding = false;
336
+ if (daemonHasOwner) {
337
+ const defaultOwner = daemonCfg.owners[0];
338
+ while (true) {
339
+ const prompt = `Owner AID [${defaultOwner}]: `;
340
+ const ownerInput = (await ask(prompt)).trim();
341
+ // User pressed Enter - use default owner
342
+ if (!ownerInput) {
343
+ owner = defaultOwner;
344
+ break;
345
+ }
346
+ // User entered "new" or "qr" - trigger QR binding flow
347
+ if (ownerInput.toLowerCase() === 'new' || ownerInput.toLowerCase() === 'qr') {
348
+ owner = undefined;
349
+ skipQrBinding = false;
350
+ break;
351
+ }
352
+ // User entered a specific AID - validate and use it
353
+ if (!isValidAid(ownerInput)) {
354
+ console.log(` ⚠ Invalid Owner AID "${ownerInput}": must be a valid multi-level domain (e.g. alice.agentid.pub)`);
355
+ console.log(` 提示: 直接回车使用 ${defaultOwner},输入 "new" 扫码绑定新 AID`);
356
+ continue;
357
+ }
358
+ owner = ownerInput;
359
+ skipQrBinding = true; // Skip QR if user manually entered an AID
360
+ break;
361
+ }
362
+ }
363
+ // Name + description for agent.md
364
+ const defaultName = aid.split('.')[0];
365
+ const agentName = (await ask(`Display name [${defaultName}]: `)).trim() || defaultName;
366
+ const agentDescription = (await ask('Description (optional): ')).trim() || '';
367
+ if (ownRl)
368
+ rl.close();
369
+ const agentConfig = withStaticOwner({
370
+ $schema_version: CONFIG_SCHEMA_VERSION,
371
+ aid,
372
+ enabled: true,
373
+ lifecycle: 'created',
374
+ channels: [],
375
+ projects: { defaultPath: projectPath },
376
+ }, owner);
377
+ saveAgent(agentConfig);
378
+ ensureInitialRoleRegistry(aid);
379
+ saveInitialBehavior(aid, baseagent);
380
+ ensureAgentDirSkeleton(aid);
381
+ // Generate and upload agent.md
382
+ let agentmdUploaded = false;
383
+ try {
384
+ const { buildInitialAgentMd, agentmdPut } = await import('../aun/aid/index.js');
385
+ let content = buildInitialAgentMd({ aid });
386
+ content = content.replace(/^name:\s*".*?"$/m, `name: "${agentName}"`);
387
+ if (agentDescription) {
388
+ content = content.replace(/^description:\s*".*?"$/m, `description: "${agentDescription}"`);
389
+ }
390
+ const aunPath = process.env.AUN_HOME || defaultAunPath();
391
+ // agentmdPut 会写本地文件到 agentMdPath(aid) 并调用 publishAgentMd
392
+ // Upload with retry (3 attempts, 2s delay between retries)
393
+ const MAX_ATTEMPTS = 3;
394
+ const RETRY_DELAY_MS = 2000;
395
+ let lastError;
396
+ for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
397
+ try {
398
+ if (attempt > 1) {
399
+ process.stdout.write(` ↻ agent.md 上传重试 (${attempt}/${MAX_ATTEMPTS})...\n`);
400
+ await new Promise(r => setTimeout(r, RETRY_DELAY_MS));
401
+ }
402
+ await agentmdPut(content, { aid, aunPath });
403
+ agentmdUploaded = true;
404
+ break;
405
+ }
406
+ catch (e) {
407
+ lastError = e;
408
+ }
409
+ }
410
+ if (!agentmdUploaded) {
411
+ console.warn(` ⚠ agent.md upload failed: ${lastError?.message || lastError}`);
412
+ console.warn(` → Retry later with: ec aid agentmd put ${aid}`);
413
+ }
414
+ // Yield to allow the SDK WebSocket to fully close before IPC
415
+ await new Promise(r => setTimeout(r, 0));
416
+ }
417
+ catch (e) {
418
+ console.warn(` ⚠ agent.md generation failed: ${e?.message || e}`);
419
+ }
420
+ // Attempt hot-load via IPC (if daemon is running).
421
+ // Cold-starting a new agent (connecting AUN WebSocket) routinely takes
422
+ // >3s, so use a generous timeout to avoid a false "service not running"
423
+ // report while the daemon actually finishes bringing the agent online.
424
+ let hotLoaded = false;
425
+ let hotLoadError;
426
+ try {
427
+ const ipcResult = await ipcQuery(p.socket, { type: 'evolagent.load', aid }, 30_000);
428
+ if (ipcResult?.ok) {
429
+ hotLoaded = true;
430
+ }
431
+ else if (ipcResult) {
432
+ hotLoadError = ipcResult.error;
433
+ }
434
+ }
435
+ catch { /* daemon not running */ }
436
+ let ownerBoundAid;
437
+ let ownerBindSkipped = false;
438
+ // Only prompt for agent owner binding if:
439
+ // 1. User didn't manually input owner during setup (owner is undefined)
440
+ // 2. User didn't skip QR binding explicitly (skipQrBinding is false)
441
+ // 3. We're in an interactive terminal
442
+ // 4. The daemon (daemon.json) has at least one owner configured
443
+ if (!owner && !skipQrBinding && process.stdin.isTTY && daemonHasOwner) {
444
+ console.log('\n📡 Agent owner 绑定 — 请用 evol app 扫描二维码,10 分钟内有效\n');
445
+ try {
446
+ const { runAgentOwnerQrBindFlow } = await import('./init-channel.js');
447
+ const bound = await runAgentOwnerQrBindFlow(aid, agentName, 'append');
448
+ if (bound?.boundAid) {
449
+ ownerBoundAid = bound.boundAid;
450
+ console.log(` ✓ 已配置 agent owner: ${bound.boundAid}`);
451
+ }
452
+ else {
453
+ ownerBindSkipped = true;
454
+ ownerBoundAid = await promptAgentOwnerManually(aid);
455
+ }
456
+ }
457
+ catch (e) {
458
+ ownerBindSkipped = true;
459
+ console.log(` ⚠ 扫码绑定不可用: ${e?.message || e}`);
460
+ ownerBoundAid = await promptAgentOwnerManually(aid);
461
+ }
462
+ }
463
+ return {
464
+ ok: true,
465
+ aid,
466
+ configPath: toPosix(path.join(agentDirPath, 'config.json')),
467
+ aidCreated,
468
+ agentmdUploaded,
469
+ hotLoaded,
470
+ hotLoadError,
471
+ ownerBoundAid,
472
+ ownerBindSkipped,
473
+ };
474
+ }
475
+ finally {
476
+ if (ownRl) {
477
+ try {
478
+ rl.close();
479
+ }
480
+ catch { /* ignore */ }
481
+ }
482
+ }
483
+ }
484
+ async function promptAgentOwnerManually(aid) {
485
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
486
+ try {
487
+ while (true) {
488
+ const raw = await new Promise(resolve => rl.question('\n扫码未完成。手动输入 agent owner AID(留空跳过): ', resolve));
489
+ const owner = raw.trim();
490
+ if (!owner) {
491
+ console.log(' 已跳过 agent owner 配置');
492
+ return undefined;
493
+ }
494
+ const { isValidAid } = await import('../aun/aid/index.js');
495
+ if (!isValidAid(owner)) {
496
+ console.log(' ⚠ Owner AID 格式无效(需合法多级域名,如 alice.agentid.pub)');
497
+ continue;
498
+ }
499
+ const agent = loadAgent(aid);
500
+ if (!agent) {
501
+ console.log(` ⚠ 无法加载 agent 配置: ${aid}`);
502
+ return undefined;
503
+ }
504
+ saveAgent(withStaticOwner(agent, owner));
505
+ try {
506
+ const result = await ipcQuery(resolvePaths().socket, { type: 'evolagent.reload', name: aid }, 30_000);
507
+ if (result?.ok)
508
+ console.log(' ✓ agent owner 已热重载');
509
+ }
510
+ catch { /* daemon not running */ }
511
+ console.log(` ✓ 已配置 agent owner: ${owner}`);
512
+ return owner;
513
+ }
514
+ }
515
+ finally {
516
+ rl.close();
517
+ }
518
+ }
519
+ export async function agentCreateNonInteractive(opts) {
520
+ const p = resolvePaths();
521
+ const { isValidAid, aidCreate } = await import('../aun/aid/index.js');
522
+ opts.onPhase?.('validating', 'begin');
523
+ /** 校验失败:透出 failed 进度并返回原结构(控制流不变)。 */
524
+ const failValidating = (error) => {
525
+ opts.onPhase?.('validating', 'failed', error);
526
+ return { ok: false, error };
527
+ };
528
+ if (!isValidAid(opts.aid)) {
529
+ return failValidating(`Invalid AID "${opts.aid}": must be a valid multi-level domain (e.g. mybot.agentid.pub)`);
530
+ }
531
+ const agentDirPath = path.join(p.agentsDir, opts.aid);
532
+ const configExists = fs.existsSync(path.join(agentDirPath, 'config.json'));
533
+ if (configExists && !opts.force) {
534
+ return failValidating(`Agent "${opts.aid}" already exists: ${agentDirPath}/config.json (use --force to overwrite)`);
535
+ }
536
+ // Baseagent
537
+ const available = detectAvailableBaseagents();
538
+ if (available.length === 0) {
539
+ return failValidating(`No usable baseagent detected. Install claude/gemini CLI or codex CLI with app-server.`);
540
+ }
541
+ let baseagent;
542
+ if (opts.baseagent) {
543
+ if (!BASEAGENT_CANDIDATES.includes(opts.baseagent)) {
544
+ return failValidating(`Invalid baseagent: ${opts.baseagent} (options: ${BASEAGENT_CANDIDATES.join('/')})`);
545
+ }
546
+ if (!available.includes(opts.baseagent)) {
547
+ const reason = opts.baseagent === 'codex'
548
+ ? getCodexAppServerAvailability().reason
549
+ : undefined;
550
+ return failValidating(reason || `${opts.baseagent} is not available in the current environment (available: ${available.join('/')})`);
551
+ }
552
+ baseagent = opts.baseagent;
553
+ }
554
+ else {
555
+ baseagent = pickDefaultBaseagent(available);
556
+ }
557
+ if (!path.isAbsolute(opts.project)) {
558
+ return failValidating(`--project must be absolute: ${opts.project}`);
559
+ }
560
+ if (!fs.existsSync(opts.project)) {
561
+ try {
562
+ fs.mkdirSync(opts.project, { recursive: true });
563
+ }
564
+ catch (e) {
565
+ return failValidating(`Failed to create ${opts.project}: ${e?.message || e}`);
566
+ }
567
+ }
568
+ if (opts.owner && !isValidAid(opts.owner)) {
569
+ return failValidating(`Invalid owner: ${opts.owner}`);
570
+ }
571
+ opts.onPhase?.('validating', 'done');
572
+ // Register AID
573
+ opts.onPhase?.('registering_aid', 'begin');
574
+ let aidCreated = false;
575
+ try {
576
+ const result = await aidCreate(opts.aid);
577
+ try {
578
+ await result.client.close();
579
+ }
580
+ catch { /* ignore */ }
581
+ aidCreated = !result.alreadyExisted;
582
+ opts.onPhase?.('registering_aid', 'done', aidCreated ? 'created' : 'existed');
583
+ }
584
+ catch (e) {
585
+ const error = `AID creation failed: ${e?.message || e}`;
586
+ opts.onPhase?.('registering_aid', 'failed', error);
587
+ return { ok: false, error };
588
+ }
589
+ // Force 模式下若 agent 已存在,保留生命周期(避免重复 bootstrap/welcome)。
590
+ let preservedLifecycle = 'created';
591
+ if (configExists) {
592
+ try {
593
+ const existing = loadAgent(opts.aid);
594
+ if (existing?.lifecycle)
595
+ preservedLifecycle = existing.lifecycle;
596
+ }
597
+ catch { /* ignore */ }
598
+ }
599
+ const agentConfig = withStaticOwner({
600
+ $schema_version: CONFIG_SCHEMA_VERSION,
601
+ aid: opts.aid,
602
+ enabled: true,
603
+ lifecycle: preservedLifecycle,
604
+ channels: [],
605
+ projects: { defaultPath: opts.project },
606
+ }, opts.owner);
607
+ opts.onPhase?.('config_saved', 'begin');
608
+ saveAgent(agentConfig);
609
+ ensureInitialRoleRegistry(opts.aid);
610
+ saveInitialBehavior(opts.aid, baseagent);
611
+ ensureAgentDirSkeleton(opts.aid);
612
+ opts.onPhase?.('config_saved', 'done');
613
+ // Generate and upload agent.md
614
+ opts.onPhase?.('uploading_agentmd', 'begin');
615
+ let agentmdUploaded = false;
616
+ try {
617
+ const { buildInitialAgentMd, agentmdPut } = await import('../aun/aid/index.js');
618
+ const agentName = opts.name || opts.aid.split('.')[0];
619
+ const agentDescription = opts.description || '';
620
+ let content = buildInitialAgentMd({ aid: opts.aid });
621
+ content = content.replace(/^name:\s*".*?"$/m, `name: "${agentName}"`);
622
+ if (agentDescription) {
623
+ content = content.replace(/^description:\s*".*?"$/m, `description: "${agentDescription}"`);
624
+ }
625
+ const aunPath = process.env.AUN_HOME || defaultAunPath();
626
+ // agentmdPut 会写本地文件到 agentMdPath(aid) 并调用 publishAgentMd
627
+ const MAX_ATTEMPTS = 3;
628
+ const RETRY_DELAY_MS = 2000;
629
+ let lastError;
630
+ for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
631
+ try {
632
+ if (attempt > 1)
633
+ await new Promise(r => setTimeout(r, RETRY_DELAY_MS));
634
+ await agentmdPut(content, { aid: opts.aid, aunPath });
635
+ agentmdUploaded = true;
636
+ opts.onPhase?.('uploading_agentmd', 'done');
637
+ break;
638
+ }
639
+ catch (e) {
640
+ lastError = e;
641
+ }
642
+ }
643
+ if (!agentmdUploaded) {
644
+ console.warn(`⚠ agent.md upload failed: ${lastError?.message || lastError}`);
645
+ console.warn(` Retry later with: ec aid agentmd put ${opts.aid}`);
646
+ opts.onPhase?.('uploading_agentmd', 'warn', `upload failed: ${lastError?.message || lastError}`);
647
+ }
648
+ await new Promise(r => setTimeout(r, 0));
649
+ }
650
+ catch (e) {
651
+ console.warn(`⚠ agent.md generation failed: ${e?.message || e}`);
652
+ opts.onPhase?.('uploading_agentmd', 'warn', `generation failed: ${e?.message || e}`);
653
+ }
654
+ // Attempt hot-load via IPC (if daemon is running).
655
+ // Cold-starting a new agent (connecting AUN WebSocket) routinely takes
656
+ // >3s, so use a generous timeout to avoid a false "service not running"
657
+ // report while the daemon actually finishes bringing the agent online.
658
+ let hotLoaded = false;
659
+ let hotLoadError;
660
+ opts.onPhase?.('hot_loading', 'begin');
661
+ try {
662
+ const ipcResult = await ipcQuery(p.socket, { type: 'evolagent.load', aid: opts.aid }, 30_000);
663
+ if (ipcResult?.ok) {
664
+ hotLoaded = true;
665
+ opts.onPhase?.('hot_loading', 'done');
666
+ }
667
+ else if (ipcResult) {
668
+ hotLoadError = ipcResult.error;
669
+ opts.onPhase?.('hot_loading', 'warn', hotLoadError);
670
+ }
671
+ else {
672
+ opts.onPhase?.('hot_loading', 'warn', 'daemon not running');
673
+ }
674
+ }
675
+ catch {
676
+ opts.onPhase?.('hot_loading', 'warn', 'daemon not running'); /* daemon not running */
677
+ }
678
+ return {
679
+ ok: true,
680
+ aid: opts.aid,
681
+ ownerAid: opts.owner,
682
+ configPath: toPosix(path.join(agentDirPath, 'config.json')),
683
+ aidCreated,
684
+ agentmdUploaded,
685
+ hotLoaded,
686
+ hotLoadError,
687
+ };
688
+ }
689
+ // ==================== agentSyncAids (deprecated) ====================
690
+ /** @deprecated sync-aids 已废弃,不再从 CLI 调用 */
691
+ export async function agentSyncAids() {
692
+ const p = resolvePaths();
693
+ const { aidList } = await import('../aun/aid/index.js');
694
+ const aunPath = process.env.AUN_HOME || defaultAunPath();
695
+ const allAids = aidList(aunPath);
696
+ const localAids = allAids.filter(a => a.hasPrivateKey).map(a => a.aid);
697
+ if (localAids.length === 0) {
698
+ return { ok: true, created: [], template: null, hotReloaded: false };
699
+ }
700
+ const { agents } = loadAllAgents();
701
+ const existingAids = new Set(agents.map(a => a.aid));
702
+ // Find template (earliest mtime)
703
+ let templateAgent = agents[0] || null;
704
+ if (agents.length > 1) {
705
+ let earliestMtime = Infinity;
706
+ for (const a of agents) {
707
+ const configPath = path.join(p.agentsDir, a.aid, 'config.json');
708
+ try {
709
+ const stat = fs.statSync(configPath);
710
+ if (stat.mtimeMs < earliestMtime) {
711
+ earliestMtime = stat.mtimeMs;
712
+ templateAgent = a;
713
+ }
714
+ }
715
+ catch { }
716
+ }
717
+ }
718
+ if (!templateAgent) {
719
+ return { ok: false, error: '没有可用的模板 agent。请先创建第一个 agent:ec agent new <aid>' };
720
+ }
721
+ const defaults = loadDefaults();
722
+ const rootPath = agentProjectRootFromDefaults(defaults, p.root);
723
+ const created = [];
724
+ for (const aid of localAids) {
725
+ if (existingAids.has(aid))
726
+ continue;
727
+ const projectPath = deriveAgentProjectPath(rootPath, aid);
728
+ const { roles: _legacyRoles, ...templateConfig } = JSON.parse(JSON.stringify(templateAgent));
729
+ const newConfig = {
730
+ ...templateConfig,
731
+ aid,
732
+ channels: [],
733
+ projects: { defaultPath: projectPath },
734
+ $schema_version: CONFIG_SCHEMA_VERSION,
735
+ };
736
+ try {
737
+ saveAgent(newConfig);
738
+ ensureInitialRoleRegistry(aid);
739
+ ensureAgentDirSkeleton(aid);
740
+ created.push(aid);
741
+ }
742
+ catch { }
743
+ }
744
+ // Hot-reload if running
745
+ let hotReloaded = false;
746
+ if (created.length > 0) {
747
+ try {
748
+ const result = await ipcQuery(p.socket, { type: 'evolagent.resync' });
749
+ hotReloaded = !!result?.ok;
750
+ }
751
+ catch { }
752
+ }
753
+ return { ok: true, created, template: templateAgent.aid, hotReloaded };
754
+ }
755
+ // ==================== agentReload ====================
756
+ export async function agentReload(aid) {
757
+ const p = resolvePaths();
758
+ if (!aid) {
759
+ // Full resync
760
+ try {
761
+ const result = await ipcQuery(p.socket, { type: 'evolagent.resync' });
762
+ if (result === null) {
763
+ return { ok: false, error: 'evolcore 未运行,请先 ec start' };
764
+ }
765
+ if (result?.ok) {
766
+ return { ok: true, results: result.results || [] };
767
+ }
768
+ return { ok: false, error: result?.error || 'unknown error' };
769
+ }
770
+ catch {
771
+ return { ok: false, error: 'evolcore 未运行,请先 ec start' };
772
+ }
773
+ }
774
+ // Single agent reload
775
+ try {
776
+ const result = await ipcQuery(p.socket, { type: 'evolagent.reload', name: aid });
777
+ if (result === null) {
778
+ return { ok: false, error: 'evolcore 未运行,请先 ec start 后再 reload' };
779
+ }
780
+ if (result?.ok) {
781
+ return { ok: true };
782
+ }
783
+ return { ok: false, error: result?.error || 'unknown error' };
784
+ }
785
+ catch {
786
+ return { ok: false, error: 'evolcore 未运行,请先 ec start 后再 reload' };
787
+ }
788
+ }
789
+ // ==================== agentEnable / agentDisable ====================
790
+ export async function agentEnable(aid) {
791
+ return agentSetEnabled(aid, true);
792
+ }
793
+ export async function agentDisable(aid) {
794
+ return agentSetEnabled(aid, false);
795
+ }
796
+ async function agentSetEnabled(aid, enabled) {
797
+ const p = resolvePaths();
798
+ let config;
799
+ try {
800
+ config = loadAgent(aid);
801
+ }
802
+ catch (e) {
803
+ return { ok: false, error: `Failed to read config: ${e?.message || e}` };
804
+ }
805
+ if (!config) {
806
+ return { ok: false, error: `Agent "${aid}" not found` };
807
+ }
808
+ config.enabled = enabled;
809
+ saveAgent(config);
810
+ // Try to hot-reload if daemon is running, but don't fail if it's not
811
+ let reloaded = false;
812
+ try {
813
+ const result = await ipcQuery(p.socket, { type: 'evolagent.reload', name: aid }, 1000);
814
+ if (result?.ok) {
815
+ reloaded = true;
816
+ }
817
+ }
818
+ catch {
819
+ // Daemon not running or unreachable — that's ok, config is already saved
820
+ }
821
+ return { ok: true, aid, enabled, reloaded };
822
+ }
823
+ async function runManagedAgentConfig(args, options) {
824
+ const sessionId = process.env.EVOLCORE_SESSION_ID;
825
+ if (!sessionId || options.trustedLocal)
826
+ return { managed: false };
827
+ const delegationToken = process.env[AGENT_DELEGATION_TOKEN_ENV];
828
+ if (!delegationToken) {
829
+ return {
830
+ managed: true,
831
+ error: { ok: false, code: 'DELEGATION_REQUIRED', error: `${AGENT_DELEGATION_TOKEN_ENV} is not set for the active task` },
832
+ };
833
+ }
834
+ try {
835
+ const response = await ipcQuery(resolvePaths().socket, {
836
+ type: 'config.op',
837
+ argv: ['config', ...args],
838
+ sessionId,
839
+ delegationToken,
840
+ }, 10_000);
841
+ if (!response) {
842
+ return { managed: true, error: { ok: false, code: 'DAEMON_UNAVAILABLE', error: 'Unable to connect to the evolcore daemon' } };
843
+ }
844
+ if (!response.ok || !response.result?.ok) {
845
+ return {
846
+ managed: true,
847
+ error: { ok: false, code: response.code || 'CONFIG_AUTH_FAILED', error: response.error || 'config operation failed' },
848
+ };
849
+ }
850
+ return { managed: true, response };
851
+ }
852
+ catch {
853
+ return { managed: true, error: { ok: false, code: 'DAEMON_UNAVAILABLE', error: 'Unable to connect to the evolcore daemon' } };
854
+ }
855
+ }
856
+ function executeLocalAgentConfig(args) {
857
+ initConfigManager();
858
+ const resolved = resolveConfigCommand(['config', ...args]);
859
+ if (!resolved.ok)
860
+ return { ok: false, code: resolved.code, error: resolved.reason };
861
+ return executeResolvedConfigCommand(resolved.command);
862
+ }
863
+ export async function agentGet(aid, key, options = {}) {
864
+ const managed = await runManagedAgentConfig(['get', key, '--self', aid], options);
865
+ if (managed.managed) {
866
+ if ('error' in managed)
867
+ return managed.error;
868
+ const result = managed.response.result;
869
+ if (result.ok && result.subcommand === 'get')
870
+ return { ok: true, aid, key, value: result.value };
871
+ return { ok: false, code: 'UNEXPECTED_RESULT', error: 'config get returned an unexpected result' };
872
+ }
873
+ const p = resolvePaths();
874
+ if (!fs.existsSync(path.join(p.agentsDir, aid, 'config.json'))) {
875
+ return { ok: false, error: `Agent "${aid}" not found` };
876
+ }
877
+ try {
878
+ const result = executeLocalAgentConfig(['get', key, '--self', aid]);
879
+ return result.ok && result.subcommand === 'get'
880
+ ? { ok: true, aid, key, value: result.value }
881
+ : { ok: false, code: result.ok ? 'UNEXPECTED_RESULT' : result.code, error: result.ok ? 'config get returned an unexpected result' : result.error };
882
+ }
883
+ catch (e) {
884
+ return { ok: false, error: e?.message || String(e) };
885
+ }
886
+ }
887
+ export async function agentSet(aid, key, rawValue, options = {}) {
888
+ const managed = await runManagedAgentConfig(['set', key, rawValue, '--self', aid], options);
889
+ if (managed.managed) {
890
+ if ('error' in managed)
891
+ return managed.error;
892
+ const result = managed.response.result;
893
+ if (result.ok && result.subcommand === 'set') {
894
+ return { ok: true, aid, key, value: result.value, reloaded: false };
895
+ }
896
+ return { ok: false, code: 'UNEXPECTED_RESULT', error: 'config set returned an unexpected result' };
897
+ }
898
+ const p = resolvePaths();
899
+ if (!fs.existsSync(path.join(p.agentsDir, aid, 'config.json'))) {
900
+ return { ok: false, error: `Agent "${aid}" not found` };
901
+ }
902
+ let value;
903
+ try {
904
+ const result = executeLocalAgentConfig(['set', key, rawValue, '--self', aid]);
905
+ if (!result.ok)
906
+ return { ok: false, code: result.code, error: result.error };
907
+ if (result.subcommand !== 'set')
908
+ return { ok: false, code: 'UNEXPECTED_RESULT', error: 'config set returned an unexpected result' };
909
+ value = result.value;
910
+ }
911
+ catch (e) {
912
+ return { ok: false, error: e?.message || String(e) };
913
+ }
914
+ // Try hot-reload
915
+ let reloaded = false;
916
+ try {
917
+ const result = await ipcQuery(p.socket, { type: 'evolagent.reload', name: aid });
918
+ reloaded = !!result?.ok;
919
+ }
920
+ catch { }
921
+ return { ok: true, aid, key, value, reloaded };
922
+ }
923
+ /**
924
+ * Add or overwrite a channel instance on a per-agent config.
925
+ *
926
+ * - AUN type is rejected: AUN is implicit (managed by agent.aid + channel-loader),
927
+ * writing to channels[] has no effect.
928
+ * - mode='add' + existing (type, name) → error
929
+ * - mode='overwrite' + missing (type, name) → error
930
+ *
931
+ * Saves config atomically and triggers a hot-reload IPC. If the daemon is not
932
+ * running or reload fails, returns reloaded:false (no error).
933
+ */
934
+ export async function agentChannelUpsert(opts) {
935
+ const p = resolvePaths();
936
+ const SUPPORTED_TYPES = new Set(['feishu', 'wechat', 'dingtalk', 'qqbot', 'wecom']);
937
+ if (opts.channel.type === 'aun') {
938
+ return { ok: false, error: 'AUN channel cannot be configured via channels[] (managed implicitly by agent.aid)' };
939
+ }
940
+ if (!SUPPORTED_TYPES.has(opts.channel.type)) {
941
+ return { ok: false, error: `Unsupported channel type: ${opts.channel.type} (allowed: ${[...SUPPORTED_TYPES].join('/')})` };
942
+ }
943
+ if (!isValidChannelName(opts.channel.name)) {
944
+ return { ok: false, error: `Invalid channel name: ${JSON.stringify(opts.channel.name)} (empty or contains '#')` };
945
+ }
946
+ let config;
947
+ try {
948
+ config = loadAgent(opts.aid);
949
+ }
950
+ catch (e) {
951
+ return { ok: false, error: `Failed to load agent config: ${e?.message || e}` };
952
+ }
953
+ if (!config) {
954
+ return { ok: false, error: `Agent "${opts.aid}" not found` };
955
+ }
956
+ const channels = config.channels || [];
957
+ const matchIdx = channels.findIndex(c => c.type === opts.channel.type && c.name === opts.channel.name);
958
+ if (opts.mode === 'add') {
959
+ if (matchIdx >= 0) {
960
+ return { ok: false, error: `Channel (${opts.channel.type}, ${opts.channel.name}) already exists; pick a different name or use overwrite` };
961
+ }
962
+ channels.push(opts.channel);
963
+ }
964
+ else {
965
+ if (matchIdx < 0) {
966
+ return { ok: false, error: `Channel (${opts.channel.type}, ${opts.channel.name}) not found` };
967
+ }
968
+ channels[matchIdx] = opts.channel;
969
+ }
970
+ config.channels = channels;
971
+ saveAgent(config);
972
+ let reloaded = false;
973
+ try {
974
+ const result = await ipcQuery(p.socket, { type: 'evolagent.reload', name: opts.aid });
975
+ reloaded = !!result?.ok;
976
+ }
977
+ catch { /* daemon not running or reload failed */ }
978
+ return {
979
+ ok: true,
980
+ aid: opts.aid,
981
+ channelKey: `${opts.channel.type}#${opts.aid}#${opts.channel.name}`,
982
+ reloaded,
983
+ };
984
+ }
985
+ // ==================== agentDelete ====================
986
+ export async function agentDelete(aid, purge = false) {
987
+ const p = resolvePaths();
988
+ const agentDir = path.join(p.agentsDir, aid);
989
+ const configPath = path.join(agentDir, 'config.json');
990
+ if (!fs.existsSync(configPath)) {
991
+ return { ok: false, error: `Agent "${aid}" not found` };
992
+ }
993
+ // Try to stop via IPC first
994
+ let stopped = false;
995
+ try {
996
+ const result = await ipcQuery(p.socket, { type: 'evolagent.reload', name: aid });
997
+ stopped = !!result?.ok;
998
+ }
999
+ catch { }
1000
+ if (purge) {
1001
+ fs.rmSync(agentDir, { recursive: true, force: true });
1002
+ }
1003
+ else {
1004
+ fs.unlinkSync(configPath);
1005
+ // 清理构建进度文件(非 purge 删除只移除 config.json,需显式清理 create-status.json)
1006
+ const { removeCreateStatus } = await import('../core/message/create-status.js');
1007
+ removeCreateStatus(agentDir);
1008
+ }
1009
+ // Trigger resync so daemon drops the agent
1010
+ try {
1011
+ await ipcQuery(p.socket, { type: 'evolagent.resync' });
1012
+ }
1013
+ catch { }
1014
+ return { ok: true, aid, purged: purge, stopped };
1015
+ }
1016
+ // ==================== agentRename ====================
1017
+ export async function agentRename(aid, name) {
1018
+ const config = loadAgent(aid);
1019
+ if (!config) {
1020
+ return { ok: false, error: `Agent "${aid}" not found` };
1021
+ }
1022
+ const agentMdFilePath = getAgentMdPath(aid);
1023
+ if (!fs.existsSync(agentMdFilePath)) {
1024
+ return { ok: false, error: `agent.md not found: ${toPosix(agentMdFilePath)}` };
1025
+ }
1026
+ const content = fs.readFileSync(agentMdFilePath, 'utf-8');
1027
+ const quotedName = JSON.stringify(name);
1028
+ let next;
1029
+ if (/^---\n[\s\S]*?\n---/.test(content)) {
1030
+ const frontmatter = content.match(/^---\n([\s\S]*?)\n---/);
1031
+ const bodyStart = frontmatter ? frontmatter[0].length : 0;
1032
+ const fm = frontmatter?.[1] ?? '';
1033
+ const nextFm = /^name:\s*.*$/m.test(fm)
1034
+ ? fm.replace(/^name:\s*.*$/m, `name: ${quotedName}`)
1035
+ : `name: ${quotedName}\n${fm}`;
1036
+ next = `---\n${nextFm}\n---${content.slice(bodyStart)}`;
1037
+ }
1038
+ else {
1039
+ next = `---\naid: ${JSON.stringify(aid)}\nname: ${quotedName}\n---\n${content}`;
1040
+ }
1041
+ fs.mkdirSync(path.dirname(agentMdFilePath), { recursive: true });
1042
+ fs.writeFileSync(agentMdFilePath, next);
1043
+ return { ok: true, aid, name };
1044
+ }
1045
+ // ==================== agentReady ====================
1046
+ export async function agentReady(aid) {
1047
+ const p = resolvePaths();
1048
+ const config = loadAgent(aid);
1049
+ if (!config)
1050
+ return { ok: false, error: `Agent "${aid}" not found` };
1051
+ saveAgent(withLifecycleForWrite(config, 'active'));
1052
+ let reloaded = false;
1053
+ try {
1054
+ const result = await ipcQuery(p.socket, { type: 'evolagent.bootstrapComplete', aid }, 30_000);
1055
+ reloaded = !!result?.ok;
1056
+ }
1057
+ catch { /* daemon not running */ }
1058
+ return { ok: true, aid, reloaded };
1059
+ }