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/init.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import fs from 'fs';
2
+ import os from 'os';
2
3
  import path from 'path';
3
4
  import readline from 'readline';
4
5
  import { resolvePaths, ensureDataDirs } from '../paths.js';
@@ -7,28 +8,54 @@ import { scanInstances } from '../utils/instance-registry.js';
7
8
  import { saveDefaultsSafe, loadAllAgents, loadDaemonConfig, saveDaemonConfig } from '../config-store.js';
8
9
  import { generateControlAid, resolveControlIssuer } from '../aun/aid/control-aid.js';
9
10
  import { getCodexAppServerAvailability, isCodexAppServerAvailable } from '../agents/codex-runner.js';
11
+ import { resolveEcagentConfig } from '../agents/baseagent.js';
10
12
  import { defaultProjectsRoot } from '../utils/project-path.js';
11
13
  import { WEB_CLI_BIN, WEB_PACKAGE_LATEST } from '../product.js';
12
14
  // ==================== Helpers ====================
13
15
  function ask(rl, question) {
14
16
  return new Promise(resolve => rl.question(question, resolve));
15
17
  }
16
- const BASEAGENT_CANDIDATES = ['claude', 'codex', 'gemini'];
18
+ /** 展开用户在交互式输入中使用的 home 目录简写(shell 不会替 readline 自动做这件事)。 */
19
+ export function expandLeadingTildePath(value, homeDir = os.homedir()) {
20
+ if (value === '~')
21
+ return homeDir;
22
+ if (value.startsWith('~/') || value.startsWith('~\\')) {
23
+ return path.join(homeDir, value.slice(2));
24
+ }
25
+ return value;
26
+ }
27
+ const BASEAGENT_CANDIDATES = ['claude', 'codex', 'gemini', 'ecagent'];
28
+ const AUTO_BASEAGENT_CANDIDATES = ['claude', 'codex', 'gemini'];
17
29
  function isBaseagentAvailable(baseagent) {
18
30
  if (baseagent === 'codex')
19
31
  return isCodexAppServerAvailable();
32
+ if (baseagent === 'ecagent')
33
+ return true;
20
34
  return commandExists(baseagent);
21
35
  }
22
36
  function detectAvailable() {
23
37
  return BASEAGENT_CANDIDATES.filter(isBaseagentAvailable);
24
38
  }
25
39
  function pickDefault(available) {
26
- return (available.includes('claude') ? 'claude' : available[0]);
40
+ return AUTO_BASEAGENT_CANDIDATES.find(candidate => available.includes(candidate));
41
+ }
42
+ function ecagentUnavailableReason() {
43
+ try {
44
+ resolveEcagentConfig({ agents: { ecagent: {} } });
45
+ return undefined;
46
+ }
47
+ catch (error) {
48
+ return error instanceof Error ? error.message : String(error);
49
+ }
27
50
  }
28
51
  function buildDefaults(chosen, available, projectsDefaultPath) {
29
52
  const baseagents = {};
30
- for (const b of available)
53
+ for (const b of available) {
54
+ // ecagent is bundled but opt-in; do not declare it merely because it is available.
55
+ if (b === 'ecagent' && chosen !== 'ecagent')
56
+ continue;
31
57
  baseagents[b] = {};
58
+ }
32
59
  return {
33
60
  $schema_version: 1,
34
61
  active_baseagent: chosen,
@@ -94,7 +121,7 @@ export async function cmdInit(options) {
94
121
  // 云部署非交互式路径:当带 --owner 或 --format json 时走结构化分支,与交互式 tail 完全隔离。
95
122
  // 预留方案 B:当 daemon 支持 Control Plane 无 agent 启动时,可在此分支跳过 agent new 预创建。
96
123
  if (options?.nonInteractive && (options.owner || options.format === 'json' || options.projectpath || options.ecweb)) {
97
- return cmdInitNonInteractive({
124
+ await cmdInitNonInteractive({
98
125
  owner: options.owner,
99
126
  baseagent: options.baseagent,
100
127
  projectpath: options.projectpath,
@@ -102,6 +129,7 @@ export async function cmdInit(options) {
102
129
  force: options.force,
103
130
  format: options.format,
104
131
  });
132
+ return true;
105
133
  }
106
134
  const p = resolvePaths();
107
135
  ensureDataDirs();
@@ -110,17 +138,17 @@ export async function cmdInit(options) {
110
138
  if (aliveMains.length > 0) {
111
139
  const pids = aliveMains.map(m => m.record.pid).join(', ');
112
140
  console.log(`❌ EvolCore 正在运行 (PID: ${pids}),请先执行 ec stop`);
113
- return;
141
+ return false;
114
142
  }
115
143
  // ── 2. 探测 baseagent ──
116
144
  const available = detectAvailable();
117
- if (available.length === 0) {
145
+ if (available.length === 0 || (available.length === 1 && available[0] === 'ecagent' && ecagentUnavailableReason())) {
118
146
  console.log('❌ 未检测到可用 baseagent。请安装至少一款:');
119
147
  console.log(' - claude CLI');
120
148
  console.log(' - gemini CLI');
121
149
  console.log(' - codex CLI with app-server');
122
150
  console.log('\n安装后重新运行 ec init');
123
- return;
151
+ return false;
124
152
  }
125
153
  console.log('检测到可用的 baseagent:');
126
154
  for (const b of available)
@@ -142,19 +170,31 @@ export async function cmdInit(options) {
142
170
  if (options.baseagent) {
143
171
  if (!BASEAGENT_CANDIDATES.includes(options.baseagent)) {
144
172
  console.log(`❌ 无效 baseagent: ${options.baseagent}(可选: ${BASEAGENT_CANDIDATES.join('/')})`);
145
- return; // 硬错误:不落 tail
173
+ return false; // 硬错误:不落 tail
146
174
  }
147
175
  if (!available.includes(options.baseagent)) {
148
176
  const reason = options.baseagent === 'codex'
149
177
  ? getCodexAppServerAvailability().reason
150
178
  : undefined;
151
179
  console.log(`❌ ${options.baseagent} 当前环境不可用${reason ? `:${reason}` : `(可用: ${available.join('/')})`}`);
152
- return; // 硬错误:不落 tail
180
+ return false; // 硬错误:不落 tail
181
+ }
182
+ if (options.baseagent === 'ecagent') {
183
+ const reason = ecagentUnavailableReason();
184
+ if (reason) {
185
+ console.log(`❌ ecagent 当前环境不可用:${reason}`);
186
+ return false;
187
+ }
153
188
  }
154
189
  chosen = options.baseagent;
155
190
  }
156
191
  else {
157
- chosen = pickDefault(available);
192
+ const defaultBaseagent = pickDefault(available);
193
+ if (!defaultBaseagent) {
194
+ console.log('❌ ecagent 为可选 baseagent,请显式使用 --baseagent ecagent,并配置 API key');
195
+ return false;
196
+ }
197
+ chosen = defaultBaseagent;
158
198
  }
159
199
  writeDefaults(chosen, available);
160
200
  console.log(`✓ 已${defaultsExisted ? '覆盖' : '创建'}: ${defaultsPath}`);
@@ -167,19 +207,24 @@ export async function cmdInit(options) {
167
207
  await runInteractive();
168
208
  }
169
209
  // ── 共享 tail(单一出口):提示创建 agent + 生成控制 AID ──
170
- await initTail();
210
+ return initTail({ invokedByStart: options?.invokedByStart });
171
211
  // ── 内部函数 ──
172
212
  async function runInteractive() {
173
213
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
174
214
  async function askBaseagent() {
175
215
  const defaultBa = pickDefault(available);
176
- if (available.length === 1) {
216
+ if (available.length === 1 && defaultBa) {
177
217
  console.log(` baseagent: ${defaultBa}`);
178
218
  return defaultBa;
179
219
  }
180
220
  let chosen = null;
181
221
  while (chosen === null) {
182
- const input = (await ask(rl, `默认 baseagent (${available.join('/')}) [${defaultBa}]: `)).trim() || defaultBa;
222
+ const defaultHint = defaultBa ? ` [${defaultBa}]` : '';
223
+ const input = (await ask(rl, `默认 baseagent (${available.join('/')})${defaultHint}: `)).trim() || defaultBa;
224
+ if (!input) {
225
+ console.log(' ecagent 为可选项,请显式输入 ecagent');
226
+ continue;
227
+ }
183
228
  if (!BASEAGENT_CANDIDATES.includes(input)) {
184
229
  console.log(` 无效选择,可选: ${BASEAGENT_CANDIDATES.join('/')}`);
185
230
  continue;
@@ -188,35 +233,49 @@ export async function cmdInit(options) {
188
233
  console.log(` ${input} 当前环境不可用(可用: ${available.join('/')})`);
189
234
  continue;
190
235
  }
236
+ if (input === 'ecagent') {
237
+ const reason = ecagentUnavailableReason();
238
+ if (reason) {
239
+ console.log(` ecagent 当前环境不可用:${reason}`);
240
+ continue;
241
+ }
242
+ }
191
243
  chosen = input;
192
244
  }
193
245
  return chosen;
194
246
  }
195
247
  async function askProjectsDefaultPath() {
196
248
  const defaultDir = path.join(defaultProjectsRoot(p.root), 'default');
197
- const input = (await ask(rl, `项目默认目录 [${defaultDir}]: `)).trim();
198
- const resolved = input || defaultDir;
199
- if (!path.isAbsolute(resolved)) {
200
- console.log(' ⚠ 需要绝对路径,已跳过');
201
- return undefined;
202
- }
203
- if (!fs.existsSync(resolved)) {
204
- const create = (await ask(rl, ` 目录不存在,是否创建?[Y/n]: `)).trim().toLowerCase();
205
- if (create === '' || create === 'y' || create === 'yes') {
206
- try {
207
- fs.mkdirSync(resolved, { recursive: true });
249
+ while (true) {
250
+ const input = (await ask(rl, `项目默认目录 [${defaultDir}](输入 skip 跳过): `)).trim();
251
+ if (input.toLowerCase() === 'skip') {
252
+ console.log(' 已跳过项目默认目录配置');
253
+ return undefined;
254
+ }
255
+ const resolved = expandLeadingTildePath(input || defaultDir);
256
+ if (!path.isAbsolute(resolved)) {
257
+ console.log(' 请输入绝对路径或回车使用默认值;输入 skip 可跳过');
258
+ continue;
259
+ }
260
+ if (!fs.existsSync(resolved)) {
261
+ const create = (await ask(rl, ` 目录不存在,是否创建?[Y/n]: `)).trim().toLowerCase();
262
+ if (create === '' || create === 'y' || create === 'yes') {
263
+ try {
264
+ fs.mkdirSync(resolved, { recursive: true });
265
+ }
266
+ catch (e) {
267
+ console.log(` ⚠ 创建目录失败: ${e?.message || e}`);
268
+ continue;
269
+ }
270
+ console.log(` ✓ 已创建 ${resolved}`);
208
271
  }
209
- catch (e) {
210
- console.log(` 创建目录失败: ${e?.message || e}`);
211
- return undefined;
272
+ else {
273
+ console.log(' 未创建目录,请重新输入路径;输入 skip 可跳过');
274
+ continue;
212
275
  }
213
- console.log(` ✓ 已创建 ${resolved}`);
214
- }
215
- else {
216
- return undefined;
217
276
  }
277
+ return resolved;
218
278
  }
219
- return resolved;
220
279
  }
221
280
  try {
222
281
  if (defaultsExisted) {
@@ -249,15 +308,16 @@ export async function cmdInit(options) {
249
308
  }
250
309
  }
251
310
  /** 补全控制 AID + owners(可单独调用,不走 baseagent 向导)。 */
252
- export async function initTail() {
311
+ export async function initTail(options = {}) {
253
312
  // 提示创建 agent(两分支汇合后执行一次)
254
313
  const { agents } = loadAllAgents();
255
- if (agents.length === 0) {
314
+ if (agents.length === 0 && !options.invokedByStart) {
256
315
  console.log('\n提示:尚无 agent,运行以下命令创建:');
257
316
  console.log(' ec agent new <aid>.agentid.pub');
258
317
  }
259
318
  // 控制 AID:daemon 进程身份。缺失则生成并写回 daemon.json(幂等:已存在则跳过)。
260
319
  const daemonConfig = loadDaemonConfig();
320
+ let controlAidReady = !!daemonConfig.aid;
261
321
  if (daemonConfig.aid) {
262
322
  const targetIssuer = resolveControlIssuer();
263
323
  const currentIssuer = daemonConfig.aid.split('.').slice(1).join('.');
@@ -271,10 +331,12 @@ export async function initTail() {
271
331
  try {
272
332
  const { aid } = await generateControlAid();
273
333
  saveDaemonConfig({ ...daemonConfig, $schema_version: daemonConfig.$schema_version ?? 1, aid });
334
+ controlAidReady = true;
274
335
  console.log(`✓ 已生成控制 AID: ${aid}`);
275
336
  }
276
337
  catch (e) {
277
338
  console.error(`⚠️ 控制 AID 生成失败(Gateway 不可达?联网后重跑 ec init 补全): ${e?.message || e}`);
339
+ controlAidReady = false;
278
340
  }
279
341
  }
280
342
  // 交互式询问:使用单一 readline 实例避免 stdin 状态混乱
@@ -368,6 +430,13 @@ export async function initTail() {
368
430
  }
369
431
  }
370
432
  }
433
+ // ec start 会接着启动 daemon,并在其 ready 后给出可用的 Agent 创建入口。
434
+ if (options.invokedByStart)
435
+ return controlAidReady;
436
+ if (!controlAidReady) {
437
+ console.error('\n❌ EvolCore 初始化未完成:控制 AID 未配置。联网后重新运行 ec init。');
438
+ return false;
439
+ }
371
440
  // 初始化完成总结
372
441
  console.log('\n✓ EvolCore 初始化完成');
373
442
  const finalCfg = loadDaemonConfig();
@@ -385,6 +454,7 @@ export async function initTail() {
385
454
  console.log(`\n✓ 已有 ${finalAgents.length} 个 agent,可直接运行:`);
386
455
  console.log(' ec start');
387
456
  }
457
+ return controlAidReady;
388
458
  }
389
459
  /**
390
460
  * Present instance selection menu when existing instances are found.
@@ -480,8 +550,8 @@ export async function cmdInitNonInteractive(opts, dependencies = {}) {
480
550
  }
481
551
  // ── 3. baseagent 探测 + 校验 ──
482
552
  const available = dependencies.availableBaseagents ?? detectAvailable();
483
- if (available.length === 0) {
484
- fail('BASEAGENT_UNAVAILABLE', 'no baseagent CLI detected (install claude/codex/gemini)', EXIT_USAGE, format);
553
+ if (available.length === 0 || (available.length === 1 && available[0] === 'ecagent' && ecagentUnavailableReason())) {
554
+ fail('BASEAGENT_UNAVAILABLE', 'no usable baseagent detected (install claude/codex/gemini or configure bundled ecagent)', EXIT_USAGE, format);
485
555
  }
486
556
  let chosenBaseagent;
487
557
  if (opts.baseagent) {
@@ -492,10 +562,19 @@ export async function cmdInitNonInteractive(opts, dependencies = {}) {
492
562
  const reason = opts.baseagent === 'codex' ? getCodexAppServerAvailability().reason : undefined;
493
563
  fail('BASEAGENT_UNAVAILABLE', `${opts.baseagent} not available${reason ? `: ${reason}` : ''}`, EXIT_USAGE, format);
494
564
  }
565
+ if (opts.baseagent === 'ecagent') {
566
+ const reason = ecagentUnavailableReason();
567
+ if (reason)
568
+ fail('BASEAGENT_UNAVAILABLE', reason, EXIT_USAGE, format);
569
+ }
495
570
  chosenBaseagent = opts.baseagent;
496
571
  }
497
572
  else {
498
- chosenBaseagent = pickDefault(available);
573
+ const defaultBaseagent = pickDefault(available);
574
+ if (!defaultBaseagent) {
575
+ fail('BASEAGENT_UNAVAILABLE', 'ecagent is optional; pass --baseagent ecagent after configuring its API key', EXIT_USAGE, format);
576
+ }
577
+ chosenBaseagent = defaultBaseagent;
499
578
  }
500
579
  // ── 4. projectpath 校验 + 创建 ──
501
580
  let projectsDefaultPath;
@@ -307,31 +307,20 @@ async function runCheck(aid, formatJson, kickTest) {
307
307
  // 统计每个 AID 的总消息活跃度(遍历所有本地 AID 的 sessions)
308
308
  const { resolvePaths } = await import('../paths.js');
309
309
  const p = resolvePaths();
310
- const sessAunDir = path.join(p.sessionsDir, 'aun');
311
- const encode = (s) => s.replace(/[/%\\:*?"<>|]/g, ch => '%' + ch.charCodeAt(0).toString(16).toUpperCase().padStart(2, '0'));
312
- const decode = (s) => s.replace(/%([0-9A-Fa-f]{2})/g, (_, hex) => String.fromCharCode(parseInt(hex, 16)));
310
+ const { scanChatDirs } = await import('../core/session/session-fs-store.js');
313
311
  function getGlobalMessageCounts() {
314
312
  const counts = new Map();
315
313
  try {
316
- const localDirs = fs.readdirSync(sessAunDir, { withFileTypes: true });
317
- for (const localDir of localDirs) {
318
- if (!localDir.isDirectory())
314
+ for (const chat of scanChatDirs(p.sessionsDir)) {
315
+ if (chat.channelType !== 'aun')
319
316
  continue;
320
- const localAid = decode(localDir.name);
321
- const peerDirs = fs.readdirSync(path.join(sessAunDir, localDir.name), { withFileTypes: true });
322
- for (const peerDir of peerDirs) {
323
- if (!peerDir.isDirectory())
324
- continue;
325
- const peerId = decode(peerDir.name);
326
- const msgFile = path.join(sessAunDir, localDir.name, peerDir.name, 'messages.jsonl');
327
- try {
328
- const content = fs.readFileSync(msgFile, 'utf-8');
329
- const lineCount = content.split('\n').filter(l => l.trim()).length;
330
- counts.set(peerId, (counts.get(peerId) ?? 0) + lineCount);
331
- counts.set(localAid, (counts.get(localAid) ?? 0) + lineCount);
332
- }
333
- catch { }
317
+ const msgFile = path.join(chat.dirPath, 'messages.jsonl');
318
+ try {
319
+ const lineCount = fs.readFileSync(msgFile, 'utf-8').split('\n').filter(l => l.trim()).length;
320
+ counts.set(chat.channelId, (counts.get(chat.channelId) ?? 0) + lineCount);
321
+ counts.set(chat.selfAID, (counts.get(chat.selfAID) ?? 0) + lineCount);
334
322
  }
323
+ catch { }
335
324
  }
336
325
  }
337
326
  catch { }
@@ -2,7 +2,7 @@ import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { spawn, execFile } from 'child_process';
4
4
  import { promisify } from 'util';
5
- import { resolvePaths, getPackageRoot } from '../paths.js';
5
+ import { resolvePaths, getPackageRoot, daemonControlDir } from '../paths.js';
6
6
  import { loadDaemonConfig } from '../config-store.js';
7
7
  import * as platform from '../utils/cross-platform.js';
8
8
  import { EventBus } from '../core/event-bus.js';
@@ -14,6 +14,7 @@ import { scanInstances, cleanupInstances, writeRestartMonitor, removeRestartMoni
14
14
  import { WEB_PACKAGE_NAME } from '../product.js';
15
15
  import { shouldSuppressRealRestart } from '../utils/restart-safety.js';
16
16
  import { rotateStdoutLog } from '../utils/log-writer.js';
17
+ import { inspectDataMigrationRequirement } from '../core/data-migration.js';
17
18
  const execFileAsync = promisify(execFile);
18
19
  // 清理 Claude Code 环境变量,防止 SDK 认为是嵌套会话
19
20
  function cleanEnv() {
@@ -72,6 +73,14 @@ export async function cmdRestartMonitor() {
72
73
  return s.mains.some(m => m.alive);
73
74
  };
74
75
  log('Restart monitor started');
76
+ const migrationRequirement = inspectDataMigrationRequirement(p.root);
77
+ if (migrationRequirement.required) {
78
+ const message = `Pending user-data migration (${migrationRequirement.operationCount} operations); leaving the current service running. Run ec data migrate --dry-run, then ec data migrate --apply.`;
79
+ log(message);
80
+ console.error(`❌ ${message}`);
81
+ process.exitCode = 2;
82
+ return;
83
+ }
75
84
  // Test daemons live in one temporary HOME per test, so the current HOME's
76
85
  // instance registry cannot see them. Their strict environment markers make
77
86
  // them safe to reap globally on every real restart.
@@ -84,7 +93,7 @@ export async function cmdRestartMonitor() {
84
93
  }
85
94
  }
86
95
  // restart-pending.json 只留给新主进程发送发起会话内的“重启成功”回执。
87
- const pendingFile = path.join(p.dataDir, 'restart-pending.json');
96
+ const pendingFile = path.join(daemonControlDir(), 'restart-pending.json');
88
97
  // 等待所有活 main 进程退出(可能不止一个)
89
98
  const oldStatus = scanInstances();
90
99
  const aliveMains = oldStatus.mains.filter(m => m.alive);
@@ -110,7 +110,7 @@ Create 参数模式支持:
110
110
  --tz <时区> (仅 cron)
111
111
  --target-channel <channelKey> --target-channel-id <ID> (AUN 可简写为 aun)
112
112
  --target-session <main|thread> [--target-thread-id <threadId>]
113
- --trigger-thread <per-run|by-trigger>
113
+ --trigger-thread <by-run|by-trigger> (兼容旧值 per-run)
114
114
  --name <名称>
115
115
 
116
116
  通用输出参数:
@@ -1,7 +1,7 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { resolvePaths } from '../paths.js';
4
- import { decodeDirSegment, readAllJsonlLines } from '../core/session/session-fs-store.js';
4
+ import { chatDirPath, readAllJsonlLines, scanChatDirs } from '../core/session/session-fs-store.js';
5
5
  import { isTransientProtocolMessage } from '../core/message/message-log.js';
6
6
  // ==================== ANSI ====================
7
7
  const isTTY = !!process.stdout.isTTY;
@@ -135,36 +135,26 @@ function shortAid(aid) {
135
135
  // ==================== Data Layer ====================
136
136
  export function getSessionsAunDir() {
137
137
  const p = resolvePaths();
138
- return path.join(p.sessionsDir, 'aun');
138
+ return p.sessionsDir;
139
139
  }
140
140
  export function listLocalAids(aunDir) {
141
- try {
142
- return fs.readdirSync(aunDir, { withFileTypes: true })
143
- .filter(e => e.isDirectory())
144
- .map(e => decodeDirSegment(e.name));
145
- }
146
- catch {
147
- return [];
148
- }
141
+ return [...new Set(scanChatDirs(aunDir)
142
+ .filter(chat => chat.channelType === 'aun')
143
+ .map(chat => chat.selfAID))].sort();
149
144
  }
150
145
  export function listPeers(aunDir, localAid) {
151
- const aidDir = path.join(aunDir, encodeSegment(localAid));
152
- try {
153
- return fs.readdirSync(aidDir, { withFileTypes: true })
154
- .filter(e => e.isDirectory() && !e.name.startsWith('_'))
155
- .map(e => decodeDirSegment(e.name));
156
- }
157
- catch {
158
- return [];
159
- }
146
+ return scanChatDirs(aunDir)
147
+ .filter(chat => chat.channelType === 'aun' && chat.selfAID === localAid)
148
+ .map(chat => chat.channelId)
149
+ .sort();
160
150
  }
161
151
  export function readMessages(aunDir, localAid, peerId) {
162
- const msgPath = path.join(aunDir, encodeSegment(localAid), encodeSegment(peerId), 'messages.jsonl');
152
+ const msgPath = path.join(chatDirPath(aunDir, 'aun', peerId, localAid), 'messages.jsonl');
163
153
  return readAllJsonlLines(msgPath)
164
154
  .filter(m => !isTransientProtocolMessage(m));
165
155
  }
166
156
  function readPeerName(aunDir, localAid, peerId) {
167
- const activePath = path.join(aunDir, encodeSegment(localAid), encodeSegment(peerId), 'active.json');
157
+ const activePath = path.join(chatDirPath(aunDir, 'aun', peerId, localAid), 'active.json');
168
158
  try {
169
159
  const data = JSON.parse(fs.readFileSync(activePath, 'utf-8'));
170
160
  return data?.metadata?.peerName || null;
@@ -173,9 +163,6 @@ function readPeerName(aunDir, localAid, peerId) {
173
163
  return null;
174
164
  }
175
165
  }
176
- function encodeSegment(s) {
177
- return s.replace(/[/%\\:*?"<>|]/g, ch => '%' + ch.charCodeAt(0).toString(16).toUpperCase().padStart(2, '0'));
178
- }
179
166
  export function loadAidInfo(aunDir, aid) {
180
167
  const peers = listPeers(aunDir, aid);
181
168
  let totalIn = 0, totalOut = 0;
@@ -457,7 +444,7 @@ export async function cmdWatchMsg() {
457
444
  watcher.close();
458
445
  watcher = null;
459
446
  }
460
- const aidDir = path.join(aunDir, encodeSegment(aid));
447
+ const aidDir = path.dirname(path.dirname(chatDirPath(aunDir, 'aun', '__watch__', aid)));
461
448
  try {
462
449
  watcher = fs.watch(aidDir, { recursive: true }, (_, filename) => {
463
450
  if (filename && filename.endsWith('messages.jsonl')) {
@@ -4,18 +4,6 @@ export function validAgentOwnerIds(agent) {
4
4
  .map(value => String(value || '').trim())
5
5
  .filter(isValidAid))];
6
6
  }
7
- export function configuredChannelKeys(selfAid, agent) {
8
- const keys = (agent?.channels ?? [])
9
- .filter(channel => channel && channel.enabled !== false)
10
- .map(channel => {
11
- const type = String(channel.type || '').trim().toLowerCase();
12
- const name = String(channel.name || '').trim();
13
- return type && name ? `${type}#${selfAid}#${name}` : '';
14
- })
15
- .filter(Boolean);
16
- keys.push(`aun#${selfAid}#main`);
17
- return [...new Set(keys)];
18
- }
19
7
  export function validateAccessPolicyDomain(selfAid, agent) {
20
8
  const access = agent.access;
21
9
  if (!access)
@@ -26,13 +14,5 @@ export function validateAccessPolicyDomain(selfAid, agent) {
26
14
  message: `Agent ${selfAid} cannot use ${access.policyMode} without an Owner`,
27
15
  };
28
16
  }
29
- const configured = new Set(configuredChannelKeys(selfAid, agent));
30
- const unknown = access.allowedGroupChannels.find(channelKey => !configured.has(channelKey));
31
- if (unknown) {
32
- return {
33
- code: 'unknown_group_access_channel',
34
- message: `Group access channel is not configured or enabled for ${selfAid}: ${unknown}`,
35
- };
36
- }
37
17
  return undefined;
38
18
  }
@@ -1,6 +1,6 @@
1
1
  import crypto from 'crypto';
2
2
  import { ConfigError, ConfigTarget, mutateConfig, read, validateConfig, } from './config-manager.js';
3
- import { configuredChannelKeys, validAgentOwnerIds, validateAccessPolicyDomain, } from './access-policy-domain.js';
3
+ import { validAgentOwnerIds, validateAccessPolicyDomain, } from './access-policy-domain.js';
4
4
  export class AccessPolicyError extends Error {
5
5
  code;
6
6
  data;
@@ -13,20 +13,13 @@ export class AccessPolicyError extends Error {
13
13
  }
14
14
  export const DEFAULT_ACCESS_POLICY = Object.freeze({
15
15
  policyMode: 'open',
16
- allowedGroupChannels: Object.freeze([]),
17
16
  requestLimit: Object.freeze({ maxTimes: 600, coolDown: 60 * 60 }),
18
17
  });
19
18
  export function listAgentOwners(selfAid) {
20
19
  const agent = read(ConfigTarget.Agent, { self: selfAid }, { cache: true });
21
20
  return validAgentOwnerIds(agent);
22
21
  }
23
- export function listConfiguredChannelKeys(selfAid) {
24
- const agent = read(ConfigTarget.Agent, { self: selfAid }, { cache: true });
25
- if (!agent)
26
- return [];
27
- return configuredChannelKeys(selfAid, agent);
28
- }
29
- /** Missing AgentConfig.access uses the compatibility view; malformed Agent config never does. */
22
+ /** Missing AgentConfig.access uses the default open policy; malformed Agent config never does. */
30
23
  export function readAccessPolicy(selfAid) {
31
24
  return readAccessPolicySnapshot(selfAid).policy;
32
25
  }
@@ -49,7 +42,7 @@ export function mutateAccessPolicy(input) {
49
42
  });
50
43
  }
51
44
  const next = input.update(clonePolicy(current.policy));
52
- assertAccessPolicyWrite(input, agent, current.policy, next);
45
+ assertAccessPolicyWrite(input, agent, next);
53
46
  agent.access = clonePolicy(next);
54
47
  return {
55
48
  policy: clonePolicy(next),
@@ -85,37 +78,20 @@ function accessPolicySnapshotFromAgent(selfAid, agent) {
85
78
  const policy = clonePolicy(access);
86
79
  return { policy, accessRevision: accessPolicyRevision(policy) };
87
80
  }
88
- function assertAccessPolicyWrite(input, agent, current, policy) {
81
+ function assertAccessPolicyWrite(input, agent, policy) {
89
82
  const owners = validAgentOwnerIds(agent);
90
83
  const normalizedActorAid = String(input.actorAid || '').trim();
91
84
  const isOwner = input.actorRole === 'owner' || owners.includes(normalizedActorAid);
92
- const isGroupChannelAdmin = input.actorRole === 'admin' && input.resource === 'access.allowedGroupChannels';
93
- if (!isOwner && !isGroupChannelAdmin) {
94
- if (input.resource === 'access.allowedGroupChannels') {
95
- throw new AccessPolicyError('access_policy_manager_required', 'Only an Agent Owner or Admin can change allowed group channels');
96
- }
85
+ if (!isOwner) {
97
86
  throw new AccessPolicyError('access_policy_owner_required', 'Only an Agent Owner can change access policy');
98
87
  }
99
- if (isGroupChannelAdmin) {
100
- if (current.policyMode !== policy.policyMode
101
- || current.requestLimit.maxTimes !== policy.requestLimit.maxTimes
102
- || current.requestLimit.coolDown !== policy.requestLimit.coolDown) {
103
- throw new AccessPolicyError('access_policy_owner_required', 'An Agent Admin may only change allowed group channels');
104
- }
105
- }
106
88
  if (policy.policyMode !== 'open' && owners.length === 0) {
107
89
  throw new AccessPolicyError('restricted_mode_without_owner', `Agent ${input.selfAid} cannot use ${policy.policyMode} without an Owner`);
108
90
  }
109
- const configured = new Set(configuredChannelKeys(input.selfAid, agent));
110
- const unknown = policy.allowedGroupChannels.find(channelKey => !configured.has(channelKey));
111
- if (unknown) {
112
- throw new AccessPolicyError('unknown_group_access_channel', `Group access channel is not configured or enabled for ${input.selfAid}: ${unknown}`);
113
- }
114
91
  }
115
92
  function clonePolicy(policy) {
116
93
  return {
117
94
  policyMode: policy.policyMode,
118
- allowedGroupChannels: [...policy.allowedGroupChannels],
119
95
  requestLimit: { ...policy.requestLimit },
120
96
  };
121
97
  }
@@ -40,7 +40,9 @@ export function getManagementCommandPermissions(role) {
40
40
  'config.unset': { allow: true, scopes: ['agent', 'relation'] },
41
41
  'role.policy.write': { allow: true, dangerous: true, scopes: ['agent'], constraints: { requireAgentAdmin: true } },
42
42
  '*': { allow: true },
43
- 'agent.reload': { allow: true, dangerous: true, scopes: ['agent'], constraints: { targetCurrentAgentOnly: true, requireAgentAdmin: true } },
43
+ // Reload is valid from an Agent's own channel and the daemon control plane.
44
+ // The control-channel gate still requires a daemon owner for cross-Agent reloads.
45
+ 'agent.reload': { allow: true, dangerous: true, scopes: ['agent', 'control'], constraints: { targetCurrentAgentOnly: true, requireAgentAdmin: true } },
44
46
  'dangerous:*': { allow: true, dangerous: true, constraints: { requireDaemonOwner: true } },
45
47
  };
46
48
  }
@@ -25,7 +25,7 @@ const BASEAGENT_BEHAVIOR_FIELDS = new Set([
25
25
  'mode',
26
26
  'useVertex',
27
27
  ]);
28
- const SUPPORTED_BASEAGENTS = new Set(['claude', 'codex', 'gemini', 'hermes']);
28
+ const SUPPORTED_BASEAGENTS = new Set(['claude', 'codex', 'gemini', 'ecagent', 'hermes']);
29
29
  const SENSITIVE_TOP_FIELDS = new Set([
30
30
  '$schema_version',
31
31
  'aid',