evolcore 0.0.5 → 0.0.7

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 (323) hide show
  1. package/CHANGELOG.md +49 -0
  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 +88 -8
  21. package/dist/cli/data-command.js +132 -0
  22. package/dist/cli/index.js +11 -3
  23. package/dist/cli/init.js +76 -17
  24. package/dist/cli/net-check.js +9 -20
  25. package/dist/cli/restart-monitor.js +25 -2
  26. package/dist/cli/trigger-command.js +1 -1
  27. package/dist/cli/watch-msg.js +12 -25
  28. package/dist/config/builtin-roles.js +3 -1
  29. package/dist/config/config-field-policy.js +1 -1
  30. package/dist/config/gateway-config.js +21 -4
  31. package/dist/config/resolved-config-op.js +10 -0
  32. package/dist/core/command/command-handler.js +2 -2
  33. package/dist/core/command/menu-handler.js +11 -9
  34. package/dist/core/command/role-menu.js +11 -0
  35. package/dist/core/command/slash-handler.js +18 -14
  36. package/dist/core/data-migration.js +1434 -0
  37. package/dist/core/handoff/store.js +14 -4
  38. package/dist/core/inference/text-inference.js +42 -1
  39. package/dist/core/message/pending-hints.js +1 -1
  40. package/dist/core/message/response-engine.js +406 -167
  41. package/dist/core/message/retry-scheduler.js +71 -0
  42. package/dist/core/model/model-catalog.js +7 -2
  43. package/dist/core/model/model-fallback.js +81 -0
  44. package/dist/core/protected-paths.js +41 -8
  45. package/dist/core/session/adapters/ecagent-session-file-adapter.js +112 -0
  46. package/dist/core/session/session-fs-store.js +283 -59
  47. package/dist/core/session/session-manager.js +19 -21
  48. package/dist/core/session/session-title.js +1 -0
  49. package/dist/core/session/session-turn-coordinator.js +8 -1
  50. package/dist/core/system-channels.js +1 -0
  51. package/dist/index.js +13 -10
  52. package/dist/paths.js +35 -27
  53. package/dist/trigger/feedback.js +16 -0
  54. package/dist/trigger/legacy-session-history.js +19 -0
  55. package/dist/trigger/manager.js +12 -1
  56. package/dist/trigger/parser.js +4 -2
  57. package/dist/trigger/scheduler.js +185 -15
  58. package/dist/trigger/session-lock.js +62 -0
  59. package/dist/trigger/state.js +64 -0
  60. package/dist/trigger/validation.js +32 -2
  61. package/dist/utils/aid-bind.js +1 -1
  62. package/dist/utils/error-utils.js +9 -0
  63. package/dist/utils/instance-registry.js +2 -2
  64. package/dist/utils/welcome.js +2 -3
  65. package/ecagent/LICENSE +21 -0
  66. package/ecagent/NOTICE +11 -0
  67. package/ecagent/README.md +61 -0
  68. package/ecagent/dist/agent-loop.d.ts +24 -0
  69. package/ecagent/dist/agent-loop.d.ts.map +1 -0
  70. package/ecagent/dist/agent-loop.js +547 -0
  71. package/ecagent/dist/agent-loop.js.map +1 -0
  72. package/ecagent/dist/agent.d.ts +127 -0
  73. package/ecagent/dist/agent.d.ts.map +1 -0
  74. package/ecagent/dist/agent.js +386 -0
  75. package/ecagent/dist/agent.js.map +1 -0
  76. package/ecagent/dist/harness/agent-harness.d.ts +95 -0
  77. package/ecagent/dist/harness/agent-harness.d.ts.map +1 -0
  78. package/ecagent/dist/harness/agent-harness.js +926 -0
  79. package/ecagent/dist/harness/agent-harness.js.map +1 -0
  80. package/ecagent/dist/harness/compaction/branch-summarization.d.ts +51 -0
  81. package/ecagent/dist/harness/compaction/branch-summarization.d.ts.map +1 -0
  82. package/ecagent/dist/harness/compaction/branch-summarization.js +174 -0
  83. package/ecagent/dist/harness/compaction/branch-summarization.js.map +1 -0
  84. package/ecagent/dist/harness/compaction/compaction.d.ts +95 -0
  85. package/ecagent/dist/harness/compaction/compaction.d.ts.map +1 -0
  86. package/ecagent/dist/harness/compaction/compaction.js +528 -0
  87. package/ecagent/dist/harness/compaction/compaction.js.map +1 -0
  88. package/ecagent/dist/harness/compaction/utils.d.ts +25 -0
  89. package/ecagent/dist/harness/compaction/utils.d.ts.map +1 -0
  90. package/ecagent/dist/harness/compaction/utils.js +131 -0
  91. package/ecagent/dist/harness/compaction/utils.js.map +1 -0
  92. package/ecagent/dist/harness/env/nodejs.d.ts +51 -0
  93. package/ecagent/dist/harness/env/nodejs.d.ts.map +1 -0
  94. package/ecagent/dist/harness/env/nodejs.js +513 -0
  95. package/ecagent/dist/harness/env/nodejs.js.map +1 -0
  96. package/ecagent/dist/harness/messages.d.ts +51 -0
  97. package/ecagent/dist/harness/messages.d.ts.map +1 -0
  98. package/ecagent/dist/harness/messages.js +102 -0
  99. package/ecagent/dist/harness/messages.js.map +1 -0
  100. package/ecagent/dist/harness/prompt-templates.d.ts +48 -0
  101. package/ecagent/dist/harness/prompt-templates.d.ts.map +1 -0
  102. package/ecagent/dist/harness/prompt-templates.js +230 -0
  103. package/ecagent/dist/harness/prompt-templates.js.map +1 -0
  104. package/ecagent/dist/harness/session/jsonl-repo.d.ts +26 -0
  105. package/ecagent/dist/harness/session/jsonl-repo.d.ts.map +1 -0
  106. package/ecagent/dist/harness/session/jsonl-repo.js +100 -0
  107. package/ecagent/dist/harness/session/jsonl-repo.js.map +1 -0
  108. package/ecagent/dist/harness/session/jsonl-storage.d.ts +34 -0
  109. package/ecagent/dist/harness/session/jsonl-storage.d.ts.map +1 -0
  110. package/ecagent/dist/harness/session/jsonl-storage.js +234 -0
  111. package/ecagent/dist/harness/session/jsonl-storage.js.map +1 -0
  112. package/ecagent/dist/harness/session/memory-repo.d.ts +18 -0
  113. package/ecagent/dist/harness/session/memory-repo.d.ts.map +1 -0
  114. package/ecagent/dist/harness/session/memory-repo.js +44 -0
  115. package/ecagent/dist/harness/session/memory-repo.js.map +1 -0
  116. package/ecagent/dist/harness/session/memory-storage.d.ts +25 -0
  117. package/ecagent/dist/harness/session/memory-storage.d.ts.map +1 -0
  118. package/ecagent/dist/harness/session/memory-storage.js +111 -0
  119. package/ecagent/dist/harness/session/memory-storage.js.map +1 -0
  120. package/ecagent/dist/harness/session/repo-utils.d.ts +11 -0
  121. package/ecagent/dist/harness/session/repo-utils.d.ts.map +1 -0
  122. package/ecagent/dist/harness/session/repo-utils.js +39 -0
  123. package/ecagent/dist/harness/session/repo-utils.js.map +1 -0
  124. package/ecagent/dist/harness/session/session.d.ts +47 -0
  125. package/ecagent/dist/harness/session/session.d.ts.map +1 -0
  126. package/ecagent/dist/harness/session/session.js +245 -0
  127. package/ecagent/dist/harness/session/session.js.map +1 -0
  128. package/ecagent/dist/harness/session/uuid.d.ts +2 -0
  129. package/ecagent/dist/harness/session/uuid.d.ts.map +1 -0
  130. package/ecagent/dist/harness/session/uuid.js +50 -0
  131. package/ecagent/dist/harness/session/uuid.js.map +1 -0
  132. package/ecagent/dist/harness/skills.d.ts +44 -0
  133. package/ecagent/dist/harness/skills.d.ts.map +1 -0
  134. package/ecagent/dist/harness/skills.js +311 -0
  135. package/ecagent/dist/harness/skills.js.map +1 -0
  136. package/ecagent/dist/harness/system-prompt.d.ts +3 -0
  137. package/ecagent/dist/harness/system-prompt.d.ts.map +1 -0
  138. package/ecagent/dist/harness/system-prompt.js +30 -0
  139. package/ecagent/dist/harness/system-prompt.js.map +1 -0
  140. package/ecagent/dist/harness/types.d.ts +619 -0
  141. package/ecagent/dist/harness/types.d.ts.map +1 -0
  142. package/ecagent/dist/harness/types.js +81 -0
  143. package/ecagent/dist/harness/types.js.map +1 -0
  144. package/ecagent/dist/harness/utils/shell-output.d.ts +14 -0
  145. package/ecagent/dist/harness/utils/shell-output.d.ts.map +1 -0
  146. package/ecagent/dist/harness/utils/shell-output.js +126 -0
  147. package/ecagent/dist/harness/utils/shell-output.js.map +1 -0
  148. package/ecagent/dist/harness/utils/truncate.d.ts +70 -0
  149. package/ecagent/dist/harness/utils/truncate.d.ts.map +1 -0
  150. package/ecagent/dist/harness/utils/truncate.js +290 -0
  151. package/ecagent/dist/harness/utils/truncate.js.map +1 -0
  152. package/ecagent/dist/index.d.ts +22 -0
  153. package/ecagent/dist/index.d.ts.map +1 -0
  154. package/ecagent/dist/index.js +27 -0
  155. package/ecagent/dist/index.js.map +1 -0
  156. package/ecagent/dist/node.d.ts +3 -0
  157. package/ecagent/dist/node.d.ts.map +1 -0
  158. package/ecagent/dist/node.js +3 -0
  159. package/ecagent/dist/node.js.map +1 -0
  160. package/ecagent/dist/proxy.d.ts +69 -0
  161. package/ecagent/dist/proxy.d.ts.map +1 -0
  162. package/ecagent/dist/proxy.js +278 -0
  163. package/ecagent/dist/proxy.js.map +1 -0
  164. package/ecagent/dist/runtime/api/lazy.d.ts +15 -0
  165. package/ecagent/dist/runtime/api/lazy.d.ts.map +1 -0
  166. package/ecagent/dist/runtime/api/lazy.js +59 -0
  167. package/ecagent/dist/runtime/api/lazy.js.map +1 -0
  168. package/ecagent/dist/runtime/api/pi-messages.d.ts +97 -0
  169. package/ecagent/dist/runtime/api/pi-messages.d.ts.map +1 -0
  170. package/ecagent/dist/runtime/api/pi-messages.js +307 -0
  171. package/ecagent/dist/runtime/api/pi-messages.js.map +1 -0
  172. package/ecagent/dist/runtime/api/pi-messages.lazy.d.ts +3 -0
  173. package/ecagent/dist/runtime/api/pi-messages.lazy.d.ts.map +1 -0
  174. package/ecagent/dist/runtime/api/pi-messages.lazy.js +3 -0
  175. package/ecagent/dist/runtime/api/pi-messages.lazy.js.map +1 -0
  176. package/ecagent/dist/runtime/auth/context.d.ts +7 -0
  177. package/ecagent/dist/runtime/auth/context.d.ts.map +1 -0
  178. package/ecagent/dist/runtime/auth/context.js +42 -0
  179. package/ecagent/dist/runtime/auth/context.js.map +1 -0
  180. package/ecagent/dist/runtime/auth/credential-store.d.ts +16 -0
  181. package/ecagent/dist/runtime/auth/credential-store.d.ts.map +1 -0
  182. package/ecagent/dist/runtime/auth/credential-store.js +39 -0
  183. package/ecagent/dist/runtime/auth/credential-store.js.map +1 -0
  184. package/ecagent/dist/runtime/auth/helpers.d.ts +20 -0
  185. package/ecagent/dist/runtime/auth/helpers.d.ts.map +1 -0
  186. package/ecagent/dist/runtime/auth/helpers.js +46 -0
  187. package/ecagent/dist/runtime/auth/helpers.js.map +1 -0
  188. package/ecagent/dist/runtime/auth/resolve.d.ts +26 -0
  189. package/ecagent/dist/runtime/auth/resolve.d.ts.map +1 -0
  190. package/ecagent/dist/runtime/auth/resolve.js +100 -0
  191. package/ecagent/dist/runtime/auth/resolve.js.map +1 -0
  192. package/ecagent/dist/runtime/auth/types.d.ts +180 -0
  193. package/ecagent/dist/runtime/auth/types.d.ts.map +1 -0
  194. package/ecagent/dist/runtime/auth/types.js +2 -0
  195. package/ecagent/dist/runtime/auth/types.js.map +1 -0
  196. package/ecagent/dist/runtime/compat.d.ts +28 -0
  197. package/ecagent/dist/runtime/compat.d.ts.map +1 -0
  198. package/ecagent/dist/runtime/compat.js +86 -0
  199. package/ecagent/dist/runtime/compat.js.map +1 -0
  200. package/ecagent/dist/runtime/index.d.ts +20 -0
  201. package/ecagent/dist/runtime/index.d.ts.map +1 -0
  202. package/ecagent/dist/runtime/index.js +17 -0
  203. package/ecagent/dist/runtime/index.js.map +1 -0
  204. package/ecagent/dist/runtime/models.d.ts +142 -0
  205. package/ecagent/dist/runtime/models.d.ts.map +1 -0
  206. package/ecagent/dist/runtime/models.js +245 -0
  207. package/ecagent/dist/runtime/models.js.map +1 -0
  208. package/ecagent/dist/runtime/oauth.d.ts +2 -0
  209. package/ecagent/dist/runtime/oauth.d.ts.map +1 -0
  210. package/ecagent/dist/runtime/oauth.js +2 -0
  211. package/ecagent/dist/runtime/oauth.js.map +1 -0
  212. package/ecagent/dist/runtime/providers/faux.d.ts +97 -0
  213. package/ecagent/dist/runtime/providers/faux.d.ts.map +1 -0
  214. package/ecagent/dist/runtime/providers/faux.js +395 -0
  215. package/ecagent/dist/runtime/providers/faux.js.map +1 -0
  216. package/ecagent/dist/runtime/types.d.ts +607 -0
  217. package/ecagent/dist/runtime/types.d.ts.map +1 -0
  218. package/ecagent/dist/runtime/types.js +2 -0
  219. package/ecagent/dist/runtime/types.js.map +1 -0
  220. package/ecagent/dist/runtime/utils/diagnostics.d.ts +19 -0
  221. package/ecagent/dist/runtime/utils/diagnostics.d.ts.map +1 -0
  222. package/ecagent/dist/runtime/utils/diagnostics.js +25 -0
  223. package/ecagent/dist/runtime/utils/diagnostics.js.map +1 -0
  224. package/ecagent/dist/runtime/utils/event-stream.d.ts +21 -0
  225. package/ecagent/dist/runtime/utils/event-stream.d.ts.map +1 -0
  226. package/ecagent/dist/runtime/utils/event-stream.js +77 -0
  227. package/ecagent/dist/runtime/utils/event-stream.js.map +1 -0
  228. package/ecagent/dist/runtime/utils/headers.d.ts +4 -0
  229. package/ecagent/dist/runtime/utils/headers.d.ts.map +1 -0
  230. package/ecagent/dist/runtime/utils/headers.js +18 -0
  231. package/ecagent/dist/runtime/utils/headers.js.map +1 -0
  232. package/ecagent/dist/runtime/utils/json-parse.d.ts +16 -0
  233. package/ecagent/dist/runtime/utils/json-parse.d.ts.map +1 -0
  234. package/ecagent/dist/runtime/utils/json-parse.js +113 -0
  235. package/ecagent/dist/runtime/utils/json-parse.js.map +1 -0
  236. package/ecagent/dist/runtime/utils/oauth/types.d.ts +64 -0
  237. package/ecagent/dist/runtime/utils/oauth/types.d.ts.map +1 -0
  238. package/ecagent/dist/runtime/utils/oauth/types.js +2 -0
  239. package/ecagent/dist/runtime/utils/oauth/types.js.map +1 -0
  240. package/ecagent/dist/runtime/utils/overflow.d.ts +58 -0
  241. package/ecagent/dist/runtime/utils/overflow.d.ts.map +1 -0
  242. package/ecagent/dist/runtime/utils/overflow.js +158 -0
  243. package/ecagent/dist/runtime/utils/overflow.js.map +1 -0
  244. package/ecagent/dist/runtime/utils/provider-env.d.ts +7 -0
  245. package/ecagent/dist/runtime/utils/provider-env.d.ts.map +1 -0
  246. package/ecagent/dist/runtime/utils/provider-env.js +44 -0
  247. package/ecagent/dist/runtime/utils/provider-env.js.map +1 -0
  248. package/ecagent/dist/runtime/utils/retry.d.ts +12 -0
  249. package/ecagent/dist/runtime/utils/retry.d.ts.map +1 -0
  250. package/ecagent/dist/runtime/utils/retry.js +90 -0
  251. package/ecagent/dist/runtime/utils/retry.js.map +1 -0
  252. package/ecagent/dist/runtime/utils/typebox-helpers.d.ts +17 -0
  253. package/ecagent/dist/runtime/utils/typebox-helpers.d.ts.map +1 -0
  254. package/ecagent/dist/runtime/utils/typebox-helpers.js +21 -0
  255. package/ecagent/dist/runtime/utils/typebox-helpers.js.map +1 -0
  256. package/ecagent/dist/runtime/utils/validation.d.ts +18 -0
  257. package/ecagent/dist/runtime/utils/validation.d.ts.map +1 -0
  258. package/ecagent/dist/runtime/utils/validation.js +269 -0
  259. package/ecagent/dist/runtime/utils/validation.js.map +1 -0
  260. package/ecagent/dist/types.d.ts +401 -0
  261. package/ecagent/dist/types.d.ts.map +1 -0
  262. package/ecagent/dist/types.js +2 -0
  263. package/ecagent/dist/types.js.map +1 -0
  264. package/ecagent/package.json +93 -0
  265. package/ecagent/src/agent-loop.ts +792 -0
  266. package/ecagent/src/agent.ts +575 -0
  267. package/ecagent/src/harness/agent-harness.ts +1029 -0
  268. package/ecagent/src/harness/compaction/branch-summarization.ts +261 -0
  269. package/ecagent/src/harness/compaction/compaction.ts +753 -0
  270. package/ecagent/src/harness/compaction/utils.ts +144 -0
  271. package/ecagent/src/harness/env/nodejs.ts +569 -0
  272. package/ecagent/src/harness/messages.ts +164 -0
  273. package/ecagent/src/harness/prompt-templates.ts +267 -0
  274. package/ecagent/src/harness/session/jsonl-repo.ts +179 -0
  275. package/ecagent/src/harness/session/jsonl-storage.ts +314 -0
  276. package/ecagent/src/harness/session/memory-repo.ts +50 -0
  277. package/ecagent/src/harness/session/memory-storage.ts +133 -0
  278. package/ecagent/src/harness/session/repo-utils.ts +51 -0
  279. package/ecagent/src/harness/session/session.ts +338 -0
  280. package/ecagent/src/harness/session/uuid.ts +54 -0
  281. package/ecagent/src/harness/skills.ts +375 -0
  282. package/ecagent/src/harness/system-prompt.ts +34 -0
  283. package/ecagent/src/harness/types.ts +838 -0
  284. package/ecagent/src/harness/utils/shell-output.ts +135 -0
  285. package/ecagent/src/harness/utils/truncate.ts +344 -0
  286. package/ecagent/src/index.ts +46 -0
  287. package/ecagent/src/node.ts +2 -0
  288. package/ecagent/src/proxy.ts +367 -0
  289. package/ecagent/src/runtime/api/lazy.ts +70 -0
  290. package/ecagent/src/runtime/api/pi-messages.lazy.ts +4 -0
  291. package/ecagent/src/runtime/api/pi-messages.ts +436 -0
  292. package/ecagent/src/runtime/auth/context.ts +45 -0
  293. package/ecagent/src/runtime/auth/credential-store.ts +47 -0
  294. package/ecagent/src/runtime/auth/helpers.ts +46 -0
  295. package/ecagent/src/runtime/auth/resolve.ts +141 -0
  296. package/ecagent/src/runtime/auth/types.ts +182 -0
  297. package/ecagent/src/runtime/compat.ts +158 -0
  298. package/ecagent/src/runtime/index.ts +31 -0
  299. package/ecagent/src/runtime/models.ts +452 -0
  300. package/ecagent/src/runtime/oauth.ts +1 -0
  301. package/ecagent/src/runtime/providers/faux.ts +538 -0
  302. package/ecagent/src/runtime/types.ts +718 -0
  303. package/ecagent/src/runtime/utils/diagnostics.ts +45 -0
  304. package/ecagent/src/runtime/utils/event-stream.ts +88 -0
  305. package/ecagent/src/runtime/utils/headers.ts +18 -0
  306. package/ecagent/src/runtime/utils/json-parse.ts +124 -0
  307. package/ecagent/src/runtime/utils/oauth/types.ts +79 -0
  308. package/ecagent/src/runtime/utils/overflow.ts +165 -0
  309. package/ecagent/src/runtime/utils/provider-env.ts +52 -0
  310. package/ecagent/src/runtime/utils/retry.ts +101 -0
  311. package/ecagent/src/runtime/utils/typebox-helpers.ts +24 -0
  312. package/ecagent/src/runtime/utils/validation.ts +310 -0
  313. package/ecagent/src/types.ts +430 -0
  314. package/ecagent/tsconfig.build.json +32 -0
  315. package/kits/schemas/_meta.json +7 -4
  316. package/kits/schemas/agent-config.schema.10.json +370 -0
  317. package/kits/schemas/defaults.schema.4.json +93 -0
  318. package/kits/schemas/relation-config.schema.8.json +69 -0
  319. package/kits/templates/roles/admin.json +1 -1
  320. package/package.json +22 -6
  321. package/skills/eclink/SKILL.md +70 -0
  322. package/skills/eclink/agents/openai.yaml +4 -0
  323. package/assets/.env.template +0 -4
@@ -20,22 +20,98 @@ function resolveAunSelfAID(selfAID, channelKey) {
20
20
  return inferred;
21
21
  throw new Error('[SessionFsStore] AUN chat directory requires selfAID or an aun channel key.');
22
22
  }
23
+ function resolveSessionChannelKey(channelType, selfAID, channelKey) {
24
+ if (channelKey?.trim())
25
+ return channelKey.trim();
26
+ return `${channelType}#${selfAID}#main`;
27
+ }
28
+ function encodeChannelSessionComponent(value) {
29
+ // encodeURIComponent leaves `*` untouched, but it is illegal in a Windows
30
+ // filename. A trailing dot is also rejected by Windows filesystems.
31
+ const encoded = encodeURIComponent(value).replace(/\*/g, '%2A');
32
+ return encoded.endsWith('.') ? `${encoded.slice(0, -1)}%2E` : encoded;
33
+ }
34
+ const MAX_CHANNEL_SESSION_KEY_BYTES = 255;
35
+ function formatChannelSessionKeyParts(channelType, channelName, channelId) {
36
+ if (!channelType || !channelName || !channelId) {
37
+ throw new Error('[SessionFsStore] channel session key components must not be empty.');
38
+ }
39
+ const key = [channelType, channelName, channelId]
40
+ .map(encodeChannelSessionComponent)
41
+ .join('#');
42
+ const bytes = Buffer.byteLength(key, 'utf8');
43
+ if (bytes > MAX_CHANNEL_SESSION_KEY_BYTES) {
44
+ throw new Error(`[SessionFsStore] channel session key is ${bytes} bytes; filesystem limit is ${MAX_CHANNEL_SESSION_KEY_BYTES} bytes.`);
45
+ }
46
+ return key;
47
+ }
23
48
  /**
24
- * 计算 chat 目录的完整路径。
25
- * - aun: sessionsDir/aun/<urlEncode(selfAID)>/<urlEncode(channelId)>/
26
- * - wecom: sessionsDir/wecom/<urlEncode(channelKey)>/<urlEncode(channelId)>/
27
- * - 其它: sessionsDir/<channelType>/<urlEncode(channelId)>/
49
+ * Session 的磁盘路由键。它将实例名和会话路由 ID 合并为一个目录段;
50
+ * Agent 身份由父目录 `agents/<aid>/` 提供,不能省略这个父目录。
51
+ *
52
+ * This is intentionally distinct from SessionFile.sessionKey, which includes
53
+ * the thread ID and is used as the agent-internal session routing key.
28
54
  */
29
- export function chatDirPath(sessionsDir, channelType, channelId, selfAID, channelKey) {
30
- if (channelType === 'aun') {
31
- return path.join(sessionsDir, channelType, encodeSegment(resolveAunSelfAID(selfAID, channelKey)), encodeSegment(channelId));
55
+ export function formatChannelSessionKey(channelType, channelId, channelKey) {
56
+ const parts = channelKey?.split('#');
57
+ const channelName = parts?.length === 3 && parts[0] === channelType && parts[2]?.trim()
58
+ ? parts[2].trim()
59
+ : 'main';
60
+ return formatChannelSessionKeyParts(channelType, channelName, channelId);
61
+ }
62
+ function parseChannelSessionKeyComponents(key) {
63
+ const parts = key.split('#');
64
+ if (parts.length !== 3 || !parts[0] || !parts[1] || !parts[2])
65
+ return undefined;
66
+ try {
67
+ const channelType = decodeURIComponent(parts[0]);
68
+ const channelName = decodeURIComponent(parts[1]);
69
+ const channelId = decodeURIComponent(parts[2]);
70
+ if (!channelType || !channelName || !channelId)
71
+ return undefined;
72
+ return { channelType, channelName, channelId };
32
73
  }
33
- if (channelType === 'wecom' && channelKey?.startsWith('wecom#')) {
34
- return path.join(sessionsDir, channelType, encodeSegment(channelKey), encodeSegment(channelId));
74
+ catch {
75
+ return undefined;
35
76
  }
36
- return path.join(sessionsDir, channelType, encodeSegment(channelId));
37
77
  }
38
- /** 解码目录段(用于扫描时把目录名还原为原始 channelId/selfAID) */
78
+ /** Parse only the canonical channelSessionKey emitted by the current formatter. */
79
+ export function tryParseChannelSessionKey(key) {
80
+ const parsed = parseChannelSessionKeyComponents(key);
81
+ if (!parsed)
82
+ return undefined;
83
+ try {
84
+ return formatChannelSessionKeyParts(parsed.channelType, parsed.channelName, parsed.channelId) === key
85
+ ? parsed
86
+ : undefined;
87
+ }
88
+ catch {
89
+ return undefined;
90
+ }
91
+ }
92
+ /** Parse the immediately preceding v3 encoding for explicit migration only. */
93
+ export function tryParseLegacyChannelSessionKey(key) {
94
+ return parseChannelSessionKeyComponents(key);
95
+ }
96
+ /**
97
+ * 计算 chat 目录的完整路径。
98
+ * - agents/<selfAID>/sessions/<channelSessionKey>/
99
+ *
100
+ * The relation peerKey intentionally is not used here: it omits the channel
101
+ * instance. channelSessionKey retains the configured instance name and the
102
+ * chat route in one path segment.
103
+ */
104
+ export function chatDirPath(sessionsRoot, channelType, channelId, selfAID, channelKey) {
105
+ const aid = channelType === 'aun'
106
+ ? resolveAunSelfAID(selfAID, channelKey)
107
+ : (selfAID?.trim() || trySelfAIDFromChannelKey(channelKey) || '_unknown');
108
+ return path.join(sessionsRoot, encodeSegment(aid), 'sessions', formatChannelSessionKey(channelType, channelId, resolveSessionChannelKey(channelType, aid, channelKey)));
109
+ }
110
+ function trySelfAIDFromChannelKey(channelKey) {
111
+ const parts = channelKey?.split('#');
112
+ return parts?.length === 3 && parts[1]?.trim() ? parts[1].trim() : undefined;
113
+ }
114
+ /** 解码目录段(用于扫描时把目录名还原为逻辑路径键) */
39
115
  export function decodeDirSegment(seg) {
40
116
  return decodeSegment(seg);
41
117
  }
@@ -143,22 +219,193 @@ export function readAllJsonlLines(filePath) {
143
219
  }
144
220
  }
145
221
  /**
146
- * 扫描所有 chat 目录。
147
- * - aun: channelType / selfAID / channelId (3层)
148
- * - wecom: channelType / channelKey / channelId (3层;兼容旧的2层目录)
149
- * - 其它: channelType / channelId (2层)
222
+ * Scan the current per-Agent layout:
223
+ * `agents/<aid>/sessions/<channelSessionKey>/`.
150
224
  */
151
- export function scanChatDirs(sessionsDir) {
225
+ export function scanChatDirs(sessionsRoot) {
152
226
  const results = [];
153
- let typeEntries;
227
+ let agentEntries;
154
228
  try {
155
- typeEntries = fs.readdirSync(sessionsDir, { withFileTypes: true });
229
+ agentEntries = fs.readdirSync(sessionsRoot, { withFileTypes: true });
156
230
  }
157
231
  catch (e) {
158
232
  if (e.code === 'ENOENT')
159
233
  return [];
160
234
  throw e;
161
235
  }
236
+ const hasAgentLayout = agentEntries.some(entry => entry.isDirectory() && (fs.existsSync(path.join(sessionsRoot, entry.name, 'config.json'))
237
+ || fs.existsSync(path.join(sessionsRoot, entry.name, 'sessions'))));
238
+ if (!hasAgentLayout)
239
+ return [];
240
+ for (const agentEntry of agentEntries) {
241
+ if (!agentEntry.isDirectory())
242
+ continue;
243
+ const sessionsDir = path.join(sessionsRoot, agentEntry.name, 'sessions');
244
+ let sessionEntries;
245
+ try {
246
+ sessionEntries = fs.readdirSync(sessionsDir, { withFileTypes: true });
247
+ }
248
+ catch {
249
+ continue;
250
+ }
251
+ const selfAID = decodeSegment(agentEntry.name);
252
+ for (const sessionEntry of sessionEntries) {
253
+ if (!sessionEntry.isDirectory())
254
+ continue;
255
+ const channelSessionKey = sessionEntry.name;
256
+ const parsed = tryParseChannelSessionKey(channelSessionKey);
257
+ if (!parsed)
258
+ continue;
259
+ const dirPath = path.join(sessionsDir, sessionEntry.name);
260
+ if (!hasSessionPayload(dirPath))
261
+ continue;
262
+ results.push({
263
+ channelType: parsed.channelType,
264
+ selfAID,
265
+ channelId: parsed.channelId,
266
+ channelKey: parsed.channelType === 'daemon'
267
+ ? 'daemon'
268
+ : `${parsed.channelType}#${selfAID}#${parsed.channelName}`,
269
+ channelSessionKey,
270
+ dirPath,
271
+ });
272
+ }
273
+ }
274
+ return results.sort((left, right) => left.dirPath < right.dirPath ? -1 : left.dirPath > right.dirPath ? 1 : 0);
275
+ }
276
+ function hasSessionPayload(dirPath) {
277
+ try {
278
+ return fs.readdirSync(dirPath, { withFileTypes: true }).some(entry => (entry.isFile() && (entry.name === 'active.json'
279
+ || entry.name === 'messages.jsonl'
280
+ || (entry.name.startsWith('meta_') && entry.name.endsWith('.jsonl')))) || (entry.isDirectory() && entry.name === '_threads'));
281
+ }
282
+ catch {
283
+ return false;
284
+ }
285
+ }
286
+ /**
287
+ * Scan non-canonical single-level v3 keys. The runtime ignores these entries;
288
+ * only the explicit migration planner may copy them into their canonical key.
289
+ */
290
+ export function scanNonCanonicalAgentChatDirs(sessionsRoot) {
291
+ const results = [];
292
+ let agentEntries;
293
+ try {
294
+ agentEntries = fs.readdirSync(sessionsRoot, { withFileTypes: true });
295
+ }
296
+ catch {
297
+ return results;
298
+ }
299
+ for (const agentEntry of agentEntries) {
300
+ if (!agentEntry.isDirectory())
301
+ continue;
302
+ const selfAID = decodeSegment(agentEntry.name);
303
+ const sessionsDir = path.join(sessionsRoot, agentEntry.name, 'sessions');
304
+ let sessionEntries;
305
+ try {
306
+ sessionEntries = fs.readdirSync(sessionsDir, { withFileTypes: true });
307
+ }
308
+ catch {
309
+ continue;
310
+ }
311
+ for (const sessionEntry of sessionEntries) {
312
+ if (!sessionEntry.isDirectory() || tryParseChannelSessionKey(sessionEntry.name))
313
+ continue;
314
+ const parsed = tryParseLegacyChannelSessionKey(sessionEntry.name);
315
+ const dirPath = path.join(sessionsDir, sessionEntry.name);
316
+ if (!parsed || !hasSessionPayload(dirPath))
317
+ continue;
318
+ results.push({
319
+ ...parsed,
320
+ selfAID,
321
+ channelKey: parsed.channelType === 'daemon'
322
+ ? 'daemon'
323
+ : `${parsed.channelType}#${selfAID}#${parsed.channelName}`,
324
+ channelSessionKey: sessionEntry.name,
325
+ dirPath,
326
+ });
327
+ }
328
+ }
329
+ return results.sort((left, right) => left.dirPath < right.dirPath ? -1 : left.dirPath > right.dirPath ? 1 : 0);
330
+ }
331
+ /**
332
+ * Scan the immediately preceding per-Agent layout:
333
+ * `agents/<aid>/sessions/<channelKey>/<channelId>/`.
334
+ *
335
+ * The runtime must not use this scanner. It exists solely for the explicit
336
+ * migration that copies the old two-level layout into channelSessionKey.
337
+ */
338
+ export function scanNestedAgentChatDirs(sessionsRoot) {
339
+ const results = [];
340
+ let agentEntries;
341
+ try {
342
+ agentEntries = fs.readdirSync(sessionsRoot, { withFileTypes: true });
343
+ }
344
+ catch {
345
+ return results;
346
+ }
347
+ for (const agentEntry of agentEntries) {
348
+ if (!agentEntry.isDirectory())
349
+ continue;
350
+ const selfAID = decodeSegment(agentEntry.name);
351
+ const sessionsDir = path.join(sessionsRoot, agentEntry.name, 'sessions');
352
+ let channelEntries;
353
+ try {
354
+ channelEntries = fs.readdirSync(sessionsDir, { withFileTypes: true });
355
+ }
356
+ catch {
357
+ continue;
358
+ }
359
+ for (const channelEntry of channelEntries) {
360
+ if (!channelEntry.isDirectory())
361
+ continue;
362
+ const channelKey = decodeSegment(channelEntry.name);
363
+ const parts = channelKey.split('#');
364
+ const isDaemon = channelKey === 'daemon';
365
+ const isCurrentKey = parts.length === 3 && parts[1] === selfAID && !!parts[0] && !!parts[2];
366
+ const isReversedLegacyKey = parts.length === 3 && parts[0] === selfAID && !!parts[1] && !!parts[2];
367
+ const isAgentChannel = isCurrentKey || isReversedLegacyKey;
368
+ if (!isDaemon && !isAgentChannel)
369
+ continue;
370
+ const channelDir = path.join(sessionsDir, channelEntry.name);
371
+ // A single-level channelSessionKey can look superficially like a legacy
372
+ // key when an AID happens to match an instance name. Its own active.json
373
+ // proves it is a current chat directory, not a legacy channel parent.
374
+ if (fs.existsSync(path.join(channelDir, 'active.json')))
375
+ continue;
376
+ const channelType = isDaemon ? 'daemon' : (isCurrentKey ? parts[0] : parts[1]);
377
+ let chatEntries;
378
+ try {
379
+ chatEntries = fs.readdirSync(channelDir, { withFileTypes: true });
380
+ }
381
+ catch {
382
+ continue;
383
+ }
384
+ for (const chatEntry of chatEntries) {
385
+ if (!chatEntry.isDirectory())
386
+ continue;
387
+ results.push({
388
+ channelType,
389
+ selfAID,
390
+ channelKey,
391
+ channelId: decodeSegment(chatEntry.name),
392
+ dirPath: path.join(channelDir, chatEntry.name),
393
+ });
394
+ }
395
+ }
396
+ }
397
+ return results.sort((left, right) => left.dirPath < right.dirPath ? -1 : left.dirPath > right.dirPath ? 1 : 0);
398
+ }
399
+ /** Scan the pre-migration data/sessions layout without changing it. */
400
+ export function scanLegacyChatDirs(sessionsDir) {
401
+ const results = [];
402
+ let typeEntries;
403
+ try {
404
+ typeEntries = fs.readdirSync(sessionsDir, { withFileTypes: true });
405
+ }
406
+ catch {
407
+ return results;
408
+ }
162
409
  for (const typeEntry of typeEntries) {
163
410
  if (!typeEntry.isDirectory())
164
411
  continue;
@@ -172,7 +419,6 @@ export function scanChatDirs(sessionsDir) {
172
419
  continue;
173
420
  }
174
421
  if (channelType === 'aun') {
175
- // 3-layer: aun/selfAID/channelId
176
422
  for (const selfEntry of level2Entries) {
177
423
  if (!selfEntry.isDirectory())
178
424
  continue;
@@ -184,19 +430,15 @@ export function scanChatDirs(sessionsDir) {
184
430
  catch {
185
431
  continue;
186
432
  }
187
- for (const chatEntry of chatEntries) {
188
- if (!chatEntry.isDirectory())
189
- continue;
190
- results.push({
191
- channelType,
192
- selfAID: decodeSegment(selfEntry.name),
193
- channelId: decodeSegment(chatEntry.name),
194
- dirPath: path.join(selfDir, chatEntry.name),
195
- });
196
- }
433
+ for (const chatEntry of chatEntries)
434
+ if (chatEntry.isDirectory())
435
+ results.push({
436
+ channelType, selfAID: decodeSegment(selfEntry.name), channelId: decodeSegment(chatEntry.name), dirPath: path.join(selfDir, chatEntry.name),
437
+ });
197
438
  }
439
+ continue;
198
440
  }
199
- else if (channelType === 'wecom') {
441
+ if (channelType === 'wecom') {
200
442
  for (const entry of level2Entries) {
201
443
  if (!entry.isDirectory())
202
444
  continue;
@@ -210,43 +452,25 @@ export function scanChatDirs(sessionsDir) {
210
452
  catch {
211
453
  continue;
212
454
  }
213
- for (const chatEntry of chatEntries) {
214
- if (!chatEntry.isDirectory())
215
- continue;
216
- results.push({
217
- channelType,
218
- selfAID: '',
219
- channelKey: decoded,
220
- channelId: decodeSegment(chatEntry.name),
221
- dirPath: path.join(entryDir, chatEntry.name),
222
- });
223
- }
455
+ for (const chatEntry of chatEntries)
456
+ if (chatEntry.isDirectory())
457
+ results.push({
458
+ channelType, selfAID: '', channelKey: decoded, channelId: decodeSegment(chatEntry.name), dirPath: path.join(entryDir, chatEntry.name),
459
+ });
224
460
  }
225
461
  else {
226
- results.push({
227
- channelType,
228
- selfAID: '',
229
- channelId: decoded,
230
- dirPath: entryDir,
231
- });
462
+ results.push({ channelType, selfAID: '', channelId: decoded, dirPath: entryDir });
232
463
  }
233
464
  }
465
+ continue;
234
466
  }
235
- else {
236
- // 2-layer: channelType/channelId
237
- for (const chatEntry of level2Entries) {
238
- if (!chatEntry.isDirectory())
239
- continue;
467
+ for (const chatEntry of level2Entries)
468
+ if (chatEntry.isDirectory())
240
469
  results.push({
241
- channelType,
242
- selfAID: '',
243
- channelId: decodeSegment(chatEntry.name),
244
- dirPath: path.join(typeDir, chatEntry.name),
470
+ channelType, selfAID: '', channelId: decodeSegment(chatEntry.name), dirPath: path.join(typeDir, chatEntry.name),
245
471
  });
246
- }
247
- }
248
472
  }
249
- return results;
473
+ return results.sort((left, right) => left.dirPath < right.dirPath ? -1 : left.dirPath > right.dirPath ? 1 : 0);
250
474
  }
251
475
  export function scanMetaFiles(chatDir) {
252
476
  try {
@@ -38,9 +38,6 @@ export class SessionManager {
38
38
  this.sessionsDir = sessionsDir;
39
39
  this.eventBus = eventBus;
40
40
  this.identityResolver = identityResolver;
41
- this.migrateChannelKeyFormat();
42
- this.migrateUnknownAunDirs();
43
- this.migrateWecomChannelDirs();
44
41
  }
45
42
  setIdentityResolver(resolver) {
46
43
  this.identityResolver = resolver;
@@ -379,7 +376,7 @@ export class SessionManager {
379
376
  logger.error(`[SessionManager] validateSessionFile: no adapter for baseagent=${session.baseagent}, session=${session.id}`);
380
377
  return agentSessionId;
381
378
  }
382
- if (adapter.checkExists(session.projectPath, agentSessionId))
379
+ if (adapter.checkExists(session.projectPath, agentSessionId, session.selfAID))
383
380
  return agentSessionId;
384
381
  logger.warn(`Session file not found for ${session.baseagent}: ${agentSessionId}, clearing session ID`);
385
382
  session.agentSessionId = undefined;
@@ -1316,16 +1313,16 @@ export class SessionManager {
1316
1313
  });
1317
1314
  return session;
1318
1315
  }
1319
- async scanCliSessions(projectPath, baseagent, limit = 10) {
1316
+ async scanCliSessions(projectPath, baseagent, limit = 10, agentAid) {
1320
1317
  const adapter = this.getFileAdapter(baseagent);
1321
1318
  if (!adapter)
1322
1319
  return [];
1323
- return adapter.scanCliSessions(projectPath, limit);
1320
+ return adapter.scanCliSessions(projectPath, limit, agentAid);
1324
1321
  }
1325
- async listImportableCliSessions(projectPath, baseagent) {
1322
+ async listImportableCliSessions(projectPath, baseagent, agentAid) {
1326
1323
  const boundSessionIds = this.listBoundAgentSessionIds(projectPath, baseagent);
1327
1324
  const displayLimit = 10;
1328
- const cliSessions = await this.scanCliSessions(projectPath, baseagent, boundSessionIds.size + displayLimit);
1325
+ const cliSessions = await this.scanCliSessions(projectPath, baseagent, boundSessionIds.size + displayLimit, agentAid);
1329
1326
  return cliSessions
1330
1327
  .filter(session => !boundSessionIds.has(session.uuid))
1331
1328
  .slice(0, displayLimit);
@@ -1344,25 +1341,25 @@ export class SessionManager {
1344
1341
  }
1345
1342
  return sessionIds;
1346
1343
  }
1347
- checkSessionFileExists(projectPath, agentSessionId, baseagent) {
1344
+ checkSessionFileExists(projectPath, agentSessionId, baseagent, agentAid) {
1348
1345
  const adapter = this.getFileAdapter(baseagent);
1349
1346
  if (!adapter)
1350
1347
  return false;
1351
- return adapter.checkExists(projectPath, agentSessionId);
1348
+ return adapter.checkExists(projectPath, agentSessionId, agentAid);
1352
1349
  }
1353
- readSessionFirstMessage(projectPath, agentSessionId, baseagent) {
1350
+ readSessionFirstMessage(projectPath, agentSessionId, baseagent, agentAid) {
1354
1351
  const adapter = this.getFileAdapter(baseagent);
1355
1352
  if (!adapter)
1356
1353
  return null;
1357
- return adapter.readFirstMessage(projectPath, agentSessionId);
1354
+ return adapter.readFirstMessage(projectPath, agentSessionId, agentAid);
1358
1355
  }
1359
- readSessionLastUserMessage(projectPath, agentSessionId, baseagent) {
1356
+ readSessionLastUserMessage(projectPath, agentSessionId, baseagent, agentAid) {
1360
1357
  const adapter = this.getFileAdapter(baseagent);
1361
1358
  if (!adapter)
1362
1359
  return null;
1363
- return adapter.readLastUserMessage(projectPath, agentSessionId);
1360
+ return adapter.readLastUserMessage(projectPath, agentSessionId, agentAid);
1364
1361
  }
1365
- getSessionFileInfo(projectPath, agentSessionId, baseagent) {
1362
+ getSessionFileInfo(projectPath, agentSessionId, baseagent, agentAid) {
1366
1363
  if (!baseagent) {
1367
1364
  logger.error(`[SessionManager] getSessionFileInfo: baseagent is empty, returning turns=0. agentSessionId=${agentSessionId}`);
1368
1365
  return { turns: 0 };
@@ -1372,13 +1369,13 @@ export class SessionManager {
1372
1369
  logger.error(`[SessionManager] getSessionFileInfo: no adapter for baseagent=${baseagent}, returning turns=0`);
1373
1370
  return { turns: 0 };
1374
1371
  }
1375
- return adapter.getFileInfo(projectPath, agentSessionId);
1372
+ return adapter.getFileInfo(projectPath, agentSessionId, agentAid);
1376
1373
  }
1377
- async listSdkSessions(projectPath, baseagent) {
1374
+ async listSdkSessions(projectPath, baseagent, agentAid) {
1378
1375
  const adapter = this.getFileAdapter(baseagent);
1379
1376
  if (!adapter?.listSdkSessions)
1380
1377
  return [];
1381
- return adapter.listSdkSessions(projectPath);
1378
+ return adapter.listSdkSessions(projectPath, agentAid);
1382
1379
  }
1383
1380
  async getSessionByUuidPrefix(channel, channelId, uuidPrefix) {
1384
1381
  const chatDir = this.resolveChatDirSafe(channel, channelId);
@@ -1391,15 +1388,16 @@ export class SessionManager {
1391
1388
  }
1392
1389
  return matched[0];
1393
1390
  }
1394
- async importCliSession(channel, channelId, projectPath, agentSessionId, baseagent = 'claude') {
1391
+ async importCliSession(channel, channelId, projectPath, agentSessionId, baseagent = 'claude', agentAid) {
1395
1392
  const inheritedChatType = this.getActiveChatType(channel, channelId);
1396
- const fileInfo = this.getSessionFileInfo(projectPath, agentSessionId, baseagent);
1393
+ const resolvedAgentAid = agentAid || this.deriveChannelIdentity(channel, channelId).selfAID;
1394
+ const fileInfo = this.getSessionFileInfo(projectPath, agentSessionId, baseagent, resolvedAgentAid);
1397
1395
  const name = fileInfo.title || `CLI会话-${new Date().toLocaleString('zh-CN', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' })}`;
1398
1396
  // Derive selfAID and channelType from existing sessions
1399
1397
  const existingDir = this.findExistingChatDir(channel, channelId);
1400
1398
  const identity = this.deriveChannelIdentity(channel, channelId);
1401
1399
  let channelType = identity.channelType;
1402
- let selfAID = identity.selfAID;
1400
+ let selfAID = identity.selfAID || resolvedAgentAid || '';
1403
1401
  if (existingDir) {
1404
1402
  const active = readJsonFile(path.join(existingDir, 'active.json'));
1405
1403
  if (active) {
@@ -30,6 +30,7 @@ const BACKEND_SYNTHETIC_CLI_PROMPT_PREFIXES = {
30
30
  ],
31
31
  codex: [],
32
32
  gemini: [],
33
+ ecagent: [],
33
34
  };
34
35
  // Claude Code 斜杠命令 / skill / hook 注入的脚手架,经真实会话数据核对,形态包括:
35
36
  // - <local-command-caveat> / <local-command-stdout> / <local-command-stderr> 等 local-command 标签族
@@ -142,9 +142,16 @@ export class SessionTurnCoordinator {
142
142
  if (evidence.openToolUseIds.length > 0)
143
143
  return { ok: false, reason: 'tool_open' };
144
144
  const madeProgress = evidence.textCount > 0 || evidence.toolUseCount > 0 || !!evidence.resultText?.trim();
145
- if (!evidence.inputAccepted && !madeProgress)
145
+ // Not every runner can emit input_accepted. Token usage is equally strong
146
+ // evidence that the upstream model consumed the input, even for a silent result.
147
+ const inputConsumed = evidence.inputAccepted
148
+ || evidence.hasTokenUsage === true
149
+ || evidence.inputTokens > 0
150
+ || evidence.outputTokens > 0;
151
+ if (!inputConsumed && !madeProgress)
146
152
  return { ok: false, reason: 'input_not_consumed' };
147
153
  const emptySuccess = !evidence.explicitNoop
154
+ && !evidence.hasTokenUsage
148
155
  && evidence.inputTokens === 0
149
156
  && evidence.outputTokens === 0
150
157
  && !madeProgress;
@@ -14,6 +14,7 @@ export function isSystemControlChannel(channel) {
14
14
  export function isNonAgentSessionChannel(channel) {
15
15
  return channel === 'daemon' || isSystemControlChannel(channel);
16
16
  }
17
+ /** Explicit maintenance helper; startup never invokes it. */
17
18
  export function cleanupLegacySystemControlSessions(sessionsDir) {
18
19
  const removed = [];
19
20
  for (const channel of LEGACY_SYSTEM_CONTROL_CHANNELS) {
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // 【重要】最先加载环境变量,确保后续模块初始化时可用
2
2
  import dotenv from 'dotenv';
3
3
  import path from 'path';
4
- import { ensureDataDirs, resolvePaths, getPackageRoot, agentMdPath, eckDebugDir } from './paths.js';
4
+ import { ensureDataDirs, resolvePaths, getPackageRoot, agentMdPath, eckDebugDir, daemonControlDir, daemonQueuePath } from './paths.js';
5
5
  // 立即加载 .env 文件(在其他模块导入之前)
6
6
  try {
7
7
  dotenv.config({ path: path.join(resolvePaths().root, '.env') });
@@ -12,6 +12,7 @@ catch {
12
12
  import { ClaudeSessionFileAdapter } from './core/session/adapters/claude-session-file-adapter.js';
13
13
  import { CodexSessionFileAdapter } from './core/session/adapters/codex-session-file-adapter.js';
14
14
  import { GeminiSessionFileAdapter } from './core/session/adapters/gemini-session-file-adapter.js';
15
+ import { EcagentSessionFileAdapter } from './core/session/adapters/ecagent-session-file-adapter.js';
15
16
  import { loadDefaults, loadAllAgents, migrateIdentitiesIfNeeded, loadDaemonConfig, initializeEckSnapshotsConfig } from './config-store.js';
16
17
  import { ConfigTarget, initConfigManager, onConfigWrite } from './config/config-manager.js';
17
18
  import { ensureRoleConfigV4OnStartup } from './config/role-config-v4-startup.js';
@@ -30,6 +31,7 @@ import { SessionManager } from './core/session/session-manager.js';
30
31
  import { ClaudeAgentPlugin } from './agents/claude-runner.js';
31
32
  import { CodexAgentPlugin } from './agents/codex-runner.js';
32
33
  import { GeminiAgentPlugin } from './agents/gemini-runner.js';
34
+ import { EcagentAgentPlugin } from './agents/ecagent-runner.js';
33
35
  import { FeishuChannelPlugin, registerPendingFeishuContactBind } from './channels/feishu.js';
34
36
  import { WechatChannelPlugin, registerPendingWechatContactBind } from './channels/wechat.js';
35
37
  import { AUN_HANDOFF_MARKER_FIELD, AUNChannel, AUNChannelPlugin } from './channels/aun.js';
@@ -78,13 +80,13 @@ import { TriggerAuditLogger } from './trigger/audit.js';
78
80
  import { TriggerScriptExecutor } from './trigger/script-executor.js';
79
81
  import { TriggerFeedbackDispatcher } from './trigger/feedback.js';
80
82
  import { TriggerRuntimeScheduler } from './trigger/scheduler.js';
83
+ import { TargetSessionLockRegistry } from './trigger/session-lock.js';
81
84
  import { DaemonChannel } from './channels/daemon.js';
82
85
  import { definitionRevision, normalizeTriggerDefinition } from './trigger/validation.js';
83
86
  import { applyTriggerPatch } from './trigger/patch.js';
84
87
  import { validateModelSelectionForRole } from './core/model/model-permission.js';
85
88
  import { constrainRuntimePermissionMode, validateRuntimeStringFieldOverride, } from './core/role/runtime-policy.js';
86
89
  import { atomicWriteJson } from './core/session/session-fs-store.js';
87
- import { cleanupLegacySystemControlSessions } from './core/system-channels.js';
88
90
  import { appendMessageLog, buildOutboundEntry, classifyAunPayloadForLog } from './core/message/message-log.js';
89
91
  import fs from 'fs';
90
92
  import crypto from 'crypto';
@@ -844,21 +846,19 @@ async function main() {
844
846
  });
845
847
  return roleToSessionIdentity(detail.effectiveRole);
846
848
  };
847
- const removedLegacyControlSessions = cleanupLegacySystemControlSessions(paths.sessionsDir);
848
- if (removedLegacyControlSessions.length > 0) {
849
- logger.info(`[startup] Removed legacy synthetic control sessions: ${removedLegacyControlSessions.join(', ')}`);
850
- }
851
849
  const sessionManager = new SessionManager(paths.sessionsDir, eventBus, resolveSessionIdentity);
852
850
  logger.info('✓ Database initialized');
853
851
  // 注册会话文件适配器(Claude / Codex 各自的会话文件操作)
854
852
  sessionManager.registerFileAdapter(new ClaudeSessionFileAdapter());
855
853
  sessionManager.registerFileAdapter(new CodexSessionFileAdapter());
856
854
  sessionManager.registerFileAdapter(new GeminiSessionFileAdapter());
855
+ sessionManager.registerFileAdapter(new EcagentSessionFileAdapter());
857
856
  // Agent 插件系统:每个 EvolAgent × 每个 baseagent 一个独立 runner(H1/H2 修复)
858
857
  const agentLoader = new AgentLoader();
859
858
  agentLoader.register(new ClaudeAgentPlugin());
860
859
  agentLoader.register(new CodexAgentPlugin());
861
860
  agentLoader.register(new GeminiAgentPlugin());
861
+ agentLoader.register(new EcagentAgentPlugin());
862
862
  const agentInstances = agentLoader.createAll(agentRegistry, {
863
863
  onSessionIdUpdate: async (sessionId, agentSessionId) => {
864
864
  await sessionManager.updateAgentSessionIdBySessionId(sessionId, agentSessionId);
@@ -1010,7 +1010,7 @@ async function main() {
1010
1010
  const messageQueue = new MessageQueue(async (message) => {
1011
1011
  await processor.processMessage(message);
1012
1012
  }, {
1013
- persistencePath: path.join(resolvePaths().dataDir, 'message-queue.json'),
1013
+ persistencePath: daemonQueuePath(),
1014
1014
  });
1015
1015
  // 设置中断回调(精确中断正在处理的 agent)
1016
1016
  messageQueue.setInterruptCallback(async (sessionKey, baseagent, evolagentName, reason = 'new_message') => {
@@ -1074,6 +1074,7 @@ async function main() {
1074
1074
  responseEngine.setHandoffRuntime(handoffRuntime);
1075
1075
  // Trigger runtime: daemon-level script + feedback scheduler.
1076
1076
  const triggerSchedulers = new Map();
1077
+ const targetSessionLocks = new TargetSessionLockRegistry();
1077
1078
  const triggerAudit = new TriggerAuditLogger();
1078
1079
  const triggerScriptExecutor = new TriggerScriptExecutor();
1079
1080
  const daemonChannel = new DaemonChannel(sessionManager, messageQueue, {
@@ -1446,7 +1447,7 @@ async function main() {
1446
1447
  baseagent: owner.baseagent,
1447
1448
  getBaseagent: () => agentRegistry.get(owner.aid)?.baseagent || owner.baseagent,
1448
1449
  authorizeExecution: authorizeTriggerExecution,
1449
- }, eventBus);
1450
+ }, eventBus, targetSessionLocks);
1450
1451
  triggerSchedulers.set(owner.aid, scheduler);
1451
1452
  try {
1452
1453
  await scheduler.init();
@@ -1710,7 +1711,7 @@ async function main() {
1710
1711
  }
1711
1712
  const connectedChannels = new Set();
1712
1713
  const onlineNoticeSent = new Set();
1713
- const pendingFile = path.join(resolvePaths().dataDir, 'restart-pending.json');
1714
+ const pendingFile = path.join(daemonControlDir(), 'restart-pending.json');
1714
1715
  let pendingRestartNoticeInFlight = null;
1715
1716
  let pendingRestartNoticeSent = false;
1716
1717
  const sendOnlineNoticeForChannel = (inst) => {
@@ -1960,7 +1961,9 @@ async function main() {
1960
1961
  }
1961
1962
  }
1962
1963
  else {
1963
- logger.warn(`ECWeb 配置已启用但未找到 ${ecwebEntry}`);
1964
+ // 正式安装时 ECWeb 是独立的全局 ec-web 包,由 ec start/restart 的
1965
+ // 启动链路管理;runtime 目录没有内嵌构建产物是正常状态。
1966
+ logger.debug(`ECWeb runtime entry absent (managed externally): ${ecwebEntry}`);
1964
1967
  }
1965
1968
  }
1966
1969
  // 控制 AID 接收 owner 指令: