evolcore 0.0.28 → 0.0.29

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 (183) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/bin/ec-safe-output.js +270 -54
  3. package/dist/agents/baseagent-availability.js +37 -1
  4. package/dist/agents/baseagent.js +19 -9
  5. package/dist/agents/claude-runner.js +171 -13
  6. package/dist/agents/codex/app-server-client.js +6 -2
  7. package/dist/agents/codex/diagnostics.js +4 -3
  8. package/dist/agents/codex-runner.js +396 -95
  9. package/dist/agents/gemini-runner.js +35 -17
  10. package/dist/aun/group/history.js +130 -90
  11. package/dist/aun/group/operations.js +1 -3
  12. package/dist/aun/menu/transport.js +5 -3
  13. package/dist/aun/msg/managed-operation.js +70 -0
  14. package/dist/aun/msg/p2p.js +1 -3
  15. package/dist/aun/msg/upload.js +2 -2
  16. package/dist/aun/outbox.js +1 -37
  17. package/dist/channels/aun.js +249 -89
  18. package/dist/channels/daemon.js +18 -1
  19. package/dist/channels/feishu.js +1 -2
  20. package/dist/cli/agent-command.js +23 -3
  21. package/dist/cli/agent-create.js +68 -2
  22. package/dist/cli/agent.js +12 -2
  23. package/dist/cli/aun-commands.js +87 -13
  24. package/dist/cli/cli-argv.js +26 -0
  25. package/dist/cli/daemon-commands.js +40 -10
  26. package/dist/cli/data-command.js +4 -4
  27. package/dist/cli/handoff-command.js +2 -0
  28. package/dist/cli/index.js +18 -1
  29. package/dist/cli/managed-command-guard.js +10 -0
  30. package/dist/cli/memory-command.js +441 -0
  31. package/dist/cli/restart-monitor.js +62 -6
  32. package/dist/cli/skill-command.js +41 -0
  33. package/dist/cli/task-context.js +2 -23
  34. package/dist/cli/trigger-command.js +9 -1
  35. package/dist/cli/watch-msg-diag.js +209 -0
  36. package/dist/config/config-field-policy.js +52 -3
  37. package/dist/config/config-manager.js +17 -4
  38. package/dist/config/contact-book-store.js +2 -2
  39. package/dist/config/mention-mode.js +7 -2
  40. package/dist/config/merge.js +28 -9
  41. package/dist/config/resolved-config-op.js +1 -1
  42. package/dist/config/runtime-env.js +137 -0
  43. package/dist/config/schema-registry.js +11 -1
  44. package/dist/config-store.js +10 -10
  45. package/dist/core/audit/event-key.js +3 -1
  46. package/dist/core/audit/log-integrity.js +4 -0
  47. package/dist/core/auth/agent-delegation.js +2 -1
  48. package/dist/core/auth/auth-gateway.js +13 -6
  49. package/dist/core/auth/authorization-audit.js +54 -19
  50. package/dist/core/auth/operation-authorizer.js +17 -0
  51. package/dist/core/auth/operation-catalog.js +9 -0
  52. package/dist/core/causation/context.js +5 -1
  53. package/dist/core/command/agent-control.js +1 -2
  54. package/dist/core/command/command-handler.js +51 -23
  55. package/dist/core/command/memory-slash.js +270 -0
  56. package/dist/core/command/menu-catalog.js +1 -0
  57. package/dist/core/command/menu-handler.js +315 -60
  58. package/dist/core/command/menu-protocol.js +42 -4
  59. package/dist/core/command/slash-gate.js +10 -10
  60. package/dist/core/command/slash-handler.js +206 -64
  61. package/dist/core/data-migration.js +282 -505
  62. package/dist/core/event-catalog.js +26 -0
  63. package/dist/core/evolagent-registry.js +6 -0
  64. package/dist/core/evolagent.js +0 -10
  65. package/dist/core/handoff/store.js +1 -20
  66. package/dist/core/inference/text-inference.js +57 -1
  67. package/dist/core/memory/consolidation-context.js +505 -0
  68. package/dist/core/memory/consolidation-ops-records.js +124 -0
  69. package/dist/core/memory/consolidation-trigger.js +394 -0
  70. package/dist/core/memory/index-manager.js +308 -0
  71. package/dist/core/memory/launch-consolidation.js +395 -0
  72. package/dist/core/memory/memory-admin.js +234 -0
  73. package/dist/core/memory/memory-document.js +100 -0
  74. package/dist/core/memory/memory-session-policy.js +41 -0
  75. package/dist/core/memory/memory-signature.js +257 -0
  76. package/dist/core/memory/memory-writer.js +432 -0
  77. package/dist/core/memory/staleness.js +117 -0
  78. package/dist/core/message/auto-mention-gate.js +454 -0
  79. package/dist/core/message/im-renderer.js +69 -16
  80. package/dist/core/message/message-bridge.js +287 -21
  81. package/dist/core/message/message-log.js +1 -0
  82. package/dist/core/message/message-queue.js +114 -27
  83. package/dist/core/message/pending-hints.js +329 -161
  84. package/dist/core/message/response-engine.js +567 -80
  85. package/dist/core/permission/ec-command-parser.js +1029 -80
  86. package/dist/core/permission/protected-paths.js +376 -126
  87. package/dist/core/permission/sandbox-runtime.js +40 -12
  88. package/dist/core/permission/tool-error-code.js +96 -13
  89. package/dist/core/permission/tool-policy.js +276 -23
  90. package/dist/core/role/runtime-policy.js +3 -7
  91. package/dist/core/session/{runner-task-history.js → runner-event-summary.js} +1 -1
  92. package/dist/core/session/session-fs-store.js +0 -76
  93. package/dist/core/session/session-idle-monitor.js +355 -0
  94. package/dist/core/session/session-manager.js +478 -17
  95. package/dist/core/session/session-mapper.js +33 -16
  96. package/dist/core/session/session-renew.js +217 -155
  97. package/dist/core/session/{session-turns.js → session-transcript.js} +35 -6
  98. package/dist/core/skill/global-watcher.js +282 -0
  99. package/dist/core/skill/index-generator.js +84 -0
  100. package/dist/core/skill/startup.js +73 -0
  101. package/dist/core/skill/switch-resolver.js +115 -0
  102. package/dist/core/task/task-activity-store.js +219 -0
  103. package/dist/eck/kit-renderer.js +30 -3
  104. package/dist/eck/manifest-engine.js +26 -4
  105. package/dist/index.js +708 -18
  106. package/dist/ipc.js +564 -2
  107. package/dist/paths.js +30 -9
  108. package/dist/response-system/engines/v1/proactive-flow.js +116 -15
  109. package/dist/response-system/modes/single-session/index.js +3 -0
  110. package/dist/trigger/feedback.js +1 -0
  111. package/dist/trigger/manager.js +0 -10
  112. package/dist/trigger/scheduler.js +11 -0
  113. package/dist/trigger/validation.js +22 -0
  114. package/dist/utils/avatar-upload.js +59 -18
  115. package/dist/utils/error-dict.json +6 -3
  116. package/dist/utils/error-utils.js +27 -0
  117. package/dist/utils/logger.js +32 -10
  118. package/dist/utils/media-cache.js +84 -0
  119. package/dist/utils/msg-diag.js +97 -0
  120. package/dist/utils/welcome.js +37 -8
  121. package/kits/docs/AUTHORING.md +314 -0
  122. package/kits/docs/GUIDE.md +2 -0
  123. package/kits/docs/INDEX.md +2 -0
  124. package/kits/docs/aun/CHEATSHEET.md +4 -0
  125. package/kits/docs/aun/SDK.md +60 -0
  126. package/kits/docs/context-assembly.md +10 -6
  127. package/kits/docs/evolcore/INDEX.md +17 -3
  128. package/kits/docs/evolcore/agent-create.md +24 -0
  129. package/kits/docs/evolcore/agent.md +5 -2
  130. package/kits/docs/evolcore/config.md +1 -1
  131. package/kits/docs/evolcore/ec-capabilities.md +364 -0
  132. package/kits/docs/evolcore/group.md +34 -18
  133. package/kits/docs/evolcore/memory-architecture.md +78 -0
  134. package/kits/docs/evolcore/memory-consolidation-batches.md +133 -0
  135. package/kits/docs/evolcore/memory-consolidation.md +263 -0
  136. package/kits/docs/evolcore/memory-tags.md +63 -0
  137. package/kits/docs/evolcore/memory.md +110 -0
  138. package/kits/docs/evolcore/menu-client.md +27 -11
  139. package/kits/{schemas → docs/evolcore}/menu-exec-schema-commands.md +12 -6
  140. package/kits/docs/evolcore/menu-protocol.md +213 -0
  141. package/kits/docs/evolcore/msg.md +2 -0
  142. package/kits/docs/evolcore/trigger.md +7 -5
  143. package/kits/docs/path-registry.md +6 -8
  144. package/kits/docs/prompt-loading-architecture.md +5 -3
  145. package/kits/docs/venues/group.md +5 -4
  146. package/kits/eck_manifest.json +294 -88
  147. package/kits/eck_manifest.memory.json +257 -0
  148. package/kits/rules/03-identity.md +8 -8
  149. package/kits/rules/05-venue.md +4 -2
  150. package/kits/rules/06-channel.md +1 -1
  151. package/kits/schemas/_meta.json +276 -47
  152. package/kits/schemas/agent-config.schema.12.json +38 -2
  153. package/kits/schemas/agent-config.schema.13.json +591 -0
  154. package/kits/schemas/daemon.schema.6.json +9 -9
  155. package/kits/schemas/defaults.schema.5.json +29 -0
  156. package/kits/schemas/relation-config.schema.8.json +14 -1
  157. package/kits/schemas/relation-config.schema.9.json +129 -0
  158. package/kits/schemas/role-config.schema.1.json +3 -3
  159. package/kits/skills/.switches.json +3 -0
  160. package/kits/skills/ec-skill/SKILL.md +217 -0
  161. package/kits/skills/ec-skill/scripts/cli.mjs +128 -0
  162. package/kits/skills/ec-skill/scripts/deps.mjs +107 -0
  163. package/kits/skills/ec-skill/scripts/frontmatter.mjs +104 -0
  164. package/kits/skills/ec-skill/scripts/skill.mjs +629 -0
  165. package/kits/skills/ec-skill/scripts/template/SKILL.md.tmpl +30 -0
  166. package/kits/templates/decision/auto-mention.md +25 -0
  167. package/kits/templates/decision/session-renew.md +22 -0
  168. package/kits/templates/memory-consolidation/forget.md +104 -0
  169. package/kits/templates/memory-consolidation/generalize.md +76 -0
  170. package/kits/templates/memory-consolidation/notes.md +133 -0
  171. package/kits/templates/memory-consolidation/profile.md +58 -0
  172. package/kits/templates/memory-consolidation/promote.md +72 -0
  173. package/kits/templates/memory-consolidation/workflow.md +75 -0
  174. package/kits/templates/system-fragments/bootstrap.md +14 -5
  175. package/kits/templates/system-fragments/commands.md +1 -0
  176. package/kits/templates/system-fragments/group-history.md +6 -0
  177. package/kits/templates/system-fragments/identity.md +0 -3
  178. package/kits/templates/system-fragments/memory.md +79 -0
  179. package/kits/templates/system-fragments/reply.md +1 -0
  180. package/kits/templates/system-fragments/venue.md +1 -1
  181. package/package.json +4 -6
  182. package/dist/trigger/legacy-session-history.js +0 -19
  183. package/kits/templates/system-fragments/group-unread-history.md +0 -7
@@ -222,7 +222,24 @@ export class DaemonChannel {
222
222
  const threadId = mode.thread === 'by_run'
223
223
  ? `trigger:${trigger.id}:${runId}`
224
224
  : `trigger:${trigger.id}`;
225
- return await this.sessionManager.getOrCreateSession(this.channelName, channelId, projectPath, threadId, { channelKey: this.channelKey, peerId: `trigger:${trigger.id}`, peerName: trigger.name }, trigger.name, undefined, 'private', baseagent, trigger.agentAid, this.channelName, 'system');
225
+ // 记忆整理会话:把会话原型与整理模式写进 metadata,供渲染层选 manifest / 手册,
226
+ // 供 ipc 侧校验写命令的 --scope。
227
+ const { memoryMode, memoryScope, consolidationBatchDir, consolidationChatDir, consolidationMemoryDir } = trigger.execution;
228
+ // 反馈目标与触发方式(launch-consolidation 在 normalize 后补挂的字段):
229
+ // 收尾时据此投递反馈,并判断是否该反馈(定时触发不反馈)。
230
+ const { feedbackOrigin, consolidationTriggeredBy } = trigger.execution;
231
+ return await this.sessionManager.getOrCreateSession(this.channelName, channelId, projectPath, threadId, {
232
+ channelKey: this.channelKey,
233
+ peerId: `trigger:${trigger.id}`,
234
+ peerName: trigger.name,
235
+ ...(memoryMode ? { sessionType: 'memory', memoryMode } : {}),
236
+ ...(memoryScope ? { memoryScope } : {}),
237
+ ...(consolidationBatchDir ? { consolidationBatchDir } : {}),
238
+ ...(consolidationChatDir ? { consolidationChatDir } : {}),
239
+ ...(consolidationMemoryDir ? { consolidationMemoryDir } : {}),
240
+ ...(feedbackOrigin ? { feedbackOrigin } : {}),
241
+ ...(consolidationTriggeredBy ? { consolidationTriggeredBy } : {}),
242
+ }, trigger.name, undefined, 'private', baseagent, trigger.agentAid, this.channelName, 'system');
226
243
  }
227
244
  executionDeadlineAt(startedAt = Date.now()) {
228
245
  return this.totalExecutionMs === undefined
@@ -1734,8 +1734,7 @@ export class FeishuChannelPlugin {
1734
1734
  case 'system.error':
1735
1735
  case 'result.error': {
1736
1736
  const sendCtx = { ...(replyCtx ?? {}) };
1737
- if (payload.kind === 'result.text' && payload.isFinal)
1738
- sendCtx.title = '✅ 最终回复:';
1737
+ // isFinal is now carried via payload.ext by response-engine; no title injection here.
1739
1738
  if (replyCtx?.metadata?.onThreadCreated)
1740
1739
  sendCtx.onThreadCreated = replyCtx.metadata.onThreadCreated;
1741
1740
  const ids = await channel.sendMessage(channelId, payload.text, sendCtx);
@@ -4,6 +4,9 @@ import { getArgValue, isHelpFlag, wantsHelp } from './help.js';
4
4
  import { isHostChinese } from '../utils/locale.js';
5
5
  import { readTaskRuntimeContextFromEnv } from './task-context.js';
6
6
  import { parseAgentWelcomeArgs } from './agent-create.js';
7
+ import { assertEcSingleLine } from './cli-argv.js';
8
+ import { canonicalizeEvolcoreCommandArgv } from '../core/permission/ec-command-parser.js';
9
+ import { parseAgentAvatarArgs } from './agent-create.js';
7
10
  function printBootstrapDeliveries(result) {
8
11
  for (const delivery of result?.deliveries ?? []) {
9
12
  if (delivery.status === 'sent')
@@ -40,7 +43,13 @@ function formatBytes(bytes) {
40
43
  }
41
44
  // ==================== Agent ====================
42
45
  export async function cmdAgent(args) {
46
+ args = canonicalizeEvolcoreCommandArgv(['ec', 'agent', ...args]).slice(2);
43
47
  const sub = args[0];
48
+ if (sub === 'new') {
49
+ const nameIndex = args.indexOf('--name');
50
+ if (nameIndex >= 0)
51
+ assertEcSingleLine(args[nameIndex + 1], '--name');
52
+ }
44
53
  const formatJson = getArgValue(args, '--format') === 'json';
45
54
  const commands = new Set(['list', 'show', 'new', 'initialize', 'enable', 'disable', 'get', 'set', 'persona', 'welcome', 'rename', 'ready', 'reload', 'delete']);
46
55
  if (sub === 'create') {
@@ -95,7 +104,7 @@ Options:
95
104
  }
96
105
  if ((sub === 'new' && args.includes('--initialize')) || sub === 'initialize') {
97
106
  if (wantsHelp(args)) {
98
- console.log('ec agent new <aid> --initialize --name <name> --description <text> --tags <json> [--owner <aid> | --owners <json>] [--persona-file <path> | --persona-text <text>] [--wait 0..300] [--dry-run] [--format json]\nec agent initialize <aid> --resume [--wait 0..300] [--format json]\n--initialize implies --non-interactive; waits for activation (default 60 seconds).');
107
+ console.log('ec agent new <aid> --initialize ... [--avatar-file <path> | --avatar-url <https-url>] [--wait 0..300] [--format json]\nec agent initialize <aid> --resume [--wait 0..300] [--format json]\n--initialize implies --non-interactive; waits for activation (default 60 seconds).');
99
108
  return;
100
109
  }
101
110
  try {
@@ -270,6 +279,8 @@ Options:
270
279
  --owner <aid> 默认: daemon.json 第一个 owner
271
280
  --owners <json-array> 多个 Owner;与 --owner 互斥
272
281
  --handoff <context> 当前任务的 Bootstrap 上下文
282
+ --avatar-file <path> 头像图片文件(沿用 ec aid avatar)
283
+ --avatar-url <https-url> 头像 HTTPS 地址(沿用 ec aid avatar)
273
284
  --name <display-name> 默认: package.json name,否则目录名
274
285
  --description <text> 默认: package.json description,否则使用 EvolCore 默认描述
275
286
  --type <type> 主体类型: human|agent|group|service|device|org|host(默认 agent)
@@ -306,8 +317,10 @@ Options:
306
317
  return idx !== -1 && idx + 1 < args.length ? args[idx + 1] : undefined;
307
318
  };
308
319
  let ownerOptions;
320
+ let avatarOptions;
309
321
  try {
310
322
  ownerOptions = parseAgentOwnerArgs(args);
323
+ avatarOptions = parseAgentAvatarArgs(args);
311
324
  }
312
325
  catch (error) {
313
326
  const result = { ok: false, error: error instanceof Error ? error.message : String(error) };
@@ -336,6 +349,7 @@ Options:
336
349
  project: getArg('--project'),
337
350
  baseagent: getArg('--baseagent'),
338
351
  ...ownerOptions,
352
+ ...avatarOptions,
339
353
  handoff: getArg('--handoff'),
340
354
  name: getArg('--name'),
341
355
  description: getArg('--description'),
@@ -381,6 +395,8 @@ Options:
381
395
  type: plan.type,
382
396
  tags: plan.tags,
383
397
  force: args.includes('--force'),
398
+ avatarFile: plan.avatarFile,
399
+ avatarUrl: plan.avatarUrl,
384
400
  });
385
401
  if (!result.ok) {
386
402
  if (formatJson) {
@@ -498,11 +514,14 @@ Options:
498
514
  热重载 agent 配置。
499
515
  无参数 全量 resync(扫磁盘,新增上线、删除下线、修改热更新)
500
516
  <aid> 仅热重载指定 agent
501
- --force 中断目标 Agent 的运行中任务并清空排队任务后重载(仅与 <aid> 同用)`);
517
+ --force 中断目标 Agent 的运行中任务并清空排队任务后重载(仅与 <aid> 同用)
518
+ 重载时自动读取当前终端的 ANTHROPIC_* 环境变量`);
502
519
  return;
503
520
  }
504
521
  const target = args[1] && !args[1].startsWith('--') ? args[1] : undefined;
505
- const result = await agentReload(target, { force: args.includes('--force') });
522
+ const result = await agentReload(target, {
523
+ force: args.includes('--force'),
524
+ });
506
525
  if (!result.ok) {
507
526
  if (formatJson) {
508
527
  console.log(JSON.stringify(result));
@@ -906,6 +925,7 @@ Options:
906
925
  console.error('用法: ec agent rename <aid> <name>');
907
926
  process.exit(1);
908
927
  }
928
+ assertEcSingleLine(name, '<name>');
909
929
  const result = await agentRename(aid, name);
910
930
  if (!result.ok) {
911
931
  if (formatJson) {
@@ -19,6 +19,7 @@ import { AGENT_DESCRIPTION_MAX_LENGTH, AGENT_TAG_MAX_COUNT, AGENT_TAG_MIN_COUNT,
19
19
  import { atomicWriteJson } from '../utils/atomic-write.js';
20
20
  import { isHClassPath, isLClassPath, isSameOrDescendant } from '../core/permission/protected-paths.js';
21
21
  import { MAIN_CLI_BIN } from '../product.js';
22
+ import { uploadAvatarFile, uploadAvatarUrl } from '../utils/avatar-upload.js';
22
23
  /** Parse the current Agent's Bootstrap welcome subcommand. */
23
24
  export function parseAgentWelcomeArgs(args) {
24
25
  const tail = args.at(-2) === '--format' && args.at(-1) === 'json' ? args.slice(0, -2) : args;
@@ -29,6 +30,25 @@ export function parseAgentWelcomeArgs(args) {
29
30
  }
30
31
  throw new Error(`用法: ${MAIN_CLI_BIN} agent welcome show | --text <text> [--format json]`);
31
32
  }
33
+ export function parseAgentAvatarArgs(argv) {
34
+ const hasFile = argv.includes('--avatar-file');
35
+ const hasUrl = argv.includes('--avatar-url');
36
+ if (argv.filter(arg => arg === '--avatar-file').length > 1 || argv.filter(arg => arg === '--avatar-url').length > 1) {
37
+ throw new Error('--avatar-file and --avatar-url may each be supplied once');
38
+ }
39
+ if (hasFile && hasUrl)
40
+ throw new Error('--avatar-file and --avatar-url are mutually exclusive');
41
+ const flag = hasFile ? '--avatar-file' : hasUrl ? '--avatar-url' : undefined;
42
+ if (!flag)
43
+ return {};
44
+ const value = argv[argv.indexOf(flag) + 1];
45
+ if (!value || value.startsWith('--'))
46
+ throw new Error(`${flag} requires a value`);
47
+ if (value.startsWith('attachment:') && !process.env.EVOLCORE_SESSION_ID) {
48
+ throw new Error('attachment:<id> avatar sources require a managed Agent task');
49
+ }
50
+ return hasFile ? { avatarFile: value } : { avatarUrl: value };
51
+ }
32
52
  export function parseAgentTags(raw) {
33
53
  if (raw === undefined)
34
54
  return undefined;
@@ -203,7 +223,8 @@ export function resolveAgentCreatePlan(options) {
203
223
  const type = requestedType ? (normalizeAgentType(requestedType) ?? requestedType) : DEFAULT_AGENT_TYPE;
204
224
  const configExists = isValidAid(aid) && fs.existsSync(path.join(resolvePaths().agentsDir, aid, 'config.json'));
205
225
  return {
206
- aid, project, baseagent, owner, owners, handoff: options.handoff, name, description, type, tags: options.tags,
226
+ aid, project, baseagent, owner, owners, handoff: options.handoff, avatarFile: options.avatarFile, avatarUrl: options.avatarUrl,
227
+ name, description, type, tags: options.tags,
207
228
  force: options.force === true, configExists, baseagentSource, ownerSource,
208
229
  nameSource: options.name?.trim() ? 'argument' : metadata.name ? 'package' : 'directory',
209
230
  descriptionSource: explicitDescription ? 'argument' : packageDescription ? 'package' : 'default',
@@ -216,6 +237,8 @@ export function validateAgentCreatePlan(plan) {
216
237
  return ownerError;
217
238
  if (!isValidAid(plan.aid))
218
239
  return `Invalid AID "${plan.aid}": must be a valid multi-level domain (e.g. mybot.agentid.pub)`;
240
+ if (plan.avatarFile !== undefined && plan.avatarUrl !== undefined)
241
+ return '--avatar-file and --avatar-url are mutually exclusive';
219
242
  if (plan.configExists && !plan.force)
220
243
  return `Agent "${plan.aid}" already exists (use --force to overwrite)`;
221
244
  if (plan.tags !== undefined) {
@@ -655,6 +678,26 @@ export async function agentCreateNonInteractive(opts, runtime = {}) {
655
678
  agentmdError = `generation failed: ${e?.message || e}`;
656
679
  opts.onPhase?.('uploading_agentmd', 'warn', agentmdError);
657
680
  }
681
+ let avatarUploaded = false;
682
+ if (opts.avatarFile || opts.avatarUrl) {
683
+ opts.onPhase?.('uploading_avatar', 'begin');
684
+ try {
685
+ if (!agentmdUploaded)
686
+ throw new Error('头像上传前 agent.md 尚未成功发布');
687
+ const avatar = opts.avatarFile
688
+ ? await uploadAvatarFile(opts.aid, opts.avatarFile)
689
+ : await uploadAvatarUrl(opts.aid, opts.avatarUrl);
690
+ if (!avatar.ok)
691
+ throw new Error(avatar.error || '头像上传失败');
692
+ avatarUploaded = true;
693
+ opts.onPhase?.('uploading_avatar', 'done', `${avatar.width}x${avatar.height} ${avatar.mime}`);
694
+ }
695
+ catch (error) {
696
+ const detail = error instanceof Error ? error.message : String(error);
697
+ opts.onPhase?.('uploading_avatar', 'failed', detail);
698
+ return { ok: false, code: 'AVATAR_UPLOAD_FAILED', error: `头像上传失败:${detail}` };
699
+ }
700
+ }
658
701
  let hotLoaded = false;
659
702
  let hotLoadError;
660
703
  opts.onPhase?.('hot_loading', 'begin');
@@ -692,6 +735,7 @@ export async function agentCreateNonInteractive(opts, runtime = {}) {
692
735
  agentmdError,
693
736
  hotLoaded,
694
737
  hotLoadError,
738
+ ...(opts.avatarFile || opts.avatarUrl ? { avatarUploaded } : {}),
695
739
  ...ownerContactSync,
696
740
  };
697
741
  }
@@ -712,6 +756,7 @@ export function initializationFingerprint(plan) {
712
756
  return createHash('sha256').update(JSON.stringify([
713
757
  plan.aid, plan.project, plan.baseagent, plan.owners, plan.name,
714
758
  plan.description, plan.type, plan.tags, plan.persona ?? null,
759
+ plan.avatarFile ?? null, plan.avatarUrl ?? null,
715
760
  ])).digest('hex');
716
761
  }
717
762
  export function readInitialization(aid) {
@@ -749,6 +794,7 @@ export function initializationSummary(aid) {
749
794
  nextStep: r.status === 'failed' ? `ec agent initialize ${aid} --resume` : undefined,
750
795
  agentmd: { published: r.published === true, verified: r.verified === true },
751
796
  persona: { provided: r.plan.persona !== undefined },
797
+ avatar: { requested: r.plan.avatarFile !== undefined || r.plan.avatarUrl !== undefined, uploaded: r.avatarUploaded === true },
752
798
  welcome: r.welcome, startedAt: r.startedAt, updatedAt: r.updatedAt,
753
799
  };
754
800
  }
@@ -782,6 +828,8 @@ export function validateInitializationPlan(plan) {
782
828
  }
783
829
  if (plan.type !== 'agent')
784
830
  failure('INVALID_ARGS', 'Initialized targets must have type=agent');
831
+ if (plan.avatarFile !== undefined && plan.avatarUrl !== undefined)
832
+ failure('INVALID_ARGS', 'avatar-file and avatar-url are mutually exclusive');
785
833
  const error = validateAgentCreatePlan({
786
834
  ...plan, force: false, configExists: false, baseagentSource: 'argument', ownerSource: 'argument',
787
835
  nameSource: 'argument', descriptionSource: 'argument', typeSource: 'argument',
@@ -958,6 +1006,7 @@ export class AgentInitializationService {
958
1006
  failure('INITIALIZE_PLAN_CONFLICT');
959
1007
  fs.writeFileSync(file, p.persona, { encoding: 'utf8', mode: 0o600 });
960
1008
  }
1009
+ const resolvedAvatarFile = p.avatarFile;
961
1010
  await syncOwnerContactsAfterCreate(p.aid, p.owners);
962
1011
  this.phase(r, 'uploading_agentmd');
963
1012
  let remote;
@@ -1013,6 +1062,15 @@ export class AgentInitializationService {
1013
1062
  if (!fs.existsSync(agentMdPath(p.aid)) || !matchesCard(fs.readFileSync(agentMdPath(p.aid), 'utf8'), p)) {
1014
1063
  failure('INITIALIZE_AGENTMD_VERIFY_FAILED');
1015
1064
  }
1065
+ if ((resolvedAvatarFile || p.avatarUrl) && !r.avatarUploaded) {
1066
+ const avatar = resolvedAvatarFile
1067
+ ? await uploadAvatarFile(p.aid, resolvedAvatarFile)
1068
+ : await uploadAvatarUrl(p.aid, p.avatarUrl);
1069
+ if (!avatar.ok)
1070
+ failure('INITIALIZE_AVATAR_FAILED', avatar.error || 'avatar upload failed');
1071
+ r.avatarUploaded = true;
1072
+ saveInitialization(r);
1073
+ }
1016
1074
  r.published = true;
1017
1075
  r.verified = true;
1018
1076
  this.phase(r, 'hot_loading');
@@ -1074,7 +1132,7 @@ export function parseInitializationArgs(args) {
1074
1132
  throw new Error('Invalid initialization command or AID');
1075
1133
  const booleans = new Set(resume ? ['--resume'] : ['--initialize', '--non-interactive', '--dry-run']);
1076
1134
  const strings = new Set(resume ? ['--wait', '--format'] : [
1077
- '--project', '--baseagent', '--owner', '--owners', '--name', '--description', '--tags', '--type',
1135
+ '--project', '--baseagent', '--owner', '--owners', '--name', '--description', '--tags', '--type', '--avatar-file', '--avatar-url',
1078
1136
  '--persona-file', '--persona-text', '--wait', '--format',
1079
1137
  ]);
1080
1138
  const seen = new Set();
@@ -1114,16 +1172,24 @@ export function resolveInitializationPlan(args) {
1114
1172
  if (parsed.resume)
1115
1173
  throw new Error('A resume request cannot supply a new plan');
1116
1174
  const v = parsed.values;
1175
+ if (v['--avatar-file'] !== undefined && v['--avatar-url'] !== undefined)
1176
+ throw new Error('--avatar-file and --avatar-url are mutually exclusive');
1177
+ if (v['--avatar-file']?.startsWith('attachment:') && !process.env.EVOLCORE_SESSION_ID) {
1178
+ throw new Error('attachment:<id> avatar sources require a managed Agent task');
1179
+ }
1117
1180
  if (v['--persona-file'] !== undefined)
1118
1181
  throw new Error('CLI must submit persona file contents as text');
1119
1182
  const p = resolveAgentCreatePlan({
1120
1183
  aid: parsed.aid, project: v['--project'], baseagent: v['--baseagent'], ...parseAgentOwnerArgs(args),
1184
+ avatarFile: v['--avatar-file'], avatarUrl: v['--avatar-url'],
1121
1185
  name: v['--name'], description: v['--description'], tags: parseAgentTags(v['--tags']), type: v['--type'],
1122
1186
  });
1123
1187
  return {
1124
1188
  aid: p.aid, project: p.project, baseagent: p.baseagent ?? '', owners: p.owners ?? (p.owner ? [p.owner] : []),
1125
1189
  name: p.name, description: p.description, tags: p.tags, type: p.type,
1126
1190
  ...(v['--persona-text'] !== undefined ? { persona: v['--persona-text'] } : {}),
1191
+ ...(v['--avatar-file'] !== undefined ? { avatarFile: v['--avatar-file'] } : {}),
1192
+ ...(v['--avatar-url'] !== undefined ? { avatarUrl: v['--avatar-url'] } : {}),
1127
1193
  };
1128
1194
  }
1129
1195
  export function readInitializationPersona(file, managed) {
package/dist/cli/agent.js CHANGED
@@ -19,6 +19,7 @@ import { findPostBootstrapWelcomeOutbox } from '../core/bootstrap-messages.js';
19
19
  import { resolveAgentLifecycle } from '../config/lifecycle.js';
20
20
  import { initializationSummary } from './agent-create.js';
21
21
  import { parseAgentWelcomeMetadata, readAgentWelcomeTemplate, renderPostBootstrapWelcomeMessage, saveAgentWelcomeTemplate, validateWelcomeTemplate } from '../utils/welcome.js';
22
+ import { RUNTIME_ENVIRONMENT_NAMES } from '../config/runtime-env.js';
22
23
  export { agentCreateInteractive, agentCreateNonInteractive, parseAgentOwnerArgs, resolveAgentCreatePlan, validateAgentCreatePlan, validateAgentOwnerOptions, } from './agent-create.js';
23
24
  /** Route agent lifecycle commands from a managed task through daemon IPC. */
24
25
  export async function runManagedAgentOperation(argv) {
@@ -306,13 +307,17 @@ const AGENT_RELOAD_TIMEOUT_MS = 45_000;
306
307
  const AGENT_RESYNC_TIMEOUT_MS = 5 * 60_000;
307
308
  export async function agentReload(aid, options = {}) {
308
309
  const p = resolvePaths();
310
+ const runtimeEnv = Object.fromEntries(RUNTIME_ENVIRONMENT_NAMES.map(name => [name, process.env[name] || null]));
309
311
  if (!aid) {
310
312
  if (options.force) {
311
313
  return { ok: false, code: 'INVALID_ARGS', error: '--force requires a target Agent AID' };
312
314
  }
313
315
  // Full resync
314
316
  try {
315
- const result = await ipcQuery(p.socket, { type: 'evolagent.resync' }, AGENT_RESYNC_TIMEOUT_MS, { throwOnError: true });
317
+ const result = await ipcQuery(p.socket, {
318
+ type: 'evolagent.resync',
319
+ runtimeEnv,
320
+ }, AGENT_RESYNC_TIMEOUT_MS, { throwOnError: true });
316
321
  if (result === null) {
317
322
  return { ok: false, code: 'IPC_INVALID_RESPONSE', error: 'daemon IPC 未返回有效响应,请检查 ec status' };
318
323
  }
@@ -327,7 +332,12 @@ export async function agentReload(aid, options = {}) {
327
332
  }
328
333
  // Single agent reload
329
334
  try {
330
- const result = await ipcQuery(p.socket, { type: 'evolagent.reload', name: aid, force: options.force === true }, AGENT_RELOAD_TIMEOUT_MS, { throwOnError: true });
335
+ const result = await ipcQuery(p.socket, {
336
+ type: 'evolagent.reload',
337
+ name: aid,
338
+ force: options.force === true,
339
+ runtimeEnv,
340
+ }, AGENT_RELOAD_TIMEOUT_MS, { throwOnError: true });
331
341
  if (result === null) {
332
342
  return { ok: false, code: 'IPC_INVALID_RESPONSE', error: 'daemon IPC 未返回有效响应,请检查 ec status' };
333
343
  }
@@ -7,6 +7,9 @@ import { ipcQuery } from '../ipc.js';
7
7
  import { AGENT_DELEGATION_TOKEN_ENV, readDelegationCredential } from '../core/auth/agent-delegation.js';
8
8
  import { isManagementRole } from '../config/builtin-roles.js';
9
9
  import { readBestTaskRuntimeContext } from './task-context.js';
10
+ import { assertEcSingleLine } from './cli-argv.js';
11
+ import { canonicalizeEvolcoreCommandArgv } from '../core/permission/ec-command-parser.js';
12
+ import { ManagedGroupOperationError } from '../aun/msg/managed-operation.js';
10
13
  // ==================== AID ====================
11
14
  function resolveAunPath(args) {
12
15
  const idx = args.indexOf('--aun-path');
@@ -318,14 +321,17 @@ function renderManagedGroupResult(args, result, formatJson) {
318
321
  if (message.content_truncated)
319
322
  console.log('正文尚未读完');
320
323
  if (message.content_truncated && message.msgId) {
321
- console.log('--all --message ' + message.msgId + ' --offset ' + message.next_offset);
324
+ console.log('--message ' + message.msgId + ' --offset ' + message.next_offset);
322
325
  }
323
326
  }
324
327
  }
325
328
  if (result.next_offset != null)
326
329
  console.log('next_offset=' + result.next_offset);
327
- if (result.has_more)
328
- console.log('还有更多历史');
330
+ if (result.has_more) {
331
+ const cursor = result.next_before ?? result.next_after;
332
+ const direction = result.next_before ? 'before' : 'after';
333
+ console.log(`还有更多历史,请使用 --${direction} ${cursor.ts} --${direction}-id ${cursor.id}`);
334
+ }
329
335
  return;
330
336
  }
331
337
  if (sub === 'list') {
@@ -389,6 +395,31 @@ function renderManagedGroupResult(args, result, formatJson) {
389
395
  console.log(typeof result.message === 'string' ? result.message : `✓ group ${sub} 完成`);
390
396
  return;
391
397
  }
398
+ if (sub === 'mention') {
399
+ const renderMode = (value) => {
400
+ if (value === 'mention-only')
401
+ return '@提及时响应';
402
+ if (value === 'disabled')
403
+ return '所有消息响应';
404
+ if (value === 'auto')
405
+ return 'auto(本地自动判断)';
406
+ return String(value);
407
+ };
408
+ const sourceLabel = result.source === 'relation' ? '本群覆盖'
409
+ : result.source === 'agent' ? 'Agent 全局'
410
+ : result.source === 'aun' ? 'AUN 服务端'
411
+ : result.source === 'defaults' ? '默认配置' : '内置默认';
412
+ if (result.override === null) {
413
+ console.log(`@ 处理模式: ${renderMode(result.mode)}(跟随上级设置,当前生效来源:${sourceLabel})`);
414
+ return;
415
+ }
416
+ if (result.override === undefined) {
417
+ console.log(`@ 处理模式: ${renderMode(result.mode)}(${sourceLabel})`);
418
+ return;
419
+ }
420
+ console.log(`@ 处理模式: ${renderMode(result.override)}(本群覆盖${result.mode !== undefined ? `,实际生效 ${renderMode(result.mode)}` : ''})`);
421
+ return;
422
+ }
392
423
  console.log(`✓ group ${sub} 完成`);
393
424
  }
394
425
  export async function cmdAid(args) {
@@ -1204,6 +1235,7 @@ Options:
1204
1235
  }
1205
1236
  // ==================== Msg ====================
1206
1237
  export async function cmdMsg(args) {
1238
+ args = canonicalizeEvolcoreCommandArgv(['ec', 'msg', ...args]).slice(2);
1207
1239
  const sub = args[0];
1208
1240
  const aunPath = resolveAunPath(args);
1209
1241
  const formatJson = getArgValue(args, '--format') === 'json';
@@ -1487,6 +1519,7 @@ Options:
1487
1519
  body = { mode: 'text', text };
1488
1520
  }
1489
1521
  else if (linkVal) {
1522
+ assertEcSingleLine(getArgValue(args, '--title'), '--title');
1490
1523
  body = {
1491
1524
  mode: 'link',
1492
1525
  url: linkVal,
@@ -1695,25 +1728,28 @@ function printGroupSendAidHint() {
1695
1728
  console.error('查询真实群 AID: ec group list <发送方AID>');
1696
1729
  }
1697
1730
  export async function cmdGroup(args) {
1731
+ args = canonicalizeEvolcoreCommandArgv(['ec', 'group', ...args]).slice(2);
1698
1732
  const sub = args[0];
1733
+ if (sub === 'create')
1734
+ assertEcSingleLine(args[2], '<name>');
1735
+ if (sub === 'update')
1736
+ assertEcSingleLine(getArgValue(args, '--name'), '--name');
1699
1737
  const aunPath = resolveAunPath(args);
1700
1738
  const formatJson = getArgValue(args, '--format') === 'json';
1701
1739
  const appSlot = getArgValue(args, '--app');
1702
1740
  if (sub === 'history' && wantsHelp(args)) {
1703
- console.log(`用法: ec group history <self-aid> <group-aid> --unread|--all [options]
1741
+ console.log(`用法: ec group history <self-aid> <group-aid> [options]
1704
1742
  本地 messages.jsonl 查询,不访问 Gateway,不推进 group pull/ack 游标。
1705
- --unread 返回最早未读的未提及背景,并自动记录本页已读
1706
- --all 浏览群会话历史(返回最近 N 条),不改变已读状态
1743
+ 查询始终只读,不维护本地已读游标
1707
1744
  --limit <N> 默认 50,范围 1–500;另有 24 KiB 单页消息预算
1708
1745
  --thread <main|all|id> 当前群任务缺省继承当前话题;其他调用缺省 all
1709
1746
  --direction <in|out|all> 按方向筛选,默认 all
1710
1747
  --before/--after <time> 按接收时间筛选,开区间;epoch 毫秒或 ISO 时间
1748
+ --before-id/--after-id <id> 同一时间戳分页用的 64 位游标(需配合对应时间)
1711
1749
  --format <text|json> 默认 text
1712
- --message <msg-id> 配合 --all 查看指定长消息正文
1750
+ --message <msg-id> 查看指定长消息正文
1713
1751
  --offset <bytes> 配合 --message 从 next_offset 继续读取,默认 0
1714
- --unread 返回的消息会记为已读;--all(包括 --all --message)始终只读,之后仍可回看。
1715
- visitor 不开放历史;群权限仍由托管会话校验。
1716
- 旧日志通过 --all 查询,只有带本地未提及标记的新记录参与 --unread。`);
1752
+ visitor 不开放历史;群权限仍由托管会话校验。`);
1717
1753
  return;
1718
1754
  }
1719
1755
  const rulesActionHelp = sub === 'rules' && isHelpFlag(args[3]);
@@ -1726,7 +1762,7 @@ visitor 不开放历史;群权限仍由托管会话校验。
1726
1762
  send <from> <group-id> --payload <json> 发送自定义 payload
1727
1763
  pull <from> <group-id> [--after-seq N] [--limit N] --app <name> 拉取群消息(必须使用独立 slot)
1728
1764
  ack <from> <group-id> <seq> [--app <name>] 确认已读
1729
- history <from> <group-id> [--unread|--all] 查询本地群消息历史
1765
+ history <from> <group-id> [options] 查询本地群消息历史(只读)
1730
1766
 
1731
1767
  群管理:
1732
1768
  create <from> <name> [--visibility public|private] [--description D]
@@ -1741,6 +1777,8 @@ visitor 不开放历史;群权限仍由托管会话校验。
1741
1777
  rules <from> <group-id> set <file> 上传并发布群规则文件
1742
1778
  rules <from> <group-id> publish 发布当前群空间 /rules.md
1743
1779
  rules <from> <group-id> [--mode M] [--question Q] [--max-pending N] 查看/更新入群规则
1780
+ mention <from> <group-id> [<mention-only|disabled|auto>] [--clear]
1781
+ 查看/设置本群 @ 提及模式(不带 mode 为查看;--clear 清除本群覆盖跟随上级)
1744
1782
 
1745
1783
  成员:
1746
1784
  join <from> <group-id> [--message M] [--answer A] 申请加入
@@ -2599,7 +2637,43 @@ g- 是名称的一部分,会完整保留;默认命名格式为 g-10000 至 g
2599
2637
  });
2600
2638
  return;
2601
2639
  }
2602
- console.error(`未知子命令: ${sub}\n用法: ec group [send|pull|ack|history|create|list|info|update|dissolve|suspend|resume|rules|join|leave|invite|invites|accept-invite|reject-invite|revoke-invite|kick|members|online|role|owner|ban|unban|block|unblock|blocklist|report-status|member-statuses]`);
2640
+ if (sub === 'mention') {
2641
+ const groupId = requireGroupId();
2642
+ if (args[3])
2643
+ assertEcSingleLine(args[3], '<mode>');
2644
+ const values = collectPositional(args, 3);
2645
+ if (values.length > 1) {
2646
+ console.error('用法: ec group mention <from> <group-id> [<mention-only|disabled|auto>] [--clear]');
2647
+ process.exit(1);
2648
+ }
2649
+ const mode = values[0];
2650
+ const clear = args.includes('--clear');
2651
+ if (mode !== undefined && clear) {
2652
+ console.error('❌ <mode> 与 --clear 互斥');
2653
+ process.exit(1);
2654
+ }
2655
+ if (mode !== undefined && !['mention-only', 'disabled', 'auto'].includes(mode)) {
2656
+ console.error(`❌ 无效提及模式: ${mode}(可选 mention-only|disabled|auto)`);
2657
+ process.exit(1);
2658
+ }
2659
+ const operationArgs = ['mention', from, groupId, ...(mode ? [mode] : []), ...(clear ? ['--clear'] : [])];
2660
+ const { resolveManagedGroupOperation, executeManagedGroupOperation } = await import('../aun/msg/managed-operation.js');
2661
+ const parsed = resolveManagedGroupOperation(operationArgs);
2662
+ if (!parsed.ok)
2663
+ failManagedGroup(formatJson, parsed.code, parsed.reason);
2664
+ try {
2665
+ const result = await executeManagedGroupOperation(parsed.command);
2666
+ renderManagedGroupResult(operationArgs, result, formatJson);
2667
+ }
2668
+ catch (error) {
2669
+ if (error instanceof ManagedGroupOperationError) {
2670
+ failManagedGroup(formatJson, error.code, error.message);
2671
+ }
2672
+ throw error;
2673
+ }
2674
+ return;
2675
+ }
2676
+ console.error(`未知子命令: ${sub}\n用法: ec group [send|pull|ack|history|create|list|info|update|dissolve|suspend|resume|rules|mention|join|leave|invite|invites|accept-invite|reject-invite|revoke-invite|kick|members|online|role|owner|ban|unban|block|unblock|blocklist|report-status|member-statuses]`);
2603
2677
  process.exit(1);
2604
2678
  }
2605
2679
  // ==================== Main ====================
@@ -2625,7 +2699,7 @@ const VALUE_FLAGS = new Set([
2625
2699
  '--aun-path', '--thread', '--return', '--handoff-context',
2626
2700
  ]);
2627
2701
  const BOOLEAN_FLAGS = new Set([
2628
- '--encrypt', '--no-encrypt', '--mention-all',
2702
+ '--encrypt', '--no-encrypt', '--mention-all', '--clear',
2629
2703
  ]);
2630
2704
  function collectPositional(args, startIdx, extraValueFlags) {
2631
2705
  const out = [];
@@ -1,8 +1,34 @@
1
+ import { decodeEvolcoreInlineText } from '../core/permission/ec-command-parser.js';
1
2
  export function normalizeCliArgv(argv) {
2
3
  if (argv[0] === 'ec' && argv[1])
3
4
  return argv.slice(1);
4
5
  return argv;
5
6
  }
7
+ export function decodeEcInlineText(value) {
8
+ return decodeEvolcoreInlineText(value);
9
+ }
10
+ export function decodeEcInlineOptions(args, flags) {
11
+ const decoded = [...args];
12
+ for (let index = 0; index < decoded.length; index++) {
13
+ const flag = decoded[index];
14
+ if (flag === '--')
15
+ break;
16
+ if (flags.has(flag) && decoded[index + 1] !== undefined) {
17
+ decoded[++index] = decodeEcInlineText(decoded[index]);
18
+ continue;
19
+ }
20
+ const separator = flag.indexOf('=');
21
+ if (separator > 0 && flags.has(flag.slice(0, separator))) {
22
+ decoded[index] = flag.slice(0, separator + 1) + decodeEcInlineText(flag.slice(separator + 1));
23
+ }
24
+ }
25
+ return decoded;
26
+ }
27
+ export function assertEcSingleLine(value, field) {
28
+ if (value !== undefined && /[\x00-\x1f\x7f]/.test(value)) {
29
+ throw new Error(`${field} 不允许换行或控制字符`);
30
+ }
31
+ }
6
32
  export function splitConfigBatchGetArgv(inputArgv) {
7
33
  const argv = normalizeCliArgv(inputArgv);
8
34
  if (argv[0] !== 'config' || argv[1] !== 'get')
@@ -291,18 +291,14 @@ async function launchPackageLifecycle(source, log, options = {}) {
291
291
  platform.cleanupMonitorStage(launch.stageDir);
292
292
  return { ok: result.ok, ...(!result.ok ? { reason: result.reason } : {}) };
293
293
  }
294
- function requireCompletedDataMigration(root, action = 'start') {
294
+ function requireCompletedDataMigration(root) {
295
295
  const requirement = inspectDataMigrationRequirement(root);
296
296
  if (!requirement.required)
297
297
  return true;
298
- console.error(action === 'restart'
299
- ? `❌ 检测到 ${requirement.operationCount} 项待确认的用户数据迁移,本次不停止当前服务。`
300
- : `❌ 检测到 ${requirement.operationCount} 项待确认的用户数据迁移,服务未启动。`);
298
+ console.error(`❌ 检测到 ${requirement.operationCount} 项待确认的用户数据迁移,服务未启动。`);
301
299
  console.error(' 先执行: ec data migrate --dry-run');
302
300
  console.error(' 确认后: ec data migrate --apply');
303
- console.error(' 迁移成功并校验后,再执行 ec start 或 ec restart。');
304
- if (action === 'restart')
305
- console.error(' 本次 ec restart 仅完成只读检查,未停止当前服务。');
301
+ console.error(' 迁移成功并校验后,再执行 ec start。');
306
302
  process.exitCode = 2;
307
303
  return false;
308
304
  }
@@ -925,9 +921,8 @@ export async function cmdRestart(opts = {}) {
925
921
  }
926
922
  // The shared launchPackageLifecycle path delegates to launchMonitorAndConfirm;
927
923
  // the monitor retains shutdown, orphan checks, build and rollback ownership.
928
- // Network preflight must finish before a confirmed monitor stops the daemon.
929
- if (!requireCompletedDataMigration(resolveRoot(), 'restart'))
930
- return;
924
+ // restart-monitor performs the read-only preflight before stopping the
925
+ // current daemon, then applies the migration during the stopped window.
931
926
  const launched = await launchPackageLifecycle('restart', console.log, { clear: opts.clear });
932
927
  if (!launched.ok) {
933
928
  console.error(`❌ 重启中止:restart-monitor 未能启动(${launched.reason});当前服务未主动停止`);
@@ -1809,6 +1804,7 @@ async function cmdWatchMenu() {
1809
1804
  { key: 'log', label: 'log', desc: 'real-time log tail' },
1810
1805
  { key: 'aid', label: 'aid', desc: 'AID connection stats' },
1811
1806
  { key: 'msg', label: 'msg', desc: 'message inspector' },
1807
+ { key: 'diag', label: 'diag', desc: 'message lifecycle diagnostics (AUN→queue→task)' },
1812
1808
  { key: 'web', label: 'web', desc: 'browser dashboard (aid/msg/session)' },
1813
1809
  ];
1814
1810
  let index = 0;
@@ -1873,6 +1869,10 @@ async function cmdWatchMenu() {
1873
1869
  const { cmdWatchMsg } = await import('./watch-msg.js');
1874
1870
  await cmdWatchMsg();
1875
1871
  }
1872
+ else if (chosen === 'diag') {
1873
+ const { cmdWatchMsgDiag } = await import('./watch-msg-diag.js');
1874
+ await cmdWatchMsgDiag([]);
1875
+ }
1876
1876
  else if (chosen === 'web') {
1877
1877
  await cmdWatchWeb();
1878
1878
  }
@@ -3295,6 +3295,36 @@ export async function cmdWatchCommand(args) {
3295
3295
  const { cmdWatchMsg } = await import('./watch-msg.js');
3296
3296
  await cmdWatchMsg();
3297
3297
  }
3298
+ else if (args[0] === 'diag' || args[0] === 'msg-diag') {
3299
+ if (isHelpFlag(args[1])) {
3300
+ console.log(`用法: ec watch diag
3301
+
3302
+ 实时诊断消息生命周期,从 AUN SDK 入站到 daemon 处理全过程。
3303
+
3304
+ 显示内容:
3305
+ ✓ AUN入站 消息到达 AUN SDK
3306
+ → 队列入队 消息进入 daemon 队列
3307
+ ⚡ 任务创建 daemon 创建处理任务
3308
+ ✓ 任务完成 任务处理完成
3309
+ ← AUN出站 回复消息发送
3310
+
3311
+ 每条消息显示:
3312
+ - 时间戳(精确到毫秒)
3313
+ - 阶段标识
3314
+ - AID(前12字符)
3315
+ - 消息ID
3316
+ - 内容预览(前50字符)
3317
+
3318
+ 用途:
3319
+ 排查消息丢失、处理延迟、队列阻塞等问题
3320
+
3321
+ 操作:
3322
+ Ctrl+C 退出`);
3323
+ return;
3324
+ }
3325
+ const { cmdWatchMsgDiag } = await import('./watch-msg-diag.js');
3326
+ await cmdWatchMsgDiag(args.slice(1));
3327
+ }
3298
3328
  else if (args[0] === 'log' || args[0] === 'logs') {
3299
3329
  const requested = args.slice(1);
3300
3330
  if (requested.length > 0) {