evolcore 0.0.19 → 0.0.21

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 (136) hide show
  1. package/CHANGELOG.md +43 -1
  2. package/README.md +60 -9
  3. package/bin/install-codex-managed-hooks.mjs +61 -0
  4. package/dist/agents/baseagent.js +10 -6
  5. package/dist/agents/claude-runner.js +383 -111
  6. package/dist/agents/codex-app-server-client.js +10 -2
  7. package/dist/agents/codex-runner.js +405 -137
  8. package/dist/agents/ecagent-runner.js +174 -63
  9. package/dist/agents/gemini-runner.js +130 -30
  10. package/dist/agents/request-identity.js +25 -0
  11. package/dist/agents/runner-types.js +19 -0
  12. package/dist/aun/aid/agentmd.js +66 -2
  13. package/dist/aun/aid/identity.js +4 -1
  14. package/dist/aun/aid/index.js +1 -1
  15. package/dist/aun/msg/group.js +86 -9
  16. package/dist/aun/msg/history.js +213 -36
  17. package/dist/aun/msg/managed-operation.js +58 -9
  18. package/dist/aun/msg/p2p.js +26 -11
  19. package/dist/aun/outbox.js +295 -68
  20. package/dist/aun/service-proxy.js +43 -25
  21. package/dist/channels/aun.js +1004 -273
  22. package/dist/channels/daemon.js +6 -1
  23. package/dist/cli/agent-command.js +4 -3
  24. package/dist/cli/agent.js +66 -56
  25. package/dist/cli/aun-commands.js +177 -42
  26. package/dist/cli/command-log.js +10 -11
  27. package/dist/cli/contact.js +1 -0
  28. package/dist/cli/daemon-commands.js +81 -121
  29. package/dist/cli/index.js +1 -0
  30. package/dist/cli/init.js +76 -24
  31. package/dist/cli/restart-monitor.js +3 -3
  32. package/dist/cli/task-context.js +46 -0
  33. package/dist/cli/trigger-command.js +1 -1
  34. package/dist/cli/watch-logs.js +10 -3
  35. package/dist/config/builtin-roles.js +1 -0
  36. package/dist/config/config-field-policy.js +16 -5
  37. package/dist/config/config-manager.js +226 -24
  38. package/dist/config/config-operation-service.js +1 -2
  39. package/dist/config/contact-operation-service.js +32 -1
  40. package/dist/config/contact-request-service.js +44 -0
  41. package/dist/config/daemon-services.js +186 -0
  42. package/dist/config/gateway-config.js +29 -16
  43. package/dist/config/lifecycle.js +16 -5
  44. package/dist/config/role-service.js +54 -3
  45. package/dist/config/schema-migration.js +550 -0
  46. package/dist/config-store.js +161 -12
  47. package/dist/core/agent-application-service.js +279 -0
  48. package/dist/core/audit/log-integrity.js +102 -0
  49. package/dist/core/auth/agent-delegation.js +31 -1
  50. package/dist/core/auth/auth-gateway.js +33 -4
  51. package/dist/core/auth/authorization-audit.js +155 -4
  52. package/dist/core/auth/operation-authorizer.js +41 -1
  53. package/dist/core/auth/operation-catalog.js +9 -1
  54. package/dist/core/bootstrap-messages.js +2 -2
  55. package/dist/core/bootstrap-service.js +27 -38
  56. package/dist/core/causation/aun-association.js +7 -4
  57. package/dist/core/channel-loader.js +0 -2
  58. package/dist/core/command/agent-control.js +56 -16
  59. package/dist/core/command/command-handler.js +290 -44
  60. package/dist/core/command/connect-menu.js +3 -4
  61. package/dist/core/command/group-menu.js +5 -7
  62. package/dist/core/command/menu-handler.js +279 -80
  63. package/dist/core/command/role-menu.js +21 -11
  64. package/dist/core/command/slash-gate.js +85 -18
  65. package/dist/core/command/slash-handler.js +350 -32
  66. package/dist/core/event-catalog.js +5 -0
  67. package/dist/core/evolagent.js +9 -4
  68. package/dist/core/handoff/dispatcher.js +4 -0
  69. package/dist/core/handoff/runtime.js +10 -0
  70. package/dist/core/handoff/store.js +32 -9
  71. package/dist/core/inference/text-inference.js +7 -15
  72. package/dist/core/message/im-renderer.js +83 -84
  73. package/dist/core/message/{inbound-admission.js → message-admission.js} +51 -1
  74. package/dist/core/message/message-bridge.js +124 -10
  75. package/dist/core/message/message-log.js +14 -7
  76. package/dist/core/message/message-queue.js +206 -16
  77. package/dist/core/message/message-utils.js +12 -5
  78. package/dist/core/message/response-engine.js +495 -72
  79. package/dist/core/message/send-receipt.js +1 -0
  80. package/dist/core/message/stream-debouncer.js +9 -2
  81. package/dist/core/model/model-catalog.js +23 -15
  82. package/dist/core/model/model-diagnostics.js +28 -10
  83. package/dist/core/permission/approval-gateway.js +180 -6
  84. package/dist/core/permission/ec-command-parser.js +465 -56
  85. package/dist/core/permission/mode.js +18 -3
  86. package/dist/core/{protected-paths.js → permission/protected-paths.js} +17 -5
  87. package/dist/core/permission/readonly-shell-query.js +263 -9
  88. package/dist/core/permission/sandbox-runtime.js +205 -13
  89. package/dist/core/permission/tool-policy.js +673 -62
  90. package/dist/core/relation/peer-identity.js +18 -0
  91. package/dist/core/session/session-fs-store.js +154 -5
  92. package/dist/core/session/session-manager.js +299 -30
  93. package/dist/core/session/session-renew.js +19 -12
  94. package/dist/core/session/session-turn-coordinator.js +11 -4
  95. package/dist/eck/kit-renderer.js +18 -9
  96. package/dist/index.js +283 -65
  97. package/dist/ipc.js +374 -24
  98. package/dist/paths.js +64 -7
  99. package/dist/response-system/context-builder.js +1 -7
  100. package/dist/trigger/anomaly-store.js +1 -0
  101. package/dist/trigger/feedback.js +56 -5
  102. package/dist/trigger/history.js +79 -4
  103. package/dist/trigger/legacy-session-history.js +2 -2
  104. package/dist/trigger/parser.js +3 -2
  105. package/dist/trigger/validation.js +6 -1
  106. package/dist/utils/atomic-write.js +27 -0
  107. package/dist/utils/ecweb-utils.js +16 -2
  108. package/dist/utils/error-utils.js +4 -1
  109. package/dist/utils/logger.js +21 -2
  110. package/dist/utils/process-tree-stats.js +24 -4
  111. package/dist/utils/process-tree-worker.js +31 -0
  112. package/dist/utils/project-path.js +1 -2
  113. package/dist/utils/stats.js +52 -18
  114. package/dist/utils/welcome.js +2 -2
  115. package/kits/docs/INDEX.md +1 -1
  116. package/kits/docs/evolcore/INDEX.md +1 -1
  117. package/kits/docs/evolcore/contact.md +7 -1
  118. package/kits/docs/evolcore/msg.md +16 -0
  119. package/kits/rules/01-overview.md +1 -1
  120. package/kits/rules/03-identity.md +1 -1
  121. package/kits/rules/05-venue.md +1 -1
  122. package/kits/schemas/_meta.json +10 -5
  123. package/kits/schemas/agent-config.schema.10.json +2 -1
  124. package/kits/schemas/agent-config.schema.11.json +421 -0
  125. package/kits/schemas/daemon.schema.5.json +135 -0
  126. package/kits/schemas/daemon.schema.6.json +131 -0
  127. package/kits/schemas/defaults.schema.5.json +119 -0
  128. package/kits/schemas/migrations/README.md +3 -1
  129. package/kits/schemas/relation-config.schema.8.json +13 -0
  130. package/kits/schemas/role-config.schema.1.json +1 -2
  131. package/kits/schemas/single-session.schema.3.json +32 -0
  132. package/kits/templates/roles/admin.json +1 -0
  133. package/kits/templates/roles/member.json +1 -0
  134. package/kits/templates/roles/visitor.json +1 -0
  135. package/package.json +7 -3
  136. package/skills/eclink/SKILL.md +2 -0
@@ -5,6 +5,7 @@ import { isExplicitGroupId } from '../aun/group-identity.js';
5
5
  import { getArgValue, isHelpFlag, wantsHelp } from './help.js';
6
6
  import { ipcQuery } from '../ipc.js';
7
7
  import { AGENT_DELEGATION_TOKEN_ENV } from '../core/auth/agent-delegation.js';
8
+ import { readBestTaskRuntimeContext } from './task-context.js';
8
9
  // ==================== AID ====================
9
10
  function resolveAunPath(args) {
10
11
  const idx = args.indexOf('--aun-path');
@@ -98,6 +99,14 @@ async function runManagedAid(args, formatJson) {
98
99
  if (!delegationToken) {
99
100
  failManagedAid(formatJson, 'DELEGATION_REQUIRED', `${AGENT_DELEGATION_TOKEN_ENV} is not set for the active task`);
100
101
  }
102
+ const { resolveManagedAidOperation } = await import('../aun/aid/managed-operation.js');
103
+ const parsed = resolveManagedAidOperation(['aid', ...args]);
104
+ if (!parsed.ok)
105
+ failManagedAid(formatJson, parsed.code, parsed.reason);
106
+ const runtime = await readBestTaskRuntimeContext();
107
+ if (runtime?.selfAid && parsed.command.aid !== runtime.selfAid.replace(/^@/, '')) {
108
+ failManagedAid(formatJson, 'SCOPE_MISMATCH', `托管任务只能操作当前 self AID:${runtime.selfAid}`);
109
+ }
101
110
  const response = await ipcQuery(resolvePaths().socket, {
102
111
  type: 'aid.op',
103
112
  argv: ['aid', ...args],
@@ -153,6 +162,12 @@ async function runManagedGroup(args, formatJson) {
153
162
  if (args.includes('--aun-path') || args.includes('--app')) {
154
163
  failManagedGroup(formatJson, 'NOT_ALLOWED', '--aun-path and --app are not allowed in a managed task');
155
164
  }
165
+ const { resolveManagedGroupOperation } = await import('../aun/msg/managed-operation.js');
166
+ const parsed = resolveManagedGroupOperation(['group', ...args]);
167
+ if (!parsed.ok)
168
+ failManagedGroup(formatJson, parsed.code, parsed.reason);
169
+ const runtime = await readBestTaskRuntimeContext();
170
+ validateManagedGroupScope(parsed.command.from, parsed.command.groupId, runtime, formatJson);
156
171
  const response = await ipcQuery(resolvePaths().socket, {
157
172
  type: 'group.op',
158
173
  argv: ['group', ...args],
@@ -174,6 +189,33 @@ async function runManagedMsg(args, formatJson) {
174
189
  if (!delegationToken) {
175
190
  failManagedGroup(formatJson, 'DELEGATION_REQUIRED', `${AGENT_DELEGATION_TOKEN_ENV} is not set for the active task`);
176
191
  }
192
+ const { resolveManagedMsgOperation } = await import('../aun/msg/managed-operation.js');
193
+ const parsed = resolveManagedMsgOperation(['msg', ...args]);
194
+ if (!parsed.ok)
195
+ failManagedGroup(formatJson, parsed.code, parsed.reason);
196
+ const runtime = await readBestTaskRuntimeContext();
197
+ if (runtime?.selfAid
198
+ && parsed.command.from !== runtime.selfAid.replace(/^@/, '')) {
199
+ failManagedGroup(formatJson, 'SCOPE_MISMATCH', `托管任务只能使用当前 self AID:${runtime.selfAid}`);
200
+ }
201
+ const isSelectorHistory = parsed.command.kind === 'history'
202
+ && !!parsed.command.channelKey
203
+ && !!parsed.command.peerId;
204
+ if (isSelectorHistory) {
205
+ // Trigger tasks retain their originating channel in runtime.channel. The
206
+ // daemon-created session route is the stable, non-spoofable discriminator.
207
+ if (runtime && !runtime.channelId?.startsWith('trigger:')) {
208
+ failManagedGroup(formatJson, 'SCOPE_MISMATCH', '跨渠道 history selector 仅允许可信 Trigger 会话使用');
209
+ }
210
+ }
211
+ else {
212
+ if (runtime && (runtime.channel !== 'aun' || runtime.chatType !== 'private')) {
213
+ failManagedGroup(formatJson, 'SCOPE_MISMATCH', 'ec msg history/online 只能读取当前 AUN 私聊关系');
214
+ }
215
+ if (runtime?.channelId && parsed.command.target !== runtime.channelId.replace(/^@/, '')) {
216
+ failManagedGroup(formatJson, 'SCOPE_MISMATCH', `只能读取当前 AUN 私聊对端:${runtime.channelId}`);
217
+ }
218
+ }
177
219
  const response = await ipcQuery(resolvePaths().socket, {
178
220
  type: 'msg.op',
179
221
  argv: ['msg', ...args],
@@ -187,6 +229,21 @@ async function runManagedMsg(args, formatJson) {
187
229
  }
188
230
  return response.result;
189
231
  }
232
+ function validateManagedGroupScope(from, groupId, runtime, formatJson) {
233
+ if (!runtime)
234
+ return;
235
+ if (runtime.selfAid && from !== runtime.selfAid.replace(/^@/, '')) {
236
+ failManagedGroup(formatJson, 'SCOPE_MISMATCH', `托管任务只能使用当前 self AID:${runtime.selfAid}`);
237
+ }
238
+ if (!groupId || (runtime.peerRole !== 'member' && runtime.peerRole !== 'visitor'))
239
+ return;
240
+ if (runtime.channel !== 'aun' || runtime.chatType !== 'group') {
241
+ failManagedGroup(formatJson, 'ARGUMENT_MISMATCH', `当前角色 ${runtime.peerRole} 只能在 AUN 群聊任务中操作当前群`);
242
+ }
243
+ if (!runtime.channelId || groupId !== runtime.channelId) {
244
+ failManagedGroup(formatJson, 'ARGUMENT_MISMATCH', `当前角色 ${runtime.peerRole} 只能操作当前群 ${runtime.channelId ?? '<unknown>'},不能操作 ${groupId}`);
245
+ }
246
+ }
190
247
  function renderManagedMsgResult(sub, result, formatJson) {
191
248
  if (formatJson) {
192
249
  console.log(JSON.stringify(result));
@@ -992,7 +1049,7 @@ Commands:
992
1049
  ack <from> <seq> [--app <name>] 确认已读
993
1050
  recall <from> <message-id> [<message-id>...] 撤回消息
994
1051
  online <from> <target-aid> [<target-aid>...] 查询在线状态
995
- history <from> <target-aid> [筛选选项] 查询本地消息历史
1052
+ history <from> [<target-aid>|selector] [筛选选项] 查询本地消息历史
996
1053
 
997
1054
  Options:
998
1055
  --text-from-file <path> 从文件读取文本(UTF-8),用于超长消息或避免 Shell 转义
@@ -1008,6 +1065,8 @@ Options:
1008
1065
  --direction <value> 历史方向:in|out|all
1009
1066
  --channel <type> 历史渠道:aun|feishu|wechat|dingtalk|qqbot|wecom
1010
1067
  --channel-name <name> 历史渠道实例名(非 AUN 渠道必须匹配 Agent 配置)
1068
+ --channel-key <key> 非 AUN 已落盘渠道的完整实例键(与 --peer-id 成对)
1069
+ --peer-id <id> 非 AUN 渠道的原生对端 ID(与 --channel-key 成对)
1011
1070
  --return <none|required> 跨会话发送必填:none=仅补充目标上下文,required=回答回流来源
1012
1071
  --content-type <mime> 显式覆盖 MIME(仅 --file 模式)
1013
1072
  --as <type> 附件类型:image|video|voice|file(仅 --file 模式)
@@ -1029,13 +1088,16 @@ Options:
1029
1088
  ec msg ack alice.agentid.pub 42 --app my-bot
1030
1089
  ec msg recall alice.agentid.pub msg-uuid-1 msg-uuid-2
1031
1090
  ec msg online alice.agentid.pub bob.agentid.pub carol.agentid.pub
1032
- ec msg history alice.agentid.pub bob.agentid.pub --session latest --limit 50`);
1091
+ ec msg history alice.agentid.pub bob.agentid.pub --session latest --limit 50
1092
+ ec msg history alice.agentid.pub --channel-key feishu#alice.agentid.pub#main --peer-id ou_xxx --session all --format json`);
1033
1093
  return;
1034
1094
  }
1035
1095
  if (sub === 'history' && wantsHelp(args)) {
1036
- console.log(`用法: ec msg history <self-aid> <target-aid> [options]
1096
+ console.log(`用法: ec msg history <self-aid> [<target-aid> | --channel-key <key> --peer-id <id>] [options]
1097
+
1098
+ 查询指定会话对的本地历史,不调用 message.pull,也不影响收件箱游标。非 AUN selector 按已落盘的 session metadata、完整 channelKey 与原生 peer ID 精确读取,不能传入文件路径。
1037
1099
 
1038
- 查询指定会话对的本地历史,不调用 message.pull,也不影响收件箱游标。非 AUN 历史仅通过当前 Agent 已配置的渠道实例读取,不能传入文件路径。
1100
+ 托管会话限制:普通会话只能使用当前 self AID 和当前 AUN 私聊对端;可信 daemon Trigger 可用精确的 --channel-key/--peer-id selector 读取已落盘的非 AUN 私聊历史。托管任务不支持 --channel、--channel-name、--aun-path 或 --app。
1039
1101
 
1040
1102
  Options:
1041
1103
  --session <latest|all|id> 默认 latest;all 查询全部;也可指定会话 ID
@@ -1043,16 +1105,16 @@ Options:
1043
1105
  --before <time> 开区间;epoch 毫秒或 ISO 时间
1044
1106
  --after <time> 开区间;epoch 毫秒或 ISO 时间
1045
1107
  --direction <value> in|out|all(默认 all)
1046
- --channel <type> aun|feishu|wechat|dingtalk|qqbot|wecom(默认 aun)
1047
- --channel-name <name> 渠道实例名;省略时仅允许该类型恰好配置一个实例
1108
+ --channel-key <key> AUN 已落盘渠道完整键;必须与 --peer-id 同时给出
1109
+ --peer-id <id> 原生私聊对端 ID;必须与 --channel-key 同时给出
1048
1110
  --format <text|json> 输出格式(默认 text)
1049
1111
 
1050
1112
  示例:
1051
1113
  ec msg history alice.agentid.pub bob.agentid.pub
1052
1114
  ec msg history alice.agentid.pub bob.agentid.pub --session latest
1053
1115
  ec msg history alice.agentid.pub bob.agentid.pub --session all
1054
- ec msg history alice.agentid.pub oc_xxx --channel feishu --channel-name main --session all --format json
1055
- ec msg history alice.agentid.pub bob.agentid.pub --session meta_20260713_1783920000000 --format json`);
1116
+ ec msg history alice.agentid.pub bob.agentid.pub --session meta_20260713_1783920000000 --format json
1117
+ ec msg history alice.agentid.pub --channel-key feishu#alice.agentid.pub#main --peer-id ou_xxx --session all --format json`);
1056
1118
  return;
1057
1119
  }
1058
1120
  const from = args[1];
@@ -1073,14 +1135,12 @@ Options:
1073
1135
  const { msgSend, msgPull, msgAck, msgRecall, msgOnline, msgHistory } = await import('../aun/msg/index.js');
1074
1136
  const commonOpts = { aunPath, slotId: appSlot };
1075
1137
  if (sub === 'history') {
1076
- const target = args[2];
1077
- if (!target) {
1078
- console.error('用法: ec msg history <self-aid> <target-aid> [options]');
1079
- process.exit(1);
1080
- }
1081
- const historyValueFlags = new Set(['--session', '--limit', '--before', '--after', '--direction', '--channel', '--channel-name', '--format']);
1138
+ const target = args[2] && !args[2].startsWith('--') ? args[2] : undefined;
1139
+ const historyValueFlags = new Set([
1140
+ '--session', '--limit', '--before', '--after', '--direction', '--channel', '--channel-name', '--format', '--channel-key', '--peer-id',
1141
+ ]);
1082
1142
  const seenHistoryFlags = new Set();
1083
- for (let index = 3; index < args.length; index++) {
1143
+ for (let index = target ? 3 : 2; index < args.length; index++) {
1084
1144
  const flag = args[index];
1085
1145
  if (!historyValueFlags.has(flag)) {
1086
1146
  console.error(`❌ 未知 history 参数: ${flag}`);
@@ -1117,25 +1177,47 @@ Options:
1117
1177
  console.error(`❌ --after 必须是 epoch 毫秒或有效时间: ${afterRaw}`);
1118
1178
  process.exit(1);
1119
1179
  }
1120
- const channel = getArgValue(args, '--channel') ?? 'aun';
1121
- const channelTypes = new Set(['aun', 'feishu', 'wechat', 'dingtalk', 'qqbot', 'wecom']);
1122
- if (!channelTypes.has(channel)) {
1123
- console.error(`❌ 不支持的 channel: ${channel}`);
1180
+ const channelKey = getArgValue(args, '--channel-key');
1181
+ const peerId = getArgValue(args, '--peer-id');
1182
+ const usesSelector = channelKey !== undefined || peerId !== undefined;
1183
+ if (usesSelector && (channelKey === undefined || peerId === undefined)) {
1184
+ console.error('❌ --channel-key 与 --peer-id 必须同时提供');
1124
1185
  process.exit(1);
1125
1186
  }
1126
- let channelName;
1127
- try {
1128
- channelName = resolveHistoryChannelName(from, channel, getArgValue(args, '--channel-name'));
1187
+ if (usesSelector && target) {
1188
+ console.error('❌ --channel-key/--peer-id 不能与 positional target 同时使用');
1189
+ process.exit(1);
1129
1190
  }
1130
- catch (error) {
1131
- console.error(`❌ ${error instanceof Error ? error.message : String(error)}`);
1191
+ if (usesSelector && (getArgValue(args, '--channel') !== undefined || getArgValue(args, '--channel-name') !== undefined)) {
1192
+ console.error('❌ --channel-key/--peer-id 不能与 --channel --channel-name 同时使用');
1132
1193
  process.exit(1);
1133
1194
  }
1195
+ if (!usesSelector && !target) {
1196
+ console.error('用法: ec msg history <self-aid> <target-aid> [options]');
1197
+ process.exit(1);
1198
+ }
1199
+ let channel;
1200
+ let channelName;
1201
+ if (!usesSelector) {
1202
+ channel = getArgValue(args, '--channel') ?? 'aun';
1203
+ const channelTypes = new Set(['aun', 'feishu', 'wechat', 'dingtalk', 'qqbot', 'wecom']);
1204
+ if (!channelTypes.has(channel)) {
1205
+ console.error(`❌ 不支持的 channel: ${channel}`);
1206
+ process.exit(1);
1207
+ }
1208
+ try {
1209
+ channelName = resolveHistoryChannelName(from, channel, getArgValue(args, '--channel-name'));
1210
+ }
1211
+ catch (error) {
1212
+ console.error(`❌ ${error instanceof Error ? error.message : String(error)}`);
1213
+ process.exit(1);
1214
+ }
1215
+ }
1134
1216
  const result = msgHistory({
1135
1217
  from,
1136
- target,
1137
- channel: channel,
1138
- channelName,
1218
+ ...(usesSelector
1219
+ ? { channelKey: channelKey, peerId: peerId }
1220
+ : { target: target, channel: channel, channelName }),
1139
1221
  session: getArgValue(args, '--session'),
1140
1222
  limit,
1141
1223
  before,
@@ -1555,23 +1637,25 @@ Options:
1555
1637
  // 收集 --mention(可多次;每次的值支持逗号分隔多个 aid)
1556
1638
  const collectMentions = () => {
1557
1639
  const mentions = [];
1640
+ const optionsEnd = args.indexOf('--');
1641
+ const optionArgs = optionsEnd >= 0 ? args.slice(0, optionsEnd) : args;
1558
1642
  for (let i = 0; i < args.length; i++) {
1643
+ if (args[i] === '--')
1644
+ break;
1559
1645
  if (args[i] !== '--mention')
1560
1646
  continue;
1561
1647
  const val = args[i + 1];
1562
1648
  if (val === undefined || val.startsWith('--')) {
1563
- console.error(`❌ --mention 后面缺少 <aid>`);
1564
- process.exit(1);
1649
+ failManagedGroup(formatJson, 'INVALID_ARGUMENT', '--mention 后面缺少 <aid>');
1565
1650
  }
1566
1651
  for (const aid of val.split(',').map(s => s.trim()).filter(Boolean)) {
1567
1652
  if (!isValidAid(aid)) {
1568
- console.error(`❌ --mention 的 aid 无效: ${aid}`);
1569
- process.exit(1);
1653
+ failManagedGroup(formatJson, 'INVALID_AID', `--mention 的 aid 无效: ${aid}`);
1570
1654
  }
1571
1655
  mentions.push({ aid });
1572
1656
  }
1573
1657
  }
1574
- if (args.includes('--mention-all')) {
1658
+ if (optionArgs.includes('--mention-all')) {
1575
1659
  mentions.push({ scope: 'all' });
1576
1660
  }
1577
1661
  return mentions;
@@ -1638,17 +1722,61 @@ Options:
1638
1722
  // ---- 消息 ----
1639
1723
  if (sub === 'send') {
1640
1724
  const groupId = requireGroupId();
1641
- const fileVal = getArgValue(args, '--file');
1642
- const payloadVal = getArgValue(args, '--payload');
1725
+ // Group sends use the dedicated AUN message daemon route rather than the
1726
+ // managed group-operation IPC parser. Apply the same task scope checks
1727
+ // before building or uploading the payload, so a bad target fails locally
1728
+ // instead of reaching the transport layer first.
1729
+ if (process.env.EVOLCORE_SESSION_ID) {
1730
+ if (args.includes('--aun-path') || args.includes('--app')) {
1731
+ failManagedGroup(formatJson, 'NOT_ALLOWED', '--aun-path and --app are not allowed in a managed task');
1732
+ }
1733
+ const runtime = await readBestTaskRuntimeContext();
1734
+ validateManagedGroupScope(from, groupId, runtime, formatJson);
1735
+ }
1736
+ const optionsEnd = args.indexOf('--', 3);
1737
+ const optionArgs = optionsEnd >= 0 ? args.slice(0, optionsEnd) : args;
1738
+ const getSendArgValue = (flag) => getArgValue(optionArgs, flag);
1739
+ const fileVal = getSendArgValue('--file');
1740
+ const payloadVal = getSendArgValue('--payload');
1741
+ const bodyPositionals = collectPositional(args, 3);
1742
+ const bodyFlags = ['--file', '--payload', '--as', '--content-type', '--text', '--transcript', '--thread', '--return'];
1743
+ for (let index = 3; index < args.length; index++) {
1744
+ const flag = args[index];
1745
+ if (flag === '--')
1746
+ break;
1747
+ if (bodyFlags.includes(flag)) {
1748
+ if (index + 1 >= args.length || args[index + 1].startsWith('--')) {
1749
+ failManagedGroup(formatJson, 'INVALID_ARGUMENT', `${flag} 后面缺少值`);
1750
+ }
1751
+ index++;
1752
+ }
1753
+ }
1754
+ if (optionArgs.includes('--encrypt') && optionArgs.includes('--no-encrypt')) {
1755
+ failManagedGroup(formatJson, 'INVALID_ARGUMENT', '--encrypt 与 --no-encrypt 不能同时使用');
1756
+ }
1757
+ if (fileVal && payloadVal) {
1758
+ failManagedGroup(formatJson, 'INVALID_ARGUMENT', '--file and --payload are mutually exclusive');
1759
+ }
1760
+ if ((fileVal || payloadVal) && bodyPositionals.length > 0) {
1761
+ failManagedGroup(formatJson, 'INVALID_ARGUMENT', '文本正文不能与 --file 或 --payload 同时使用');
1762
+ }
1643
1763
  let body;
1644
1764
  if (fileVal) {
1765
+ const attachmentType = getSendArgValue('--as');
1766
+ if (attachmentType && !['image', 'video', 'voice', 'file'].includes(attachmentType)) {
1767
+ failManagedGroup(formatJson, 'INVALID_ARGUMENT', `--as 仅支持 image|video|voice|file: ${attachmentType}`);
1768
+ }
1769
+ const transcript = getSendArgValue('--transcript');
1770
+ if (transcript !== undefined && attachmentType !== 'voice') {
1771
+ failManagedGroup(formatJson, 'INVALID_ARGUMENT', '--transcript 仅能与 --as voice 一起使用');
1772
+ }
1645
1773
  body = {
1646
1774
  mode: 'file',
1647
1775
  filePath: fileVal,
1648
- as: getArgValue(args, '--as'),
1649
- contentType: getArgValue(args, '--content-type'),
1650
- text: getArgValue(args, '--text'),
1651
- transcript: getArgValue(args, '--transcript'),
1776
+ as: attachmentType,
1777
+ contentType: getSendArgValue('--content-type'),
1778
+ text: getSendArgValue('--text'),
1779
+ transcript,
1652
1780
  };
1653
1781
  }
1654
1782
  else if (payloadVal) {
@@ -1660,9 +1788,17 @@ Options:
1660
1788
  console.error(`❌ --payload 解析失败: ${e.message}`);
1661
1789
  process.exit(1);
1662
1790
  }
1791
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
1792
+ failManagedGroup(formatJson, 'INVALID_ARGUMENT', '--payload 必须是 JSON 对象');
1793
+ }
1663
1794
  body = { mode: 'payload', payload: parsed };
1664
1795
  }
1665
1796
  else {
1797
+ const fileOnlyFlags = ['--as', '--content-type', '--text', '--transcript']
1798
+ .filter(flag => optionArgs.includes(flag));
1799
+ if (fileOnlyFlags.length > 0) {
1800
+ failManagedGroup(formatJson, 'INVALID_ARGUMENT', `${fileOnlyFlags.join(', ')} 仅能与 --file 一起使用`);
1801
+ }
1666
1802
  const text = collectPositional(args, 3).join(' ');
1667
1803
  if (!text) {
1668
1804
  console.error('❌ 缺少消息内容(文本或 --file/--payload)');
@@ -1675,11 +1811,10 @@ Options:
1675
1811
  const encryptGroup = args.includes('--no-encrypt')
1676
1812
  ? false
1677
1813
  : args.includes('--encrypt') ? true : undefined;
1678
- const thread = getArgValue(args, '--thread');
1679
- const returnPolicyRaw = getArgValue(args, '--return');
1814
+ const thread = getSendArgValue('--thread');
1815
+ const returnPolicyRaw = getSendArgValue('--return');
1680
1816
  if (returnPolicyRaw && returnPolicyRaw !== 'required' && returnPolicyRaw !== 'none') {
1681
- console.error(`❌ --return 仅支持 required|none: ${returnPolicyRaw}`);
1682
- process.exit(1);
1817
+ failManagedGroup(formatJson, 'INVALID_ARGUMENT', `--return 仅支持 required|none: ${returnPolicyRaw}`);
1683
1818
  }
1684
1819
  const result = await groupSend({
1685
1820
  from,
@@ -5,17 +5,16 @@
5
5
  * 挂在唯一 choke-point(src/cli/index.ts 的 main() 第一行),所有子命令必经,
6
6
  * 无需改动任何具体命令集代码。
7
7
  *
8
- * 两级落盘:
9
- * - 汇总日志 $EVOLCORE_HOME/logs/commands-YYYYMMDD.jsonl:所有命令(不论有无 AID)都记
10
- * 一份,一处看全貌;记录带 selfAid,按 agent 过滤一行 grep 即可。
11
- * - per-agent 日志 agents/<aid>/logs/commands-YYYYMMDD.jsonl:能同步解析出 self-AID 时
12
- * 额外再记一份,便于单独查看某个 agent。
8
+ * 统一落盘到 $EVOLCORE_HOME/logs/commands-YYYYMMDD.jsonl。所有命令(不论有无
9
+ * AID)都记一份;记录带 selfAid,可直接按 agent 过滤。Agent 业务数据目录不存放
10
+ * 审计或诊断日志。
13
11
  *
14
12
  * 原则:日志写失败静默降级,绝不影响命令执行(沿用 log-writer.ts 的取舍)。
15
13
  */
16
14
  import fs from 'fs';
17
15
  import path from 'path';
18
- import { agentLogsDir, resolvePaths } from '../paths.js';
16
+ import { resolvePaths } from '../paths.js';
17
+ import { migrateLegacyAgentJsonlLogs } from '../core/audit/log-integrity.js';
19
18
  import { getArgValue } from './help.js';
20
19
  import { readTaskRuntimeContextFromEnv } from './task-context.js';
21
20
  /** Keep path derivation from accepting arbitrary option values. */
@@ -52,6 +51,10 @@ function resolveSelfAid(args) {
52
51
  * @param args 完整 argv 原样
53
52
  */
54
53
  export function logCliCommand(cmd, args) {
54
+ const paths = resolvePaths();
55
+ // This entry point runs for every CLI command, including status/help commands
56
+ // that do not otherwise call ensureDataDirs().
57
+ migrateLegacyAgentJsonlLogs(paths.agentsDir, paths.logs);
55
58
  const selfAid = resolveSelfAid(args);
56
59
  const line = JSON.stringify({
57
60
  ts: localTimestamp(),
@@ -62,11 +65,7 @@ export function logCliCommand(cmd, args) {
62
65
  sessionId: process.env.EVOLCORE_SESSION_ID || undefined,
63
66
  }) + '\n';
64
67
  const fileName = `commands-${dayTag()}.jsonl`;
65
- // 汇总日志:所有命令(不论有无 AID)都记一份,一处看全貌。
66
- appendLine(resolvePaths().logs, fileName, line);
67
- // per-agent 日志:有 AID 时再额外记一份到该 agent 自己的 logs/,便于单独查看。
68
- if (selfAid)
69
- appendLine(agentLogsDir(selfAid), fileName, line);
68
+ appendLine(paths.logs, fileName, line);
70
69
  }
71
70
  /** 本地时间字符串 YYYY-MM-DD HH:mm:ss(不含时区偏移,直接可读)。 */
72
71
  function localTimestamp() {
@@ -59,6 +59,7 @@ function fail(message, code = 'INVALID_ARGUMENT', data) {
59
59
  }
60
60
  function printHelp() {
61
61
  console.log(`Usage:
62
+ ec contact add <primaryId> --self <agent-aid>
62
63
  ec contact block <primaryId> --self <agent-aid>
63
64
  ec contact unblock <primaryId> --self <agent-aid>
64
65
  ec contact list --blocked --self <agent-aid>