evolcore 0.0.25 → 0.0.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (304) hide show
  1. package/CHANGELOG.md +632 -565
  2. package/LICENSE +21 -21
  3. package/README.md +455 -455
  4. package/bin/codex-managed-acl.ps1 +203 -160
  5. package/bin/codex-managed-hook.mjs +208 -208
  6. package/bin/ec-safe-output.js +327 -327
  7. package/bin/ec.js +40 -29
  8. package/bin/install-codex-managed-hooks.mjs +582 -555
  9. package/dist/agents/baseagent-availability.js +68 -0
  10. package/dist/agents/baseagent.js +96 -46
  11. package/dist/agents/claude-runner.js +172 -33
  12. package/dist/agents/codex/app-server-client.js +76 -31
  13. package/dist/agents/codex/catalog.js +1 -1
  14. package/dist/agents/codex/diagnostics.js +34 -1
  15. package/dist/agents/codex-runner.js +434 -446
  16. package/dist/agents/ecagent-runner.js +174 -34
  17. package/dist/agents/gemini-runner.js +8 -7
  18. package/dist/agents/runner-types.js +75 -19
  19. package/dist/aun/aid/agentmd.js +151 -36
  20. package/dist/aun/aid/client.js +1 -1
  21. package/dist/aun/aid/control-aid.js +180 -1
  22. package/dist/aun/aid/identity.js +4 -4
  23. package/dist/aun/aid/index.js +1 -1
  24. package/dist/aun/aid/store.js +17 -13
  25. package/dist/aun/group/history.js +303 -0
  26. package/dist/aun/group/identity.js +43 -0
  27. package/dist/aun/group/index.js +5 -0
  28. package/dist/aun/{msg/group.js → group/operations.js} +162 -39
  29. package/dist/aun/{menu-client.js → menu/client.js} +85 -6
  30. package/dist/aun/menu/index.js +2 -0
  31. package/dist/aun/menu/transport.js +49 -0
  32. package/dist/aun/msg/index.js +2 -1
  33. package/dist/aun/msg/managed-operation.js +85 -19
  34. package/dist/aun/msg/mention-schema.js +51 -0
  35. package/dist/aun/msg/p2p.js +19 -9
  36. package/dist/aun/msg/upload.js +43 -80
  37. package/dist/aun/outbox.js +29 -7
  38. package/dist/aun/rpc/client.js +1 -0
  39. package/dist/aun/storage/client.js +39 -50
  40. package/dist/channels/aun.js +842 -318
  41. package/dist/channels/daemon.js +10 -6
  42. package/dist/cli/agent-command.js +212 -89
  43. package/dist/cli/agent-create.js +626 -11
  44. package/dist/cli/agent.js +125 -13
  45. package/dist/cli/aun-commands.js +487 -337
  46. package/dist/cli/bench.js +20 -20
  47. package/dist/cli/config.js +55 -33
  48. package/dist/cli/contact.js +72 -15
  49. package/dist/cli/ctl-command.js +24 -24
  50. package/dist/cli/daemon-commands.js +140 -63
  51. package/dist/cli/diag.js +31 -0
  52. package/dist/cli/fs-command.js +70 -70
  53. package/dist/cli/handoff-command.js +46 -14
  54. package/dist/cli/index.js +91 -79
  55. package/dist/cli/init.js +34 -1
  56. package/dist/cli/link-rules.js +21 -21
  57. package/dist/cli/managed-command-guard.js +5 -1
  58. package/dist/cli/menu-client.js +51 -32
  59. package/dist/cli/model.js +3 -3
  60. package/dist/cli/net-check.js +22 -22
  61. package/dist/cli/queue-command.js +31 -31
  62. package/dist/cli/response.js +32 -32
  63. package/dist/cli/restart-monitor.js +103 -42
  64. package/dist/cli/stats.js +51 -51
  65. package/dist/cli/task-command.js +59 -0
  66. package/dist/cli/task-context.js +97 -49
  67. package/dist/cli/trigger-command.js +74 -64
  68. package/dist/config/builtin-roles.js +11 -0
  69. package/dist/config/config-field-policy.js +11 -0
  70. package/dist/config/config-manager.js +32 -4
  71. package/dist/config/config-operation-service.js +160 -0
  72. package/dist/config/contact-book-store.js +1 -1
  73. package/dist/config/contact-book.js +1 -1
  74. package/dist/config/contact-operation-service.js +97 -8
  75. package/dist/config/peer-role-resolver.js +1 -1
  76. package/dist/config/resolved-config-op.js +30 -7
  77. package/dist/config-store.js +1 -1
  78. package/dist/core/agent-application-service.js +7 -1
  79. package/dist/core/audit/event-key.js +17 -2
  80. package/dist/core/audit/log-integrity.js +34 -8
  81. package/dist/core/auth/agent-delegation.js +48 -14
  82. package/dist/core/auth/auth-gateway.js +2 -0
  83. package/dist/core/auth/authorization-audit.js +53 -49
  84. package/dist/core/auth/operation-authorizer.js +90 -7
  85. package/dist/core/auth/operation-catalog.js +145 -4
  86. package/dist/core/auth/remote-access-store.js +155 -10
  87. package/dist/core/bootstrap-messages.js +66 -7
  88. package/dist/core/bootstrap-service.js +111 -20
  89. package/dist/core/command/agent-control.js +88 -7
  90. package/dist/core/command/agent-storage-menu.js +443 -0
  91. package/dist/core/command/cli-intent-parser.js +19 -0
  92. package/dist/core/command/command-handler.js +159 -65
  93. package/dist/core/command/data-root-resolver.js +273 -0
  94. package/dist/core/command/diag-service.js +118 -0
  95. package/dist/core/command/group-menu.js +7 -2
  96. package/dist/core/command/menu-catalog.js +46 -7
  97. package/dist/core/command/menu-handler.js +392 -100
  98. package/dist/core/command/menu-protocol.js +45 -6
  99. package/dist/core/command/menu-token-store.js +41 -8
  100. package/dist/core/command/role-menu.js +2 -2
  101. package/dist/core/command/slash-gate.js +34 -4
  102. package/dist/core/command/slash-handler.js +123 -43
  103. package/dist/core/event-catalog.js +4 -0
  104. package/dist/core/evolagent-registry.js +28 -2
  105. package/dist/core/group/invite-service.js +924 -0
  106. package/dist/core/handoff/runtime.js +205 -27
  107. package/dist/core/handoff/store.js +193 -4
  108. package/dist/core/interaction-router.js +22 -8
  109. package/dist/core/message/im-renderer.js +146 -34
  110. package/dist/core/message/message-bridge.js +163 -20
  111. package/dist/core/message/message-log.js +5 -3
  112. package/dist/core/message/message-queue.js +99 -41
  113. package/dist/core/message/message-utils.js +1 -0
  114. package/dist/core/message/response-engine.js +755 -144
  115. package/dist/core/model/catalog-service.js +79 -49
  116. package/dist/core/model/context-window-suffix.js +33 -0
  117. package/dist/core/model/model-catalog.js +1 -1
  118. package/dist/core/model/model-permission.js +12 -3
  119. package/dist/core/permission/approval-gateway.js +36 -3
  120. package/dist/core/permission/ec-command-parser.js +112 -41
  121. package/dist/core/permission/protected-paths.js +134 -16
  122. package/dist/core/permission/sandbox-runtime.js +108 -17
  123. package/dist/core/permission/tool-error-code.js +15 -0
  124. package/dist/core/permission/tool-policy.js +9 -8
  125. package/dist/core/relation/peer-identity.js +57 -6
  126. package/dist/core/relation/shared-data.js +106 -0
  127. package/dist/core/role/runtime-policy.js +64 -0
  128. package/dist/core/runtime-lock.js +10 -26
  129. package/dist/core/session/runner-task-history.js +170 -0
  130. package/dist/core/session/session-manager.js +34 -6
  131. package/dist/core/session/session-mapper.js +41 -0
  132. package/dist/core/session/session-turn-coordinator.js +37 -7
  133. package/dist/core/session/session-turns.js +48 -7
  134. package/dist/core/task/task-recovery-service.js +176 -0
  135. package/dist/core/task/task-resume-operation.js +32 -0
  136. package/dist/eck/group-rules-sync.js +5 -3
  137. package/dist/eck/kit-renderer.js +1 -0
  138. package/dist/index.js +588 -217
  139. package/dist/ipc.js +193 -15
  140. package/dist/paths.js +17 -4
  141. package/dist/product.js +2 -0
  142. package/dist/response-system/engines/v1/proactive-flow.js +59 -2
  143. package/dist/stats/billing.js +41 -15
  144. package/dist/stats/db.js +159 -159
  145. package/dist/stats/eck-vars.js +9 -9
  146. package/dist/stats/price-resolver.js +28 -10
  147. package/dist/stats/query.js +139 -139
  148. package/dist/stats/role-budget.js +6 -6
  149. package/dist/stats/writer.js +51 -51
  150. package/dist/trigger/event-source.js +19 -0
  151. package/dist/trigger/feedback.js +233 -40
  152. package/dist/trigger/parser.js +55 -5
  153. package/dist/trigger/patch.js +9 -0
  154. package/dist/trigger/scheduler.js +30 -4
  155. package/dist/trigger/validation.js +20 -3
  156. package/dist/utils/cross-platform.js +30 -1
  157. package/dist/utils/ecweb-utils.js +1 -0
  158. package/dist/utils/error-dict.json +160 -160
  159. package/dist/utils/error-utils.js +61 -2
  160. package/dist/utils/evolcore-version.js +129 -0
  161. package/dist/utils/logger.js +26 -14
  162. package/dist/utils/macos-autostart.js +22 -22
  163. package/dist/utils/model-prices.jsonl +23 -23
  164. package/dist/utils/npm-ops.js +176 -35
  165. package/dist/utils/tool-summary.js +59 -3
  166. package/dist/utils/welcome.js +196 -23
  167. package/ecagent/LICENSE +21 -21
  168. package/ecagent/NOTICE +11 -11
  169. package/ecagent/README.md +61 -61
  170. package/ecagent/dist/harness/agent-harness.js +3 -0
  171. package/ecagent/dist/harness/compaction/branch-summarization.js +30 -30
  172. package/ecagent/dist/harness/compaction/compaction.js +83 -83
  173. package/ecagent/dist/harness/messages.js +7 -7
  174. package/ecagent/dist/harness/session/jsonl-storage.js +21 -2
  175. package/kits/docs/GUIDE.md +20 -20
  176. package/kits/docs/INDEX.md +60 -66
  177. package/kits/docs/aun/CHEATSHEET.md +19 -19
  178. package/kits/docs/aun/SYNC_PROTOCOL.md +15 -15
  179. package/kits/docs/channels/aun.md +69 -56
  180. package/kits/docs/channels/feishu.md +56 -56
  181. package/kits/docs/context-assembly.md +379 -367
  182. package/kits/docs/eck_templates/GUIDE.template.md +22 -22
  183. package/kits/docs/eck_templates/INDEX.template.md +28 -28
  184. package/kits/docs/eck_templates/path-registry.template.md +33 -33
  185. package/kits/docs/eck_templates/runtime.template.md +19 -19
  186. package/kits/docs/evolcore/INDEX.md +76 -71
  187. package/kits/docs/evolcore/agent-create.md +202 -165
  188. package/kits/docs/evolcore/agent.md +99 -67
  189. package/kits/docs/evolcore/aid.md +68 -60
  190. package/kits/docs/evolcore/codex-windows-sandbox.md +146 -146
  191. package/kits/docs/evolcore/config.md +241 -195
  192. package/kits/docs/evolcore/contact.md +75 -68
  193. package/kits/docs/evolcore/ctl.md +46 -46
  194. package/kits/docs/evolcore/event.md +216 -216
  195. package/kits/docs/evolcore/fs-architecture.md +1215 -1215
  196. package/kits/docs/evolcore/fs.md +110 -110
  197. package/kits/docs/evolcore/group-collaboration.md +244 -244
  198. package/kits/docs/evolcore/group-fs.md +17 -17
  199. package/kits/docs/evolcore/group-rules.md +251 -251
  200. package/kits/docs/evolcore/group.md +267 -152
  201. package/kits/docs/evolcore/menu-client.md +58 -34
  202. package/kits/docs/evolcore/model.md +50 -50
  203. package/kits/docs/evolcore/msg.md +169 -165
  204. package/kits/docs/evolcore/response.md +75 -75
  205. package/kits/docs/evolcore/rpc.md +37 -37
  206. package/kits/docs/evolcore/self-summary.md +29 -29
  207. package/kits/docs/evolcore/stats.md +83 -83
  208. package/kits/docs/evolcore/storage.md +53 -50
  209. package/kits/docs/evolcore/task.md +14 -0
  210. package/kits/docs/evolcore/trigger.md +623 -575
  211. package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -26
  212. package/kits/docs/identity/PATH_OPS.md +16 -16
  213. package/kits/docs/identity/ROLE_DETAIL.md +25 -25
  214. package/kits/docs/identity/identity-tools.md +26 -26
  215. package/kits/docs/path-registry.md +62 -62
  216. package/kits/docs/prompt-loading-architecture.md +269 -269
  217. package/kits/docs/venues/aun-group.md +45 -45
  218. package/kits/docs/venues/aun-private.md +10 -10
  219. package/kits/docs/venues/client-desktop.md +10 -10
  220. package/kits/docs/venues/client-mobile.md +10 -10
  221. package/kits/docs/venues/feishu-group.md +13 -13
  222. package/kits/docs/venues/feishu-private.md +9 -9
  223. package/kits/docs/venues/group.md +42 -25
  224. package/kits/docs/venues/private.md +10 -10
  225. package/kits/eck_manifest.auxiliary.json +43 -43
  226. package/kits/eck_manifest.json +217 -190
  227. package/kits/eck_message_manifest.json +74 -74
  228. package/kits/migrations/README-role-config-v4.md +32 -32
  229. package/kits/migrations/migrate-contact-book-v2.mjs +748 -748
  230. package/kits/migrations/migrate-role-config-v4.mjs +614 -614
  231. package/kits/migrations/migrate-role-config-v5.mjs +365 -365
  232. package/kits/migrations/rename-config-file.mjs +99 -99
  233. package/kits/rules/01-overview.md +166 -162
  234. package/kits/rules/02-navigation.md +96 -96
  235. package/kits/rules/03-identity.md +36 -36
  236. package/kits/rules/04-relation.md +76 -75
  237. package/kits/rules/05-venue.md +60 -60
  238. package/kits/rules/06-channel.md +65 -63
  239. package/kits/schemas/_meta.json +53 -53
  240. package/kits/schemas/agent-config.schema.1.json +177 -177
  241. package/kits/schemas/agent-config.schema.10.json +395 -395
  242. package/kits/schemas/agent-config.schema.11.json +421 -421
  243. package/kits/schemas/agent-config.schema.12.json +451 -427
  244. package/kits/schemas/agent-config.schema.2.json +239 -239
  245. package/kits/schemas/agent-config.schema.3.json +119 -119
  246. package/kits/schemas/agent-config.schema.4.json +208 -208
  247. package/kits/schemas/agent-config.schema.5.json +326 -326
  248. package/kits/schemas/agent-config.schema.6.json +322 -322
  249. package/kits/schemas/agent-config.schema.7.json +303 -303
  250. package/kits/schemas/agent-config.schema.8.json +304 -304
  251. package/kits/schemas/agent-config.schema.9.json +364 -364
  252. package/kits/schemas/contact-book.schema.1.json +36 -36
  253. package/kits/schemas/contact-book.schema.2.json +43 -43
  254. package/kits/schemas/contact-book.schema.3.json +67 -67
  255. package/kits/schemas/daemon.schema.1.json +91 -91
  256. package/kits/schemas/daemon.schema.2.json +101 -101
  257. package/kits/schemas/daemon.schema.3.json +123 -123
  258. package/kits/schemas/daemon.schema.4.json +136 -136
  259. package/kits/schemas/daemon.schema.5.json +135 -135
  260. package/kits/schemas/daemon.schema.6.json +131 -131
  261. package/kits/schemas/defaults.schema.1.json +81 -81
  262. package/kits/schemas/defaults.schema.2.json +85 -85
  263. package/kits/schemas/defaults.schema.3.json +73 -73
  264. package/kits/schemas/defaults.schema.4.json +107 -107
  265. package/kits/schemas/defaults.schema.5.json +119 -119
  266. package/kits/schemas/menu-exec-schema-commands.md +208 -208
  267. package/kits/schemas/migrations/README.md +30 -30
  268. package/kits/schemas/relation-config.schema.1.json +158 -158
  269. package/kits/schemas/relation-config.schema.2.json +73 -73
  270. package/kits/schemas/relation-config.schema.3.json +50 -50
  271. package/kits/schemas/relation-config.schema.4.json +47 -47
  272. package/kits/schemas/relation-config.schema.5.json +47 -47
  273. package/kits/schemas/relation-config.schema.6.json +46 -46
  274. package/kits/schemas/relation-config.schema.7.json +59 -59
  275. package/kits/schemas/relation-config.schema.8.json +103 -103
  276. package/kits/schemas/role-config.schema.1.json +215 -201
  277. package/kits/schemas/role-registry.schema.1.json +35 -35
  278. package/kits/schemas/single-session.schema.1.json +31 -31
  279. package/kits/schemas/single-session.schema.2.json +57 -57
  280. package/kits/schemas/single-session.schema.3.json +32 -32
  281. package/kits/templates/bootstrap-welcome.md +17 -17
  282. package/kits/templates/message-fragments/handoff-context-to-target.md +9 -9
  283. package/kits/templates/message-fragments/handoff-request-to-target.md +19 -19
  284. package/kits/templates/message-fragments/handoff-response-to-origin.md +9 -9
  285. package/kits/templates/message-fragments/inject-default.md +2 -2
  286. package/kits/templates/message-fragments/item.md +2 -2
  287. package/kits/templates/roles/admin.json +92 -92
  288. package/kits/templates/roles/member.json +101 -96
  289. package/kits/templates/roles/owner.json +9 -9
  290. package/kits/templates/roles/visitor.json +90 -85
  291. package/kits/templates/system-fragments/baseagent.md +14 -14
  292. package/kits/templates/system-fragments/bootstrap.md +53 -34
  293. package/kits/templates/system-fragments/channel.md +28 -64
  294. package/kits/templates/system-fragments/commands.md +36 -28
  295. package/kits/templates/system-fragments/group-unread-history.md +7 -0
  296. package/kits/templates/system-fragments/identity.md +13 -13
  297. package/kits/templates/system-fragments/relation.md +19 -19
  298. package/kits/templates/system-fragments/reply.md +56 -0
  299. package/kits/templates/system-fragments/session.md +59 -59
  300. package/kits/templates/system-fragments/venue.md +31 -31
  301. package/package.json +104 -98
  302. package/dist/aun/group-identity.js +0 -9
  303. /package/dist/aun/{group-fs-download.js → group/fs-download.js} +0 -0
  304. /package/dist/aun/{msg/group-index.js → group/index-rpc.js} +0 -0
@@ -1,12 +1,13 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import crypto from 'node:crypto';
4
- import { createShortConnection } from './rpc/client.js';
5
- import { SLOT } from './aid/store.js';
6
- import { isValidAid } from './aid/validation.js';
7
- import { resolveRoot } from '../paths.js';
8
- import { readInstalledEvolcoreVersion } from '../utils/evolcore-version.js';
9
- import { buildMenuClientProtectedHeaders, parseMenuResponse, validateMenuRequest, } from '../core/command/menu-protocol.js';
4
+ import { createShortConnection } from '../rpc/client.js';
5
+ import { MENU_MESSAGE_TTL_SECONDS } from './transport.js';
6
+ import { SLOT } from '../aid/store.js';
7
+ import { isValidAid } from '../aid/validation.js';
8
+ import { resolveRoot } from '../../paths.js';
9
+ import { readInstalledEvolcoreVersion } from '../../utils/evolcore-version.js';
10
+ import { buildMenuClientProtectedHeaders, parseMenuResponse, validateMenuRequest, } from '../../core/command/menu-protocol.js';
10
11
  const DEFAULT_TIMEOUT_MS = 15_000;
11
12
  const MAX_TIMEOUT_MS = 120_000;
12
13
  const POLL_INTERVAL_MS = 200;
@@ -22,6 +23,83 @@ export class MenuClientError extends Error {
22
23
  this.name = 'MenuClientError';
23
24
  }
24
25
  }
26
+ /** Uses the daemon's existing authenticated AUN channel and its normal ACK path. */
27
+ export class ManagedMenuClient {
28
+ send;
29
+ pending = new Map();
30
+ constructor(send) {
31
+ this.send = send;
32
+ }
33
+ accept(fromAid, payload, encrypted) {
34
+ if (!payload || typeof payload !== 'object' || payload.type !== 'menu.response')
35
+ return false;
36
+ if (!encrypted)
37
+ return true;
38
+ try {
39
+ const response = parseMenuResponse(payload);
40
+ this.pending.get(this.key(fromAid, response.id))?.resolve(response);
41
+ }
42
+ catch { /* Invalid responses cannot complete a pending request. */ }
43
+ return true;
44
+ }
45
+ close() {
46
+ for (const pending of this.pending.values())
47
+ pending.reject(new MenuClientError('AUN_DISCONNECTED', 'AUN channel disconnected'));
48
+ this.pending.clear();
49
+ }
50
+ async request(options) {
51
+ if (!isValidAid(options.fromAid) || !isValidAid(options.targetAid))
52
+ throw new MenuClientError('INVALID_ARGUMENT', 'Valid sender and target AIDs are required');
53
+ if (options.aunPath !== undefined || options.slotId !== undefined || options.encrypt === false)
54
+ throw new MenuClientError('INVALID_ARGUMENT', 'Managed Menu uses the daemon connection with E2EE; connection overrides are forbidden');
55
+ const request = { ...options.request, id: options.request.id || crypto.randomUUID() };
56
+ const validation = validateMenuRequest(request);
57
+ if (validation)
58
+ throw new MenuClientError(validation.code, validation.message);
59
+ const timeout = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
60
+ if (!Number.isFinite(timeout) || timeout < 1000 || timeout > MAX_TIMEOUT_MS)
61
+ throw new MenuClientError('INVALID_ARGUMENT', 'timeout must be between 1s and 120s');
62
+ const key = this.key(options.targetAid, request.id);
63
+ if (this.pending.has(key))
64
+ throw new MenuClientError('CLIENT_BUSY', 'Request ID is already in flight for this daemon');
65
+ const headers = buildMenuClientProtectedHeaders({ clientName: 'ec-menu-client', clientVersion: readInstalledEvolcoreVersion(), remoteAccessToken: options.remoteAccessToken ?? options.ecwebToken });
66
+ const attempts = request.type === 'menu.query' || request.type === 'menu.options' ? 2 : 1;
67
+ let timer;
68
+ try {
69
+ for (let attempt = 0; attempt < attempts; attempt++) {
70
+ const response = new Promise((resolve, reject) => {
71
+ this.pending.set(key, { resolve, reject });
72
+ timer = setTimeout(() => reject(new MenuClientError('TIMEOUT', 'Remote Menu response timed out')), timeout);
73
+ });
74
+ const sent = this.send({
75
+ to: options.targetAid, payload: request, encrypt: true,
76
+ ttl: MENU_MESSAGE_TTL_SECONDS, protected_headers: headers,
77
+ })
78
+ .then(result => {
79
+ if (!(result?.message_id ?? result?.message?.message_id))
80
+ throw new MenuClientError('SEND_FAILED', 'message.send returned no message_id');
81
+ });
82
+ try {
83
+ const [, value] = await Promise.all([sent, response]);
84
+ return value;
85
+ }
86
+ catch (error) {
87
+ if (!(error instanceof MenuClientError) || error.code !== 'TIMEOUT' || attempt + 1 === attempts)
88
+ throw error;
89
+ }
90
+ finally {
91
+ clearTimeout(timer);
92
+ }
93
+ }
94
+ throw new MenuClientError('TIMEOUT', 'Remote Menu response timed out');
95
+ }
96
+ finally {
97
+ clearTimeout(timer);
98
+ this.pending.delete(key);
99
+ }
100
+ }
101
+ key(target, id) { return JSON.stringify([target, id]); }
102
+ }
25
103
  async function callClient(conn, method, params) {
26
104
  try {
27
105
  return await conn.call(method, params);
@@ -220,6 +298,7 @@ async function sendOnce(options, request, timeoutMs) {
220
298
  to: options.targetAid,
221
299
  payload: request,
222
300
  encrypt,
301
+ ttl: MENU_MESSAGE_TTL_SECONDS,
223
302
  ...(encrypt ? { protected_headers: headers } : {}),
224
303
  });
225
304
  const messageId = sent?.message_id ?? sent?.message?.message_id;
@@ -0,0 +1,2 @@
1
+ export { ManagedMenuClient, MenuClientError, executeMenuClientRequest } from './client.js';
2
+ export { MENU_MESSAGE_TTL_SECONDS, MENU_VOLATILE_SDK_VERSION, menuSenderDeviceId, menuTransportParams, supportsMenuVolatileTransport, } from './transport.js';
@@ -0,0 +1,49 @@
1
+ /** Message Service TTL in seconds; unrelated to Menu execution/client timeouts. */
2
+ export const MENU_MESSAGE_TTL_SECONDS = 60;
3
+ export const MENU_VOLATILE_SDK_VERSION = '0.5.17';
4
+ function parseSdkVersion(value) {
5
+ if (typeof value !== 'string' || !/^\d+\.\d+\.\d+$/.test(value.trim()))
6
+ return undefined;
7
+ const parts = value.trim().split('.').map(Number);
8
+ return parts.every(Number.isSafeInteger) ? parts : undefined;
9
+ }
10
+ /** Volatile cursors and device targeting require the SDK's 0.5.17 contract. */
11
+ export function supportsMenuVolatileTransport(value) {
12
+ const version = parseSdkVersion(value);
13
+ if (!version)
14
+ return false;
15
+ const minimum = parseSdkVersion(MENU_VOLATILE_SDK_VERSION);
16
+ return version[0] > minimum[0]
17
+ || (version[0] === minimum[0] && (version[1] > minimum[1]
18
+ || (version[1] === minimum[1] && version[2] >= minimum[2])));
19
+ }
20
+ /** Read only the SDK's public sender_device_id, never a payload/recipient device. */
21
+ export function menuSenderDeviceId(value) {
22
+ if (typeof value !== 'string' || value.length > 256 || /[\u0000-\u001f\u007f]/.test(value))
23
+ return undefined;
24
+ return value.trim() || undefined;
25
+ }
26
+ /** Enforce the Menu wire policy at the final P2P send boundary, including retries. */
27
+ export function menuTransportParams(method, params) {
28
+ if (method !== 'message.send' || typeof params.payload?.type !== 'string'
29
+ || !params.payload.type.startsWith('menu.'))
30
+ return params;
31
+ if (params.payload.type !== 'menu.response')
32
+ return { ...params, ttl: MENU_MESSAGE_TTL_SECONDS };
33
+ const { to_device_id: target, menu_client_sdk_version: sdkVersion, ...rest } = params;
34
+ delete rest.device_id;
35
+ if (!supportsMenuVolatileTransport(sdkVersion ?? params.protected_headers?.sdk_version)) {
36
+ // Evol App versions backed by fastaun < 0.5.17 do not consume the
37
+ // volatile cursor or understand device-targeted Menu responses.
38
+ return rest;
39
+ }
40
+ if (target === undefined)
41
+ return { ...rest, ttl: MENU_MESSAGE_TTL_SECONDS };
42
+ const deviceId = menuSenderDeviceId(target);
43
+ if (!deviceId) {
44
+ throw Object.assign(new Error('Invalid Menu reply device'), { code: 'INVALID_MENU_REPLY_DEVICE', retryable: false });
45
+ }
46
+ // device_id takes precedence in the SDK; do not let it override the captured
47
+ // request sender. A rejected target is never retried as an AID broadcast.
48
+ return { ...rest, ttl: MENU_MESSAGE_TTL_SECONDS, to_device_id: deviceId };
49
+ }
@@ -1,4 +1,5 @@
1
1
  export { msgSend, msgPull, msgAck, msgRecall, msgOnline, } from './p2p.js';
2
2
  export { msgHistory, MAX_MSG_HISTORY_LIMIT } from './history.js';
3
- export { groupSend, groupPull, groupAck, groupCreate, groupInfo, groupList, groupUpdate, groupDissolve, groupSuspend, groupResume, groupJoin, groupLeave, groupInvite, groupInvites, groupAcceptInvite, groupRejectInvite, groupRevokeInvite, groupKick, groupMembers, groupOnline, groupSetRole, groupTransferOwner, groupBan, groupUnban, groupBanlist, groupRules, groupUpdateRules, groupRulesFileGet, groupRulesFileSet, groupRulesFilePublish, } from './group.js';
3
+ export { groupHistory, parseGroupHistoryArgs, GroupHistoryError } from '../group/index.js';
4
+ export { groupSend, groupPull, groupAck, groupCreate, groupInfo, groupList, groupUpdate, groupDissolve, groupSuspend, groupResume, groupJoin, groupLeave, groupInvite, groupInvites, groupAcceptInvite, groupRejectInvite, groupRevokeInvite, groupKick, groupMembers, groupOnline, groupSetRole, groupTransferOwner, groupBan, groupUnban, groupBanlist, groupBlock, groupUnblock, groupBlocklist, groupReportStatus, groupMemberStatuses, groupRules, groupUpdateRules, groupRulesFileGet, groupRulesFileSet, groupRulesFilePublish, } from '../group/index.js';
4
5
  export { uploadFileAndBuildPayload, inferPayloadType, isValidPayloadType } from './upload.js';
@@ -1,20 +1,23 @@
1
1
  import { TextDecoder } from 'util';
2
2
  import { getAidStore, loadClient, SLOT } from '../aid/index.js';
3
3
  import { msgHistory } from './history.js';
4
+ import { groupHistory, parseGroupHistoryArgs, GroupHistoryError } from '../group/history.js';
4
5
  import { msgOnline } from './p2p.js';
5
6
  import { resolveRoot } from '../../paths.js';
6
- import { isExplicitGroupId } from '../group-identity.js';
7
+ import { normalizeGroupAid } from '../group/identity.js';
7
8
  import { isValidAid } from '../aid/validation.js';
8
9
  import { tryParseChannelKey } from '../../core/channel-loader.js';
9
- import { downloadGroupFsBytes } from '../group-fs-download.js';
10
- import { groupBan, groupBanlist, groupCreate, groupDissolve, groupInfo, groupInvite, groupJoin, groupKick, groupLeave, groupList, groupMembers, groupOnline, groupPull, groupAck, groupAcceptInvite, groupInvites, groupRejectInvite, groupRevokeInvite, groupResume, groupRules, groupRulesFileGet, groupRulesFilePublish, groupRulesFileSet, groupSetRole, groupSuspend, groupTransferOwner, groupUnban, groupUpdate, groupUpdateRules, } from './group.js';
10
+ import { downloadGroupFsBytes } from '../group/fs-download.js';
11
+ import { groupBan, groupBanlist, groupBlock, groupUnblock, groupBlocklist, groupReportStatus, groupMemberStatuses, groupCreate, groupDissolve, groupInfo, groupInvite, groupJoin, groupKick, groupLeave, groupList, groupMembers, groupOnline, groupPull, groupAck, groupAcceptInvite, groupInvites, groupRejectInvite, groupRevokeInvite, groupResume, groupRules, groupRulesFileGet, groupRulesFilePublish, groupRulesFileSet, groupSetRole, groupSuspend, groupTransferOwner, groupUnban, groupUpdate, groupUpdateRules, } from '../group/operations.js';
11
12
  /** Rules uploaded by a managed task are carried as bounded UTF-8 content. */
12
13
  export const MAX_MANAGED_GROUP_RULES_BYTES = 4 * 1024;
13
14
  const RELATION_GROUP_READ_KINDS = new Set([
14
15
  'info',
15
16
  'pull',
17
+ 'history',
16
18
  'members',
17
19
  'online',
20
+ 'member-statuses',
18
21
  'rules.get',
19
22
  'rules.get-legacy',
20
23
  ]);
@@ -22,6 +25,11 @@ const RELATION_GROUP_READ_KINDS = new Set([
22
25
  export function isManagedGroupRelationRead(command) {
23
26
  return RELATION_GROUP_READ_KINDS.has(command.kind);
24
27
  }
28
+ /** Members may change only the current Agent's own reception and reported status. */
29
+ export function isManagedGroupRelationWrite(command) {
30
+ return command.kind === 'report-status'
31
+ || ((command.kind === 'block' || command.kind === 'unblock') && command.memberAid === command.from);
32
+ }
25
33
  export class ManagedGroupOperationError extends Error {
26
34
  code;
27
35
  constructor(code, message) {
@@ -33,6 +41,7 @@ export class ManagedGroupOperationError extends Error {
33
41
  const GROUP_VALUE_FLAGS = new Set([
34
42
  '--format', '--after-seq', '--limit', '--visibility', '--description', '--join-mode', '--group-id',
35
43
  '--name', '--page', '--size', '--mode', '--question', '--max-pending', '--message', '--answer', '--duration', '--member-type',
44
+ '--reason', '--online-status', '--work-status',
36
45
  ]);
37
46
  function failGroup(code, reason) {
38
47
  return { ok: false, code, reason };
@@ -73,13 +82,16 @@ function requiredAid(value, label) {
73
82
  return failGroup('INVALID_AID', `invalid ${label}: ${aid}`);
74
83
  return aid;
75
84
  }
76
- function requiredGroup(value) {
85
+ function requiredGroup(value, from) {
77
86
  const groupId = String(value || '').trim();
78
87
  if (!groupId)
79
88
  return failGroup('INVALID_ARGUMENT', 'missing group AID');
80
- if (!isExplicitGroupId(groupId))
81
- return failGroup('INVALID_GROUP', `invalid group AID: ${groupId}`);
82
- return groupId;
89
+ try {
90
+ return normalizeGroupAid(groupId, from?.slice(from.indexOf('.') + 1));
91
+ }
92
+ catch (error) {
93
+ return failGroup('INVALID_GROUP', `invalid group AID: ${groupId}: ${error.message}`);
94
+ }
83
95
  }
84
96
  function numberFlag(flags, name) {
85
97
  const raw = flags.get(name);
@@ -108,7 +120,7 @@ function withFromAndGroup(sub, parsed, operationId, groupRequired = true) {
108
120
  return from;
109
121
  let groupId;
110
122
  if (groupRequired) {
111
- const parsedGroup = requiredGroup(parsed.positionals[1]);
123
+ const parsedGroup = requiredGroup(parsed.positionals[1], from);
112
124
  if (typeof parsedGroup !== 'string')
113
125
  return parsedGroup;
114
126
  groupId = parsedGroup;
@@ -121,6 +133,15 @@ export function resolveManagedGroupOperation(argv) {
121
133
  const sub = raw[0];
122
134
  if (!sub || sub === 'send')
123
135
  return failGroup('NOT_ALLOWED', 'managed group operations do not allow send; use the group send daemon route');
136
+ if (sub === 'history') {
137
+ try {
138
+ const history = parseGroupHistoryArgs(raw);
139
+ return command('history', 'ec.group.history', ['group', ...raw], history.from, { groupId: history.groupId, history });
140
+ }
141
+ catch (error) {
142
+ return failGroup('INVALID_ARGUMENT', error instanceof Error ? error.message : String(error));
143
+ }
144
+ }
124
145
  const parsed = parseFlags(raw.slice(1));
125
146
  if (!('positionals' in parsed))
126
147
  return parsed;
@@ -186,9 +207,10 @@ export function resolveManagedGroupOperation(argv) {
186
207
  return failGroup('INVALID_ARGUMENT', 'invalid --visibility');
187
208
  if (joinMode !== undefined && !['open', 'approval', 'invite_only', 'closed'].includes(joinMode))
188
209
  return failGroup('INVALID_ARGUMENT', 'invalid --join-mode');
189
- const groupId = values.get('--group-id');
190
- if (groupId !== undefined && !isExplicitGroupId(groupId))
191
- return failGroup('INVALID_GROUP', `invalid --group-id: ${groupId}`);
210
+ const rawGroupId = values.get('--group-id');
211
+ const groupId = rawGroupId === undefined ? undefined : requiredGroup(rawGroupId, from);
212
+ if (groupId !== undefined && typeof groupId !== 'string')
213
+ return groupId;
192
214
  return command(sub, 'ec.group.create', ['group', sub, from, name], from, { name, visibility, description: values.get('--description'), joinMode, groupId });
193
215
  }
194
216
  const simple = new Map([
@@ -197,12 +219,16 @@ export function resolveManagedGroupOperation(argv) {
197
219
  ['join', 'ec.group.join'], ['leave', 'ec.group.leave'], ['invite', 'ec.group.invite'], ['kick', 'ec.group.kick'],
198
220
  ['members', 'ec.group.members'], ['online', 'ec.group.online'], ['role', 'ec.group.role'], ['owner', 'ec.group.owner'],
199
221
  ['ban', 'ec.group.ban'], ['unban', 'ec.group.unban'],
222
+ ['block', 'ec.group.block'], ['unblock', 'ec.group.unblock'], ['blocklist', 'ec.group.blocklist'],
223
+ ['report-status', 'ec.group.report-status'], ['member-statuses', 'ec.group.member-statuses'],
200
224
  ]);
201
225
  if (simple.has(sub)) {
202
226
  const allowedFlags = {
203
227
  pull: ['--after-seq', '--limit'], ack: [], info: [], update: ['--name', '--description'], dissolve: [],
204
228
  suspend: [], resume: [], join: ['--message', '--answer'], leave: [], invite: ['--member-type'], kick: [],
205
229
  members: ['--page', '--size'], online: [], role: [], owner: [], ban: ['--duration'], unban: [],
230
+ block: ['--reason'], unblock: [], blocklist: [],
231
+ 'report-status': ['--online-status', '--work-status'], 'member-statuses': [],
206
232
  };
207
233
  const flagsError = onlyFlags(values, allowedFlags[sub] ?? []);
208
234
  if (flagsError)
@@ -248,18 +274,30 @@ export function resolveManagedGroupOperation(argv) {
248
274
  return checked;
249
275
  }
250
276
  const memberType = values.get('--member-type');
251
- if (memberType !== undefined && memberType !== 'human' && memberType !== 'ai')
277
+ if (memberType !== undefined && memberType !== 'human' && memberType !== 'agent')
252
278
  return failGroup('INVALID_ARGUMENT', 'invalid --member-type');
253
279
  extra.members = members;
254
280
  extra.memberType = memberType;
255
281
  }
256
- else if (sub === 'kick' || sub === 'unban') {
282
+ else if (sub === 'kick' || sub === 'unban' || sub === 'block' || sub === 'unblock') {
257
283
  if (positionals.length !== 3)
258
284
  return failGroup('INVALID_ARGUMENT', `${sub} requires a member AID`);
259
285
  const memberAid = requiredAid(positionals[2], 'member AID');
260
286
  if (typeof memberAid !== 'string')
261
287
  return memberAid;
262
288
  extra.memberAid = memberAid;
289
+ if (sub === 'block')
290
+ extra.reason = values.get('--reason');
291
+ }
292
+ else if (sub === 'report-status') {
293
+ if (positionals.length !== 2)
294
+ return failGroup('INVALID_ARGUMENT', 'report-status accepts exactly <from> <group-aid>');
295
+ const onlineStatus = values.get('--online-status');
296
+ const workStatus = values.get('--work-status');
297
+ if (onlineStatus === undefined || workStatus === undefined) {
298
+ return failGroup('INVALID_ARGUMENT', 'report-status requires --online-status and --work-status');
299
+ }
300
+ Object.assign(extra, { onlineStatus, workStatus });
263
301
  }
264
302
  else if (sub === 'members') {
265
303
  if (positionals.length !== 2)
@@ -305,7 +343,7 @@ export function resolveManagedGroupOperation(argv) {
305
343
  extra.durationSeconds = durationSeconds;
306
344
  extra.list = positionals.length === 2;
307
345
  }
308
- else if (sub === 'info' || sub === 'dissolve' || sub === 'suspend' || sub === 'resume' || sub === 'leave' || sub === 'online') {
346
+ else if (sub === 'info' || sub === 'dissolve' || sub === 'suspend' || sub === 'resume' || sub === 'leave' || sub === 'online' || sub === 'blocklist' || sub === 'member-statuses') {
309
347
  if (positionals.length !== 2)
310
348
  return failGroup('INVALID_ARGUMENT', `${sub} accepts exactly <from> <group-aid>`);
311
349
  }
@@ -318,7 +356,7 @@ export function resolveManagedGroupOperation(argv) {
318
356
  const from = requiredAid(positionals[0], 'from AID');
319
357
  if (typeof from !== 'string')
320
358
  return from;
321
- const groupId = requiredGroup(positionals[1]);
359
+ const groupId = requiredGroup(positionals[1], from);
322
360
  if (typeof groupId !== 'string')
323
361
  return groupId;
324
362
  const rest = positionals.slice(2);
@@ -354,7 +392,17 @@ export function resolveManagedGroupOperation(argv) {
354
392
  return failGroup('NOT_ALLOWED', `unsupported managed group command: ${sub}`);
355
393
  }
356
394
  /** Execute a parsed command using daemon-owned AUN credentials and root. */
357
- export async function executeManagedGroupOperation(command, rulesContent) {
395
+ export async function executeManagedGroupOperation(command, rulesContent, historyContext = {}) {
396
+ if (command.kind === 'history') {
397
+ try {
398
+ return groupHistory(command.history, historyContext);
399
+ }
400
+ catch (error) {
401
+ if (error instanceof GroupHistoryError)
402
+ throw new ManagedGroupOperationError(error.code, error.message);
403
+ throw error;
404
+ }
405
+ }
358
406
  const opts = { aunPath: resolveRoot() };
359
407
  let result;
360
408
  switch (command.kind) {
@@ -427,6 +475,21 @@ export async function executeManagedGroupOperation(command, rulesContent) {
427
475
  case 'unban':
428
476
  result = await groupUnban({ from: command.from, groupId: command.groupId, memberAid: command.memberAid, ...opts });
429
477
  break;
478
+ case 'block':
479
+ result = await groupBlock({ from: command.from, groupId: command.groupId, memberAid: command.memberAid, reason: command.reason, ...opts });
480
+ break;
481
+ case 'unblock':
482
+ result = await groupUnblock({ from: command.from, groupId: command.groupId, memberAid: command.memberAid, ...opts });
483
+ break;
484
+ case 'blocklist':
485
+ result = await groupBlocklist({ from: command.from, groupId: command.groupId, ...opts });
486
+ break;
487
+ case 'report-status':
488
+ result = await groupReportStatus({ from: command.from, groupId: command.groupId, onlineStatus: command.onlineStatus, workStatus: command.workStatus, ...opts });
489
+ break;
490
+ case 'member-statuses':
491
+ result = await groupMemberStatuses({ from: command.from, groupId: command.groupId, ...opts });
492
+ break;
430
493
  case 'rules.get':
431
494
  result = await groupRulesFileGet({ from: command.from, groupId: command.groupId, ...opts });
432
495
  break;
@@ -678,10 +741,13 @@ function parseRemote(value, allowEmpty) {
678
741
  const separator = value.indexOf(':');
679
742
  if (separator <= 0)
680
743
  return failFs('INVALID_PATH', 'remote path must use <group-aid>:<absolute-path>');
681
- const groupId = value.slice(0, separator).trim();
744
+ let groupId;
682
745
  const remotePath = value.slice(separator + 1);
683
- if (!isValidAid(groupId) || !isExplicitGroupId(groupId)) {
684
- return failFs('INVALID_GROUP', `invalid group AID: ${groupId}`);
746
+ try {
747
+ groupId = normalizeGroupAid(value.slice(0, separator).trim());
748
+ }
749
+ catch (error) {
750
+ return failFs('INVALID_GROUP', `invalid group AID: ${error.message}`);
685
751
  }
686
752
  if (remotePath === '' && allowEmpty)
687
753
  return { groupId, remotePath: '' };
@@ -18,3 +18,54 @@ export function normalizeAunMentionEntries(raw) {
18
18
  }
19
19
  return entries;
20
20
  }
21
+ /** Reject unsupported attention values before normalizing the outbound payload. */
22
+ export function parseAunAttention(value) {
23
+ if (value === undefined)
24
+ return undefined;
25
+ if (value === 'completed' || value === 'failed' || value === 'required' || value === 'interrupted')
26
+ return value;
27
+ throw Object.assign(new Error('attention must be completed, failed, required, or interrupted'), {
28
+ code: 'INVALID_ATTENTION',
29
+ });
30
+ }
31
+ /**
32
+ * Project runner state as a pair, using the structured status as the authority.
33
+ * Action cards always require action. All other business payloads omit both
34
+ * fields, including explicit/custom payloads and entries retried from outbox.
35
+ */
36
+ export function withAunAttention(payload, fallback) {
37
+ parseAunAttention(payload.attention);
38
+ parseAunAttention(fallback);
39
+ const attention = payload.type === 'action_card'
40
+ ? 'required'
41
+ : payload.type === 'task.status' && typeof payload.status === 'string'
42
+ ? aunStatusAttention(payload.status)
43
+ : undefined;
44
+ const normalized = { ...payload };
45
+ delete normalized.terminal;
46
+ delete normalized.attention;
47
+ return attention === undefined
48
+ ? normalized
49
+ : { ...normalized, terminal: attention !== 'required', attention };
50
+ }
51
+ export function aunStatusAttention(status) {
52
+ switch (status) {
53
+ case 'done':
54
+ case 'completed': return 'completed';
55
+ case 'error':
56
+ case 'timeout': return 'failed';
57
+ case 'interrupted': return 'interrupted';
58
+ case 'requires_action': return 'required';
59
+ default: return undefined;
60
+ }
61
+ }
62
+ /** Business messages and command feedback never inherit runner attention. */
63
+ export function aunOutboundAttention(payload) {
64
+ parseAunAttention(payload.attention);
65
+ switch (payload.kind) {
66
+ case 'interaction': return 'required';
67
+ default: return payload.kind.startsWith('status.')
68
+ ? aunStatusAttention(payload.kind.slice(7))
69
+ : undefined;
70
+ }
71
+ }
@@ -8,9 +8,9 @@ import { normalizeCodexTextPayload, readBestTaskRuntimeContext, runtimeRefMessag
8
8
  import { chatDirPath } from '../../core/session/session-fs-store.js';
9
9
  import { resolvePaths } from '../../paths.js';
10
10
  import { ipcQuery } from '../../ipc.js';
11
- import { AGENT_DELEGATION_TOKEN_ENV } from '../../core/auth/agent-delegation.js';
11
+ import { readDelegationCredential } from '../../core/auth/agent-delegation.js';
12
12
  import { loadDaemonConfig } from '../../config-store.js';
13
- import { normalizeAunMentionEntries } from './mention-schema.js';
13
+ import { normalizeAunMentionEntries, withAunAttention } from './mention-schema.js';
14
14
  const DAEMON_SEND_TIMEOUT_GRACE_MS = 10_000;
15
15
  const DEFAULT_IDLE_TIMEOUT_MS = 120_000;
16
16
  const MAX_TIMER_MS = 2_147_483_647;
@@ -67,7 +67,7 @@ function messageLogContent(body) {
67
67
  }
68
68
  function applyRoutingPayloadFields(payload, args, runtimeContext, includeRuntimeRef = true) {
69
69
  const threadId = msgSendThreadId(args, payload, runtimeContext);
70
- if (threadId && !payload.thread_id)
70
+ if (threadId)
71
71
  payload.thread_id = threadId;
72
72
  if (!includeRuntimeRef)
73
73
  return;
@@ -110,7 +110,7 @@ function msgSendLogMetadata(args, payload, runtimeContext) {
110
110
  }
111
111
  async function tryDaemonMsgSend(args, payload, runtimeContext, file) {
112
112
  const delegatedTask = !!process.env.EVOLCORE_SESSION_ID
113
- || !!process.env[AGENT_DELEGATION_TOKEN_ENV];
113
+ || !!readDelegationCredential();
114
114
  if (!runtimeContext) {
115
115
  return delegatedTask
116
116
  ? { ok: false, code: 'DELEGATION_REQUIRED', error: 'Task runtime context is unavailable' }
@@ -135,11 +135,12 @@ async function tryDaemonMsgSend(args, payload, runtimeContext, file) {
135
135
  encrypt: args.encrypt,
136
136
  thread,
137
137
  returnPolicy: args.returnPolicy,
138
+ handoffContext: args.handoffContext,
138
139
  originSessionId: runtimeContext.sessionId,
139
140
  originMessageId: runtimeContext.messageId,
140
- delegationToken: process.env[AGENT_DELEGATION_TOKEN_ENV],
141
+ delegationToken: readDelegationCredential(),
141
142
  log: msgSendLogMetadata(args, payload, runtimeContext),
142
- }, daemonTaskSendIpcTimeoutMs(!!file));
143
+ }, daemonTaskSendIpcTimeoutMs(!!file), { throwOnError: true });
143
144
  return response ?? {
144
145
  ok: false,
145
146
  code: 'AUN_DAEMON_UNAVAILABLE',
@@ -213,6 +214,10 @@ async function appendMsgSendOutboundLog(args, result, opts) {
213
214
  catch { /* daemon 不在或 IPC 失败都忽略 */ }
214
215
  }
215
216
  export async function msgSend(args) {
217
+ if (args.handoffContext !== undefined && (!process.env.EVOLCORE_SESSION_ID
218
+ || !args.handoffContext.trim() || args.handoffContext.length > 64 * 1024)) {
219
+ return { ok: false, code: 'INVALID_HANDOFF_CONTEXT', error: 'handoff context requires a managed session and 1–65536 characters' };
220
+ }
216
221
  const runtimeContext = await readBestTaskRuntimeContext();
217
222
  // Contact admission is a daemon-owned policy for managed Agent tasks. A
218
223
  // non-managed CLI caller may use any valid AUN sender identity and must not
@@ -223,7 +228,7 @@ export async function msgSend(args) {
223
228
  try {
224
229
  let payload;
225
230
  if (args.body.mode !== 'file') {
226
- payload = buildSimplePayload(args.body);
231
+ payload = withAunAttention(buildSimplePayload(args.body));
227
232
  normalizeCodexTextPayload(payload, runtimeContext);
228
233
  if (Object.prototype.hasOwnProperty.call(payload, 'mentions')) {
229
234
  payload.mentions = normalizeAunMentionEntries(payload.mentions);
@@ -267,7 +272,7 @@ export async function msgSend(args) {
267
272
  const idStore = await getAidStore({ slotId: args.slotId ?? SLOT.cli, aunPath: args.aunPath });
268
273
  let peerIdentity;
269
274
  try {
270
- peerIdentity = await PeerIdentityCache.resolve('aun', args.to, selfAgentDir, idStore, false);
275
+ peerIdentity = await PeerIdentityCache.resolve('aun', args.to, selfAgentDir, idStore, false, args.aunPath);
271
276
  }
272
277
  finally {
273
278
  try {
@@ -292,6 +297,7 @@ export async function msgSend(args) {
292
297
  payload.mentions = normalizeAunMentionEntries(payload.mentions);
293
298
  }
294
299
  const encrypt = resolveAunMessageEncrypt(args.encrypt);
300
+ payload = withAunAttention(payload);
295
301
  const sendParams = { to: args.to, payload, encrypt };
296
302
  const result = await conn.call('message.send', sendParams);
297
303
  const messageId = typeof result?.message_id === 'string' && result.message_id.trim()
@@ -348,11 +354,15 @@ export async function msgPull(args) {
348
354
  return {
349
355
  ok: true,
350
356
  messages: result?.messages ?? [],
351
- count: result?.count ?? 0,
357
+ count: result?.count ?? result?.messages?.length ?? 0,
352
358
  latest_seq: result?.latest_seq ?? 0,
353
359
  earliest_available_seq: result?.earliest_available_seq ?? null,
354
360
  ephemeral_earliest_available_seq: result?.ephemeral_earliest_available_seq ?? null,
355
361
  ephemeral_dropped_count: result?.ephemeral_dropped_count ?? 0,
362
+ ...(typeof result?.latest_volatile_cursor === 'string' ? { latest_volatile_cursor: result.latest_volatile_cursor } : {}),
363
+ ...(typeof result?.volatile_earliest_cursor === 'string' ? { volatile_earliest_cursor: result.volatile_earliest_cursor } : {}),
364
+ ...(typeof result?.volatile_dropped_count === 'number' ? { volatile_dropped_count: result.volatile_dropped_count } : {}),
365
+ ...(typeof result?.volatile_has_more === 'boolean' ? { volatile_has_more: result.volatile_has_more } : {}),
356
366
  };
357
367
  }
358
368
  catch (e) {