evolcore 0.0.26 → 0.0.28

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 (177) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/bin/codex-managed-acl.ps1 +43 -0
  3. package/dist/agents/baseagent-availability.js +68 -0
  4. package/dist/agents/baseagent.js +10 -5
  5. package/dist/agents/claude-runner.js +123 -22
  6. package/dist/agents/codex/app-server-client.js +70 -26
  7. package/dist/agents/codex/catalog.js +7 -1
  8. package/dist/agents/codex-runner.js +363 -421
  9. package/dist/agents/ecagent-runner.js +170 -25
  10. package/dist/agents/gemini-runner.js +6 -0
  11. package/dist/agents/runner-types.js +50 -2
  12. package/dist/aun/aid/agentmd.js +73 -31
  13. package/dist/aun/aid/client.js +1 -1
  14. package/dist/aun/aid/control-aid.js +180 -1
  15. package/dist/aun/aid/identity.js +3 -3
  16. package/dist/aun/group/history.js +303 -0
  17. package/dist/aun/group/identity.js +43 -0
  18. package/dist/aun/group/index.js +5 -0
  19. package/dist/aun/{msg/group.js → group/operations.js} +161 -38
  20. package/dist/aun/{menu-client.js → menu/client.js} +13 -7
  21. package/dist/aun/menu/index.js +2 -0
  22. package/dist/aun/menu/transport.js +49 -0
  23. package/dist/aun/msg/index.js +2 -1
  24. package/dist/aun/msg/managed-operation.js +84 -18
  25. package/dist/aun/msg/mention-schema.js +51 -0
  26. package/dist/aun/msg/p2p.js +13 -8
  27. package/dist/aun/msg/upload.js +43 -88
  28. package/dist/aun/outbox.js +29 -7
  29. package/dist/aun/rpc/client.js +1 -0
  30. package/dist/aun/storage/client.js +39 -50
  31. package/dist/channels/aun.js +544 -239
  32. package/dist/channels/daemon.js +10 -6
  33. package/dist/cli/agent-command.js +108 -6
  34. package/dist/cli/agent-create.js +515 -2
  35. package/dist/cli/agent.js +109 -8
  36. package/dist/cli/aun-commands.js +191 -42
  37. package/dist/cli/config.js +8 -5
  38. package/dist/cli/contact.js +2 -2
  39. package/dist/cli/ctl-command.js +2 -2
  40. package/dist/cli/daemon-commands.js +111 -162
  41. package/dist/cli/diag.js +3 -3
  42. package/dist/cli/fs-command.js +3 -3
  43. package/dist/cli/handoff-command.js +6 -6
  44. package/dist/cli/index.js +8 -2
  45. package/dist/cli/init.js +34 -1
  46. package/dist/cli/managed-command-guard.js +2 -1
  47. package/dist/cli/menu-client.js +13 -5
  48. package/dist/cli/model.js +3 -3
  49. package/dist/cli/queue-command.js +2 -2
  50. package/dist/cli/response.js +2 -2
  51. package/dist/cli/restart-monitor.js +81 -19
  52. package/dist/cli/task-command.js +59 -0
  53. package/dist/cli/task-context.js +97 -49
  54. package/dist/cli/trigger-command.js +20 -10
  55. package/dist/config/builtin-roles.js +10 -0
  56. package/dist/config/config-field-policy.js +11 -0
  57. package/dist/config/config-manager.js +32 -4
  58. package/dist/config/config-operation-service.js +20 -5
  59. package/dist/config/contact-book-store.js +1 -1
  60. package/dist/config/contact-book.js +1 -1
  61. package/dist/config/contact-operation-service.js +1 -1
  62. package/dist/config/peer-role-resolver.js +1 -1
  63. package/dist/config/resolved-config-op.js +3 -0
  64. package/dist/config-store.js +1 -1
  65. package/dist/core/agent-application-service.js +5 -0
  66. package/dist/core/audit/event-key.js +17 -2
  67. package/dist/core/audit/log-integrity.js +34 -8
  68. package/dist/core/auth/agent-delegation.js +48 -14
  69. package/dist/core/auth/auth-gateway.js +2 -0
  70. package/dist/core/auth/authorization-audit.js +34 -38
  71. package/dist/core/auth/operation-authorizer.js +67 -5
  72. package/dist/core/auth/operation-catalog.js +110 -0
  73. package/dist/core/auth/remote-access-store.js +115 -9
  74. package/dist/core/bootstrap-messages.js +66 -7
  75. package/dist/core/bootstrap-service.js +110 -19
  76. package/dist/core/command/agent-control.js +88 -7
  77. package/dist/core/command/agent-storage-menu.js +443 -0
  78. package/dist/core/command/cli-intent-parser.js +19 -0
  79. package/dist/core/command/command-handler.js +105 -40
  80. package/dist/core/command/data-root-resolver.js +273 -0
  81. package/dist/core/command/diag-service.js +292 -27
  82. package/dist/core/command/group-menu.js +7 -2
  83. package/dist/core/command/menu-catalog.js +34 -4
  84. package/dist/core/command/menu-handler.js +364 -96
  85. package/dist/core/command/menu-protocol.js +43 -6
  86. package/dist/core/command/role-menu.js +2 -2
  87. package/dist/core/command/slash-handler.js +61 -37
  88. package/dist/core/event-catalog.js +4 -0
  89. package/dist/core/evolagent-registry.js +28 -2
  90. package/dist/core/group/invite-service.js +924 -0
  91. package/dist/core/interaction-router.js +22 -8
  92. package/dist/core/message/im-renderer.js +94 -16
  93. package/dist/core/message/message-bridge.js +140 -13
  94. package/dist/core/message/message-log.js +5 -3
  95. package/dist/core/message/message-queue.js +73 -41
  96. package/dist/core/message/response-engine.js +729 -118
  97. package/dist/core/permission/approval-gateway.js +36 -3
  98. package/dist/core/permission/ec-command-parser.js +76 -36
  99. package/dist/core/permission/protected-paths.js +134 -16
  100. package/dist/core/permission/sandbox-runtime.js +72 -3
  101. package/dist/core/permission/tool-error-code.js +15 -0
  102. package/dist/core/permission/tool-policy.js +8 -8
  103. package/dist/core/relation/peer-identity.js +57 -6
  104. package/dist/core/relation/shared-data.js +106 -0
  105. package/dist/core/role/runtime-policy.js +64 -0
  106. package/dist/core/runtime-lock.js +10 -26
  107. package/dist/core/session/runner-task-history.js +170 -0
  108. package/dist/core/session/session-manager.js +38 -6
  109. package/dist/core/session/session-mapper.js +41 -0
  110. package/dist/core/session/session-turn-coordinator.js +37 -7
  111. package/dist/core/session/session-turns.js +48 -7
  112. package/dist/core/task/task-recovery-service.js +176 -0
  113. package/dist/core/task/task-resume-operation.js +32 -0
  114. package/dist/eck/group-rules-sync.js +5 -3
  115. package/dist/eck/kit-renderer.js +1 -0
  116. package/dist/index.js +322 -207
  117. package/dist/ipc.js +141 -15
  118. package/dist/paths.js +17 -4
  119. package/dist/product.js +3 -0
  120. package/dist/trigger/event-source.js +19 -0
  121. package/dist/trigger/feedback.js +233 -40
  122. package/dist/trigger/parser.js +55 -5
  123. package/dist/trigger/patch.js +9 -0
  124. package/dist/trigger/scheduler.js +30 -4
  125. package/dist/trigger/validation.js +20 -3
  126. package/dist/utils/atomic-write.js +98 -0
  127. package/dist/utils/cross-platform.js +87 -16
  128. package/dist/utils/ecweb-utils.js +1 -0
  129. package/dist/utils/error-utils.js +40 -2
  130. package/dist/utils/logger.js +23 -13
  131. package/dist/utils/npm-ops.js +119 -4
  132. package/dist/utils/package-version-info.js +244 -0
  133. package/dist/utils/tool-summary.js +59 -3
  134. package/dist/utils/welcome.js +174 -1
  135. package/dist/utils/windows-shell-trust.js +37 -0
  136. package/ecagent/dist/harness/agent-harness.js +3 -0
  137. package/ecagent/dist/harness/session/jsonl-storage.js +21 -2
  138. package/kits/docs/INDEX.md +0 -6
  139. package/kits/docs/channels/aun.md +23 -2
  140. package/kits/docs/channels/feishu.md +1 -1
  141. package/kits/docs/context-assembly.md +29 -17
  142. package/kits/docs/evolcore/INDEX.md +8 -3
  143. package/kits/docs/evolcore/agent-create.md +39 -7
  144. package/kits/docs/evolcore/agent.md +33 -1
  145. package/kits/docs/evolcore/aid.md +9 -1
  146. package/kits/docs/evolcore/config.md +40 -1
  147. package/kits/docs/evolcore/group-collaboration.md +1 -1
  148. package/kits/docs/evolcore/group.md +117 -2
  149. package/kits/docs/evolcore/menu-client.md +16 -0
  150. package/kits/docs/evolcore/msg.md +3 -0
  151. package/kits/docs/evolcore/storage.md +3 -0
  152. package/kits/docs/evolcore/task.md +14 -0
  153. package/kits/docs/evolcore/trigger.md +51 -3
  154. package/kits/docs/venues/group.md +5 -0
  155. package/kits/eck_manifest.json +27 -0
  156. package/kits/rules/01-overview.md +11 -7
  157. package/kits/rules/04-relation.md +2 -1
  158. package/kits/rules/05-venue.md +1 -1
  159. package/kits/rules/06-channel.md +6 -4
  160. package/kits/schemas/agent-config.schema.12.json +25 -1
  161. package/kits/schemas/daemon.schema.6.json +2 -2
  162. package/kits/schemas/defaults.schema.5.json +1 -1
  163. package/kits/schemas/relation-config.schema.1.json +1 -1
  164. package/kits/schemas/relation-config.schema.5.json +1 -1
  165. package/kits/schemas/role-config.schema.1.json +14 -0
  166. package/kits/templates/roles/member.json +5 -0
  167. package/kits/templates/roles/visitor.json +5 -0
  168. package/kits/templates/system-fragments/baseagent.md +1 -1
  169. package/kits/templates/system-fragments/bootstrap.md +26 -9
  170. package/kits/templates/system-fragments/channel.md +8 -44
  171. package/kits/templates/system-fragments/commands.md +10 -2
  172. package/kits/templates/system-fragments/group-unread-history.md +7 -0
  173. package/kits/templates/system-fragments/reply.md +56 -0
  174. package/package.json +7 -3
  175. package/dist/aun/group-identity.js +0 -9
  176. /package/dist/aun/{group-fs-download.js → group/fs-download.js} +0 -0
  177. /package/dist/aun/{msg/group-index.js → group/index-rpc.js} +0 -0
@@ -0,0 +1,176 @@
1
+ import { replyContextFromSession } from '../message/message-utils.js';
2
+ export const TASK_RECOVERY_PROMPT = '继续当前会话尚未完成的任务。先核对最新用户指令、已有结果和任务状态;若已完成、用户已停止或正在等待必要输入/审批,则不要重复执行。对可能已产生副作用的步骤先核实结果,再继续未完成的工作。';
3
+ function lastTask(state) {
4
+ return state?.active ?? state?.lastTerminal;
5
+ }
6
+ function sameRecoveryRun(previous, requested) {
7
+ return !!previous && !!requested
8
+ && previous.triggerId === requested.triggerId && previous.runId === requested.runId
9
+ && previous.taskId === requested.taskId && previous.generation === requested.generation;
10
+ }
11
+ function canRecover(session, automatic, origin) {
12
+ const state = session.metadata?.turnState;
13
+ const task = lastTask(state);
14
+ if (!task)
15
+ return !automatic && !!session.processingState;
16
+ if (task.status === 'committed' || task.status === 'waiting_interaction')
17
+ return false;
18
+ if (state?.active?.interactions.some(item => !item.settlement))
19
+ return false;
20
+ const context = task.recoveryContext;
21
+ if ((context?.baseagent && context.baseagent !== session.baseagent)
22
+ || (context?.agentSessionId && context.agentSessionId !== session.agentSessionId))
23
+ return false;
24
+ const reason = 'cancelReason' in task ? task.cancelReason : 'reason' in task ? task.reason : undefined;
25
+ if (reason && /interaction|approval|permission|denied|recalled|new_message|empty_prompt/i.test(reason))
26
+ return false;
27
+ if (task.status === 'cancelled' || task.status === 'superseded' || reason === 'stop' || reason === 'explicit_cancel') {
28
+ return !automatic && (reason === 'stop' || reason === 'explicit_cancel');
29
+ }
30
+ if (!automatic)
31
+ return true;
32
+ if (state?.active)
33
+ return false;
34
+ return !context?.originTriggerId || sameRecoveryRun(context.recovery, origin);
35
+ }
36
+ /** Check again at dequeue: a newer task, /stop, /renew or backend switch wins. */
37
+ export function isTaskRecoveryCurrent(session, recovery) {
38
+ const state = session.metadata?.turnState;
39
+ return !session.deletedAt
40
+ && session.agentSessionId === recovery.agentSessionId
41
+ && session.baseagent === recovery.baseagent
42
+ && (state?.generation ?? 0) === recovery.generation
43
+ && lastTask(state)?.taskId === recovery.taskId
44
+ && canRecover(session, recovery.automatic, recovery.origin);
45
+ }
46
+ export class TaskRecoveryService {
47
+ deps;
48
+ constructor(deps) {
49
+ this.deps = deps;
50
+ }
51
+ async resume(request) {
52
+ const result = (status, message, ok = true) => ({
53
+ ok, status, sessionId: request.sessionId, message,
54
+ });
55
+ return this.deps.queue.withSessionBarrier(request.sessionId, async () => {
56
+ if (request.signal?.aborted)
57
+ return result('not_recoverable', '恢复请求已取消', false);
58
+ if (request.automatic && (!request.expectedTaskId || !Number.isSafeInteger(request.expectedGeneration))) {
59
+ return result('not_recoverable', '自动恢复必须指定原任务及其版本', false);
60
+ }
61
+ const session = await this.deps.sessions.getSessionById(request.sessionId);
62
+ if (!session || session.deletedAt || (request.agentAid && session.selfAID !== request.agentAid)) {
63
+ return result('not_found', '目标会话不存在或不属于当前 Agent', false);
64
+ }
65
+ const state = session.metadata?.turnState;
66
+ const task = lastTask(state);
67
+ const trigger = request.message?.triggerMeta;
68
+ const origin = request.automatic && trigger
69
+ && typeof trigger.runId === 'string' && typeof trigger.attemptId === 'string'
70
+ && request.expectedTaskId && request.expectedGeneration !== undefined
71
+ ? { taskId: request.expectedTaskId, generation: request.expectedGeneration,
72
+ triggerId: trigger.triggerId, runId: trigger.runId, attemptId: trigger.attemptId }
73
+ : undefined;
74
+ const retry = sameRecoveryRun(task?.recoveryContext?.recovery, origin);
75
+ if (!retry && ((request.expectedTaskId !== undefined && task?.taskId !== request.expectedTaskId)
76
+ || (request.expectedGeneration !== undefined && task?.generation !== request.expectedGeneration))) {
77
+ return result('stale', '原任务状态已变化,已忽略过期恢复请求');
78
+ }
79
+ if (retry && task?.recoveryContext?.recovery?.attemptId === origin?.attemptId) {
80
+ return result('stale', '该恢复 attempt 已执行,已忽略重复请求');
81
+ }
82
+ if (state?.active?.status === 'waiting_interaction'
83
+ || state?.active?.interactions.some(item => !item.settlement)) {
84
+ return result('waiting_interaction', '任务正在等待必要输入或审批,请先完成交互');
85
+ }
86
+ let runner;
87
+ try {
88
+ runner = this.deps.getRunner(session);
89
+ }
90
+ catch {
91
+ return result('not_recoverable', '原 baseagent 当前不可用', false);
92
+ }
93
+ if (!request.automatic && this.deps.resumePause(session.id)) {
94
+ return result('resumed_pause', '✓ 已继续暂停的任务');
95
+ }
96
+ // Other gate holders may have captured an older cutoff. Recovery must
97
+ // see messages admitted during the session-store read as well.
98
+ const snapshot = this.deps.queue.getSessionSnapshot(session.id);
99
+ // task:error is published before queue cleanup. Only the exact durable
100
+ // failed task may queue its successor while that batch is winding down.
101
+ const finishingFailedTask = request.automatic && !!request.expectedTaskId
102
+ && !state?.active && state?.lastTerminal?.status === 'failed'
103
+ && !snapshot.dispatching
104
+ && snapshot.activeTaskIds.length > 0 && snapshot.activeTaskIds.every(id => id === task?.taskId);
105
+ if (snapshot.prequeue || snapshot.queued || ((snapshot.active || snapshot.dispatching
106
+ || runner.hasActiveStream(session.id)) && !finishingFailedTask)) {
107
+ return result('already_running', '任务正在执行或已有待处理消息,无需重复恢复');
108
+ }
109
+ if (task?.status === 'committed')
110
+ return result('completed', '上一任务已正常完成,无需恢复');
111
+ if (!canRecover(session, request.automatic === true, origin)) {
112
+ return result('not_recoverable', '当前没有可恢复的异常任务', false);
113
+ }
114
+ if (!runner.capabilities?.taskRecovery || !session.agentSessionId) {
115
+ return result('not_recoverable', '原 baseagent 会话不可恢复;不会创建替代会话', false);
116
+ }
117
+ if (runner.resolveSessionFile && !runner.resolveSessionFile(session.agentSessionId, session.projectPath)) {
118
+ return result('not_recoverable', '原 baseagent 会话文件不存在;不会创建替代会话', false);
119
+ }
120
+ if (!this.deps.hasChannel(session))
121
+ return result('not_recoverable', '原会话渠道当前不可用', false);
122
+ const context = task?.recoveryContext;
123
+ const peerId = context?.peerId || request.peerId || session.metadata?.peerId;
124
+ const peerName = session.metadata?.peerName;
125
+ const peerType = session.metadata?.peerType;
126
+ const channelType = session.channelType || session.channel.split('#')[0];
127
+ const encrypted = context?.encrypted ?? request.encrypted;
128
+ if (!peerId || (channelType === 'aun' && typeof encrypted !== 'boolean')) {
129
+ return result('not_recoverable', '缺少原任务身份或加密状态,无法安全恢复', false);
130
+ }
131
+ const recovery = {
132
+ generation: state?.generation ?? 0, taskId: task?.taskId,
133
+ agentSessionId: session.agentSessionId, baseagent: session.baseagent,
134
+ automatic: request.automatic === true,
135
+ origin,
136
+ };
137
+ const replyContext = replyContextFromSession(session);
138
+ const message = {
139
+ ...request.message,
140
+ ...(trigger ? { triggerMeta: {
141
+ ...trigger,
142
+ originPeerId: peerId,
143
+ originChannelKey: session.metadata?.channelKey || session.channel,
144
+ originChannelType: channelType,
145
+ originChatType: session.chatType === 'group' ? 'group' : 'private',
146
+ originConversationId: session.channelId,
147
+ originControl: false,
148
+ } } : {}),
149
+ channel: session.metadata?.channelKey || session.channel,
150
+ channelType, channelId: session.channelId, selfAID: session.selfAID,
151
+ baseagent: session.baseagent, threadId: session.threadId,
152
+ chatType: session.chatType === 'group' ? 'group' : 'private',
153
+ groupId: session.chatType === 'group' ? session.metadata?.groupId || session.channelId : undefined,
154
+ peerId, peerName, peerType,
155
+ encrypted, content: TASK_RECOVERY_PROMPT,
156
+ source: request.message?.source ?? 'recovery',
157
+ messageId: request.message?.messageId ?? `recovery:${session.id}:${recovery.generation}:${task?.taskId ?? session.processingState}`,
158
+ timestamp: Date.now(), boundSessionId: session.id, taskRecovery: recovery,
159
+ replyContext: { ...replyContext, metadata: { ...replyContext?.metadata, encrypted } },
160
+ };
161
+ // Keep the completion promise alive for Trigger cancellation, but release
162
+ // the dequeue gate as soon as admission is durable (never await the run).
163
+ let admitted;
164
+ const persisted = new Promise(resolve => { admitted = resolve; });
165
+ const completion = this.deps.queue.enqueue(session.id, message, session.projectPath, {
166
+ interruptible: false, onPersisted: admitted, selfAID: session.selfAID,
167
+ agentName: session.selfAID, sessionKeyField: session.sessionKey, signal: request.signal,
168
+ });
169
+ await Promise.race([persisted, completion]);
170
+ // Manual callers need only the acknowledgement. Trigger callers retain
171
+ // completion to observe failure and wait for the interrupt barrier.
172
+ void completion.catch(() => { });
173
+ return { ...result('queued_recovery', '✓ 已排入恢复队列,将继续原 baseagent 会话'), completion };
174
+ });
175
+ }
176
+ }
@@ -0,0 +1,32 @@
1
+ /** IPC has authenticated the transport; this enforces the Agent/task boundary. */
2
+ export async function executeTaskResume(request, deps) {
3
+ const authorization = request.sessionId
4
+ ? await deps.authorizeManaged({
5
+ sessionId: request.sessionId,
6
+ delegationToken: request.delegationToken,
7
+ delegationCommandHash: request.delegationCommandHash,
8
+ }, 'ec.task.resume')
9
+ : undefined;
10
+ if (authorization && !authorization.ok)
11
+ return authorization;
12
+ if (!authorization && !request.localControl) {
13
+ return { ok: false, code: 'INVALID_CONTROL_TOKEN', error: 'valid local control token is required' };
14
+ }
15
+ if (authorization && !authorization.management && authorization.dataScope !== 'daemon') {
16
+ return { ok: false, code: 'NOT_ALLOWED', error: 'task resume requires Agent management authority' };
17
+ }
18
+ const target = await deps.sessions.getSessionById(request.targetSessionId);
19
+ if (!target || target.deletedAt || !target.selfAID) {
20
+ return { ok: false, code: 'SESSION_NOT_FOUND', error: 'target Agent session not found' };
21
+ }
22
+ if (authorization && authorization.dataScope !== 'daemon' && target.selfAID !== authorization.aid) {
23
+ return { ok: false, code: 'SCOPE_MISMATCH', error: 'only sessions of the current Agent can be resumed' };
24
+ }
25
+ // Identity, encryption, task version and backend binding are read from the
26
+ // target's durable state. None may be overridden by the CLI payload.
27
+ const { completion: _completion, ...result } = await deps.recover({
28
+ sessionId: target.id,
29
+ agentAid: target.selfAID,
30
+ });
31
+ return result;
32
+ }
@@ -2,10 +2,10 @@ import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { TextDecoder } from 'util';
4
4
  import { getAidStore, isValidAid, loadClient, SLOT } from '../aun/aid/index.js';
5
- import { downloadGroupFsBytes, GroupFsDownloadError } from '../aun/group-fs-download.js';
6
- import { checkGroupIndex as checkGroupIndexRpc, getGroupIndex as getGroupIndexRpc } from '../aun/msg/group-index.js';
5
+ import { downloadGroupFsBytes, GroupFsDownloadError, checkGroupIndex as checkGroupIndexRpc, getGroupIndex as getGroupIndexRpc, } from '../aun/group/index.js';
7
6
  import { agentRelationsDir } from '../paths.js';
8
7
  import { formatPeerKey } from '../core/relation/peer-identity.js';
8
+ import { syncRelationSharedFile } from '../core/relation/shared-data.js';
9
9
  import { atomicWriteBytes } from '../utils/atomic-write.js';
10
10
  import { logger } from '../utils/logger.js';
11
11
  import { invalidateKitCache } from './kit-renderer.js';
@@ -23,8 +23,10 @@ export async function syncGroupRulesContext(opts) {
23
23
  const group = lazyGroupClient(selfAid);
24
24
  try {
25
25
  const resolved = await resolveGroupRules(group.client, groupId, paths.localRulesPath);
26
- if (resolved.changed)
26
+ if (resolved.changed) {
27
+ syncRelationSharedFile(selfAid, paths.peerKey, RULES_LOCAL_PATH);
27
28
  invalidateKitCache();
29
+ }
28
30
  return {
29
31
  groupRulesPath: resolved.usable ? paths.localRulesPath : undefined,
30
32
  groupRulesStatus: resolved.status,
@@ -228,6 +228,7 @@ const PARAM_DESCRIPTIONS = {
228
228
  channel: '渠道类型(aun/feishu/wechat/dingtalk/qqbot/wecom)',
229
229
  venueUid: '场所唯一标识(预留)',
230
230
  mentionMode: '群 @ 处理模式(mention-only=被@才响应 / disabled=所有消息都响应)',
231
+ groupHistoryAvailable: '本地群历史读取能力是否可用',
231
232
  clientType: '客户端类型(desktop/web/mobile)',
232
233
  permissionMode: '权限模式(readonly/auto/request/bypass;旧值会安全迁移)',
233
234
  capabilities: '当前渠道支持的能力列表',