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
@@ -10,22 +10,108 @@ function encodeSegment(s) {
10
10
  function decodeSegment(s) {
11
11
  return s.replace(/%([0-9A-Fa-f]{2})/g, (_, hex) => String.fromCharCode(parseInt(hex, 16)));
12
12
  }
13
+ function resolveAunSelfAID(selfAID, channelKey) {
14
+ const direct = selfAID?.trim();
15
+ if (direct && direct !== '_unknown')
16
+ return direct;
17
+ const parts = channelKey?.split('#');
18
+ const inferred = parts?.length === 3 && parts[0] === 'aun' ? parts[1]?.trim() : undefined;
19
+ if (inferred && inferred !== '_unknown')
20
+ return inferred;
21
+ throw new Error('[SessionFsStore] AUN chat directory requires selfAID or an aun channel key.');
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
+ }
13
48
  /**
14
- * 计算 chat 目录的完整路径。
15
- * - aun: sessionsDir/aun/<urlEncode(selfAID|'_unknown')>/<urlEncode(channelId)>/
16
- * - wecom: sessionsDir/wecom/<urlEncode(channelKey)>/<urlEncode(channelId)>/
17
- * - 其它: 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.
18
54
  */
19
- export function chatDirPath(sessionsDir, channelType, channelId, selfAID, channelKey) {
20
- if (channelType === 'aun') {
21
- return path.join(sessionsDir, channelType, encodeSegment(selfAID || '_unknown'), 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 };
73
+ }
74
+ catch {
75
+ return undefined;
76
+ }
77
+ }
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;
22
87
  }
23
- if (channelType === 'wecom' && channelKey?.startsWith('wecom#')) {
24
- return path.join(sessionsDir, channelType, encodeSegment(channelKey), encodeSegment(channelId));
88
+ catch {
89
+ return undefined;
25
90
  }
26
- return path.join(sessionsDir, channelType, encodeSegment(channelId));
27
91
  }
28
- /** 解码目录段(用于扫描时把目录名还原为原始 channelId/selfAID) */
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
+ /** 解码目录段(用于扫描时把目录名还原为逻辑路径键) */
29
115
  export function decodeDirSegment(seg) {
30
116
  return decodeSegment(seg);
31
117
  }
@@ -47,9 +133,8 @@ export function formatTimestamp(epochMs) {
47
133
  const ss = String(d.getSeconds()).padStart(2, '0');
48
134
  return `${yyyy}-${mo}-${dd} ${hh}:${mi}:${ss}`;
49
135
  }
50
- export function atomicWriteJson(filePath, data) {
136
+ function atomicWrite(filePath, content) {
51
137
  const tmpPath = filePath + '.tmp';
52
- const content = JSON.stringify(data, null, 2) + '\n';
53
138
  const fd = fs.openSync(tmpPath, 'w');
54
139
  fs.writeSync(fd, content);
55
140
  fs.fsyncSync(fd);
@@ -63,6 +148,12 @@ export function atomicWriteJson(filePath, data) {
63
148
  }
64
149
  fs.renameSync(tmpPath, filePath);
65
150
  }
151
+ export function atomicWriteJson(filePath, data) {
152
+ atomicWrite(filePath, JSON.stringify(data, null, 2) + '\n');
153
+ }
154
+ export function atomicWriteText(filePath, content) {
155
+ atomicWrite(filePath, content);
156
+ }
66
157
  export function appendJsonl(filePath, record) {
67
158
  const line = JSON.stringify(record) + '\n';
68
159
  const fd = fs.openSync(filePath, 'a');
@@ -128,22 +219,193 @@ export function readAllJsonlLines(filePath) {
128
219
  }
129
220
  }
130
221
  /**
131
- * 扫描所有 chat 目录。
132
- * - aun: channelType / selfAID / channelId (3层)
133
- * - wecom: channelType / channelKey / channelId (3层;兼容旧的2层目录)
134
- * - 其它: channelType / channelId (2层)
222
+ * Scan the current per-Agent layout:
223
+ * `agents/<aid>/sessions/<channelSessionKey>/`.
135
224
  */
136
- export function scanChatDirs(sessionsDir) {
225
+ export function scanChatDirs(sessionsRoot) {
137
226
  const results = [];
138
- let typeEntries;
227
+ let agentEntries;
139
228
  try {
140
- typeEntries = fs.readdirSync(sessionsDir, { withFileTypes: true });
229
+ agentEntries = fs.readdirSync(sessionsRoot, { withFileTypes: true });
141
230
  }
142
231
  catch (e) {
143
232
  if (e.code === 'ENOENT')
144
233
  return [];
145
234
  throw e;
146
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
+ }
147
409
  for (const typeEntry of typeEntries) {
148
410
  if (!typeEntry.isDirectory())
149
411
  continue;
@@ -157,7 +419,6 @@ export function scanChatDirs(sessionsDir) {
157
419
  continue;
158
420
  }
159
421
  if (channelType === 'aun') {
160
- // 3-layer: aun/selfAID/channelId
161
422
  for (const selfEntry of level2Entries) {
162
423
  if (!selfEntry.isDirectory())
163
424
  continue;
@@ -169,19 +430,15 @@ export function scanChatDirs(sessionsDir) {
169
430
  catch {
170
431
  continue;
171
432
  }
172
- for (const chatEntry of chatEntries) {
173
- if (!chatEntry.isDirectory())
174
- continue;
175
- results.push({
176
- channelType,
177
- selfAID: decodeSegment(selfEntry.name),
178
- channelId: decodeSegment(chatEntry.name),
179
- dirPath: path.join(selfDir, chatEntry.name),
180
- });
181
- }
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
+ });
182
438
  }
439
+ continue;
183
440
  }
184
- else if (channelType === 'wecom') {
441
+ if (channelType === 'wecom') {
185
442
  for (const entry of level2Entries) {
186
443
  if (!entry.isDirectory())
187
444
  continue;
@@ -195,43 +452,25 @@ export function scanChatDirs(sessionsDir) {
195
452
  catch {
196
453
  continue;
197
454
  }
198
- for (const chatEntry of chatEntries) {
199
- if (!chatEntry.isDirectory())
200
- continue;
201
- results.push({
202
- channelType,
203
- selfAID: '',
204
- channelKey: decoded,
205
- channelId: decodeSegment(chatEntry.name),
206
- dirPath: path.join(entryDir, chatEntry.name),
207
- });
208
- }
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
+ });
209
460
  }
210
461
  else {
211
- results.push({
212
- channelType,
213
- selfAID: '',
214
- channelId: decoded,
215
- dirPath: entryDir,
216
- });
462
+ results.push({ channelType, selfAID: '', channelId: decoded, dirPath: entryDir });
217
463
  }
218
464
  }
465
+ continue;
219
466
  }
220
- else {
221
- // 2-layer: channelType/channelId
222
- for (const chatEntry of level2Entries) {
223
- if (!chatEntry.isDirectory())
224
- continue;
467
+ for (const chatEntry of level2Entries)
468
+ if (chatEntry.isDirectory())
225
469
  results.push({
226
- channelType,
227
- selfAID: '',
228
- channelId: decodeSegment(chatEntry.name),
229
- dirPath: path.join(typeDir, chatEntry.name),
470
+ channelType, selfAID: '', channelId: decodeSegment(chatEntry.name), dirPath: path.join(typeDir, chatEntry.name),
230
471
  });
231
- }
232
- }
233
472
  }
234
- return results;
473
+ return results.sort((left, right) => left.dirPath < right.dirPath ? -1 : left.dirPath > right.dirPath ? 1 : 0);
235
474
  }
236
475
  export function scanMetaFiles(chatDir) {
237
476
  try {