evolcore 0.0.25 → 0.0.27

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 (304) hide show
  1. package/CHANGELOG.md +632 -565
  2. package/LICENSE +21 -21
  3. package/README.md +455 -455
  4. package/bin/codex-managed-acl.ps1 +203 -160
  5. package/bin/codex-managed-hook.mjs +208 -208
  6. package/bin/ec-safe-output.js +327 -327
  7. package/bin/ec.js +40 -29
  8. package/bin/install-codex-managed-hooks.mjs +582 -555
  9. package/dist/agents/baseagent-availability.js +68 -0
  10. package/dist/agents/baseagent.js +96 -46
  11. package/dist/agents/claude-runner.js +172 -33
  12. package/dist/agents/codex/app-server-client.js +76 -31
  13. package/dist/agents/codex/catalog.js +1 -1
  14. package/dist/agents/codex/diagnostics.js +34 -1
  15. package/dist/agents/codex-runner.js +434 -446
  16. package/dist/agents/ecagent-runner.js +174 -34
  17. package/dist/agents/gemini-runner.js +8 -7
  18. package/dist/agents/runner-types.js +75 -19
  19. package/dist/aun/aid/agentmd.js +151 -36
  20. package/dist/aun/aid/client.js +1 -1
  21. package/dist/aun/aid/control-aid.js +180 -1
  22. package/dist/aun/aid/identity.js +4 -4
  23. package/dist/aun/aid/index.js +1 -1
  24. package/dist/aun/aid/store.js +17 -13
  25. package/dist/aun/group/history.js +303 -0
  26. package/dist/aun/group/identity.js +43 -0
  27. package/dist/aun/group/index.js +5 -0
  28. package/dist/aun/{msg/group.js → group/operations.js} +162 -39
  29. package/dist/aun/{menu-client.js → menu/client.js} +85 -6
  30. package/dist/aun/menu/index.js +2 -0
  31. package/dist/aun/menu/transport.js +49 -0
  32. package/dist/aun/msg/index.js +2 -1
  33. package/dist/aun/msg/managed-operation.js +85 -19
  34. package/dist/aun/msg/mention-schema.js +51 -0
  35. package/dist/aun/msg/p2p.js +19 -9
  36. package/dist/aun/msg/upload.js +43 -80
  37. package/dist/aun/outbox.js +29 -7
  38. package/dist/aun/rpc/client.js +1 -0
  39. package/dist/aun/storage/client.js +39 -50
  40. package/dist/channels/aun.js +842 -318
  41. package/dist/channels/daemon.js +10 -6
  42. package/dist/cli/agent-command.js +212 -89
  43. package/dist/cli/agent-create.js +626 -11
  44. package/dist/cli/agent.js +125 -13
  45. package/dist/cli/aun-commands.js +487 -337
  46. package/dist/cli/bench.js +20 -20
  47. package/dist/cli/config.js +55 -33
  48. package/dist/cli/contact.js +72 -15
  49. package/dist/cli/ctl-command.js +24 -24
  50. package/dist/cli/daemon-commands.js +140 -63
  51. package/dist/cli/diag.js +31 -0
  52. package/dist/cli/fs-command.js +70 -70
  53. package/dist/cli/handoff-command.js +46 -14
  54. package/dist/cli/index.js +91 -79
  55. package/dist/cli/init.js +34 -1
  56. package/dist/cli/link-rules.js +21 -21
  57. package/dist/cli/managed-command-guard.js +5 -1
  58. package/dist/cli/menu-client.js +51 -32
  59. package/dist/cli/model.js +3 -3
  60. package/dist/cli/net-check.js +22 -22
  61. package/dist/cli/queue-command.js +31 -31
  62. package/dist/cli/response.js +32 -32
  63. package/dist/cli/restart-monitor.js +103 -42
  64. package/dist/cli/stats.js +51 -51
  65. package/dist/cli/task-command.js +59 -0
  66. package/dist/cli/task-context.js +97 -49
  67. package/dist/cli/trigger-command.js +74 -64
  68. package/dist/config/builtin-roles.js +11 -0
  69. package/dist/config/config-field-policy.js +11 -0
  70. package/dist/config/config-manager.js +32 -4
  71. package/dist/config/config-operation-service.js +160 -0
  72. package/dist/config/contact-book-store.js +1 -1
  73. package/dist/config/contact-book.js +1 -1
  74. package/dist/config/contact-operation-service.js +97 -8
  75. package/dist/config/peer-role-resolver.js +1 -1
  76. package/dist/config/resolved-config-op.js +30 -7
  77. package/dist/config-store.js +1 -1
  78. package/dist/core/agent-application-service.js +7 -1
  79. package/dist/core/audit/event-key.js +17 -2
  80. package/dist/core/audit/log-integrity.js +34 -8
  81. package/dist/core/auth/agent-delegation.js +48 -14
  82. package/dist/core/auth/auth-gateway.js +2 -0
  83. package/dist/core/auth/authorization-audit.js +53 -49
  84. package/dist/core/auth/operation-authorizer.js +90 -7
  85. package/dist/core/auth/operation-catalog.js +145 -4
  86. package/dist/core/auth/remote-access-store.js +155 -10
  87. package/dist/core/bootstrap-messages.js +66 -7
  88. package/dist/core/bootstrap-service.js +111 -20
  89. package/dist/core/command/agent-control.js +88 -7
  90. package/dist/core/command/agent-storage-menu.js +443 -0
  91. package/dist/core/command/cli-intent-parser.js +19 -0
  92. package/dist/core/command/command-handler.js +159 -65
  93. package/dist/core/command/data-root-resolver.js +273 -0
  94. package/dist/core/command/diag-service.js +118 -0
  95. package/dist/core/command/group-menu.js +7 -2
  96. package/dist/core/command/menu-catalog.js +46 -7
  97. package/dist/core/command/menu-handler.js +392 -100
  98. package/dist/core/command/menu-protocol.js +45 -6
  99. package/dist/core/command/menu-token-store.js +41 -8
  100. package/dist/core/command/role-menu.js +2 -2
  101. package/dist/core/command/slash-gate.js +34 -4
  102. package/dist/core/command/slash-handler.js +123 -43
  103. package/dist/core/event-catalog.js +4 -0
  104. package/dist/core/evolagent-registry.js +28 -2
  105. package/dist/core/group/invite-service.js +924 -0
  106. package/dist/core/handoff/runtime.js +205 -27
  107. package/dist/core/handoff/store.js +193 -4
  108. package/dist/core/interaction-router.js +22 -8
  109. package/dist/core/message/im-renderer.js +146 -34
  110. package/dist/core/message/message-bridge.js +163 -20
  111. package/dist/core/message/message-log.js +5 -3
  112. package/dist/core/message/message-queue.js +99 -41
  113. package/dist/core/message/message-utils.js +1 -0
  114. package/dist/core/message/response-engine.js +755 -144
  115. package/dist/core/model/catalog-service.js +79 -49
  116. package/dist/core/model/context-window-suffix.js +33 -0
  117. package/dist/core/model/model-catalog.js +1 -1
  118. package/dist/core/model/model-permission.js +12 -3
  119. package/dist/core/permission/approval-gateway.js +36 -3
  120. package/dist/core/permission/ec-command-parser.js +112 -41
  121. package/dist/core/permission/protected-paths.js +134 -16
  122. package/dist/core/permission/sandbox-runtime.js +108 -17
  123. package/dist/core/permission/tool-error-code.js +15 -0
  124. package/dist/core/permission/tool-policy.js +9 -8
  125. package/dist/core/relation/peer-identity.js +57 -6
  126. package/dist/core/relation/shared-data.js +106 -0
  127. package/dist/core/role/runtime-policy.js +64 -0
  128. package/dist/core/runtime-lock.js +10 -26
  129. package/dist/core/session/runner-task-history.js +170 -0
  130. package/dist/core/session/session-manager.js +34 -6
  131. package/dist/core/session/session-mapper.js +41 -0
  132. package/dist/core/session/session-turn-coordinator.js +37 -7
  133. package/dist/core/session/session-turns.js +48 -7
  134. package/dist/core/task/task-recovery-service.js +176 -0
  135. package/dist/core/task/task-resume-operation.js +32 -0
  136. package/dist/eck/group-rules-sync.js +5 -3
  137. package/dist/eck/kit-renderer.js +1 -0
  138. package/dist/index.js +588 -217
  139. package/dist/ipc.js +193 -15
  140. package/dist/paths.js +17 -4
  141. package/dist/product.js +2 -0
  142. package/dist/response-system/engines/v1/proactive-flow.js +59 -2
  143. package/dist/stats/billing.js +41 -15
  144. package/dist/stats/db.js +159 -159
  145. package/dist/stats/eck-vars.js +9 -9
  146. package/dist/stats/price-resolver.js +28 -10
  147. package/dist/stats/query.js +139 -139
  148. package/dist/stats/role-budget.js +6 -6
  149. package/dist/stats/writer.js +51 -51
  150. package/dist/trigger/event-source.js +19 -0
  151. package/dist/trigger/feedback.js +233 -40
  152. package/dist/trigger/parser.js +55 -5
  153. package/dist/trigger/patch.js +9 -0
  154. package/dist/trigger/scheduler.js +30 -4
  155. package/dist/trigger/validation.js +20 -3
  156. package/dist/utils/cross-platform.js +30 -1
  157. package/dist/utils/ecweb-utils.js +1 -0
  158. package/dist/utils/error-dict.json +160 -160
  159. package/dist/utils/error-utils.js +61 -2
  160. package/dist/utils/evolcore-version.js +129 -0
  161. package/dist/utils/logger.js +26 -14
  162. package/dist/utils/macos-autostart.js +22 -22
  163. package/dist/utils/model-prices.jsonl +23 -23
  164. package/dist/utils/npm-ops.js +176 -35
  165. package/dist/utils/tool-summary.js +59 -3
  166. package/dist/utils/welcome.js +196 -23
  167. package/ecagent/LICENSE +21 -21
  168. package/ecagent/NOTICE +11 -11
  169. package/ecagent/README.md +61 -61
  170. package/ecagent/dist/harness/agent-harness.js +3 -0
  171. package/ecagent/dist/harness/compaction/branch-summarization.js +30 -30
  172. package/ecagent/dist/harness/compaction/compaction.js +83 -83
  173. package/ecagent/dist/harness/messages.js +7 -7
  174. package/ecagent/dist/harness/session/jsonl-storage.js +21 -2
  175. package/kits/docs/GUIDE.md +20 -20
  176. package/kits/docs/INDEX.md +60 -66
  177. package/kits/docs/aun/CHEATSHEET.md +19 -19
  178. package/kits/docs/aun/SYNC_PROTOCOL.md +15 -15
  179. package/kits/docs/channels/aun.md +69 -56
  180. package/kits/docs/channels/feishu.md +56 -56
  181. package/kits/docs/context-assembly.md +379 -367
  182. package/kits/docs/eck_templates/GUIDE.template.md +22 -22
  183. package/kits/docs/eck_templates/INDEX.template.md +28 -28
  184. package/kits/docs/eck_templates/path-registry.template.md +33 -33
  185. package/kits/docs/eck_templates/runtime.template.md +19 -19
  186. package/kits/docs/evolcore/INDEX.md +76 -71
  187. package/kits/docs/evolcore/agent-create.md +202 -165
  188. package/kits/docs/evolcore/agent.md +99 -67
  189. package/kits/docs/evolcore/aid.md +68 -60
  190. package/kits/docs/evolcore/codex-windows-sandbox.md +146 -146
  191. package/kits/docs/evolcore/config.md +241 -195
  192. package/kits/docs/evolcore/contact.md +75 -68
  193. package/kits/docs/evolcore/ctl.md +46 -46
  194. package/kits/docs/evolcore/event.md +216 -216
  195. package/kits/docs/evolcore/fs-architecture.md +1215 -1215
  196. package/kits/docs/evolcore/fs.md +110 -110
  197. package/kits/docs/evolcore/group-collaboration.md +244 -244
  198. package/kits/docs/evolcore/group-fs.md +17 -17
  199. package/kits/docs/evolcore/group-rules.md +251 -251
  200. package/kits/docs/evolcore/group.md +267 -152
  201. package/kits/docs/evolcore/menu-client.md +58 -34
  202. package/kits/docs/evolcore/model.md +50 -50
  203. package/kits/docs/evolcore/msg.md +169 -165
  204. package/kits/docs/evolcore/response.md +75 -75
  205. package/kits/docs/evolcore/rpc.md +37 -37
  206. package/kits/docs/evolcore/self-summary.md +29 -29
  207. package/kits/docs/evolcore/stats.md +83 -83
  208. package/kits/docs/evolcore/storage.md +53 -50
  209. package/kits/docs/evolcore/task.md +14 -0
  210. package/kits/docs/evolcore/trigger.md +623 -575
  211. package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -26
  212. package/kits/docs/identity/PATH_OPS.md +16 -16
  213. package/kits/docs/identity/ROLE_DETAIL.md +25 -25
  214. package/kits/docs/identity/identity-tools.md +26 -26
  215. package/kits/docs/path-registry.md +62 -62
  216. package/kits/docs/prompt-loading-architecture.md +269 -269
  217. package/kits/docs/venues/aun-group.md +45 -45
  218. package/kits/docs/venues/aun-private.md +10 -10
  219. package/kits/docs/venues/client-desktop.md +10 -10
  220. package/kits/docs/venues/client-mobile.md +10 -10
  221. package/kits/docs/venues/feishu-group.md +13 -13
  222. package/kits/docs/venues/feishu-private.md +9 -9
  223. package/kits/docs/venues/group.md +42 -25
  224. package/kits/docs/venues/private.md +10 -10
  225. package/kits/eck_manifest.auxiliary.json +43 -43
  226. package/kits/eck_manifest.json +217 -190
  227. package/kits/eck_message_manifest.json +74 -74
  228. package/kits/migrations/README-role-config-v4.md +32 -32
  229. package/kits/migrations/migrate-contact-book-v2.mjs +748 -748
  230. package/kits/migrations/migrate-role-config-v4.mjs +614 -614
  231. package/kits/migrations/migrate-role-config-v5.mjs +365 -365
  232. package/kits/migrations/rename-config-file.mjs +99 -99
  233. package/kits/rules/01-overview.md +166 -162
  234. package/kits/rules/02-navigation.md +96 -96
  235. package/kits/rules/03-identity.md +36 -36
  236. package/kits/rules/04-relation.md +76 -75
  237. package/kits/rules/05-venue.md +60 -60
  238. package/kits/rules/06-channel.md +65 -63
  239. package/kits/schemas/_meta.json +53 -53
  240. package/kits/schemas/agent-config.schema.1.json +177 -177
  241. package/kits/schemas/agent-config.schema.10.json +395 -395
  242. package/kits/schemas/agent-config.schema.11.json +421 -421
  243. package/kits/schemas/agent-config.schema.12.json +451 -427
  244. package/kits/schemas/agent-config.schema.2.json +239 -239
  245. package/kits/schemas/agent-config.schema.3.json +119 -119
  246. package/kits/schemas/agent-config.schema.4.json +208 -208
  247. package/kits/schemas/agent-config.schema.5.json +326 -326
  248. package/kits/schemas/agent-config.schema.6.json +322 -322
  249. package/kits/schemas/agent-config.schema.7.json +303 -303
  250. package/kits/schemas/agent-config.schema.8.json +304 -304
  251. package/kits/schemas/agent-config.schema.9.json +364 -364
  252. package/kits/schemas/contact-book.schema.1.json +36 -36
  253. package/kits/schemas/contact-book.schema.2.json +43 -43
  254. package/kits/schemas/contact-book.schema.3.json +67 -67
  255. package/kits/schemas/daemon.schema.1.json +91 -91
  256. package/kits/schemas/daemon.schema.2.json +101 -101
  257. package/kits/schemas/daemon.schema.3.json +123 -123
  258. package/kits/schemas/daemon.schema.4.json +136 -136
  259. package/kits/schemas/daemon.schema.5.json +135 -135
  260. package/kits/schemas/daemon.schema.6.json +131 -131
  261. package/kits/schemas/defaults.schema.1.json +81 -81
  262. package/kits/schemas/defaults.schema.2.json +85 -85
  263. package/kits/schemas/defaults.schema.3.json +73 -73
  264. package/kits/schemas/defaults.schema.4.json +107 -107
  265. package/kits/schemas/defaults.schema.5.json +119 -119
  266. package/kits/schemas/menu-exec-schema-commands.md +208 -208
  267. package/kits/schemas/migrations/README.md +30 -30
  268. package/kits/schemas/relation-config.schema.1.json +158 -158
  269. package/kits/schemas/relation-config.schema.2.json +73 -73
  270. package/kits/schemas/relation-config.schema.3.json +50 -50
  271. package/kits/schemas/relation-config.schema.4.json +47 -47
  272. package/kits/schemas/relation-config.schema.5.json +47 -47
  273. package/kits/schemas/relation-config.schema.6.json +46 -46
  274. package/kits/schemas/relation-config.schema.7.json +59 -59
  275. package/kits/schemas/relation-config.schema.8.json +103 -103
  276. package/kits/schemas/role-config.schema.1.json +215 -201
  277. package/kits/schemas/role-registry.schema.1.json +35 -35
  278. package/kits/schemas/single-session.schema.1.json +31 -31
  279. package/kits/schemas/single-session.schema.2.json +57 -57
  280. package/kits/schemas/single-session.schema.3.json +32 -32
  281. package/kits/templates/bootstrap-welcome.md +17 -17
  282. package/kits/templates/message-fragments/handoff-context-to-target.md +9 -9
  283. package/kits/templates/message-fragments/handoff-request-to-target.md +19 -19
  284. package/kits/templates/message-fragments/handoff-response-to-origin.md +9 -9
  285. package/kits/templates/message-fragments/inject-default.md +2 -2
  286. package/kits/templates/message-fragments/item.md +2 -2
  287. package/kits/templates/roles/admin.json +92 -92
  288. package/kits/templates/roles/member.json +101 -96
  289. package/kits/templates/roles/owner.json +9 -9
  290. package/kits/templates/roles/visitor.json +90 -85
  291. package/kits/templates/system-fragments/baseagent.md +14 -14
  292. package/kits/templates/system-fragments/bootstrap.md +53 -34
  293. package/kits/templates/system-fragments/channel.md +28 -64
  294. package/kits/templates/system-fragments/commands.md +36 -28
  295. package/kits/templates/system-fragments/group-unread-history.md +7 -0
  296. package/kits/templates/system-fragments/identity.md +13 -13
  297. package/kits/templates/system-fragments/relation.md +19 -19
  298. package/kits/templates/system-fragments/reply.md +56 -0
  299. package/kits/templates/system-fragments/session.md +59 -59
  300. package/kits/templates/system-fragments/venue.md +31 -31
  301. package/package.json +104 -98
  302. package/dist/aun/group-identity.js +0 -9
  303. /package/dist/aun/{group-fs-download.js → group/fs-download.js} +0 -0
  304. /package/dist/aun/{msg/group-index.js → group/index-rpc.js} +0 -0
@@ -1,9 +1,13 @@
1
+ import { loadAgent } from '../../config-store.js';
2
+ import { initializationBlocksTasks } from '../../cli/agent-create.js';
1
3
  import { createHash, randomBytes } from 'crypto';
4
+ import { agentStorageMenuService, isAgentStorageMenuRequest } from '../command/agent-storage-menu.js';
2
5
  import { logger } from '../../utils/logger.js';
3
6
  import { StreamDebouncer } from './stream-debouncer.js';
4
7
  import { appendMessageLog, appendMessageLogStrict, buildInboundEntry, isTransientProtocolMessage } from './message-log.js';
5
8
  import { buildEnvelope, isInteractionSendAccepted, sendInteractionPayload } from './message-utils.js';
6
9
  import { chatDirPath } from '../session/session-fs-store.js';
10
+ import { readTopicLable } from '../session/session-mapper.js';
7
11
  import { formatChannelKey, tryParseChannelKey } from '../channel-loader.js';
8
12
  import { agentDir, resolvePaths } from '../../paths.js';
9
13
  import { listStaticAgentAdmins, listStaticAgentOwners, resolvePeerRoleDetail, } from '../../config/peer-role-resolver.js';
@@ -25,8 +29,9 @@ import { createRootCausation, deriveCausation, normalizeCausation } from '../cau
25
29
  import { recordCausationSpan } from '../causation/audit.js';
26
30
  import { renderActionAsText } from '../interaction-router.js';
27
31
  import { planApprovalRoute } from '../permission/approval-gateway.js';
28
- import { parseFullAccessCommand } from '../command/slash-gate.js';
32
+ import { guardGroupSlashCommand, parseFullAccessCommand } from '../command/slash-gate.js';
29
33
  import { isFullAccessEnabled } from '../../config-store.js';
34
+ import { createAgentPolicyAuthorization } from '../role/runtime-policy.js';
30
35
  import { auditFullAccessEvent } from '../auth/authorization-audit.js';
31
36
  import { consumeAdmissionReplyBudget, clearAdmissionReplyBudgets, evaluateInboundAdmissionPreflight, evaluateOrdinaryInboundAdmission, } from './message-admission.js';
32
37
  const CONTACT_REJECTION_NOTICE = '[rejection] Not accepting messages from you.';
@@ -113,6 +118,7 @@ export class MessageBridge {
113
118
  aidStatsCollector;
114
119
  contactBindRuntimeChecker;
115
120
  interactionRouter;
121
+ groupInviteReviewService;
116
122
  constructor(defaultProjectPath, sessionManager, processor, messageQueue, cmdHandler, eventBus, defaultDebounce, processOwnersProvider) {
117
123
  this.defaultProjectPath = defaultProjectPath;
118
124
  this.sessionManager = sessionManager;
@@ -144,6 +150,12 @@ export class MessageBridge {
144
150
  setInteractionRouter(router) {
145
151
  this.interactionRouter = router;
146
152
  }
153
+ setGroupInviteReviewService(service) {
154
+ this.groupInviteReviewService = service;
155
+ }
156
+ registerGroupInviteReviewChannel(binding) {
157
+ this.groupInviteReviewService?.attach(binding);
158
+ }
147
159
  /**
148
160
  * AUN supplies an authenticated sender AID, while the IM-side code proves
149
161
  * possession of the external account. This command only creates that
@@ -483,7 +495,7 @@ export class MessageBridge {
483
495
  ...(msg.replyContext ?? {}),
484
496
  metadata: {
485
497
  ...(msg.replyContext?.metadata ?? {}),
486
- ...evolMenuResponseTransportMetadata(),
498
+ ...evolMenuResponseTransportMetadata(msg.protectedHeaders?.sdk_version),
487
499
  },
488
500
  },
489
501
  }), {
@@ -500,9 +512,31 @@ export class MessageBridge {
500
512
  async sendAdmissionControlReply(context, msg, sendReply, text) {
501
513
  if (!text || !consumeAdmissionReplyBudget(context))
502
514
  return false;
503
- await sendReply(msg.channelId, text, msg.replyContext);
515
+ if (context.chatType === 'group' && /^\s*\/(?:bind|request)(?:\s|$)/i.test(msg.content)) {
516
+ await this.sendSlashControlReply(context.channelKey, msg, sendReply, { kind: 'command.error', text });
517
+ }
518
+ else {
519
+ await sendReply(msg.channelId, text, msg.replyContext);
520
+ }
504
521
  return true;
505
522
  }
523
+ /** Controls handled before the normal slash handler still need structured group feedback. */
524
+ async sendSlashControlReply(channel, msg, sendReply, payload, adapter = this.processor.getChannelInfo?.(channel)?.adapter) {
525
+ if (msg.chatType === 'group' && adapter?.send) {
526
+ await adapter.send(buildEnvelope({
527
+ taskId: `cmd-${randomBytes(5).toString('hex')}`,
528
+ channel,
529
+ channelId: msg.channelId,
530
+ chatmode: 'interactive',
531
+ replyContext: {
532
+ ...msg.replyContext,
533
+ ...(msg.threadId ? { threadId: msg.threadId } : {}),
534
+ },
535
+ }), payload);
536
+ return;
537
+ }
538
+ await sendReply(msg.channelId, payload.text, msg.replyContext);
539
+ }
506
540
  admissionDeniedNotice(context, channelType) {
507
541
  if (context.contactStatus === 'pending') {
508
542
  return '联系人申请审核中;可再次发送 /request 更新说明。';
@@ -773,6 +807,20 @@ export class MessageBridge {
773
807
  }
774
808
  return;
775
809
  }
810
+ if (!trustedHandoffEcho && selfAid && actorId && this.groupInviteReviewService) {
811
+ const inviteCommand = await this.groupInviteReviewService.handleCommand({
812
+ selfAid,
813
+ ownerAid: actorId,
814
+ channelKey,
815
+ channelType: resolvedChannelType,
816
+ chatType,
817
+ content,
818
+ });
819
+ if (inviteCommand.handled) {
820
+ await this.sendAdmissionControlReply(admissionPreflight.context, msg, sendReply, inviteCommand.text ?? '群邀请命令已处理。');
821
+ return;
822
+ }
823
+ }
776
824
  if (!trustedHandoffEcho && await this.handleReservedContactControl({
777
825
  context: admissionPreflight.context,
778
826
  msg,
@@ -801,6 +849,16 @@ export class MessageBridge {
801
849
  : { isMenu: false };
802
850
  let menuResponseWarning;
803
851
  if (menuControl.isMenu) {
852
+ // Menu is an AUN private control plane. Group messages must never
853
+ // execute Menu commands or participate in token issuance/rotation.
854
+ if (msg.chatType === 'group') {
855
+ this.logMenuDiagnostic('unsupported-chat-type', channelName, msg, menuControl, 'MENU_PRIVATE_ONLY');
856
+ logMenuRequestCompleted(menuControl.request, undefined, this.menuFlowContext(channelName, msg), {
857
+ delivery: 'dropped',
858
+ reason: 'unsupported-chat-type',
859
+ });
860
+ return;
861
+ }
804
862
  // AUN captures this mark at its adapter boundary. Legacy/test
805
863
  // adapters fall back to the bridge entry mark captured above.
806
864
  Object.assign(msg, menuTiming);
@@ -837,7 +895,10 @@ export class MessageBridge {
837
895
  const tokenSupplied = Object.prototype.hasOwnProperty.call(menuControl.request, 'menu_token');
838
896
  const tokenValid = (menuTokenRequired || tokenSupplied)
839
897
  && !!tokenOwnerAid
840
- && this.menuTokenStore.use(tokenOwnerAid, msg.peerId, menuControl.request.menu_token);
898
+ && this.menuTokenStore.use(tokenOwnerAid, msg.peerId, menuControl.request.menu_token, {
899
+ deviceId: msg.peerDeviceId,
900
+ slotId: msg.peerSlotId,
901
+ });
841
902
  if (menuTokenRequired && !tokenValid) {
842
903
  this.logMenuDiagnostic('token-required', channelName, msg, menuControl, 'MENU_TOKEN_REQUIRED');
843
904
  await this.sendMenuResponse(adapter, channelName, msg, menuFailure(header, {
@@ -915,6 +976,11 @@ export class MessageBridge {
915
976
  // internal per-call authorization carried into ResponseEngine.
916
977
  const fullAccessCommand = parseFullAccessCommand(content);
917
978
  if (fullAccessCommand.matched) {
979
+ const groupGuard = guardGroupSlashCommand(content, chatType);
980
+ if (groupGuard?.kind === 'command.error') {
981
+ await this.sendSlashControlReply(channelName, msg, sendReply, groupGuard, adapter);
982
+ return;
983
+ }
918
984
  const reject = async (text) => {
919
985
  logger.warn(`[MessageBridge] fullaccess command rejected: channel=${channelName} `
920
986
  + `actor=${authSubject.principalId ?? authSubject.actorId ?? '<none>'} reason=${text}`);
@@ -987,6 +1053,11 @@ export class MessageBridge {
987
1053
  ...(chatType === 'private' ? { routeId: msg.channelId } : {}),
988
1054
  });
989
1055
  }
1056
+ const initializationConfig = selfAid ? loadAgent(selfAid) : null;
1057
+ if (initializationConfig && initializationBlocksTasks(initializationConfig)) {
1058
+ await sendReply(msg.channelId, 'Agent 初始化尚未完成,请由创建者查询 ec agent show 或恢复初始化。');
1059
+ return;
1060
+ }
990
1061
  // 1. owner 绑定(按实例名绑定)
991
1062
  if (adapter && actorId && roleDetail.effectiveRole === 'owner') {
992
1063
  await this.bootstrapService?.tryStartBootstrap({
@@ -1002,6 +1073,13 @@ export class MessageBridge {
1002
1073
  // 2. 命令快速路径(去除引用前缀后检查,兼容话题中引用上文的情况)
1003
1074
  const contentForCmd = content.replace(/^(>[^\n]*\n)+\n?/, '').trim();
1004
1075
  const cmdContent = contentForCmd || content;
1076
+ if (msg.source !== 'handoff' && !executionPermissionOverride) {
1077
+ const groupGuard = guardGroupSlashCommand(cmdContent, chatType);
1078
+ if (groupGuard?.kind === 'command.error') {
1079
+ await this.sendSlashControlReply(channelName, msg, sendReply, groupGuard, adapter);
1080
+ return;
1081
+ }
1082
+ }
1005
1083
  const isCmd = msg.source !== 'handoff'
1006
1084
  // Once /fa has been accepted, its remainder is always model input.
1007
1085
  // A prompt such as `/fa /status` must not be re-routed as the
@@ -1059,6 +1137,14 @@ export class MessageBridge {
1059
1137
  return sendReply(msg.channelId, text, msg.replyContext);
1060
1138
  }, msg.peerId, msg.threadId, msg.chatType, msg.source, msg.replyContext, msg.messageId, msg.selfAID, authSubject))
1061
1139
  return;
1140
+ // Commands retain their normal route. Only authenticated direct model
1141
+ // input can obtain the Agent policy's per-call authorization.
1142
+ if (!executionPermissionOverride && selfAid && owningAgent?.aid === selfAid) {
1143
+ executionPermissionOverride = createAgentPolicyAuthorization({
1144
+ agentAid: selfAid, actor: roleDetail.actor, chatType,
1145
+ source: msg.source, peerType: msg.peerType,
1146
+ });
1147
+ }
1062
1148
  // 3. session 解析(使用 Channel 层填充的 chatType)
1063
1149
  if (!(await this.canCreateThreadSession(channelName, msg, chatType))) {
1064
1150
  // 静默丢弃:绝不向群里注入回复。
@@ -1102,7 +1188,7 @@ export class MessageBridge {
1102
1188
  // has been appended and create another renewed session.
1103
1189
  releaseAdmission = await this.acquireAdmission(admissionRoute);
1104
1190
  const hadMainSession = msg.threadId ? true : this.sessionManager.hasMainSession(channelName, msg.channelId, msg.channelType || effectiveChannelType, msg.selfAID || selfAid);
1105
- let session = await this.sessionManager.getOrCreateSession(channelName, msg.channelId, effectiveProjectPath, msg.threadId, Object.keys(metadata).length ? metadata : undefined, this.extractTopicName(msg), msg.peerId, chatType, owningAgent?.baseagent, msg.selfAID, msg.channelType || effectiveChannelType, msg.peerType, identity);
1191
+ let session = await this.sessionManager.getOrCreateSession(channelName, msg.channelId, effectiveProjectPath, msg.threadId, Object.keys(metadata).length ? metadata : undefined, this.extractTopicName(msg), msg.peerId, chatType, owningAgent?.baseagent, msg.selfAID, msg.channelType || effectiveChannelType, msg.peerType, identity, readTopicLable(msg) ?? readTopicLable(msg.replyContext?.metadata));
1106
1192
  if (session.threadId && typeof this.messageQueue.reserveSessionIngress === 'function') {
1107
1193
  releaseSessionIngress = this.messageQueue.reserveSessionIngress(session.id);
1108
1194
  }
@@ -1158,6 +1244,7 @@ export class MessageBridge {
1158
1244
  mentions: msg.mentions, mentionAids: msg.mentionAids, threadId: msg.threadId,
1159
1245
  isMentioned: msg.isMentioned,
1160
1246
  topicName: this.extractTopicName(msg),
1247
+ lable: session.lable,
1161
1248
  replyContext: msg.replyContext,
1162
1249
  source: msg.source,
1163
1250
  ...(executionPermissionOverride ? { executionPermissionOverride } : {}),
@@ -1380,6 +1467,8 @@ export class MessageBridge {
1380
1467
  const menuAuthSubject = {
1381
1468
  ...authSubject,
1382
1469
  requestId: parsed.id,
1470
+ menuEncrypted: msg.encrypted === true,
1471
+ fromControlChannel: authSubject.fromControlChannel || msg.isControlChannel === true,
1383
1472
  ...(activeSession?.id ? { sessionId: activeSession.id } : {}),
1384
1473
  };
1385
1474
  const isTokenRequest = parsed.type === 'menu.token.request';
@@ -1410,8 +1499,22 @@ export class MessageBridge {
1410
1499
  return;
1411
1500
  }
1412
1501
  }
1502
+ // Reauthorize even cached responses: roots expire and static owners can be revoked.
1503
+ const isStorageRequest = isAgentStorageMenuRequest(parsed.request);
1504
+ if (isStorageRequest) {
1505
+ try {
1506
+ agentStorageMenuService.preflight(parsed.request, menuAuthSubject);
1507
+ }
1508
+ catch (error) {
1509
+ await this.sendMenuResponse(adapter, channel, msg, menuFailure(header, normalizeMenuError(error)));
1510
+ return;
1511
+ }
1512
+ }
1413
1513
  const scope = msg.chatType === 'group' ? (msg.groupId || msg.channelId) : msg.peerId;
1414
- const dedupKey = [msg.selfAID || channel, channelType, msg.chatType || 'private', scope, msg.peerId, msg.threadId || '', parsed.id].join('\u001f');
1514
+ // Separate installations can generate identical request IDs. Token
1515
+ // issuance and replay must remain scoped to the authenticated requester.
1516
+ const dedupKey = [msg.selfAID || channel, channelType, msg.chatType || 'private', scope, msg.peerId,
1517
+ msg.peerDeviceId || '', msg.peerSlotId || '', msg.threadId || '', parsed.id].join('\u001f');
1415
1518
  let afterResponse;
1416
1519
  const deduped = await this.menuDeduper.execute(dedupKey, menuPayloadFingerprint(parsed.raw), () => isTokenRequest
1417
1520
  ? this.issueMenuTokenResponse(header, channel, msg)
@@ -1424,6 +1527,16 @@ export class MessageBridge {
1424
1527
  }));
1425
1528
  return;
1426
1529
  }
1530
+ // A replay may have awaited an in-flight request; check again before release.
1531
+ if (isStorageRequest) {
1532
+ try {
1533
+ agentStorageMenuService.preflight(parsed.request, menuAuthSubject, false);
1534
+ }
1535
+ catch (error) {
1536
+ await this.sendMenuResponse(adapter, channel, msg, menuFailure(header, normalizeMenuError(error)));
1537
+ return;
1538
+ }
1539
+ }
1427
1540
  if (deduped.replayed)
1428
1541
  this.logMenuDiagnostic('request-replay', channel, msg, parsed);
1429
1542
  if (deduped.retry)
@@ -1438,7 +1551,20 @@ export class MessageBridge {
1438
1551
  await deferredEffect();
1439
1552
  }
1440
1553
  catch (error) {
1441
- logger.warn(`[MenuControl] deferred effect failed: request=${parsed.id} error=${error instanceof Error ? error.message : String(error)}`);
1554
+ const detail = error instanceof Error ? error.message : String(error);
1555
+ const code = error && typeof error === 'object' && 'code' in error
1556
+ ? String(error.code ?? 'EXEC_FAILED')
1557
+ : 'EXEC_FAILED';
1558
+ logger.warn(`[MenuControl] deferred effect failed: request=${parsed.id} error=${detail}`);
1559
+ // File fetch is acknowledged before the upload completes. Send a
1560
+ // second response with the same request id so the client can finish
1561
+ // the pending fetch with an actionable error instead of timing out.
1562
+ const failedResponse = menuFailure(header, {
1563
+ code,
1564
+ message: detail.startsWith('文件发送失败') ? detail : `文件发送失败: ${detail}`,
1565
+ });
1566
+ await this.menuDeduper.replaceResult(dedupKey, menuPayloadFingerprint(parsed.raw), deduped.value, failedResponse);
1567
+ await this.sendMenuResponse(adapter, channel, msg, failedResponse);
1442
1568
  }
1443
1569
  }
1444
1570
  }
@@ -1448,11 +1574,20 @@ export class MessageBridge {
1448
1574
  const name = header.name ?? '';
1449
1575
  const cmd = typeof request.cmd === 'string' ? request.cmd : undefined;
1450
1576
  const args = request.args;
1577
+ const transportReplyContext = {
1578
+ ...(msg.replyContext ?? {}),
1579
+ ...(msg.threadId ? { threadId: msg.threadId } : {}),
1580
+ };
1581
+ // Explicit group context can target a different conversation. Its
1582
+ // sessions must not inherit the thread of the source conversation.
1583
+ const replyContextFor = (channelId) => channelId === msg.channelId ? transportReplyContext : undefined;
1451
1584
  switch (request.type) {
1452
1585
  case 'menu.list':
1453
1586
  if (args?.view === 'catalog') {
1454
1587
  validateMenuCatalogArgs(args);
1455
- const data = await this.cmdHandler.getMenuCatalog(channel, msg.channelId, authSubject, args.context, msg.isControlChannel ? 'control' : 'agent');
1588
+ const data = await this.cmdHandler.getMenuCatalog(channel, msg.channelId, authSubject, args.context, msg.isControlChannel ? 'control' : 'agent', !msg.isControlChannel || (msg.encrypted === true
1589
+ && msg.protectedHeaders?.auth_method !== 'remote-access-token'
1590
+ && msg.protectedHeaders?.auth_method !== 'ecweb-token'));
1456
1591
  return menuSuccess(header, data);
1457
1592
  }
1458
1593
  if (args?.view !== undefined) {
@@ -1462,7 +1597,7 @@ export class MessageBridge {
1462
1597
  case 'menu.query': {
1463
1598
  if (name === 'menu' && args?.view === 'values') {
1464
1599
  validateMenuValueBatchArgs(args);
1465
- const data = await this.cmdHandler.execMenuValueBatch(channel, msg.channelId, authSubject, args, args.context, msg.isControlChannel ? 'control' : 'agent');
1600
+ const data = await this.cmdHandler.execMenuValueBatch(channel, msg.channelId, authSubject, args, args.context, msg.isControlChannel ? 'control' : 'agent', transportReplyContext);
1466
1601
  return menuSuccess(header, data);
1467
1602
  }
1468
1603
  if (name === 'menu') {
@@ -1475,7 +1610,7 @@ export class MessageBridge {
1475
1610
  channelId: msg.channelId,
1476
1611
  args,
1477
1612
  });
1478
- const result = await this.cmdHandler.execMenuQuery(this.resolveCmd(name, cmd), channel, execution.channelId, execution.subject.actorId, execution.args, execution.subject.chatType, execution.subject.fromControlChannel, execution.subject.identity, execution.subject);
1613
+ const result = await this.cmdHandler.execMenuQuery(this.resolveCmd(name, cmd), channel, execution.channelId, execution.subject.actorId, execution.args, execution.subject.chatType, execution.subject.fromControlChannel, execution.subject.identity, execution.subject, 'menu', replyContextFor(execution.channelId));
1479
1614
  if ('error' in result)
1480
1615
  throw result;
1481
1616
  return menuSuccess(header, result.data);
@@ -1492,7 +1627,7 @@ export class MessageBridge {
1492
1627
  if ((name === 'topic' || cmd === '/topic') && args?.mode === 'fork-turns' && msg.threadId && !args?.sourceThreadId) {
1493
1628
  optionArgs = { ...(execution.args ?? {}), sourceThreadId: msg.threadId };
1494
1629
  }
1495
- const data = await this.cmdHandler.getSubMenuItems(this.resolveCmd(name, cmd), channel, execution.channelId, execution.subject.actorId, optionArgs, execution.subject.identity, execution.subject.chatType, execution.subject.fromControlChannel, execution.subject) ?? [];
1630
+ const data = await this.cmdHandler.getSubMenuItems(this.resolveCmd(name, cmd), channel, execution.channelId, execution.subject.actorId, optionArgs, execution.subject.identity, execution.subject.chatType, execution.subject.fromControlChannel, execution.subject, 'menu', replyContextFor(execution.channelId)) ?? [];
1496
1631
  return menuSuccess(header, data);
1497
1632
  }
1498
1633
  case 'menu.update': {
@@ -1503,10 +1638,7 @@ export class MessageBridge {
1503
1638
  channelId: msg.channelId,
1504
1639
  args,
1505
1640
  });
1506
- const result = await this.cmdHandler.execMenuUpdate(this.resolveCmd(name, cmd), request.value, channel, execution.channelId, execution.subject.actorId, execution.subject.identity, execution.subject.fromControlChannel, execution.args, execution.subject, 'menu', {
1507
- ...(msg.replyContext ?? {}),
1508
- ...(msg.threadId ? { threadId: msg.threadId } : {}),
1509
- });
1641
+ const result = await this.cmdHandler.execMenuUpdate(this.resolveCmd(name, cmd), request.value, channel, execution.channelId, execution.subject.actorId, execution.subject.identity, execution.subject.fromControlChannel, execution.args, execution.subject, 'menu', replyContextFor(execution.channelId));
1510
1642
  if ('error' in result)
1511
1643
  throw result;
1512
1644
  return menuSuccess(header, result.data);
@@ -1569,9 +1701,11 @@ export class MessageBridge {
1569
1701
  const agentName = this.agentRegistry?.resolveByChannel(channel)?.name ?? '<unknown>';
1570
1702
  const replyContext = {
1571
1703
  ...(msg.replyContext ?? {}),
1704
+ ...(msg.peerDeviceId ? { toDeviceId: msg.peerDeviceId } : {}),
1572
1705
  metadata: {
1573
1706
  ...(msg.replyContext?.metadata ?? {}),
1574
- ...evolMenuResponseTransportMetadata(),
1707
+ ...evolMenuResponseTransportMetadata(msg.protectedHeaders?.sdk_version),
1708
+ ...(isAgentStorageMenuRequest(request) ? { encrypted: true } : {}),
1575
1709
  },
1576
1710
  };
1577
1711
  if (msg.channelType === 'aun' && !replyContext.delivery) {
@@ -1615,6 +1749,9 @@ export class MessageBridge {
1615
1749
  clientName: this.menuTokenHeaderValue(headers.client_name ?? headers.app_name),
1616
1750
  clientVersion: this.menuTokenHeaderValue(headers.client_version ?? headers.app_version),
1617
1751
  evolVersion: this.menuTokenHeaderValue(headers.evol_version),
1752
+ }, {
1753
+ deviceId: msg.peerDeviceId,
1754
+ slotId: msg.peerSlotId,
1618
1755
  });
1619
1756
  if (!issued) {
1620
1757
  return menuFailure(header, { code: 'MENU_TOKEN_REJECTED', message: '授权申请未通过' });
@@ -1732,20 +1869,25 @@ export class MessageBridge {
1732
1869
  logger.info(`[${channel}] ${channelId}: ${content}${source === 'card-trigger' ? ' [card]' : ''}`);
1733
1870
  const adapter = this.processor.getChannelInfo?.(channel)?.adapter;
1734
1871
  const cmdResult = await this.cmdHandler.handle(content, channel, channelId, async (_cid, text, opts, updatePayload) => {
1735
- if (updatePayload && adapter?.send) {
1872
+ const payload = updatePayload ?? (chatType === 'group' ? { kind: 'command.result', text } : undefined);
1873
+ if (payload && adapter?.send) {
1736
1874
  const envelope = buildEnvelope({
1737
1875
  taskId: `cmd-update-${randomBytes(5).toString('hex')}`,
1738
1876
  channel,
1739
1877
  channelId,
1740
1878
  agentName: this.agentRegistry?.resolveByChannel(channel)?.name ?? '<unknown>',
1741
1879
  chatmode: 'interactive',
1742
- replyContext: opts ?? replyContext,
1880
+ replyContext: {
1881
+ ...(opts ?? {}),
1882
+ ...(replyContext ?? {}),
1883
+ metadata: { ...opts?.metadata, ...replyContext?.metadata },
1884
+ },
1743
1885
  });
1744
- await adapter.send(envelope, updatePayload);
1886
+ await adapter.send(envelope, payload);
1745
1887
  return;
1746
1888
  }
1747
1889
  await sendReply(text, opts, updatePayload);
1748
- }, userId, threadId, chatType, source, messageId, selfAID, authSubject?.identity, authSubject, replyContext);
1890
+ }, userId, threadId, chatType, source, messageId, selfAID, authSubject?.identity, authSubject, replyContext, true);
1749
1891
  logger.debug(`[MessageBridge] handleCommand: result type=${typeof cmdResult}`);
1750
1892
  if (cmdResult === undefined)
1751
1893
  return false;
@@ -1830,6 +1972,7 @@ export class MessageBridge {
1830
1972
  }
1831
1973
  /** 注销单个渠道的 debouncer(热重载断开渠道时调用) */
1832
1974
  removeChannel(channelName) {
1975
+ this.groupInviteReviewService?.detach(channelName);
1833
1976
  const d = this.debouncers.get(channelName);
1834
1977
  if (d) {
1835
1978
  d.dispose();
@@ -1,6 +1,6 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import { appendJsonl, chatDirPath } from '../session/session-fs-store.js';
3
+ import { appendJsonl, appendJsonlDurable, chatDirPath } from '../session/session-fs-store.js';
4
4
  import { logger } from '../../utils/logger.js';
5
5
  const TRANSIENT_PROTOCOL_TYPE_PREFIXES = [
6
6
  'contact.request.',
@@ -313,13 +313,13 @@ export function appendMessageLog(chatDir, entry) {
313
313
  }
314
314
  }
315
315
  /** Strict conversation-history append used by handoff reply binding. */
316
- export function appendMessageLogStrict(chatDir, entry) {
316
+ export function appendMessageLogStrict(chatDir, entry, durable = false) {
317
317
  if (isTransientProtocolMessage(entry))
318
318
  return;
319
319
  if (entry.dir === 'in' && hasDuplicate(chatDir, entry.msgId))
320
320
  return;
321
321
  fs.mkdirSync(chatDir, { recursive: true });
322
- appendJsonl(messageLogPath(chatDir), entry);
322
+ (durable ? appendJsonlDurable : appendJsonl)(messageLogPath(chatDir), entry);
323
323
  if (entry.dir === 'in')
324
324
  rememberMessageId(chatDir, entry.msgId);
325
325
  }
@@ -343,6 +343,8 @@ export function buildInboundEntry(opts) {
343
343
  msgType: isCommand ? 'command' : (opts.msgType ?? 'text'),
344
344
  payloadType: opts.payloadType,
345
345
  payloadSummary: opts.payloadSummary,
346
+ groupAttention: opts.groupAttention,
347
+ groupDelivery: opts.groupDelivery,
346
348
  content: opts.content,
347
349
  replyTo: opts.replyTo ?? null,
348
350
  agent: null,