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
package/dist/index.js CHANGED
@@ -14,6 +14,7 @@ import { CodexSessionFileAdapter } from './core/session/adapters/codex-session-f
14
14
  import { GeminiSessionFileAdapter } from './core/session/adapters/gemini-session-file-adapter.js';
15
15
  import { EcagentSessionFileAdapter } from './core/session/adapters/ecagent-session-file-adapter.js';
16
16
  import { autoMigrateIfNeeded, loadDefaults, loadAllAgents, migrateIdentitiesIfNeeded, loadDaemonConfig, initializeEckSnapshotsConfig, isFullAccessEnabled } from './config-store.js';
17
+ import { migrateRelationSharedDirectories } from './core/relation/shared-data.js';
17
18
  import { DEFAULT_ECWEB_PORT, ecwebService } from './config/daemon-services.js';
18
19
  import { ConfigTarget, initConfigManager, onConfigWrite, read as readConfig, shouldFailFastForMissingOwners } from './config/config-manager.js';
19
20
  import { ensureRoleConfigV4OnStartup, ensureRoleConfigV5OnStartup } from './config/role-migration-startup.js';
@@ -46,13 +47,15 @@ import { MessageQueue } from './core/message/message-queue.js';
46
47
  import { MessageBridge } from './core/message/message-bridge.js';
47
48
  import { evaluateOutboundContactAdmission } from './core/message/message-admission.js';
48
49
  import { enforceMenuResponseSize, evaluateMenuClientCompatibility, evolMenuResponseTransportMetadata, MenuRequestDeduper, hasValidMenuId, menuFailure, menuPayloadFingerprint, normalizeMenuResponseTiming, parseMenuControl, validateMenuRequest, withMenuProcessingTime, logMenuRequestCompleted, logMenuRequestReceived } from './core/command/menu-protocol.js';
49
- import { readInstalledEvolcoreVersion } from './utils/evolcore-version.js';
50
+ import { computeBuildId, readBuildIdentity, readEvolcoreRuntimeFlavor, readEvolcoreStartedAtIso, readInstalledEvolcoreVersion, tagErrorTextWithEvolcoreVersion, } from './utils/evolcore-version.js';
50
51
  import { recoverAllRoleMutationsSync } from './core/command/role-menu.js';
51
52
  import { HandoffRuntime } from './core/handoff/runtime.js';
52
53
  import { BootstrapService, completeBootstrapWithWelcome } from './core/bootstrap-service.js';
53
54
  import { postBootstrapWelcomeOperationId } from './core/bootstrap-messages.js';
54
55
  import { MessageCache } from './core/message/message-cache.js';
55
56
  import { CommandHandler, isProcessLevelOwner } from './core/command/command-handler.js';
57
+ import { executeLocalInitializationCommand, AgentInitializationService, hasSuppliedInitialization, readInitialization, parseInitializationArgs, parseAgentWelcomeArgs, resolveInitializationPlan, } from './cli/agent-create.js';
58
+ import { loadAgent } from './config-store.js';
56
59
  import { AgentApplicationService } from './core/agent-application-service.js';
57
60
  import { EventBus } from './core/event-bus.js';
58
61
  import { eventPatternIncludesInternal, getEventCatalog } from './core/event-catalog.js';
@@ -60,12 +63,13 @@ import { StatsCollector } from './utils/stats.js';
60
63
  import { AidStatsCollector } from './utils/stats.js';
61
64
  import { PermissionGateway } from './core/permission/approval-gateway.js';
62
65
  import { InteractionRouter, registerAdapterInteractions } from './core/interaction-router.js';
66
+ import { GroupInviteReviewService } from './core/group/invite-service.js';
63
67
  import { AgentDelegationRegistry, authorizeDelegatedAunMsgSend, hasTrustedFullAccessDelegation, trustedExecutionIdentityInput, } from './core/auth/agent-delegation.js';
64
68
  import { authorizeOperation, buildAuthSubject, canApprovePersistentFullAccessTrigger, isCrossAgentTriggerOperationAllowed, } from './core/auth/auth-gateway.js';
65
69
  import { authorizationDenialData, formatAuthorizationDenial } from './core/auth/authorization-denial.js';
66
70
  import { auditFullAccessEvent } from './core/auth/authorization-audit.js';
67
71
  import { parsePeerKey } from './core/relation/peer-identity.js';
68
- import { isHClassPath } from './core/permission/protected-paths.js';
72
+ import { isHClassPath, isSameOrDescendant } from './core/permission/protected-paths.js';
69
73
  import { ChannelLoader, tryParseChannelKey } from './core/channel-loader.js';
70
74
  import { AgentLoader } from './core/baseagent-loader.js';
71
75
  import { EvolAgentRegistry } from './core/evolagent-registry.js';
@@ -73,8 +77,11 @@ import { createRunnerReloadTransaction, disposeAgentInstances } from './core/run
73
77
  import { AgentReloadBusyError, AgentReloadCoordinator } from './core/agent-reload-coordinator.js';
74
78
  import { buildReloadHooks } from './core/channel-loader.js';
75
79
  import { IpcServer } from './ipc.js';
80
+ import { executeTaskResume } from './core/task/task-resume-operation.js';
81
+ import { TASK_RECOVERY_PROMPT } from './core/task/task-recovery-service.js';
76
82
  import { logger, setLogLevel } from './utils/logger.js';
77
83
  import { remoteAccessService } from './core/auth/remote-access-store.js';
84
+ import { DiagService, executeDiagIpc } from './core/command/diag-service.js';
78
85
  import { createEcwebSupervisor } from './utils/ecweb-supervisor.js';
79
86
  import { writeMain, removeAll, isMainWinner, scanInstances } from './utils/instance-registry.js';
80
87
  import { detectDuplicates } from './core/evolagent-registry.js';
@@ -97,6 +104,7 @@ import { DAEMON_RUNTIME_EPOCH_ENV, ensureProcessManagedTempDir } from './cli/tas
97
104
  import { appendMessageLog, buildOutboundEntry, classifyAunPayloadForLog } from './core/message/message-log.js';
98
105
  import { normalizeAunMentionEntries } from './aun/msg/mention-schema.js';
99
106
  import { MAIN_PACKAGE_NAME } from './product.js';
107
+ import { sanitizeMenuLogValue } from './core/command/menu-protocol.js';
100
108
  import fs from 'fs';
101
109
  import crypto from 'crypto';
102
110
  import { fileURLToPath } from 'url';
@@ -147,6 +155,45 @@ function summarizeOutboundPayload(payload) {
147
155
  }
148
156
  return s;
149
157
  }
158
+ /**
159
+ * 给用户可见的错误文案打上 EvolCore 版本标记,便于从对端截图直接判断版本与运行形态。
160
+ *
161
+ * 打标范围只含"出错了"语义的 payload:
162
+ * - `result.error` / `system.error`:任务级与系统级失败(含沙箱、权限、超时)
163
+ * - `status.error`:终态元数据,附带结构化版本字段供前端不受截断影响地展示
164
+ * - `command.error`:仅当带 `reasonCode`(授权/权限拒绝)时才打。命令参数写错、
165
+ * 未知命令这类输入校验反馈不打——它们与版本无关,逐条加前缀只会变成噪音。
166
+ *
167
+ * 返回新对象,不改调用方持有的 payload(同一 payload 可能被重复发送到多个渠道)。
168
+ */
169
+ function tagErrorPayloadWithEvolcoreVersion(payload) {
170
+ switch (payload.kind) {
171
+ case 'result.error':
172
+ case 'system.error':
173
+ return { ...payload, text: tagErrorTextWithEvolcoreVersion(payload.text) };
174
+ case 'command.error':
175
+ if (!payload.reasonCode)
176
+ return payload;
177
+ return { ...payload, text: tagErrorTextWithEvolcoreVersion(payload.text) };
178
+ case 'status.error': {
179
+ const metadata = payload.metadata;
180
+ return {
181
+ ...payload,
182
+ metadata: {
183
+ ...metadata,
184
+ ...(typeof metadata?.message === 'string'
185
+ ? { message: tagErrorTextWithEvolcoreVersion(metadata.message) }
186
+ : {}),
187
+ evolcoreVersion: readInstalledEvolcoreVersion(),
188
+ evolcoreFlavor: readEvolcoreRuntimeFlavor(),
189
+ evolcoreStartedAt: readEvolcoreStartedAtIso(),
190
+ },
191
+ };
192
+ }
193
+ default:
194
+ return payload;
195
+ }
196
+ }
150
197
  function shouldCountSentPayload(payload) {
151
198
  return [
152
199
  'result.text',
@@ -333,153 +380,153 @@ function seedUpgradeCheckTrigger(manager, owner) {
333
380
  // take the watchdog down with it.
334
381
  const monitorEntry = platform.resolveCliEntry(packageRoot);
335
382
  const stageParent = path.join(resolvePaths().root, 'data', 'monitor-stage');
336
- fs.writeFileSync(scriptPath, `#!/usr/bin/env node
337
- const fs = require('node:fs');
338
- const path = require('node:path');
339
- const { existsSync, readFileSync } = fs;
340
- const { spawn } = require('node:child_process');
341
-
342
- const CLI_PATH = ${JSON.stringify(monitorEntry.entry)};
343
- const CLI_ARGS = ${JSON.stringify(monitorEntry.args)};
344
- const PACKAGE_ROOT = ${JSON.stringify(packageRoot)};
345
- const STAGE_PARENT = ${JSON.stringify(stageParent)};
346
- const PACKAGE_JSON = ${JSON.stringify(packageJsonPath)};
347
- const PACKAGE_NAME = ${JSON.stringify(MAIN_PACKAGE_NAME)};
348
- const DEV_MODE = ${devMode ? 'true' : 'false'};
349
-
350
- function emit(outcome, text) {
351
- process.stdout.write(JSON.stringify({ outcome, text, files: [] }));
352
- }
353
-
354
- function compareVersions(a, b) {
355
- const pa = a.split('-')[0].split('.').map(Number);
356
- const pb = b.split('-')[0].split('.').map(Number);
357
- const count = Math.max(pa.length, pb.length);
358
- for (let i = 0; i < count; i += 1) {
359
- const av = Number.isFinite(pa[i]) ? pa[i] : 0;
360
- const bv = Number.isFinite(pb[i]) ? pb[i] : 0;
361
- if (av < bv) return -1;
362
- if (av > bv) return 1;
363
- }
364
- return 0;
365
- }
366
-
367
- async function latestVersion() {
368
- const controller = new AbortController();
369
- const timer = setTimeout(() => controller.abort(), 10_000);
370
- try {
371
- const response = await fetch('https://registry.npmjs.org/' + encodeURIComponent(PACKAGE_NAME) + '/latest', {
372
- signal: controller.signal,
373
- headers: { Accept: 'application/json' },
374
- });
375
- if (!response.ok) return null;
376
- const data = await response.json();
377
- return typeof data.version === 'string' ? data.version : null;
378
- } finally {
379
- clearTimeout(timer);
380
- }
381
- }
382
-
383
- async function main() {
384
- if (DEV_MODE) {
385
- emit('noop', 'upgrade check skipped in dev mode');
386
- return;
387
- }
388
-
389
- let localVersion;
390
- try {
391
- localVersion = JSON.parse(readFileSync(PACKAGE_JSON, 'utf8')).version;
392
- } catch {
393
- emit('error', 'failed to read local ec version');
394
- return;
395
- }
396
- if (typeof localVersion !== 'string' || !localVersion) {
397
- emit('error', 'failed to read local ec version');
398
- return;
399
- }
400
-
401
- let remoteVersion;
402
- try {
403
- remoteVersion = await latestVersion();
404
- } catch {
405
- remoteVersion = null;
406
- }
407
- if (!remoteVersion) {
408
- emit('error', 'failed to check evolcore latest version');
409
- return;
410
- }
411
- if (compareVersions(localVersion, remoteVersion) >= 0) {
412
- emit('noop', 'evolcore is already up to date (' + localVersion + '; latest ' + remoteVersion + ')');
413
- return;
414
- }
415
- if (!existsSync(CLI_PATH)) {
416
- emit('error', 'restart-monitor entry not found: ' + CLI_PATH);
417
- return;
418
- }
419
-
420
- // Installed mode: copy the monitor out of the package before it upgrades that
421
- // same package. Mirrors prepareMonitorStage(); kept inline because this file
422
- // is generated standalone CJS. Any failure degrades to launching in place —
423
- // losing upgrade isolation beats losing the upgrade.
424
- let launchArgs = CLI_ARGS;
425
- const launchEnv = { ...process.env };
426
- if (!DEV_MODE && STAGE_PARENT) {
427
- try {
428
- const stageDir = path.join(STAGE_PARENT, 'upgrade-' + process.pid + '-' + Date.now());
429
- fs.mkdirSync(stageDir, { recursive: true });
430
- fs.cpSync(path.join(PACKAGE_ROOT, 'dist'), path.join(stageDir, 'dist'), { recursive: true });
431
- if (existsSync(PACKAGE_JSON)) fs.copyFileSync(PACKAGE_JSON, path.join(stageDir, 'package.json'));
432
- const modulesSource = path.join(PACKAGE_ROOT, 'node_modules');
433
- if (existsSync(modulesSource)) {
434
- const staged = path.join(stageDir, 'node_modules');
435
- try {
436
- // junction works unelevated on Windows; dir symlink elsewhere.
437
- fs.symlinkSync(modulesSource, staged, process.platform === 'win32' ? 'junction' : 'dir');
438
- fs.readdirSync(staged);
439
- } catch (linkError) {
440
- fs.rmSync(staged, { recursive: true, force: true });
441
- fs.cpSync(modulesSource, staged, { recursive: true });
442
- }
443
- }
444
- const stagedEntry = path.join(stageDir, 'dist', 'cli', 'index.js');
445
- if (existsSync(stagedEntry)) {
446
- launchArgs = [stagedEntry];
447
- launchEnv.EVOLCORE_TARGET_PACKAGE_ROOT = PACKAGE_ROOT;
448
- launchEnv.EVOLCORE_MONITOR_STAGE = stageDir;
449
- }
450
- } catch (stageError) {
451
- // Fall through with the in-place entry.
452
- }
453
- }
454
-
455
- try {
456
- const child = spawn(process.execPath, [...launchArgs, 'restart-monitor'], {
457
- detached: true,
458
- stdio: 'ignore',
459
- windowsHide: process.platform === 'win32',
460
- env: launchEnv,
461
- });
462
- child.unref();
463
- const pid = child.pid;
464
- // The daemon is still running here — the monitor is what stops it. So a
465
- // monitor that dies on load costs the upgrade, not the service; still,
466
- // report it accurately instead of claiming success.
467
- await new Promise(resolve => setTimeout(resolve, 3000));
468
- let alive = false;
469
- try { process.kill(pid, 0); alive = true; } catch (e) { alive = false; }
470
- if (!alive) {
471
- emit('error', 'restart-monitor exited during startup; upgrade to ' + remoteVersion + ' not applied');
472
- return;
473
- }
474
- emit('success', 'evolcore upgrade available: ' + localVersion + ' -> ' + remoteVersion + '; restart-monitor started');
475
- } catch (error) {
476
- emit('error', 'failed to start restart-monitor: ' + (error && error.message ? error.message : String(error)));
477
- }
478
- }
479
-
480
- main().catch((error) => {
481
- emit('error', 'upgrade check failed: ' + (error && error.message ? error.message : String(error)));
482
- });
383
+ fs.writeFileSync(scriptPath, `#!/usr/bin/env node
384
+ const fs = require('node:fs');
385
+ const path = require('node:path');
386
+ const { existsSync, readFileSync } = fs;
387
+ const { spawn } = require('node:child_process');
388
+
389
+ const CLI_PATH = ${JSON.stringify(monitorEntry.entry)};
390
+ const CLI_ARGS = ${JSON.stringify(monitorEntry.args)};
391
+ const PACKAGE_ROOT = ${JSON.stringify(packageRoot)};
392
+ const STAGE_PARENT = ${JSON.stringify(stageParent)};
393
+ const PACKAGE_JSON = ${JSON.stringify(packageJsonPath)};
394
+ const PACKAGE_NAME = ${JSON.stringify(MAIN_PACKAGE_NAME)};
395
+ const DEV_MODE = ${devMode ? 'true' : 'false'};
396
+
397
+ function emit(outcome, text) {
398
+ process.stdout.write(JSON.stringify({ outcome, text, files: [] }));
399
+ }
400
+
401
+ function compareVersions(a, b) {
402
+ const pa = a.split('-')[0].split('.').map(Number);
403
+ const pb = b.split('-')[0].split('.').map(Number);
404
+ const count = Math.max(pa.length, pb.length);
405
+ for (let i = 0; i < count; i += 1) {
406
+ const av = Number.isFinite(pa[i]) ? pa[i] : 0;
407
+ const bv = Number.isFinite(pb[i]) ? pb[i] : 0;
408
+ if (av < bv) return -1;
409
+ if (av > bv) return 1;
410
+ }
411
+ return 0;
412
+ }
413
+
414
+ async function latestVersion() {
415
+ const controller = new AbortController();
416
+ const timer = setTimeout(() => controller.abort(), 10_000);
417
+ try {
418
+ const response = await fetch('https://registry.npmjs.org/' + encodeURIComponent(PACKAGE_NAME) + '/latest', {
419
+ signal: controller.signal,
420
+ headers: { Accept: 'application/json' },
421
+ });
422
+ if (!response.ok) return null;
423
+ const data = await response.json();
424
+ return typeof data.version === 'string' ? data.version : null;
425
+ } finally {
426
+ clearTimeout(timer);
427
+ }
428
+ }
429
+
430
+ async function main() {
431
+ if (DEV_MODE) {
432
+ emit('noop', 'upgrade check skipped in dev mode');
433
+ return;
434
+ }
435
+
436
+ let localVersion;
437
+ try {
438
+ localVersion = JSON.parse(readFileSync(PACKAGE_JSON, 'utf8')).version;
439
+ } catch {
440
+ emit('error', 'failed to read local ec version');
441
+ return;
442
+ }
443
+ if (typeof localVersion !== 'string' || !localVersion) {
444
+ emit('error', 'failed to read local ec version');
445
+ return;
446
+ }
447
+
448
+ let remoteVersion;
449
+ try {
450
+ remoteVersion = await latestVersion();
451
+ } catch {
452
+ remoteVersion = null;
453
+ }
454
+ if (!remoteVersion) {
455
+ emit('error', 'failed to check evolcore latest version');
456
+ return;
457
+ }
458
+ if (compareVersions(localVersion, remoteVersion) >= 0) {
459
+ emit('noop', 'evolcore is already up to date (' + localVersion + '; latest ' + remoteVersion + ')');
460
+ return;
461
+ }
462
+ if (!existsSync(CLI_PATH)) {
463
+ emit('error', 'restart-monitor entry not found: ' + CLI_PATH);
464
+ return;
465
+ }
466
+
467
+ // Installed mode: copy the monitor out of the package before it upgrades that
468
+ // same package. Mirrors prepareMonitorStage(); kept inline because this file
469
+ // is generated standalone CJS. Any failure degrades to launching in place —
470
+ // losing upgrade isolation beats losing the upgrade.
471
+ let launchArgs = CLI_ARGS;
472
+ const launchEnv = { ...process.env };
473
+ if (!DEV_MODE && STAGE_PARENT) {
474
+ try {
475
+ const stageDir = path.join(STAGE_PARENT, 'upgrade-' + process.pid + '-' + Date.now());
476
+ fs.mkdirSync(stageDir, { recursive: true });
477
+ fs.cpSync(path.join(PACKAGE_ROOT, 'dist'), path.join(stageDir, 'dist'), { recursive: true });
478
+ if (existsSync(PACKAGE_JSON)) fs.copyFileSync(PACKAGE_JSON, path.join(stageDir, 'package.json'));
479
+ const modulesSource = path.join(PACKAGE_ROOT, 'node_modules');
480
+ if (existsSync(modulesSource)) {
481
+ const staged = path.join(stageDir, 'node_modules');
482
+ try {
483
+ // junction works unelevated on Windows; dir symlink elsewhere.
484
+ fs.symlinkSync(modulesSource, staged, process.platform === 'win32' ? 'junction' : 'dir');
485
+ fs.readdirSync(staged);
486
+ } catch (linkError) {
487
+ fs.rmSync(staged, { recursive: true, force: true });
488
+ fs.cpSync(modulesSource, staged, { recursive: true });
489
+ }
490
+ }
491
+ const stagedEntry = path.join(stageDir, 'dist', 'cli', 'index.js');
492
+ if (existsSync(stagedEntry)) {
493
+ launchArgs = [stagedEntry];
494
+ launchEnv.EVOLCORE_TARGET_PACKAGE_ROOT = PACKAGE_ROOT;
495
+ launchEnv.EVOLCORE_MONITOR_STAGE = stageDir;
496
+ }
497
+ } catch (stageError) {
498
+ // Fall through with the in-place entry.
499
+ }
500
+ }
501
+
502
+ try {
503
+ const child = spawn(process.execPath, [...launchArgs, 'restart-monitor'], {
504
+ detached: true,
505
+ stdio: 'ignore',
506
+ windowsHide: process.platform === 'win32',
507
+ env: launchEnv,
508
+ });
509
+ child.unref();
510
+ const pid = child.pid;
511
+ // The daemon is still running here — the monitor is what stops it. So a
512
+ // monitor that dies on load costs the upgrade, not the service; still,
513
+ // report it accurately instead of claiming success.
514
+ await new Promise(resolve => setTimeout(resolve, 3000));
515
+ let alive = false;
516
+ try { process.kill(pid, 0); alive = true; } catch (e) { alive = false; }
517
+ if (!alive) {
518
+ emit('error', 'restart-monitor exited during startup; upgrade to ' + remoteVersion + ' not applied');
519
+ return;
520
+ }
521
+ emit('success', 'evolcore upgrade available: ' + localVersion + ' -> ' + remoteVersion + '; restart-monitor started');
522
+ } catch (error) {
523
+ emit('error', 'failed to start restart-monitor: ' + (error && error.message ? error.message : String(error)));
524
+ }
525
+ }
526
+
527
+ main().catch((error) => {
528
+ emit('error', 'upgrade check failed: ' + (error && error.message ? error.message : String(error)));
529
+ });
483
530
  `);
484
531
  // Remove the pre-Node script left by older installations so it cannot be
485
532
  // selected by a stale trigger definition or run manually by accident.
@@ -489,6 +536,51 @@ main().catch((error) => {
489
536
  atomicWriteJson(manager.definitionPath(definition.id), definition);
490
537
  fs.rmSync(manager.activePath(definition.id), { force: true });
491
538
  }
539
+ /**
540
+ * Keep one daemon-owned recovery Trigger for all local Agent sessions. The
541
+ * event-session feedback path resolves the owning Agent from task:error rather
542
+ * than treating the daemon AID as the target Agent.
543
+ */
544
+ function seedTaskRecoveryTrigger(manager, owner) {
545
+ const now = Date.now();
546
+ const definition = normalizeTriggerDefinition({
547
+ $schema_version: 3.1,
548
+ id: '__task-recovery',
549
+ agentAid: owner.aid,
550
+ enabled: true,
551
+ name: '__task-recovery',
552
+ description: 'System trigger: recover failed tasks for local Agents.',
553
+ createdAt: now,
554
+ updatedAt: now,
555
+ origin: {
556
+ channelKey: 'daemon',
557
+ channelType: 'daemon',
558
+ channelId: owner.originPeerId || owner.aid,
559
+ session: 'main',
560
+ peerId: owner.originPeerId || owner.aid,
561
+ sessionKey: `daemon#${owner.originPeerId || owner.aid}#__task-recovery__`,
562
+ },
563
+ source: {
564
+ type: 'event',
565
+ eventPattern: 'task:error',
566
+ },
567
+ execution: {
568
+ type: 'target_session',
569
+ prompt: TASK_RECOVERY_PROMPT,
570
+ onError: 'retry',
571
+ noopSentinel: '[[NOOP]]',
572
+ },
573
+ feedback: { strategy: 'event_session' },
574
+ reliability: {
575
+ concurrency: 'forbid',
576
+ missedPolicy: 'run_once',
577
+ retry: { maxAttempts: 2, backoffMs: 30_000 },
578
+ },
579
+ });
580
+ fs.mkdirSync(manager.triggerDir(definition.id), { recursive: true });
581
+ atomicWriteJson(manager.definitionPath(definition.id), definition);
582
+ fs.rmSync(manager.activePath(definition.id), { force: true });
583
+ }
492
584
  function removeLegacyAgentUpgradeCheck(manager) {
493
585
  let existing;
494
586
  try {
@@ -610,6 +702,7 @@ async function runBindBootstrapDaemon(daemonCfg) {
610
702
  }));
611
703
  ipcServer.setControlToken(controlToken);
612
704
  ipcServer.setRemoteAccessService(remoteAccessService);
705
+ ipcServer.setRemoteAccessOwnerProvider(() => daemonCfg.owners?.[0]);
613
706
  ipcServer.setShutdownExecutor(async (reason) => {
614
707
  if (!shutdown) {
615
708
  pendingShutdownReason = reason;
@@ -757,6 +850,12 @@ async function main() {
757
850
  // Service managers do not necessarily export TMPDIR. Establish the private
758
851
  // managed root before any runner or task runtime is initialized.
759
852
  ensureProcessManagedTempDir();
853
+ const runtimeEntry = fileURLToPath(import.meta.url);
854
+ const diskDistDir = path.join(getPackageRoot(), 'dist');
855
+ const runtimeIsDist = path.resolve(path.dirname(runtimeEntry)) === path.resolve(diskDistDir);
856
+ const runtimeBuildIdentity = readBuildIdentity(runtimeIsDist ? path.dirname(runtimeEntry) : diskDistDir, readEvolcoreVersion());
857
+ if (!runtimeIsDist)
858
+ runtimeBuildIdentity.id = null;
760
859
  // 启动信息:目录类型 + 版本号 + 代码最新时间戳
761
860
  {
762
861
  const pkgRoot = getPackageRoot();
@@ -798,6 +897,10 @@ async function main() {
798
897
  console.error(`[EvolCore] 代码时间: ${latestMtime ? fmtTime(latestMtime) : '?'}`);
799
898
  }
800
899
  console.error('[EvolCore] ✓ Version info printed');
900
+ const diskBuildId = computeBuildId(diskDistDir);
901
+ console.error(`[EvolCore] 运行 build ID: ${runtimeIsDist ? (runtimeBuildIdentity.id ?? '?') : 'source'}`);
902
+ console.error(`[EvolCore] 磁盘 dist build ID: ${diskBuildId ?? '?'}`);
903
+ logger.info(`[Build] runtimeMode=${runtimeIsDist ? 'dist' : 'source'} runtimeBuildId=${runtimeBuildIdentity.id ?? 'source'} diskDistBuildId=${diskBuildId ?? 'unknown'} match=${runtimeIsDist && runtimeBuildIdentity.id !== null && runtimeBuildIdentity.id === diskBuildId}`);
801
904
  // 过滤飞书 SDK 的 info 日志
802
905
  const originalLog = console.log;
803
906
  const originalInfo = console.info;
@@ -906,6 +1009,10 @@ async function main() {
906
1009
  }
907
1010
  // ── 自动迁移 ──
908
1011
  migrateIdentitiesIfNeeded();
1012
+ const relationSharedMigration = migrateRelationSharedDirectories();
1013
+ if (relationSharedMigration.copied > 0) {
1014
+ logger.info(`[migrate] Synced ${relationSharedMigration.copied} relation shared file(s) across ${relationSharedMigration.relations} relation(s)`);
1015
+ }
909
1016
  // daemon.json 的旧 ecweb/serviceProxy 结构已在启动前归一化为 services[]。
910
1017
  // ── 配置体系初始化(schema 字段不相交硬约束校验)──
911
1018
  try {
@@ -975,13 +1082,13 @@ async function main() {
975
1082
  port: ecwebConfig.port ?? DEFAULT_ECWEB_PORT,
976
1083
  logger,
977
1084
  });
978
- try {
979
- await ecwebSupervisor.start();
980
- onShutdown(() => ecwebSupervisor?.stop());
981
- }
982
- catch (error) {
1085
+ // ECWeb is optional and has its own health checks/restart loop. Starting
1086
+ // it must not hold up the daemon's agent/IPC initialization or ready
1087
+ // signal; status will report it as starting until the first healthy probe.
1088
+ onShutdown(() => ecwebSupervisor?.stop());
1089
+ void ecwebSupervisor.start().catch((error) => {
983
1090
  logger.warn(`ECWeb supervisor 启动失败(不影响 daemon 主流程): ${error?.message || error}`);
984
- }
1091
+ });
985
1092
  }
986
1093
  if (bindBootstrapMode && daemonCfg.aid && loadAllAgents().agents.length === 0) {
987
1094
  await runBindBootstrapDaemon(daemonCfg);
@@ -1188,6 +1295,11 @@ async function main() {
1188
1295
  });
1189
1296
  // 交互路由器
1190
1297
  const interactionRouter = new InteractionRouter();
1298
+ const groupInviteReviewService = new GroupInviteReviewService(interactionRouter);
1299
+ const removeGroupInviteConfigListener = onConfigWrite(({ target, selector }) => {
1300
+ if (target === ConfigTarget.Agent && selector.self)
1301
+ groupInviteReviewService.configurationChanged(selector.self);
1302
+ });
1191
1303
  // 为所有支持权限的 agent 设置 gateway
1192
1304
  for (const inst of agentInstances) {
1193
1305
  inst.agent.setPermissionGateway?.(permissionGateway);
@@ -1276,6 +1388,7 @@ async function main() {
1276
1388
  // 创建命令处理器
1277
1389
  const cmdHandler = new CommandHandler(sessionManager, agentMap, messageCache, eventBus, primaryRunnerKey);
1278
1390
  let agentApplicationService;
1391
+ let agentInitializationService;
1279
1392
  cmdHandler.setAgentDelegationRegistry(agentDelegationRegistry);
1280
1393
  cmdHandler.setPermissionGateway(permissionGateway);
1281
1394
  cmdHandler.setInteractionRouter(interactionRouter);
@@ -1321,10 +1434,13 @@ async function main() {
1321
1434
  dedupPath: daemonMessageDedupPath(),
1322
1435
  });
1323
1436
  // 设置中断回调(精确中断正在处理的 agent)
1324
- messageQueue.setInterruptCallback(async (sessionKey, baseagent, evolagentName, reason = 'new_message') => {
1437
+ messageQueue.setInterruptCallback(async (sessionKey, baseagent, evolagentName, reason = 'new_message', timeout) => {
1325
1438
  void baseagent;
1326
1439
  void evolagentName;
1327
- await responseEngine.interruptSession(sessionKey, reason);
1440
+ if (timeout)
1441
+ await responseEngine.timeoutSession(sessionKey, timeout);
1442
+ else
1443
+ await responseEngine.interruptSession(sessionKey, reason);
1328
1444
  });
1329
1445
  messageQueue.setEventBus(eventBus);
1330
1446
  // Restore persisted input before Trigger schedulers start. Trigger-owned work
@@ -1480,7 +1596,7 @@ async function main() {
1480
1596
  };
1481
1597
  };
1482
1598
  const validateTriggerFeedbackChannels = (definition) => {
1483
- if (definition.feedback.strategy === 'silent')
1599
+ if (definition.feedback.strategy === 'silent' || definition.feedback.strategy === 'event_session')
1484
1600
  return;
1485
1601
  const target = definition.feedback.strategy === 'target'
1486
1602
  ? definition.feedback.target
@@ -1563,6 +1679,9 @@ async function main() {
1563
1679
  if (!effortDecision.ok)
1564
1680
  throw new Error(effortDecision.message || 'trigger effort is not allowed by current role');
1565
1681
  }
1682
+ if (!actor.management && definition.feedback.strategy === 'event_session') {
1683
+ throw new Error('event-session feedback requires owner/admin');
1684
+ }
1566
1685
  if (!actor.management && definition.feedback.strategy === 'target') {
1567
1686
  const target = definition.feedback.target;
1568
1687
  const origin = actor.origin;
@@ -1670,6 +1789,9 @@ async function main() {
1670
1789
  if (privilegedReason && !persistedFullAccessApproval && !hasDaemonTriggerAuthority(subject)) {
1671
1790
  return { allowed: false, reason: `${privilegedReason} requires current DaemonOwner` };
1672
1791
  }
1792
+ if (definition.feedback.strategy === 'event_session' && !isManagementRole(role)) {
1793
+ return { allowed: false, reason: 'event-session feedback requires current owner/admin role' };
1794
+ }
1673
1795
  if (definition.feedback.strategy === 'target' && !isManagementRole(role)) {
1674
1796
  const target = definition.feedback.target;
1675
1797
  if (!target
@@ -1779,6 +1901,7 @@ async function main() {
1779
1901
  const manager = new TriggerDefinitionManager(owner.aid);
1780
1902
  if (opts.seedUpgradeCheck) {
1781
1903
  seedUpgradeCheckTrigger(manager, owner);
1904
+ seedTaskRecoveryTrigger(manager, owner);
1782
1905
  }
1783
1906
  else {
1784
1907
  removeLegacyAgentUpgradeCheck(manager);
@@ -1789,6 +1912,8 @@ async function main() {
1789
1912
  getChannel: getTriggerChannel,
1790
1913
  sessionManager,
1791
1914
  messageQueue,
1915
+ recoverTask: request => responseEngine.recoverTask(request),
1916
+ inspectTargetExecution: (sessionId, attemptId, selfAid) => responseEngine.inspectTargetExecution(sessionId, attemptId, selfAid),
1792
1917
  eventBus,
1793
1918
  tryHandoff: async (input, text, target, binding) => {
1794
1919
  if (input.signal?.aborted) {
@@ -1905,9 +2030,21 @@ async function main() {
1905
2030
  const inst = channelInstances.find(candidate => candidate.adapter.channelKey === channelKey);
1906
2031
  return !!inst && inst.channel.getStatus?.().connected === true;
1907
2032
  };
2033
+ cmdHandler.setAgentStorageClientResolver((channelKey, targetAid) => {
2034
+ const inst = channelInstances.find(candidate => candidate.channelType === 'aun'
2035
+ && (candidate.adapter.channelKey === channelKey || candidate.adapter.channelName === channelKey)
2036
+ && candidate.channel.getAid?.() === targetAid);
2037
+ if (!inst || inst.channel.getStatus?.().connected !== true)
2038
+ return undefined;
2039
+ // A reconnect replaces the Client; never capture it during startup.
2040
+ const client = inst.channel.getClient?.();
2041
+ return client?.aid === targetAid ? client : undefined;
2042
+ });
1908
2043
  const msgBridge = new MessageBridge(defaultProjectPath, sessionManager, processor, messageQueue, cmdHandler, eventBus, primaryAgent?.config.debounce, () => processLevelOwners);
1909
2044
  msgBridge.setAgentRegistry(agentRegistry);
1910
2045
  msgBridge.setInteractionRouter(interactionRouter);
2046
+ msgBridge.setGroupInviteReviewService(groupInviteReviewService);
2047
+ cmdHandler.setGroupInviteDecisionResolver((selfAid, ownerAid, inviteId, action) => groupInviteReviewService.decideManaged(selfAid, ownerAid, inviteId, action));
1911
2048
  msgBridge.setContactBindRuntimeChecker(isContactBindChannelReady);
1912
2049
  msgBridge.setAidStatsCollector(aidStatsCollector);
1913
2050
  cmdHandler.setContactRequestSubmitter((input) => msgBridge.submitAgentContactRequest(input));
@@ -1918,7 +2055,9 @@ async function main() {
1918
2055
  function registerChannelInstance(inst) {
1919
2056
  // 0. 包装 adapter.send,记录所有出站到 channel-out.log
1920
2057
  const originalSend = inst.adapter.send.bind(inst.adapter);
1921
- inst.adapter.send = async (envelope, payload) => {
2058
+ inst.adapter.send = async (envelope, rawPayload) => {
2059
+ // 版本标记在记账之前打,让 channel-out.log 与对端看到的文案一致。
2060
+ const payload = rawPayload ? tagErrorPayloadWithEvolcoreVersion(rawPayload) : rawPayload;
1922
2061
  const owningAgent = agentRegistry.resolveByChannel(inst.adapter.channelKey)
1923
2062
  ?? agentRegistry.resolveByChannel(inst.adapter.channelName);
1924
2063
  logger.channelOut({
@@ -1929,7 +2068,7 @@ async function main() {
1929
2068
  sessionId: envelope.sessionId,
1930
2069
  agentAid: owningAgent?.aid,
1931
2070
  agentName: envelope.agentName,
1932
- payload: summarizeOutboundPayload(payload),
2071
+ payload: sanitizeMenuLogValue(summarizeOutboundPayload(payload)),
1933
2072
  });
1934
2073
  const result = await originalSend(envelope, payload);
1935
2074
  // AUN adapters return an explicit receipt. Only a receipt with a remote
@@ -2316,6 +2455,14 @@ async function main() {
2316
2455
  // A failed bind (for example, a stale Windows named pipe) must abort startup
2317
2456
  // without creating a second AUN session that can kick the old daemon.
2318
2457
  let controlChannel;
2458
+ const diagService = new DiagService(() => ({ controlAid: loadDaemonConfig().aid, owners: loadDaemonConfig().owners ?? [] }), async (to, text) => {
2459
+ if (!controlChannel)
2460
+ throw new Error('Control AID channel is unavailable');
2461
+ const result = await controlChannel.sendDaemonMsg({ to, payload: { type: 'text', text }, encrypt: true });
2462
+ if (!result.ok)
2463
+ throw new Error(result.error ?? 'Invitation send failed');
2464
+ });
2465
+ cmdHandler.setDiagService(diagService);
2319
2466
  let shutdown;
2320
2467
  let pendingShutdownReason;
2321
2468
  const ipcServer = new IpcServer(resolvePaths().socket, () => {
@@ -2335,6 +2482,13 @@ async function main() {
2335
2482
  return {
2336
2483
  pid: process.pid,
2337
2484
  uptime: snap.uptimeMs,
2485
+ build: {
2486
+ runtimeMode: runtimeIsDist ? 'dist' : 'source',
2487
+ runtimeId: runtimeBuildIdentity.id,
2488
+ diskDistId: computeBuildId(diskDistDir),
2489
+ entry: runtimeBuildIdentity.entry,
2490
+ version: runtimeBuildIdentity.version,
2491
+ },
2338
2492
  controlPlane: {
2339
2493
  ready: true,
2340
2494
  owned: processLevelOwners.length > 0,
@@ -2384,6 +2538,7 @@ async function main() {
2384
2538
  return cmdHandler.handleCtl(cmd, sessionId, delegation.grant);
2385
2539
  });
2386
2540
  ipcServer.setDaemonRuntimeEpoch(agentDelegationRegistry.getRuntimeEpoch());
2541
+ ipcServer.setRemoteAccessOwnerProvider(() => processLevelOwners[0]);
2387
2542
  cmdHandler.setDaemonMonitorProvider(() => ipcServer.getMonitorSnapshot().snapshot);
2388
2543
  // Register every IPC executor/provider before exposing the endpoint. The
2389
2544
  // function declaration is hoisted, while its invocation remains here so a
@@ -2446,14 +2601,16 @@ async function main() {
2446
2601
  aunSdkLog: daemonCfg.debug?.aunSdkLog,
2447
2602
  });
2448
2603
  // connect() 失败不置空实例:AUNChannel 内部有无限重连(SDK auto_reconnect +
2449
- // scheduleReconnect),首连失败后台会自愈;保留实例供 status 显示 disconnected。
2450
- try {
2451
- await controlChannel.connect();
2604
+ // scheduleReconnect),首连失败会自愈;启动连接放到后台,避免 ready signal
2605
+ // 被远端网关/TLS 首连延迟阻塞。Attach the handlers below before the
2606
+ // promise can complete so an early control message is still accepted.
2607
+ const controlConnectPromise = controlChannel.connect()
2608
+ .then(() => {
2452
2609
  logger.info(`✓ 控制 AID 已连接: ${daemonCfg.aid}`);
2453
- }
2454
- catch (e) {
2610
+ })
2611
+ .catch((e) => {
2455
2612
  logger.warn(`控制 AID 首连失败(后台自动重连,不影响 daemon 主流程): ${e?.message || e}`);
2456
- }
2613
+ });
2457
2614
  // 控制 AID 接收 daemon-owner 或有效远程访问 token 授权的指令:
2458
2615
  // 1. /pair — daemon 远程控制配对码(文本快路径)
2459
2616
  // 2. menu.* JSON — 路由到 cmdHandler.execMenuForControl(进程级 + 全量权限)
@@ -2481,7 +2638,10 @@ async function main() {
2481
2638
  catch {
2482
2639
  parsed = null;
2483
2640
  }
2484
- const menuResponseContext = () => controlReplyContext(opts, evolMenuResponseTransportMetadata());
2641
+ const menuResponseContext = () => ({
2642
+ ...controlReplyContext(opts, evolMenuResponseTransportMetadata(opts.protectedHeaders?.sdk_version)),
2643
+ ...(opts.peerDeviceId ? { toDeviceId: opts.peerDeviceId } : {}),
2644
+ });
2485
2645
  const sendControlMenuResponse = async (response) => {
2486
2646
  const timedResponse = withMenuProcessingTime(enforceMenuResponseSize(response, 128 * 1024 - 1024), menuTiming);
2487
2647
  const request = activeMenuRequest ?? {
@@ -2522,6 +2682,13 @@ async function main() {
2522
2682
  const isRoleMenu = menuControl.isMenu
2523
2683
  && (menuControl.name === 'role' || menuControl.request.cmd === '/role');
2524
2684
  const currentProcessOwners = loadDaemonConfig().owners ?? [];
2685
+ if (/^\/diag(?:\s|$)/i.test(text)) {
2686
+ const command = text.slice(5).trim();
2687
+ const result = await diagService.execute(command.toLowerCase() === 'stop'
2688
+ ? { action: 'stop' } : { action: 'start', problem: command }, opts.peerId);
2689
+ await controlChannel.sendMessage(opts.channelId, result.message, controlReplyContext(opts));
2690
+ return;
2691
+ }
2525
2692
  if (menuControl.isMenu && !hasValidMenuId(menuControl)) {
2526
2693
  logger.warn(`[ControlMenu] dropped malformed request without id type=${menuControl.type}`);
2527
2694
  logMenuRequestCompleted(menuControl.request, undefined, menuFlowContext, {
@@ -2559,9 +2726,9 @@ async function main() {
2559
2726
  return;
2560
2727
  }
2561
2728
  if (text.toLowerCase() === '/pair') {
2562
- const pair = remoteAccessService.getPairingCode(opts.peerId);
2729
+ const pair = remoteAccessService.getPairingCode(opts.peerId, Date.now(), 'pair');
2563
2730
  const mins = Math.max(0, Math.round((pair.expiresAt - Date.now()) / 60000));
2564
- const reply = `远程控制配对码:${pair.code}(约 ${mins} 分钟内有效)`;
2731
+ const reply = `远程控制配对码:${pair.code}(约 ${mins} 分钟内有效,仅可配对一次)`;
2565
2732
  await controlChannel.sendMessage(opts.channelId, reply, controlReplyContext(opts));
2566
2733
  return;
2567
2734
  }
@@ -2572,7 +2739,7 @@ async function main() {
2572
2739
  await sendControlMenuResponse(menuFailure({ id: menuControl.id, ...(menuControl.name?.trim() ? { name: menuControl.name } : {}) }, validationError));
2573
2740
  return;
2574
2741
  }
2575
- const deduped = await controlMenuDeduper.execute([opts.channelId, opts.peerId, menuControl.id].join('\u001f'), menuPayloadFingerprint(menuControl.raw), () => cmdHandler.execMenuForControl(parsed, opts.peerId, {
2742
+ const deduped = await controlMenuDeduper.execute([opts.channelId, opts.peerId, opts.peerDeviceId || '', menuControl.id].join('\u001f'), menuPayloadFingerprint(menuControl.raw), () => cmdHandler.execMenuForControl(parsed, opts.peerId, {
2576
2743
  encrypted: opts.encrypted,
2577
2744
  protectedHeaders: opts.protectedHeaders,
2578
2745
  }));
@@ -2595,7 +2762,7 @@ async function main() {
2595
2762
  return;
2596
2763
  }
2597
2764
  // owner 发的其他内容:提示可用指令
2598
- await controlChannel.sendMessage(opts.channelId, '可用指令:/pair(获取远程控制配对码)', controlReplyContext(opts));
2765
+ await controlChannel.sendMessage(opts.channelId, '可用指令:/pair(获取远程控制配对码)、/diag <问题描述>、/diag stop', controlReplyContext(opts));
2599
2766
  }
2600
2767
  catch (e) {
2601
2768
  if (activeMenuRequest && !menuCompletionLogged) {
@@ -2608,6 +2775,7 @@ async function main() {
2608
2775
  logger.warn(`控制 AID 消息处理失败: ${e?.message || e}`);
2609
2776
  }
2610
2777
  });
2778
+ void controlConnectPromise;
2611
2779
  // ── Service Proxy:把本地服务(ecweb 等)通过控制 AID 暴露到 AUN 网络 ──
2612
2780
  // 挂在控制 AUNChannel 上,动态解引用其 client(规避重连换 client)。
2613
2781
  // 失败只 warn,不影响 daemon 主流程。
@@ -2769,6 +2937,41 @@ async function main() {
2769
2937
  });
2770
2938
  }
2771
2939
  }
2940
+ async function completeAgentBootstrap(aid) {
2941
+ const aunInstance = channelInstances.find(candidate => {
2942
+ if (candidate.channelType !== 'aun')
2943
+ return false;
2944
+ const owner = agentRegistry.resolveByChannel(candidate.adapter.channelKey)
2945
+ ?? agentRegistry.resolveByChannel(candidate.adapter.channelName);
2946
+ return owner?.aid === aid;
2947
+ });
2948
+ const welcome = aunInstance?.channel;
2949
+ const owningAgent = agentRegistry.get(aid);
2950
+ const firstOwnerAid = getFirstStaticAgentOwner(aid);
2951
+ const sessions = aunInstance && firstOwnerAid && owningAgent
2952
+ ? {
2953
+ ensurePostBootstrapMainSession: async (owner) => {
2954
+ const ownerAid = owner || firstOwnerAid;
2955
+ const channel = aunInstance.adapter.channelName;
2956
+ const active = await sessionManager.getActiveSession(channel, ownerAid);
2957
+ const ensured = await sessionManager.ensureNewMainSessionForOperation({
2958
+ channel,
2959
+ channelId: ownerAid,
2960
+ projectPath: active?.projectPath || owningAgent.projectPath || defaultProjectPath,
2961
+ baseagent: owningAgent.baseagent || active?.baseagent || primaryBaseagent,
2962
+ operationId: postBootstrapWelcomeOperationId(aid, ownerAid),
2963
+ identityMetadata: {
2964
+ ...(active?.metadata ?? {}),
2965
+ channelKey: aunInstance.adapter.channelKey,
2966
+ peerId: ownerAid,
2967
+ },
2968
+ });
2969
+ return { sessionId: ensured.session.id, created: ensured.created };
2970
+ },
2971
+ }
2972
+ : undefined;
2973
+ return completeBootstrapWithWelcome(bootstrapService, aid, welcome, sessions);
2974
+ }
2772
2975
  function configureIpc() {
2773
2976
  ipcServer.setShutdownExecutor(async (reason) => {
2774
2977
  if (!shutdown) {
@@ -2801,39 +3004,10 @@ async function main() {
2801
3004
  return { ok: false, code: 'INVALID_CONTROL_TOKEN', error: 'valid local control token is required' };
2802
3005
  }
2803
3006
  }
2804
- const aunInstance = channelInstances.find(candidate => {
2805
- if (candidate.channelType !== 'aun')
2806
- return false;
2807
- const owner = agentRegistry.resolveByChannel(candidate.adapter.channelKey)
2808
- ?? agentRegistry.resolveByChannel(candidate.adapter.channelName);
2809
- return owner?.aid === aid;
2810
- });
2811
- const welcome = aunInstance?.channel;
2812
- const owningAgent = agentRegistry.get(aid);
2813
- const firstOwnerAid = getFirstStaticAgentOwner(aid);
2814
- const sessions = aunInstance && firstOwnerAid && owningAgent
2815
- ? {
2816
- ensurePostBootstrapMainSession: async (owner) => {
2817
- const ownerAid = owner || firstOwnerAid;
2818
- const channel = aunInstance.adapter.channelName;
2819
- const active = await sessionManager.getActiveSession(channel, ownerAid);
2820
- const ensured = await sessionManager.ensureNewMainSessionForOperation({
2821
- channel,
2822
- channelId: ownerAid,
2823
- projectPath: active?.projectPath || owningAgent.projectPath || defaultProjectPath,
2824
- baseagent: owningAgent.baseagent || active?.baseagent || primaryBaseagent,
2825
- operationId: postBootstrapWelcomeOperationId(aid, ownerAid),
2826
- identityMetadata: {
2827
- ...(active?.metadata ?? {}),
2828
- channelKey: aunInstance.adapter.channelKey,
2829
- peerId: ownerAid,
2830
- },
2831
- });
2832
- return { sessionId: ensured.session.id, created: ensured.created };
2833
- },
2834
- }
2835
- : undefined;
2836
- return completeBootstrapWithWelcome(bootstrapService, aid, welcome, sessions);
3007
+ if (hasSuppliedInitialization(aid) || loadAgent(aid)?.initialization) {
3008
+ return { ok: false, code: 'INITIALIZATION_MANAGED', error: 'Use the initialization service for this Agent' };
3009
+ }
3010
+ return completeAgentBootstrap(aid);
2837
3011
  });
2838
3012
  ipcServer.setDingtalkContactBindExecutor({
2839
3013
  register: (cmd) => registerPendingDingtalkContactBind(cmd),
@@ -2859,6 +3033,17 @@ async function main() {
2859
3033
  ipcServer.setConfigOperationExecutor((argv, sessionId, delegationToken, delegationCommandHash) => cmdHandler.handleConfigOperation(argv, sessionId, delegationToken, delegationCommandHash));
2860
3034
  ipcServer.setContactOperationExecutor((argv, sessionId, delegationToken, delegationCommandHash) => cmdHandler.handleContactOperation(argv, sessionId, delegationToken, delegationCommandHash));
2861
3035
  ipcServer.setAidOperationExecutor((argv, sessionId, delegationToken, delegationCommandHash) => cmdHandler.handleAidOperation(argv, sessionId, delegationToken, delegationCommandHash));
3036
+ ipcServer.setDiagExecutor(cmd => executeDiagIpc(diagService, agentDelegationRegistry, loadDaemonConfig().owners ?? [], cmd));
3037
+ ipcServer.setMenuClientExecutor(async (cmd) => {
3038
+ if (!Array.isArray(cmd.argv) || cmd.argv.some((v) => typeof v !== 'string') || typeof cmd.sessionId !== 'string')
3039
+ return { ok: false, code: 'INVALID_ARGUMENT', error: 'A managed session and Menu argv are required' };
3040
+ return cmdHandler.handleMenuClientOperation(cmd.argv, cmd.sessionId, cmd.delegationToken, cmd.delegationCommandHash, async (options) => {
3041
+ const instance = channelInstances.find(item => item.channelType === 'aun' && item.channel.getAidState()?.aid === options.fromAid);
3042
+ if (!instance)
3043
+ throw new Error('Current Agent AUN channel is unavailable');
3044
+ return instance.channel.requestRemoteMenu(options);
3045
+ });
3046
+ });
2862
3047
  ipcServer.setGroupOperationExecutor((argv, sessionId, delegationToken, delegationCommandHash) => cmdHandler.handleGroupOperation(argv, sessionId, delegationToken, delegationCommandHash));
2863
3048
  ipcServer.setMsgOperationExecutor((argv, sessionId, delegationToken, delegationCommandHash) => cmdHandler.handleMsgOperation(argv, sessionId, delegationToken, delegationCommandHash));
2864
3049
  ipcServer.setFsOperationExecutor((argv, sessionId, delegationToken, delegationCommandHash) => cmdHandler.handleFsOperation(argv, sessionId, delegationToken, delegationCommandHash));
@@ -2930,6 +3115,14 @@ async function main() {
2930
3115
  aidStatsCollector.recordOutbound(params.aid, params.toPeer, Buffer.byteLength(params.text || '', 'utf-8'), params.text, false, params.encrypt, params.chatmode, 'send');
2931
3116
  });
2932
3117
  ipcServer.setTaskRuntimeContextProvider(({ sessionId }) => responseEngine.getTaskRuntimeContext(sessionId));
3118
+ ipcServer.setTaskRuntimeContextAuthorizer(({ sessionId, delegationToken, delegationCommandHash }) => {
3119
+ // Codex persistent threads carry a command-scoped authorization. Looking
3120
+ // up fresh routing context must not consume the subsequent CLI operation.
3121
+ const authorization = agentDelegationRegistry.validateForContext(delegationToken, sessionId, delegationCommandHash);
3122
+ return authorization.ok
3123
+ ? { ok: true }
3124
+ : { ok: false, code: authorization.code, error: authorization.reason };
3125
+ });
2933
3126
  ipcServer.setCodexPreToolUseExecutor(async (params) => {
2934
3127
  for (const runner of new Set(agentMap.values())) {
2935
3128
  if (typeof runner?.evaluatePreToolUse !== 'function')
@@ -3005,7 +3198,14 @@ async function main() {
3005
3198
  error: decision.reason,
3006
3199
  };
3007
3200
  }
3008
- return { ok: true, aid: selfAid, currentSessionId: params.sessionId, managementRole };
3201
+ return {
3202
+ ok: true,
3203
+ aid: selfAid,
3204
+ currentSessionId: params.sessionId,
3205
+ managementRole,
3206
+ role: subject.role,
3207
+ actorId: delegation.grant.actorId,
3208
+ };
3009
3209
  };
3010
3210
  const resolveHandoffQueryScope = async (params, operation) => {
3011
3211
  if (params.sessionId) {
@@ -3027,6 +3227,22 @@ async function main() {
3027
3227
  return scope;
3028
3228
  return responseEngine.returnAuthorizedHandoffResult({ sessionId: params.sessionId, handoffId: params.handoffId, content: params.content }, scope.aid);
3029
3229
  });
3230
+ ipcServer.setHandoffRecoverReturnExecutor(async (params) => {
3231
+ if (!params.sessionId)
3232
+ return { ok: false, code: 'HANDOFF_CALL_SESSION_INVALID', error: 'current session is required' };
3233
+ const scope = await resolveManagedHandoffScope({ ...params, sessionId: params.sessionId }, 'ec.handoff.recover');
3234
+ if (!scope.ok)
3235
+ return scope;
3236
+ if (scope.role !== 'owner') {
3237
+ return { ok: false, code: 'ROLE_ACCESS_DENIED', error: 'handoff recovery requires the current Agent owner' };
3238
+ }
3239
+ return responseEngine.recoverAuthorizedHandoffResult({
3240
+ sessionId: params.sessionId,
3241
+ handoffId: params.handoffId,
3242
+ content: params.content,
3243
+ actorId: scope.actorId,
3244
+ }, scope.aid);
3245
+ });
3030
3246
  ipcServer.setHandoffStatusExecutor(async (params) => {
3031
3247
  const scope = await resolveHandoffQueryScope(params, 'ec.handoff.status');
3032
3248
  if (!scope.ok)
@@ -3177,6 +3393,7 @@ async function main() {
3177
3393
  thread: params.thread,
3178
3394
  targetChatType: params.scope === 'group' ? 'group' : 'private',
3179
3395
  explicitReturnPolicy: params.returnPolicy,
3396
+ handoffContext: params.handoffContext,
3180
3397
  originAuthorization: {
3181
3398
  actorId: delegation.grant.actorId,
3182
3399
  channelKey: delegation.grant.channel,
@@ -3580,6 +3797,57 @@ async function main() {
3580
3797
  }),
3581
3798
  aunAidStats: () => aidStatsCollector.getAllSnapshots(),
3582
3799
  });
3800
+ agentInitializationService = new AgentInitializationService({
3801
+ register: async (aid) => {
3802
+ const { aidCreate } = await import('./aun/aid/identity.js');
3803
+ const created = await aidCreate(aid);
3804
+ await created.client.close();
3805
+ },
3806
+ publish: async (aid, content) => {
3807
+ const { agentmdPut } = await import('./aun/aid/agentmd.js');
3808
+ await agentmdPut(content, { aid });
3809
+ },
3810
+ fetch: async (aid) => {
3811
+ const { agentmdGet } = await import('./aun/aid/agentmd.js');
3812
+ const card = await agentmdGet(aid, { withVerification: true, remoteOnly: true });
3813
+ return { content: card.content, verified: card.verification.status === 'verified' };
3814
+ },
3815
+ hotLoad: async (aid) => {
3816
+ await coreHotLoadAgent(aid);
3817
+ const until = Date.now() + 20_000;
3818
+ while (Date.now() < until) {
3819
+ if (channelInstances.some(inst => inst.channelType === 'aun'
3820
+ && inst.channel.getAidState?.()?.aid === aid
3821
+ && inst.channel.getAidState?.()?.status === 'connected'))
3822
+ return;
3823
+ await new Promise(resolve => setTimeout(resolve, 250));
3824
+ }
3825
+ throw Object.assign(new Error('AUN connection unavailable'), { code: 'INITIALIZE_CHANNEL_UNAVAILABLE' });
3826
+ },
3827
+ activate: completeAgentBootstrap,
3828
+ authorize: actor => actor === 'local-control' || (loadDaemonConfig().owners ?? []).includes(actor),
3829
+ usable: aid => {
3830
+ const agent = agentRegistry.get(aid);
3831
+ return !!agent && agent.config.enabled !== false && agentMap.has(`${aid}::${agent.baseagent}`)
3832
+ && channelInstances.some(inst => inst.channelType === 'aun'
3833
+ && inst.channel.getAidState?.()?.aid === aid
3834
+ && inst.channel.getAidState?.()?.status === 'connected');
3835
+ },
3836
+ });
3837
+ agentApplicationService.setInitializationService(agentInitializationService);
3838
+ ipcServer.setInitializationExecutor(async (cmd) => executeLocalInitializationCommand(agentInitializationService, cmd));
3839
+ for (const entry of fs.readdirSync(resolvePaths().agentsDir, { withFileTypes: true })) {
3840
+ if (!entry.isDirectory())
3841
+ continue;
3842
+ try {
3843
+ const record = readInitialization(entry.name);
3844
+ if (record?.status === 'in_progress')
3845
+ agentInitializationService.resume(entry.name, record.actor);
3846
+ }
3847
+ catch {
3848
+ logger.warn('[Initialization] Recovery requires review for an incomplete operation');
3849
+ }
3850
+ }
3583
3851
  cmdHandler.setAgentApplicationService(agentApplicationService);
3584
3852
  ipcServer.setAgentApplicationService(agentApplicationService);
3585
3853
  ipcServer.setStatsProvider(() => statsCollector.getSnapshot());
@@ -3682,6 +3950,8 @@ async function main() {
3682
3950
  ? {
3683
3951
  ok: true,
3684
3952
  aid: subject.dataScope === 'daemon' ? (params.targetAid ?? delegation.grant.selfAid) : delegation.grant.selfAid,
3953
+ dataScope: subject.dataScope,
3954
+ management: isManagementRole(subject.role),
3685
3955
  }
3686
3956
  : { ok: false, code: decision.code, error: decision.reason };
3687
3957
  }
@@ -3709,26 +3979,41 @@ async function main() {
3709
3979
  return fail('INVALID_DELEGATION', 'delegation does not match the current session');
3710
3980
  }
3711
3981
  const taskAid = delegation.grant.selfAid;
3982
+ let welcomeArgs;
3983
+ if (sub === 'welcome') {
3984
+ try {
3985
+ welcomeArgs = parseAgentWelcomeArgs(argv.slice(2));
3986
+ }
3987
+ catch (error) {
3988
+ return fail('INVALID_ARGS', error.message);
3989
+ }
3990
+ if (delegation.grant.chatType !== 'private')
3991
+ return fail('NOT_ALLOWED', 'welcome requires a private conversation');
3992
+ }
3712
3993
  const targetAid = sub === 'list'
3713
3994
  ? undefined
3714
3995
  : sub === 'persona'
3715
3996
  ? taskAid
3716
- : sub === 'show'
3717
- ? (argv[2] && !argv[2].startsWith('--') ? argv[2] : taskAid)
3718
- : sub === 'reload'
3719
- ? (argv[2] && !argv[2].startsWith('--') ? argv[2] : undefined)
3720
- : (argv[2] && !argv[2].startsWith('--') ? argv[2] : undefined);
3997
+ : sub === 'welcome'
3998
+ ? taskAid
3999
+ : sub === 'show'
4000
+ ? (argv[2] && !argv[2].startsWith('--') ? argv[2] : taskAid)
4001
+ : sub === 'reload'
4002
+ ? (argv[2] && !argv[2].startsWith('--') ? argv[2] : undefined)
4003
+ : (argv[2] && !argv[2].startsWith('--') ? argv[2] : undefined);
3721
4004
  const operation = sub === 'list' ? 'agent.list'
3722
4005
  : sub === 'show' ? 'agent.show'
3723
4006
  : sub === 'persona' ? 'agent.persona'
3724
- : sub === 'new' ? 'agent.create'
3725
- : sub === 'enable' ? 'agent.enable'
3726
- : sub === 'disable' ? 'agent.disable'
3727
- : sub === 'reload' ? 'agent.reload'
3728
- : sub === 'delete' ? 'agent.delete' : undefined;
4007
+ : sub === 'welcome' ? 'agent.welcome'
4008
+ : sub === 'initialize' || (sub === 'new' && argv.includes('--initialize')) ? 'agent.initialize'
4009
+ : sub === 'new' ? 'agent.create'
4010
+ : sub === 'enable' ? 'agent.enable'
4011
+ : sub === 'disable' ? 'agent.disable'
4012
+ : sub === 'reload' ? 'agent.reload'
4013
+ : sub === 'delete' ? 'agent.delete' : undefined;
3729
4014
  if (!operation)
3730
4015
  return fail('INVALID_ARGS', `unsupported managed agent command: ${sub}`);
3731
- if (sub === 'new' && !argv.includes('--non-interactive')) {
4016
+ if (sub === 'new' && !argv.includes('--non-interactive') && !argv.includes('--initialize')) {
3732
4017
  return fail('MANAGED_NON_INTERACTIVE_REQUIRED', 'interactive Agent creation is unavailable in a managed task', 'MANAGED_NON_INTERACTIVE_REQUIRED');
3733
4018
  }
3734
4019
  if (sub === 'delete' && argv.includes('--purge') && !argv.includes('--confirm-purge')) {
@@ -3757,7 +4042,7 @@ async function main() {
3757
4042
  subject,
3758
4043
  intent: {
3759
4044
  operation,
3760
- scope: operation === 'agent.show' || operation === 'agent.persona'
4045
+ scope: operation === 'agent.show' || operation === 'agent.persona' || operation === 'agent.welcome'
3761
4046
  ? 'agent'
3762
4047
  : operation === 'agent.reload' && targetAid
3763
4048
  ? 'agent'
@@ -3768,6 +4053,7 @@ async function main() {
3768
4053
  aid: targetAid ?? taskAid,
3769
4054
  taskAgentAid: taskAid,
3770
4055
  targetAgentAid: targetAid ?? taskAid,
4056
+ ...(welcomeArgs ? { action: welcomeArgs.action } : {}),
3771
4057
  },
3772
4058
  },
3773
4059
  auditMetadata: { taskId: delegation.grant.taskId, messageId: delegation.grant.messageId },
@@ -3803,6 +4089,7 @@ async function main() {
3803
4089
  ok: true,
3804
4090
  aid: agent.aid,
3805
4091
  status: agent.status,
4092
+ lifecycle: agent.lifecycle ?? null,
3806
4093
  identity: agent.identity ?? { name: null, description: null },
3807
4094
  config: {
3808
4095
  baseagent: agent.config?.baseagent ?? null,
@@ -3818,6 +4105,7 @@ async function main() {
3818
4105
  : { config: null, agent_md: null, project: null, data: null },
3819
4106
  ...(subject.dataScope === 'daemon' ? {} : { redacted: true }),
3820
4107
  ...(agent.createProgress ? { createProgress: agent.createProgress } : {}),
4108
+ ...(agent.initialization ? { initialization: agent.initialization } : {}),
3821
4109
  } };
3822
4110
  }
3823
4111
  if (operation === 'agent.persona') {
@@ -3852,6 +4140,48 @@ async function main() {
3852
4140
  ...(result.saved ? { saved: true } : {}),
3853
4141
  } };
3854
4142
  }
4143
+ if (operation === 'agent.welcome') {
4144
+ if (subject.role !== 'owner' && !(welcomeArgs?.action === 'show' && subject.role === 'admin')) {
4145
+ return fail('NOT_ALLOWED', 'welcome requires the current Agent Owner (admin may read)');
4146
+ }
4147
+ const { agentWelcome } = await import('./cli/agent.js');
4148
+ const result = await agentWelcome(taskAid, { text: welcomeArgs?.text, trustedLocal: true });
4149
+ if (!result.ok)
4150
+ return fail(result.code ?? 'INTERNAL', result.error);
4151
+ return { ok: true, result: {
4152
+ ok: true,
4153
+ aid: taskAid,
4154
+ content: result.content,
4155
+ ...(result.path ? { path: result.path } : {}),
4156
+ ...(result.saved ? { saved: true } : {}),
4157
+ ...(result.bytes !== undefined ? { bytes: result.bytes } : {}),
4158
+ ...(result.sha256 ? { sha256: result.sha256 } : {}),
4159
+ } };
4160
+ }
4161
+ if (operation === 'agent.initialize') {
4162
+ try {
4163
+ const parsed = parseInitializationArgs(argv.slice(1));
4164
+ const actor = delegation.grant.actorId;
4165
+ if (parsed.resume)
4166
+ return { ok: true, result: agentInitializationService.resume(parsed.aid, actor) };
4167
+ const createPermission = authorizeOperation({
4168
+ source: 'agent-tool', subject,
4169
+ intent: { operation: 'agent.create', scope: 'control', source: 'agent-tool', args: { aid: parsed.aid } },
4170
+ auditMetadata: { taskId: delegation.grant.taskId, messageId: delegation.grant.messageId },
4171
+ });
4172
+ if (!createPermission.allow)
4173
+ return fail(createPermission.code, createPermission.reason);
4174
+ if (!argv.includes('--owner') && !argv.includes('--owners'))
4175
+ return fail('INITIALIZE_OWNER_MISMATCH', 'Explicit Owner required');
4176
+ const plan = resolveInitializationPlan(argv.slice(1));
4177
+ if (!plan.owners.includes(actor))
4178
+ return fail('INITIALIZE_OWNER_MISMATCH', 'The authenticated requesting Owner must be retained');
4179
+ return { ok: true, result: agentInitializationService.start(plan, actor, parsed.dryRun) };
4180
+ }
4181
+ catch (error) {
4182
+ return fail(error.code ?? 'INVALID_ARGS', error.message);
4183
+ }
4184
+ }
3855
4185
  if (operation === 'agent.create') {
3856
4186
  const aid = argv[2] && !argv[2].startsWith('--') ? argv[2] : undefined;
3857
4187
  if (!aid)
@@ -3879,6 +4209,7 @@ async function main() {
3879
4209
  handoff: value('--handoff'),
3880
4210
  name: value('--name'),
3881
4211
  description: value('--description'),
4212
+ type: value('--type'),
3882
4213
  tags,
3883
4214
  force: argv.includes('--force'),
3884
4215
  });
@@ -3936,6 +4267,7 @@ async function main() {
3936
4267
  owners: plan.owners,
3937
4268
  name: plan.name,
3938
4269
  description: plan.description,
4270
+ type: plan.type,
3939
4271
  tags: plan.tags,
3940
4272
  force: argv.includes('--force'),
3941
4273
  };
@@ -3977,6 +4309,11 @@ async function main() {
3977
4309
  const result = await execAgentAction(sub, { aid: targetAid ?? taskAid, force: argv.includes('--force') }, delegation.grant.actorId, eventBus, agentApplicationService);
3978
4310
  return 'error' in result ? fail(result.code, result.error) : { ok: true, result: { ok: true, ...(result.data ?? {}) } };
3979
4311
  });
4312
+ ipcServer.setTaskResumeExecutor(params => executeTaskResume(params, {
4313
+ sessions: sessionManager,
4314
+ authorizeManaged: authorizeManagedAgentOperation,
4315
+ recover: request => responseEngine.recoverTask(request),
4316
+ }));
3980
4317
  ipcServer.setQueueOperationExecutor(async (params) => {
3981
4318
  const operation = `ec.queue.${params.action ?? 'read'}`;
3982
4319
  const authorization = await authorizeManagedAgentOperation(params, operation);
@@ -4017,6 +4354,38 @@ async function main() {
4017
4354
  const authorization = await authorizeManagedAgentOperation({ ...params, targetAid: params.aid }, params.operation);
4018
4355
  return authorization.ok ? { ok: true } : authorization;
4019
4356
  });
4357
+ ipcServer.setStorageUploadExecutor(async (params) => {
4358
+ const authorization = await authorizeManagedAgentOperation({ ...params, targetAid: params.aid }, 'storage.upload');
4359
+ if (!authorization.ok)
4360
+ return authorization;
4361
+ const session = await sessionManager.getSessionById(params.sessionId);
4362
+ if (!session)
4363
+ return { ok: false, code: 'SESSION_NOT_FOUND', error: 'current session not found' };
4364
+ try {
4365
+ const projectPath = fs.realpathSync(path.resolve(session.projectPath));
4366
+ const requestedFile = path.resolve(session.projectPath, params.localFile);
4367
+ const requestedStat = fs.lstatSync(requestedFile);
4368
+ if (requestedStat.isSymbolicLink()) {
4369
+ return { ok: false, code: 'INVALID_LOCAL_FILE', error: 'Upload source must not be a symbolic link' };
4370
+ }
4371
+ const localFile = fs.realpathSync(requestedFile);
4372
+ const stat = fs.lstatSync(localFile);
4373
+ if (!isSameOrDescendant(localFile, projectPath) || isHClassPath(localFile)) {
4374
+ return { ok: false, code: 'INVALID_LOCAL_PATH', error: 'Upload source must be an unprotected file inside the current session project' };
4375
+ }
4376
+ if (!stat.isFile() || stat.isSymbolicLink()) {
4377
+ return { ok: false, code: 'INVALID_LOCAL_FILE', error: 'Upload source must be a regular non-symlink file' };
4378
+ }
4379
+ const { storageUpload } = await import('./aun/storage/index.js');
4380
+ const result = await storageUpload(authorization.aid, localFile, params.remotePath, { isPublic: params.isPublic });
4381
+ return result.ok
4382
+ ? { ok: true, objectKey: result.objectKey, publicUrl: result.publicUrl }
4383
+ : { ok: false, code: 'STORAGE_UPLOAD_FAILED', error: result.error };
4384
+ }
4385
+ catch (error) {
4386
+ return { ok: false, code: 'INVALID_LOCAL_FILE', error: error instanceof Error ? error.message : String(error) };
4387
+ }
4388
+ });
4020
4389
  ipcServer.setTriggerExecutor(async (cmd) => {
4021
4390
  const schedulerFor = (agentAid) => {
4022
4391
  const scheduler = triggerSchedulers.get(agentAid);
@@ -4420,6 +4789,8 @@ async function main() {
4420
4789
  timestamp: Date.now()
4421
4790
  });
4422
4791
  // 断开插件系统的渠道
4792
+ removeGroupInviteConfigListener();
4793
+ await shutdownStep('stop group invitation review', () => groupInviteReviewService.dispose());
4423
4794
  await shutdownStep('disconnect channels', () => channelLoader.disconnectAll(channelInstances));
4424
4795
  for (const inst of channelInstances) {
4425
4796
  const type = inst.channelType || inst.adapter.channelName;