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
@@ -25,6 +25,7 @@ const GROUP_MANAGEMENT_OPERATIONS = [
25
25
  ['ec.group.list', 'read', ['agent'], 'List groups joined by the current Agent'],
26
26
  ['ec.group.info', 'read', ['relation', 'agent'], 'Read group information'],
27
27
  ['ec.group.pull', 'read', ['relation', 'agent'], 'Pull group messages'],
28
+ ['ec.group.history', 'read', ['relation', 'agent'], 'Read local group conversation history'],
28
29
  ['ec.group.members', 'read', ['relation', 'agent'], 'List group members'],
29
30
  ['ec.group.online', 'read', ['relation', 'agent'], 'List online group members'],
30
31
  ['ec.group.rules.get', 'read', ['relation', 'agent'], 'Read the published group rules file'],
@@ -47,6 +48,11 @@ const GROUP_MANAGEMENT_OPERATIONS = [
47
48
  ['ec.group.owner', 'write-agent', ['agent'], 'Transfer group ownership'],
48
49
  ['ec.group.ban', 'write-agent', ['agent'], 'Read or update the group ban list'],
49
50
  ['ec.group.unban', 'write-agent', ['agent'], 'Remove a group ban'],
51
+ ['ec.group.block', 'write-own', ['relation', 'agent'], 'Block group message reception'],
52
+ ['ec.group.unblock', 'write-own', ['relation', 'agent'], 'Unblock group message reception'],
53
+ ['ec.group.blocklist', 'read', ['agent'], 'Read the group block list'],
54
+ ['ec.group.report-status', 'write-own', ['relation', 'agent'], 'Report the current Agent group status'],
55
+ ['ec.group.member-statuses', 'read', ['relation', 'agent'], 'Read reported group member statuses'],
50
56
  ['ec.group.rules.set', 'write-agent', ['agent'], 'Upload and publish group rules content'],
51
57
  ['ec.group.rules.publish', 'write-agent', ['agent'], 'Publish the current group rules file'],
52
58
  ['ec.group.rules.update', 'write-agent', ['agent'], 'Update group join rules'],
@@ -100,6 +106,13 @@ const HANDOFF_WRITE_OPERATIONS = [{
100
106
  defaultScopes: ['relation', 'agent'],
101
107
  description: 'Return the current handoff result',
102
108
  sources: ['agent-tool'],
109
+ }, {
110
+ id: 'ec.handoff.recover',
111
+ category: 'write-agent',
112
+ dangerous: true,
113
+ defaultScopes: ['agent'],
114
+ description: 'Recover an open handoff after its target session changed',
115
+ sources: ['agent-tool'],
103
116
  }];
104
117
  const QUEUE_OPERATIONS = [['read', 'Read the current Agent message queue'], ['clear', 'Clear the current Agent message queue'], ['cancel', 'Cancel a current Agent queued message'], ['interrupt', 'Interrupt a current Agent session']].map(([kind, description]) => ({
105
118
  id: `ec.queue.${kind}`,
@@ -358,6 +371,86 @@ const OPERATIONS = [
358
371
  description: '获取文件内容(需路径沙箱检查)',
359
372
  sources: ['slash', 'menu'],
360
373
  },
374
+ {
375
+ id: 'file.root.agent',
376
+ category: 'read',
377
+ dangerous: false,
378
+ defaultScopes: ['agent'],
379
+ description: '获取 Agent 内部数据根目录句柄',
380
+ sources: ['menu'],
381
+ },
382
+ {
383
+ id: 'file.root.relation',
384
+ category: 'read',
385
+ dangerous: false,
386
+ defaultScopes: ['relation', 'agent'],
387
+ description: '获取当前或 owner 指定关系的共享数据根目录句柄',
388
+ sources: ['menu'],
389
+ },
390
+ {
391
+ id: 'file.data.list',
392
+ category: 'read',
393
+ dangerous: false,
394
+ defaultScopes: ['relation', 'agent'],
395
+ description: '列出已授权内部数据根目录',
396
+ sources: ['menu'],
397
+ },
398
+ {
399
+ id: 'file.data.query',
400
+ category: 'read',
401
+ dangerous: false,
402
+ defaultScopes: ['relation', 'agent'],
403
+ description: '查询已授权内部数据根文件元信息',
404
+ sources: ['menu'],
405
+ },
406
+ {
407
+ id: 'file.data.fetch',
408
+ category: 'read',
409
+ dangerous: false,
410
+ defaultScopes: ['relation', 'agent'],
411
+ description: '下载已授权内部数据根文件',
412
+ sources: ['menu'],
413
+ },
414
+ {
415
+ id: 'file.root.storage',
416
+ category: 'read',
417
+ dangerous: false,
418
+ defaultScopes: ['agent'],
419
+ description: '获取 Agent 自有云存储根句柄',
420
+ sources: ['menu'],
421
+ },
422
+ {
423
+ id: 'file.storage.list',
424
+ category: 'read',
425
+ dangerous: false,
426
+ defaultScopes: ['agent'],
427
+ description: '列出 Agent 自有云存储目录',
428
+ sources: ['menu'],
429
+ },
430
+ {
431
+ id: 'file.storage.query',
432
+ category: 'read',
433
+ dangerous: false,
434
+ defaultScopes: ['agent'],
435
+ description: '查询 Agent 自有云存储元信息',
436
+ sources: ['menu'],
437
+ },
438
+ {
439
+ id: 'file.storage.fetch',
440
+ category: 'read',
441
+ dangerous: false,
442
+ defaultScopes: ['agent'],
443
+ description: '获取 Agent 自有云存储短时下载链接',
444
+ sources: ['menu'],
445
+ },
446
+ {
447
+ id: 'file.storage.quota',
448
+ category: 'read',
449
+ dangerous: false,
450
+ defaultScopes: ['agent'],
451
+ description: '查询 Agent 自有云存储配额',
452
+ sources: ['menu'],
453
+ },
361
454
  // ── Trigger Operations ──
362
455
  {
363
456
  id: 'trigger.list',
@@ -446,7 +539,7 @@ const OPERATIONS = [
446
539
  dangerous: false,
447
540
  defaultScopes: ['agent'],
448
541
  description: 'Assign a role to a private or group relation',
449
- sources: ['menu', 'ecweb', 'control'],
542
+ sources: ['menu', 'menu.cli', 'ecweb', 'control', 'agent-tool'],
450
543
  },
451
544
  {
452
545
  id: 'role.revoke',
@@ -454,7 +547,7 @@ const OPERATIONS = [
454
547
  dangerous: false,
455
548
  defaultScopes: ['agent'],
456
549
  description: 'Revoke a private or group relation role assignment',
457
- sources: ['menu', 'ecweb', 'control'],
550
+ sources: ['menu', 'menu.cli', 'ecweb', 'control', 'agent-tool'],
458
551
  },
459
552
  {
460
553
  id: 'role.policy.read',
@@ -470,7 +563,7 @@ const OPERATIONS = [
470
563
  dangerous: false,
471
564
  defaultScopes: ['agent'],
472
565
  description: 'Read private and group relation role assignments',
473
- sources: ['menu', 'ecweb', 'control'],
566
+ sources: ['menu', 'menu.cli', 'ecweb', 'control', 'agent-tool'],
474
567
  },
475
568
  {
476
569
  id: 'role.policy.write',
@@ -485,7 +578,7 @@ const OPERATIONS = [
485
578
  category: 'read',
486
579
  dangerous: false,
487
580
  defaultScopes: ['agent'],
488
- description: 'Read the Agent contact block list',
581
+ description: 'Read Agent contacts, pending requests, and blocked contacts',
489
582
  sources: ['agent-tool'],
490
583
  },
491
584
  {
@@ -504,6 +597,14 @@ const OPERATIONS = [
504
597
  description: 'Block or unblock a canonical contact across bound private channels',
505
598
  sources: ['agent-tool'],
506
599
  },
600
+ {
601
+ id: 'contact.review',
602
+ category: 'write-agent',
603
+ dangerous: false,
604
+ defaultScopes: ['agent'],
605
+ description: 'Review pending Agent contact requests',
606
+ sources: ['agent-tool'],
607
+ },
507
608
  {
508
609
  id: 'connect.read',
509
610
  category: 'read',
@@ -609,6 +710,14 @@ const OPERATIONS = [
609
710
  description: '读取或更新当前 Agent 的 persona.md',
610
711
  sources: ['agent-tool', 'control'],
611
712
  },
713
+ {
714
+ id: 'agent.welcome',
715
+ category: 'write-agent',
716
+ dangerous: false,
717
+ defaultScopes: ['agent'],
718
+ description: '读取或更新当前 Agent 的 Bootstrap welcome.md',
719
+ sources: ['agent-tool', 'control'],
720
+ },
612
721
  {
613
722
  id: 'agent.create',
614
723
  category: 'process',
@@ -617,6 +726,14 @@ const OPERATIONS = [
617
726
  description: '创建新 agent',
618
727
  sources: ['menu', 'menu.cli', 'agent-tool', 'control'],
619
728
  },
729
+ {
730
+ id: 'agent.initialize',
731
+ category: 'process',
732
+ dangerous: true,
733
+ defaultScopes: ['control'],
734
+ description: 'Create and initialize an Agent from an explicit final plan',
735
+ sources: ['menu', 'menu.cli', 'agent-tool', 'control'],
736
+ },
620
737
  {
621
738
  id: 'agent.enable',
622
739
  category: 'process',
@@ -666,6 +783,14 @@ const OPERATIONS = [
666
783
  description: '查看系统状态(敏感信息)',
667
784
  sources: ['slash', 'menu', 'menu.cli', 'agent-tool'],
668
785
  },
786
+ {
787
+ id: 'system.paircode',
788
+ category: 'process',
789
+ dangerous: true,
790
+ defaultScopes: ['process'],
791
+ description: '获取 daemon 远程控制配对码',
792
+ sources: ['menu', 'control'],
793
+ },
669
794
  {
670
795
  id: 'activity.current',
671
796
  category: 'read',
@@ -912,6 +1037,14 @@ const OPERATIONS = [
912
1037
  ...HANDOFF_READ_OPERATIONS,
913
1038
  ...HANDOFF_WRITE_OPERATIONS,
914
1039
  ...QUEUE_OPERATIONS,
1040
+ {
1041
+ id: 'ec.task.resume',
1042
+ category: 'write-agent',
1043
+ dangerous: false,
1044
+ defaultScopes: ['agent'],
1045
+ description: 'Resume a paused or abnormally interrupted Agent session task',
1046
+ sources: ['agent-tool', 'control'],
1047
+ },
915
1048
  {
916
1049
  id: 'ec.version',
917
1050
  category: 'read',
@@ -968,6 +1101,14 @@ const OPERATIONS = [
968
1101
  description: '通过 ec msg send 发送私聊消息',
969
1102
  sources: ['agent-tool'],
970
1103
  },
1104
+ {
1105
+ id: 'ec.menu.request',
1106
+ category: 'write-agent',
1107
+ dangerous: false,
1108
+ defaultScopes: ['relation'],
1109
+ description: 'Send an authenticated remote Control Menu request through the current Agent daemon channel',
1110
+ sources: ['agent-tool'],
1111
+ },
971
1112
  {
972
1113
  id: 'ec.msg.file',
973
1114
  category: 'write-own',
@@ -3,6 +3,7 @@ import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { resolvePaths } from '../../paths.js';
5
5
  export const REMOTE_ACCESS_TOKEN_TTL_MS = 7 * 24 * 60 * 60 * 1000;
6
+ export const DIAGNOSTIC_TOKEN_TTL_MS = 60 * 60 * 1000;
6
7
  // 与 owner 绑定二维码(BindService)的默认有效期保持一致:10 分钟。
7
8
  export const REMOTE_ACCESS_PAIRING_TTL_MS = 10 * 60 * 1000;
8
9
  export function remoteAccessTokenStorePath() {
@@ -11,13 +12,21 @@ export function remoteAccessTokenStorePath() {
11
12
  function loadStore() {
12
13
  try {
13
14
  const value = JSON.parse(fs.readFileSync(remoteAccessTokenStorePath(), 'utf8'));
14
- if (Array.isArray(value.tokens))
15
- return value;
15
+ if (Array.isArray(value.tokens)) {
16
+ return {
17
+ tokens: value.tokens,
18
+ pairingIssuances: Array.isArray(value.pairingIssuances) ? value.pairingIssuances : [],
19
+ };
20
+ }
16
21
  }
17
22
  catch { }
18
- return { tokens: [] };
23
+ return { tokens: [], pairingIssuances: [] };
19
24
  }
20
25
  function saveStore(store) {
26
+ // Issuance history contains no credentials. Keep 30 days; live tokens retain
27
+ // their own provenance even after the corresponding history is pruned.
28
+ const cutoff = Date.now() - 30 * 24 * 60 * 60 * 1000;
29
+ store.pairingIssuances = store.pairingIssuances.filter(record => record.expiresAt >= cutoff);
21
30
  const file = remoteAccessTokenStorePath();
22
31
  fs.mkdirSync(path.dirname(file), { recursive: true });
23
32
  const temporary = `${file}.${process.pid}.tmp`;
@@ -42,7 +51,8 @@ function pruneExpired(store, now) {
42
51
  const before = store.tokens.length;
43
52
  store.tokens = store.tokens.filter(record => typeof record?.token === 'string'
44
53
  && typeof record.lastActive === 'number'
45
- && now - record.lastActive < REMOTE_ACCESS_TOKEN_TTL_MS);
54
+ && Number.isFinite(record.lastActive)
55
+ && now - record.lastActive < (record.diagnostic ? DIAGNOSTIC_TOKEN_TTL_MS : REMOTE_ACCESS_TOKEN_TTL_MS));
46
56
  return store.tokens.length !== before;
47
57
  }
48
58
  function validLabel(value) {
@@ -85,18 +95,105 @@ export class RemoteAccessService {
85
95
  pairingCode = '';
86
96
  pairingExpiresAt = 0;
87
97
  pairingAuthorizedBy;
88
- getPairingCode(authorizedBy, now = Date.now()) {
89
- if (!this.pairingCode || now >= this.pairingExpiresAt) {
90
- this.pairingCode = String(crypto.randomInt(0, 1_000_000)).padStart(6, '0');
98
+ pairingId = '';
99
+ pairingIssuedAt = 0;
100
+ pairingIssuers = [];
101
+ // Do not recycle consumed codes during their original validity window,
102
+ // including when other sources have issued intervening codes.
103
+ recentPairingCodes = new Map();
104
+ /** Synchronous reservation: concurrent invitations cannot issue two grants. */
105
+ issueDiagnosticToken(controlAid, agentAid, ownerAid, now = Date.now()) {
106
+ const store = loadStore();
107
+ pruneExpired(store, now);
108
+ if (store.tokens.some(record => record.diagnostic?.controlAid === controlAid)) {
109
+ throw Object.assign(new Error('本机已有有效诊断授权,请先执行 /diag stop 或 ec diag stop。'), { code: 'DIAG_ALREADY_ACTIVE' });
110
+ }
111
+ const record = {
112
+ token: crypto.randomBytes(32).toString('hex'), createdAt: now, lastActive: now,
113
+ label: 'diagnostic', authorizedBy: ownerAid,
114
+ diagnostic: { controlAid, agentAid, ownerAid, caseId: crypto.randomUUID() },
115
+ };
116
+ store.tokens.push(record);
117
+ saveStore(store);
118
+ return record;
119
+ }
120
+ getDiagnosticToken(controlAid, now = Date.now()) {
121
+ const store = loadStore();
122
+ if (pruneExpired(store, now))
123
+ saveStore(store);
124
+ return store.tokens.find(record => record.diagnostic?.controlAid === controlAid);
125
+ }
126
+ /** Expected token prevents a failed older send from revoking a newer grant. */
127
+ revokeDiagnosticToken(controlAid, expectedToken) {
128
+ const store = loadStore();
129
+ const pruned = pruneExpired(store, Date.now());
130
+ const index = store.tokens.findIndex(record => record.diagnostic?.controlAid === controlAid
131
+ && (expectedToken === undefined || record.token === expectedToken));
132
+ if (index < 0) {
133
+ if (pruned)
134
+ saveStore(store);
135
+ return undefined;
136
+ }
137
+ const [record] = store.tokens.splice(index, 1);
138
+ saveStore(store);
139
+ return record;
140
+ }
141
+ getPairingCode(authorizedBy, now = Date.now(), source = 'host-cli') {
142
+ const owner = validOwner(authorizedBy);
143
+ if (!['host-cli', 'pair', 'menu', 'ecweb-local'].includes(source)
144
+ || ((source === 'pair' || source === 'menu') && !owner)) {
145
+ throw new Error('pairing source requires a valid source and an owner for /pair or Menu');
146
+ }
147
+ const generated = !this.pairingCode || now >= this.pairingExpiresAt;
148
+ if (generated) {
149
+ for (const [code, expiresAt] of this.recentPairingCodes) {
150
+ if (now >= expiresAt)
151
+ this.recentPairingCodes.delete(code);
152
+ }
153
+ // Choose uniformly from unused values without an unbounded retry loop.
154
+ let code = crypto.randomInt(0, 1_000_000 - this.recentPairingCodes.size);
155
+ for (const occupied of [...this.recentPairingCodes.keys()].sort((a, b) => a - b)) {
156
+ if (occupied > code)
157
+ break;
158
+ code++;
159
+ }
160
+ this.pairingCode = String(code).padStart(6, '0');
161
+ this.pairingId = crypto.randomUUID();
162
+ this.pairingIssuedAt = now;
91
163
  this.pairingExpiresAt = now + REMOTE_ACCESS_PAIRING_TTL_MS;
164
+ this.recentPairingCodes.set(code, this.pairingExpiresAt);
92
165
  this.pairingAuthorizedBy = undefined;
166
+ this.pairingIssuers = [];
93
167
  }
94
- const owner = validOwner(authorizedBy);
168
+ const previousOwner = this.pairingAuthorizedBy;
95
169
  if (!this.pairingAuthorizedBy && owner)
96
170
  this.pairingAuthorizedBy = owner;
171
+ const issuer = {
172
+ source,
173
+ issuedAt: now,
174
+ ...((source === 'pair' || source === 'menu') && owner ? { authorizedBy: owner } : {}),
175
+ };
176
+ const addedIssuer = !this.pairingIssuers.some(candidate => candidate.source === issuer.source
177
+ && candidate.authorizedBy === issuer.authorizedBy);
178
+ if (addedIssuer) {
179
+ this.pairingIssuers.push(issuer);
180
+ }
181
+ try {
182
+ this.recordPairingIssuance();
183
+ }
184
+ catch (error) {
185
+ if (generated)
186
+ this.clearPairingCode();
187
+ else {
188
+ this.pairingAuthorizedBy = previousOwner;
189
+ if (addedIssuer)
190
+ this.pairingIssuers.pop();
191
+ }
192
+ throw error;
193
+ }
97
194
  return { code: this.pairingCode, expiresAt: this.pairingExpiresAt };
98
195
  }
99
- redeemPairingCode(code, label, authorizedBy, now = Date.now()) {
196
+ redeemPairingCode(code, label, now = Date.now()) {
100
197
  if (typeof code !== 'string' || !/^\d{6}$/.test(code)) {
101
198
  return { ok: false, code: 'INVALID_ARGUMENT', error: 'pairing code must be six digits' };
102
199
  }
@@ -110,17 +207,65 @@ export class RemoteAccessService {
110
207
  const token = crypto.randomBytes(32).toString('hex');
111
208
  const store = loadStore();
112
209
  pruneExpired(store, now);
113
- const owner = validOwner(authorizedBy) ?? this.pairingAuthorizedBy;
210
+ const owner = this.pairingAuthorizedBy;
211
+ const pairingId = this.pairingId;
212
+ const pairingIssuers = this.pairingIssuers.map(issuer => ({ ...issuer }));
114
213
  store.tokens.push({
115
214
  token,
116
215
  createdAt,
117
216
  lastActive: createdAt,
118
217
  label: validLabel(label),
119
218
  ...(owner ? { authorizedBy: owner } : {}),
219
+ pairingId,
220
+ pairingIssuers,
120
221
  });
222
+ const issuance = store.pairingIssuances.find(record => record.pairingId === pairingId);
223
+ if (issuance) {
224
+ issuance.redeemedAt = createdAt;
225
+ }
226
+ else {
227
+ store.pairingIssuances.push({
228
+ pairingId,
229
+ issuedAt: this.pairingIssuedAt,
230
+ expiresAt: this.pairingExpiresAt,
231
+ issuers: pairingIssuers,
232
+ redeemedAt: createdAt,
233
+ });
234
+ }
121
235
  saveStore(store);
236
+ // A pairing code is a one-time capability. Clear it only after the token
237
+ // and provenance record have been atomically saved. No await separates
238
+ // validation, persistence and consumption, so concurrent IPC calls cannot
239
+ // grant the same code twice.
240
+ this.clearPairingCode();
122
241
  return { ok: true, token, expiresAt: createdAt + REMOTE_ACCESS_TOKEN_TTL_MS };
123
242
  }
243
+ clearPairingCode() {
244
+ this.pairingCode = '';
245
+ this.pairingExpiresAt = 0;
246
+ this.pairingAuthorizedBy = undefined;
247
+ this.pairingId = '';
248
+ this.pairingIssuedAt = 0;
249
+ this.pairingIssuers = [];
250
+ }
251
+ recordPairingIssuance() {
252
+ if (!this.pairingId || !this.pairingCode)
253
+ return;
254
+ const store = loadStore();
255
+ const existing = store.pairingIssuances.find(record => record.pairingId === this.pairingId);
256
+ const record = {
257
+ pairingId: this.pairingId,
258
+ issuedAt: this.pairingIssuedAt,
259
+ expiresAt: this.pairingExpiresAt,
260
+ issuers: this.pairingIssuers.map(issuer => ({ ...issuer })),
261
+ ...(existing?.redeemedAt ? { redeemedAt: existing.redeemedAt } : {}),
262
+ };
263
+ if (existing)
264
+ Object.assign(existing, record);
265
+ else
266
+ store.pairingIssuances.push(record);
267
+ saveStore(store);
268
+ }
124
269
  issueLocalToken(label, now = Date.now()) {
125
270
  const token = crypto.randomBytes(32).toString('hex');
126
271
  const store = loadStore();
@@ -1,10 +1,12 @@
1
1
  import fs from 'fs';
2
+ import { loadAgent } from '../config-store.js';
3
+ import { resolveAgentLifecycle } from '../config/lifecycle.js';
2
4
  import { agentMdPath, resolvePaths } from '../paths.js';
3
5
  import { parseAgentDisplayName, resolveAgentDisplayName } from '../aun/aid/agentmd.js';
4
- import { generateWelcomeMessage } from '../utils/welcome.js';
6
+ import { generateWelcomeMessage, parseAgentWelcomeMetadata, readAgentWelcomeTemplate, renderPostBootstrapWelcomeMessage, } from '../utils/welcome.js';
5
7
  import * as outbox from '../aun/outbox.js';
6
8
  import { chatDirPath } from './session/session-fs-store.js';
7
- import { hasMessageLogOperation } from './message/message-log.js';
9
+ import { hasMessageLogOperation, findMessageLogOperationMessageId } from './message/message-log.js';
8
10
  import { isDeliveryTarget, sameDeliveryTarget } from './message/message-utils.js';
9
11
  export const BOOTSTRAP_MESSAGE_TTL_MS = 30 * 24 * 60 * 60 * 1000;
10
12
  export const BOOTSTRAP_NOTIFICATION_WINDOW_MS = 24 * 60 * 60 * 1000;
@@ -32,10 +34,27 @@ export function isPostBootstrapWelcomeOperationId(operationId, aid) {
32
34
  export function renderPostBootstrapWelcome(aid, owner, ownerName) {
33
35
  const mdPath = agentMdPath(aid);
34
36
  const agentMd = fs.existsSync(mdPath) ? fs.readFileSync(mdPath, 'utf-8') : '';
35
- const agentName = parseAgentDisplayName(agentMd)
37
+ const metadata = parseAgentWelcomeMetadata(agentMd);
38
+ const agentName = metadata.name || parseAgentDisplayName(agentMd)
36
39
  || resolveAgentDisplayName(aid)
37
40
  || aid.split('.')[0];
38
41
  const fallbackOwnerName = owner.replace(/^@/, '').split('.')[0];
42
+ const template = readAgentWelcomeTemplate(aid);
43
+ if (template) {
44
+ return renderPostBootstrapWelcomeMessage({
45
+ template,
46
+ agentName,
47
+ agentDescription: metadata.description,
48
+ agentAid: aid,
49
+ agentTags: metadata.tags,
50
+ ownerName: ownerName || fallbackOwnerName,
51
+ ownerAid: owner,
52
+ });
53
+ }
54
+ const config = loadAgent(aid);
55
+ if (config && resolveAgentLifecycle(config) === 'bootstrapping' && config.initialization?.mode !== 'supplied') {
56
+ throw new Error('WELCOME_REQUIRED: Owner-confirmed welcome.md is required before activation');
57
+ }
39
58
  return generateWelcomeMessage({
40
59
  channelType: 'aun',
41
60
  agentName,
@@ -49,15 +68,18 @@ export function preparePostBootstrapWelcomeOutbox(aid, owner, ownerName, deliver
49
68
  const legacyOperationId = postBootstrapWelcomeOperationId(aid);
50
69
  if (hasMessageLogOperation(chatDir, operationId) || hasMessageLogOperation(chatDir, legacyOperationId))
51
70
  return;
52
- const legacy = outbox.findByDedupeKey(aid, legacyOperationId);
71
+ const legacy = outbox.findByDedupeKey(aid, legacyOperationId, { includeTerminal: true });
53
72
  if (legacy?.channelId === owner)
54
73
  return;
55
- const existing = outbox.findByDedupeKey(aid, operationId);
74
+ const existing = outbox.findByDedupeKey(aid, operationId, { includeTerminal: true });
56
75
  if (existing
57
76
  && existing.channelId === owner
58
77
  && isDeliveryTarget(existing.delivery)
59
78
  && sameDeliveryTarget(existing.delivery, delivery))
60
79
  return;
80
+ // Render before touching an existing route record: malformed content must not
81
+ // remove the only durable record of an earlier attempt.
82
+ const text = renderPostBootstrapWelcome(aid, owner, ownerName);
61
83
  if (existing)
62
84
  outbox.remove(aid, existing.id);
63
85
  outbox.enqueue(aid, {
@@ -66,7 +88,7 @@ export function preparePostBootstrapWelcomeOutbox(aid, owner, ownerName, deliver
66
88
  dedupeKey: operationId,
67
89
  critical: true,
68
90
  type: 'text',
69
- text: renderPostBootstrapWelcome(aid, owner, ownerName),
91
+ text,
70
92
  ttl: BOOTSTRAP_MESSAGE_TTL_MS,
71
93
  context: {
72
94
  delivery,
@@ -79,6 +101,43 @@ export function preparePostBootstrapWelcomeOutbox(aid, owner, ownerName, deliver
79
101
  },
80
102
  });
81
103
  }
104
+ /** Read durable evidence, including failures and already accepted operations. */
105
+ export function postBootstrapWelcomeStatus(aid, owners) {
106
+ const entries = findPostBootstrapWelcomeOutbox(aid, { includeTerminal: true });
107
+ return {
108
+ deliveries: Array.from(new Set(owners)).map(owner => {
109
+ const operationId = postBootstrapWelcomeOperationId(aid, owner);
110
+ const chatDir = chatDirPath(resolvePaths().sessionsDir, 'aun', owner, aid);
111
+ const loggedId = findMessageLogOperationMessageId(chatDir, operationId)
112
+ ?? findMessageLogOperationMessageId(chatDir, postBootstrapWelcomeOperationId(aid));
113
+ const entry = entries.find(candidate => candidate.channelId === owner
114
+ && candidate.delivery?.chatType === 'private'
115
+ && (candidate.dedupeKey === operationId || candidate.dedupeKey === postBootstrapWelcomeOperationId(aid)));
116
+ const base = { owner, operationId, ...(entry ? { outboxId: entry.id } : {}) };
117
+ if (loggedId)
118
+ return { ...base, status: 'sent', messageId: loggedId };
119
+ if (!entry)
120
+ return { ...base, status: 'not_prepared' };
121
+ const state = outbox.deliveryState(aid, entry.id);
122
+ if (state.status === 'sent' || state.status === 'failed')
123
+ return { ...base, ...state };
124
+ if (state.status === 'missing')
125
+ return { ...base, status: 'not_prepared' };
126
+ return { ...base, status: 'queued', error: entry.lastError, code: entry.lastErrorCode };
127
+ }),
128
+ };
129
+ }
130
+ /** Supersede unsent setup prompts when the agent becomes active. Keep diagnostic evidence. */
131
+ export function cancelPendingBootstrapPrompts(aid) {
132
+ for (const entry of outbox.findByDedupePrefix(aid, bootstrapInitialMessageOperationPrefix(aid))) {
133
+ if (entry.deliveryReceipt)
134
+ continue;
135
+ outbox.markTerminal(aid, entry.id, {
136
+ code: 'BOOTSTRAP_SUPERSEDED',
137
+ error: 'Bootstrap is complete; the initial prompt is no longer needed',
138
+ }, entry);
139
+ }
140
+ }
82
141
  export function hasPendingPostBootstrapWelcomeOutbox(aid) {
83
142
  return findPostBootstrapWelcomeOutbox(aid)
84
143
  .some(entry => outbox.isDeliveryForChannel(entry.delivery, entry.channelId));
@@ -92,7 +151,7 @@ export function findPostBootstrapWelcomeOutbox(aid, opts = {}) {
92
151
  }
93
152
  export function pendingPostBootstrapWelcomeOwners(aid) {
94
153
  return findPostBootstrapWelcomeOutbox(aid)
95
- .filter(entry => outbox.isDeliveryForChannel(entry.delivery, entry.channelId))
154
+ .filter(entry => !entry.context?.sessionId && outbox.isDeliveryForChannel(entry.delivery, entry.channelId))
96
155
  .map(entry => entry.channelId)
97
156
  .filter((owner, index, all) => all.indexOf(owner) === index);
98
157
  }