evolcore 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (379) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/LICENSE +21 -0
  3. package/MIGRATION-0.5.0.md +378 -0
  4. package/README.md +318 -14
  5. package/ROLE_ACCESS_CONTROL.md +174 -0
  6. package/assets/.env.template +4 -0
  7. package/bin/ec-safe-output.js +161 -0
  8. package/bin/ec.js +29 -0
  9. package/dist/agents/baseagent.js +163 -0
  10. package/dist/agents/claude-runner.js +2565 -0
  11. package/dist/agents/codex-app-server-client.js +448 -0
  12. package/dist/agents/codex-runner.js +2682 -0
  13. package/dist/agents/gemini-runner.js +666 -0
  14. package/dist/agents/runner-types.js +75 -0
  15. package/dist/aun/aid/agentmd.js +216 -0
  16. package/dist/aun/aid/client.js +132 -0
  17. package/dist/aun/aid/control-aid.js +91 -0
  18. package/dist/aun/aid/identity.js +518 -0
  19. package/dist/aun/aid/index.js +4 -0
  20. package/dist/aun/aid/store.js +74 -0
  21. package/dist/aun/aid/types.js +1 -0
  22. package/dist/aun/aid/validation.js +21 -0
  23. package/dist/aun/group-identity.js +10 -0
  24. package/dist/aun/msg/group-index.js +6 -0
  25. package/dist/aun/msg/group.js +1231 -0
  26. package/dist/aun/msg/history.js +123 -0
  27. package/dist/aun/msg/index.js +5 -0
  28. package/dist/aun/msg/p2p.js +393 -0
  29. package/dist/aun/msg/payload-type.js +27 -0
  30. package/dist/aun/msg/upload.js +137 -0
  31. package/dist/aun/outbox.js +168 -0
  32. package/dist/aun/rpc/caller.js +42 -0
  33. package/dist/aun/rpc/connection.js +25 -0
  34. package/dist/aun/rpc/index.js +2 -0
  35. package/dist/aun/service-proxy.js +225 -0
  36. package/dist/aun/storage/download.js +29 -0
  37. package/dist/aun/storage/index.js +3 -0
  38. package/dist/aun/storage/manage.js +10 -0
  39. package/dist/aun/storage/upload.js +68 -0
  40. package/dist/channels/aun.js +4164 -0
  41. package/dist/channels/contact-bind-code.js +134 -0
  42. package/dist/channels/daemon.js +422 -0
  43. package/dist/channels/dingtalk.js +1479 -0
  44. package/dist/channels/feishu.js +1865 -0
  45. package/dist/channels/qqbot.js +409 -0
  46. package/dist/channels/wechat.js +817 -0
  47. package/dist/channels/wecom-card.js +101 -0
  48. package/dist/channels/wecom-onboarding.js +82 -0
  49. package/dist/channels/wecom-state.js +191 -0
  50. package/dist/channels/wecom.js +1157 -0
  51. package/dist/cli/agent-command.js +642 -0
  52. package/dist/cli/agent.js +1059 -0
  53. package/dist/cli/aun-commands.js +2003 -0
  54. package/dist/cli/bench.js +1228 -0
  55. package/dist/cli/cli-argv.js +66 -0
  56. package/dist/cli/code-stats.js +329 -0
  57. package/dist/cli/command-log.js +82 -0
  58. package/dist/cli/config-selector.js +69 -0
  59. package/dist/cli/config.js +261 -0
  60. package/dist/cli/contact.js +71 -0
  61. package/dist/cli/ctl-command.js +62 -0
  62. package/dist/cli/daemon-commands.js +2750 -0
  63. package/dist/cli/fs-command.js +1447 -0
  64. package/dist/cli/handoff-command.js +302 -0
  65. package/dist/cli/help.js +35 -0
  66. package/dist/cli/index.js +374 -0
  67. package/dist/cli/init-channel.js +1372 -0
  68. package/dist/cli/init.js +590 -0
  69. package/dist/cli/link-rules.js +240 -0
  70. package/dist/cli/model.js +591 -0
  71. package/dist/cli/net-check.js +723 -0
  72. package/dist/cli/queue-command.js +150 -0
  73. package/dist/cli/raw-key-input.js +25 -0
  74. package/dist/cli/response.js +344 -0
  75. package/dist/cli/restart-monitor.js +480 -0
  76. package/dist/cli/stats.js +609 -0
  77. package/dist/cli/task-context.js +80 -0
  78. package/dist/cli/trigger-command.js +545 -0
  79. package/dist/cli/version.js +93 -0
  80. package/dist/cli/watch-logs.js +33 -0
  81. package/dist/cli/watch-msg.js +673 -0
  82. package/dist/config/boot-log.js +266 -0
  83. package/dist/config/builtin-role-templates.js +42 -0
  84. package/dist/config/builtin-roles.js +91 -0
  85. package/dist/config/config-batch-get.js +11 -0
  86. package/dist/config/config-field-policy.js +261 -0
  87. package/dist/config/config-manager.js +1120 -0
  88. package/dist/config/config-operation-service.js +384 -0
  89. package/dist/config/contact-alias.js +68 -0
  90. package/dist/config/contact-book-store.js +454 -0
  91. package/dist/config/contact-book-v2-startup.js +35 -0
  92. package/dist/config/contact-book.js +224 -0
  93. package/dist/config/contact-operation-service.js +110 -0
  94. package/dist/config/gateway-config.js +858 -0
  95. package/dist/config/lifecycle.js +17 -0
  96. package/dist/config/mention-mode.js +27 -0
  97. package/dist/config/merge.js +161 -0
  98. package/dist/config/owner-policy.js +4 -0
  99. package/dist/config/peer-role-resolver.js +218 -0
  100. package/dist/config/resolved-config-op.js +483 -0
  101. package/dist/config/role-config-v4-startup.js +32 -0
  102. package/dist/config/role-config-v5-startup.js +27 -0
  103. package/dist/config/role-ranks.js +18 -0
  104. package/dist/config/role-schema.js +105 -0
  105. package/dist/config/role-service.js +156 -0
  106. package/dist/config/role-store.js +215 -0
  107. package/dist/config/roles.js +64 -0
  108. package/dist/config/schema-registry.js +154 -0
  109. package/dist/config/snapshot.js +598 -0
  110. package/dist/config-store.js +501 -0
  111. package/dist/core/auth/agent-delegation.js +111 -0
  112. package/dist/core/auth/auth-gateway.js +166 -0
  113. package/dist/core/auth/authenticated-actor.js +6 -0
  114. package/dist/core/auth/authorization-audit.js +119 -0
  115. package/dist/core/auth/operation-authorizer.js +720 -0
  116. package/dist/core/auth/operation-catalog.js +731 -0
  117. package/dist/core/baseagent-loader.js +54 -0
  118. package/dist/core/bootstrap-service.js +175 -0
  119. package/dist/core/capability/capability-manager.js +316 -0
  120. package/dist/core/capability/providers/claude-capability-provider.js +176 -0
  121. package/dist/core/capability/providers/codex-capability-provider.js +148 -0
  122. package/dist/core/capability/providers/gemini-capability-provider.js +10 -0
  123. package/dist/core/capability/types.js +27 -0
  124. package/dist/core/causation/audit.js +103 -0
  125. package/dist/core/causation/aun-association.js +111 -0
  126. package/dist/core/causation/context.js +93 -0
  127. package/dist/core/causation/index.js +4 -0
  128. package/dist/core/causation/types.js +2 -0
  129. package/dist/core/channel-loader.js +277 -0
  130. package/dist/core/command/agent-control.js +616 -0
  131. package/dist/core/command/cli-intent-parser.js +225 -0
  132. package/dist/core/command/command-handler.js +1733 -0
  133. package/dist/core/command/connect-menu.js +374 -0
  134. package/dist/core/command/menu-handler.js +3452 -0
  135. package/dist/core/command/menu-protocol.js +247 -0
  136. package/dist/core/command/role-menu.js +1623 -0
  137. package/dist/core/command/slash-gate.js +148 -0
  138. package/dist/core/command/slash-handler.js +3066 -0
  139. package/dist/core/daemon-file-cache.js +222 -0
  140. package/dist/core/event-bus.js +32 -0
  141. package/dist/core/event-catalog.js +810 -0
  142. package/dist/core/evolagent-registry.js +545 -0
  143. package/dist/core/evolagent.js +342 -0
  144. package/dist/core/handoff/dispatcher.js +229 -0
  145. package/dist/core/handoff/mutex.js +46 -0
  146. package/dist/core/handoff/runtime.js +324 -0
  147. package/dist/core/handoff/store.js +537 -0
  148. package/dist/core/handoff/types.js +12 -0
  149. package/dist/core/inference/text-inference.js +173 -0
  150. package/dist/core/interaction-registration.js +10 -0
  151. package/dist/core/interaction-router.js +278 -0
  152. package/dist/core/message/create-status.js +67 -0
  153. package/dist/core/message/im-renderer.js +659 -0
  154. package/dist/core/message/items-formatter.js +76 -0
  155. package/dist/core/message/logical-queue-bridge.js +123 -0
  156. package/dist/core/message/message-bridge.js +874 -0
  157. package/dist/core/message/message-cache.js +56 -0
  158. package/dist/core/message/message-log.js +339 -0
  159. package/dist/core/message/message-processor.js +4 -0
  160. package/dist/core/message/message-queue.js +1446 -0
  161. package/dist/core/message/message-utils.js +76 -0
  162. package/dist/core/message/peer-mode.js +105 -0
  163. package/dist/core/message/pending-hints.js +232 -0
  164. package/dist/core/message/response-depth.js +33 -0
  165. package/dist/core/message/response-engine.js +4020 -0
  166. package/dist/core/message/response-snapshot.js +83 -0
  167. package/dist/core/message/send-receipt.js +24 -0
  168. package/dist/core/message/stream-debouncer.js +139 -0
  169. package/dist/core/message/stream-idle-monitor.js +124 -0
  170. package/dist/core/model/config-scope.js +162 -0
  171. package/dist/core/model/field-scope.js +78 -0
  172. package/dist/core/model/model-catalog.js +227 -0
  173. package/dist/core/model/model-diagnostics.js +182 -0
  174. package/dist/core/model/model-permission.js +90 -0
  175. package/dist/core/permission/approval-gateway.js +1017 -0
  176. package/dist/core/permission/ec-command-parser.js +347 -0
  177. package/dist/core/permission/execution-sandbox.js +16 -0
  178. package/dist/core/permission/index.js +6 -0
  179. package/dist/core/permission/mode.js +24 -0
  180. package/dist/core/permission/sandbox-runtime.js +265 -0
  181. package/dist/core/permission/tool-policy.js +1019 -0
  182. package/dist/core/permission/unix-socket-policy.js +99 -0
  183. package/dist/core/protected-paths.js +332 -0
  184. package/dist/core/relation/peer-identity.js +222 -0
  185. package/dist/core/relation/peer-key.js +1 -0
  186. package/dist/core/role/runtime-policy.js +141 -0
  187. package/dist/core/session/adapters/claude-session-file-adapter.js +218 -0
  188. package/dist/core/session/adapters/codex-session-file-adapter.js +333 -0
  189. package/dist/core/session/adapters/gemini-session-file-adapter.js +181 -0
  190. package/dist/core/session/session-file-adapter.js +7 -0
  191. package/dist/core/session/session-file-health.js +45 -0
  192. package/dist/core/session/session-fs-store.js +273 -0
  193. package/dist/core/session/session-key.js +24 -0
  194. package/dist/core/session/session-manager.js +1643 -0
  195. package/dist/core/session/session-mapper.js +100 -0
  196. package/dist/core/session/session-renew.js +314 -0
  197. package/dist/core/session/session-title.js +128 -0
  198. package/dist/core/session/session-turn-coordinator.js +205 -0
  199. package/dist/core/session/session-turns.js +67 -0
  200. package/dist/core/system-channels.js +29 -0
  201. package/dist/eck/baseagent-caps.js +18 -0
  202. package/dist/eck/detect.js +47 -0
  203. package/dist/eck/group-rules-sync.js +345 -0
  204. package/dist/eck/init.js +77 -0
  205. package/dist/eck/kit-renderer.js +359 -0
  206. package/dist/eck/manifest-engine.js +446 -0
  207. package/dist/eck/message-renderer.js +199 -0
  208. package/dist/eck/rules-loader.js +28 -0
  209. package/dist/index.js +2926 -4
  210. package/dist/ipc.js +777 -0
  211. package/dist/paths.js +262 -0
  212. package/dist/product.js +18 -0
  213. package/dist/response-system/context-builder.js +71 -0
  214. package/dist/response-system/coordinator.js +117 -0
  215. package/dist/response-system/decision-executor.js +86 -0
  216. package/dist/response-system/engines/v1/index.js +21 -0
  217. package/dist/response-system/engines/v1/interactive-flow.js +27 -0
  218. package/dist/response-system/engines/v1/proactive-flow.js +137 -0
  219. package/dist/response-system/engines/v1/types.js +1 -0
  220. package/dist/response-system/extensions.js +41 -0
  221. package/dist/response-system/index.js +6 -0
  222. package/dist/response-system/modes/index.js +7 -0
  223. package/dist/response-system/modes/single-session/index.js +72 -0
  224. package/dist/response-system/queues/fifo-queue.js +44 -0
  225. package/dist/response-system/queues/index.js +6 -0
  226. package/dist/response-system/queues/lifo-queue.js +42 -0
  227. package/dist/response-system/queues/priority-queue.js +63 -0
  228. package/dist/response-system/registry.js +97 -0
  229. package/dist/response-system/resolver.js +37 -0
  230. package/dist/response-system/selector.js +23 -0
  231. package/dist/response-system/types.js +7 -0
  232. package/dist/stats/billing.js +163 -0
  233. package/dist/stats/budget.js +93 -0
  234. package/dist/stats/db.js +403 -0
  235. package/dist/stats/eck-vars.js +89 -0
  236. package/dist/stats/index.js +11 -0
  237. package/dist/stats/normalizer.js +80 -0
  238. package/dist/stats/price-resolver.js +138 -0
  239. package/dist/stats/query.js +763 -0
  240. package/dist/stats/role-budget.js +168 -0
  241. package/dist/stats/writer.js +151 -0
  242. package/dist/trigger/anomaly-store.js +258 -0
  243. package/dist/trigger/audit.js +152 -0
  244. package/dist/trigger/event-source.js +119 -0
  245. package/dist/trigger/feedback.js +685 -0
  246. package/dist/trigger/history.js +290 -0
  247. package/dist/trigger/manager.js +294 -0
  248. package/dist/trigger/parser.js +595 -0
  249. package/dist/trigger/patch.js +155 -0
  250. package/dist/trigger/scheduler.js +1602 -0
  251. package/dist/trigger/script-executor.js +155 -0
  252. package/dist/trigger/state.js +145 -0
  253. package/dist/trigger/types.js +1 -0
  254. package/dist/trigger/validation.js +634 -0
  255. package/dist/types.js +12 -0
  256. package/dist/utils/aid-bind.js +313 -0
  257. package/dist/utils/atomic-write.js +95 -0
  258. package/dist/utils/avatar-upload.js +123 -0
  259. package/dist/utils/cross-platform.js +297 -0
  260. package/dist/utils/ecweb-utils.js +73 -0
  261. package/dist/utils/error-dict.json +153 -0
  262. package/dist/utils/error-utils.js +349 -0
  263. package/dist/utils/instance-registry.js +444 -0
  264. package/dist/utils/locale.js +21 -0
  265. package/dist/utils/log-writer.js +270 -0
  266. package/dist/utils/logger.js +89 -0
  267. package/dist/utils/markdown-to-plain-text.js +20 -0
  268. package/dist/utils/media-cache.js +274 -0
  269. package/dist/utils/model-prices.jsonl +20 -0
  270. package/dist/utils/npm-ops.js +210 -0
  271. package/dist/utils/process-introspect.js +133 -0
  272. package/dist/utils/process-tree-stats.js +271 -0
  273. package/dist/utils/project-path.js +74 -0
  274. package/dist/utils/restart-safety.js +31 -0
  275. package/dist/utils/stats.js +410 -0
  276. package/dist/utils/system-memory.js +62 -0
  277. package/dist/utils/tool-summary.js +284 -0
  278. package/dist/utils/welcome.js +268 -0
  279. package/kits/docs/GUIDE.md +20 -0
  280. package/kits/docs/INDEX.md +66 -0
  281. package/kits/docs/aun/CHEATSHEET.md +19 -0
  282. package/kits/docs/aun/SYNC_PROTOCOL.md +15 -0
  283. package/kits/docs/channels/aun.md +65 -0
  284. package/kits/docs/channels/feishu.md +56 -0
  285. package/kits/docs/context-assembly.md +366 -0
  286. package/kits/docs/eck_templates/GUIDE.template.md +22 -0
  287. package/kits/docs/eck_templates/INDEX.template.md +28 -0
  288. package/kits/docs/eck_templates/path-registry.template.md +33 -0
  289. package/kits/docs/eck_templates/runtime.template.md +19 -0
  290. package/kits/docs/evolcore/INDEX.md +68 -0
  291. package/kits/docs/evolcore/agent.md +77 -0
  292. package/kits/docs/evolcore/aid.md +52 -0
  293. package/kits/docs/evolcore/config.md +149 -0
  294. package/kits/docs/evolcore/contact.md +57 -0
  295. package/kits/docs/evolcore/ctl.md +46 -0
  296. package/kits/docs/evolcore/event.md +216 -0
  297. package/kits/docs/evolcore/fs-architecture.md +1215 -0
  298. package/kits/docs/evolcore/fs.md +110 -0
  299. package/kits/docs/evolcore/group-fs.md +17 -0
  300. package/kits/docs/evolcore/group-rules.md +226 -0
  301. package/kits/docs/evolcore/group.md +150 -0
  302. package/kits/docs/evolcore/model.md +50 -0
  303. package/kits/docs/evolcore/msg.md +136 -0
  304. package/kits/docs/evolcore/response.md +75 -0
  305. package/kits/docs/evolcore/rpc.md +37 -0
  306. package/kits/docs/evolcore/self-summary.md +29 -0
  307. package/kits/docs/evolcore/stats.md +83 -0
  308. package/kits/docs/evolcore/storage.md +50 -0
  309. package/kits/docs/evolcore/trigger.md +539 -0
  310. package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -0
  311. package/kits/docs/identity/PATH_OPS.md +16 -0
  312. package/kits/docs/identity/ROLE_DETAIL.md +23 -0
  313. package/kits/docs/identity/identity-tools.md +26 -0
  314. package/kits/docs/path-registry.md +43 -0
  315. package/kits/docs/prompt-loading-architecture.md +266 -0
  316. package/kits/docs/venues/aun-group.md +45 -0
  317. package/kits/docs/venues/aun-private.md +10 -0
  318. package/kits/docs/venues/client-desktop.md +10 -0
  319. package/kits/docs/venues/client-mobile.md +10 -0
  320. package/kits/docs/venues/feishu-group.md +13 -0
  321. package/kits/docs/venues/feishu-private.md +9 -0
  322. package/kits/docs/venues/group.md +25 -0
  323. package/kits/docs/venues/private.md +10 -0
  324. package/kits/eck_manifest.auxiliary.json +43 -0
  325. package/kits/eck_manifest.json +203 -0
  326. package/kits/eck_message_manifest.json +63 -0
  327. package/kits/migrations/README-role-config-v4.md +32 -0
  328. package/kits/migrations/migrate-contact-book-v2.mjs +747 -0
  329. package/kits/migrations/migrate-role-config-v4.mjs +623 -0
  330. package/kits/migrations/migrate-role-config-v5.mjs +346 -0
  331. package/kits/migrations/rename-config-file.mjs +99 -0
  332. package/kits/rules/01-overview.md +142 -0
  333. package/kits/rules/02-navigation.md +76 -0
  334. package/kits/rules/03-identity.md +34 -0
  335. package/kits/rules/04-relation.md +59 -0
  336. package/kits/rules/05-venue.md +44 -0
  337. package/kits/rules/06-channel.md +59 -0
  338. package/kits/schemas/_meta.json +32 -0
  339. package/kits/schemas/agent-config.schema.1.json +177 -0
  340. package/kits/schemas/agent-config.schema.2.json +239 -0
  341. package/kits/schemas/agent-config.schema.3.json +119 -0
  342. package/kits/schemas/agent-config.schema.4.json +208 -0
  343. package/kits/schemas/agent-config.schema.5.json +326 -0
  344. package/kits/schemas/agent-config.schema.6.json +322 -0
  345. package/kits/schemas/contact-book.schema.1.json +36 -0
  346. package/kits/schemas/contact-book.schema.2.json +43 -0
  347. package/kits/schemas/daemon.schema.1.json +90 -0
  348. package/kits/schemas/defaults.schema.1.json +81 -0
  349. package/kits/schemas/menu-exec-schema-commands.md +208 -0
  350. package/kits/schemas/migrations/README.md +28 -0
  351. package/kits/schemas/relation-config.schema.1.json +158 -0
  352. package/kits/schemas/relation-config.schema.2.json +73 -0
  353. package/kits/schemas/relation-config.schema.3.json +50 -0
  354. package/kits/schemas/relation-config.schema.4.json +47 -0
  355. package/kits/schemas/relation-config.schema.5.json +47 -0
  356. package/kits/schemas/role-config.schema.1.json +201 -0
  357. package/kits/schemas/role-registry.schema.1.json +35 -0
  358. package/kits/schemas/single-session.schema.1.json +31 -0
  359. package/kits/templates/bootstrap-welcome.md +15 -0
  360. package/kits/templates/message-fragments/handoff-request-to-target.md +13 -0
  361. package/kits/templates/message-fragments/handoff-response-to-origin.md +10 -0
  362. package/kits/templates/message-fragments/inject-default.md +2 -0
  363. package/kits/templates/message-fragments/item.md +2 -0
  364. package/kits/templates/roles/admin.json +9 -0
  365. package/kits/templates/roles/member.json +41 -0
  366. package/kits/templates/roles/owner.json +9 -0
  367. package/kits/templates/roles/visitor.json +40 -0
  368. package/kits/templates/system-fragments/baseagent.md +14 -0
  369. package/kits/templates/system-fragments/bootstrap.md +16 -0
  370. package/kits/templates/system-fragments/channel.md +48 -0
  371. package/kits/templates/system-fragments/commands.md +28 -0
  372. package/kits/templates/system-fragments/identity.md +11 -0
  373. package/kits/templates/system-fragments/relation.md +19 -0
  374. package/kits/templates/system-fragments/session.md +53 -0
  375. package/kits/templates/system-fragments/venue.md +31 -0
  376. package/package.json +50 -15
  377. package/dist/index.d.ts +0 -7
  378. package/dist/index.d.ts.map +0 -1
  379. package/dist/index.js.map +0 -1
@@ -0,0 +1,75 @@
1
+ export const BASEAGENT_RUNNER_UNAVAILABLE = 'BASEAGENT_RUNNER_UNAVAILABLE';
2
+ export class BaseagentRunnerUnavailableError extends Error {
3
+ evolagentName;
4
+ baseagent;
5
+ availableBaseagents;
6
+ code = BASEAGENT_RUNNER_UNAVAILABLE;
7
+ constructor(evolagentName, baseagent, availableBaseagents) {
8
+ super(`Baseagent runner unavailable: ${evolagentName}::${baseagent}`);
9
+ this.evolagentName = evolagentName;
10
+ this.baseagent = baseagent;
11
+ this.availableBaseagents = availableBaseagents;
12
+ this.name = 'BaseagentRunnerUnavailableError';
13
+ }
14
+ }
15
+ // ── 类型守卫 ──
16
+ export function hasModelSwitcher(agent) {
17
+ return typeof agent.setModel === 'function' && typeof agent.listModels === 'function';
18
+ }
19
+ export function hasPermissionController(agent) {
20
+ return typeof agent.setMode === 'function' && typeof agent.listModes === 'function';
21
+ }
22
+ export function hasCompact(agent) {
23
+ return typeof agent.compact === 'function';
24
+ }
25
+ export function hasClearSession(agent) {
26
+ return typeof agent.clearSession === 'function';
27
+ }
28
+ // ── Token usage helper functions ──
29
+ export function numericToken(value) {
30
+ return typeof value === 'number' && Number.isFinite(value) ? value : 0;
31
+ }
32
+ export function contextTokensForUsage(usage, isClaudeModel) {
33
+ if (!usage)
34
+ return 0;
35
+ if (!isClaudeModel)
36
+ return numericToken(usage.input_tokens);
37
+ return numericToken(usage.input_tokens)
38
+ + numericToken(usage.cache_creation_input_tokens)
39
+ + numericToken(usage.cache_read_input_tokens);
40
+ }
41
+ export function usageForContext(usage) {
42
+ const iterations = Array.isArray(usage?.iterations) ? usage.iterations : undefined;
43
+ const lastIteration = iterations?.slice().reverse().find(it => contextTokensForUsage(it, true) > 0);
44
+ return lastIteration ?? usage;
45
+ }
46
+ /** Models whose base ID uses a 1M context window when sent to the SDK with [1m]. */
47
+ export const ONE_M_CONTEXT_MODEL_PREFIXES = ['claude-opus-4-8', 'claude-sonnet-4-6'];
48
+ const ONE_M_CONTEXT_MODEL_ALIASES = ['opus', 'sonnet'];
49
+ const CLAUDE_CONTEXT_MODEL_ALIASES = ['opus', 'sonnet', 'haiku'];
50
+ export function isClaudeContextUsageModel(model) {
51
+ if (!model)
52
+ return false;
53
+ const baseModel = model.replace(/\[1m\]$/i, '');
54
+ return /^claude-/i.test(baseModel) || CLAUDE_CONTEXT_MODEL_ALIASES.includes(baseModel);
55
+ }
56
+ export function isOneMillionContextModel(model) {
57
+ if (!model)
58
+ return false;
59
+ if (ONE_M_CONTEXT_MODEL_ALIASES.includes(model))
60
+ return true;
61
+ if (/\[1m\]$/i.test(model))
62
+ return true;
63
+ if (/deepseek-v4/i.test(model))
64
+ return true;
65
+ const baseModel = model.replace(/\[1m\]$/i, '');
66
+ return ONE_M_CONTEXT_MODEL_PREFIXES.some(prefix => baseModel === prefix || baseModel.startsWith(`${prefix}-`));
67
+ }
68
+ /** Real context window size: 1M models = 1000000, otherwise 200000. */
69
+ export function realContextWindowForModel(model) {
70
+ return isOneMillionContextModel(model) ? 1000000 : 200000;
71
+ }
72
+ /** autoCompact trigger threshold: 180000 for all models (conservative for cache limits). */
73
+ export function autoCompactWindowForModel(_model) {
74
+ return 180000;
75
+ }
@@ -0,0 +1,216 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { getAidStore, loadAid, SLOT } from './store.js';
4
+ import { agentMdPath, aidLocalDir, resolveRoot } from '../../paths.js';
5
+ export function buildInitialAgentMd(opts) {
6
+ const agentName = opts.aid.split('.')[0];
7
+ const agentType = opts.type || 'ai';
8
+ return `---\naid: "${opts.aid}"\nname: "${agentName}"\ntype: "${agentType}"\nversion: "1.0.0"\ndescription: ""\ntags:\n - evolcore\n---\n`;
9
+ }
10
+ function ensureTrailingNewline(content) {
11
+ return content.endsWith('\n') ? content : `${content}\n`;
12
+ }
13
+ function lineValue(line) {
14
+ return line.replace(/\r?\n$/, '');
15
+ }
16
+ function lineEnding(line, fallback) {
17
+ return line.match(/\r?\n$/)?.[0] ?? fallback;
18
+ }
19
+ function yamlDoubleQuote(value) {
20
+ return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
21
+ }
22
+ /**
23
+ * Remove the SDK-generated trailing signature block before modifying agent.md.
24
+ * Only a block at EOF is stripped, so body text that mentions AUN-SIGNATURE is preserved.
25
+ */
26
+ export function stripAgentMdSignature(content) {
27
+ const signature = content.match(/(?:\r?\n)?<!-- AUN-SIGNATURE[\s\S]*?-->\s*$/);
28
+ if (!signature || signature.index === undefined)
29
+ return ensureTrailingNewline(content);
30
+ return ensureTrailingNewline(content.slice(0, signature.index));
31
+ }
32
+ function escapeRegExp(value) {
33
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
34
+ }
35
+ function updateAgentMdFrontmatterField(content, key, value, insertAfterKey) {
36
+ const original = content;
37
+ const payload = stripAgentMdSignature(content);
38
+ const lines = payload.match(/[^\n]*\n|[^\n]+$/g) ?? [];
39
+ const openLine = lines[0];
40
+ if (!openLine || lineValue(openLine) !== '---') {
41
+ throw new Error('agent.md has no YAML frontmatter');
42
+ }
43
+ const closeIndex = lines.findIndex((line, index) => index > 0 && lineValue(line) === '---');
44
+ if (closeIndex < 0) {
45
+ throw new Error('agent.md frontmatter is not closed');
46
+ }
47
+ const defaultEol = lineEnding(openLine, '\n');
48
+ const fmLines = lines.slice(1, closeIndex);
49
+ const fieldPattern = new RegExp(`^${escapeRegExp(key)}\\s*:`);
50
+ const fieldIndex = fmLines.findIndex(line => fieldPattern.test(lineValue(line)));
51
+ if (fieldIndex >= 0) {
52
+ const existingLine = fmLines[fieldIndex] ?? '';
53
+ fmLines[fieldIndex] = `${key}: ${yamlDoubleQuote(value)}${lineEnding(existingLine, defaultEol)}`;
54
+ }
55
+ else {
56
+ const anchorPattern = insertAfterKey ? new RegExp(`^${escapeRegExp(insertAfterKey)}\\s*:`) : null;
57
+ const anchorIndex = anchorPattern ? fmLines.findIndex(line => anchorPattern.test(lineValue(line))) : -1;
58
+ const insertIndex = anchorIndex >= 0 ? anchorIndex + 1 : fmLines.length;
59
+ fmLines.splice(insertIndex, 0, `${key}: ${yamlDoubleQuote(value)}${defaultEol}`);
60
+ }
61
+ const updated = ensureTrailingNewline([
62
+ lines[0],
63
+ ...fmLines,
64
+ ...lines.slice(closeIndex),
65
+ ].join(''));
66
+ return { content: updated, changed: updated !== original };
67
+ }
68
+ export function updateAgentMdFrontmatterAvatar(content, avatarUrl) {
69
+ return updateAgentMdFrontmatterField(content, 'avatar', avatarUrl, 'version');
70
+ }
71
+ export function updateAgentMdFrontmatterName(content, name) {
72
+ return updateAgentMdFrontmatterField(content, 'name', name, 'aid');
73
+ }
74
+ /** Normalize an SDK verification result to the local union type. */
75
+ function normalizeVerification(v) {
76
+ const status = v.status === 'verified' ? 'verified' : v.status === 'unsigned' ? 'unsigned' : 'invalid';
77
+ return { status, ...(v.reason ? { reason: String(v.reason) } : {}) };
78
+ }
79
+ /**
80
+ * Verify locally-cached agent.md content offline via the AID value object.
81
+ * Loads the peer cert through the store (no network, no private key required).
82
+ */
83
+ function verifyLocal(store, aid, content) {
84
+ if (!content.includes('AUN-SIGNATURE')) {
85
+ return { status: 'unsigned' };
86
+ }
87
+ try {
88
+ const aidObj = loadAid(store, aid);
89
+ const r = aidObj.verifyAgentMd(content);
90
+ if (!r.ok)
91
+ return { status: 'invalid', reason: r.error.message };
92
+ return normalizeVerification(r.data);
93
+ }
94
+ catch (e) {
95
+ // loadAid throws AidLoadError when the peer cert is missing/invalid.
96
+ return { status: 'invalid', reason: `certificate not available: ${String(e?.message || e).slice(0, 100)}` };
97
+ }
98
+ }
99
+ export async function agentmdGet(aid, opts) {
100
+ const aunPath = opts?.aunPath ?? resolveRoot();
101
+ const store = opts?.store ?? await getAidStore({ slotId: SLOT.cli, aunPath });
102
+ const ownStore = !opts?.store;
103
+ const localPath = agentMdPath(aid);
104
+ try {
105
+ let content;
106
+ let verification;
107
+ const r = await store.downloadAgentMd(aid);
108
+ if (r.ok) {
109
+ content = r.data.content;
110
+ verification = normalizeVerification(r.data.verification);
111
+ }
112
+ else {
113
+ // Network/fetch failed — fall back to local file.
114
+ if (!fs.existsSync(localPath)) {
115
+ throw new Error(`fetch agent.md failed for ${aid}: ${r.error.message}`);
116
+ }
117
+ content = fs.readFileSync(localPath, 'utf-8');
118
+ if (opts?.withVerification) {
119
+ verification = verifyLocal(store, aid, content);
120
+ }
121
+ }
122
+ if (!opts?.withVerification)
123
+ return content;
124
+ return { content, verification: verification ?? { status: 'unsigned' } };
125
+ }
126
+ finally {
127
+ if (ownStore)
128
+ try {
129
+ store.close();
130
+ }
131
+ catch { /* ignore */ }
132
+ }
133
+ }
134
+ /**
135
+ * Upload agent.md: write local file → store.uploadAgentMd (auto-auth + sign + upload).
136
+ *
137
+ * fastaun 0.4.7: uploadAgentMd moved from AUNClient to AIDStore; the store builds
138
+ * its own LocalTokenStore + AuthFlow internally, so no AUNClient/authenticate needed.
139
+ */
140
+ export async function agentmdPut(content, opts) {
141
+ const aunPath = opts.aunPath ?? resolveRoot();
142
+ const store = opts.store ?? await getAidStore({ slotId: SLOT.cli, aunPath });
143
+ const ownStore = !opts.store;
144
+ const dir = aidLocalDir(opts.aid);
145
+ const filePath = path.join(dir, 'agent.md');
146
+ const existed = fs.existsSync(filePath);
147
+ // 先写本地文件(与旧行为一致:本地内容更新应在上传失败时仍保留)
148
+ fs.mkdirSync(dir, { recursive: true });
149
+ fs.writeFileSync(filePath, content, 'utf-8');
150
+ try {
151
+ const r = await store.uploadAgentMd(opts.aid, content);
152
+ if (!r.ok)
153
+ throw new Error(`upload agent.md failed for ${opts.aid}: ${r.error.message}`);
154
+ }
155
+ catch (e) {
156
+ // 上传失败:仅当文件原本不存在(本次新建)时回滚,避免留下孤儿文件;
157
+ // 已存在的文件保留新内容(旧语义)。
158
+ if (!existed)
159
+ try {
160
+ fs.unlinkSync(filePath);
161
+ }
162
+ catch { /* ignore */ }
163
+ throw e;
164
+ }
165
+ finally {
166
+ if (ownStore)
167
+ try {
168
+ store.close();
169
+ }
170
+ catch { /* ignore */ }
171
+ }
172
+ }
173
+ /**
174
+ * Check if agent.md is up-to-date (30-day TTL), fetch if changed.
175
+ * Returns changed=true + content when a new version was downloaded.
176
+ *
177
+ * verification 透传 SDK 的验签结果:
178
+ * - downloaded(changed=true)时为 SDK downloadAgentMd 的 verification
179
+ * - 命中本地缓存或网络失败 fallback 时为 undefined(调用方需自行离线验签或视为未验证)
180
+ *
181
+ * Note: store.checkAgentMd tracks freshness via the store's in-memory cache,
182
+ * so a freshly-built store reports local_found=false and will fetch.
183
+ */
184
+ export async function agentmdSync(aid, opts) {
185
+ const aunPath = opts?.aunPath ?? resolveRoot();
186
+ const store = opts?.store ?? await getAidStore({ slotId: SLOT.cli, aunPath });
187
+ const ownStore = !opts?.store;
188
+ const localPath = agentMdPath(aid);
189
+ try {
190
+ const check = await store.checkAgentMd(aid, 30);
191
+ // In sync (cache fresh) — return local file content with cached verification status.
192
+ if (check.ok && !check.data.needs_update && check.data.local_found) {
193
+ const content = fs.existsSync(localPath) ? fs.readFileSync(localPath, 'utf-8') : undefined;
194
+ const verification = check.data.verify_status
195
+ ? normalizeVerification({ status: check.data.verify_status, reason: check.data.verify_error || undefined })
196
+ : undefined;
197
+ return { changed: false, content, verification };
198
+ }
199
+ // Needs update (or check failed) — fetch fresh content.
200
+ // SDK's downloadAgentMd persists to disk internally (AgentMdManager.saveRecord → writeContent).
201
+ const fetched = await store.downloadAgentMd(aid);
202
+ if (fetched.ok) {
203
+ return { changed: true, content: fetched.data.content, verification: normalizeVerification(fetched.data.verification) };
204
+ }
205
+ // Fetch failed (network) — fall back to local file if present.
206
+ const content = fs.existsSync(localPath) ? fs.readFileSync(localPath, 'utf-8') : undefined;
207
+ return { changed: false, content };
208
+ }
209
+ finally {
210
+ if (ownStore)
211
+ try {
212
+ store.close();
213
+ }
214
+ catch { /* ignore */ }
215
+ }
216
+ }
@@ -0,0 +1,132 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { execFileSync } from 'child_process';
5
+ import { isWindows } from '../../utils/cross-platform.js';
6
+ /**
7
+ * Suppress SDK console logs (DEBUG/INFO/WARN) in CLI context.
8
+ * Call once at CLI entry point — NOT at module load time, to avoid
9
+ * affecting the daemon process which imports this module for slash commands.
10
+ */
11
+ export function suppressSdkLogs() {
12
+ process.env.AUN_LOG_INI_DISABLE = '1';
13
+ const _origLog = console.log;
14
+ const _origInfo = console.info;
15
+ const _origWarn = console.warn;
16
+ const _origStderrWrite = process.stderr.write.bind(process.stderr);
17
+ const SDK_LOG_RE = /^\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+\]\[(?:DEBUG|INFO|WARN|ERROR)\]/;
18
+ const KEYSTORE_WARN_RE = /seed migration failed; continuing with legacy \.seed/;
19
+ console.log = (...args) => { if (typeof args[0] === 'string' && SDK_LOG_RE.test(args[0]))
20
+ return; _origLog(...args); };
21
+ console.info = (...args) => { if (typeof args[0] === 'string' && SDK_LOG_RE.test(args[0]))
22
+ return; _origInfo(...args); };
23
+ console.warn = (...args) => { if (typeof args[0] === 'string' && SDK_LOG_RE.test(args[0]))
24
+ return; _origWarn(...args); };
25
+ console.error = (...args) => { if (typeof args[0] === 'string' && SDK_LOG_RE.test(args[0]))
26
+ return; process.stderr.write(args.map(String).join(' ') + '\n'); };
27
+ // Also intercept direct stderr writes from Rust SDK
28
+ process.stderr.write = function (chunk, encoding, callback) {
29
+ const str = String(chunk);
30
+ // Filter out keystore migration warnings and other SDK logs
31
+ if (SDK_LOG_RE.test(str) || KEYSTORE_WARN_RE.test(str)) {
32
+ if (typeof encoding === 'function') {
33
+ encoding(); // callback is in encoding position
34
+ }
35
+ else if (callback) {
36
+ callback();
37
+ }
38
+ return true;
39
+ }
40
+ return _origStderrWrite(chunk, encoding, callback);
41
+ };
42
+ }
43
+ // ==================== Constants ====================
44
+ export const MIN_AUN_CORE_SDK = [0, 4, 3];
45
+ export const AUN_CORE_SDK_PKG = '@agentunion/fastaun';
46
+ // ==================== SDK & Environment ====================
47
+ function compareVersion(a, min) {
48
+ const parts = a.split('.').map(n => parseInt(n, 10));
49
+ if (parts.length < 3 || parts.some(isNaN))
50
+ return false;
51
+ if (parts[0] !== min[0])
52
+ return parts[0] > min[0];
53
+ if (parts[1] !== min[1])
54
+ return parts[1] > min[1];
55
+ return parts[2] >= min[2];
56
+ }
57
+ export function isAunSdkVersionOk(version) {
58
+ return compareVersion(version, MIN_AUN_CORE_SDK);
59
+ }
60
+ export function resolveAunCoreSdkPkg() {
61
+ try {
62
+ let dir = path.dirname(fileURLToPath(import.meta.url));
63
+ while (true) {
64
+ const candidate = path.join(dir, 'node_modules', AUN_CORE_SDK_PKG, 'package.json');
65
+ if (fs.existsSync(candidate)) {
66
+ const data = JSON.parse(fs.readFileSync(candidate, 'utf-8'));
67
+ if (data.name === AUN_CORE_SDK_PKG)
68
+ return { version: data.version, path: candidate };
69
+ }
70
+ const parent = path.dirname(dir);
71
+ if (parent === dir)
72
+ break;
73
+ dir = parent;
74
+ }
75
+ }
76
+ catch { /* fall through */ }
77
+ try {
78
+ const npmCmd = isWindows ? 'npm.cmd' : 'npm';
79
+ const globalRoot = execFileSync(npmCmd, ['root', '-g'], {
80
+ encoding: 'utf-8', timeout: 10000, shell: isWindows,
81
+ }).trim();
82
+ const pkgPath = path.join(globalRoot, AUN_CORE_SDK_PKG, 'package.json');
83
+ if (fs.existsSync(pkgPath)) {
84
+ const data = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
85
+ return { version: data.version, path: pkgPath };
86
+ }
87
+ }
88
+ catch { /* not found */ }
89
+ return null;
90
+ }
91
+ export async function ensureAunSdk() {
92
+ const installed = resolveAunCoreSdkPkg();
93
+ if (installed && isAunSdkVersionOk(installed.version))
94
+ return;
95
+ const { npmInstallGlobal } = await import('../../utils/npm-ops.js');
96
+ console.log(`正在安装 ${AUN_CORE_SDK_PKG}@latest...`);
97
+ await npmInstallGlobal(`${AUN_CORE_SDK_PKG}@latest`);
98
+ }
99
+ export function isAunSdkReady() {
100
+ const installed = resolveAunCoreSdkPkg();
101
+ return !!(installed && isAunSdkVersionOk(installed.version));
102
+ }
103
+ // ==================== CA Root ====================
104
+ export async function downloadCaRoot(aunPath, gatewayUrl, indent = '') {
105
+ const caDir = path.join(aunPath, 'CA', 'root');
106
+ const caCertPath = path.join(caDir, 'root.crt');
107
+ if (fs.existsSync(caCertPath))
108
+ return true;
109
+ if (!gatewayUrl)
110
+ return false;
111
+ try {
112
+ fs.mkdirSync(caDir, { recursive: true });
113
+ const gwHttp = gatewayUrl.replace(/^wss?:/, 'https:').replace(/\/aun$/, '');
114
+ const resp = await fetch(`${gwHttp}/pki/chain`, { redirect: 'follow' });
115
+ if (!resp.ok) {
116
+ console.warn(`${indent}⚠ CA 根证书下载失败: HTTP ${resp.status}`);
117
+ return false;
118
+ }
119
+ const body = await resp.text();
120
+ if (!body.includes('BEGIN CERTIFICATE')) {
121
+ console.warn(`${indent}⚠ CA 根证书响应内容无效,跳过写入`);
122
+ return false;
123
+ }
124
+ fs.writeFileSync(caCertPath, body);
125
+ console.log(`${indent}✓ CA 根证书已下载`);
126
+ return true;
127
+ }
128
+ catch (e) {
129
+ console.warn(`${indent}⚠ CA 根证书下载失败: ${e},可稍后手动下载`);
130
+ return false;
131
+ }
132
+ }
@@ -0,0 +1,91 @@
1
+ import crypto from 'crypto';
2
+ import { aidCreate } from './index.js';
3
+ import { getAidStore, SLOT } from './store.js';
4
+ import { logger } from '../../utils/logger.js';
5
+ const MAX_ATTEMPTS = 5;
6
+ /** 解析控制 AID 的 issuer:环境变量 EVOLCORE_ISSUER → 兜底 agentid.pub */
7
+ export function resolveControlIssuer() {
8
+ const env = process.env.EVOLCORE_ISSUER?.trim();
9
+ if (env) {
10
+ // Validate issuer format: must be valid domain-like structure
11
+ if (!/^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$/i.test(env)) {
12
+ logger.error(`[control-aid] Invalid EVOLCORE_ISSUER format: ${env}, using default`);
13
+ return 'agentid.pub';
14
+ }
15
+ // Prevent localhost/local domains
16
+ if (env === 'localhost' || env.endsWith('.localhost') || env.startsWith('127.') || env.startsWith('0.')) {
17
+ logger.error(`[control-aid] EVOLCORE_ISSUER cannot be localhost/local: ${env}, using default`);
18
+ return 'agentid.pub';
19
+ }
20
+ // Length limit
21
+ if (env.length > 253) {
22
+ logger.error(`[control-aid] EVOLCORE_ISSUER too long: ${env.length} chars, using default`);
23
+ return 'agentid.pub';
24
+ }
25
+ }
26
+ return env || 'agentid.pub';
27
+ }
28
+ /** 生成候选控制 AID:ec + 5位随机数字 + .{issuer} */
29
+ export function candidateAid(issuer) {
30
+ const n = crypto.randomInt(10000, 100000); // 5 位:10000-99999
31
+ const finalIssuer = issuer || resolveControlIssuer();
32
+ return `ec${n}.${finalIssuer}`;
33
+ }
34
+ /**
35
+ * 候选 AID 是否已在 PKI 注册。
36
+ *
37
+ * 不用 store.exists(它走 HTTP HEAD /pki/cert/<aid>)——部分 Gateway 实现
38
+ * (Python websockets HTTP 处理)对 HEAD 直接空响应断连(curl 52 / socket hang up),
39
+ * 导致 exists 误报"网关不可达"。改用 store.resolve(走 GET),语义等价且 GET 正常返回:
40
+ * - resolve ok → 证书存在(HTTP 200)→ 已注册
41
+ * - CERT_NOT_FOUND → 404 → 未注册
42
+ * - 其它 error → 真·网络错误,向上抛出供 fail-fast
43
+ * skipAgentMd:true 避免多拉一次 agent.md(控制 AID 本就不传 agent.md)。
44
+ */
45
+ async function candidateExists(store, candidate) {
46
+ const r = await store.resolve(candidate, { skipAgentMd: true });
47
+ if (r.ok)
48
+ return true;
49
+ if (r.error?.code === 'CERT_NOT_FOUND')
50
+ return false;
51
+ throw new Error(`Gateway 不可达,无法查重控制 AID:${r.error?.message ?? 'unknown'}`);
52
+ }
53
+ /**
54
+ * 生成控制 AID:循环候选 → candidateExists 查重(权威 PKI 判据)→ 不冲突则 aidCreate。
55
+ * - 查重走 GET 证书(见 candidateExists;不拉 agent.md,控制 AID 本就不传 agent.md)
56
+ * - fail-fast:查重探测失败(网关不可达)立即抛错,不掩盖成"均冲突"
57
+ * - agent.md 不上传:aidCreate 仅注册身份 + 写私钥,不调 agentmdPut
58
+ */
59
+ export async function generateControlAid() {
60
+ const issuer = resolveControlIssuer();
61
+ const store = await getAidStore({ slotId: SLOT.cli });
62
+ try {
63
+ for (let i = 0; i < MAX_ATTEMPTS; i++) {
64
+ const candidate = candidateAid(issuer);
65
+ if (await candidateExists(store, candidate)) {
66
+ logger.info(`[control-aid] ${candidate} 已注册,重试 (${i + 1}/${MAX_ATTEMPTS})`);
67
+ continue;
68
+ }
69
+ const created = await aidCreate(candidate);
70
+ // 清理 aidCreate 内部另开的 client/store——关闭失败不可丢弃已注册的 AID(否则下次 init
71
+ // 会把它当冲突,白白消耗一次重试)。close 异常降级为 warn。
72
+ try {
73
+ await created.client?.close?.();
74
+ }
75
+ catch (e) {
76
+ logger.warn(`[control-aid] client.close() 失败(非致命): ${e}`);
77
+ }
78
+ try {
79
+ await created.store?.close?.();
80
+ }
81
+ catch (e) {
82
+ logger.warn(`[control-aid] store.close() 失败(非致命): ${e}`);
83
+ }
84
+ return { aid: created.aid, gateway: created.gateway };
85
+ }
86
+ throw new Error(`无法生成控制 AID:连续 ${MAX_ATTEMPTS} 次候选均冲突`);
87
+ }
88
+ finally {
89
+ store.close();
90
+ }
91
+ }