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
@@ -119,9 +119,12 @@ const GEMINI_H_CLASS_PATTERNS = [
119
119
  String.raw `(?:^|[/\\"])daemon\.pid(?:[/\\"]|$)`,
120
120
  String.raw `\.json_`,
121
121
  String.raw `\.json\.migrated`,
122
+ String.raw `agents[/\\][^/\\"]+[/\\](?:sessions|data[/\\](?:channels|handoff|outbox\.jsonl|contact-audit\.jsonl))`,
123
+ String.raw `data[/\\]triggers(?:[/\\]|$)`,
124
+ String.raw `data[/\\](?:sessions|contact-book-audit\.jsonl|feishu-seen-[^/\\"]+\.jsonl|[a-z0-9_-]+-greeted-[^/\\"]+\.jsonl|channel-state|daemon[/\\](?:control|migrations|message-queue\.json))`,
122
125
  ];
123
126
  const GEMINI_L_CLASS_PATTERNS = [
124
- String.raw `data[/\\]triggers(?:[/\\]|$)`,
127
+ String.raw `agents[/\\][^/\\"]+[/\\]triggers(?:[/\\]|$)`,
125
128
  ];
126
129
  const GEMINI_L_CLASS_WRITE_TOOLS = [
127
130
  'write_file', 'replace', 'delete_file', 'move_file', 'rename_file',
@@ -65,11 +65,27 @@ export function isOneMillionContextModel(model) {
65
65
  const baseModel = model.replace(/\[1m\]$/i, '');
66
66
  return ONE_M_CONTEXT_MODEL_PREFIXES.some(prefix => baseModel === prefix || baseModel.startsWith(`${prefix}-`));
67
67
  }
68
- /** Real context window size: 1M models = 1000000, otherwise 200000. */
68
+ /**
69
+ * EvolCore's conservative local context-window catalog: 1M models = 1,000,000;
70
+ * every other model falls back to 200,000 until provider metadata is available.
71
+ * This is an operational guardrail, not a claim about an upstream provider's
72
+ * advertised capacity.
73
+ */
69
74
  export function realContextWindowForModel(model) {
70
75
  return isOneMillionContextModel(model) ? 1000000 : 200000;
71
76
  }
72
- /** autoCompact trigger threshold: 350000 for all models. */
73
- export function autoCompactWindowForModel(_model) {
74
- return 350000;
77
+ /**
78
+ * The SDK's compaction threshold must leave room for the next model turn and
79
+ * its tool results. Keep a 10k reserve for 200k-class windows and retain the
80
+ * historical 350k cap for large-context models.
81
+ */
82
+ export function autoCompactWindowForContextWindow(contextWindow) {
83
+ if (!contextWindow || contextWindow <= 0)
84
+ return undefined;
85
+ const reserve = Math.min(10_000, Math.floor(contextWindow * 0.05));
86
+ return Math.min(350_000, Math.max(1, contextWindow - reserve));
87
+ }
88
+ /** Auto-compact threshold derived from the configured model context window. */
89
+ export function autoCompactWindowForModel(model) {
90
+ return autoCompactWindowForContextWindow(realContextWindowForModel(model)) ?? 350_000;
75
91
  }
@@ -6,13 +6,24 @@ import { resolvePaths } from '../../paths.js';
6
6
  const DEFAULT_LIMIT = 50;
7
7
  export const MAX_MSG_HISTORY_LIMIT = 500;
8
8
  const SESSION_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,255}$/;
9
+ const CHANNELS = new Set(['aun', 'feishu', 'wechat', 'dingtalk', 'qqbot', 'wecom']);
9
10
  export function msgHistory(args, options = {}) {
10
11
  if (!isValidAid(args.from)) {
11
12
  return error('INVALID_SELF_AID', `无效 self AID: ${args.from}`);
12
13
  }
13
- if (!isValidAid(args.target)) {
14
+ const channel = args.channel ?? 'aun';
15
+ if (!CHANNELS.has(channel)) {
16
+ return error('INVALID_CHANNEL', `不支持的 channel: ${String(args.channel)}`);
17
+ }
18
+ if (channel === 'aun' && !isValidAid(args.target)) {
14
19
  return error('INVALID_TARGET_AID', `无效 target AID: ${args.target}`);
15
20
  }
21
+ if (channel !== 'aun' && !isSafeChannelPeerId(args.target)) {
22
+ return error('INVALID_TARGET_ID', `无效 ${channel} 对端 ID`);
23
+ }
24
+ if (args.channelName !== undefined && !isSafeChannelName(args.channelName)) {
25
+ return error('INVALID_CHANNEL_NAME', '无效 channel 实例名');
26
+ }
16
27
  const limit = args.limit ?? DEFAULT_LIMIT;
17
28
  if (!Number.isInteger(limit) || limit < 1 || limit > MAX_MSG_HISTORY_LIMIT) {
18
29
  return error('INVALID_LIMIT', `limit 必须是 1-${MAX_MSG_HISTORY_LIMIT} 的整数`);
@@ -31,8 +42,8 @@ export function msgHistory(args, options = {}) {
31
42
  return error('INVALID_TIME_RANGE', 'after 必须早于 before');
32
43
  }
33
44
  const sessionsDir = options.sessionsDir ?? resolvePaths().sessionsDir;
34
- const chatDir = chatDirPath(sessionsDir, 'aun', args.target, args.from);
35
- const resolvedSession = resolveSessionFilter(chatDir, args.from, args.target, args.session ?? 'latest');
45
+ const chatDir = chatDirPath(sessionsDir, channel, args.target, args.from, channel === 'aun' && !args.channelName ? undefined : `${channel}#${args.from}#${args.channelName ?? 'main'}`);
46
+ const resolvedSession = resolveSessionFilter(chatDir, args.from, args.target, channel, args.session ?? 'latest');
36
47
  if (!resolvedSession.ok)
37
48
  return resolvedSession;
38
49
  let entries;
@@ -60,7 +71,7 @@ export function msgHistory(args, options = {}) {
60
71
  messages,
61
72
  };
62
73
  }
63
- function resolveSessionFilter(chatDir, selfAid, targetAid, requested) {
74
+ function resolveSessionFilter(chatDir, selfAid, targetAid, channel, requested) {
64
75
  if (requested === 'all')
65
76
  return { ok: true };
66
77
  if (requested === 'latest') {
@@ -68,7 +79,7 @@ function resolveSessionFilter(chatDir, selfAid, targetAid, requested) {
68
79
  if (!active?.id) {
69
80
  return error('LATEST_SESSION_NOT_FOUND', '该 AID 对暂无最新会话');
70
81
  }
71
- if (!sessionBelongsToPair(active, selfAid, targetAid)) {
82
+ if (!sessionBelongsToPair(active, selfAid, targetAid, channel)) {
72
83
  return error('SESSION_SCOPE_MISMATCH', 'active.json 不属于指定 AID 对');
73
84
  }
74
85
  return { ok: true, sessionId: active.id };
@@ -78,7 +89,7 @@ function resolveSessionFilter(chatDir, selfAid, targetAid, requested) {
78
89
  }
79
90
  const active = readJsonFile(path.join(chatDir, 'active.json'));
80
91
  if (active?.id === requested) {
81
- if (!sessionBelongsToPair(active, selfAid, targetAid)) {
92
+ if (!sessionBelongsToPair(active, selfAid, targetAid, channel)) {
82
93
  return error('SESSION_SCOPE_MISMATCH', '指定会话不属于该 AID 对');
83
94
  }
84
95
  return { ok: true, sessionId: requested };
@@ -90,15 +101,15 @@ function resolveSessionFilter(chatDir, selfAid, targetAid, requested) {
90
101
  const session = readLastJsonlLine(sessionPath);
91
102
  if (!session || session.id !== requested)
92
103
  continue;
93
- if (!sessionBelongsToPair(session, selfAid, targetAid)) {
104
+ if (!sessionBelongsToPair(session, selfAid, targetAid, channel)) {
94
105
  return error('SESSION_SCOPE_MISMATCH', '指定会话不属于该 AID 对');
95
106
  }
96
107
  return { ok: true, sessionId: requested };
97
108
  }
98
109
  return error('SESSION_NOT_FOUND', `在该 AID 对中找不到会话: ${requested}`);
99
110
  }
100
- function sessionBelongsToPair(session, selfAid, targetAid) {
101
- if (session.channelType && session.channelType !== 'aun')
111
+ function sessionBelongsToPair(session, selfAid, targetAid, channel) {
112
+ if (session.channelType && session.channelType !== channel)
102
113
  return false;
103
114
  if (session.channelId && session.channelId !== targetAid)
104
115
  return false;
@@ -107,6 +118,18 @@ function sessionBelongsToPair(session, selfAid, targetAid) {
107
118
  return false;
108
119
  return true;
109
120
  }
121
+ function isSafeChannelPeerId(value) {
122
+ return typeof value === 'string'
123
+ && value.trim().length > 0
124
+ && value.length <= 512
125
+ && !/[\u0000-\u001f\u007f]/.test(value);
126
+ }
127
+ function isSafeChannelName(value) {
128
+ return typeof value === 'string'
129
+ && value.trim().length > 0
130
+ && value.length <= 128
131
+ && !/[\u0000-\u001f\u007f#\/\\]/.test(value);
132
+ }
110
133
  function isMessageLogEntry(value) {
111
134
  if (!value || typeof value !== 'object')
112
135
  return false;
@@ -1,14 +1,11 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import crypto from 'crypto';
4
- import { resolvePaths } from '../paths.js';
4
+ import { agentOutboxPath } from '../paths.js';
5
5
  const MAX_ENTRIES_PER_AID = 20;
6
6
  const DEFAULT_TTL = 300_000; // 5 minutes
7
- function outboxDir() {
8
- return resolvePaths().outboxDir;
9
- }
10
7
  function outboxFile(aid) {
11
- return path.join(outboxDir(), `${aid}.jsonl`);
8
+ return agentOutboxPath(aid);
12
9
  }
13
10
  function generateId() {
14
11
  const ts = Date.now();
@@ -48,8 +45,7 @@ function writeEntries(aid, entries) {
48
45
  catch { }
49
46
  return;
50
47
  }
51
- const dir = outboxDir();
52
- fs.mkdirSync(dir, { recursive: true });
48
+ fs.mkdirSync(path.dirname(file), { recursive: true });
53
49
  fs.writeFileSync(file, entries.map(e => JSON.stringify(e)).join('\n') + '\n');
54
50
  }
55
51
  export function enqueue(aid, opts) {
@@ -68,9 +64,8 @@ export function enqueue(aid, opts) {
68
64
  ttl: opts.ttl ?? DEFAULT_TTL,
69
65
  postSend: opts.postSend,
70
66
  };
71
- const dir = outboxDir();
72
- fs.mkdirSync(dir, { recursive: true });
73
67
  const file = outboxFile(aid);
68
+ fs.mkdirSync(path.dirname(file), { recursive: true });
74
69
  // Enforce cap: read existing, drop oldest if over limit
75
70
  let entries = readEntries(aid);
76
71
  if (entries.length >= MAX_ENTRIES_PER_AID) {
@@ -317,6 +317,10 @@ export class AUNChannel {
317
317
  logger.error(`${this.logPrefix()} Failed to send card action error:`, err);
318
318
  });
319
319
  }
320
+ auditActionCardInteraction(behavior, cardMessageId, interactionId, result, textLength) {
321
+ logger.info(`${this.logPrefix()} ActionInteraction audit: behavior=${behavior ?? 'reply'} `
322
+ + `cardId=${cardMessageId} interactionId=${interactionId} result=${result} textLength=${textLength}`);
323
+ }
320
324
  extractTextPayload(payload, channelId, senderAid) {
321
325
  if (typeof payload === 'string')
322
326
  return payload;
@@ -329,8 +333,8 @@ export class AUNChannel {
329
333
  : typeof obj.card_message_id === 'string' ? obj.card_message_id : '';
330
334
  const cardInfo = cardMsgId ? this.cardMessageIdMap.get(cardMsgId) : undefined;
331
335
  if (cardInfo) {
332
- // compose 按钮点击后客户端收卡聚焦输入框,用户随后发送的自由文本由客户端结构化
333
- // 回传为 action_card_reply{behavior:"compose", text},没有按钮 value/action_value
336
+ // compose 按钮由新版客户端在卡片内采集输入;旧版客户端可继续从主编辑器
337
+ // 采集。两者最终都回传 action_card_reply{behavior:"compose", text}。
334
338
  // 映射为 _custom_input + values.custom_text,复用 claude-runner 已有的自定义输入分支
335
339
  // (原为多选表单提交而设),不需要新增 action 类型,也不必拦截消息队列。
336
340
  const behaviorRaw = typeof obj.behavior === 'string' ? obj.behavior : undefined;
@@ -399,16 +403,35 @@ export class AUNChannel {
399
403
  // ActionInteraction:后端明确接受后才消费 mapping。撤权、过期或
400
404
  // 重复点击返回 false 时保持 fail-closed,并提示用户卡片已失效。
401
405
  if (channelId && this.isGroupId(channelId) && !cardClickerAid) {
406
+ this.auditActionCardInteraction(behaviorRaw, cardMsgId, cardInfo.requestId, 'rejected_unauthenticated', text.length);
402
407
  this.notifyCardActionFailure(channelId, '⚠️ 无法验证卡片操作者身份', undefined, threadId);
403
408
  return '';
404
409
  }
405
410
  if (channelId && this.isGroupId(channelId) && cardInfo.initiatorAid
406
411
  && cardClickerAid !== cardInfo.initiatorAid) {
412
+ this.auditActionCardInteraction(behaviorRaw, cardMsgId, cardInfo.requestId, 'rejected_non_initiator', text.length);
407
413
  this.notifyCardActionFailure(channelId, '⚠️ 仅卡片发起者可操作', cardClickerAid, threadId);
408
414
  return '';
409
415
  }
416
+ // Compose reply 入参校验:text 必须非空且未超长。
417
+ // 校验失败不调用 callback、不消费卡片 mapping,让用户可修改重试。
418
+ if (isComposeReply) {
419
+ const trimmedText = text.trim();
420
+ const MAX_COMPOSE_TEXT_LENGTH = 64 * 1024; // 64K 字符
421
+ if (!trimmedText) {
422
+ this.auditActionCardInteraction(behaviorRaw, cardMsgId, cardInfo.requestId, 'rejected_empty', text.length);
423
+ this.notifyCardActionFailure(channelId, '⚠️ 输入内容不能为空,请重新输入', cardClickerAid, threadId);
424
+ return '';
425
+ }
426
+ if (text.length > MAX_COMPOSE_TEXT_LENGTH) {
427
+ this.auditActionCardInteraction(behaviorRaw, cardMsgId, cardInfo.requestId, 'rejected_too_long', text.length);
428
+ this.notifyCardActionFailure(channelId, `⚠️ 输入内容过长(${text.length} 字符,限制 ${MAX_COMPOSE_TEXT_LENGTH}),请缩短后重试`, cardClickerAid, threadId);
429
+ return '';
430
+ }
431
+ }
410
432
  if (this.interactionCallback) {
411
433
  if (this.interactionActionsInFlight.has(cardMsgId)) {
434
+ this.auditActionCardInteraction(behaviorRaw, cardMsgId, cardInfo.requestId, 'rejected_in_flight', text.length);
412
435
  this.notifyCardActionFailure(channelId, '⏳ 操作正在处理,请勿重复点击', cardClickerAid, threadId);
413
436
  return '';
414
437
  }
@@ -428,6 +451,7 @@ export class AUNChannel {
428
451
  };
429
452
  const settle = (accepted) => {
430
453
  this.interactionActionsInFlight.delete(cardMsgId);
454
+ this.auditActionCardInteraction(behaviorRaw, cardMsgId, cardInfo.requestId, accepted ? 'accepted' : 'backend_rejected', text.length);
431
455
  if (accepted) {
432
456
  this.forgetInteractionCard(cardMsgId, cardInfo.requestId);
433
457
  return;
@@ -448,6 +472,7 @@ export class AUNChannel {
448
472
  if (result && typeof result.then === 'function') {
449
473
  Promise.resolve(result).then(accepted => settle(accepted !== false), err => {
450
474
  this.interactionActionsInFlight.delete(cardMsgId);
475
+ this.auditActionCardInteraction(behaviorRaw, cardMsgId, cardInfo.requestId, 'callback_error', text.length);
451
476
  logger.error(`${this.logPrefix()} ActionInteraction callback failed: request=${cardInfo.requestId}`, err);
452
477
  this.notifyCardActionFailure(channelId, '❌ 操作失败,请稍后重试', cardClickerAid, threadId);
453
478
  });
@@ -458,11 +483,13 @@ export class AUNChannel {
458
483
  }
459
484
  catch (err) {
460
485
  this.interactionActionsInFlight.delete(cardMsgId);
486
+ this.auditActionCardInteraction(behaviorRaw, cardMsgId, cardInfo.requestId, 'callback_error', text.length);
461
487
  logger.error(`${this.logPrefix()} ActionInteraction callback failed: request=${cardInfo.requestId}`, err);
462
488
  this.notifyCardActionFailure(channelId, '❌ 操作失败,请稍后重试', cardClickerAid, threadId);
463
489
  }
464
490
  }
465
491
  else {
492
+ this.auditActionCardInteraction(behaviorRaw, cardMsgId, cardInfo.requestId, 'handler_unavailable', text.length);
466
493
  this.notifyCardActionFailure(channelId, '❌ 操作失败:交互处理器未就绪', cardClickerAid, threadId);
467
494
  }
468
495
  }
@@ -4130,10 +4157,9 @@ export class AUNChannelPlugin {
4130
4157
  label: btn.label, value: btn.key, style: btn.style ?? 'default', behavior: 'reply',
4131
4158
  })),
4132
4159
  };
4133
- // AUN action_card 无法内嵌输入框(只有按钮),手动输入用协议原生的 compose
4134
- // behavior:客户端收到点击后自行收卡+聚焦输入框,用户在 timeout 内的下一条
4135
- // 消息由客户端结构化回传为 action_card_reply{behavior:"compose"}(见
4136
- // extractTextPayload 的 compose 分支),不依赖 interceptNextMessage 拦截消息队列。
4160
+ // AUN action_card compose behavior 声明自由文本动作。新版客户端
4161
+ // 在卡片内采集并确认;旧版客户端可继续从主编辑器采集。两者都结构化
4162
+ // 回传 action_card_reply{behavior:"compose"},Core 不拦截普通消息队列。
4137
4163
  if (action.allowCustomInput) {
4138
4164
  aunCard.actions.push({ label: '✏️ 手动输入', style: 'default', behavior: 'compose', compose_timeout_ms: cardTtlMs });
4139
4165
  }
@@ -1,3 +1,4 @@
1
+ import { normalizeExecutionMode } from '../trigger/validation.js';
1
2
  import { resolvePeerRoleDetail } from '../config/peer-role-resolver.js';
2
3
  const DEFAULT_WATCHDOG_MS = 11 * 60 * 1000;
3
4
  export class DaemonChannel {
@@ -204,10 +205,11 @@ export class DaemonChannel {
204
205
  this.resetWatchdog(pendingId, slot);
205
206
  return true;
206
207
  }
207
- async getOrCreateConversationSession(trigger, projectPath, baseagent, runId, attemptId) {
208
+ async getOrCreateConversationSession(trigger, projectPath, baseagent, runId, _attemptId) {
208
209
  const channelId = `trigger:${trigger.id}`;
209
- const threadId = trigger.execution.thread === 'per_run'
210
- ? `trigger:${trigger.id}:${attemptId ?? runId}`
210
+ const mode = normalizeExecutionMode(trigger.execution);
211
+ const threadId = mode.thread === 'by_run'
212
+ ? `trigger:${trigger.id}:${runId}`
211
213
  : `trigger:${trigger.id}`;
212
214
  return await this.sessionManager.getOrCreateSession(this.channelName, channelId, projectPath, threadId, { channelKey: this.channelKey, peerId: `trigger:${trigger.id}`, peerName: trigger.name }, trigger.name, undefined, 'private', baseagent, trigger.agentAid, this.channelName, 'system');
213
215
  }
@@ -218,7 +220,7 @@ export class DaemonChannel {
218
220
  const session = await this.sessionManager.getSessionById(sessionId);
219
221
  if (!session?.agentSessionId)
220
222
  return undefined;
221
- if (!this.sessionManager.checkSessionFileExists(session.projectPath, session.agentSessionId, session.baseagent))
223
+ if (!this.sessionManager.checkSessionFileExists(session.projectPath, session.agentSessionId, session.baseagent, session.selfAID))
222
224
  return undefined;
223
225
  return session;
224
226
  }
@@ -309,6 +311,7 @@ export class DaemonChannel {
309
311
  numTurns: typeof metadata?.numTurns === 'number' ? metadata.numTurns : undefined,
310
312
  tokenUsage: isObject(metadata?.tokenUsage) ? metadata.tokenUsage : undefined,
311
313
  contextUsage: metadata?.contextUsage,
314
+ contextRecoveryAttempts: typeof metadata?.contextRecoveryAttempts === 'number' ? metadata.contextRecoveryAttempts : undefined,
312
315
  toolCallCount: Math.max(slot.acc.toolCallCount, completedToolCallCount),
313
316
  },
314
317
  });
@@ -9,7 +9,7 @@ import { createSendFileMarkerPattern } from '../core/message/file-markers.js';
9
9
  import { sentReceipt, suppressedReceipt } from '../core/message/send-receipt.js';
10
10
  import { initWelcomeManager, sendWelcomeIfNeeded } from '../utils/welcome.js';
11
11
  import { CONTACT_BIND_CODE_TTL_MS, CONTACT_BIND_MAX_FAILED_ATTEMPTS, ContactBindCodeRegistry, handleExplicitContactBindCommand, } from '../config/contact-bind-code.js';
12
- import { resolvePaths } from '../paths.js';
12
+ import { channelStatePath } from '../paths.js';
13
13
  const DINGTALK_QUEUED_EMOTION = 'Pin';
14
14
  const DINGTALK_THINKING_EMOTION = 'BusinessTrip';
15
15
  const DINGTALK_DONE_EMOTION = 'Done';
@@ -1280,8 +1280,6 @@ export class DingtalkChannelPlugin {
1280
1280
  return null;
1281
1281
  if (!isValidCredential(inst.clientId) || !isValidCredential(inst.clientSecret))
1282
1282
  return null;
1283
- const stateKey = createHash('sha256').update(`${ctx.agentName}:${inst.name}`).digest('hex').slice(0, 16);
1284
- const dataDir = resolvePaths().dataDir;
1285
1283
  if (inst.cardCallbackRouteKey) {
1286
1284
  logger.warn(`[DingTalk] cardCallbackRouteKey is ignored for ${inst.name}; interactive cards use Stream callbacks`);
1287
1285
  }
@@ -1291,8 +1289,8 @@ export class DingtalkChannelPlugin {
1291
1289
  requireMention: inst.requireMention,
1292
1290
  freeResponseChats: inst.freeResponseChats,
1293
1291
  cardTemplateId: inst.cardTemplateId,
1294
- seenMsgFile: path.join(dataDir, `dingtalk-seen-${stateKey}.jsonl`),
1295
- routeFile: path.join(dataDir, `dingtalk-routes-${stateKey}.json`),
1292
+ seenMsgFile: channelStatePath(inst.name, 'dingtalk-seen.jsonl'),
1293
+ routeFile: channelStatePath(inst.name, 'routes.json'),
1296
1294
  }, ctx.agentName, inst.name);
1297
1295
  const mode = resolveShowActivities(inst);
1298
1296
  const adapter = {
@@ -6,7 +6,7 @@ import { logger } from '../utils/logger.js';
6
6
  import { formatItemsAsText } from '../core/message/items-formatter.js';
7
7
  import { createSendFileMarkerPattern } from '../core/message/file-markers.js';
8
8
  import { initWelcomeManager, sendWelcomeIfNeeded } from '../utils/welcome.js';
9
- import { chatDirPath, decodeDirSegment } from '../core/session/session-fs-store.js';
9
+ import { channelStatePath } from '../paths.js';
10
10
  import { sentReceipt, suppressedReceipt } from '../core/message/send-receipt.js';
11
11
  import { ContactBindCodeRegistry, handleExplicitContactBindCommand, } from '../config/contact-bind-code.js';
12
12
  const feishuContactBinds = new ContactBindCodeRegistry({
@@ -724,15 +724,14 @@ export class FeishuChannel {
724
724
  catch { }
725
725
  }
726
726
  seenMessageFile(chatId) {
727
- if (!this.config.sessionsDir)
727
+ if (!this.channelName)
728
728
  return undefined;
729
- return path.join(chatDirPath(this.config.sessionsDir, 'feishu', chatId), 'seen-messages.jsonl');
729
+ return channelStatePath(this.channelName, 'dedup', 'feishu', encodeURIComponent(chatId) + '.jsonl');
730
730
  }
731
731
  loadSeenMessages() {
732
- this.migrateLegacySeenMessages();
733
- if (!this.config.sessionsDir)
732
+ if (!this.channelName)
734
733
  return;
735
- const feishuDir = path.join(this.config.sessionsDir, 'feishu');
734
+ const feishuDir = channelStatePath(this.channelName, 'dedup', 'feishu');
736
735
  let entries;
737
736
  try {
738
737
  entries = fs.readdirSync(feishuDir, { withFileTypes: true });
@@ -743,10 +742,16 @@ export class FeishuChannel {
743
742
  const cutoff = Date.now() - 24 * 60 * 60 * 1000;
744
743
  let loaded = 0;
745
744
  for (const entry of entries) {
746
- if (!entry.isDirectory())
745
+ if (!entry.isFile() || !entry.name.endsWith('.jsonl'))
747
746
  continue;
748
- const chatId = decodeDirSegment(entry.name);
749
- const seenFile = path.join(feishuDir, entry.name, 'seen-messages.jsonl');
747
+ let chatId;
748
+ try {
749
+ chatId = decodeURIComponent(entry.name.slice(0, -'.jsonl'.length));
750
+ }
751
+ catch {
752
+ continue;
753
+ }
754
+ const seenFile = path.join(feishuDir, entry.name);
750
755
  let lines;
751
756
  try {
752
757
  lines = fs.readFileSync(seenFile, 'utf-8').split('\n').filter(Boolean);
@@ -784,88 +789,7 @@ export class FeishuChannel {
784
789
  this.rewriteSeenMessageFile(chatId, this.seenMessages.get(chatId) ?? new Map());
785
790
  }
786
791
  if (loaded > 0)
787
- logger.info(`[Feishu] Loaded ${loaded} seen message ID(s) from chat directories`);
788
- }
789
- migrateLegacySeenMessages() {
790
- const legacyFile = this.config.seenMsgFile;
791
- if (!legacyFile || !this.config.sessionsDir || !fs.existsSync(legacyFile))
792
- return;
793
- const cutoff = Date.now() - 24 * 60 * 60 * 1000;
794
- const records = [];
795
- try {
796
- for (const line of fs.readFileSync(legacyFile, 'utf-8').split('\n')) {
797
- if (!line)
798
- continue;
799
- try {
800
- const { id, ts } = JSON.parse(line);
801
- if (typeof id === 'string' && typeof ts === 'number' && ts > cutoff) {
802
- records.push({ id, ts, channel: this.channelName });
803
- }
804
- }
805
- catch { }
806
- }
807
- }
808
- catch {
809
- return;
810
- }
811
- const chatIds = this.findChannelChatIds();
812
- if (chatIds.length === 0)
813
- return;
814
- try {
815
- for (const chatId of chatIds) {
816
- const seenFile = this.seenMessageFile(chatId);
817
- if (!seenFile)
818
- continue;
819
- fs.mkdirSync(path.dirname(seenFile), { recursive: true });
820
- if (records.length > 0) {
821
- fs.appendFileSync(seenFile, records.map(record => JSON.stringify(record)).join('\n') + '\n');
822
- }
823
- }
824
- fs.unlinkSync(legacyFile);
825
- logger.info(`[Feishu] Migrated ${records.length} seen message ID(s) into ${chatIds.length} chat director${chatIds.length === 1 ? 'y' : 'ies'}`);
826
- }
827
- catch (error) {
828
- logger.warn('[Feishu] Failed to migrate legacy seen-message file:', error);
829
- }
830
- }
831
- findChannelChatIds() {
832
- if (!this.config.sessionsDir || !this.channelName)
833
- return [];
834
- const feishuDir = path.join(this.config.sessionsDir, 'feishu');
835
- let entries;
836
- try {
837
- entries = fs.readdirSync(feishuDir, { withFileTypes: true });
838
- }
839
- catch {
840
- return [];
841
- }
842
- const chatIds = [];
843
- for (const entry of entries) {
844
- if (!entry.isDirectory())
845
- continue;
846
- const chatDir = path.join(feishuDir, entry.name);
847
- let candidates;
848
- try {
849
- candidates = ['active.json', ...fs.readdirSync(chatDir).filter(name => /^meta_.*\.jsonl$/.test(name))];
850
- }
851
- catch {
852
- continue;
853
- }
854
- let matched = false;
855
- for (const candidate of candidates) {
856
- try {
857
- const firstLine = fs.readFileSync(path.join(chatDir, candidate), 'utf-8').split('\n').find(Boolean);
858
- if (firstLine && JSON.parse(firstLine).channel === this.channelName) {
859
- matched = true;
860
- break;
861
- }
862
- }
863
- catch { }
864
- }
865
- if (matched)
866
- chatIds.push(decodeDirSegment(entry.name));
867
- }
868
- return chatIds;
792
+ logger.info(`[Feishu] Loaded ${loaded} seen message ID(s) from channel state`);
869
793
  }
870
794
  cleanupSeenMessages() {
871
795
  const cutoff = Date.now() - 24 * 60 * 60 * 1000;
@@ -892,21 +816,8 @@ export class FeishuChannel {
892
816
  if (!seenFile)
893
817
  return;
894
818
  try {
895
- const foreignRecords = [];
896
- if (fs.existsSync(seenFile)) {
897
- for (const line of fs.readFileSync(seenFile, 'utf-8').split('\n')) {
898
- if (!line)
899
- continue;
900
- try {
901
- const record = JSON.parse(line);
902
- if (record.channel && this.channelName && record.channel !== this.channelName)
903
- foreignRecords.push(record);
904
- }
905
- catch { }
906
- }
907
- }
908
819
  const ownRecords = [...chatMessages.entries()].map(([id, ts]) => ({ id, ts, channel: this.channelName }));
909
- const records = [...foreignRecords, ...ownRecords];
820
+ const records = ownRecords;
910
821
  if (records.length === 0) {
911
822
  fs.rmSync(seenFile, { force: true });
912
823
  }
@@ -1788,7 +1699,6 @@ export function hasMarkdownSyntax(text) {
1788
1699
  return markdownPatterns.some(pattern => pattern.test(text));
1789
1700
  }
1790
1701
  import { middleOutputModePolicy, resolveShowActivities, showActivitiesPolicy } from '../core/channel-loader.js';
1791
- import { resolvePaths } from '../paths.js';
1792
1702
  export class FeishuChannelPlugin {
1793
1703
  name = 'feishu';
1794
1704
  async createInstance(inst, ctx) {
@@ -1797,8 +1707,6 @@ export class FeishuChannelPlugin {
1797
1707
  const channel = new FeishuChannel({
1798
1708
  appId: inst.appId,
1799
1709
  appSecret: inst.appSecret,
1800
- sessionsDir: resolvePaths().sessionsDir,
1801
- seenMsgFile: path.join(resolvePaths().dataDir, `feishu-seen-${inst.name}.jsonl`),
1802
1710
  }, ctx.agentName, inst.name);
1803
1711
  const mode = resolveShowActivities(inst);
1804
1712
  const adapter = {
@@ -1,7 +1,7 @@
1
1
  import crypto from 'node:crypto';
2
2
  import fs from 'fs';
3
3
  import path from 'path';
4
- import { resolvePaths } from '../paths.js';
4
+ import { channelStatePath } from '../paths.js';
5
5
  import { logger } from '../utils/logger.js';
6
6
  import { sanitizeFileName, saveToUploads, safeFetch, bufferToInboundImage } from '../utils/media-cache.js';
7
7
  import { markdownToPlainText } from '../utils/markdown-to-plain-text.js';
@@ -208,9 +208,9 @@ export class WechatChannel {
208
208
  this.agentAid = agentAid;
209
209
  this.channelName = channelName;
210
210
  this.config = config;
211
- const dataDir = resolvePaths().dataDir;
212
- this.syncBufPath = path.join(dataDir, 'wechat-sync-buf.txt');
213
- this.contextTokensPath = path.join(dataDir, 'wechat-context-tokens.json');
211
+ const stateKey = channelName || `wechat#${agentAid || '_unknown'}#main`;
212
+ this.syncBufPath = channelStatePath(stateKey, 'sync-buffer.txt');
213
+ this.contextTokensPath = channelStatePath(stateKey, 'context-tokens.json');
214
214
  // 初始化 welcomeManager(使用共享帮助函数)
215
215
  if (agentAid && channelName) {
216
216
  this.welcomeManager = initWelcomeManager('wechat', agentAid, channelName);
@@ -1,7 +1,6 @@
1
1
  import crypto from 'node:crypto';
2
2
  import fs from 'node:fs';
3
3
  import os from 'node:os';
4
- import path from 'node:path';
5
4
  import { logger } from '../utils/logger.js';
6
5
  import { requireOptional } from '../utils/npm-ops.js';
7
6
  import { middleOutputModePolicy, resolveShowActivities, showActivitiesPolicy } from '../core/channel-loader.js';
@@ -9,7 +8,7 @@ import { formatItemsAsText } from '../core/message/items-formatter.js';
9
8
  import { createSendFileMarkerPattern } from '../core/message/file-markers.js';
10
9
  import { initWelcomeManager, sendWelcomeIfNeeded } from '../utils/welcome.js';
11
10
  import { bufferToInboundImage, sanitizeFileName, saveToUploads, validateUrl } from '../utils/media-cache.js';
12
- import { agentDataDir } from '../paths.js';
11
+ import { channelStatePath } from '../paths.js';
13
12
  import { atomicReadJson, atomicWriteJson } from '../utils/atomic-write.js';
14
13
  import { ContactBindCodeRegistry, handleExplicitContactBindCommand, } from '../config/contact-bind-code.js';
15
14
  function wecomErrorCode(error) {
@@ -280,11 +279,10 @@ export class PersistentWecomCardStore {
280
279
  }
281
280
  }
282
281
  export function wecomChannelDataPaths(agentAid, channelKey) {
283
- const safeKey = encodeURIComponent(channelKey);
284
- const dir = path.join(agentDataDir(agentAid), 'channels', safeKey);
282
+ void agentAid;
285
283
  return {
286
- dedupFile: path.join(dir, 'wecom-message-dedup.json'),
287
- cardFile: path.join(dir, 'wecom-cards.json'),
284
+ dedupFile: channelStatePath(channelKey, 'dedup.json'),
285
+ cardFile: channelStatePath(channelKey, 'cards.json'),
288
286
  };
289
287
  }
290
288
  export function createWecomCardTaskId(channelKey, interactionId) {
@@ -137,7 +137,7 @@ Options:
137
137
  ec agent new <aid> --non-interactive [选项]
138
138
 
139
139
  非交互模式选项:
140
- --baseagent <claude|codex|gemini> 默认: PATH 中第一个可用
140
+ --baseagent <claude|codex|gemini|ecagent> 默认: PATH 中第一个可用;ecagent 需显式选择
141
141
  --project <absolute path> 必填
142
142
  --owner <aid>
143
143
  --name <display-name>