evolcore 0.0.1 → 0.0.3

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 (379) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/LICENSE +21 -0
  3. package/MIGRATION-0.5.0.md +378 -0
  4. package/README.md +318 -14
  5. package/ROLE_ACCESS_CONTROL.md +174 -0
  6. package/assets/.env.template +4 -0
  7. package/bin/ec-safe-output.js +161 -0
  8. package/bin/ec.js +29 -0
  9. package/dist/agents/baseagent.js +163 -0
  10. package/dist/agents/claude-runner.js +2565 -0
  11. package/dist/agents/codex-app-server-client.js +448 -0
  12. package/dist/agents/codex-runner.js +2682 -0
  13. package/dist/agents/gemini-runner.js +666 -0
  14. package/dist/agents/runner-types.js +75 -0
  15. package/dist/aun/aid/agentmd.js +216 -0
  16. package/dist/aun/aid/client.js +132 -0
  17. package/dist/aun/aid/control-aid.js +91 -0
  18. package/dist/aun/aid/identity.js +518 -0
  19. package/dist/aun/aid/index.js +4 -0
  20. package/dist/aun/aid/store.js +74 -0
  21. package/dist/aun/aid/types.js +1 -0
  22. package/dist/aun/aid/validation.js +21 -0
  23. package/dist/aun/group-identity.js +10 -0
  24. package/dist/aun/msg/group-index.js +6 -0
  25. package/dist/aun/msg/group.js +1231 -0
  26. package/dist/aun/msg/history.js +123 -0
  27. package/dist/aun/msg/index.js +5 -0
  28. package/dist/aun/msg/p2p.js +393 -0
  29. package/dist/aun/msg/payload-type.js +27 -0
  30. package/dist/aun/msg/upload.js +137 -0
  31. package/dist/aun/outbox.js +168 -0
  32. package/dist/aun/rpc/caller.js +42 -0
  33. package/dist/aun/rpc/connection.js +25 -0
  34. package/dist/aun/rpc/index.js +2 -0
  35. package/dist/aun/service-proxy.js +225 -0
  36. package/dist/aun/storage/download.js +29 -0
  37. package/dist/aun/storage/index.js +3 -0
  38. package/dist/aun/storage/manage.js +10 -0
  39. package/dist/aun/storage/upload.js +68 -0
  40. package/dist/channels/aun.js +4164 -0
  41. package/dist/channels/contact-bind-code.js +134 -0
  42. package/dist/channels/daemon.js +422 -0
  43. package/dist/channels/dingtalk.js +1479 -0
  44. package/dist/channels/feishu.js +1865 -0
  45. package/dist/channels/qqbot.js +409 -0
  46. package/dist/channels/wechat.js +817 -0
  47. package/dist/channels/wecom-card.js +101 -0
  48. package/dist/channels/wecom-onboarding.js +82 -0
  49. package/dist/channels/wecom-state.js +191 -0
  50. package/dist/channels/wecom.js +1157 -0
  51. package/dist/cli/agent-command.js +642 -0
  52. package/dist/cli/agent.js +1059 -0
  53. package/dist/cli/aun-commands.js +2003 -0
  54. package/dist/cli/bench.js +1228 -0
  55. package/dist/cli/cli-argv.js +66 -0
  56. package/dist/cli/code-stats.js +329 -0
  57. package/dist/cli/command-log.js +82 -0
  58. package/dist/cli/config-selector.js +69 -0
  59. package/dist/cli/config.js +261 -0
  60. package/dist/cli/contact.js +71 -0
  61. package/dist/cli/ctl-command.js +62 -0
  62. package/dist/cli/daemon-commands.js +2750 -0
  63. package/dist/cli/fs-command.js +1447 -0
  64. package/dist/cli/handoff-command.js +302 -0
  65. package/dist/cli/help.js +35 -0
  66. package/dist/cli/index.js +374 -0
  67. package/dist/cli/init-channel.js +1372 -0
  68. package/dist/cli/init.js +590 -0
  69. package/dist/cli/link-rules.js +240 -0
  70. package/dist/cli/model.js +591 -0
  71. package/dist/cli/net-check.js +723 -0
  72. package/dist/cli/queue-command.js +150 -0
  73. package/dist/cli/raw-key-input.js +25 -0
  74. package/dist/cli/response.js +344 -0
  75. package/dist/cli/restart-monitor.js +480 -0
  76. package/dist/cli/stats.js +609 -0
  77. package/dist/cli/task-context.js +80 -0
  78. package/dist/cli/trigger-command.js +545 -0
  79. package/dist/cli/version.js +93 -0
  80. package/dist/cli/watch-logs.js +33 -0
  81. package/dist/cli/watch-msg.js +673 -0
  82. package/dist/config/boot-log.js +266 -0
  83. package/dist/config/builtin-role-templates.js +42 -0
  84. package/dist/config/builtin-roles.js +91 -0
  85. package/dist/config/config-batch-get.js +11 -0
  86. package/dist/config/config-field-policy.js +261 -0
  87. package/dist/config/config-manager.js +1120 -0
  88. package/dist/config/config-operation-service.js +384 -0
  89. package/dist/config/contact-alias.js +68 -0
  90. package/dist/config/contact-book-store.js +454 -0
  91. package/dist/config/contact-book-v2-startup.js +35 -0
  92. package/dist/config/contact-book.js +224 -0
  93. package/dist/config/contact-operation-service.js +110 -0
  94. package/dist/config/gateway-config.js +858 -0
  95. package/dist/config/lifecycle.js +17 -0
  96. package/dist/config/mention-mode.js +27 -0
  97. package/dist/config/merge.js +161 -0
  98. package/dist/config/owner-policy.js +4 -0
  99. package/dist/config/peer-role-resolver.js +218 -0
  100. package/dist/config/resolved-config-op.js +483 -0
  101. package/dist/config/role-config-v4-startup.js +32 -0
  102. package/dist/config/role-config-v5-startup.js +27 -0
  103. package/dist/config/role-ranks.js +18 -0
  104. package/dist/config/role-schema.js +105 -0
  105. package/dist/config/role-service.js +156 -0
  106. package/dist/config/role-store.js +215 -0
  107. package/dist/config/roles.js +64 -0
  108. package/dist/config/schema-registry.js +154 -0
  109. package/dist/config/snapshot.js +598 -0
  110. package/dist/config-store.js +501 -0
  111. package/dist/core/auth/agent-delegation.js +111 -0
  112. package/dist/core/auth/auth-gateway.js +166 -0
  113. package/dist/core/auth/authenticated-actor.js +6 -0
  114. package/dist/core/auth/authorization-audit.js +119 -0
  115. package/dist/core/auth/operation-authorizer.js +720 -0
  116. package/dist/core/auth/operation-catalog.js +731 -0
  117. package/dist/core/baseagent-loader.js +54 -0
  118. package/dist/core/bootstrap-service.js +175 -0
  119. package/dist/core/capability/capability-manager.js +316 -0
  120. package/dist/core/capability/providers/claude-capability-provider.js +176 -0
  121. package/dist/core/capability/providers/codex-capability-provider.js +148 -0
  122. package/dist/core/capability/providers/gemini-capability-provider.js +10 -0
  123. package/dist/core/capability/types.js +27 -0
  124. package/dist/core/causation/audit.js +103 -0
  125. package/dist/core/causation/aun-association.js +111 -0
  126. package/dist/core/causation/context.js +93 -0
  127. package/dist/core/causation/index.js +4 -0
  128. package/dist/core/causation/types.js +2 -0
  129. package/dist/core/channel-loader.js +277 -0
  130. package/dist/core/command/agent-control.js +616 -0
  131. package/dist/core/command/cli-intent-parser.js +225 -0
  132. package/dist/core/command/command-handler.js +1733 -0
  133. package/dist/core/command/connect-menu.js +374 -0
  134. package/dist/core/command/menu-handler.js +3452 -0
  135. package/dist/core/command/menu-protocol.js +247 -0
  136. package/dist/core/command/role-menu.js +1623 -0
  137. package/dist/core/command/slash-gate.js +148 -0
  138. package/dist/core/command/slash-handler.js +3066 -0
  139. package/dist/core/daemon-file-cache.js +222 -0
  140. package/dist/core/event-bus.js +32 -0
  141. package/dist/core/event-catalog.js +810 -0
  142. package/dist/core/evolagent-registry.js +545 -0
  143. package/dist/core/evolagent.js +342 -0
  144. package/dist/core/handoff/dispatcher.js +229 -0
  145. package/dist/core/handoff/mutex.js +46 -0
  146. package/dist/core/handoff/runtime.js +324 -0
  147. package/dist/core/handoff/store.js +537 -0
  148. package/dist/core/handoff/types.js +12 -0
  149. package/dist/core/inference/text-inference.js +173 -0
  150. package/dist/core/interaction-registration.js +10 -0
  151. package/dist/core/interaction-router.js +278 -0
  152. package/dist/core/message/create-status.js +67 -0
  153. package/dist/core/message/im-renderer.js +659 -0
  154. package/dist/core/message/items-formatter.js +76 -0
  155. package/dist/core/message/logical-queue-bridge.js +123 -0
  156. package/dist/core/message/message-bridge.js +874 -0
  157. package/dist/core/message/message-cache.js +56 -0
  158. package/dist/core/message/message-log.js +339 -0
  159. package/dist/core/message/message-processor.js +4 -0
  160. package/dist/core/message/message-queue.js +1446 -0
  161. package/dist/core/message/message-utils.js +76 -0
  162. package/dist/core/message/peer-mode.js +105 -0
  163. package/dist/core/message/pending-hints.js +232 -0
  164. package/dist/core/message/response-depth.js +33 -0
  165. package/dist/core/message/response-engine.js +4020 -0
  166. package/dist/core/message/response-snapshot.js +83 -0
  167. package/dist/core/message/send-receipt.js +24 -0
  168. package/dist/core/message/stream-debouncer.js +139 -0
  169. package/dist/core/message/stream-idle-monitor.js +124 -0
  170. package/dist/core/model/config-scope.js +162 -0
  171. package/dist/core/model/field-scope.js +78 -0
  172. package/dist/core/model/model-catalog.js +227 -0
  173. package/dist/core/model/model-diagnostics.js +182 -0
  174. package/dist/core/model/model-permission.js +90 -0
  175. package/dist/core/permission/approval-gateway.js +1017 -0
  176. package/dist/core/permission/ec-command-parser.js +347 -0
  177. package/dist/core/permission/execution-sandbox.js +16 -0
  178. package/dist/core/permission/index.js +6 -0
  179. package/dist/core/permission/mode.js +24 -0
  180. package/dist/core/permission/sandbox-runtime.js +265 -0
  181. package/dist/core/permission/tool-policy.js +1019 -0
  182. package/dist/core/permission/unix-socket-policy.js +99 -0
  183. package/dist/core/protected-paths.js +332 -0
  184. package/dist/core/relation/peer-identity.js +222 -0
  185. package/dist/core/relation/peer-key.js +1 -0
  186. package/dist/core/role/runtime-policy.js +141 -0
  187. package/dist/core/session/adapters/claude-session-file-adapter.js +218 -0
  188. package/dist/core/session/adapters/codex-session-file-adapter.js +333 -0
  189. package/dist/core/session/adapters/gemini-session-file-adapter.js +181 -0
  190. package/dist/core/session/session-file-adapter.js +7 -0
  191. package/dist/core/session/session-file-health.js +45 -0
  192. package/dist/core/session/session-fs-store.js +273 -0
  193. package/dist/core/session/session-key.js +24 -0
  194. package/dist/core/session/session-manager.js +1643 -0
  195. package/dist/core/session/session-mapper.js +100 -0
  196. package/dist/core/session/session-renew.js +314 -0
  197. package/dist/core/session/session-title.js +128 -0
  198. package/dist/core/session/session-turn-coordinator.js +205 -0
  199. package/dist/core/session/session-turns.js +67 -0
  200. package/dist/core/system-channels.js +29 -0
  201. package/dist/eck/baseagent-caps.js +18 -0
  202. package/dist/eck/detect.js +47 -0
  203. package/dist/eck/group-rules-sync.js +345 -0
  204. package/dist/eck/init.js +77 -0
  205. package/dist/eck/kit-renderer.js +359 -0
  206. package/dist/eck/manifest-engine.js +446 -0
  207. package/dist/eck/message-renderer.js +199 -0
  208. package/dist/eck/rules-loader.js +28 -0
  209. package/dist/index.js +2926 -4
  210. package/dist/ipc.js +777 -0
  211. package/dist/paths.js +262 -0
  212. package/dist/product.js +18 -0
  213. package/dist/response-system/context-builder.js +71 -0
  214. package/dist/response-system/coordinator.js +117 -0
  215. package/dist/response-system/decision-executor.js +86 -0
  216. package/dist/response-system/engines/v1/index.js +21 -0
  217. package/dist/response-system/engines/v1/interactive-flow.js +27 -0
  218. package/dist/response-system/engines/v1/proactive-flow.js +137 -0
  219. package/dist/response-system/engines/v1/types.js +1 -0
  220. package/dist/response-system/extensions.js +41 -0
  221. package/dist/response-system/index.js +6 -0
  222. package/dist/response-system/modes/index.js +7 -0
  223. package/dist/response-system/modes/single-session/index.js +72 -0
  224. package/dist/response-system/queues/fifo-queue.js +44 -0
  225. package/dist/response-system/queues/index.js +6 -0
  226. package/dist/response-system/queues/lifo-queue.js +42 -0
  227. package/dist/response-system/queues/priority-queue.js +63 -0
  228. package/dist/response-system/registry.js +97 -0
  229. package/dist/response-system/resolver.js +37 -0
  230. package/dist/response-system/selector.js +23 -0
  231. package/dist/response-system/types.js +7 -0
  232. package/dist/stats/billing.js +163 -0
  233. package/dist/stats/budget.js +93 -0
  234. package/dist/stats/db.js +403 -0
  235. package/dist/stats/eck-vars.js +89 -0
  236. package/dist/stats/index.js +11 -0
  237. package/dist/stats/normalizer.js +80 -0
  238. package/dist/stats/price-resolver.js +138 -0
  239. package/dist/stats/query.js +763 -0
  240. package/dist/stats/role-budget.js +168 -0
  241. package/dist/stats/writer.js +151 -0
  242. package/dist/trigger/anomaly-store.js +258 -0
  243. package/dist/trigger/audit.js +152 -0
  244. package/dist/trigger/event-source.js +119 -0
  245. package/dist/trigger/feedback.js +685 -0
  246. package/dist/trigger/history.js +290 -0
  247. package/dist/trigger/manager.js +294 -0
  248. package/dist/trigger/parser.js +595 -0
  249. package/dist/trigger/patch.js +155 -0
  250. package/dist/trigger/scheduler.js +1602 -0
  251. package/dist/trigger/script-executor.js +155 -0
  252. package/dist/trigger/state.js +145 -0
  253. package/dist/trigger/types.js +1 -0
  254. package/dist/trigger/validation.js +634 -0
  255. package/dist/types.js +12 -0
  256. package/dist/utils/aid-bind.js +313 -0
  257. package/dist/utils/atomic-write.js +95 -0
  258. package/dist/utils/avatar-upload.js +123 -0
  259. package/dist/utils/cross-platform.js +297 -0
  260. package/dist/utils/ecweb-utils.js +73 -0
  261. package/dist/utils/error-dict.json +153 -0
  262. package/dist/utils/error-utils.js +349 -0
  263. package/dist/utils/instance-registry.js +444 -0
  264. package/dist/utils/locale.js +21 -0
  265. package/dist/utils/log-writer.js +270 -0
  266. package/dist/utils/logger.js +89 -0
  267. package/dist/utils/markdown-to-plain-text.js +20 -0
  268. package/dist/utils/media-cache.js +274 -0
  269. package/dist/utils/model-prices.jsonl +20 -0
  270. package/dist/utils/npm-ops.js +210 -0
  271. package/dist/utils/process-introspect.js +133 -0
  272. package/dist/utils/process-tree-stats.js +271 -0
  273. package/dist/utils/project-path.js +74 -0
  274. package/dist/utils/restart-safety.js +31 -0
  275. package/dist/utils/stats.js +410 -0
  276. package/dist/utils/system-memory.js +62 -0
  277. package/dist/utils/tool-summary.js +284 -0
  278. package/dist/utils/welcome.js +268 -0
  279. package/kits/docs/GUIDE.md +20 -0
  280. package/kits/docs/INDEX.md +66 -0
  281. package/kits/docs/aun/CHEATSHEET.md +19 -0
  282. package/kits/docs/aun/SYNC_PROTOCOL.md +15 -0
  283. package/kits/docs/channels/aun.md +65 -0
  284. package/kits/docs/channels/feishu.md +56 -0
  285. package/kits/docs/context-assembly.md +366 -0
  286. package/kits/docs/eck_templates/GUIDE.template.md +22 -0
  287. package/kits/docs/eck_templates/INDEX.template.md +28 -0
  288. package/kits/docs/eck_templates/path-registry.template.md +33 -0
  289. package/kits/docs/eck_templates/runtime.template.md +19 -0
  290. package/kits/docs/evolcore/INDEX.md +68 -0
  291. package/kits/docs/evolcore/agent.md +77 -0
  292. package/kits/docs/evolcore/aid.md +52 -0
  293. package/kits/docs/evolcore/config.md +149 -0
  294. package/kits/docs/evolcore/contact.md +57 -0
  295. package/kits/docs/evolcore/ctl.md +46 -0
  296. package/kits/docs/evolcore/event.md +216 -0
  297. package/kits/docs/evolcore/fs-architecture.md +1215 -0
  298. package/kits/docs/evolcore/fs.md +110 -0
  299. package/kits/docs/evolcore/group-fs.md +17 -0
  300. package/kits/docs/evolcore/group-rules.md +226 -0
  301. package/kits/docs/evolcore/group.md +150 -0
  302. package/kits/docs/evolcore/model.md +50 -0
  303. package/kits/docs/evolcore/msg.md +136 -0
  304. package/kits/docs/evolcore/response.md +75 -0
  305. package/kits/docs/evolcore/rpc.md +37 -0
  306. package/kits/docs/evolcore/self-summary.md +29 -0
  307. package/kits/docs/evolcore/stats.md +83 -0
  308. package/kits/docs/evolcore/storage.md +50 -0
  309. package/kits/docs/evolcore/trigger.md +539 -0
  310. package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -0
  311. package/kits/docs/identity/PATH_OPS.md +16 -0
  312. package/kits/docs/identity/ROLE_DETAIL.md +23 -0
  313. package/kits/docs/identity/identity-tools.md +26 -0
  314. package/kits/docs/path-registry.md +43 -0
  315. package/kits/docs/prompt-loading-architecture.md +266 -0
  316. package/kits/docs/venues/aun-group.md +45 -0
  317. package/kits/docs/venues/aun-private.md +10 -0
  318. package/kits/docs/venues/client-desktop.md +10 -0
  319. package/kits/docs/venues/client-mobile.md +10 -0
  320. package/kits/docs/venues/feishu-group.md +13 -0
  321. package/kits/docs/venues/feishu-private.md +9 -0
  322. package/kits/docs/venues/group.md +25 -0
  323. package/kits/docs/venues/private.md +10 -0
  324. package/kits/eck_manifest.auxiliary.json +43 -0
  325. package/kits/eck_manifest.json +203 -0
  326. package/kits/eck_message_manifest.json +63 -0
  327. package/kits/migrations/README-role-config-v4.md +32 -0
  328. package/kits/migrations/migrate-contact-book-v2.mjs +747 -0
  329. package/kits/migrations/migrate-role-config-v4.mjs +623 -0
  330. package/kits/migrations/migrate-role-config-v5.mjs +346 -0
  331. package/kits/migrations/rename-config-file.mjs +99 -0
  332. package/kits/rules/01-overview.md +142 -0
  333. package/kits/rules/02-navigation.md +76 -0
  334. package/kits/rules/03-identity.md +34 -0
  335. package/kits/rules/04-relation.md +59 -0
  336. package/kits/rules/05-venue.md +44 -0
  337. package/kits/rules/06-channel.md +59 -0
  338. package/kits/schemas/_meta.json +32 -0
  339. package/kits/schemas/agent-config.schema.1.json +177 -0
  340. package/kits/schemas/agent-config.schema.2.json +239 -0
  341. package/kits/schemas/agent-config.schema.3.json +119 -0
  342. package/kits/schemas/agent-config.schema.4.json +208 -0
  343. package/kits/schemas/agent-config.schema.5.json +326 -0
  344. package/kits/schemas/agent-config.schema.6.json +322 -0
  345. package/kits/schemas/contact-book.schema.1.json +36 -0
  346. package/kits/schemas/contact-book.schema.2.json +43 -0
  347. package/kits/schemas/daemon.schema.1.json +90 -0
  348. package/kits/schemas/defaults.schema.1.json +81 -0
  349. package/kits/schemas/menu-exec-schema-commands.md +208 -0
  350. package/kits/schemas/migrations/README.md +28 -0
  351. package/kits/schemas/relation-config.schema.1.json +158 -0
  352. package/kits/schemas/relation-config.schema.2.json +73 -0
  353. package/kits/schemas/relation-config.schema.3.json +50 -0
  354. package/kits/schemas/relation-config.schema.4.json +47 -0
  355. package/kits/schemas/relation-config.schema.5.json +47 -0
  356. package/kits/schemas/role-config.schema.1.json +201 -0
  357. package/kits/schemas/role-registry.schema.1.json +35 -0
  358. package/kits/schemas/single-session.schema.1.json +31 -0
  359. package/kits/templates/bootstrap-welcome.md +15 -0
  360. package/kits/templates/message-fragments/handoff-request-to-target.md +13 -0
  361. package/kits/templates/message-fragments/handoff-response-to-origin.md +10 -0
  362. package/kits/templates/message-fragments/inject-default.md +2 -0
  363. package/kits/templates/message-fragments/item.md +2 -0
  364. package/kits/templates/roles/admin.json +9 -0
  365. package/kits/templates/roles/member.json +41 -0
  366. package/kits/templates/roles/owner.json +9 -0
  367. package/kits/templates/roles/visitor.json +40 -0
  368. package/kits/templates/system-fragments/baseagent.md +14 -0
  369. package/kits/templates/system-fragments/bootstrap.md +16 -0
  370. package/kits/templates/system-fragments/channel.md +48 -0
  371. package/kits/templates/system-fragments/commands.md +28 -0
  372. package/kits/templates/system-fragments/identity.md +11 -0
  373. package/kits/templates/system-fragments/relation.md +19 -0
  374. package/kits/templates/system-fragments/session.md +53 -0
  375. package/kits/templates/system-fragments/venue.md +31 -0
  376. package/package.json +50 -15
  377. package/dist/index.d.ts +0 -7
  378. package/dist/index.d.ts.map +0 -1
  379. package/dist/index.js.map +0 -1
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Gemini SessionFileAdapter
3
+ *
4
+ * Reads Gemini CLI session files from ~/.gemini/tmp/{project}/chats/.
5
+ * Gemini stores sessions as JSON files with naming convention:
6
+ * session-{YYYY-MM-DDTHH-mm}-{uuid-prefix}.json
7
+ *
8
+ * Each file contains:
9
+ * { sessionId, projectHash, startTime, lastUpdated, messages, kind }
10
+ *
11
+ * Messages have type: 'user' | 'gemini' (not 'role').
12
+ */
13
+ import { logger } from '../../../utils/logger.js';
14
+ import { buildCliSessionPreview } from '../session-title.js';
15
+ import path from 'path';
16
+ import fs from 'fs';
17
+ import os from 'os';
18
+ export class GeminiSessionFileAdapter {
19
+ baseagent = 'gemini';
20
+ getGeminiHome() {
21
+ return path.join(os.homedir(), '.gemini');
22
+ }
23
+ /**
24
+ * Resolve Gemini project key for a project path.
25
+ * Priority:
26
+ * 1. ~/.gemini/projects.json explicit mapping
27
+ * 2. .project_root file content match under ~/.gemini/tmp/<project-key>/
28
+ * 3. basename(projectPath) fallback
29
+ */
30
+ resolveProjectKey(projectPath) {
31
+ const geminiHome = this.getGeminiHome();
32
+ const projectsPath = path.join(geminiHome, 'projects.json');
33
+ try {
34
+ if (fs.existsSync(projectsPath)) {
35
+ const data = JSON.parse(fs.readFileSync(projectsPath, 'utf-8'));
36
+ const mapped = data?.projects?.[projectPath];
37
+ if (typeof mapped === 'string' && mapped.trim())
38
+ return mapped;
39
+ }
40
+ }
41
+ catch (error) {
42
+ logger.debug('[GeminiAdapter] Failed to read projects.json:', error);
43
+ }
44
+ const tmpDir = path.join(geminiHome, 'tmp');
45
+ if (fs.existsSync(tmpDir)) {
46
+ try {
47
+ for (const entry of fs.readdirSync(tmpDir, { withFileTypes: true })) {
48
+ if (!entry.isDirectory())
49
+ continue;
50
+ const rootFile = path.join(tmpDir, entry.name, '.project_root');
51
+ if (!fs.existsSync(rootFile))
52
+ continue;
53
+ try {
54
+ const content = fs.readFileSync(rootFile, 'utf-8').trim();
55
+ if (content === projectPath)
56
+ return entry.name;
57
+ }
58
+ catch {
59
+ // ignore broken .project_root
60
+ }
61
+ }
62
+ }
63
+ catch (error) {
64
+ logger.debug('[GeminiAdapter] Failed to scan tmp project roots:', error);
65
+ }
66
+ }
67
+ return path.basename(projectPath);
68
+ }
69
+ /**
70
+ * Resolve the Gemini chats directory for a given project path.
71
+ */
72
+ resolveChatsDir(projectPath) {
73
+ const geminiHome = this.getGeminiHome();
74
+ const projectKey = this.resolveProjectKey(projectPath);
75
+ return path.join(geminiHome, 'tmp', projectKey, 'chats');
76
+ }
77
+ /**
78
+ * Find the session file matching a given session UUID.
79
+ * Gemini files are named session-{date}-{uuid-prefix}.json where
80
+ * uuid-prefix is the first 8 chars of the session UUID.
81
+ */
82
+ findSessionFile(projectPath, agentSessionId) {
83
+ const chatsDir = this.resolveChatsDir(projectPath);
84
+ if (!fs.existsSync(chatsDir))
85
+ return null;
86
+ const uuidPrefix = agentSessionId.substring(0, 8);
87
+ try {
88
+ const files = fs.readdirSync(chatsDir);
89
+ const match = files.find(f => f.includes(uuidPrefix) && f.endsWith('.json'));
90
+ return match ? path.join(chatsDir, match) : null;
91
+ }
92
+ catch {
93
+ return null;
94
+ }
95
+ }
96
+ readSessionData(projectPath, agentSessionId) {
97
+ const filePath = this.findSessionFile(projectPath, agentSessionId);
98
+ if (!filePath)
99
+ return null;
100
+ try {
101
+ return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
102
+ }
103
+ catch (error) {
104
+ logger.warn(`[GeminiAdapter] Failed to read session file: ${filePath}`, error);
105
+ return null;
106
+ }
107
+ }
108
+ checkExists(projectPath, agentSessionId) {
109
+ return this.findSessionFile(projectPath, agentSessionId) !== null;
110
+ }
111
+ getFileInfo(projectPath, agentSessionId) {
112
+ const data = this.readSessionData(projectPath, agentSessionId);
113
+ if (!data)
114
+ return { turns: 0 };
115
+ const msgs = data.messages || [];
116
+ const userTurns = msgs.filter((m) => m.type === 'user').length;
117
+ const preview = this.buildSessionPreview(data, false, false, 80);
118
+ return { turns: userTurns, title: preview.title || undefined };
119
+ }
120
+ readFirstMessage(projectPath, agentSessionId) {
121
+ return this.readUserMessage(projectPath, agentSessionId, 'first');
122
+ }
123
+ readLastUserMessage(projectPath, agentSessionId) {
124
+ return this.readUserMessage(projectPath, agentSessionId, 'last');
125
+ }
126
+ scanCliSessions(projectPath, limit = 10) {
127
+ const chatsDir = this.resolveChatsDir(projectPath);
128
+ if (!fs.existsSync(chatsDir))
129
+ return [];
130
+ try {
131
+ const files = fs.readdirSync(chatsDir)
132
+ .filter(f => f.startsWith('session-') && f.endsWith('.json'))
133
+ .map((f) => {
134
+ const filePath = path.join(chatsDir, f);
135
+ const stat = fs.statSync(filePath);
136
+ // Extract UUID from filename: session-{date}-{uuid-prefix}.json
137
+ // Read the file to get full UUID
138
+ try {
139
+ const data = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
140
+ const preview = this.buildSessionPreview(data, false, false, 80);
141
+ if (!preview.hasReal)
142
+ return null;
143
+ return { uuid: data.sessionId, mtime: stat.mtimeMs, title: preview.title || undefined };
144
+ }
145
+ catch {
146
+ return null;
147
+ }
148
+ })
149
+ .filter((e) => e !== null)
150
+ .sort((a, b) => b.mtime - a.mtime)
151
+ .slice(0, limit);
152
+ return files;
153
+ }
154
+ catch (error) {
155
+ logger.warn('[GeminiAdapter] scanCliSessions failed:', error);
156
+ return [];
157
+ }
158
+ }
159
+ readUserMessage(projectPath, agentSessionId, which) {
160
+ const data = this.readSessionData(projectPath, agentSessionId);
161
+ if (!data)
162
+ return null;
163
+ return this.buildSessionPreview(data, which === 'last').title;
164
+ }
165
+ buildSessionPreview(data, reverse = false, includeSyntheticFallback = true, maxLength = 50) {
166
+ const messages = (data.messages || [])
167
+ .filter((message) => message.type === 'user')
168
+ .map((message) => this.extractRawUserText(message.content))
169
+ .filter((text) => !!text);
170
+ if (reverse)
171
+ messages.reverse();
172
+ return buildCliSessionPreview(messages, 'gemini', { includeSyntheticFallback, maxLength });
173
+ }
174
+ extractRawUserText(content) {
175
+ const text = Array.isArray(content)
176
+ ? content.find((part) => part?.text)?.text || ''
177
+ : String(content || '');
178
+ const normalized = text.trim().replace(/\s+/g, ' ');
179
+ return normalized || null;
180
+ }
181
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * SessionFileAdapter — 会话文件操作的 Agent 抽象层
3
+ *
4
+ * 不同 Agent 后端(Claude / Codex)使用不同的会话文件存储格式和路径。
5
+ * 此接口将文件操作抽象为统一 API,由各 Agent 适配器分别实现。
6
+ */
7
+ export {};
@@ -0,0 +1,45 @@
1
+ import fsPromises from 'fs/promises';
2
+ import { logger } from '../../utils/logger.js';
3
+ /**
4
+ * 检查会话文件健康度(接收完整文件路径)
5
+ */
6
+ export async function checkSessionFile(sessionFile) {
7
+ const issues = [];
8
+ try {
9
+ const stats = await fsPromises.stat(sessionFile);
10
+ const sizeMB = stats.size / (1024 * 1024);
11
+ if (stats.size > 50 * 1024 * 1024) {
12
+ issues.push(`会话文件过大: ${sizeMB.toFixed(1)}MB`);
13
+ }
14
+ const content = await fsPromises.readFile(sessionFile, 'utf-8');
15
+ const lines = content.split('\n').filter(l => l.trim());
16
+ for (let i = 0; i < lines.length; i++) {
17
+ try {
18
+ JSON.parse(lines[i]);
19
+ }
20
+ catch (e) {
21
+ issues.push(`会话文件格式损坏(第 ${i + 1} 行)`);
22
+ return { healthy: false, issues, corrupt: true, fileSize: stats.size };
23
+ }
24
+ }
25
+ return {
26
+ healthy: issues.length === 0,
27
+ issues,
28
+ fileSize: stats.size
29
+ };
30
+ }
31
+ catch (error) {
32
+ logger.error('[SessionFileHealth] Check failed:', error);
33
+ issues.push(`文件读取失败: ${error.message}`);
34
+ return { healthy: false, issues, corrupt: true };
35
+ }
36
+ }
37
+ /**
38
+ * 备份单个会话文件(在同目录下创建 .bak 副本)
39
+ */
40
+ export async function backupSessionFile(sessionFile) {
41
+ const backupPath = `${sessionFile}.bak-${Date.now()}`;
42
+ await fsPromises.copyFile(sessionFile, backupPath);
43
+ logger.info(`[SessionFileHealth] Backup created: ${backupPath}`);
44
+ return backupPath;
45
+ }
@@ -0,0 +1,273 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ // 文件系统非法字符(Windows 最严格):< > : " / \ | ? *
4
+ // 还有控制字符 0x00-0x1F。我们把这些字符编码为 %XX(hex 大写)。
5
+ // `%` 本身也要转义为 %25,保证可逆。
6
+ const UNSAFE_CHARS_RE = /[<>:"/\\|?*\x00-\x1F%]/g;
7
+ function encodeSegment(s) {
8
+ return s.replace(UNSAFE_CHARS_RE, ch => '%' + ch.charCodeAt(0).toString(16).toUpperCase().padStart(2, '0'));
9
+ }
10
+ function decodeSegment(s) {
11
+ return s.replace(/%([0-9A-Fa-f]{2})/g, (_, hex) => String.fromCharCode(parseInt(hex, 16)));
12
+ }
13
+ /**
14
+ * 计算 chat 目录的完整路径。
15
+ * - aun: sessionsDir/aun/<urlEncode(selfAID|'_unknown')>/<urlEncode(channelId)>/
16
+ * - wecom: sessionsDir/wecom/<urlEncode(channelKey)>/<urlEncode(channelId)>/
17
+ * - 其它: sessionsDir/<channelType>/<urlEncode(channelId)>/
18
+ */
19
+ export function chatDirPath(sessionsDir, channelType, channelId, selfAID, channelKey) {
20
+ if (channelType === 'aun') {
21
+ return path.join(sessionsDir, channelType, encodeSegment(selfAID || '_unknown'), encodeSegment(channelId));
22
+ }
23
+ if (channelType === 'wecom' && channelKey?.startsWith('wecom#')) {
24
+ return path.join(sessionsDir, channelType, encodeSegment(channelKey), encodeSegment(channelId));
25
+ }
26
+ return path.join(sessionsDir, channelType, encodeSegment(channelId));
27
+ }
28
+ /** 解码目录段(用于扫描时把目录名还原为原始 channelId/selfAID) */
29
+ export function decodeDirSegment(seg) {
30
+ return decodeSegment(seg);
31
+ }
32
+ export function generateSessionId(now) {
33
+ const ts = now ?? Date.now();
34
+ const d = new Date(ts);
35
+ const yyyy = d.getFullYear();
36
+ const mm = String(d.getMonth() + 1).padStart(2, '0');
37
+ const dd = String(d.getDate()).padStart(2, '0');
38
+ return `meta_${yyyy}${mm}${dd}_${ts}`;
39
+ }
40
+ export function formatTimestamp(epochMs) {
41
+ const d = new Date(epochMs);
42
+ const yyyy = d.getFullYear();
43
+ const mo = String(d.getMonth() + 1).padStart(2, '0');
44
+ const dd = String(d.getDate()).padStart(2, '0');
45
+ const hh = String(d.getHours()).padStart(2, '0');
46
+ const mi = String(d.getMinutes()).padStart(2, '0');
47
+ const ss = String(d.getSeconds()).padStart(2, '0');
48
+ return `${yyyy}-${mo}-${dd} ${hh}:${mi}:${ss}`;
49
+ }
50
+ export function atomicWriteJson(filePath, data) {
51
+ const tmpPath = filePath + '.tmp';
52
+ const content = JSON.stringify(data, null, 2) + '\n';
53
+ const fd = fs.openSync(tmpPath, 'w');
54
+ fs.writeSync(fd, content);
55
+ fs.fsyncSync(fd);
56
+ fs.closeSync(fd);
57
+ try {
58
+ fs.unlinkSync(filePath);
59
+ }
60
+ catch (e) {
61
+ if (e.code !== 'ENOENT')
62
+ throw e;
63
+ }
64
+ fs.renameSync(tmpPath, filePath);
65
+ }
66
+ export function appendJsonl(filePath, record) {
67
+ const line = JSON.stringify(record) + '\n';
68
+ const fd = fs.openSync(filePath, 'a');
69
+ fs.writeSync(fd, line);
70
+ fs.fsyncSync(fd);
71
+ fs.closeSync(fd);
72
+ }
73
+ export function readJsonFile(filePath) {
74
+ try {
75
+ const content = fs.readFileSync(filePath, 'utf-8');
76
+ return JSON.parse(content);
77
+ }
78
+ catch (e) {
79
+ if (e.code === 'ENOENT')
80
+ return undefined;
81
+ if (e instanceof SyntaxError)
82
+ return undefined;
83
+ throw e;
84
+ }
85
+ }
86
+ export function readLastJsonlLine(filePath) {
87
+ try {
88
+ const content = fs.readFileSync(filePath, 'utf-8');
89
+ const lines = content.trimEnd().split('\n');
90
+ for (let i = lines.length - 1; i >= 0; i--) {
91
+ const line = lines[i].trim();
92
+ if (!line)
93
+ continue;
94
+ try {
95
+ return JSON.parse(line);
96
+ }
97
+ catch {
98
+ continue;
99
+ }
100
+ }
101
+ return undefined;
102
+ }
103
+ catch (e) {
104
+ if (e.code === 'ENOENT')
105
+ return undefined;
106
+ throw e;
107
+ }
108
+ }
109
+ export function readAllJsonlLines(filePath) {
110
+ try {
111
+ const content = fs.readFileSync(filePath, 'utf-8');
112
+ const results = [];
113
+ for (const line of content.split('\n')) {
114
+ const trimmed = line.trim();
115
+ if (!trimmed)
116
+ continue;
117
+ try {
118
+ results.push(JSON.parse(trimmed));
119
+ }
120
+ catch { /* skip corrupt line */ }
121
+ }
122
+ return results;
123
+ }
124
+ catch (e) {
125
+ if (e.code === 'ENOENT')
126
+ return [];
127
+ throw e;
128
+ }
129
+ }
130
+ /**
131
+ * 扫描所有 chat 目录。
132
+ * - aun: channelType / selfAID / channelId (3层)
133
+ * - wecom: channelType / channelKey / channelId (3层;兼容旧的2层目录)
134
+ * - 其它: channelType / channelId (2层)
135
+ */
136
+ export function scanChatDirs(sessionsDir) {
137
+ const results = [];
138
+ let typeEntries;
139
+ try {
140
+ typeEntries = fs.readdirSync(sessionsDir, { withFileTypes: true });
141
+ }
142
+ catch (e) {
143
+ if (e.code === 'ENOENT')
144
+ return [];
145
+ throw e;
146
+ }
147
+ for (const typeEntry of typeEntries) {
148
+ if (!typeEntry.isDirectory())
149
+ continue;
150
+ const channelType = typeEntry.name;
151
+ const typeDir = path.join(sessionsDir, channelType);
152
+ let level2Entries;
153
+ try {
154
+ level2Entries = fs.readdirSync(typeDir, { withFileTypes: true });
155
+ }
156
+ catch {
157
+ continue;
158
+ }
159
+ if (channelType === 'aun') {
160
+ // 3-layer: aun/selfAID/channelId
161
+ for (const selfEntry of level2Entries) {
162
+ if (!selfEntry.isDirectory())
163
+ continue;
164
+ const selfDir = path.join(typeDir, selfEntry.name);
165
+ let chatEntries;
166
+ try {
167
+ chatEntries = fs.readdirSync(selfDir, { withFileTypes: true });
168
+ }
169
+ catch {
170
+ continue;
171
+ }
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
+ }
182
+ }
183
+ }
184
+ else if (channelType === 'wecom') {
185
+ for (const entry of level2Entries) {
186
+ if (!entry.isDirectory())
187
+ continue;
188
+ const decoded = decodeSegment(entry.name);
189
+ const entryDir = path.join(typeDir, entry.name);
190
+ if (decoded.startsWith('wecom#') && decoded.split('#').length === 3) {
191
+ let chatEntries;
192
+ try {
193
+ chatEntries = fs.readdirSync(entryDir, { withFileTypes: true });
194
+ }
195
+ catch {
196
+ continue;
197
+ }
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
+ }
209
+ }
210
+ else {
211
+ results.push({
212
+ channelType,
213
+ selfAID: '',
214
+ channelId: decoded,
215
+ dirPath: entryDir,
216
+ });
217
+ }
218
+ }
219
+ }
220
+ else {
221
+ // 2-layer: channelType/channelId
222
+ for (const chatEntry of level2Entries) {
223
+ if (!chatEntry.isDirectory())
224
+ continue;
225
+ results.push({
226
+ channelType,
227
+ selfAID: '',
228
+ channelId: decodeSegment(chatEntry.name),
229
+ dirPath: path.join(typeDir, chatEntry.name),
230
+ });
231
+ }
232
+ }
233
+ }
234
+ return results;
235
+ }
236
+ export function scanMetaFiles(chatDir) {
237
+ try {
238
+ const entries = fs.readdirSync(chatDir);
239
+ return entries
240
+ .filter(f => f.startsWith('meta_') && f.endsWith('.jsonl'))
241
+ .sort();
242
+ }
243
+ catch (e) {
244
+ if (e.code === 'ENOENT')
245
+ return [];
246
+ throw e;
247
+ }
248
+ }
249
+ export function ensureChatDir(sessionsDir, channelType, channelId, selfAID, channelKey) {
250
+ const dir = chatDirPath(sessionsDir, channelType, channelId, selfAID, channelKey);
251
+ fs.mkdirSync(dir, { recursive: true });
252
+ fs.mkdirSync(path.join(dir, '_threads'), { recursive: true });
253
+ fs.mkdirSync(path.join(dir, '_trash'), { recursive: true });
254
+ return dir;
255
+ }
256
+ export function readThreadIndex(chatDir) {
257
+ const raw = readJsonFile(path.join(chatDir, '_threads', 'thread-index.json')) || {};
258
+ // Migrate legacy format: { threadId: sessionId } → { threadId: { sessionId, sessionKey, metaFile } }
259
+ const result = {};
260
+ for (const [tid, val] of Object.entries(raw)) {
261
+ if (typeof val === 'string') {
262
+ // Legacy: val is sessionId
263
+ result[tid] = { sessionId: val, sessionKey: '', metaFile: `${val}.jsonl` };
264
+ }
265
+ else {
266
+ result[tid] = val;
267
+ }
268
+ }
269
+ return result;
270
+ }
271
+ export function writeThreadIndex(chatDir, index) {
272
+ atomicWriteJson(path.join(chatDir, '_threads', 'thread-index.json'), index);
273
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * sessionKey: agent 内部会话路由键。
3
+ * 格式: channelType#urlEncode(channelId)#urlEncode(threadId)
4
+ * 无话题时 threadId 固定为 'main'。
5
+ */
6
+ export const DEFAULT_THREAD_ID = 'main';
7
+ export function formatSessionKey(channelType, channelId, threadId) {
8
+ const tid = threadId || DEFAULT_THREAD_ID;
9
+ return `${channelType}#${encodeURIComponent(channelId)}#${encodeURIComponent(tid)}`;
10
+ }
11
+ export function parseSessionKey(key) {
12
+ const first = key.indexOf('#');
13
+ if (first <= 0)
14
+ throw new Error(`Invalid session key: ${key}`);
15
+ const rest = key.slice(first + 1);
16
+ const second = rest.indexOf('#');
17
+ if (second <= 0)
18
+ throw new Error(`Invalid session key (missing threadId): ${key}`);
19
+ return {
20
+ channelType: key.slice(0, first),
21
+ channelId: decodeURIComponent(rest.slice(0, second)),
22
+ threadId: decodeURIComponent(rest.slice(second + 1)),
23
+ };
24
+ }