evolcore 0.0.4 → 0.0.6

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 (332) hide show
  1. package/CHANGELOG.md +65 -3
  2. package/README.md +4 -12
  3. package/dist/agents/baseagent.js +39 -0
  4. package/dist/agents/claude-runner.js +10 -2
  5. package/dist/agents/codex-runner.js +37 -8
  6. package/dist/agents/ecagent-runner.js +1159 -0
  7. package/dist/agents/gemini-runner.js +4 -1
  8. package/dist/agents/runner-types.js +20 -4
  9. package/dist/aun/msg/history.js +32 -9
  10. package/dist/aun/outbox.js +4 -9
  11. package/dist/channels/aun.js +32 -6
  12. package/dist/channels/daemon.js +7 -4
  13. package/dist/channels/dingtalk.js +3 -5
  14. package/dist/channels/feishu.js +16 -108
  15. package/dist/channels/wechat.js +4 -4
  16. package/dist/channels/wecom.js +4 -6
  17. package/dist/cli/agent-command.js +1 -1
  18. package/dist/cli/agent.js +42 -11
  19. package/dist/cli/aun-commands.js +50 -3
  20. package/dist/cli/daemon-commands.js +127 -21
  21. package/dist/cli/data-command.js +132 -0
  22. package/dist/cli/index.js +11 -3
  23. package/dist/cli/init.js +116 -37
  24. package/dist/cli/net-check.js +9 -20
  25. package/dist/cli/restart-monitor.js +11 -2
  26. package/dist/cli/trigger-command.js +1 -1
  27. package/dist/cli/watch-msg.js +12 -25
  28. package/dist/config/access-policy-domain.js +0 -20
  29. package/dist/config/access-policy.js +5 -29
  30. package/dist/config/builtin-roles.js +3 -1
  31. package/dist/config/config-field-policy.js +1 -1
  32. package/dist/config/gateway-config.js +21 -4
  33. package/dist/config/resolved-config-op.js +10 -0
  34. package/dist/core/auth/operation-catalog.js +1 -1
  35. package/dist/core/command/command-handler.js +2 -2
  36. package/dist/core/command/connect-menu.js +20 -42
  37. package/dist/core/command/menu-handler.js +11 -9
  38. package/dist/core/command/role-menu.js +27 -0
  39. package/dist/core/command/slash-handler.js +18 -14
  40. package/dist/core/data-migration.js +1434 -0
  41. package/dist/core/handoff/store.js +14 -4
  42. package/dist/core/inference/text-inference.js +42 -1
  43. package/dist/core/message/inbound-admission.js +23 -13
  44. package/dist/core/message/pending-hints.js +1 -1
  45. package/dist/core/message/response-engine.js +406 -167
  46. package/dist/core/message/retry-scheduler.js +71 -0
  47. package/dist/core/model/model-catalog.js +7 -2
  48. package/dist/core/model/model-fallback.js +81 -0
  49. package/dist/core/protected-paths.js +41 -8
  50. package/dist/core/session/adapters/ecagent-session-file-adapter.js +112 -0
  51. package/dist/core/session/session-fs-store.js +300 -61
  52. package/dist/core/session/session-manager.js +225 -21
  53. package/dist/core/session/session-title.js +1 -0
  54. package/dist/core/session/session-turn-coordinator.js +8 -1
  55. package/dist/core/system-channels.js +1 -0
  56. package/dist/index.js +10 -9
  57. package/dist/paths.js +35 -27
  58. package/dist/trigger/feedback.js +16 -0
  59. package/dist/trigger/legacy-session-history.js +19 -0
  60. package/dist/trigger/manager.js +12 -1
  61. package/dist/trigger/parser.js +4 -2
  62. package/dist/trigger/scheduler.js +185 -15
  63. package/dist/trigger/session-lock.js +62 -0
  64. package/dist/trigger/state.js +64 -0
  65. package/dist/trigger/validation.js +32 -2
  66. package/dist/utils/aid-bind.js +1 -1
  67. package/dist/utils/cross-platform.js +8 -2
  68. package/dist/utils/error-utils.js +9 -0
  69. package/dist/utils/process-introspect.js +19 -3
  70. package/dist/utils/welcome.js +2 -3
  71. package/ecagent/LICENSE +21 -0
  72. package/ecagent/NOTICE +11 -0
  73. package/ecagent/README.md +61 -0
  74. package/ecagent/dist/agent-loop.d.ts +24 -0
  75. package/ecagent/dist/agent-loop.d.ts.map +1 -0
  76. package/ecagent/dist/agent-loop.js +547 -0
  77. package/ecagent/dist/agent-loop.js.map +1 -0
  78. package/ecagent/dist/agent.d.ts +127 -0
  79. package/ecagent/dist/agent.d.ts.map +1 -0
  80. package/ecagent/dist/agent.js +386 -0
  81. package/ecagent/dist/agent.js.map +1 -0
  82. package/ecagent/dist/harness/agent-harness.d.ts +95 -0
  83. package/ecagent/dist/harness/agent-harness.d.ts.map +1 -0
  84. package/ecagent/dist/harness/agent-harness.js +926 -0
  85. package/ecagent/dist/harness/agent-harness.js.map +1 -0
  86. package/ecagent/dist/harness/compaction/branch-summarization.d.ts +51 -0
  87. package/ecagent/dist/harness/compaction/branch-summarization.d.ts.map +1 -0
  88. package/ecagent/dist/harness/compaction/branch-summarization.js +174 -0
  89. package/ecagent/dist/harness/compaction/branch-summarization.js.map +1 -0
  90. package/ecagent/dist/harness/compaction/compaction.d.ts +95 -0
  91. package/ecagent/dist/harness/compaction/compaction.d.ts.map +1 -0
  92. package/ecagent/dist/harness/compaction/compaction.js +528 -0
  93. package/ecagent/dist/harness/compaction/compaction.js.map +1 -0
  94. package/ecagent/dist/harness/compaction/utils.d.ts +25 -0
  95. package/ecagent/dist/harness/compaction/utils.d.ts.map +1 -0
  96. package/ecagent/dist/harness/compaction/utils.js +131 -0
  97. package/ecagent/dist/harness/compaction/utils.js.map +1 -0
  98. package/ecagent/dist/harness/env/nodejs.d.ts +51 -0
  99. package/ecagent/dist/harness/env/nodejs.d.ts.map +1 -0
  100. package/ecagent/dist/harness/env/nodejs.js +513 -0
  101. package/ecagent/dist/harness/env/nodejs.js.map +1 -0
  102. package/ecagent/dist/harness/messages.d.ts +51 -0
  103. package/ecagent/dist/harness/messages.d.ts.map +1 -0
  104. package/ecagent/dist/harness/messages.js +102 -0
  105. package/ecagent/dist/harness/messages.js.map +1 -0
  106. package/ecagent/dist/harness/prompt-templates.d.ts +48 -0
  107. package/ecagent/dist/harness/prompt-templates.d.ts.map +1 -0
  108. package/ecagent/dist/harness/prompt-templates.js +230 -0
  109. package/ecagent/dist/harness/prompt-templates.js.map +1 -0
  110. package/ecagent/dist/harness/session/jsonl-repo.d.ts +26 -0
  111. package/ecagent/dist/harness/session/jsonl-repo.d.ts.map +1 -0
  112. package/ecagent/dist/harness/session/jsonl-repo.js +100 -0
  113. package/ecagent/dist/harness/session/jsonl-repo.js.map +1 -0
  114. package/ecagent/dist/harness/session/jsonl-storage.d.ts +34 -0
  115. package/ecagent/dist/harness/session/jsonl-storage.d.ts.map +1 -0
  116. package/ecagent/dist/harness/session/jsonl-storage.js +234 -0
  117. package/ecagent/dist/harness/session/jsonl-storage.js.map +1 -0
  118. package/ecagent/dist/harness/session/memory-repo.d.ts +18 -0
  119. package/ecagent/dist/harness/session/memory-repo.d.ts.map +1 -0
  120. package/ecagent/dist/harness/session/memory-repo.js +44 -0
  121. package/ecagent/dist/harness/session/memory-repo.js.map +1 -0
  122. package/ecagent/dist/harness/session/memory-storage.d.ts +25 -0
  123. package/ecagent/dist/harness/session/memory-storage.d.ts.map +1 -0
  124. package/ecagent/dist/harness/session/memory-storage.js +111 -0
  125. package/ecagent/dist/harness/session/memory-storage.js.map +1 -0
  126. package/ecagent/dist/harness/session/repo-utils.d.ts +11 -0
  127. package/ecagent/dist/harness/session/repo-utils.d.ts.map +1 -0
  128. package/ecagent/dist/harness/session/repo-utils.js +39 -0
  129. package/ecagent/dist/harness/session/repo-utils.js.map +1 -0
  130. package/ecagent/dist/harness/session/session.d.ts +47 -0
  131. package/ecagent/dist/harness/session/session.d.ts.map +1 -0
  132. package/ecagent/dist/harness/session/session.js +245 -0
  133. package/ecagent/dist/harness/session/session.js.map +1 -0
  134. package/ecagent/dist/harness/session/uuid.d.ts +2 -0
  135. package/ecagent/dist/harness/session/uuid.d.ts.map +1 -0
  136. package/ecagent/dist/harness/session/uuid.js +50 -0
  137. package/ecagent/dist/harness/session/uuid.js.map +1 -0
  138. package/ecagent/dist/harness/skills.d.ts +44 -0
  139. package/ecagent/dist/harness/skills.d.ts.map +1 -0
  140. package/ecagent/dist/harness/skills.js +311 -0
  141. package/ecagent/dist/harness/skills.js.map +1 -0
  142. package/ecagent/dist/harness/system-prompt.d.ts +3 -0
  143. package/ecagent/dist/harness/system-prompt.d.ts.map +1 -0
  144. package/ecagent/dist/harness/system-prompt.js +30 -0
  145. package/ecagent/dist/harness/system-prompt.js.map +1 -0
  146. package/ecagent/dist/harness/types.d.ts +619 -0
  147. package/ecagent/dist/harness/types.d.ts.map +1 -0
  148. package/ecagent/dist/harness/types.js +81 -0
  149. package/ecagent/dist/harness/types.js.map +1 -0
  150. package/ecagent/dist/harness/utils/shell-output.d.ts +14 -0
  151. package/ecagent/dist/harness/utils/shell-output.d.ts.map +1 -0
  152. package/ecagent/dist/harness/utils/shell-output.js +126 -0
  153. package/ecagent/dist/harness/utils/shell-output.js.map +1 -0
  154. package/ecagent/dist/harness/utils/truncate.d.ts +70 -0
  155. package/ecagent/dist/harness/utils/truncate.d.ts.map +1 -0
  156. package/ecagent/dist/harness/utils/truncate.js +290 -0
  157. package/ecagent/dist/harness/utils/truncate.js.map +1 -0
  158. package/ecagent/dist/index.d.ts +22 -0
  159. package/ecagent/dist/index.d.ts.map +1 -0
  160. package/ecagent/dist/index.js +27 -0
  161. package/ecagent/dist/index.js.map +1 -0
  162. package/ecagent/dist/node.d.ts +3 -0
  163. package/ecagent/dist/node.d.ts.map +1 -0
  164. package/ecagent/dist/node.js +3 -0
  165. package/ecagent/dist/node.js.map +1 -0
  166. package/ecagent/dist/proxy.d.ts +69 -0
  167. package/ecagent/dist/proxy.d.ts.map +1 -0
  168. package/ecagent/dist/proxy.js +278 -0
  169. package/ecagent/dist/proxy.js.map +1 -0
  170. package/ecagent/dist/runtime/api/lazy.d.ts +15 -0
  171. package/ecagent/dist/runtime/api/lazy.d.ts.map +1 -0
  172. package/ecagent/dist/runtime/api/lazy.js +59 -0
  173. package/ecagent/dist/runtime/api/lazy.js.map +1 -0
  174. package/ecagent/dist/runtime/api/pi-messages.d.ts +97 -0
  175. package/ecagent/dist/runtime/api/pi-messages.d.ts.map +1 -0
  176. package/ecagent/dist/runtime/api/pi-messages.js +307 -0
  177. package/ecagent/dist/runtime/api/pi-messages.js.map +1 -0
  178. package/ecagent/dist/runtime/api/pi-messages.lazy.d.ts +3 -0
  179. package/ecagent/dist/runtime/api/pi-messages.lazy.d.ts.map +1 -0
  180. package/ecagent/dist/runtime/api/pi-messages.lazy.js +3 -0
  181. package/ecagent/dist/runtime/api/pi-messages.lazy.js.map +1 -0
  182. package/ecagent/dist/runtime/auth/context.d.ts +7 -0
  183. package/ecagent/dist/runtime/auth/context.d.ts.map +1 -0
  184. package/ecagent/dist/runtime/auth/context.js +42 -0
  185. package/ecagent/dist/runtime/auth/context.js.map +1 -0
  186. package/ecagent/dist/runtime/auth/credential-store.d.ts +16 -0
  187. package/ecagent/dist/runtime/auth/credential-store.d.ts.map +1 -0
  188. package/ecagent/dist/runtime/auth/credential-store.js +39 -0
  189. package/ecagent/dist/runtime/auth/credential-store.js.map +1 -0
  190. package/ecagent/dist/runtime/auth/helpers.d.ts +20 -0
  191. package/ecagent/dist/runtime/auth/helpers.d.ts.map +1 -0
  192. package/ecagent/dist/runtime/auth/helpers.js +46 -0
  193. package/ecagent/dist/runtime/auth/helpers.js.map +1 -0
  194. package/ecagent/dist/runtime/auth/resolve.d.ts +26 -0
  195. package/ecagent/dist/runtime/auth/resolve.d.ts.map +1 -0
  196. package/ecagent/dist/runtime/auth/resolve.js +100 -0
  197. package/ecagent/dist/runtime/auth/resolve.js.map +1 -0
  198. package/ecagent/dist/runtime/auth/types.d.ts +180 -0
  199. package/ecagent/dist/runtime/auth/types.d.ts.map +1 -0
  200. package/ecagent/dist/runtime/auth/types.js +2 -0
  201. package/ecagent/dist/runtime/auth/types.js.map +1 -0
  202. package/ecagent/dist/runtime/compat.d.ts +28 -0
  203. package/ecagent/dist/runtime/compat.d.ts.map +1 -0
  204. package/ecagent/dist/runtime/compat.js +86 -0
  205. package/ecagent/dist/runtime/compat.js.map +1 -0
  206. package/ecagent/dist/runtime/index.d.ts +20 -0
  207. package/ecagent/dist/runtime/index.d.ts.map +1 -0
  208. package/ecagent/dist/runtime/index.js +17 -0
  209. package/ecagent/dist/runtime/index.js.map +1 -0
  210. package/ecagent/dist/runtime/models.d.ts +142 -0
  211. package/ecagent/dist/runtime/models.d.ts.map +1 -0
  212. package/ecagent/dist/runtime/models.js +245 -0
  213. package/ecagent/dist/runtime/models.js.map +1 -0
  214. package/ecagent/dist/runtime/oauth.d.ts +2 -0
  215. package/ecagent/dist/runtime/oauth.d.ts.map +1 -0
  216. package/ecagent/dist/runtime/oauth.js +2 -0
  217. package/ecagent/dist/runtime/oauth.js.map +1 -0
  218. package/ecagent/dist/runtime/providers/faux.d.ts +97 -0
  219. package/ecagent/dist/runtime/providers/faux.d.ts.map +1 -0
  220. package/ecagent/dist/runtime/providers/faux.js +395 -0
  221. package/ecagent/dist/runtime/providers/faux.js.map +1 -0
  222. package/ecagent/dist/runtime/types.d.ts +607 -0
  223. package/ecagent/dist/runtime/types.d.ts.map +1 -0
  224. package/ecagent/dist/runtime/types.js +2 -0
  225. package/ecagent/dist/runtime/types.js.map +1 -0
  226. package/ecagent/dist/runtime/utils/diagnostics.d.ts +19 -0
  227. package/ecagent/dist/runtime/utils/diagnostics.d.ts.map +1 -0
  228. package/ecagent/dist/runtime/utils/diagnostics.js +25 -0
  229. package/ecagent/dist/runtime/utils/diagnostics.js.map +1 -0
  230. package/ecagent/dist/runtime/utils/event-stream.d.ts +21 -0
  231. package/ecagent/dist/runtime/utils/event-stream.d.ts.map +1 -0
  232. package/ecagent/dist/runtime/utils/event-stream.js +77 -0
  233. package/ecagent/dist/runtime/utils/event-stream.js.map +1 -0
  234. package/ecagent/dist/runtime/utils/headers.d.ts +4 -0
  235. package/ecagent/dist/runtime/utils/headers.d.ts.map +1 -0
  236. package/ecagent/dist/runtime/utils/headers.js +18 -0
  237. package/ecagent/dist/runtime/utils/headers.js.map +1 -0
  238. package/ecagent/dist/runtime/utils/json-parse.d.ts +16 -0
  239. package/ecagent/dist/runtime/utils/json-parse.d.ts.map +1 -0
  240. package/ecagent/dist/runtime/utils/json-parse.js +113 -0
  241. package/ecagent/dist/runtime/utils/json-parse.js.map +1 -0
  242. package/ecagent/dist/runtime/utils/oauth/types.d.ts +64 -0
  243. package/ecagent/dist/runtime/utils/oauth/types.d.ts.map +1 -0
  244. package/ecagent/dist/runtime/utils/oauth/types.js +2 -0
  245. package/ecagent/dist/runtime/utils/oauth/types.js.map +1 -0
  246. package/ecagent/dist/runtime/utils/overflow.d.ts +58 -0
  247. package/ecagent/dist/runtime/utils/overflow.d.ts.map +1 -0
  248. package/ecagent/dist/runtime/utils/overflow.js +158 -0
  249. package/ecagent/dist/runtime/utils/overflow.js.map +1 -0
  250. package/ecagent/dist/runtime/utils/provider-env.d.ts +7 -0
  251. package/ecagent/dist/runtime/utils/provider-env.d.ts.map +1 -0
  252. package/ecagent/dist/runtime/utils/provider-env.js +44 -0
  253. package/ecagent/dist/runtime/utils/provider-env.js.map +1 -0
  254. package/ecagent/dist/runtime/utils/retry.d.ts +12 -0
  255. package/ecagent/dist/runtime/utils/retry.d.ts.map +1 -0
  256. package/ecagent/dist/runtime/utils/retry.js +90 -0
  257. package/ecagent/dist/runtime/utils/retry.js.map +1 -0
  258. package/ecagent/dist/runtime/utils/typebox-helpers.d.ts +17 -0
  259. package/ecagent/dist/runtime/utils/typebox-helpers.d.ts.map +1 -0
  260. package/ecagent/dist/runtime/utils/typebox-helpers.js +21 -0
  261. package/ecagent/dist/runtime/utils/typebox-helpers.js.map +1 -0
  262. package/ecagent/dist/runtime/utils/validation.d.ts +18 -0
  263. package/ecagent/dist/runtime/utils/validation.d.ts.map +1 -0
  264. package/ecagent/dist/runtime/utils/validation.js +269 -0
  265. package/ecagent/dist/runtime/utils/validation.js.map +1 -0
  266. package/ecagent/dist/types.d.ts +401 -0
  267. package/ecagent/dist/types.d.ts.map +1 -0
  268. package/ecagent/dist/types.js +2 -0
  269. package/ecagent/dist/types.js.map +1 -0
  270. package/ecagent/package.json +93 -0
  271. package/ecagent/src/agent-loop.ts +792 -0
  272. package/ecagent/src/agent.ts +575 -0
  273. package/ecagent/src/harness/agent-harness.ts +1029 -0
  274. package/ecagent/src/harness/compaction/branch-summarization.ts +261 -0
  275. package/ecagent/src/harness/compaction/compaction.ts +753 -0
  276. package/ecagent/src/harness/compaction/utils.ts +144 -0
  277. package/ecagent/src/harness/env/nodejs.ts +569 -0
  278. package/ecagent/src/harness/messages.ts +164 -0
  279. package/ecagent/src/harness/prompt-templates.ts +267 -0
  280. package/ecagent/src/harness/session/jsonl-repo.ts +179 -0
  281. package/ecagent/src/harness/session/jsonl-storage.ts +314 -0
  282. package/ecagent/src/harness/session/memory-repo.ts +50 -0
  283. package/ecagent/src/harness/session/memory-storage.ts +133 -0
  284. package/ecagent/src/harness/session/repo-utils.ts +51 -0
  285. package/ecagent/src/harness/session/session.ts +338 -0
  286. package/ecagent/src/harness/session/uuid.ts +54 -0
  287. package/ecagent/src/harness/skills.ts +375 -0
  288. package/ecagent/src/harness/system-prompt.ts +34 -0
  289. package/ecagent/src/harness/types.ts +838 -0
  290. package/ecagent/src/harness/utils/shell-output.ts +135 -0
  291. package/ecagent/src/harness/utils/truncate.ts +344 -0
  292. package/ecagent/src/index.ts +46 -0
  293. package/ecagent/src/node.ts +2 -0
  294. package/ecagent/src/proxy.ts +367 -0
  295. package/ecagent/src/runtime/api/lazy.ts +70 -0
  296. package/ecagent/src/runtime/api/pi-messages.lazy.ts +4 -0
  297. package/ecagent/src/runtime/api/pi-messages.ts +436 -0
  298. package/ecagent/src/runtime/auth/context.ts +45 -0
  299. package/ecagent/src/runtime/auth/credential-store.ts +47 -0
  300. package/ecagent/src/runtime/auth/helpers.ts +46 -0
  301. package/ecagent/src/runtime/auth/resolve.ts +141 -0
  302. package/ecagent/src/runtime/auth/types.ts +182 -0
  303. package/ecagent/src/runtime/compat.ts +158 -0
  304. package/ecagent/src/runtime/index.ts +31 -0
  305. package/ecagent/src/runtime/models.ts +452 -0
  306. package/ecagent/src/runtime/oauth.ts +1 -0
  307. package/ecagent/src/runtime/providers/faux.ts +538 -0
  308. package/ecagent/src/runtime/types.ts +718 -0
  309. package/ecagent/src/runtime/utils/diagnostics.ts +45 -0
  310. package/ecagent/src/runtime/utils/event-stream.ts +88 -0
  311. package/ecagent/src/runtime/utils/headers.ts +18 -0
  312. package/ecagent/src/runtime/utils/json-parse.ts +124 -0
  313. package/ecagent/src/runtime/utils/oauth/types.ts +79 -0
  314. package/ecagent/src/runtime/utils/overflow.ts +165 -0
  315. package/ecagent/src/runtime/utils/provider-env.ts +52 -0
  316. package/ecagent/src/runtime/utils/retry.ts +101 -0
  317. package/ecagent/src/runtime/utils/typebox-helpers.ts +24 -0
  318. package/ecagent/src/runtime/utils/validation.ts +310 -0
  319. package/ecagent/src/types.ts +430 -0
  320. package/ecagent/tsconfig.build.json +32 -0
  321. package/kits/docs/evolcore/config.md +4 -4
  322. package/kits/docs/evolcore/contact.md +2 -1
  323. package/kits/schemas/_meta.json +7 -4
  324. package/kits/schemas/agent-config.schema.10.json +370 -0
  325. package/kits/schemas/agent-config.schema.9.json +2 -12
  326. package/kits/schemas/defaults.schema.4.json +93 -0
  327. package/kits/schemas/relation-config.schema.8.json +69 -0
  328. package/kits/templates/roles/admin.json +1 -1
  329. package/package.json +22 -6
  330. package/skills/eclink/SKILL.md +70 -0
  331. package/skills/eclink/agents/openai.yaml +4 -0
  332. package/assets/.env.template +0 -4
package/dist/cli/agent.js CHANGED
@@ -13,6 +13,7 @@ import { ensureInitialRoleRegistry } from '../config/role-service.js';
13
13
  import { isValidChannelName } from '../core/channel-loader.js';
14
14
  import { commandExists } from '../utils/cross-platform.js';
15
15
  import { getCodexAppServerAvailability, isCodexAppServerAvailable } from '../agents/codex-runner.js';
16
+ import { resolveEcagentConfig } from '../agents/baseagent.js';
16
17
  import { agentProjectRootFromDefaults, deriveAgentProjectPath } from '../utils/project-path.js';
17
18
  import { AGENT_DELEGATION_TOKEN_ENV } from '../core/auth/agent-delegation.js';
18
19
  function withStaticOwner(config, owner) {
@@ -25,19 +26,29 @@ function withStaticOwner(config, owner) {
25
26
  return { ...config, owners: [...owners, value] };
26
27
  }
27
28
  // ==================== Helpers ====================
28
- const BASEAGENT_CANDIDATES = ['claude', 'codex', 'gemini'];
29
+ const BASEAGENT_CANDIDATES = ['claude', 'codex', 'gemini', 'ecagent'];
30
+ const AUTO_BASEAGENT_CANDIDATES = ['claude', 'codex', 'gemini'];
29
31
  function isBaseagentAvailable(baseagent) {
30
32
  if (baseagent === 'codex')
31
33
  return isCodexAppServerAvailable();
34
+ if (baseagent === 'ecagent')
35
+ return true;
32
36
  return commandExists(baseagent);
33
37
  }
34
38
  function detectAvailableBaseagents() {
35
39
  return BASEAGENT_CANDIDATES.filter(isBaseagentAvailable);
36
40
  }
37
41
  function pickDefaultBaseagent(available) {
38
- if (available.length === 0)
39
- return null;
40
- return available.includes('claude') ? 'claude' : available[0];
42
+ return AUTO_BASEAGENT_CANDIDATES.find(candidate => available.includes(candidate)) ?? null;
43
+ }
44
+ function ecagentUnavailableReason() {
45
+ try {
46
+ resolveEcagentConfig({ agents: { ecagent: {} } });
47
+ return undefined;
48
+ }
49
+ catch (error) {
50
+ return error instanceof Error ? error.message : String(error);
51
+ }
41
52
  }
42
53
  function buildBaseagentsBlock(chosen) {
43
54
  return { [chosen]: {} };
@@ -301,19 +312,24 @@ export async function agentCreateInteractive(opts = {}) {
301
312
  }
302
313
  // Baseagent
303
314
  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.` };
315
+ if (available.length === 0 || (available.length === 1 && available[0] === 'ecagent' && ecagentUnavailableReason())) {
316
+ return { ok: false, error: `No usable baseagent detected. Install claude/gemini CLI or codex CLI with app-server, or configure bundled ecagent.` };
306
317
  }
307
318
  const defaultBa = pickDefaultBaseagent(available);
308
319
  let baseagent;
309
- if (available.length === 1) {
320
+ if (available.length === 1 && defaultBa) {
310
321
  console.log(` Baseagent: ${defaultBa}`);
311
322
  baseagent = defaultBa;
312
323
  }
313
324
  else {
314
325
  let chosen = null;
315
326
  while (chosen === null) {
316
- const input = (await ask(`Baseagent (${available.join('/')}) [${defaultBa}]: `)).trim() || defaultBa;
327
+ const defaultHint = defaultBa ? ` [${defaultBa}]` : '';
328
+ const input = (await ask(`Baseagent (${available.join('/')})${defaultHint}: `)).trim() || defaultBa;
329
+ if (!input) {
330
+ console.log(' ecagent is optional; enter ecagent to select it explicitly.');
331
+ continue;
332
+ }
317
333
  if (!BASEAGENT_CANDIDATES.includes(input)) {
318
334
  console.log(` Invalid choice. Options: ${BASEAGENT_CANDIDATES.join('/')}`);
319
335
  continue;
@@ -322,6 +338,13 @@ export async function agentCreateInteractive(opts = {}) {
322
338
  console.log(` ${input} is not available in the current environment. Available: ${available.join('/')}`);
323
339
  continue;
324
340
  }
341
+ if (input === 'ecagent') {
342
+ const reason = ecagentUnavailableReason();
343
+ if (reason) {
344
+ console.log(` ecagent is unavailable: ${reason}`);
345
+ continue;
346
+ }
347
+ }
325
348
  chosen = input;
326
349
  }
327
350
  baseagent = chosen;
@@ -535,8 +558,8 @@ export async function agentCreateNonInteractive(opts) {
535
558
  }
536
559
  // Baseagent
537
560
  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.`);
561
+ if (available.length === 0 || (available.length === 1 && available[0] === 'ecagent' && ecagentUnavailableReason())) {
562
+ return failValidating(`No usable baseagent detected. Install claude/gemini CLI or codex CLI with app-server, or configure bundled ecagent.`);
540
563
  }
541
564
  let baseagent;
542
565
  if (opts.baseagent) {
@@ -549,10 +572,18 @@ export async function agentCreateNonInteractive(opts) {
549
572
  : undefined;
550
573
  return failValidating(reason || `${opts.baseagent} is not available in the current environment (available: ${available.join('/')})`);
551
574
  }
575
+ if (opts.baseagent === 'ecagent') {
576
+ const reason = ecagentUnavailableReason();
577
+ if (reason)
578
+ return failValidating(reason);
579
+ }
552
580
  baseagent = opts.baseagent;
553
581
  }
554
582
  else {
555
- baseagent = pickDefaultBaseagent(available);
583
+ const defaultBaseagent = pickDefaultBaseagent(available);
584
+ if (!defaultBaseagent)
585
+ return failValidating('ecagent is optional; pass --baseagent ecagent after configuring its API key');
586
+ baseagent = defaultBaseagent;
556
587
  }
557
588
  if (!path.isAbsolute(opts.project)) {
558
589
  return failValidating(`--project must be absolute: ${opts.project}`);
@@ -1,5 +1,6 @@
1
1
  import fs from 'fs';
2
- import { agentMdPath, resolveRoot } from '../paths.js';
2
+ import path from 'path';
3
+ import { agentMdPath, resolvePaths, resolveRoot } from '../paths.js';
3
4
  import { isExplicitGroupId } from '../aun/group-identity.js';
4
5
  import { getArgValue, isHelpFlag, wantsHelp } from './help.js';
5
6
  // ==================== AID ====================
@@ -814,6 +815,8 @@ Options:
814
815
  --before <time> 只返回该时间之前的历史
815
816
  --after <time> 只返回该时间之后的历史
816
817
  --direction <value> 历史方向:in|out|all
818
+ --channel <type> 历史渠道:aun|feishu|wechat|dingtalk|qqbot|wecom
819
+ --channel-name <name> 历史渠道实例名(非 AUN 渠道必须匹配 Agent 配置)
817
820
  --return <none|required> 跨会话发送必填:none=仅补充目标上下文,required=回答回流来源
818
821
  --content-type <mime> 显式覆盖 MIME(仅 --file 模式)
819
822
  --as <type> 附件类型:image|video|voice|file(仅 --file 模式)
@@ -841,7 +844,7 @@ Options:
841
844
  if (sub === 'history' && wantsHelp(args)) {
842
845
  console.log(`用法: ec msg history <self-aid> <target-aid> [options]
843
846
 
844
- 查询指定 AID 对的本地 messages.jsonl,不调用 message.pull,也不影响收件箱游标。
847
+ 查询指定会话对的本地历史,不调用 message.pull,也不影响收件箱游标。非 AUN 历史仅通过当前 Agent 已配置的渠道实例读取,不能传入文件路径。
845
848
 
846
849
  Options:
847
850
  --session <latest|all|id> 默认 latest;all 查询全部;也可指定会话 ID
@@ -849,12 +852,15 @@ Options:
849
852
  --before <time> 开区间;epoch 毫秒或 ISO 时间
850
853
  --after <time> 开区间;epoch 毫秒或 ISO 时间
851
854
  --direction <value> in|out|all(默认 all)
855
+ --channel <type> aun|feishu|wechat|dingtalk|qqbot|wecom(默认 aun)
856
+ --channel-name <name> 渠道实例名;省略时仅允许该类型恰好配置一个实例
852
857
  --format <text|json> 输出格式(默认 text)
853
858
 
854
859
  示例:
855
860
  ec msg history alice.agentid.pub bob.agentid.pub
856
861
  ec msg history alice.agentid.pub bob.agentid.pub --session latest
857
862
  ec msg history alice.agentid.pub bob.agentid.pub --session all
863
+ ec msg history alice.agentid.pub oc_xxx --channel feishu --channel-name main --session all --format json
858
864
  ec msg history alice.agentid.pub bob.agentid.pub --session meta_20260713_1783920000000 --format json`);
859
865
  return;
860
866
  }
@@ -876,7 +882,7 @@ Options:
876
882
  console.error('用法: ec msg history <self-aid> <target-aid> [options]');
877
883
  process.exit(1);
878
884
  }
879
- const historyValueFlags = new Set(['--session', '--limit', '--before', '--after', '--direction', '--format']);
885
+ const historyValueFlags = new Set(['--session', '--limit', '--before', '--after', '--direction', '--channel', '--channel-name', '--format']);
880
886
  const seenHistoryFlags = new Set();
881
887
  for (let index = 3; index < args.length; index++) {
882
888
  const flag = args[index];
@@ -915,9 +921,25 @@ Options:
915
921
  console.error(`❌ --after 必须是 epoch 毫秒或有效时间: ${afterRaw}`);
916
922
  process.exit(1);
917
923
  }
924
+ const channel = getArgValue(args, '--channel') ?? 'aun';
925
+ const channelTypes = new Set(['aun', 'feishu', 'wechat', 'dingtalk', 'qqbot', 'wecom']);
926
+ if (!channelTypes.has(channel)) {
927
+ console.error(`❌ 不支持的 channel: ${channel}`);
928
+ process.exit(1);
929
+ }
930
+ let channelName;
931
+ try {
932
+ channelName = resolveHistoryChannelName(from, channel, getArgValue(args, '--channel-name'));
933
+ }
934
+ catch (error) {
935
+ console.error(`❌ ${error instanceof Error ? error.message : String(error)}`);
936
+ process.exit(1);
937
+ }
918
938
  const result = msgHistory({
919
939
  from,
920
940
  target,
941
+ channel: channel,
942
+ channelName,
921
943
  session: getArgValue(args, '--session'),
922
944
  limit,
923
945
  before,
@@ -2001,3 +2023,28 @@ function parseMsgHistoryTime(value) {
2001
2023
  const timestamp = Date.parse(trimmed);
2002
2024
  return Number.isFinite(timestamp) ? timestamp : undefined;
2003
2025
  }
2026
+ /** Resolve a configured channel instance without exposing Agent session paths. */
2027
+ function resolveHistoryChannelName(agentAid, channel, requested) {
2028
+ if (channel === 'aun' && requested === undefined)
2029
+ return undefined;
2030
+ const configPath = path.join(resolvePaths().root, 'agents', agentAid, 'config.json');
2031
+ let config;
2032
+ try {
2033
+ config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
2034
+ }
2035
+ catch {
2036
+ throw new Error(`无法读取 Agent 渠道配置: ${agentAid}`);
2037
+ }
2038
+ const instances = (Array.isArray(config?.channels) ? config.channels : [])
2039
+ .filter((item) => item && item.type === channel && item.enabled !== false && typeof item.name === 'string');
2040
+ if (requested !== undefined) {
2041
+ if (!instances.some((item) => item.name === requested)) {
2042
+ throw new Error(`Agent 未配置 ${channel} 渠道实例: ${requested}`);
2043
+ }
2044
+ return requested;
2045
+ }
2046
+ if (instances.length !== 1) {
2047
+ throw new Error(`${channel} 渠道实例不唯一,请指定 --channel-name`);
2048
+ }
2049
+ return instances[0].name;
2050
+ }
@@ -28,7 +28,24 @@ import { getProcessStartTime } from '../utils/process-introspect.js';
28
28
  import { AGENT_DELEGATION_TOKEN_ENV } from '../core/auth/agent-delegation.js';
29
29
  import { WEB_CLI_BIN, WEB_PACKAGE_LATEST, WEB_PACKAGE_NAME } from '../product.js';
30
30
  import { rotateStdoutLog } from '../utils/log-writer.js';
31
+ import { inspectDataMigrationRequirement } from '../core/data-migration.js';
31
32
  const execFileAsync = promisify(execFile);
33
+ function printNoSelfAgentHints(options) {
34
+ const { daemonConfig, ecwebStarted, skipped } = options;
35
+ console.log('\nℹ 未配置任何 self-agent,Control Plane 已启动。');
36
+ console.log(' 命令行:ec agent new <aid>.agentid.pub');
37
+ if (daemonConfig.aid && (daemonConfig.owners?.length ?? 0) > 0) {
38
+ console.log(' Evol App:可通过进程级菜单创建 agent');
39
+ }
40
+ if (ecwebStarted) {
41
+ console.log(' ECWeb:可通过控制台创建 agent');
42
+ }
43
+ if (skipped.length > 0) {
44
+ console.log('跳过的目录:');
45
+ for (const skippedAgent of skipped)
46
+ console.log(` - ${skippedAgent.dirName}: ${skippedAgent.reason}`);
47
+ }
48
+ }
32
49
  async function probeDaemon(socketPath, timeoutMs = 1000) {
33
50
  const response = await ipcQuery(socketPath, { type: 'ping' }, timeoutMs);
34
51
  if (response?.pong !== true || !Number.isInteger(response.pid) || response.pid <= 0)
@@ -99,6 +116,53 @@ function serviceProxyNeedsEcweb(cfg) {
99
116
  return false;
100
117
  return (cfg.serviceProxy.services ?? []).some(s => s.enabled !== false && s.source === 'ecweb');
101
118
  }
119
+ function latestSourceMtime(dir) {
120
+ let latest = 0;
121
+ try {
122
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
123
+ const filePath = path.join(dir, entry.name);
124
+ if (entry.isDirectory()) {
125
+ latest = Math.max(latest, latestSourceMtime(filePath));
126
+ }
127
+ else if (entry.isFile()) {
128
+ latest = Math.max(latest, fs.statSync(filePath).mtimeMs);
129
+ }
130
+ }
131
+ }
132
+ catch { }
133
+ return latest;
134
+ }
135
+ function sourceBuildNeedsRefresh() {
136
+ const packageRoot = getPackageRoot();
137
+ const sourceCli = path.join(packageRoot, 'src', 'cli', 'index.ts');
138
+ if (!fs.existsSync(sourceCli))
139
+ return false;
140
+ const daemonEntry = path.join(packageRoot, 'dist', 'index.js');
141
+ if (!fs.existsSync(daemonEntry))
142
+ return true;
143
+ const sourceMtime = Math.max(latestSourceMtime(path.join(packageRoot, 'src')), latestSourceMtime(path.join(packageRoot, 'ecagent', 'src')));
144
+ return sourceMtime > fs.statSync(daemonEntry).mtimeMs;
145
+ }
146
+ function requireFreshSourceBuild() {
147
+ if (!sourceBuildNeedsRefresh())
148
+ return true;
149
+ console.error('❌ 源码比 daemon 编译产物更新,服务未启动。');
150
+ console.error(' 请先执行: ec stop && npm run build');
151
+ console.error(' 然后执行: ec start');
152
+ process.exitCode = 2;
153
+ return false;
154
+ }
155
+ function requireCompletedDataMigration(root) {
156
+ const requirement = inspectDataMigrationRequirement(root);
157
+ if (!requirement.required)
158
+ return true;
159
+ console.error(`❌ 检测到 ${requirement.operationCount} 项待确认的用户数据迁移,服务未启动。`);
160
+ console.error(' 先执行: ec data migrate --dry-run');
161
+ console.error(' 确认后: ec data migrate --apply');
162
+ console.error(' 迁移成功并校验后,再执行 ec start 或 ec restart。');
163
+ process.exitCode = 2;
164
+ return false;
165
+ }
102
166
  // ==================== Commands ====================
103
167
  /**
104
168
  * 检测并展示跨 HOME 残留的 evolcore 主进程。
@@ -184,6 +248,8 @@ export async function cmdStart(opts = {}) {
184
248
  return;
185
249
  }
186
250
  ensureDataDirs();
251
+ if (!requireFreshSourceBuild())
252
+ return;
187
253
  // 旧配置自动迁移(daemon.json → 新结构)
188
254
  const { autoMigrateIfNeeded } = await import('../config-store.js');
189
255
  autoMigrateIfNeeded();
@@ -196,8 +262,16 @@ export async function cmdStart(opts = {}) {
196
262
  }
197
263
  else {
198
264
  console.log('⚡ 未检测到初始化配置,自动启动初始化向导...\n');
199
- await cmdInit();
200
- return;
265
+ // cmdStart 直接调用 cmdInit,不会经过 CLI 的 `init` 分发分支;
266
+ // 这里需显式抑制 AUN SDK 的常规 keystore 日志。
267
+ const { suppressSdkLogs } = await import('../aun/aid/index.js');
268
+ suppressSdkLogs();
269
+ await cmdInit({ invokedByStart: true });
270
+ if (!loadDefaults()) {
271
+ console.log('⚠ 初始化未完成,未启动 EvolCore。');
272
+ return;
273
+ }
274
+ console.log('\n初始化完成,开始启动 EvolCore ....');
201
275
  }
202
276
  }
203
277
  // 控制 AID 门禁:缺 aid 且交互式 → 只补全控制 AID + owners(不重走 baseagent 向导)。
@@ -236,20 +310,15 @@ export async function cmdStart(opts = {}) {
236
310
  }
237
311
  // 检查至少有一个 self-agent
238
312
  const { agents, skipped } = loadAllAgents();
313
+ const isControlAidBootstrap = opts.bindBootstrap && !!daemonCfgStart.aid;
314
+ const shouldPrintNoSelfAgentHints = agents.length === 0 && !isControlAidBootstrap;
239
315
  if (agents.length === 0) {
240
- if (opts.bindBootstrap && daemonCfgStart.aid) {
316
+ if (isControlAidBootstrap) {
241
317
  console.log('ℹ 未配置任何 self-agent,绑定 bootstrap 将仅启动控制 AID');
242
318
  }
243
- else {
244
- console.log('ℹ 未配置任何 self-agent,将仅启动 Control Plane。');
245
- console.log(' 可通过控制 AID 远程创建 agent,或稍后运行 ec agent new <aid>.agentid.pub');
246
- if (skipped.length > 0) {
247
- console.log(`跳过的目录:`);
248
- for (const s of skipped)
249
- console.log(` - ${s.dirName}: ${s.reason}`);
250
- }
251
- }
252
319
  }
320
+ if (!requireCompletedDataMigration(p.root))
321
+ return;
253
322
  // 检查 instance 目录中的进程状态
254
323
  const status = scanInstances();
255
324
  const aliveMains = status.mains.filter(m => m.alive);
@@ -323,10 +392,22 @@ export async function cmdStart(opts = {}) {
323
392
  console.log(` Logs: ${p.logs}/`);
324
393
  console.log(`⏱ ready in ${((Date.now() - cmdStartedAt) / 1000).toFixed(1)}s`);
325
394
  if (!ecwebStartedBeforeDaemon) {
326
- startEcwebIfEnabled(p).catch((err) => {
395
+ startEcwebIfEnabled(p)
396
+ .then((ecwebStarted) => {
397
+ if (shouldPrintNoSelfAgentHints) {
398
+ printNoSelfAgentHints({ daemonConfig: loadDaemonConfig(), ecwebStarted, skipped });
399
+ }
400
+ })
401
+ .catch((err) => {
327
402
  console.error(`⚠ ECWeb 启动检查失败: ${err instanceof Error ? err.message : String(err)}`);
403
+ if (shouldPrintNoSelfAgentHints) {
404
+ printNoSelfAgentHints({ daemonConfig: loadDaemonConfig(), ecwebStarted: false, skipped });
405
+ }
328
406
  });
329
407
  }
408
+ else if (shouldPrintNoSelfAgentHints) {
409
+ printNoSelfAgentHints({ daemonConfig: loadDaemonConfig(), ecwebStarted: true, skipped });
410
+ }
330
411
  }, 500);
331
412
  let forwardingShutdown = false;
332
413
  const forwardSignal = (signal) => {
@@ -426,8 +507,10 @@ export async function cmdStart(opts = {}) {
426
507
  }
427
508
  console.log(`⏱ done in ${((Date.now() - cmdStartedAt) / 1000).toFixed(1)}s`);
428
509
  // ECWeb 自动后台启动
429
- if (!ecwebStartedBeforeDaemon)
430
- await startEcwebIfEnabled(p);
510
+ const ecwebStarted = ecwebStartedBeforeDaemon || await startEcwebIfEnabled(p);
511
+ if (shouldPrintNoSelfAgentHints) {
512
+ printNoSelfAgentHints({ daemonConfig: loadDaemonConfig(), ecwebStarted, skipped });
513
+ }
431
514
  return;
432
515
  }
433
516
  // 超时
@@ -514,6 +597,8 @@ export async function cmdRestart(opts = {}) {
514
597
  }
515
598
  if (rejectPidIsolatedLifecycle('restart', initialStatus, ping))
516
599
  return;
600
+ if (!requireFreshSourceBuild())
601
+ return;
517
602
  // 版本检查与自动升级
518
603
  console.log('📦 Checking for updates...');
519
604
  const upgrade = await tryUpgrade();
@@ -557,6 +642,8 @@ export async function cmdRestart(opts = {}) {
557
642
  console.log(`⚠ ${WEB_PACKAGE_NAME} upgrade failed (${ecwebUpgrade.from} → ${ecwebUpgrade.to})`);
558
643
  break;
559
644
  }
645
+ if (!requireCompletedDataMigration(resolveRoot()))
646
+ return;
560
647
  // 停止所有活 main 进程(可能不止一个)
561
648
  const status = scanInstances();
562
649
  const aliveMains = status.mains.filter(m => m.alive);
@@ -813,7 +900,7 @@ export async function cmdStatus() {
813
900
  // Runtime statistics (read from sessions filesystem)
814
901
  if (fs.existsSync(p.sessionsDir)) {
815
902
  try {
816
- const { scanChatDirs, scanMetaFiles, readJsonFile, readLastJsonlLine } = await import('../core/session/session-fs-store.js');
903
+ const { chatDirPath, scanChatDirs, scanMetaFiles, readJsonFile, readLastJsonlLine } = await import('../core/session/session-fs-store.js');
817
904
  const chatDirs = scanChatDirs(p.sessionsDir);
818
905
  const allSessions = [];
819
906
  for (const { dirPath } of chatDirs) {
@@ -832,23 +919,42 @@ export async function cmdStatus() {
832
919
  }
833
920
  // 最近 5 个(按 updatedAt 倒排)
834
921
  const recentSessions = [...allSessions].sort((a, b) => b.updatedAt - a.updatedAt).slice(0, 5);
835
- // 检测 orphansession channel 实例名不在任何 self-agent 配置内
922
+ // 检测 orphan:按物理 session 路由而非历史 meta 判定。保留的旧
923
+ // 实例副本在唯一的当前实例已存在时属于迁移来源,不应误报。
836
924
  let orphanCount = 0;
837
925
  try {
838
926
  const { agents } = loadAllAgents();
839
927
  const configChannelNames = new Set();
928
+ const configuredByAgentType = new Map();
840
929
  for (const cfg of agents) {
841
930
  // AUN 是从 agent 自身 AID 隐式派生的内建通道,不出现在 cfg.channels 里。
842
931
  // 不补这条,所有 aun#<aid>#main 会话都会被误判成 orphan。
843
932
  configChannelNames.add(`aun#${cfg.aid}#main`);
844
933
  for (const inst of cfg.channels) {
845
934
  // effective key: <type>#<selfAID>#<name>
846
- configChannelNames.add(`${inst.type}#${cfg.aid}#${inst.name}`);
935
+ const channelKey = `${inst.type}#${cfg.aid}#${inst.name}`;
936
+ configChannelNames.add(channelKey);
937
+ const identity = `${inst.type}\0${cfg.aid}`;
938
+ configuredByAgentType.set(identity, [...(configuredByAgentType.get(identity) ?? []), channelKey]);
847
939
  }
848
940
  }
849
- for (const s of allSessions) {
850
- if (!configChannelNames.has(s.channel) && !isNonAgentSessionChannel(s.channel))
851
- orphanCount++;
941
+ // The daemon control identity owns AUN transcripts but deliberately
942
+ // has no Agent config, so include it explicitly in route validity.
943
+ const daemonConfig = readJsonFile(p.daemonConfig);
944
+ if (daemonConfig?.aid)
945
+ configChannelNames.add(`aun#${daemonConfig.aid}#main`);
946
+ for (const chat of chatDirs) {
947
+ const channelKey = chat.channelKey;
948
+ if (!channelKey || configChannelNames.has(channelKey) || isNonAgentSessionChannel(channelKey))
949
+ continue;
950
+ const [type, aid] = channelKey.split('#');
951
+ const candidates = type && aid ? configuredByAgentType.get(`${type}\0${aid}`) : undefined;
952
+ if (candidates?.length === 1) {
953
+ const canonical = chatDirPath(p.sessionsDir, type, chat.channelId, type === 'aun' ? aid : undefined, candidates[0]);
954
+ if (canonical !== chat.dirPath && fs.existsSync(canonical))
955
+ continue;
956
+ }
957
+ orphanCount++;
852
958
  }
853
959
  }
854
960
  catch { }
@@ -0,0 +1,132 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { resolvePaths, resolveRoot } from '../paths.js';
4
+ import { applyDataMigration, planDataMigration, readDataMigration, verifyDataMigration } from '../core/data-migration.js';
5
+ import { scanInstances } from '../utils/instance-registry.js';
6
+ import { isProcessRunning, killProcess } from '../utils/cross-platform.js';
7
+ import { ipcQuery } from '../ipc.js';
8
+ import { cmdStart, cmdStop } from './daemon-commands.js';
9
+ function printSummary(manifest) {
10
+ const counts = manifest.operations.reduce((result, operation) => {
11
+ result[operation.status] = (result[operation.status] ?? 0) + 1;
12
+ return result;
13
+ }, {});
14
+ console.log(`migration: ${manifest.id}`);
15
+ console.log(`state: ${manifest.state}`);
16
+ console.log(`operations: ${manifest.operations.length}`);
17
+ console.log(`status: ${Object.entries(counts).map(([key, value]) => `${key}=${value}`).join(', ') || 'none'}`);
18
+ if (manifest.warnings.length) {
19
+ console.log('warnings:');
20
+ for (const warning of manifest.warnings)
21
+ console.log(` - ${warning}`);
22
+ console.log('note: 上述数据无法唯一归属,因此被原样保留;本次不会迁移、修改或删除它们。');
23
+ console.log('next: 可继续迁移已计划项;如需迁移这些保留项,请补齐所属 Agent/通道或匹配的 Trigger 定义后重新执行 --dry-run。');
24
+ }
25
+ const failed = manifest.operations.filter(operation => operation.status === 'error');
26
+ if (failed.length) {
27
+ console.log('errors:');
28
+ for (const operation of failed)
29
+ console.log(` - ${operation.id}: ${operation.error}`);
30
+ }
31
+ }
32
+ function latestMigrationId(root) {
33
+ const dir = path.join(root, 'data', 'daemon', 'migrations');
34
+ const candidates = fs.readdirSync(dir, { withFileTypes: true })
35
+ .filter(entry => entry.isFile() && entry.name.endsWith('.json'))
36
+ .map(entry => ({ name: entry.name.slice(0, -'.json'.length), mtime: fs.statSync(path.join(dir, entry.name)).mtimeMs }))
37
+ .sort((left, right) => right.mtime - left.mtime);
38
+ if (!candidates[0])
39
+ throw new Error('no migration manifest found');
40
+ return candidates[0].name;
41
+ }
42
+ async function daemonIpcReachable() {
43
+ const response = await ipcQuery(resolvePaths().socket, { type: 'ping' }, 1_000);
44
+ return response?.pong === true;
45
+ }
46
+ function liveRuntime() {
47
+ const status = scanInstances();
48
+ return {
49
+ mainCount: status.mains.filter(instance => instance.alive).length,
50
+ monitorPids: status.restartMonitors
51
+ .filter(instance => instance.alive)
52
+ .map(instance => instance.record.pid),
53
+ };
54
+ }
55
+ function delay(ms) {
56
+ return new Promise(resolve => setTimeout(resolve, ms));
57
+ }
58
+ async function stopRestartMonitors(pids) {
59
+ for (const pid of pids)
60
+ killProcess(pid);
61
+ for (let attempt = 0; attempt < 8 && pids.some(isProcessRunning); attempt++)
62
+ await delay(250);
63
+ for (const pid of pids)
64
+ if (isProcessRunning(pid))
65
+ killProcess(pid, true);
66
+ }
67
+ async function waitForMaintenanceWindow() {
68
+ for (let attempt = 0; attempt < 12; attempt++) {
69
+ const runtime = liveRuntime();
70
+ if (runtime.mainCount === 0 && runtime.monitorPids.length === 0 && !await daemonIpcReachable())
71
+ return;
72
+ await delay(250);
73
+ }
74
+ const runtime = liveRuntime();
75
+ const ipc = await daemonIpcReachable();
76
+ throw new Error(`cannot begin migration while runtime remains active (main=${runtime.mainCount}, restart-monitor=${runtime.monitorPids.length}, ipc=${ipc})`);
77
+ }
78
+ export async function cmdData(args) {
79
+ if (args[0] !== 'migrate' || args.includes('--help')) {
80
+ console.log('用法: ec data migrate --dry-run | --apply | --resume <migration-id> | --verify [migration-id]');
81
+ return;
82
+ }
83
+ const root = resolveRoot();
84
+ if (args.includes('--dry-run')) {
85
+ printSummary(planDataMigration(root));
86
+ return;
87
+ }
88
+ const resumeIndex = args.indexOf('--resume');
89
+ const verify = args.includes('--verify');
90
+ const requestedId = resumeIndex >= 0 ? args[resumeIndex + 1] : args.find(value => value.startsWith('data-'));
91
+ if (resumeIndex >= 0 && !requestedId)
92
+ throw new Error('--resume requires a migration id');
93
+ if (verify) {
94
+ // A completed migration's destination becomes live runtime data. Operator
95
+ // verification therefore validates source integrity and target structure
96
+ // without treating normal post-migration writes as a migration conflict.
97
+ const manifest = verifyDataMigration(readDataMigration(root, requestedId ?? latestMigrationId(root)), { strictTargetContent: false });
98
+ printSummary(manifest);
99
+ if (manifest.state === 'completed_with_errors')
100
+ process.exitCode = 1;
101
+ return;
102
+ }
103
+ if (!args.includes('--apply') && resumeIndex < 0)
104
+ throw new Error('select --dry-run, --apply, --resume, or --verify');
105
+ const initialRuntime = liveRuntime();
106
+ const wasRunning = initialRuntime.mainCount > 0
107
+ || initialRuntime.monitorPids.length > 0
108
+ || await daemonIpcReachable();
109
+ if (wasRunning) {
110
+ console.log('Stopping daemon and all Agent runtimes for data migration...');
111
+ // A restart monitor would otherwise relaunch the daemon in the middle of
112
+ // a copy transaction, so it must leave the maintenance window first.
113
+ await stopRestartMonitors(initialRuntime.monitorPids);
114
+ await cmdStop();
115
+ await waitForMaintenanceWindow();
116
+ }
117
+ let manifest = resumeIndex >= 0
118
+ ? applyDataMigration(readDataMigration(root, requestedId))
119
+ : applyDataMigration(planDataMigration(root));
120
+ manifest = verifyDataMigration(manifest);
121
+ printSummary(manifest);
122
+ // A partial migration must be inspected before any runtime can write the new
123
+ // layout. All independent operations have still been attempted and recorded.
124
+ if (wasRunning && manifest.state === 'completed') {
125
+ console.log('Starting daemon and Agent runtimes after verified migration...');
126
+ await cmdStart();
127
+ }
128
+ else if (wasRunning) {
129
+ console.error('Migration has errors; daemon remains stopped. Fix or resume the listed operations before restarting.');
130
+ process.exitCode = 1;
131
+ }
132
+ }
package/dist/cli/index.js CHANGED
@@ -53,6 +53,7 @@ AUN:
53
53
  mv 迁移项目目录和会话路径
54
54
 
55
55
  维护命令:
56
+ data migrate 迁移 per-Agent 用户数据(显式维护窗口)
56
57
  mode 切换开发源码与 npm 包运行模式
57
58
  restart-monitor 运行 daemon 重启监控器
58
59
  bench AUN 消息性能基准测试
@@ -103,13 +104,13 @@ export async function main(args) {
103
104
  仅初始化 defaults.json:
104
105
  ec init 交互式(写完 defaults.json 后嵌套 agent new)
105
106
  ec init --non-interactive [选项]
106
- --baseagent <claude|codex|gemini> 默认: PATH 中第一个可用项
107
+ --baseagent <claude|codex|gemini|ecagent> 默认: PATH 中第一个可用项;ecagent 需显式选择
107
108
  --force 已存在 defaults.json 时覆盖
108
109
 
109
110
  云部署非交互式初始化(结构化 JSON 输出):
110
111
  ec init --non-interactive --owner <aid> [--format json] [选项]
111
112
  --owner <aid> 必填。唯一 daemon owner AID
112
- --baseagent <claude|codex|gemini> 可选。默认从 PATH 检测
113
+ --baseagent <claude|codex|gemini|ecagent> 可选。默认从 PATH 检测,ecagent 需显式选择
113
114
  --projectpath <abs-path> 可选。默认项目目录(必须绝对路径,自动创建)
114
115
  --ecweb 可选。安装 EC Web 并启用自启动
115
116
  --format json 可选但推荐。stdout 输出 init.result JSON
@@ -161,7 +162,7 @@ export async function main(args) {
161
162
  const { suppressSdkLogs } = await import('../aun/aid/index.js');
162
163
  suppressSdkLogs();
163
164
  const nonInteractive = args.includes('--non-interactive');
164
- await cmdInit({
165
+ const initialized = await cmdInit({
165
166
  nonInteractive,
166
167
  baseagent: getArgValue(args, '--baseagent'),
167
168
  force: args.includes('--force'),
@@ -170,6 +171,8 @@ export async function main(args) {
170
171
  ecweb: args.includes('--ecweb'),
171
172
  format: getArgValue(args, '--format'),
172
173
  });
174
+ if (!initialized)
175
+ process.exitCode = 1;
173
176
  }
174
177
  break;
175
178
  case 'start':
@@ -253,6 +256,11 @@ export async function main(args) {
253
256
  }
254
257
  await cmdDiagnose();
255
258
  break;
259
+ case 'data': {
260
+ const { cmdData } = await import('./data-command.js');
261
+ await cmdData(args.slice(1));
262
+ break;
263
+ }
256
264
  case 'net': {
257
265
  const { cmdNet } = await import('./net-check.js');
258
266
  await cmdNet(args.slice(1));