evolcore 0.0.1 → 0.0.2

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 (371) hide show
  1. package/CHANGELOG.md +813 -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/assets/brand/evolcore/README.md +19 -0
  8. package/assets/brand/evolcore/evolcore-app-icon.png +0 -0
  9. package/assets/brand/evolcore/evolcore-app-icon.svg +13 -0
  10. package/assets/brand/evolcore/evolcore-brand-board.png +0 -0
  11. package/assets/brand/evolcore/evolcore-brand-board.svg +126 -0
  12. package/assets/brand/evolcore/evolcore-logo-kit.zip +0 -0
  13. package/assets/brand/evolcore/evolcore-logo-reverse.png +0 -0
  14. package/assets/brand/evolcore/evolcore-logo-reverse.svg +14 -0
  15. package/assets/brand/evolcore/evolcore-logo.png +0 -0
  16. package/assets/brand/evolcore/evolcore-logo.svg +14 -0
  17. package/assets/brand/evolcore/evolcore-mark.png +0 -0
  18. package/assets/brand/evolcore/evolcore-mark.svg +10 -0
  19. package/bin/ec-safe-output.js +161 -0
  20. package/bin/ec.js +29 -0
  21. package/dist/agents/baseagent.js +163 -0
  22. package/dist/agents/claude-runner.js +2385 -0
  23. package/dist/agents/codex-app-server-client.js +448 -0
  24. package/dist/agents/codex-runner.js +2639 -0
  25. package/dist/agents/gemini-runner.js +666 -0
  26. package/dist/agents/runner-types.js +75 -0
  27. package/dist/aun/aid/agentmd.js +216 -0
  28. package/dist/aun/aid/client.js +132 -0
  29. package/dist/aun/aid/control-aid.js +91 -0
  30. package/dist/aun/aid/identity.js +518 -0
  31. package/dist/aun/aid/index.js +4 -0
  32. package/dist/aun/aid/store.js +74 -0
  33. package/dist/aun/aid/types.js +1 -0
  34. package/dist/aun/aid/validation.js +21 -0
  35. package/dist/aun/group-identity.js +10 -0
  36. package/dist/aun/msg/group-index.js +6 -0
  37. package/dist/aun/msg/group.js +1231 -0
  38. package/dist/aun/msg/history.js +123 -0
  39. package/dist/aun/msg/index.js +5 -0
  40. package/dist/aun/msg/p2p.js +393 -0
  41. package/dist/aun/msg/payload-type.js +27 -0
  42. package/dist/aun/msg/upload.js +137 -0
  43. package/dist/aun/outbox.js +160 -0
  44. package/dist/aun/rpc/caller.js +42 -0
  45. package/dist/aun/rpc/connection.js +25 -0
  46. package/dist/aun/rpc/index.js +2 -0
  47. package/dist/aun/service-proxy.js +225 -0
  48. package/dist/aun/storage/download.js +29 -0
  49. package/dist/aun/storage/index.js +3 -0
  50. package/dist/aun/storage/manage.js +10 -0
  51. package/dist/aun/storage/upload.js +68 -0
  52. package/dist/channels/aun.js +4147 -0
  53. package/dist/channels/daemon.js +422 -0
  54. package/dist/channels/dingtalk.js +649 -0
  55. package/dist/channels/feishu.js +1789 -0
  56. package/dist/channels/qqbot.js +409 -0
  57. package/dist/channels/wechat.js +817 -0
  58. package/dist/channels/wecom.js +565 -0
  59. package/dist/cli/agent-command.js +641 -0
  60. package/dist/cli/agent.js +1059 -0
  61. package/dist/cli/aun-commands.js +1948 -0
  62. package/dist/cli/bench.js +1228 -0
  63. package/dist/cli/cli-argv.js +66 -0
  64. package/dist/cli/code-stats.js +329 -0
  65. package/dist/cli/command-log.js +82 -0
  66. package/dist/cli/config-selector.js +69 -0
  67. package/dist/cli/config.js +261 -0
  68. package/dist/cli/ctl-command.js +62 -0
  69. package/dist/cli/daemon-commands.js +2887 -0
  70. package/dist/cli/fs-command.js +1447 -0
  71. package/dist/cli/handoff-command.js +302 -0
  72. package/dist/cli/help.js +31 -0
  73. package/dist/cli/index.js +358 -0
  74. package/dist/cli/init-channel.js +1377 -0
  75. package/dist/cli/init.js +553 -0
  76. package/dist/cli/link-rules.js +240 -0
  77. package/dist/cli/model.js +590 -0
  78. package/dist/cli/net-check.js +723 -0
  79. package/dist/cli/queue-command.js +126 -0
  80. package/dist/cli/response.js +345 -0
  81. package/dist/cli/restart-monitor.js +456 -0
  82. package/dist/cli/stats.js +607 -0
  83. package/dist/cli/task-context.js +80 -0
  84. package/dist/cli/trigger-command.js +505 -0
  85. package/dist/cli/version.js +88 -0
  86. package/dist/cli/watch-logs.js +33 -0
  87. package/dist/cli/watch-msg.js +673 -0
  88. package/dist/config/boot-log.js +266 -0
  89. package/dist/config/builtin-role-templates.js +30 -0
  90. package/dist/config/builtin-roles.js +85 -0
  91. package/dist/config/config-batch-get.js +11 -0
  92. package/dist/config/config-field-policy.js +261 -0
  93. package/dist/config/config-manager.js +916 -0
  94. package/dist/config/config-operation-service.js +384 -0
  95. package/dist/config/contact-book.js +371 -0
  96. package/dist/config/gateway-config.js +858 -0
  97. package/dist/config/lifecycle.js +17 -0
  98. package/dist/config/mention-mode.js +27 -0
  99. package/dist/config/merge.js +161 -0
  100. package/dist/config/owner-policy.js +4 -0
  101. package/dist/config/peer-role-resolver.js +139 -0
  102. package/dist/config/resolved-config-op.js +483 -0
  103. package/dist/config/role-config-v4-startup.js +32 -0
  104. package/dist/config/role-config-v5-startup.js +27 -0
  105. package/dist/config/role-schema.js +105 -0
  106. package/dist/config/role-service.js +159 -0
  107. package/dist/config/role-store.js +204 -0
  108. package/dist/config/roles.js +55 -0
  109. package/dist/config/schema-registry.js +154 -0
  110. package/dist/config/snapshot.js +598 -0
  111. package/dist/config-store.js +503 -0
  112. package/dist/core/auth/agent-delegation.js +111 -0
  113. package/dist/core/auth/auth-gateway.js +166 -0
  114. package/dist/core/auth/authenticated-actor.js +6 -0
  115. package/dist/core/auth/authorization-audit.js +110 -0
  116. package/dist/core/auth/operation-authorizer.js +718 -0
  117. package/dist/core/auth/operation-catalog.js +675 -0
  118. package/dist/core/baseagent-loader.js +54 -0
  119. package/dist/core/bootstrap-service.js +175 -0
  120. package/dist/core/capability/capability-manager.js +316 -0
  121. package/dist/core/capability/providers/claude-capability-provider.js +176 -0
  122. package/dist/core/capability/providers/codex-capability-provider.js +148 -0
  123. package/dist/core/capability/providers/gemini-capability-provider.js +10 -0
  124. package/dist/core/capability/types.js +27 -0
  125. package/dist/core/causation/audit.js +103 -0
  126. package/dist/core/causation/aun-association.js +111 -0
  127. package/dist/core/causation/context.js +93 -0
  128. package/dist/core/causation/index.js +4 -0
  129. package/dist/core/causation/types.js +2 -0
  130. package/dist/core/channel-loader.js +277 -0
  131. package/dist/core/command/agent-control.js +616 -0
  132. package/dist/core/command/cli-intent-parser.js +225 -0
  133. package/dist/core/command/command-handler.js +1638 -0
  134. package/dist/core/command/menu-handler.js +3354 -0
  135. package/dist/core/command/menu-protocol.js +247 -0
  136. package/dist/core/command/role-menu.js +1524 -0
  137. package/dist/core/command/slash-gate.js +148 -0
  138. package/dist/core/command/slash-handler.js +3056 -0
  139. package/dist/core/daemon-file-cache.js +216 -0
  140. package/dist/core/event-bus.js +32 -0
  141. package/dist/core/event-catalog.js +740 -0
  142. package/dist/core/evolagent-registry.js +546 -0
  143. package/dist/core/evolagent.js +331 -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 +657 -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 +803 -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 +1436 -0
  161. package/dist/core/message/message-utils.js +70 -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 +3776 -0
  166. package/dist/core/message/response-snapshot.js +83 -0
  167. package/dist/core/message/stream-debouncer.js +130 -0
  168. package/dist/core/message/stream-idle-monitor.js +124 -0
  169. package/dist/core/model/config-scope.js +162 -0
  170. package/dist/core/model/field-scope.js +78 -0
  171. package/dist/core/model/model-catalog.js +227 -0
  172. package/dist/core/model/model-diagnostics.js +182 -0
  173. package/dist/core/model/model-permission.js +90 -0
  174. package/dist/core/permission/approval-gateway.js +1017 -0
  175. package/dist/core/permission/ec-command-parser.js +347 -0
  176. package/dist/core/permission/execution-sandbox.js +16 -0
  177. package/dist/core/permission/index.js +6 -0
  178. package/dist/core/permission/mode.js +24 -0
  179. package/dist/core/permission/sandbox-runtime.js +265 -0
  180. package/dist/core/permission/tool-policy.js +987 -0
  181. package/dist/core/permission/unix-socket-policy.js +99 -0
  182. package/dist/core/protected-paths.js +330 -0
  183. package/dist/core/relation/peer-identity.js +222 -0
  184. package/dist/core/relation/peer-key.js +1 -0
  185. package/dist/core/role/runtime-policy.js +141 -0
  186. package/dist/core/session/adapters/claude-session-file-adapter.js +218 -0
  187. package/dist/core/session/adapters/codex-session-file-adapter.js +333 -0
  188. package/dist/core/session/adapters/gemini-session-file-adapter.js +181 -0
  189. package/dist/core/session/session-file-adapter.js +7 -0
  190. package/dist/core/session/session-file-health.js +45 -0
  191. package/dist/core/session/session-fs-store.js +232 -0
  192. package/dist/core/session/session-key.js +24 -0
  193. package/dist/core/session/session-manager.js +1587 -0
  194. package/dist/core/session/session-mapper.js +100 -0
  195. package/dist/core/session/session-renew.js +314 -0
  196. package/dist/core/session/session-title.js +128 -0
  197. package/dist/core/session/session-turn-coordinator.js +205 -0
  198. package/dist/core/session/session-turns.js +67 -0
  199. package/dist/core/system-channels.js +29 -0
  200. package/dist/eck/baseagent-caps.js +18 -0
  201. package/dist/eck/detect.js +47 -0
  202. package/dist/eck/group-rules-sync.js +345 -0
  203. package/dist/eck/init.js +77 -0
  204. package/dist/eck/kit-renderer.js +359 -0
  205. package/dist/eck/manifest-engine.js +446 -0
  206. package/dist/eck/message-renderer.js +199 -0
  207. package/dist/eck/rules-loader.js +28 -0
  208. package/dist/index.js +2870 -4
  209. package/dist/ipc.js +748 -0
  210. package/dist/paths.js +262 -0
  211. package/dist/product.js +18 -0
  212. package/dist/response-system/context-builder.js +71 -0
  213. package/dist/response-system/coordinator.js +117 -0
  214. package/dist/response-system/decision-executor.js +86 -0
  215. package/dist/response-system/engines/v1/index.js +21 -0
  216. package/dist/response-system/engines/v1/interactive-flow.js +27 -0
  217. package/dist/response-system/engines/v1/proactive-flow.js +137 -0
  218. package/dist/response-system/engines/v1/types.js +1 -0
  219. package/dist/response-system/extensions.js +41 -0
  220. package/dist/response-system/index.js +6 -0
  221. package/dist/response-system/modes/index.js +7 -0
  222. package/dist/response-system/modes/single-session/index.js +72 -0
  223. package/dist/response-system/queues/fifo-queue.js +44 -0
  224. package/dist/response-system/queues/index.js +6 -0
  225. package/dist/response-system/queues/lifo-queue.js +42 -0
  226. package/dist/response-system/queues/priority-queue.js +63 -0
  227. package/dist/response-system/registry.js +97 -0
  228. package/dist/response-system/resolver.js +37 -0
  229. package/dist/response-system/selector.js +23 -0
  230. package/dist/response-system/types.js +7 -0
  231. package/dist/stats/billing.js +151 -0
  232. package/dist/stats/budget.js +93 -0
  233. package/dist/stats/db.js +403 -0
  234. package/dist/stats/eck-vars.js +89 -0
  235. package/dist/stats/index.js +11 -0
  236. package/dist/stats/normalizer.js +80 -0
  237. package/dist/stats/price-resolver.js +138 -0
  238. package/dist/stats/query.js +763 -0
  239. package/dist/stats/role-budget.js +168 -0
  240. package/dist/stats/writer.js +151 -0
  241. package/dist/trigger/anomaly-store.js +258 -0
  242. package/dist/trigger/audit.js +152 -0
  243. package/dist/trigger/event-source.js +119 -0
  244. package/dist/trigger/feedback.js +685 -0
  245. package/dist/trigger/history.js +290 -0
  246. package/dist/trigger/manager.js +291 -0
  247. package/dist/trigger/parser.js +520 -0
  248. package/dist/trigger/patch.js +148 -0
  249. package/dist/trigger/scheduler.js +1600 -0
  250. package/dist/trigger/script-executor.js +155 -0
  251. package/dist/trigger/state.js +145 -0
  252. package/dist/trigger/types.js +1 -0
  253. package/dist/trigger/validation.js +621 -0
  254. package/dist/types.js +12 -0
  255. package/dist/utils/aid-bind.js +299 -0
  256. package/dist/utils/atomic-write.js +95 -0
  257. package/dist/utils/avatar-upload.js +123 -0
  258. package/dist/utils/cross-platform.js +297 -0
  259. package/dist/utils/ecweb-utils.js +73 -0
  260. package/dist/utils/error-dict.json +153 -0
  261. package/dist/utils/error-utils.js +349 -0
  262. package/dist/utils/instance-registry.js +437 -0
  263. package/dist/utils/locale.js +21 -0
  264. package/dist/utils/log-writer.js +224 -0
  265. package/dist/utils/logger.js +89 -0
  266. package/dist/utils/markdown-to-plain-text.js +20 -0
  267. package/dist/utils/media-cache.js +271 -0
  268. package/dist/utils/model-prices.jsonl +17 -0
  269. package/dist/utils/npm-ops.js +210 -0
  270. package/dist/utils/process-introspect.js +133 -0
  271. package/dist/utils/process-tree-stats.js +271 -0
  272. package/dist/utils/project-path.js +74 -0
  273. package/dist/utils/stats.js +410 -0
  274. package/dist/utils/tool-summary.js +284 -0
  275. package/dist/utils/welcome.js +268 -0
  276. package/kits/docs/GUIDE.md +20 -0
  277. package/kits/docs/INDEX.md +65 -0
  278. package/kits/docs/aun/CHEATSHEET.md +19 -0
  279. package/kits/docs/aun/SYNC_PROTOCOL.md +15 -0
  280. package/kits/docs/channels/aun.md +65 -0
  281. package/kits/docs/channels/feishu.md +56 -0
  282. package/kits/docs/context-assembly.md +366 -0
  283. package/kits/docs/eck_templates/GUIDE.template.md +22 -0
  284. package/kits/docs/eck_templates/INDEX.template.md +28 -0
  285. package/kits/docs/eck_templates/path-registry.template.md +33 -0
  286. package/kits/docs/eck_templates/runtime.template.md +19 -0
  287. package/kits/docs/evolcore/INDEX.md +66 -0
  288. package/kits/docs/evolcore/agent.md +69 -0
  289. package/kits/docs/evolcore/aid.md +49 -0
  290. package/kits/docs/evolcore/config.md +149 -0
  291. package/kits/docs/evolcore/ctl.md +46 -0
  292. package/kits/docs/evolcore/event.md +216 -0
  293. package/kits/docs/evolcore/fs-architecture.md +1215 -0
  294. package/kits/docs/evolcore/fs.md +101 -0
  295. package/kits/docs/evolcore/group-fs.md +17 -0
  296. package/kits/docs/evolcore/group-rules.md +226 -0
  297. package/kits/docs/evolcore/group.md +141 -0
  298. package/kits/docs/evolcore/model.md +47 -0
  299. package/kits/docs/evolcore/msg.md +130 -0
  300. package/kits/docs/evolcore/response.md +80 -0
  301. package/kits/docs/evolcore/rpc.md +35 -0
  302. package/kits/docs/evolcore/self-summary.md +29 -0
  303. package/kits/docs/evolcore/stats.md +70 -0
  304. package/kits/docs/evolcore/storage.md +49 -0
  305. package/kits/docs/evolcore/trigger.md +524 -0
  306. package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -0
  307. package/kits/docs/identity/PATH_OPS.md +16 -0
  308. package/kits/docs/identity/ROLE_DETAIL.md +23 -0
  309. package/kits/docs/identity/identity-tools.md +26 -0
  310. package/kits/docs/path-registry.md +43 -0
  311. package/kits/docs/prompt-loading-architecture.md +266 -0
  312. package/kits/docs/venues/aun-group.md +45 -0
  313. package/kits/docs/venues/aun-private.md +10 -0
  314. package/kits/docs/venues/client-desktop.md +10 -0
  315. package/kits/docs/venues/client-mobile.md +10 -0
  316. package/kits/docs/venues/feishu-group.md +13 -0
  317. package/kits/docs/venues/feishu-private.md +9 -0
  318. package/kits/docs/venues/group.md +25 -0
  319. package/kits/docs/venues/private.md +10 -0
  320. package/kits/eck_manifest.auxiliary.json +43 -0
  321. package/kits/eck_manifest.json +191 -0
  322. package/kits/eck_message_manifest.json +63 -0
  323. package/kits/migrations/README-role-config-v4.md +32 -0
  324. package/kits/migrations/migrate-role-config-v4.mjs +623 -0
  325. package/kits/migrations/migrate-role-config-v5.mjs +346 -0
  326. package/kits/migrations/rename-config-file.mjs +99 -0
  327. package/kits/rules/01-overview.md +142 -0
  328. package/kits/rules/02-navigation.md +76 -0
  329. package/kits/rules/03-identity.md +34 -0
  330. package/kits/rules/04-relation.md +59 -0
  331. package/kits/rules/05-venue.md +44 -0
  332. package/kits/rules/06-channel.md +59 -0
  333. package/kits/schemas/_meta.json +29 -0
  334. package/kits/schemas/agent-config.schema.1.json +177 -0
  335. package/kits/schemas/agent-config.schema.2.json +239 -0
  336. package/kits/schemas/agent-config.schema.3.json +119 -0
  337. package/kits/schemas/agent-config.schema.4.json +208 -0
  338. package/kits/schemas/agent-config.schema.5.json +326 -0
  339. package/kits/schemas/contact-book.schema.1.json +36 -0
  340. package/kits/schemas/daemon.schema.1.json +90 -0
  341. package/kits/schemas/defaults.schema.1.json +81 -0
  342. package/kits/schemas/menu-exec-schema-commands.md +208 -0
  343. package/kits/schemas/migrations/README.md +28 -0
  344. package/kits/schemas/relation-config.schema.1.json +158 -0
  345. package/kits/schemas/relation-config.schema.2.json +73 -0
  346. package/kits/schemas/relation-config.schema.3.json +50 -0
  347. package/kits/schemas/relation-config.schema.4.json +47 -0
  348. package/kits/schemas/role-config.schema.1.json +200 -0
  349. package/kits/schemas/role-registry.schema.1.json +35 -0
  350. package/kits/schemas/single-session.schema.1.json +31 -0
  351. package/kits/templates/bootstrap-welcome.md +15 -0
  352. package/kits/templates/message-fragments/handoff-request-to-target.md +13 -0
  353. package/kits/templates/message-fragments/handoff-response-to-origin.md +10 -0
  354. package/kits/templates/message-fragments/inject-default.md +2 -0
  355. package/kits/templates/message-fragments/item.md +2 -0
  356. package/kits/templates/roles/admin.json +8 -0
  357. package/kits/templates/roles/member.json +40 -0
  358. package/kits/templates/roles/owner.json +8 -0
  359. package/kits/templates/roles/visitor.json +39 -0
  360. package/kits/templates/system-fragments/baseagent.md +14 -0
  361. package/kits/templates/system-fragments/bootstrap.md +16 -0
  362. package/kits/templates/system-fragments/channel.md +48 -0
  363. package/kits/templates/system-fragments/commands.md +26 -0
  364. package/kits/templates/system-fragments/identity.md +11 -0
  365. package/kits/templates/system-fragments/relation.md +19 -0
  366. package/kits/templates/system-fragments/session.md +53 -0
  367. package/kits/templates/system-fragments/venue.md +31 -0
  368. package/package.json +50 -15
  369. package/dist/index.d.ts +0 -7
  370. package/dist/index.d.ts.map +0 -1
  371. package/dist/index.js.map +0 -1
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Cross-platform process introspection.
3
+ *
4
+ * Provides process start time retrieval for PID reuse detection.
5
+ * Returns null when start time cannot be obtained — callers should treat
6
+ * this as "do not match" (conservative: prefer leaving an orphan over
7
+ * killing the wrong process).
8
+ */
9
+ import fs from 'fs';
10
+ import { spawnSync } from 'child_process';
11
+ import { isWindows } from './cross-platform.js';
12
+ const isMacOS = process.platform === 'darwin';
13
+ /** 容差:2 秒(覆盖 macOS 秒级精度 + 时钟漂移) */
14
+ export const START_TIME_TOLERANCE_MS = 2000;
15
+ /**
16
+ * 获取进程启动时间(Unix 毫秒)。
17
+ * 拿不到时返回 null,调用方应保守处理(不杀该 PID)。
18
+ */
19
+ export function getProcessStartTime(pid) {
20
+ try {
21
+ if (isWindows)
22
+ return getStartTimeWindows(pid);
23
+ if (isMacOS)
24
+ return getStartTimeMacOS(pid);
25
+ return getStartTimeLinux(pid);
26
+ }
27
+ catch {
28
+ return null;
29
+ }
30
+ }
31
+ /**
32
+ * 比对记录的启动时间与实际启动时间是否匹配(容差 2 秒)。
33
+ * actual === null 时返回 false(保守不匹配)。
34
+ */
35
+ export function startTimeMatches(recorded, actual) {
36
+ if (actual === null)
37
+ return false;
38
+ return Math.abs(recorded - actual) < START_TIME_TOLERANCE_MS;
39
+ }
40
+ /**
41
+ * 只读关联进程校验:允许记录时间晚于进程实际启动时间,但拒绝实际进程明显更新。
42
+ * 部分启动器会在子进程就绪后才写 instance 记录;此时 recorded 比 actual 晚是正常的。
43
+ */
44
+ export function isSameOrOlderProcess(recorded, actual) {
45
+ if (actual === null)
46
+ return true;
47
+ return actual - recorded < START_TIME_TOLERANCE_MS;
48
+ }
49
+ // ── Linux ──
50
+ function getStartTimeLinux(pid) {
51
+ let stat;
52
+ try {
53
+ stat = fs.readFileSync(`/proc/${pid}/stat`, 'utf-8');
54
+ }
55
+ catch {
56
+ return null;
57
+ }
58
+ const tail = stat.slice(stat.lastIndexOf(')') + 2);
59
+ const fields = tail.split(' ');
60
+ const starttimeJiffies = parseInt(fields[19], 10);
61
+ if (isNaN(starttimeJiffies))
62
+ return null;
63
+ let btimeSec;
64
+ try {
65
+ const m = fs.readFileSync('/proc/stat', 'utf-8').match(/^btime (\d+)/m);
66
+ if (!m)
67
+ return null;
68
+ btimeSec = parseInt(m[1], 10);
69
+ }
70
+ catch {
71
+ return null;
72
+ }
73
+ if (isNaN(btimeSec))
74
+ return null;
75
+ const clkTck = 100;
76
+ return (btimeSec + starttimeJiffies / clkTck) * 1000;
77
+ }
78
+ // ── macOS ──
79
+ function getStartTimeMacOS(pid) {
80
+ const result = spawnSync('ps', ['-p', String(pid), '-o', 'lstart='], {
81
+ encoding: 'utf-8',
82
+ timeout: 5000,
83
+ stdio: ['pipe', 'pipe', 'pipe'],
84
+ });
85
+ const out = result.stdout?.trim();
86
+ if (!out)
87
+ return null;
88
+ const t = Date.parse(out);
89
+ return isNaN(t) ? null : t;
90
+ }
91
+ // ── Windows ──
92
+ function getStartTimeWindows(pid) {
93
+ const fromPwsh = winPowerShellCreationDate(pid);
94
+ if (fromPwsh !== null)
95
+ return fromPwsh;
96
+ return winWmicCreationDate(pid);
97
+ }
98
+ function winPowerShellCreationDate(pid) {
99
+ const result = spawnSync('powershell', [
100
+ '-NoProfile',
101
+ '-Command',
102
+ `(Get-CimInstance Win32_Process -Filter 'ProcessId=${pid}').CreationDate`,
103
+ ], { encoding: 'utf-8', timeout: 8000, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });
104
+ const out = result.stdout?.trim();
105
+ if (!out)
106
+ return null;
107
+ return parseCimDate(out);
108
+ }
109
+ function winWmicCreationDate(pid) {
110
+ const result = spawnSync('wmic', ['process', 'where', `ProcessId=${pid}`, 'get', 'CreationDate', '/value'], { encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });
111
+ const out = result.stdout || '';
112
+ const m = out.match(/CreationDate=([^\r\n]+)/);
113
+ if (!m)
114
+ return null;
115
+ return parseCimDate(m[1].trim());
116
+ }
117
+ /**
118
+ * 解析 CIM/WMI 日期格式:yyyyMMddHHmmss.ffffff±TZZZ
119
+ * TZZZ 是相对 UTC 的分钟偏移(中国是 +480)
120
+ */
121
+ export function parseCimDate(s) {
122
+ if (!s)
123
+ return null;
124
+ const m = s.match(/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\.(\d{6})([+-]\d+)$/);
125
+ if (!m)
126
+ return null;
127
+ const [, y, mo, d, h, mi, sec, us, tz] = m;
128
+ const tzMin = parseInt(tz, 10);
129
+ if (isNaN(tzMin))
130
+ return null;
131
+ const localUtcMs = Date.UTC(+y, +mo - 1, +d, +h, +mi, +sec, Math.floor(+us / 1000));
132
+ return localUtcMs - tzMin * 60 * 1000;
133
+ }
@@ -0,0 +1,271 @@
1
+ import fs from 'fs';
2
+ import { spawnSync } from 'child_process';
3
+ function runnerKind(name) {
4
+ const normalized = name.toLowerCase();
5
+ if (normalized.includes('codex'))
6
+ return 'codex-app-server';
7
+ if (normalized.includes('claude'))
8
+ return 'claude-runner';
9
+ if (normalized.includes('gemini'))
10
+ return 'gemini-runner';
11
+ return null;
12
+ }
13
+ export function classifyProcessTree(processes, rootPid, explicitKinds = new Map()) {
14
+ const byPid = new Map(processes.map(process => [process.pid, process]));
15
+ const children = new Map();
16
+ for (const process of processes) {
17
+ const list = children.get(process.ppid) ?? [];
18
+ list.push(process.pid);
19
+ children.set(process.ppid, list);
20
+ }
21
+ const branchKinds = new Map();
22
+ const resolveBranchKind = (pid, visiting = new Set()) => {
23
+ if (branchKinds.has(pid))
24
+ return branchKinds.get(pid) ?? null;
25
+ if (visiting.has(pid))
26
+ return null;
27
+ visiting.add(pid);
28
+ const process = byPid.get(pid);
29
+ let kind = process ? runnerKind(process.name) : null;
30
+ if (!kind) {
31
+ for (const childPid of children.get(pid) ?? []) {
32
+ kind = resolveBranchKind(childPid, visiting);
33
+ if (kind)
34
+ break;
35
+ }
36
+ }
37
+ visiting.delete(pid);
38
+ branchKinds.set(pid, kind);
39
+ return kind;
40
+ };
41
+ const resolveAncestorKind = (process) => {
42
+ let parent = byPid.get(process.ppid);
43
+ const visited = new Set();
44
+ while (parent && !visited.has(parent.pid)) {
45
+ visited.add(parent.pid);
46
+ const kind = runnerKind(parent.name) ?? resolveBranchKind(parent.pid);
47
+ if (kind)
48
+ return kind;
49
+ parent = byPid.get(parent.ppid);
50
+ }
51
+ return null;
52
+ };
53
+ return processes.map(process => {
54
+ const explicit = explicitKinds.get(process.pid);
55
+ const normalized = process.name.toLowerCase();
56
+ let kind;
57
+ if (process.pid === rootPid)
58
+ kind = 'daemon';
59
+ else if (explicit)
60
+ kind = explicit;
61
+ else if (normalized === 'bwrap' || normalized.includes('bubblewrap'))
62
+ kind = 'sandbox';
63
+ else if (normalized === 'socat')
64
+ kind = 'runner-helper';
65
+ else
66
+ kind = runnerKind(process.name) ?? resolveBranchKind(process.pid) ?? resolveAncestorKind(process) ?? 'child';
67
+ return { ...process, kind };
68
+ });
69
+ }
70
+ const linuxClockTicks = readPositiveInt('getconf', ['CLK_TCK'], 100);
71
+ const linuxPageSize = readPositiveInt('getconf', ['PAGESIZE'], 4096);
72
+ function readPositiveInt(command, args, fallback) {
73
+ if (process.platform !== 'linux')
74
+ return fallback;
75
+ try {
76
+ const result = spawnSync(command, args, { encoding: 'utf8', timeout: 1000, stdio: ['ignore', 'pipe', 'ignore'] });
77
+ const value = Number.parseInt(result.stdout?.trim() || '', 10);
78
+ return value > 0 ? value : fallback;
79
+ }
80
+ catch {
81
+ return fallback;
82
+ }
83
+ }
84
+ function round1(value) {
85
+ return Math.round(value * 10) / 10;
86
+ }
87
+ export function parseLinuxProcStat(stat, pageSize = linuxPageSize, clockTicks = linuxClockTicks) {
88
+ const open = stat.indexOf('(');
89
+ const close = stat.lastIndexOf(')');
90
+ if (open < 1 || close <= open)
91
+ return null;
92
+ const pid = Number.parseInt(stat.slice(0, open).trim(), 10);
93
+ const fields = stat.slice(close + 2).trim().split(/\s+/);
94
+ const ppid = Number.parseInt(fields[1], 10);
95
+ const userTicks = Number.parseInt(fields[11], 10);
96
+ const systemTicks = Number.parseInt(fields[12], 10);
97
+ const startTicks = Number.parseInt(fields[19], 10);
98
+ const rssPages = Number.parseInt(fields[21], 10);
99
+ if (![pid, ppid, userTicks, systemTicks, rssPages].every(Number.isFinite))
100
+ return null;
101
+ return {
102
+ pid,
103
+ ppid,
104
+ name: stat.slice(open + 1, close),
105
+ cumulativeCpuMs: (userTicks + systemTicks) * 1000 / clockTicks,
106
+ rss: Math.max(0, rssPages) * pageSize,
107
+ startId: Number.isFinite(startTicks) ? startTicks : undefined,
108
+ };
109
+ }
110
+ function readLinuxMetrics() {
111
+ const metrics = [];
112
+ let entries;
113
+ try {
114
+ entries = fs.readdirSync('/proc');
115
+ }
116
+ catch {
117
+ return metrics;
118
+ }
119
+ for (const entry of entries) {
120
+ if (!/^\d+$/.test(entry))
121
+ continue;
122
+ try {
123
+ const metric = parseLinuxProcStat(fs.readFileSync(`/proc/${entry}/stat`, 'utf8'));
124
+ if (metric)
125
+ metrics.push(metric);
126
+ }
127
+ catch { }
128
+ }
129
+ return metrics;
130
+ }
131
+ export function parseProcessTime(value) {
132
+ const match = value.trim().match(/^(?:(\d+)-)?(?:(\d+):)?(\d+):(\d+(?:\.\d+)?)$/);
133
+ if (!match)
134
+ return 0;
135
+ const days = Number(match[1] || 0);
136
+ const hours = Number(match[2] || 0);
137
+ const minutes = Number(match[3] || 0);
138
+ const seconds = Number(match[4] || 0);
139
+ return (((days * 24 + hours) * 60 + minutes) * 60 + seconds) * 1000;
140
+ }
141
+ function readUnixPsMetrics() {
142
+ const result = spawnSync('ps', ['-axo', 'pid=,ppid=,rss=,time=,comm='], {
143
+ encoding: 'utf8',
144
+ timeout: 3000,
145
+ stdio: ['ignore', 'pipe', 'ignore'],
146
+ });
147
+ if (result.status !== 0)
148
+ return [];
149
+ const metrics = [];
150
+ for (const line of (result.stdout || '').split('\n')) {
151
+ const match = line.match(/^\s*(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(.+?)\s*$/);
152
+ if (!match)
153
+ continue;
154
+ metrics.push({
155
+ pid: Number(match[1]),
156
+ ppid: Number(match[2]),
157
+ rss: Number(match[3]) * 1024,
158
+ cumulativeCpuMs: parseProcessTime(match[4]),
159
+ name: match[5],
160
+ });
161
+ }
162
+ return metrics;
163
+ }
164
+ function readWindowsMetrics() {
165
+ const command = [
166
+ 'Get-CimInstance Win32_Process',
167
+ 'Select-Object ProcessId,ParentProcessId,Name,WorkingSetSize,KernelModeTime,UserModeTime,CreationDate',
168
+ 'ConvertTo-Json -Compress',
169
+ ].join(' | ');
170
+ const result = spawnSync('powershell', ['-NoProfile', '-Command', command], {
171
+ encoding: 'utf8',
172
+ timeout: 5000,
173
+ windowsHide: true,
174
+ stdio: ['ignore', 'pipe', 'ignore'],
175
+ });
176
+ if (result.status !== 0 || !result.stdout?.trim())
177
+ return [];
178
+ try {
179
+ const parsed = JSON.parse(result.stdout);
180
+ const rows = Array.isArray(parsed) ? parsed : [parsed];
181
+ return rows.map((row) => ({
182
+ pid: Number(row.ProcessId),
183
+ ppid: Number(row.ParentProcessId),
184
+ name: String(row.Name || 'unknown'),
185
+ rss: Number(row.WorkingSetSize) || 0,
186
+ cumulativeCpuMs: ((Number(row.KernelModeTime) || 0) + (Number(row.UserModeTime) || 0)) / 10_000,
187
+ startId: row.CreationDate ? String(row.CreationDate) : undefined,
188
+ })).filter((row) => row.pid > 0);
189
+ }
190
+ catch {
191
+ return [];
192
+ }
193
+ }
194
+ export function readProcessMetrics() {
195
+ if (process.platform === 'linux')
196
+ return readLinuxMetrics();
197
+ if (process.platform === 'win32')
198
+ return readWindowsMetrics();
199
+ return readUnixPsMetrics();
200
+ }
201
+ export class ProcessTreeSampler {
202
+ rootPid;
203
+ readMetrics;
204
+ now;
205
+ previous = new Map();
206
+ previousTs = null;
207
+ constructor(rootPid = process.pid, readMetrics = readProcessMetrics, now = Date.now) {
208
+ this.rootPid = rootPid;
209
+ this.readMetrics = readMetrics;
210
+ this.now = now;
211
+ }
212
+ sample(associatedRootPids = []) {
213
+ const timestamp = this.now();
214
+ const metrics = this.readMetrics();
215
+ const byPid = new Map(metrics.map(metric => [metric.pid, metric]));
216
+ const children = new Map();
217
+ for (const metric of metrics) {
218
+ const list = children.get(metric.ppid) ?? [];
219
+ list.push(metric.pid);
220
+ children.set(metric.ppid, list);
221
+ }
222
+ const rootPids = [...new Set([this.rootPid, ...associatedRootPids])].filter(pid => byPid.has(pid));
223
+ const selected = new Set();
224
+ const pending = [...rootPids];
225
+ while (pending.length > 0) {
226
+ const pid = pending.pop();
227
+ if (selected.has(pid))
228
+ continue;
229
+ selected.add(pid);
230
+ pending.push(...(children.get(pid) ?? []));
231
+ }
232
+ const elapsedMs = this.previousTs === null ? 0 : timestamp - this.previousTs;
233
+ const processes = [];
234
+ for (const pid of selected) {
235
+ const metric = byPid.get(pid);
236
+ if (!metric)
237
+ continue;
238
+ const previous = this.previous.get(pid);
239
+ const sameProcess = previous && (previous.startId === undefined || metric.startId === undefined || previous.startId === metric.startId);
240
+ const cpuDelta = sameProcess ? metric.cumulativeCpuMs - previous.cumulativeCpuMs : 0;
241
+ const cpuPercent = elapsedMs > 0 && cpuDelta > 0 ? cpuDelta * 100 / elapsedMs : 0;
242
+ processes.push({
243
+ pid: metric.pid,
244
+ ppid: metric.ppid,
245
+ name: metric.name,
246
+ cpuPercent: round1(cpuPercent),
247
+ rss: metric.rss,
248
+ });
249
+ }
250
+ this.previous = new Map(metrics.map(metric => [metric.pid, {
251
+ cumulativeCpuMs: metric.cumulativeCpuMs,
252
+ startId: metric.startId,
253
+ }]));
254
+ this.previousTs = timestamp;
255
+ processes.sort((left, right) => {
256
+ if (left.pid === this.rootPid)
257
+ return -1;
258
+ if (right.pid === this.rootPid)
259
+ return 1;
260
+ return left.pid - right.pid;
261
+ });
262
+ return {
263
+ rootPid: this.rootPid,
264
+ rootPids,
265
+ processCount: processes.length,
266
+ cpuPercent: round1(processes.reduce((sum, metric) => sum + metric.cpuPercent, 0)),
267
+ rss: processes.reduce((sum, metric) => sum + metric.rss, 0),
268
+ processes,
269
+ };
270
+ }
271
+ }
@@ -0,0 +1,74 @@
1
+ import fs from 'fs';
2
+ import os from 'os';
3
+ import path from 'path';
4
+ function pathForPlatform(platform) {
5
+ return platform === 'win32' ? path.win32 : path.posix;
6
+ }
7
+ function trimTrailingSeparators(value, root) {
8
+ let out = value;
9
+ while (out.length > root.length && /[\\/]$/.test(out)) {
10
+ out = out.slice(0, -1);
11
+ }
12
+ return out;
13
+ }
14
+ function normalizeForCompare(value, platform) {
15
+ const pathImpl = pathForPlatform(platform);
16
+ const resolved = pathImpl.resolve(value);
17
+ const trimmed = trimTrailingSeparators(resolved, pathImpl.parse(resolved).root);
18
+ return platform === 'win32' ? trimmed.toLowerCase() : trimmed;
19
+ }
20
+ function isWindowsUnsafeProjectRoot(rootPath, homeDir) {
21
+ const win = path.win32;
22
+ const root = normalizeForCompare(rootPath, 'win32');
23
+ const parsed = win.parse(win.resolve(rootPath));
24
+ if (root === normalizeForCompare(parsed.root, 'win32'))
25
+ return true;
26
+ const userProfileRoot = win.dirname(win.resolve(homeDir));
27
+ if (win.basename(userProfileRoot).toLowerCase() !== 'users')
28
+ return false;
29
+ return root === normalizeForCompare(userProfileRoot, 'win32');
30
+ }
31
+ export function sanitizeProjectRoot(rootPath, options = {}) {
32
+ const platform = options.platform ?? process.platform;
33
+ if (platform !== 'win32')
34
+ return rootPath;
35
+ const homeDir = options.homeDir ?? os.homedir();
36
+ if (!homeDir)
37
+ return rootPath;
38
+ return isWindowsUnsafeProjectRoot(rootPath, homeDir)
39
+ ? path.win32.join(homeDir, 'Projects')
40
+ : rootPath;
41
+ }
42
+ export function defaultProjectsRoot(evolcoreRoot, options = {}) {
43
+ const platform = options.platform ?? process.platform;
44
+ const pathImpl = pathForPlatform(platform);
45
+ if (platform === 'win32') {
46
+ const homeDir = options.homeDir ?? os.homedir();
47
+ if (homeDir && isWindowsUnsafeProjectRoot(evolcoreRoot, homeDir)) {
48
+ return path.win32.join(homeDir, 'Projects');
49
+ }
50
+ }
51
+ return pathImpl.join(evolcoreRoot, 'projects');
52
+ }
53
+ export function agentProjectRootFromDefaults(defaults, evolcoreRoot, options = {}) {
54
+ const platform = options.platform ?? process.platform;
55
+ const pathImpl = pathForPlatform(platform);
56
+ const configuredRoot = defaults?.projects?.rootPath
57
+ || (defaults?.projects?.defaultPath && pathImpl.dirname(defaults.projects.defaultPath))
58
+ || defaultProjectsRoot(evolcoreRoot, options);
59
+ return sanitizeProjectRoot(configuredRoot, options);
60
+ }
61
+ export function deriveAgentProjectPath(rootPath, aid, options = {}) {
62
+ const platform = options.platform ?? process.platform;
63
+ const pathImpl = pathForPlatform(platform);
64
+ const existsSync = options.existsSync ?? fs.existsSync;
65
+ const baseName = aid.split('.')[0];
66
+ const safeRootPath = sanitizeProjectRoot(rootPath, options);
67
+ let candidate = pathImpl.join(safeRootPath, baseName);
68
+ if (!existsSync(candidate))
69
+ return candidate;
70
+ let i = 1;
71
+ while (existsSync(`${candidate}~${i}`))
72
+ i++;
73
+ return `${candidate}~${i}`;
74
+ }