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,159 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { atomicReadJson, atomicWriteJson } from '../utils/atomic-write.js';
4
+ import { agentRoleConfig, agentRolesIndex, kitsRoleTemplatesDir, resolvePaths } from '../paths.js';
5
+ import { loadSchema } from './schema-registry.js';
6
+ import { clearRoleStoreCache, getStoredRoleRegistry, isRoleName, readRoleStore, roleRegistryExists, } from './role-store.js';
7
+ import { isManagementRole } from './builtin-roles.js';
8
+ import { validateRoleConfig } from './role-schema.js';
9
+ export class RoleServiceError extends Error {
10
+ code;
11
+ data;
12
+ constructor(code, message, data) {
13
+ super(message);
14
+ this.code = code;
15
+ this.data = data;
16
+ this.name = 'RoleServiceError';
17
+ }
18
+ }
19
+ export function readRoleRegistry(selfAid) {
20
+ return getStoredRoleRegistry(selfAid);
21
+ }
22
+ export function readRoleDefinition(selfAid, roleId) {
23
+ return readRoleStore(selfAid).definitions[roleId] ?? null;
24
+ }
25
+ export function ensureInitialRoleRegistry(selfAid) {
26
+ if (roleRegistryExists(selfAid))
27
+ return false;
28
+ writeRoleRegistry(selfAid, {
29
+ $schema_version: 1,
30
+ roles: ['owner', 'admin', 'member', 'visitor'],
31
+ defaultRoles: { private: null, group: null },
32
+ });
33
+ return true;
34
+ }
35
+ export function writeRoleRegistry(selfAid, value) {
36
+ validateRegistry(selfAid, value);
37
+ const file = agentRolesIndex(selfAid);
38
+ fs.mkdirSync(path.dirname(file), { recursive: true });
39
+ atomicWriteJson(file, { ...value, $schema_version: 1 });
40
+ clearRoleStoreCache(selfAid);
41
+ }
42
+ export function writeRoleDefinition(selfAid, roleId, value) {
43
+ if (!isRoleName(roleId))
44
+ throw new RoleServiceError('INVALID_ROLE_ID', `Invalid role id: ${roleId}`);
45
+ const normalized = { ...value, $schema_version: 1 };
46
+ validateRoleDefinition(normalized);
47
+ const registry = readRoleRegistry(selfAid);
48
+ if (!registry.roles.includes(roleId)) {
49
+ throw new RoleServiceError('ROLE_NOT_REGISTERED', `Role ${roleId} must be registered before writing its definition`);
50
+ }
51
+ const file = agentRoleConfig(selfAid, roleId);
52
+ fs.mkdirSync(path.dirname(file), { recursive: true });
53
+ atomicWriteJson(file, normalized);
54
+ clearRoleStoreCache(selfAid);
55
+ }
56
+ export function registerRole(selfAid, roleId, definition) {
57
+ if (!isRoleName(roleId))
58
+ throw new RoleServiceError('INVALID_ROLE_ID', `Invalid role id: ${roleId}`);
59
+ const registry = readRoleRegistry(selfAid);
60
+ if (registry.roles.includes(roleId))
61
+ throw new RoleServiceError('ROLE_EXISTS', `Role ${roleId} is already registered`);
62
+ writeRoleDefinitionFile(selfAid, roleId, definition);
63
+ try {
64
+ writeRoleRegistry(selfAid, { ...registry, roles: [...registry.roles, roleId] });
65
+ }
66
+ catch (error) {
67
+ try {
68
+ fs.rmSync(agentRoleConfig(selfAid, roleId), { force: true });
69
+ }
70
+ catch { }
71
+ throw error;
72
+ }
73
+ }
74
+ export function unregisterRole(selfAid, roleId) {
75
+ if (roleId === 'visitor' || isManagementRole(roleId)) {
76
+ throw new RoleServiceError('ROLE_PROTECTED', `Role ${roleId} cannot be unregistered`);
77
+ }
78
+ const registry = readRoleRegistry(selfAid);
79
+ if (!registry.roles.includes(roleId))
80
+ return;
81
+ const references = findRoleReferences(selfAid, roleId, registry);
82
+ if (references.length)
83
+ throw new RoleServiceError('ROLE_IN_USE', `Role ${roleId} is still referenced`, { references });
84
+ writeRoleRegistry(selfAid, { ...registry, roles: registry.roles.filter(item => item !== roleId) });
85
+ try {
86
+ fs.rmSync(agentRoleConfig(selfAid, roleId), { force: true });
87
+ }
88
+ catch { }
89
+ clearRoleStoreCache(selfAid);
90
+ }
91
+ export function listRoleDiagnostics(selfAid) {
92
+ return readRoleStore(selfAid).diagnostics;
93
+ }
94
+ function writeRoleDefinitionFile(selfAid, roleId, value) {
95
+ const normalized = { ...value, $schema_version: 1 };
96
+ validateRoleDefinition(normalized);
97
+ const file = agentRoleConfig(selfAid, roleId);
98
+ fs.mkdirSync(path.dirname(file), { recursive: true });
99
+ atomicWriteJson(file, normalized);
100
+ }
101
+ function validateRegistry(selfAid, value) {
102
+ const normalized = { ...value, $schema_version: 1 };
103
+ const schema = loadSchema('role-registry');
104
+ if (!schema.validate(normalized))
105
+ throw new RoleServiceError('SCHEMA_INVALID', formatSchemaErrors(schema.validate.errors));
106
+ const registry = normalized;
107
+ if (!registry.roles.includes('visitor'))
108
+ throw new RoleServiceError('VISITOR_REQUIRED', 'visitor must remain registered');
109
+ for (const role of ['private', 'group']) {
110
+ const selected = registry.defaultRoles[role];
111
+ if (selected !== null && (isManagementRole(selected) || !registry.roles.includes(selected))) {
112
+ throw new RoleServiceError('INVALID_DEFAULT_ROLE', `defaultRoles.${role} must reference an assignable registered role`);
113
+ }
114
+ if (selected !== null && !hasValidRoleDefinition(selfAid, selected)) {
115
+ throw new RoleServiceError('INVALID_DEFAULT_ROLE', `defaultRoles.${role} must reference a valid role definition`);
116
+ }
117
+ }
118
+ }
119
+ function hasValidRoleDefinition(selfAid, roleId) {
120
+ for (const file of [
121
+ agentRoleConfig(selfAid, roleId),
122
+ path.join(kitsRoleTemplatesDir(), `${roleId}.json`),
123
+ ]) {
124
+ try {
125
+ const value = atomicReadJson(file);
126
+ if (value !== null && validateRoleConfig(value).length === 0)
127
+ return true;
128
+ }
129
+ catch { }
130
+ }
131
+ return false;
132
+ }
133
+ function validateRoleDefinition(value) {
134
+ const issues = validateRoleConfig(value);
135
+ if (issues.length) {
136
+ throw new RoleServiceError('SCHEMA_INVALID', issues.map(issue => `${issue.field} ${issue.message}`).join('; '), { issues });
137
+ }
138
+ }
139
+ function findRoleReferences(selfAid, roleId, registry) {
140
+ const references = [];
141
+ if (registry.defaultRoles.private === roleId)
142
+ references.push('defaultRoles.private');
143
+ if (registry.defaultRoles.group === roleId)
144
+ references.push('defaultRoles.group');
145
+ const relations = path.join(resolvePaths().agentsDir, selfAid, 'relations');
146
+ if (!fs.existsSync(relations))
147
+ return references;
148
+ for (const entry of fs.readdirSync(relations, { withFileTypes: true })) {
149
+ if (!entry.isDirectory())
150
+ continue;
151
+ const config = atomicReadJson(path.join(relations, entry.name, 'config.json'));
152
+ if (config?.role === roleId)
153
+ references.push(`relations/${entry.name}/role`);
154
+ }
155
+ return references;
156
+ }
157
+ function formatSchemaErrors(errors) {
158
+ return (errors ?? []).map(error => `${error.instancePath || '/'} ${error.message}`).join('; ');
159
+ }
@@ -0,0 +1,204 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { atomicReadJson } from '../utils/atomic-write.js';
4
+ import { agentRoleConfig, agentRolesDir, agentRolesIndex, kitsRoleTemplatesDir, resolvePaths, } from '../paths.js';
5
+ import { applyManagementRoleCeiling, getManagementRoleDefinition } from './builtin-roles.js';
6
+ import { readBuiltinRoleTemplate } from './builtin-role-templates.js';
7
+ import { loadSchema } from './schema-registry.js';
8
+ import { validateRoleConfig } from './role-schema.js';
9
+ const ROLE_NAME_RE = /^[a-z0-9_-]+$/;
10
+ const RESERVED_ROLES = new Set(['owner', 'admin']);
11
+ const BUILTIN_ROLES = ['owner', 'admin', 'member', 'visitor'];
12
+ const cache = new Map();
13
+ export function clearRoleStoreCache(selfAid) {
14
+ if (selfAid)
15
+ cache.delete(selfAid);
16
+ else
17
+ cache.clear();
18
+ }
19
+ export function roleRegistryPath(selfAid) {
20
+ return agentRolesIndex(selfAid);
21
+ }
22
+ export function roleRegistryExists(selfAid) {
23
+ return fs.existsSync(agentRolesIndex(selfAid));
24
+ }
25
+ export function roleConfigPath(selfAid, roleId) {
26
+ return agentRoleConfig(selfAid, roleId);
27
+ }
28
+ export function isRoleName(value) {
29
+ return typeof value === 'string' && ROLE_NAME_RE.test(value);
30
+ }
31
+ export function readRoleStore(selfAid) {
32
+ const signature = storeSignature(selfAid);
33
+ const cached = cache.get(selfAid);
34
+ if (cached?.signature === signature)
35
+ return cached.snapshot;
36
+ const diagnostics = [];
37
+ const registry = readRegistry(selfAid, diagnostics);
38
+ const definitions = {};
39
+ for (const roleId of registry.roles) {
40
+ const definition = readDefinition(selfAid, roleId, diagnostics);
41
+ if (definition)
42
+ definitions[roleId] = definition;
43
+ }
44
+ const rolesDir = agentRolesDir(selfAid);
45
+ if (fs.existsSync(rolesDir)) {
46
+ for (const entry of fs.readdirSync(rolesDir, { withFileTypes: true })) {
47
+ if (!entry.isFile() || !entry.name.endsWith('.json') || entry.name === 'index.json')
48
+ continue;
49
+ const roleId = entry.name.slice(0, -'.json'.length);
50
+ if (!registry.roles.includes(roleId)) {
51
+ diagnostics.push({
52
+ code: 'UNREGISTERED_ROLE_FILE',
53
+ roleId,
54
+ file: path.relative(resolvePaths().root, path.join(rolesDir, entry.name)),
55
+ message: `Role file ${entry.name} is not registered in roles/index.json`,
56
+ });
57
+ }
58
+ }
59
+ }
60
+ const snapshot = { registry, definitions, diagnostics };
61
+ cache.set(selfAid, { signature, snapshot });
62
+ return snapshot;
63
+ }
64
+ export function getStoredRoleDefinition(selfAid, roleId) {
65
+ if (!selfAid) {
66
+ if (roleId === 'owner' || roleId === 'admin')
67
+ return getManagementRoleDefinition(roleId);
68
+ return readBuiltinRoleTemplate(roleId).definition;
69
+ }
70
+ const snapshot = readRoleStore(selfAid);
71
+ if (roleId === 'owner' || roleId === 'admin') {
72
+ return applyManagementRoleCeiling(roleId, snapshot.definitions[roleId]);
73
+ }
74
+ if (!snapshot.registry.roles.includes(roleId))
75
+ return null;
76
+ return snapshot.definitions[roleId] ?? null;
77
+ }
78
+ export function getStoredRoleRegistry(selfAid) {
79
+ return readRoleStore(selfAid).registry;
80
+ }
81
+ export function roleIsAssignable(selfAid, roleId) {
82
+ if (!roleId || !isRoleName(roleId) || RESERVED_ROLES.has(roleId))
83
+ return false;
84
+ const snapshot = readRoleStore(selfAid);
85
+ return snapshot.registry.roles.includes(roleId) && !!snapshot.definitions[roleId];
86
+ }
87
+ function readRegistry(selfAid, diagnostics) {
88
+ const file = agentRolesIndex(selfAid);
89
+ let raw;
90
+ try {
91
+ raw = atomicReadJson(file);
92
+ }
93
+ catch (error) {
94
+ diagnostics.push({
95
+ code: 'INVALID_ROLE_REGISTRY',
96
+ file: path.relative(resolvePaths().root, file),
97
+ message: `Role registry contains invalid JSON: ${error instanceof Error ? error.message : String(error)}`,
98
+ });
99
+ return fallbackRegistry();
100
+ }
101
+ if (raw !== null) {
102
+ const schema = loadSchema('role-registry');
103
+ if (schema.validate(raw)) {
104
+ const value = raw;
105
+ const roles = [...new Set(value.roles)];
106
+ const defaultRoles = { ...value.defaultRoles };
107
+ for (const scene of ['private', 'group']) {
108
+ const selected = defaultRoles[scene];
109
+ if (selected === null || (roles.includes(selected) && !RESERVED_ROLES.has(selected)))
110
+ continue;
111
+ diagnostics.push({
112
+ code: 'INVALID_ROLE_ASSIGNMENT',
113
+ roleId: selected,
114
+ file: path.relative(resolvePaths().root, file),
115
+ message: `defaultRoles.${scene} references invalid or unregistered role ${selected}; using visitor fallback`,
116
+ });
117
+ defaultRoles[scene] = null;
118
+ }
119
+ return { ...value, roles, defaultRoles };
120
+ }
121
+ diagnostics.push({
122
+ code: 'INVALID_ROLE_REGISTRY',
123
+ file: path.relative(resolvePaths().root, file),
124
+ message: 'Role registry failed schema or cross-file validation',
125
+ });
126
+ return fallbackRegistry();
127
+ }
128
+ diagnostics.push({
129
+ code: 'MISSING_ROLE_REGISTRY',
130
+ file: path.relative(resolvePaths().root, file),
131
+ message: 'roles/index.json is required; run the v5 role configuration migration before serving runtime traffic',
132
+ });
133
+ return fallbackRegistry();
134
+ }
135
+ function fallbackRegistry() {
136
+ return { $schema_version: 1, roles: [...BUILTIN_ROLES], defaultRoles: { private: null, group: null } };
137
+ }
138
+ function readDefinition(selfAid, roleId, diagnostics) {
139
+ const local = agentRoleConfig(selfAid, roleId);
140
+ for (const file of [local]) {
141
+ let raw;
142
+ try {
143
+ raw = atomicReadJson(file);
144
+ }
145
+ catch (error) {
146
+ diagnostics.push({
147
+ code: 'INVALID_ROLE_FILE',
148
+ roleId,
149
+ file: path.relative(resolvePaths().root, file),
150
+ message: `Role definition ${roleId} contains invalid JSON: ${error instanceof Error ? error.message : String(error)}`,
151
+ });
152
+ continue;
153
+ }
154
+ if (raw === null)
155
+ continue;
156
+ const issues = validateRoleConfig(raw);
157
+ if (issues.length === 0) {
158
+ const { $schema_version: _schemaVersion, ...definition } = raw;
159
+ return definition;
160
+ }
161
+ diagnostics.push({
162
+ code: file === local ? 'INVALID_ROLE_FILE' : 'INVALID_ROLE_FILE',
163
+ roleId,
164
+ file: path.relative(resolvePaths().root, file),
165
+ message: `Role definition ${roleId} failed validation: ${issues.map(issue => `${issue.field} ${issue.message}`).join('; ')}`,
166
+ });
167
+ }
168
+ const builtin = readBuiltinRoleTemplate(roleId);
169
+ if (builtin.definition)
170
+ return builtin.definition;
171
+ if (builtin.error && ['owner', 'admin', 'member', 'visitor'].includes(roleId)) {
172
+ diagnostics.push({
173
+ code: 'INVALID_ROLE_FILE',
174
+ roleId,
175
+ file: path.relative(resolvePaths().root, builtin.file),
176
+ message: `Role definition ${roleId} ${builtin.error}`,
177
+ });
178
+ }
179
+ diagnostics.push({ code: 'MISSING_ROLE_FILE', roleId, message: `No valid definition found for role ${roleId}` });
180
+ return null;
181
+ }
182
+ function storeSignature(selfAid) {
183
+ const files = [agentRolesIndex(selfAid)];
184
+ const dir = agentRolesDir(selfAid);
185
+ if (fs.existsSync(dir)) {
186
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
187
+ if (entry.isFile() && entry.name.endsWith('.json'))
188
+ files.push(path.join(dir, entry.name));
189
+ }
190
+ }
191
+ for (const roleId of ['owner', 'admin', 'member', 'visitor']) {
192
+ const template = path.join(kitsRoleTemplatesDir(), `${roleId}.json`);
193
+ files.push(template);
194
+ }
195
+ return files.map(file => {
196
+ try {
197
+ const stat = fs.statSync(file);
198
+ return `${file}:${stat.size}:${stat.mtimeMs}:${stat.ctimeMs}`;
199
+ }
200
+ catch {
201
+ return `${file}:missing`;
202
+ }
203
+ }).join('|');
204
+ }
@@ -0,0 +1,55 @@
1
+ import { getBuiltinRolesConfig, getManagementRoleDefinition, isManagementRole, isReservedRoleName, } from './builtin-roles.js';
2
+ import { clearRoleStoreCache, getStoredRoleDefinition, readRoleStore, roleIsAssignable, } from './role-store.js';
3
+ const ROLE_NAME_RE = /^[a-z0-9_-]+$/;
4
+ export { getBuiltinRolesConfig };
5
+ export function readRolesConfig(selfAid) {
6
+ const builtin = getBuiltinRolesConfig();
7
+ const stored = selfAid ? readRoleStore(selfAid) : null;
8
+ return {
9
+ $schema_version: builtin.$schema_version,
10
+ defaultRoles: stored?.registry.defaultRoles ?? builtin.defaultRoles,
11
+ roles: stored?.definitions ?? builtin.roles,
12
+ };
13
+ }
14
+ export function getRoleDefinition(role, selfAid) {
15
+ if (selfAid)
16
+ return getStoredRoleDefinition(selfAid, role);
17
+ if (isManagementRole(role))
18
+ return getManagementRoleDefinition(role);
19
+ const config = readRolesConfig();
20
+ return config.roles[role] || null;
21
+ }
22
+ export function getUserRoleDefinition(role, selfAid) {
23
+ if (isReservedRoleName(role))
24
+ return null;
25
+ const config = readRolesConfig(selfAid);
26
+ return config.roles[role] || null;
27
+ }
28
+ export function getFieldPermission(role, field, selfAid) {
29
+ const roleDef = getRoleDefinition(role, selfAid);
30
+ return roleDef?.permissions[field] ?? null;
31
+ }
32
+ export function getCommandPermissions(role, selfAid) {
33
+ const roleDef = getRoleDefinition(role, selfAid);
34
+ return roleDef?.commandPermissions || {};
35
+ }
36
+ export function clearRolesCache() {
37
+ clearRoleStoreCache();
38
+ }
39
+ export function isValidUserRoleName(role) {
40
+ return typeof role === 'string'
41
+ && ROLE_NAME_RE.test(role)
42
+ && !isReservedRoleName(role);
43
+ }
44
+ export function roleExists(role, selfAid) {
45
+ if (!role || !isValidUserRoleName(role))
46
+ return false;
47
+ return selfAid ? roleIsAssignable(selfAid, role) : !!getUserRoleDefinition(role);
48
+ }
49
+ export function normalizeDefaultRole(value, selfAid) {
50
+ if (value === null || value === undefined || value === '')
51
+ return null;
52
+ if (typeof value !== 'string')
53
+ return null;
54
+ return roleExists(value, selfAid) ? value : null;
55
+ }
@@ -0,0 +1,154 @@
1
+ /**
2
+ * schema-registry —— 配置 schema 的唯一加载入口(SSOT)。
3
+ *
4
+ * schema 文件位于 kits/schemas/,命名 `{逻辑名}.schema.{版本}.json`,随 npm 包分发。
5
+ * 每个字段带:
6
+ * - 标准 JSON Schema `type`(ajv 校验用)
7
+ * - `x-merge`:合并语义(scalar / list / dict),覆盖链类型驱动合并的依据
8
+ * schema 根带:
9
+ * - `x-logical-name` / `x-scope`
10
+ *
11
+ * v3 设计:所有参数统一在 config.json,按作用域路由到对应层级。
12
+ */
13
+ import fs from 'fs';
14
+ import path from 'path';
15
+ import Ajv from 'ajv';
16
+ import { kitsSchemasDir } from '../paths.js';
17
+ let _ajv = null;
18
+ function ajv() {
19
+ if (!_ajv) {
20
+ _ajv = new Ajv({ allErrors: true, strict: false, useDefaults: false });
21
+ }
22
+ return _ajv;
23
+ }
24
+ let _meta = null;
25
+ export function loadMeta() {
26
+ if (_meta)
27
+ return _meta;
28
+ const p = path.join(kitsSchemasDir(), '_meta.json');
29
+ _meta = JSON.parse(fs.readFileSync(p, 'utf-8'));
30
+ return _meta;
31
+ }
32
+ /** 某 schema 的当前版本号(来自 _meta.json)。 */
33
+ export function currentVersion(name) {
34
+ const meta = loadMeta();
35
+ const v = meta.schemas[name]?.currentVersion;
36
+ if (typeof v !== 'number')
37
+ throw new Error(`[schema] no currentVersion for "${name}" in _meta.json`);
38
+ return v;
39
+ }
40
+ /** 全部逻辑 schema 名(来自 _meta.json,键序)。 */
41
+ export function listSchemaNames() {
42
+ return Object.keys(loadMeta().schemas);
43
+ }
44
+ /** 是否为已知逻辑 schema 名。 */
45
+ export function isSchemaName(name) {
46
+ return Object.prototype.hasOwnProperty.call(loadMeta().schemas, name);
47
+ }
48
+ /**
49
+ * 顶层字段的 schema 声明默认值(`default` 关键字),无则 undefined。
50
+ * 仅覆盖顶层标量字段——嵌套路径的默认值不在此解析。
51
+ * 用于 `ec config get` 在各存储层都无值时回落展示出厂默认。
52
+ */
53
+ export function fieldSchemaDefault(name, fieldPath) {
54
+ if (fieldPath.includes('.'))
55
+ return undefined;
56
+ const spec = loadSchema(name).fields.get(fieldPath);
57
+ return spec && 'default' in spec ? spec.default : undefined;
58
+ }
59
+ /** 某版本的 history 元信息(date/description),无则 undefined。 */
60
+ export function schemaHistoryEntry(name, version) {
61
+ const entry = loadMeta().history.find(h => h.schema === name && h.version === version);
62
+ return entry ? { date: entry.date, description: entry.description } : undefined;
63
+ }
64
+ /** 扫描 kits/schemas/ 得到某 schema 磁盘上存在的版本号(升序)。 */
65
+ export function listSchemaVersions(name) {
66
+ const prefix = `${name}.schema.`;
67
+ const suffix = '.json';
68
+ const versions = [];
69
+ for (const file of fs.readdirSync(kitsSchemasDir())) {
70
+ if (!file.startsWith(prefix) || !file.endsWith(suffix))
71
+ continue;
72
+ const middle = file.slice(prefix.length, file.length - suffix.length);
73
+ if (!/^\d+$/.test(middle))
74
+ continue;
75
+ versions.push(Number(middle));
76
+ }
77
+ return versions.sort((a, b) => a - b);
78
+ }
79
+ /** 读指定版本的原始 schema(解析为对象,用于内容展示,不做 ajv 编译)。 */
80
+ export function readRawSchema(name, version) {
81
+ const file = schemaFilePath(name, version);
82
+ if (!fs.existsSync(file)) {
83
+ throw new Error(`[schema] no schema file for "${name}" v${version}`);
84
+ }
85
+ return JSON.parse(fs.readFileSync(file, 'utf-8'));
86
+ }
87
+ const _cache = new Map();
88
+ function schemaFilePath(name, version) {
89
+ return path.join(kitsSchemasDir(), `${name}.schema.${version}.json`);
90
+ }
91
+ function extractFields(raw) {
92
+ const fields = new Map();
93
+ const props = raw.properties || {};
94
+ for (const [name, spec] of Object.entries(props)) {
95
+ if (name === '$schema_version')
96
+ continue;
97
+ const merge = spec['x-merge'] || inferMerge(spec);
98
+ fields.set(name, {
99
+ name,
100
+ type: spec.type,
101
+ merge,
102
+ enum: spec.enum,
103
+ default: spec.default,
104
+ description: spec.description,
105
+ });
106
+ }
107
+ return fields;
108
+ }
109
+ /** 无 x-merge 时按 JSON Schema type 兜底推断合并语义。 */
110
+ function inferMerge(spec) {
111
+ if (spec.type === 'array')
112
+ return 'list';
113
+ if (spec.type === 'object')
114
+ return 'dict';
115
+ return 'scalar';
116
+ }
117
+ /** 加载指定版本的 schema entry(带 ajv 编译 + 字段表)。 */
118
+ export function loadSchema(name, version) {
119
+ const ver = version ?? currentVersion(name);
120
+ const key = `${name}@${ver}`;
121
+ const cached = _cache.get(key);
122
+ if (cached)
123
+ return cached;
124
+ const file = schemaFilePath(name, ver);
125
+ const raw = JSON.parse(fs.readFileSync(file, 'utf-8'));
126
+ const entry = {
127
+ logicalName: name,
128
+ version: ver,
129
+ scope: raw['x-scope'] || name,
130
+ raw,
131
+ validate: ajv().compile(raw),
132
+ fields: extractFields(raw),
133
+ };
134
+ _cache.set(key, entry);
135
+ return entry;
136
+ }
137
+ /** extra_backup 不得指向 .env(构建期校验)——遍历声明里的 pattern。 */
138
+ export function assertExtraBackupNotEnv(extraBackup) {
139
+ if (!extraBackup)
140
+ return;
141
+ for (const e of extraBackup) {
142
+ const pat = (e.pattern || '').toLowerCase();
143
+ const pth = (e.path || '').toLowerCase();
144
+ if (pat.includes('.env') || pth.endsWith('.env') || pat === '*') {
145
+ throw new Error(`[schema] extra_backup 不得指向 .env: ${JSON.stringify(e)}`);
146
+ }
147
+ }
148
+ }
149
+ /** 测试用:清空缓存。 */
150
+ export function _resetSchemaCache() {
151
+ _cache.clear();
152
+ _meta = null;
153
+ _ajv = null;
154
+ }