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,227 @@
1
+ /**
2
+ * model-catalog: 模型目录与单模型元数据。
3
+ *
4
+ * 目录来源(按序回退):
5
+ * {baseUrl}/v1/models → {baseUrl}/models → 内置 mock catalog
6
+ *
7
+ * baseUrl/apiKey 复用 baseagent 凭据解析链(与发消息同源),命令不接收 url/key 参数。
8
+ * 单模型详情(info)现阶段全部为 mock,{baseUrl}/models 接口补全字段后自动接真实数据。
9
+ *
10
+ * 详见 docs/model-command-design.md。
11
+ */
12
+ import { resolveAnthropicConfig, resolveOpenaiConfig } from '../../agents/baseagent.js';
13
+ import { activeBaseagent } from './config-scope.js';
14
+ import { resolveEffective } from '../../config/config-manager.js';
15
+ /** 取指定 baseagent 的 baseUrl + apiKey(复用现有解析链)。 */
16
+ function resolveCreds(self, ba) {
17
+ const baseagent = ba || activeBaseagent(self);
18
+ // baseagents 现在在 config.json 中
19
+ const config = self ? resolveEffective({ self }, { cache: true }) : { baseagents: {} };
20
+ const block = (config.baseagents || {});
21
+ try {
22
+ if (baseagent === 'codex') {
23
+ const c = block.codex;
24
+ const r = resolveOpenaiConfig({ agents: { codex: c } }, c);
25
+ return { baseUrl: r.baseUrl, apiKey: r.apiKey };
26
+ }
27
+ const c = block.claude;
28
+ const r = resolveAnthropicConfig({ agents: { claude: c } }, c);
29
+ return { baseUrl: r.baseUrl, apiKey: r.apiKey };
30
+ }
31
+ catch {
32
+ return {};
33
+ }
34
+ }
35
+ /** 从对象/字符串条目里提取 id(兼容 id/name/model 字段)。 */
36
+ function pickId(item) {
37
+ if (typeof item === 'string')
38
+ return item;
39
+ if (item && typeof item === 'object') {
40
+ const v = item.id ?? item.name ?? item.model;
41
+ return typeof v === 'string' ? v : undefined;
42
+ }
43
+ return undefined;
44
+ }
45
+ /** 从对象条目里提取厂商(兼容 owned_by/owner/provider)。 */
46
+ function pickOwner(item) {
47
+ if (item && typeof item === 'object') {
48
+ const v = item.owned_by ?? item.owner ?? item.provider;
49
+ return typeof v === 'string' ? v : undefined;
50
+ }
51
+ return undefined;
52
+ }
53
+ /**
54
+ * 通用容错解析:
55
+ * - 容器:json.data[] | json.models[] | json.data.models[] | 裸数组
56
+ * - 条目:字符串 或 {id|name|model, owned_by|owner|provider, created}
57
+ */
58
+ function genericParse(json) {
59
+ const arr = Array.isArray(json) ? json
60
+ : Array.isArray(json?.data) ? json.data
61
+ : Array.isArray(json?.models) ? json.models
62
+ : Array.isArray(json?.data?.models) ? json.data.models
63
+ : [];
64
+ const out = [];
65
+ for (const item of arr) {
66
+ const id = pickId(item);
67
+ if (!id)
68
+ continue;
69
+ out.push({ id, owned_by: pickOwner(item), created: typeof item?.created === 'number' ? item.created : undefined });
70
+ }
71
+ return out;
72
+ }
73
+ /**
74
+ * 网关专用 parser 注册表(扩展点)。
75
+ * 当前为空:ModelGate 是标准 OpenAI list 风格,genericParse 已覆盖。
76
+ * 示例(接入异形网关时取消注释并按实际格式实现):
77
+ * {
78
+ * name: 'some-gateway',
79
+ * match: (url) => url.includes('some-gateway.example.com'),
80
+ * parse: (json) => (json?.result?.modelList ?? []).map((m: any) => ({ id: m.code, owned_by: m.vendor })),
81
+ * }
82
+ */
83
+ const GATEWAY_PARSERS = [];
84
+ /** 选择 parser 并解析(专用优先,否则通用容错)。 */
85
+ export function parseModelList(json, url) {
86
+ for (const rule of GATEWAY_PARSERS) {
87
+ try {
88
+ if (rule.match(url, json))
89
+ return rule.parse(json);
90
+ }
91
+ catch { /* 专用 parser 失败则继续尝试通用解析 */ }
92
+ }
93
+ return genericParse(json);
94
+ }
95
+ async function fetchModelList(url, apiKey) {
96
+ try {
97
+ const controller = new AbortController();
98
+ const timer = setTimeout(() => controller.abort(), 5000);
99
+ const resp = await fetch(url, {
100
+ signal: controller.signal,
101
+ headers: apiKey ? { Authorization: `Bearer ${apiKey}` } : {},
102
+ });
103
+ clearTimeout(timer);
104
+ if (!resp.ok)
105
+ return null;
106
+ const json = await resp.json();
107
+ const entries = parseModelList(json, url);
108
+ return entries.length > 0 ? entries : null;
109
+ }
110
+ catch {
111
+ return null;
112
+ }
113
+ }
114
+ /** 内置 mock catalog(接口未就绪时的兜底;实测可用模型,见 docs 附录 A)。 */
115
+ const MOCK_CATALOG = [
116
+ { id: 'claude-opus-4-7', owned_by: 'anthropic' },
117
+ { id: 'claude-opus-4-6', owned_by: 'anthropic' },
118
+ { id: 'claude-sonnet-4-6', owned_by: 'anthropic' },
119
+ { id: 'claude-haiku-4-5-20251001', owned_by: 'anthropic' },
120
+ { id: 'deepseek-v4-pro', owned_by: 'deepseek' },
121
+ { id: 'deepseek-v4-flash', owned_by: 'deepseek' },
122
+ { id: 'kimi-k2.6', owned_by: 'moonshot' },
123
+ { id: 'kimi-k2.5', owned_by: 'moonshot' },
124
+ { id: 'glm-5.1', owned_by: 'zhipu' },
125
+ { id: 'glm-5', owned_by: 'zhipu' },
126
+ { id: 'glm-4.7', owned_by: 'zhipu' },
127
+ { id: 'MiniMax-M2.7', owned_by: 'minimax' },
128
+ ];
129
+ export function modelMatches(agent, candidate, current) {
130
+ if (!current)
131
+ return false;
132
+ if (candidate === current)
133
+ return true;
134
+ const resolve = typeof agent?.resolveModelId === 'function'
135
+ ? (value) => agent.resolveModelId(value) ?? value
136
+ : (value) => value;
137
+ const resolvedCandidate = resolve(candidate);
138
+ const resolvedCurrent = resolve(current);
139
+ return candidate === resolvedCurrent || resolvedCandidate === current || resolvedCandidate === resolvedCurrent;
140
+ }
141
+ /**
142
+ * 远端模型目录接口(临时)。本地 baseUrl 的 /v1/models、/models 尚未实现,
143
+ * 自动降级到此接口拉取真实可用模型列表;待本地接口就绪后此级自然不再命中。
144
+ */
145
+ const REMOTE_CATALOG_URL = 'https://mg-new.evolai.cn/claude-proxy/models';
146
+ /**
147
+ * 各 baseagent 的稳定别名(cc/SDK 自动解析到最新版本)。
148
+ * 这些是一等公民——defaults.json 默认就存别名(如 "opus"),必须可选可校验。
149
+ */
150
+ const KNOWN_ALIASES = {
151
+ claude: ['opus', 'sonnet', 'haiku'],
152
+ codex: [],
153
+ gemini: [],
154
+ };
155
+ /** 把别名作为虚拟条目并入目录头部(owned_by 标 alias,便于展示与校验)。 */
156
+ function withAliases(models, ba) {
157
+ const aliases = KNOWN_ALIASES[ba] || [];
158
+ const existing = new Set(models.map(m => m.id));
159
+ const aliasEntries = aliases
160
+ .filter(a => !existing.has(a))
161
+ .map(a => ({ id: a, owned_by: 'alias' }));
162
+ return [...aliasEntries, ...models];
163
+ }
164
+ /** 拉取模型目录:v1/models → models → 远端接口 → mock,并并入别名。 */
165
+ export async function getCatalog(self, ba) {
166
+ const baseagent = ba || activeBaseagent(self);
167
+ const { baseUrl, apiKey } = resolveCreds(self, baseagent);
168
+ if (baseUrl) {
169
+ const base = baseUrl.replace(/\/+$/, '');
170
+ const v1 = await fetchModelList(`${base}/v1/models`, apiKey);
171
+ if (v1)
172
+ return { models: withAliases(v1, baseagent), source: 'v1/models' };
173
+ const plain = await fetchModelList(`${base}/models`, apiKey);
174
+ if (plain)
175
+ return { models: withAliases(plain, baseagent), source: 'models' };
176
+ }
177
+ // 本地接口未实现 → 降级到远端目录接口(无需鉴权)
178
+ const remote = await fetchModelList(REMOTE_CATALOG_URL, apiKey);
179
+ if (remote)
180
+ return { models: withAliases(remote, baseagent), source: 'remote' };
181
+ return { models: withAliases(MOCK_CATALOG, baseagent), source: 'mock' };
182
+ }
183
+ /** claude 系判定:完整 ID 或别名(opus/sonnet/haiku)。 */
184
+ function isClaudeFamily(id) {
185
+ return /^claude-/.test(id) || ['opus', 'sonnet', 'haiku'].includes(id);
186
+ }
187
+ /** 单模型详情(现阶段 mock)。 */
188
+ export async function getModelInfo(modelId, self, ba) {
189
+ const cat = await getCatalog(self, ba);
190
+ const entry = cat.models.find(m => m.id === modelId);
191
+ const claudeFamily = isClaudeFamily(modelId);
192
+ // 远端目录把所有模型的 owned_by 标成网关名 'ModelGate',别名标成 'alias',
193
+ // 两者都不是真实厂商 → 回退到按 ID 推断。
194
+ const NON_VENDOR = new Set(['alias', 'ModelGate', '']);
195
+ const rawOwner = entry?.owned_by;
196
+ const owner = claudeFamily
197
+ ? 'anthropic'
198
+ : (rawOwner && !NON_VENDOR.has(rawOwner) ? rawOwner : inferOwner(modelId));
199
+ return {
200
+ id: modelId,
201
+ owned_by: owner,
202
+ context_window: claudeFamily ? 200000 : 128000,
203
+ max_output_tokens: 8192,
204
+ pricing: { input_per_mtok: null, output_per_mtok: null, currency: 'USD' },
205
+ modalities: ['text'],
206
+ supports_effort: claudeFamily,
207
+ status: entry ? 'available' : 'unknown',
208
+ mocked: true,
209
+ };
210
+ }
211
+ function inferOwner(id) {
212
+ if (/^claude-/.test(id))
213
+ return 'anthropic';
214
+ if (/^gpt-/.test(id))
215
+ return 'openai';
216
+ if (/^gemini-/.test(id))
217
+ return 'google';
218
+ if (/^deepseek-/.test(id))
219
+ return 'deepseek';
220
+ if (/^kimi-/.test(id))
221
+ return 'moonshot';
222
+ if (/^glm-/.test(id))
223
+ return 'zhipu';
224
+ if (/^MiniMax-/i.test(id))
225
+ return 'minimax';
226
+ return 'unknown';
227
+ }
@@ -0,0 +1,182 @@
1
+ import { resolveAnthropicConfig, resolveOpenaiConfig } from '../../agents/baseagent.js';
2
+ import { resolveEffective, resolveEffectiveFieldWithSource } from '../../config/config-manager.js';
3
+ import { constrainResolvedModelForRole } from './model-permission.js';
4
+ import { getCatalog } from './model-catalog.js';
5
+ async function fetchWithTimeout(url, opts, timeoutMs) {
6
+ const controller = new AbortController();
7
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
8
+ try {
9
+ return await fetch(url, { ...opts, signal: controller.signal });
10
+ }
11
+ catch {
12
+ return null;
13
+ }
14
+ finally {
15
+ clearTimeout(timer);
16
+ }
17
+ }
18
+ function resolveGatewayCreds(self, baseagent) {
19
+ try {
20
+ const config = self ? resolveEffective({ self }, { cache: true }) : { baseagents: {} };
21
+ const block = config.baseagents || {};
22
+ if (baseagent === 'codex') {
23
+ const codexCfg = block.codex || {};
24
+ const resolved = resolveOpenaiConfig({ agents: { codex: codexCfg } }, codexCfg);
25
+ return { baseUrl: resolved.baseUrl, apiKey: resolved.apiKey };
26
+ }
27
+ if (baseagent === 'claude') {
28
+ const claudeCfg = block.claude || {};
29
+ const resolved = resolveAnthropicConfig({ agents: { claude: claudeCfg } }, claudeCfg);
30
+ return { baseUrl: resolved.baseUrl, apiKey: resolved.apiKey };
31
+ }
32
+ }
33
+ catch {
34
+ return {};
35
+ }
36
+ return {};
37
+ }
38
+ async function responseError(resp) {
39
+ let message = `HTTP ${resp.status}`;
40
+ try {
41
+ const body = await resp.json();
42
+ message = body?.error?.message || body?.detail || body?.message || message;
43
+ }
44
+ catch {
45
+ // Keep the HTTP status when the gateway has no JSON error body.
46
+ }
47
+ return String(message).slice(0, 80);
48
+ }
49
+ export async function probeModel(baseUrl, apiKey, modelId, timeoutMs) {
50
+ const base = baseUrl.replace(/\/+$/, '');
51
+ const authHeaders = apiKey
52
+ ? { 'x-api-key': apiKey, Authorization: `Bearer ${apiKey}` }
53
+ : {};
54
+ const deadline = Date.now() + timeoutMs;
55
+ const remainingMs = () => Math.max(0, deadline - Date.now());
56
+ const anthropicStartedAt = Date.now();
57
+ const anthropic = await fetchWithTimeout(`${base}/v1/messages`, {
58
+ method: 'POST',
59
+ headers: { 'Content-Type': 'application/json', 'anthropic-version': '2023-06-01', ...authHeaders },
60
+ body: JSON.stringify({ model: modelId, max_tokens: 5, messages: [{ role: 'user', content: 'ok' }] }),
61
+ }, Math.min(5000, remainingMs()));
62
+ const anthropicMs = Date.now() - anthropicStartedAt;
63
+ if (anthropic?.ok)
64
+ return { ok: true, ms: anthropicMs, style: 'anthropic' };
65
+ if (anthropic) {
66
+ const error = await responseError(anthropic);
67
+ if (anthropic.status === 401 || anthropic.status === 403) {
68
+ return { ok: false, ms: anthropicMs, error, style: 'anthropic' };
69
+ }
70
+ }
71
+ if (remainingMs() <= 0) {
72
+ return { ok: false, ms: Date.now() - anthropicStartedAt, error: '探测超时', style: 'unknown' };
73
+ }
74
+ const openaiStartedAt = Date.now();
75
+ const openai = await fetchWithTimeout(`${base}/v1/chat/completions`, {
76
+ method: 'POST',
77
+ headers: { 'Content-Type': 'application/json', ...authHeaders },
78
+ body: JSON.stringify({ model: modelId, max_tokens: 5, messages: [{ role: 'user', content: 'ok' }] }),
79
+ }, Math.min(5000, remainingMs()));
80
+ const openaiMs = Date.now() - openaiStartedAt;
81
+ if (openai?.ok) {
82
+ try {
83
+ const body = await openai.json();
84
+ if (body?.choices || body?.id)
85
+ return { ok: true, ms: openaiMs, style: 'openai' };
86
+ }
87
+ catch {
88
+ // A successful but non-standard response cannot prove model availability.
89
+ }
90
+ }
91
+ if (openai && openai.status >= 400 && openai.status < 500) {
92
+ return { ok: false, ms: openaiMs, error: await responseError(openai), style: 'openai' };
93
+ }
94
+ return { ok: false, ms: 0, error: '网关不支持标准探测(无法确认可用性)', style: 'unknown' };
95
+ }
96
+ export async function runModelCheck(input) {
97
+ const steps = [];
98
+ const log = (step) => {
99
+ steps.push(step);
100
+ input.onStep?.(step);
101
+ };
102
+ const { baseUrl, apiKey } = resolveGatewayCreds(input.self, input.baseagent);
103
+ if (!baseUrl) {
104
+ log({ label: '网关地址', ok: false, detail: '未配置受支持的自定义网关,跳过 API 探测' });
105
+ }
106
+ else {
107
+ const startedAt = Date.now();
108
+ const ping = await fetchWithTimeout(`${baseUrl.replace(/\/+$/, '')}/v1/models`, {
109
+ method: 'GET',
110
+ headers: apiKey ? { Authorization: `Bearer ${apiKey}` } : {},
111
+ }, 4000);
112
+ const elapsed = Date.now() - startedAt;
113
+ log(ping
114
+ ? { label: '网关连通', ok: true, detail: baseUrl, ms: elapsed }
115
+ : { label: '网关连通', ok: false, detail: `${baseUrl} 无法连接(DNS/TCP 失败)`, ms: elapsed });
116
+ }
117
+ const catalog = await getCatalog(input.self, input.baseagent);
118
+ const modelIds = catalog.models.filter(model => model.owned_by !== 'alias').map(model => model.id);
119
+ log(catalog.source === 'mock'
120
+ ? { label: '模型列表', ok: false, detail: `无法获取远端列表,使用内置 mock(${modelIds.length} 个)` }
121
+ : { label: '模型列表', ok: true, detail: `来源: ${catalog.source},共 ${modelIds.length} 个模型` });
122
+ const selector = { self: input.self, peerKey: input.peerKey };
123
+ const modelResolution = resolveEffectiveFieldWithSource(`baseagents.${input.baseagent}.model`, selector, { cache: true });
124
+ const modelDecision = constrainResolvedModelForRole({
125
+ selfAid: input.self,
126
+ role: input.role,
127
+ baseagent: input.baseagent,
128
+ model: modelResolution.value,
129
+ });
130
+ const configuredModel = modelDecision.model;
131
+ const configuredModelSource = modelDecision.constrained
132
+ ? 'role'
133
+ : modelResolution.source?.target;
134
+ log(configuredModel
135
+ ? { label: '配置模型', ok: true, detail: `${configuredModel}(来源: ${configuredModelSource ?? '未知'})` }
136
+ : { label: '配置模型', ok: true, detail: '未配置(将使用 base agent 默认模型)' });
137
+ const probeResults = [];
138
+ if (baseUrl) {
139
+ const aliases = new Map();
140
+ for (const entry of catalog.models) {
141
+ if (entry.owned_by !== 'alias')
142
+ continue;
143
+ const matched = catalog.models.find(model => model.owned_by !== 'alias' && model.id.includes(entry.id));
144
+ if (matched)
145
+ aliases.set(entry.id, matched.id);
146
+ }
147
+ const resolvedConfiguredModel = configuredModel ? aliases.get(configuredModel) ?? configuredModel : undefined;
148
+ const targets = [
149
+ ...(resolvedConfiguredModel ? [{ display: configuredModel, probe: resolvedConfiguredModel }] : []),
150
+ ...modelIds.filter(id => id !== resolvedConfiguredModel).map(id => ({ display: id, probe: id })),
151
+ ];
152
+ for (let index = 0; index < targets.length; index += 2) {
153
+ const batch = await Promise.all(targets.slice(index, index + 2).map(async (target) => {
154
+ const result = await probeModel(baseUrl, apiKey, target.probe, 10_000);
155
+ return { id: target.display, ...result };
156
+ }));
157
+ for (const result of batch) {
158
+ probeResults.push(result);
159
+ log({ label: result.id.slice(0, 20), ok: result.ok, detail: result.ok ? '可用' : (result.error ?? '不可用'), ms: result.ms });
160
+ }
161
+ }
162
+ }
163
+ const availableModels = probeResults.filter(result => result.ok).map(result => result.id);
164
+ const configModelAvailable = configuredModel
165
+ ? probeResults.find(result => result.id === configuredModel)?.ok
166
+ : undefined;
167
+ return { steps, configuredModel, configuredModelSource, availableModels, configModelAvailable, catalogSource: catalog.source };
168
+ }
169
+ export function formatModelCheck(result) {
170
+ const lines = ['网关诊断'];
171
+ for (const step of result.steps) {
172
+ const elapsed = step.ms === undefined ? '' : ` (${step.ms}ms)`;
173
+ lines.push(`[${step.ok ? '✓' : '✗'}] ${step.label.padEnd(14)} ${step.detail}${elapsed}`);
174
+ }
175
+ if (result.configuredModel && result.configModelAvailable === false) {
176
+ lines.push(`⚠️ 当前配置的模型 ${result.configuredModel} 不可用`);
177
+ }
178
+ else if (result.configuredModel && result.configModelAvailable === true) {
179
+ lines.push(`✓ 当前配置的模型 ${result.configuredModel} 可用`);
180
+ }
181
+ return lines.join('\n');
182
+ }
@@ -0,0 +1,90 @@
1
+ import { getFieldPermission } from '../../config/roles.js';
2
+ export function isModelAllowedByPatterns(model, allowedModels) {
3
+ if (allowedModels.includes('*'))
4
+ return true;
5
+ return allowedModels.some(pattern => pattern.endsWith('*')
6
+ ? model.startsWith(pattern.slice(0, -1))
7
+ : model === pattern);
8
+ }
9
+ function normalizeBaseagent(baseagent) {
10
+ return (baseagent || 'claude').trim() || 'claude';
11
+ }
12
+ export function getRoleModelPermission(role, baseagent, selfAid) {
13
+ const ba = normalizeBaseagent(baseagent);
14
+ if (!role)
15
+ return { allowOverride: false, constrained: true };
16
+ const selected = getFieldPermission(role, `baseagents.${ba}.model`, selfAid);
17
+ if (!selected || !Array.isArray(selected.allowedModels)) {
18
+ return { allowOverride: true, constrained: false };
19
+ }
20
+ return {
21
+ defaultModel: typeof selected.default === 'string' ? selected.default : undefined,
22
+ allowOverride: selected.allowOverride !== false,
23
+ allowedModels: selected.allowedModels,
24
+ constrained: true,
25
+ };
26
+ }
27
+ export function resolveCandidateModel(model, resolveModelId) {
28
+ return resolveModelId?.(model) || model;
29
+ }
30
+ export function isModelAllowedForRoleBaseagent(role, baseagent, model, resolveModelId, selfAid) {
31
+ const permission = getRoleModelPermission(role, baseagent, selfAid);
32
+ if (!permission.constrained)
33
+ return true;
34
+ const allowed = permission.allowedModels || [];
35
+ const resolved = resolveCandidateModel(model, resolveModelId);
36
+ return isModelAllowedByPatterns(model, allowed) || isModelAllowedByPatterns(resolved, allowed);
37
+ }
38
+ export function filterModelsForRole(role, baseagent, models, resolveModelId, selfAid) {
39
+ const permission = getRoleModelPermission(role, baseagent, selfAid);
40
+ if (!permission.constrained)
41
+ return models;
42
+ return models.filter(model => isModelAllowedForRoleBaseagent(role, baseagent, model, resolveModelId, selfAid));
43
+ }
44
+ export function validateModelSelectionForRole(opts) {
45
+ const permission = getRoleModelPermission(opts.role, opts.baseagent, opts.selfAid);
46
+ const resolvedModel = resolveCandidateModel(opts.requestedModel, opts.resolveModelId);
47
+ const catalogModel = opts.models?.includes(resolvedModel)
48
+ ? resolvedModel
49
+ : opts.models?.includes(opts.requestedModel)
50
+ ? opts.requestedModel
51
+ : undefined;
52
+ if (opts.models && opts.models.length > 0 && !catalogModel) {
53
+ return {
54
+ ok: false,
55
+ code: 'INVALID_VALUE',
56
+ message: `无效模型: ${opts.requestedModel}`,
57
+ };
58
+ }
59
+ const targetModel = catalogModel || resolvedModel;
60
+ if (!permission.constrained)
61
+ return { ok: true, model: targetModel };
62
+ if (!permission.allowOverride) {
63
+ return {
64
+ ok: false,
65
+ code: 'MODEL_OVERRIDE_DISABLED',
66
+ message: `当前角色不允许切换模型,固定使用: ${permission.defaultModel || '角色默认模型'}`,
67
+ };
68
+ }
69
+ if (!isModelAllowedForRoleBaseagent(opts.role, opts.baseagent, targetModel, opts.resolveModelId, opts.selfAid)) {
70
+ return {
71
+ ok: false,
72
+ code: 'MODEL_NOT_ALLOWED',
73
+ message: `当前角色不允许使用模型: ${opts.requestedModel}`,
74
+ };
75
+ }
76
+ return { ok: true, model: targetModel };
77
+ }
78
+ export function constrainResolvedModelForRole(opts) {
79
+ const permission = getRoleModelPermission(opts.role, opts.baseagent, opts.selfAid);
80
+ if (!permission.constrained)
81
+ return { model: opts.model, constrained: false };
82
+ if (!permission.allowOverride)
83
+ return { model: permission.defaultModel || opts.model, constrained: true };
84
+ if (!opts.model)
85
+ return { model: permission.defaultModel, constrained: true };
86
+ if (isModelAllowedForRoleBaseagent(opts.role, opts.baseagent, opts.model, opts.resolveModelId, opts.selfAid)) {
87
+ return { model: opts.model, constrained: false };
88
+ }
89
+ return { model: permission.defaultModel || opts.model, constrained: true };
90
+ }