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
@@ -12,6 +12,7 @@ import { loadDaemonConfig } from '../../config-store.js';
12
12
  import { remoteAccessService } from '../auth/remote-access-store.js';
13
13
  import { read as cfgRead, resolveEffective, routeFieldPath, write as cfgWrite, } from '../../config/config-manager.js';
14
14
  import { execAgentAction } from './agent-control.js';
15
+ import { handleMemorySlash, memoryHelpEntries } from './memory-slash.js';
15
16
  import { authorizeOperation, buildAuthSubject } from '../auth/auth-gateway.js';
16
17
  import { authorizationNextStep, formatAuthorizationDenial } from '../auth/authorization-denial.js';
17
18
  import { resolvePermissionMode, writeScope } from '../model/config-scope.js';
@@ -29,6 +30,7 @@ import { launchMonitorAndConfirm, resolveMonitorLaunch, cleanupMonitorStage } fr
29
30
  import { scanInstances } from '../../utils/instance-registry.js';
30
31
  import { inspectDataMigrationRequirement } from '../data-migration.js';
31
32
  import { guardIdleCommand, guardGroupSlashCommand, guardKnownCommand, guardRoleCommand, guardThreadCommand, isRecognizedSlashCommand, normalizeSlashContent, probeSessionActivity, } from './slash-gate.js';
33
+ import { MAX_AUN_ATTACHMENT_SIZE } from '../../aun/msg/upload.js';
32
34
  const allEfforts = ['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
33
35
  const PERMISSION_MODE_KEYS = PUBLIC_PERMISSION_MODES;
34
36
  function defaultSlashChatmode(field) {
@@ -88,19 +90,33 @@ function resolveSlashMentionModeTarget(params) {
88
90
  ?? this.resolveSelfAID?.(params.channel);
89
91
  if (!self)
90
92
  return { error: '找不到当前 agent,无法读写 mentionMode', code: 'MISSING_AID' };
91
- return { sel: { self }, fieldPath: 'mentionMode' };
93
+ const actualChatType = params.session?.chatType || params.chatType;
94
+ if (actualChatType !== 'group') {
95
+ return { sel: { self }, fieldPath: 'mentionMode', scope: 'agent' };
96
+ }
97
+ const groupId = params.session?.metadata?.groupId || params.channelId;
98
+ if (!groupId)
99
+ return { error: '找不到当前群,无法读写 relation mentionMode', code: 'MISSING_PEER' };
100
+ const channelType = params.session?.channelType
101
+ || this.resolveChannelType?.(params.channel)
102
+ || params.channel.split('#')[0];
103
+ return {
104
+ sel: { self, peerKey: formatPeerKey(channelType, groupId) },
105
+ fieldPath: 'mentionMode',
106
+ scope: 'relation',
107
+ };
92
108
  }
93
109
  function readSlashMentionMode(target, fallback = null) {
94
110
  try {
95
111
  const mode = resolveEffective(target.sel, { cache: true }).mentionMode;
96
- return mode === 'disabled' || mode === 'mention-only' ? mode : fallback;
112
+ return mode === 'disabled' || mode === 'mention-only' || mode === 'auto' ? mode : fallback;
97
113
  }
98
114
  catch {
99
115
  return fallback;
100
116
  }
101
117
  }
102
118
  function writeSlashMentionMode(target, value) {
103
- const route = routeFieldPath(target.fieldPath, 'agent');
119
+ const route = routeFieldPath(target.fieldPath, target.scope);
104
120
  const cur = cfgRead(route.target, target.sel) || {};
105
121
  if (value === null)
106
122
  delete cur.mentionMode;
@@ -637,7 +653,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
637
653
  ' /effort - 查看推理强度',
638
654
  ' /baseagent - 查看 Agent 后端',
639
655
  ' /perm - 查看权限模式',
640
- ' /mentionmode - 查看群聊 @ 处理模式',
656
+ ' /mention - 查看群聊 @ 处理模式',
641
657
  ' /ask <选项> - 回答待处理问题',
642
658
  ' /pause - 在下一次工具调用前暂停任务',
643
659
  ' /resume - 继续已暂停的任务',
@@ -647,7 +663,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
647
663
  ...(threadId ? [' /renew - 轮换当前话题的后端会话'] : []),
648
664
  ] : []),
649
665
  '',
650
- '模型、推理强度、后端、权限与 @ 模式在群聊中仅支持查询。',
666
+ '模型、推理强度、后端与权限在群聊中仅支持查询;@ 模式可由管理员切换。',
651
667
  ];
652
668
  return { kind: 'command.result', text: lines.join('\n') };
653
669
  }
@@ -659,10 +675,8 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
659
675
  const canAnswerPermission = canAccessSlashOperation('permission.answer', 'relation');
660
676
  const canReadChatmode = canAccessSlashOperation('chatmode.current', 'relation');
661
677
  const canSetChatmode = canAccessSlashOperation('chatmode.update', 'relation');
662
- const canReadMentionmode = activeChatType === 'group'
663
- && canAccessSlashOperation('mentionmode.current', 'relation');
664
- const canSetMentionmode = activeChatType === 'group'
665
- && canAccessSlashOperation('mentionmode.update', 'relation');
678
+ const canReadMentionmode = canAccessSlashOperation('mentionmode.current', 'relation');
679
+ const canSetMentionmode = canAccessSlashOperation('mentionmode.update', 'relation');
666
680
  const canUseFile = canAccessSlashOperation('file.fetch', 'filesystem');
667
681
  const appendProcessCommands = (lines) => {
668
682
  if (!isDaemonOwner)
@@ -682,7 +696,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
682
696
  '',
683
697
  '💬 聊天设置:',
684
698
  ...(canReadChatmode || canSetChatmode ? [` /chatmode${canSetChatmode ? ' [interactive|proactive]' : ''} - 查看${canSetChatmode ? '或切换' : ''}会话模式`] : []),
685
- ...(canReadMentionmode || canSetMentionmode ? [` /mentionmode${canSetMentionmode ? ' [mention-only|disabled]' : ''} - 查看${canSetMentionmode ? '或切换' : ''}群聊 @ 处理模式`] : []),
699
+ ...(canReadMentionmode || canSetMentionmode ? [` /mention${canSetMentionmode ? ' [mention-only|disabled]' : ''} - 查看${canSetMentionmode ? '或切换' : ''}群聊 @ 处理模式`] : []),
686
700
  ] : []),
687
701
  ...(canReadPermission || canAnswerPermission ? [
688
702
  '',
@@ -772,7 +786,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
772
786
  '💬 聊天设置:',
773
787
  ' /activity [all|text|none] - 查看/控制中间输出显示模式',
774
788
  ...(canReadChatmode || canSetChatmode ? [` /chatmode${canSetChatmode ? ' [interactive|proactive]' : ''} - 查看${canSetChatmode ? '/切换' : ''}会话模式(被动响应或主动推进)`] : []),
775
- ...(canReadMentionmode || canSetMentionmode ? [` /mentionmode${canSetMentionmode ? ' [mention-only|disabled]' : ''} - 查看${canSetMentionmode ? '/切换' : ''}群聊 @ 处理模式`] : []),
789
+ ...(canReadMentionmode || canSetMentionmode ? [` /mention${canSetMentionmode ? ' [mention-only|disabled]' : ''} - 查看${canSetMentionmode ? '/切换' : ''}群聊 @ 处理模式`] : []),
776
790
  '',
777
791
  '🔐 权限管理:',
778
792
  ...(canReadPermission ? [' /perm - 查看当前权限模式'] : []),
@@ -792,6 +806,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
792
806
  ' /restart - 重启服务',
793
807
  ' /reload [aid] - 热重载 Agent 配置',
794
808
  ' /fa <提示词> - 单次使用宿主级 fullaccess 执行(别名: /fullaccess)',
809
+ ' /pair - 获取远程控制配对码',
795
810
  ] : []),
796
811
  ...(!isDaemonOwner && isAdmin ? [
797
812
  ' /reload - 热重载当前 Agent 配置',
@@ -835,6 +850,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
835
850
  // /evolhelp 命令:返回 JSON 格式的命令列表(供程序解析)
836
851
  if (normalizedContent === '/evolhelp') {
837
852
  if (groupSlash) {
853
+ const canSetMentionmode = canAccessSlashOperation('mentionmode.update', 'relation');
838
854
  const commands = [
839
855
  { command: '/help', description: '显示帮助信息', category: '帮助', roles: ['visitor', 'member', 'admin', 'owner'] },
840
856
  { command: '/status', description: '显示会话状态', category: '运维', roles: ['visitor', 'member', 'admin', 'owner'] },
@@ -843,7 +859,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
843
859
  { command: '/effort', description: '查看推理强度', category: 'Agent 与模型', roles: [identity.role] },
844
860
  { command: '/baseagent', description: '查看 Agent 后端', category: 'Agent 与模型', roles: [identity.role] },
845
861
  { command: '/perm', description: '查看当前权限模式', category: '权限管理', roles: [identity.role] },
846
- { command: '/mentionmode', description: '查看群聊 @ 处理模式', category: '聊天设置', roles: [identity.role] },
862
+ { command: '/mention', args: canSetMentionmode ? '[mention-only|disabled|auto|clear]' : undefined, description: canSetMentionmode ? '查看或切换群聊 @ 处理模式' : '查看群聊 @ 处理模式', category: '聊天设置', roles: [identity.role] },
847
863
  { command: '/ask', args: '<选项>', description: '回答 Agent 的交互式问题', category: '运维', roles: ['visitor', 'member', 'admin', 'owner'] },
848
864
  { command: '/pause', description: '在下一次工具调用前暂停当前任务', category: '运维', roles: ['admin', 'owner'] },
849
865
  { command: '/resume', description: '继续已暂停的当前任务', category: '运维', roles: ['admin', 'owner'] },
@@ -852,6 +868,10 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
852
868
  if (isAdmin) {
853
869
  commands.push({ command: '/compact', description: '压缩会话上下文', category: '会话管理', roles: ['admin', 'owner'] }, ...(threadId ? [{ command: '/renew', description: '轮换当前话题 backend', category: '会话管理', roles: ['admin', 'owner'] }] : []));
854
870
  }
871
+ // 记忆管理紧跟权限管理
872
+ const groupMemoryEntries = memoryHelpEntries({ chatType: 'group', role: identity.role, isAdmin, isOwnerSelf: false });
873
+ const lastPermIdx = commands.map(c => c.category).lastIndexOf('权限管理');
874
+ commands.splice(lastPermIdx >= 0 ? lastPermIdx + 1 : commands.length, 0, ...groupMemoryEntries);
855
875
  return { kind: 'command.result', text: JSON.stringify({ commands, categories: [...new Set(commands.map(c => c.category))] }) };
856
876
  }
857
877
  const cmds = [];
@@ -863,10 +883,8 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
863
883
  const canAnswerPermission = canAccessSlashOperation('permission.answer', 'relation');
864
884
  const canReadChatmode = canAccessSlashOperation('chatmode.current', 'relation');
865
885
  const canSetChatmode = canAccessSlashOperation('chatmode.update', 'relation');
866
- const canReadMentionmode = activeChatType === 'group'
867
- && canAccessSlashOperation('mentionmode.current', 'relation');
868
- const canSetMentionmode = activeChatType === 'group'
869
- && canAccessSlashOperation('mentionmode.update', 'relation');
886
+ const canReadMentionmode = canAccessSlashOperation('mentionmode.current', 'relation');
887
+ const canSetMentionmode = canAccessSlashOperation('mentionmode.update', 'relation');
870
888
  const canUseFile = canAccessSlashOperation('file.fetch', 'filesystem');
871
889
  // 项目
872
890
  cmds.push({ command: '/pwd', description: '显示当前项目路径', category: '项目', roles: ['admin', 'owner'] });
@@ -895,6 +913,14 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
895
913
  cmds.push({ command: '/perm', description: '查看当前角色决定的权限模式', category: '权限管理', roles: [identity.role] });
896
914
  if (canAnswerPermission)
897
915
  cmds.push({ command: '/perm', args: 'allow|always|deny', description: '审批权限请求', category: '权限管理', roles: [identity.role] });
916
+ // 记忆管理(紧跟权限管理):能一键执行的都展开成无参数条目
917
+ const memoryOwnerForHelp = this.agentRegistry?.get?.(authSubject.selfAid ?? '')?.config?.owners?.[0];
918
+ cmds.push(...memoryHelpEntries({
919
+ chatType: 'private',
920
+ role: identity.role,
921
+ isAdmin,
922
+ isOwnerSelf: isOwner && !!memoryOwnerForHelp && (authSubject.principalId ?? userId) === memoryOwnerForHelp,
923
+ }));
898
924
  // 运维
899
925
  cmds.push({ command: '/status', description: '显示会话状态', category: '运维', roles: ['visitor', 'member', 'admin', 'owner'] });
900
926
  cmds.push({ command: '/pause', description: '在下一次工具调用前暂停当前任务', category: '运维', roles: ['admin', 'owner'] });
@@ -924,7 +950,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
924
950
  if (canReadChatmode || canSetChatmode)
925
951
  cmds.push({ command: '/chatmode', ...(canSetChatmode ? { args: '[interactive|proactive]' } : {}), description: `查看${canSetChatmode ? '/切换' : ''}会话模式(被动响应或主动推进)`, category: '聊天设置', roles: [identity.role] });
926
952
  if (canReadMentionmode || canSetMentionmode)
927
- cmds.push({ command: '/mentionmode', ...(canSetMentionmode ? { args: '[mention-only|disabled]' } : {}), description: `查看${canSetMentionmode ? '/切换' : ''}群聊 @ 处理模式`, category: '聊天设置', roles: [identity.role] });
953
+ cmds.push({ command: '/mention', ...(canSetMentionmode ? { args: '[mention-only|disabled]' } : {}), description: `查看${canSetMentionmode ? '/切换' : ''}群聊 @ 处理模式`, category: '聊天设置', roles: [identity.role] });
928
954
  // 交互
929
955
  cmds.push({ command: '/ask', args: '<选项>', description: '回答 Agent 的交互式问题', category: '运维', roles: ['visitor', 'member', 'admin', 'owner'] });
930
956
  // 帮助
@@ -1242,9 +1268,34 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
1242
1268
  }
1243
1269
  }
1244
1270
  this.eventBus.publish({ type: 'agent:baseagent-changed', aid: owningAgent.aid, baseagent: args, previousBaseagent, scope: 'agent', timestamp: Date.now() });
1271
+ // 与 /new 一致:按 关系>角色>agent 解析新 backend 实际生效的 model/effort,
1272
+ // 而不是只显示 baseagent 名。
1273
+ const newBaseagentRunner = this.getAgent(channel, newSession.baseagent);
1274
+ const newModelTarget = resolveSlashRelationTarget.call(this, {
1275
+ session: newSession,
1276
+ channel,
1277
+ channelId,
1278
+ userId,
1279
+ selfAID,
1280
+ role: newSession.identity?.role || identity.role,
1281
+ chatType,
1282
+ });
1283
+ const newModelSelector = 'error' in newModelTarget
1284
+ ? { self: selfAID ?? newSession.selfAID ?? this.resolveSelfAID(channel) }
1285
+ : newModelTarget;
1286
+ let sessionModelConfig = {};
1287
+ try {
1288
+ sessionModelConfig = (resolveEffective(newModelSelector, { cache: true }).baseagents || {})[args] || {};
1289
+ }
1290
+ catch { }
1291
+ const sessionBackendModel = sessionModelConfig.model || newBaseagentRunner?.getModel?.() || owningAgent?.model;
1292
+ const sessionBackendEffort = sessionModelConfig.effort
1293
+ ?? newBaseagentRunner?.getEffort?.()
1294
+ ?? owningAgent?.effort;
1295
+ const sessionBackendBits = [args, sessionBackendModel, sessionBackendEffort].filter(Boolean).join(' · ');
1245
1296
  return {
1246
1297
  kind: 'system.notice',
1247
- text: `✓ 已切换 Baseagent: ${previousBaseagent} → ${args}\n 已创建新的主会话: ${newSession.id}\n 之前的对话历史已保留,可通过 /s 查看`,
1298
+ text: `✓ 已切换新后端 : ${sessionBackendBits}`,
1248
1299
  subtype: 'session.created',
1249
1300
  };
1250
1301
  }
@@ -2156,51 +2207,54 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
2156
2207
  return null;
2157
2208
  return { kind: 'command.result', text: `✅ 会话模式已切换: ${arg}` };
2158
2209
  }
2159
- // /mentionmode 命令:查看/切换群聊 @ 处理模式(mention-only | disabled)
2160
- // 仅群聊可用;群聊中设置需管理员权限
2161
- if (normalizedContent === '/mentionmode' || normalizedContent.startsWith('/mentionmode ')) {
2210
+ // /mention 命令:查看/切换群聊 @ 处理模式(mention-only | disabled | auto)
2211
+ // 写入层级按会话类型自动选择:群聊 → relation 级(当前群),私聊 → agent 级(Agent 全局默认)
2212
+ if (normalizedContent === '/mention' || normalizedContent.startsWith('/mention ')) {
2162
2213
  const mentionSession = await getExistingSessionForCommand();
2163
2214
  if (!mentionSession)
2164
2215
  return { kind: 'command.error', text: '❌ 当前没有活跃会话' };
2165
2216
  const mentionChatType = mentionSession.chatType || activeChatType;
2166
- if (mentionChatType !== 'group') {
2167
- return { kind: 'command.error', text: '❌ /mentionmode 仅在群聊中可用' };
2168
- }
2169
- const arg = normalizedContent.slice('/mentionmode'.length).trim();
2217
+ const isGroupChat = mentionChatType === 'group';
2218
+ const commandName = '/mention';
2219
+ const arg = normalizedContent.slice(commandName.length).trim();
2170
2220
  const mentionTarget = resolveSlashMentionModeTarget.call(this, {
2171
2221
  session: mentionSession,
2172
2222
  channel,
2223
+ channelId,
2173
2224
  selfAID,
2174
2225
  role: mentionSession.identity?.role || identity.role,
2226
+ chatType: mentionChatType,
2175
2227
  });
2176
2228
  if ('error' in mentionTarget) {
2177
2229
  return { kind: 'command.error', text: `❌ ${mentionTarget.error}` };
2178
2230
  }
2179
2231
  const mentionFallback = mentionSession.metadata?.mentionMode ?? null;
2180
2232
  const currentMode = readSlashMentionMode(mentionTarget, mentionFallback);
2233
+ const scopeLabel = mentionTarget.scope === 'relation' ? '本群' : 'Agent 全局';
2181
2234
  if (!arg) {
2182
- const displayMode = currentMode ?? '未设置(跟随群设置)';
2235
+ const displayMode = currentMode ?? '未设置(跟随上级设置)';
2183
2236
  if (groupSlash)
2184
- return { kind: 'command.result', text: `@ 处理模式: ${displayMode}` };
2237
+ return { kind: 'command.result', text: `@ 处理模式(${scopeLabel}): ${displayMode}` };
2185
2238
  // 尝试发送 CommandCard 卡片
2186
2239
  if (isAdmin) {
2187
2240
  const modes = [
2188
2241
  { key: 'mention-only', name: '提及模式', desc: '仅当被 @ 提及(含 @all)时响应群消息' },
2189
2242
  { key: 'disabled', name: '全响应模式', desc: '群内所有消息都触发响应' },
2243
+ { key: 'auto', name: '自动模式', desc: '根据群上下文判断是否需要响应' },
2190
2244
  ];
2191
2245
  const interaction = {
2192
2246
  type: 'interaction',
2193
- id: `mentionmode-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`,
2247
+ id: `mention-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`,
2194
2248
  channelId,
2195
2249
  sessionId: mentionSession.id,
2196
2250
  initiatorId: userId,
2197
2251
  kind: {
2198
2252
  kind: 'command-card',
2199
- title: '📡 @ 处理模式',
2253
+ title: `📡 @ 处理模式(${scopeLabel})`,
2200
2254
  body: modes.map(m => `${m.key === currentMode ? '✓' : '•'} **${m.key}** (${m.name}) - ${m.desc}`).join('\n'),
2201
2255
  buttons: modes.map(m => ({
2202
2256
  label: m.key === currentMode ? `✓ ${m.key}` : m.key,
2203
- command: `/mentionmode ${m.key}`,
2257
+ command: `/mention ${m.key}`,
2204
2258
  style: (m.key === currentMode ? 'primary' : 'default'),
2205
2259
  disabled: m.key === currentMode,
2206
2260
  })),
@@ -2214,23 +2268,23 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
2214
2268
  }
2215
2269
  // 降级:文本
2216
2270
  if (isAdmin) {
2217
- return { kind: 'command.result', text: `@ 处理模式: ${displayMode} 用法: /mentionmode <mention-only|disabled|clear>` };
2271
+ return { kind: 'command.result', text: `@ 处理模式(${scopeLabel}): ${displayMode} 用法: /mention <mention-only|disabled|auto|clear>` };
2218
2272
  }
2219
- return { kind: 'command.result', text: `@ 处理模式: ${displayMode}` };
2273
+ return { kind: 'command.result', text: `@ 处理模式(${scopeLabel}): ${displayMode}` };
2220
2274
  }
2221
- if (arg !== 'mention-only' && arg !== 'disabled' && arg !== 'clear') {
2222
- return { kind: 'command.error', text: `❌ 无效模式: ${arg}\n可选: mention-only / disabled / clear\n用法: /mentionmode <模式>` };
2275
+ if (arg !== 'mention-only' && arg !== 'disabled' && arg !== 'auto' && arg !== 'clear') {
2276
+ return { kind: 'command.error', text: `❌ 无效模式: ${arg}\n可选: mention-only / disabled / auto / clear\n用法: /mention <模式>` };
2223
2277
  }
2224
2278
  const mentionAuthDenied = await authorizeIntent({
2225
2279
  intent: {
2226
2280
  operation: 'mentionmode.update',
2227
- scope: 'agent',
2281
+ scope: mentionTarget.scope,
2228
2282
  source: 'slash',
2229
2283
  args: { value: arg, self: mentionTarget.sel.self },
2230
2284
  },
2231
2285
  identity,
2232
2286
  session: mentionSession,
2233
- explicitChatType: 'group',
2287
+ explicitChatType: mentionChatType,
2234
2288
  channel,
2235
2289
  channelId,
2236
2290
  userId,
@@ -2243,7 +2297,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
2243
2297
  writeSlashMentionMode(mentionTarget, null);
2244
2298
  if (this.shouldSuppressCardTriggerResult(source, channel))
2245
2299
  return null;
2246
- return { kind: 'command.result', text: '✅ 已清除本地覆盖,将跟随群设置' };
2300
+ return { kind: 'command.result', text: `✅ 已清除${scopeLabel}覆盖,将跟随上级设置` };
2247
2301
  }
2248
2302
  if (arg === currentMode) {
2249
2303
  return { kind: 'command.result', text: `当前已是 ${arg}` };
@@ -2251,7 +2305,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
2251
2305
  writeSlashMentionMode(mentionTarget, arg);
2252
2306
  if (this.shouldSuppressCardTriggerResult(source, channel))
2253
2307
  return null;
2254
- return { kind: 'command.result', text: `✅ @ 处理模式已切换: ${currentMode ?? '未设置'} → ${arg}` };
2308
+ return { kind: 'command.result', text: `✅ @ 处理模式(${scopeLabel})已切换: ${currentMode ?? '未设置'} → ${arg}` };
2255
2309
  }
2256
2310
  // /stop 命令:中断当前任务
2257
2311
  if (normalizedContent.startsWith('/stop ')) {
@@ -2570,22 +2624,27 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
2570
2624
  const mentionModeTarget = resolveSlashMentionModeTarget.call(this, {
2571
2625
  session,
2572
2626
  channel,
2627
+ channelId,
2573
2628
  selfAID,
2574
2629
  role: sessionRole,
2630
+ chatType: session.chatType,
2575
2631
  });
2576
2632
  const mentionModeFallback = session.metadata?.mentionMode ?? null;
2577
2633
  const mentionMode = 'error' in mentionModeTarget
2578
- ? (mentionModeFallback ?? '未设置(跟随群设置)')
2579
- : (readSlashMentionMode(mentionModeTarget, mentionModeFallback) ?? '未设置(跟随群设置)');
2634
+ ? (mentionModeFallback ?? '未设置')
2635
+ : (readSlashMentionMode(mentionModeTarget, mentionModeFallback) ?? '未设置');
2636
+ const mentionScopeLabel = 'error' in mentionModeTarget
2637
+ ? '本群'
2638
+ : mentionModeTarget.scope === 'relation' ? '本群' : 'Agent 全局';
2580
2639
  const chatModeLine = `会话模式: ${chatMode}`;
2581
- const mentionModeLine = session.chatType === 'group' ? `@ 处理模式: ${mentionMode}` : null;
2640
+ const mentionModeLine = `@ 处理模式(${mentionScopeLabel}): ${mentionMode}`;
2582
2641
  if (isAdmin) {
2583
2642
  const gitInfo = await getGitWorkingDirInfo(session.projectPath);
2584
2643
  lines.push(`📊 ${isThread ? '话题' : '会话'}状态 (Agent: ${agentName}):`, `渠道: ${this.resolveChannelType(channel)} / 项目: ${projectName} / 会话: ${displaySessionTitle(session.name, '(未命名)')}`, `会话ID: ${session.id}`, `项目路径: ${session.projectPath}`);
2585
2644
  if (gitInfo) {
2586
2645
  lines.push(`Git: ${gitInfo}`);
2587
2646
  }
2588
- lines.push(`会话状态: ${sessionStatus} / 轮数: ${sessionTurns} / 已记录子任务数: ${sessionSubtasks}`, sessionRoleLine, chatModeLine, ...(mentionModeLine ? [mentionModeLine] : []));
2647
+ lines.push(`会话状态: ${sessionStatus} / 轮数: ${sessionTurns} / 已记录子任务数: ${sessionSubtasks}`, sessionRoleLine, chatModeLine, mentionModeLine);
2589
2648
  if (health.consecutiveErrors > 0) {
2590
2649
  lines.push(`异常计数: ${health.consecutiveErrors}`);
2591
2650
  }
@@ -2593,7 +2652,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
2593
2652
  }
2594
2653
  else {
2595
2654
  lines.push(`📊 ${isThread ? '话题' : '会话'}状态 (Agent: ${agentName}):`, `渠道: ${channel} / 项目: ${projectName} / ${session.baseagent}会话`);
2596
- lines.push(`状态: ${sessionStatus} / 轮数: ${sessionTurns} / 已记录子任务数: ${sessionSubtasks}`, sessionRoleLine, chatModeLine, ...(mentionModeLine ? [mentionModeLine] : []), `最后活跃: ${timeStr}`);
2655
+ lines.push(`状态: ${sessionStatus} / 轮数: ${sessionTurns} / 已记录子任务数: ${sessionSubtasks}`, sessionRoleLine, chatModeLine, mentionModeLine, `最后活跃: ${timeStr}`);
2597
2656
  }
2598
2657
  if (health.lastError) {
2599
2658
  lines.push('');
@@ -2614,11 +2673,17 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
2614
2673
  await interruptPausedSessionBeforeReplacement(session || activeSession);
2615
2674
  const projectPath = this.getEffectiveDefaultPath(channel);
2616
2675
  if (sendMessage && session) {
2617
- await sendMessage(channelId, `⏳ 正在创建新会话${sessionName ? `: ${sessionName}` : ''}...`, this.getReplyContext(session), {
2618
- kind: 'system.notice',
2619
- text: `⏳ 正在创建新会话${sessionName ? `: ${sessionName}` : ''}...`,
2620
- subtype: 'session.creating',
2621
- });
2676
+ const creatingText = `⏳ 正在创建新会话${sessionName ? `: ${sessionName}` : ''}...`;
2677
+ // 私聊改回普通文本:AUN 私聊里 system.notice 会渲染成系统通知样式,
2678
+ // 这里希望它像 /compact 等命令的占位提示一样,按普通消息发送。
2679
+ const creatingPayload = activeChatType === 'private'
2680
+ ? { kind: 'command.result', text: creatingText }
2681
+ : {
2682
+ kind: 'system.notice',
2683
+ text: creatingText,
2684
+ subtype: 'session.creating',
2685
+ };
2686
+ await sendMessage(channelId, creatingText, this.getReplyContext(session), creatingPayload);
2622
2687
  }
2623
2688
  const newSessionBaseagent = this.agentRegistry?.resolveByChannel(channel)?.baseagent || this.parseDefaultBaseagent();
2624
2689
  const previousMetadata = session?.metadata || activeSession?.metadata || {};
@@ -2635,7 +2700,22 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
2635
2700
  ...(previousMetadata.peerName ? { peerName: previousMetadata.peerName } : {}),
2636
2701
  ...(previousMetadata.peerType ? { peerType: previousMetadata.peerType } : {}),
2637
2702
  };
2638
- const newSession = await this.sessionManager.createNewSession(channel, channelId, projectPath, sessionName, newSessionBaseagent, newSessionIdentityMetadata);
2703
+ // 源会话快照(执行 /new 的那个会话):供记忆整理触发用,
2704
+ // 不依赖旧会话是否还有活动 taskCtx(/new 后旧会话任务可能已结束)。
2705
+ const sourceForConsolidation = session || activeSession;
2706
+ const newSession = await this.sessionManager.createNewSession(channel, channelId, projectPath, sessionName, newSessionBaseagent, newSessionIdentityMetadata, {
2707
+ sourceSessionId: sourceForConsolidation?.id,
2708
+ sourceChannel: sourceForConsolidation?.channelType || channel,
2709
+ sourceChannelId: channelId,
2710
+ sourceChatType: activeChatType,
2711
+ sourceThreadId: sourceForConsolidation?.threadId || undefined,
2712
+ sourceSelfAid: sourceForConsolidation?.selfAID
2713
+ || selfAID
2714
+ || this.getOwningAgent(channel)?.aid
2715
+ || this.resolveSelfAID(channel)
2716
+ || undefined,
2717
+ sourcePeerId: sourceForConsolidation?.metadata?.peerId || userId || undefined,
2718
+ });
2639
2719
  const previousSession = session || activeSession;
2640
2720
  const handoffSelfAid = previousSession?.selfAID
2641
2721
  || selfAID
@@ -2689,7 +2769,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
2689
2769
  const backendBits = [newBaseagent, backendModel, backendEffort].filter(Boolean).join(' · ');
2690
2770
  return {
2691
2771
  kind: 'system.notice',
2692
- text: `✓ 已创建新会话${sessionName ? `: ${sessionName}` : ''}\n 项目: ${this.getProjectName(projectPath)}\n 后端: ${backendBits}\n 之前的对话历史已保留,可通过 /s 查看${handoffDiscardWarning ? `\n${handoffDiscardWarning}` : ''}`,
2772
+ text: `✓ 已创建新会话${sessionName ? `: ${sessionName}` : ''} : ${backendBits}${handoffDiscardWarning ? `\n${handoffDiscardWarning}` : ''}`,
2693
2773
  subtype: 'session.created',
2694
2774
  };
2695
2775
  }
@@ -3268,9 +3348,9 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
3268
3348
  if (stat.isDirectory()) {
3269
3349
  return { kind: 'command.error', text: '❌ 暂不支持发送目录\n目录打包发送将在后续版本支持' };
3270
3350
  }
3271
- const MAX_SIZE = 10 * 1024 * 1024;
3351
+ const MAX_SIZE = MAX_AUN_ATTACHMENT_SIZE;
3272
3352
  if (stat.size > MAX_SIZE) {
3273
- return { kind: 'command.error', text: `❌ 文件过大: ${(stat.size / 1024 / 1024).toFixed(1)} MB (限制 10 MB)` };
3353
+ return { kind: 'command.error', text: `❌ 文件过大: ${(stat.size / 1024 / 1024).toFixed(1)} MB (限制 ${MAX_SIZE / 1024 / 1024} MB)` };
3274
3354
  }
3275
3355
  // 找目标 channelId
3276
3356
  let targetChannelId = channelId;
@@ -3695,23 +3775,23 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
3695
3775
  }
3696
3776
  }
3697
3777
  // /rewind 命令:查看历史 / 回退会话
3698
- if (normalizedContent === '/rewind' || normalizedContent.startsWith('/rewind ')) {
3778
+ if (/^\/rewind(?:\s|$)/.test(normalizedContent)) {
3699
3779
  const session = await getExistingSessionForCommand();
3700
3780
  if (!session)
3701
3781
  return { kind: 'command.error', text: '❌ 当前没有活跃会话' };
3702
- const rewindBaseagent = session.baseagent;
3703
- if (!rewindBaseagent) {
3704
- return { kind: 'command.error', text: '❌ 当前会话缺少 baseagent 绑定,拒绝隐式切换' };
3705
- }
3706
- const rewindAgent = this.getAgent(channel, rewindBaseagent);
3707
- if (!session.agentSessionId) {
3708
- return { kind: 'command.error', text: '❌ 当前会话无历史记录\n\n请先发送一条消息,然后再使用 /rewind' };
3709
- }
3710
- if (!rewindAgent.getSessionMessages) {
3711
- return { kind: 'command.error', text: `❌ 当前 Agent (${rewindAgent.name}) 不支持 /rewind` };
3712
- }
3713
3782
  const args = normalizedContent.slice('/rewind'.length).trim();
3714
3783
  if (!args) {
3784
+ const rewindBaseagent = session.baseagent;
3785
+ if (!rewindBaseagent) {
3786
+ return { kind: 'command.error', text: '❌ 当前会话缺少 baseagent 绑定,拒绝隐式切换' };
3787
+ }
3788
+ const rewindAgent = this.getAgent(channel, rewindBaseagent);
3789
+ if (!session.agentSessionId) {
3790
+ return { kind: 'command.error', text: '❌ 当前会话无历史记录\n\n请先发送一条消息,然后再使用 /rewind' };
3791
+ }
3792
+ if (!rewindAgent.getSessionMessages) {
3793
+ return { kind: 'command.error', text: `❌ 当前 Agent (${rewindAgent.name}) 不支持 /rewind` };
3794
+ }
3715
3795
  return { kind: 'command.result', text: await this.handleRewindList(session, rewindAgent) };
3716
3796
  }
3717
3797
  // 带参(执行回退,会删除文件/改对话)需 admin+
@@ -3729,6 +3809,46 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
3729
3809
  if (!['chat', 'file', 'all'].includes(mode)) {
3730
3810
  return { kind: 'command.error', text: `❌ 无效模式 "${mode}",可选:chat | file | all` };
3731
3811
  }
3812
+ if (session.threadId) {
3813
+ // 话题只开放 N>1;N=1 会清空 backend binding,仍保留原有禁止边界。
3814
+ if (turnNum === 1) {
3815
+ return { kind: 'command.error', text: '❌ 话题会话暂不支持 /rewind 1,仅开放 N>1' };
3816
+ }
3817
+ const outcome = await withIdleTopicBackend(session.id, session.threadId, async (current) => {
3818
+ if (!current.baseagent) {
3819
+ return '❌ 当前会话缺少 baseagent 绑定,拒绝隐式切换';
3820
+ }
3821
+ if (!current.agentSessionId) {
3822
+ return '❌ 当前会话无历史记录\n\n请先发送一条消息,然后再使用 /rewind';
3823
+ }
3824
+ const currentAgent = this.getAgent(channel, current.baseagent);
3825
+ if (!currentAgent.getSessionMessages) {
3826
+ return `❌ 当前 Agent (${currentAgent.name}) 不支持 /rewind`;
3827
+ }
3828
+ return this.handleRewind(current, currentAgent, turnNum, mode);
3829
+ }, session);
3830
+ if (!outcome.ok) {
3831
+ if (outcome.reason === 'busy') {
3832
+ return { kind: 'command.error', text: '⚠️ 当前话题会话忙碌,无法回退,请稍后再试\n使用 /stop 中断当前任务后重试' };
3833
+ }
3834
+ if (outcome.reason === 'unknown') {
3835
+ return { kind: 'command.error', text: '⚠️ 当前话题会话状态未知,无法安全回退,请稍后再试' };
3836
+ }
3837
+ return { kind: 'command.error', text: '❌ 当前话题会话不存在或归属已变化' };
3838
+ }
3839
+ return { kind: 'command.result', text: outcome.value };
3840
+ }
3841
+ const rewindBaseagent = session.baseagent;
3842
+ if (!rewindBaseagent) {
3843
+ return { kind: 'command.error', text: '❌ 当前会话缺少 baseagent 绑定,拒绝隐式切换' };
3844
+ }
3845
+ const rewindAgent = this.getAgent(channel, rewindBaseagent);
3846
+ if (!session.agentSessionId) {
3847
+ return { kind: 'command.error', text: '❌ 当前会话无历史记录\n\n请先发送一条消息,然后再使用 /rewind' };
3848
+ }
3849
+ if (!rewindAgent.getSessionMessages) {
3850
+ return { kind: 'command.error', text: `❌ 当前 Agent (${rewindAgent.name}) 不支持 /rewind` };
3851
+ }
3732
3852
  await interruptPausedSessionBeforeReplacement(session);
3733
3853
  return { kind: 'command.result', text: await this.handleRewind(session, rewindAgent, turnNum, mode) };
3734
3854
  }
@@ -3780,6 +3900,28 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
3780
3900
  }
3781
3901
  }
3782
3902
  // /trigger 命令
3903
+ if (normalizedContent === '/memory' || normalizedContent.startsWith('/memory ')) {
3904
+ const memoryAgent = this.agentRegistry?.get?.(authSubject.selfAid ?? '');
3905
+ const memoryOwnerAid = memoryAgent?.config?.owners?.[0];
3906
+ const memorySession = await getExistingSessionForCommand();
3907
+ return await handleMemorySlash({
3908
+ content: normalizedContent,
3909
+ selfAid: authSubject.selfAid,
3910
+ peerKey: authSubject.peerKey,
3911
+ chatType: activeChatType === 'group' ? 'group' : 'private',
3912
+ role: identity.role,
3913
+ isAdmin,
3914
+ isOwnerSelf: isOwner && !!memoryOwnerAid && (authSubject.principalId ?? userId) === memoryOwnerAid,
3915
+ ownerAid: memoryOwnerAid,
3916
+ memoryConfig: memoryAgent?.config?.memory,
3917
+ channelType: authSubject.channelType,
3918
+ peerId: activeChatType === 'group' ? channelId : (userId ?? channelId),
3919
+ threadId,
3920
+ sessionId: memorySession?.id,
3921
+ baseagent: memorySession?.baseagent,
3922
+ launcher: this.memoryConsolidationLauncher,
3923
+ });
3924
+ }
3783
3925
  if (normalizedContent === '/trigger' || normalizedContent.startsWith('/trigger ')) {
3784
3926
  const triggerAuthDenied = await authorizeIntent({
3785
3927
  intent: {