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
@@ -24,6 +24,7 @@ import { commandExists } from '../utils/cross-platform.js';
24
24
  import { GeminiSessionFileAdapter } from '../core/session/adapters/gemini-session-file-adapter.js';
25
25
  import { logger } from '../utils/logger.js';
26
26
  import { normalizeExecutionPermissionMode, normalizePermissionMode } from '../core/permission/mode.js';
27
+ import { resolveRoot } from '../paths.js';
27
28
  import { workspaceContainsHClassPaths } from '../core/permission/protected-paths.js';
28
29
  import { buildBubblewrapCommand } from '../core/permission/sandbox-runtime.js';
29
30
  import { getManagedTaskTempDir } from '../cli/task-context.js';
@@ -103,15 +104,10 @@ function isGeminiPermissionDenial(event) {
103
104
  }
104
105
  const GEMINI_H_CLASS_PATTERNS = [
105
106
  String.raw `evolcore\.json`,
106
- String.raw `agents[/\\]defaults\.json`,
107
- String.raw `agents[/\\]defaults_\d+\.json`,
108
- String.raw `agents[/\\][^/\\"]+[/\\]config\.json`,
109
- String.raw `agents[/\\][^/\\"]+[/\\]contact\.json`,
110
- String.raw `agents[/\\][^/\\"]+[/\\]relations[/\\][^/\\"]+[/\\]config\.json`,
111
- String.raw `backups[/\\]config`,
107
+ String.raw `(?:^|[/\\"])data(?:[/\\"]|$)`,
108
+ String.raw `(?:^|[/\\"])(?:AIDs|aids)(?:[/\\"]|$)`,
112
109
  String.raw `\.snapshots`,
113
110
  String.raw `(?:^|[/\\"])CA(?:[/\\"]|$)`,
114
- String.raw `(?:AIDs|aids)[/\\][^/\\"]+[/\\](?:cert|keys)`,
115
111
  String.raw `(?:^|[/\\"])\.device_id(?:[/\\"]|$)`,
116
112
  String.raw `(?:^|[/\\"])\.env(?:[/\\"]|$)`,
117
113
  String.raw `(?:^|[/\\"])\.seed(?:[/\\"]|$)`,
@@ -119,13 +115,13 @@ const GEMINI_H_CLASS_PATTERNS = [
119
115
  String.raw `(?:^|[/\\"])\.migrated-[^/\\"]+`,
120
116
  String.raw `(?:^|[/\\"])\.lock(?:[/\\"]|$)`,
121
117
  String.raw `(?:^|[/\\"])daemon\.pid(?:[/\\"]|$)`,
122
- String.raw `\.json_`,
123
- String.raw `\.json\.migrated`,
124
- String.raw `agents[/\\][^/\\"]+[/\\](?:sessions|data[/\\](?:channels|handoff|outbox\.jsonl|contact-audit\.jsonl))`,
125
- String.raw `data[/\\]triggers(?:[/\\]|$)`,
126
- String.raw `data[/\\](?:sessions|contact-book-audit\.jsonl|feishu-seen-[^/\\"]+\.jsonl|[a-z0-9_-]+-greeted-[^/\\"]+\.jsonl|channel-state|daemon[/\\](?:control|migrations|message-queue\.json))`,
118
+ String.raw `agents[/\\][^/\\"]+[/\\]data[/\\]codex-tokens(?:[/\\]|$)`,
119
+ String.raw `agents[/\\][^/\\"]+[/\\](?:data[/\\](?:[^/\\"]+[/\\])*context-tokens\.json|(?:data[/\\](?:[^/\\"]+[/\\])*menu-tokens\.json))`,
127
120
  ];
128
121
  const GEMINI_L_CLASS_PATTERNS = [
122
+ String.raw `agents[/\\]defaults\.json`,
123
+ String.raw `agents[/\\][^/\\"]+[/\\](?:config\.json|contact\.json|contact-operations\.jsonl|relations[/\\][^/\\"]+[/\\]config\.json|roles|triggers)`,
124
+ String.raw `backups[/\\]config`,
129
125
  String.raw `agents[/\\][^/\\"]+[/\\]triggers(?:[/\\]|$)`,
130
126
  ];
131
127
  const GEMINI_L_CLASS_WRITE_TOOLS = [
@@ -165,6 +161,24 @@ export function resolveGeminiPermissionProfile(value, bootstrap = false) {
165
161
  function escapeGeminiArgsRegex(value) {
166
162
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
167
163
  }
164
+ function geminiAgentRootPattern(aid) {
165
+ return `agents[/\\]${escapeGeminiArgsRegex(aid)}(?:[/\\]|$)`;
166
+ }
167
+ function geminiHClassPatterns(options = {}) {
168
+ const patterns = [...GEMINI_H_CLASS_PATTERNS];
169
+ if (!options.selfAid)
170
+ return patterns;
171
+ try {
172
+ const agentsRoot = path.join(options.root ?? resolveRoot(), 'agents');
173
+ for (const entry of fs.readdirSync(agentsRoot, { withFileTypes: true })) {
174
+ if (!entry.isDirectory() || entry.name.toLowerCase() === options.selfAid.toLowerCase())
175
+ continue;
176
+ patterns.push(geminiAgentRootPattern(entry.name));
177
+ }
178
+ }
179
+ catch { }
180
+ return patterns;
181
+ }
168
182
  function normalizeGeminiBootstrapAgentMdPath(candidate) {
169
183
  const filePath = path.resolve(candidate);
170
184
  const aidDir = path.dirname(filePath);
@@ -198,13 +212,14 @@ function appendGeminiPolicyRule(lines, toolName, decision, priority, argsPattern
198
212
  lines.push(`decision = "${decision}"`);
199
213
  lines.push(`priority = ${priority}`, '');
200
214
  }
201
- export function buildGeminiAdminPolicy(profile, bootstrapAgentMdPath, personalMdPath) {
215
+ export function buildGeminiAdminPolicy(profile, bootstrapAgentMdPath, personalMdPath, scope = {}) {
202
216
  const lines = [];
217
+ const hClassPatterns = geminiHClassPatterns(scope);
203
218
  if (bootstrapAgentMdPath) {
204
219
  const bootstrap = normalizeGeminiBootstrapAgentMdPath(bootstrapAgentMdPath);
205
220
  // Preserve the global protected-path boundary even if a malformed internal
206
221
  // caller ever supplies a protected file as the bootstrap target.
207
- for (const pattern of GEMINI_H_CLASS_PATTERNS) {
222
+ for (const pattern of hClassPatterns) {
208
223
  appendGeminiPolicyRule(lines, '*', 'deny', 998, pattern);
209
224
  }
210
225
  for (const pattern of GEMINI_L_CLASS_PATTERNS) {
@@ -236,7 +251,7 @@ export function buildGeminiAdminPolicy(profile, bootstrapAgentMdPath, personalMd
236
251
  appendGeminiPolicyRule(lines, 'run_shell_command', 'allow', 999, pattern);
237
252
  }
238
253
  // H-class protection is mode-independent and applies to built-in and MCP tools.
239
- for (const pattern of GEMINI_H_CLASS_PATTERNS) {
254
+ for (const pattern of hClassPatterns) {
240
255
  appendGeminiPolicyRule(lines, '*', 'deny', 998, pattern);
241
256
  }
242
257
  for (const pattern of GEMINI_L_CLASS_PATTERNS) {
@@ -394,8 +409,10 @@ export class GeminiRunner {
394
409
  const runtimeLockDir = runtimeEnv?.EVOLCORE_RUNTIME_LOCK_DIR;
395
410
  const geminiHome = path.join(os.homedir(), '.gemini');
396
411
  fs.mkdirSync(geminiHome, { recursive: true, mode: 0o700 });
412
+ const selfAid = this.permissionContexts.get(sessionId)?.selfAid;
397
413
  const sandboxProbe = executionPermissionMode === 'fullaccess' ? undefined : buildBubblewrapCommand(this.resolved.cliPath, [], {
398
414
  projectPath,
415
+ selfAid,
399
416
  writablePaths: [
400
417
  geminiHome,
401
418
  ...(runtimeDir ? [runtimeDir] : []),
@@ -404,7 +421,7 @@ export class GeminiRunner {
404
421
  ...(personalMdPath ? [personalMdPath] : []),
405
422
  ],
406
423
  });
407
- if (executionPermissionMode !== 'fullaccess' && !sandboxProbe && workspaceContainsHClassPaths(projectPath)) {
424
+ if (executionPermissionMode !== 'fullaccess' && !sandboxProbe && workspaceContainsHClassPaths(projectPath, resolveRoot(), { selfAid })) {
408
425
  throw new Error('Gemini 缺少可用的路径级隔离运行时,且项目覆盖 EvolCore 受保护根;已拒绝启动本轮任务');
409
426
  }
410
427
  // Build CLI args
@@ -430,7 +447,7 @@ export class GeminiRunner {
430
447
  }
431
448
  else {
432
449
  const policyPath = path.join(managedTmpDir, `evolcore-gemini-permission-${crypto.randomUUID()}.toml`);
433
- fs.writeFileSync(policyPath, buildGeminiAdminPolicy(permissionProfile, bootstrapAgentMdPath, personalMdPath), { mode: 0o600, flag: 'wx' });
450
+ fs.writeFileSync(policyPath, buildGeminiAdminPolicy(permissionProfile, bootstrapAgentMdPath, personalMdPath, { root: resolveRoot(), selfAid }), { mode: 0o600, flag: 'wx' });
434
451
  tempFiles.push(policyPath);
435
452
  args.push(...buildGeminiPermissionArgs(permissionProfile, policyPath, !!sandboxProbe));
436
453
  }
@@ -467,6 +484,7 @@ export class GeminiRunner {
467
484
  const env = this.buildAgentEnv(sessionId, runtimeEnv);
468
485
  const sandboxedCommand = executionPermissionMode === 'fullaccess' ? undefined : buildBubblewrapCommand(this.resolved.cliPath, args, {
469
486
  projectPath,
487
+ selfAid,
470
488
  writablePaths: [
471
489
  geminiHome,
472
490
  ...(runtimeDir ? [runtimeDir] : []),
@@ -1,12 +1,11 @@
1
1
  import fs from 'fs';
2
- import path from 'path';
3
2
  import { createHash } from 'crypto';
4
3
  import { TextDecoder } from 'util';
5
4
  import { isValidAid } from '../aid/validation.js';
6
5
  import { normalizeGroupAid } from './identity.js';
7
6
  import { resolvePaths } from '../../paths.js';
8
- import { appendJsonlDurable, chatDirPath } from '../../core/session/session-fs-store.js';
9
- import { isTransientProtocolMessage, messageLogPath } from '../../core/message/message-log.js';
7
+ import { chatDirPath } from '../../core/session/session-fs-store.js';
8
+ import { isTransientProtocolMessage, messageLogPath, } from '../../core/message/message-log.js';
10
9
  export class GroupHistoryError extends Error {
11
10
  code;
12
11
  constructor(code, message) {
@@ -26,15 +25,15 @@ export function parseGroupHistoryArgs(argv) {
26
25
  fail('INVALID_ARGUMENT', 'history 需要群 AID');
27
26
  const groupId = normalizeGroupAid(args[1], from.slice(from.indexOf('.') + 1));
28
27
  const values = new Map();
29
- const flags = new Set(['--unread', '--all']);
30
- const named = new Set(['--limit', '--thread', '--before', '--after', '--direction', '--format', '--message', '--offset']);
28
+ const named = new Set([
29
+ '--limit', '--thread', '--before', '--before-id', '--after', '--after-id',
30
+ '--direction', '--format', '--message', '--offset',
31
+ ]);
31
32
  for (let i = 2; i < args.length; i++) {
32
33
  const flag = args[i];
33
34
  if (values.has(flag))
34
35
  fail('INVALID_ARGUMENT', flag + ' 不能重复');
35
- if (flags.has(flag))
36
- values.set(flag, 'true');
37
- else if (named.has(flag)) {
36
+ if (named.has(flag)) {
38
37
  const value = args[++i];
39
38
  if (!value || value.startsWith('--'))
40
39
  fail('INVALID_ARGUMENT', flag + ' 缺少值');
@@ -43,16 +42,8 @@ export function parseGroupHistoryArgs(argv) {
43
42
  else
44
43
  fail('INVALID_ARGUMENT', '不支持的 history 参数: ' + flag);
45
44
  }
46
- if (values.has('--unread') && values.has('--all'))
47
- fail('INVALID_ARGUMENT', '--unread 与 --all 互斥');
48
- if (!values.has('--unread') && !values.has('--all')) {
49
- fail('INVALID_ARGUMENT', 'history 必须明确指定 --unread 或 --all');
50
- }
51
45
  if (values.has('--format') && !['text', 'json'].includes(values.get('--format')))
52
46
  fail('INVALID_ARGUMENT', '--format 仅支持 text|json');
53
- if ((values.has('--message') || values.has('--offset')) && !values.has('--all')) {
54
- fail('INVALID_ARGUMENT', '--message/--offset 仅支持 --all');
55
- }
56
47
  if (values.has('--offset') && !values.has('--message'))
57
48
  fail('INVALID_ARGUMENT', '--offset 需要 --message');
58
49
  const offset = values.has('--offset') ? Number(values.get('--offset')) : undefined;
@@ -77,9 +68,22 @@ export function parseGroupHistoryArgs(argv) {
77
68
  const after = time('--after');
78
69
  if (before !== undefined && after !== undefined && after >= before)
79
70
  fail('INVALID_ARGUMENT', '--after 必须早于 --before');
71
+ if (values.has('--before-id') && before === undefined)
72
+ fail('INVALID_ARGUMENT', '--before-id 需要 --before');
73
+ if (values.has('--after-id') && after === undefined)
74
+ fail('INVALID_ARGUMENT', '--after-id 需要 --after');
75
+ if (values.has('--before-id') && values.has('--after-id'))
76
+ fail('INVALID_ARGUMENT', 'before 与 after 游标不能同时指定');
77
+ for (const flag of ['--before-id', '--after-id']) {
78
+ const cursorId = values.get(flag);
79
+ if (cursorId !== undefined && !/^[a-f0-9]{64}$/.test(cursorId)) {
80
+ fail('INVALID_ARGUMENT', `${flag} 必须是返回的 64 位游标 ID`);
81
+ }
82
+ }
80
83
  return {
81
- from, groupId, unread: values.has('--unread'), thread: values.get('--thread'),
82
- limit, direction, before, after, message: values.get('--message'), offset,
84
+ from, groupId, thread: values.get('--thread'),
85
+ limit, direction, before, beforeId: values.get('--before-id'),
86
+ after, afterId: values.get('--after-id'), message: values.get('--message'), offset,
83
87
  };
84
88
  }
85
89
  const DETAIL_BYTES = 8 * 1024;
@@ -135,24 +139,7 @@ function json(line) {
135
139
  return JSON.parse(line);
136
140
  }
137
141
  catch {
138
- return fail('HISTORY_CORRUPT', '历史文件含损坏的完整 JSONL 行,未推进已读状态');
139
- }
140
- }
141
- function generation(file) {
142
- try {
143
- const stat = fs.statSync(file);
144
- // First complete record plus file identity stays stable across append, but changes on replacement.
145
- let first = '';
146
- for (const line of lines(file)) {
147
- first = line;
148
- break;
149
- }
150
- return digest(JSON.stringify([stat.dev, stat.ino, stat.birthtimeMs, digest(first)]));
151
- }
152
- catch (error) {
153
- if (error.code === 'ENOENT')
154
- return 'missing';
155
- throw error;
142
+ return fail('HISTORY_CORRUPT', '历史文件含损坏的完整 JSONL 行');
156
143
  }
157
144
  }
158
145
  function* records(file, args) {
@@ -179,25 +166,8 @@ function* records(file, args) {
179
166
  if (seen.has(identity))
180
167
  continue;
181
168
  seen.add(identity);
182
- // Keep the existing read-state fingerprints compatible; redelivery seq/time do not change identity.
183
- const hash = digest(JSON.stringify([identity, entry.threadId ?? null, entry.from, entry.to,
184
- entry.msgType, entry.encrypt, entry.content, entry.payloadSummary, entry.groupAttention, entry.groupDelivery]));
185
- yield { entry, hash };
186
- }
187
- }
188
- function readState(chatDir, logGeneration) {
189
- const read = new Set();
190
- for (const line of lines(path.join(chatDir, 'group-history-read.jsonl'))) {
191
- const event = json(line);
192
- if (!event || event.version !== 1 || typeof event.generation !== 'string' || !Array.isArray(event.records)
193
- || !event.records.every(value => typeof value === 'string' && /^[a-f0-9]{64}$/.test(value))) {
194
- fail('HISTORY_STATE_CORRUPT', '群历史已读状态损坏');
195
- }
196
- if (event.generation === logGeneration)
197
- for (const value of event.records)
198
- read.add(value);
169
+ yield { entry, cursorId: digest(identity) };
199
170
  }
200
- return read;
201
171
  }
202
172
  function matchThread(entry, thread) {
203
173
  return thread === 'all' || (thread === 'main' ? !entry.threadId : entry.threadId === thread);
@@ -209,7 +179,7 @@ function utf8Prefix(content, bytes) {
209
179
  end--;
210
180
  return buffer.subarray(0, end).toString('utf8');
211
181
  }
212
- /** Local-only history. Unread queries record only the messages selected for this result. */
182
+ /** Local-only, read-only group history. */
213
183
  export function groupHistory(args, context = {}) {
214
184
  if (!isValidAid(args.from))
215
185
  fail('INVALID_SELF_AID', '无效 self AID');
@@ -220,14 +190,10 @@ export function groupHistory(args, context = {}) {
220
190
  const thread = args.thread ?? context.defaultThread ?? 'all';
221
191
  if (!thread || thread.length > 512 || /[\x00-\x1f]/.test(thread))
222
192
  fail('INVALID_THREAD', '无效 thread');
223
- const chatDir = chatDirPath(context.sessionsDir ?? resolvePaths().sessionsDir, 'aun', args.groupId, args.from);
193
+ const chatDir = chatDirPath(context.sessionsDir ?? resolvePaths().sessionsDir, 'aun', args.groupId, args.from, context.channelKey);
224
194
  const file = messageLogPath(chatDir);
225
195
  const base = { ok: true, source: 'local', self_aid: args.from, group_id: args.groupId };
226
- const logGeneration = args.unread ? generation(file) : '';
227
- const read = args.unread ? readState(chatDir, logGeneration) : new Set();
228
196
  if (args.message) {
229
- if (args.unread)
230
- fail('INVALID_ARGUMENT', '--message 仅支持 --all');
231
197
  const offset = args.offset ?? 0;
232
198
  if (!Number.isSafeInteger(offset) || offset < 0)
233
199
  fail('INVALID_ARGUMENT', '无效 offset');
@@ -246,37 +212,40 @@ export function groupHistory(args, context = {}) {
246
212
  }
247
213
  return fail('MESSAGE_NOT_FOUND', '本地当前范围内找不到该消息');
248
214
  }
215
+ const forward = args.after !== undefined;
249
216
  const selected = [];
250
- let hasMore = false;
251
217
  let bytes = 0;
218
+ let hasMore = false;
252
219
  for (const record of records(file, args)) {
253
- const { entry } = record;
220
+ const { entry, cursorId } = record;
254
221
  if (!matchThread(entry, thread)
255
- || (args.direction && args.direction !== 'all' && entry.dir !== args.direction)
256
- || (args.after !== undefined && entry.ts <= args.after)
257
- || (args.before !== undefined && entry.ts >= args.before))
258
- continue;
259
- if (args.unread && (entry.dir !== 'in' || entry.groupDelivery !== 'filtered'
260
- || entry.groupAttention !== 'unmentioned' || read.has(record.hash)))
222
+ || (args.direction && args.direction !== 'all' && entry.dir !== args.direction))
261
223
  continue;
262
- const size = Buffer.byteLength(JSON.stringify(entry));
263
- if (args.unread) {
264
- if (selected.length >= limit || (selected.length && bytes + size > MAX_PAGE_BYTES)) {
265
- hasMore = true;
266
- break;
267
- }
268
- selected.push(record);
269
- bytes += size;
224
+ if (args.after !== undefined) {
225
+ if (entry.ts < args.after || (entry.ts === args.after
226
+ && (args.afterId === undefined || cursorId <= args.afterId)))
227
+ continue;
270
228
  }
271
- else {
229
+ if (args.before !== undefined) {
230
+ if (entry.ts > args.before || (entry.ts === args.before
231
+ && (args.beforeId === undefined || cursorId >= args.beforeId)))
232
+ continue;
233
+ }
234
+ const recordBytes = Buffer.byteLength(JSON.stringify(entry));
235
+ const insertAt = selected.findIndex(candidate => (candidate.entry.ts > entry.ts
236
+ || (candidate.entry.ts === entry.ts && candidate.cursorId.localeCompare(cursorId) > 0)));
237
+ if (insertAt < 0)
272
238
  selected.push(record);
273
- bytes += size;
274
- while (selected.length > limit || (selected.length > 1 && bytes > MAX_PAGE_BYTES)) {
275
- bytes -= Buffer.byteLength(JSON.stringify(selected.shift().entry));
276
- hasMore = true;
277
- }
239
+ else
240
+ selected.splice(insertAt, 0, record);
241
+ bytes += recordBytes;
242
+ while (selected.length > limit || (selected.length > 1 && bytes > MAX_PAGE_BYTES)) {
243
+ const removed = forward ? selected.pop() : selected.shift();
244
+ bytes -= Buffer.byteLength(JSON.stringify(removed.entry));
245
+ hasMore = true;
278
246
  }
279
247
  }
248
+ const cursorRecord = forward ? selected.at(-1) : selected[0];
280
249
  const messages = selected.map(({ entry }) => {
281
250
  if (Buffer.byteLength(JSON.stringify(entry)) <= MAX_PAGE_BYTES)
282
251
  return entry;
@@ -289,15 +258,86 @@ export function groupHistory(args, context = {}) {
289
258
  };
290
259
  });
291
260
  const result = {
292
- ...base, mode: args.unread ? 'unread' : 'all', thread_id: thread,
261
+ ...base, mode: 'history', thread_id: thread,
293
262
  count: messages.length, has_more: hasMore, messages,
263
+ ...(hasMore && cursorRecord ? (forward
264
+ ? { next_after: { ts: cursorRecord.entry.ts, id: cursorRecord.cursorId } }
265
+ : { next_before: { ts: cursorRecord.entry.ts, id: cursorRecord.cursorId } }) : {}),
294
266
  };
295
- // Persist the exact returned selection, never an entire group/time/seq watermark.
296
- // Query errors above leave state untouched; --all never writes read state.
297
- if (args.unread && selected.length) {
298
- appendJsonlDurable(path.join(chatDir, 'group-history-read.jsonl'), {
299
- version: 1, generation: logGeneration, records: selected.map(record => record.hash), confirmedAt: Date.now(),
300
- });
301
- }
302
267
  return result;
303
268
  }
269
+ /** Build the bounded, read-only background snapshot used by auto attention. */
270
+ export function readGroupHistoryContext(input) {
271
+ const now = input.now ?? Date.now();
272
+ const maxAgeMs = input.maxAgeMs;
273
+ const maxMessages = input.maxMessages ?? 20;
274
+ const maxChars = input.maxChars ?? Number.MAX_SAFE_INTEGER;
275
+ const maxMessageChars = input.maxMessageChars;
276
+ const excluded = new Set(input.excludeMessageIds ?? []);
277
+ const collectAll = maxMessages >= Number.MAX_SAFE_INTEGER;
278
+ const args = { from: input.from, groupId: input.groupId };
279
+ const candidates = [];
280
+ const file = messageLogPath(chatDirPath(input.sessionsDir ?? resolvePaths().sessionsDir, 'aun', input.groupId, input.from, input.channelKey));
281
+ for (const { entry } of records(file, args)) {
282
+ if (excluded.has(entry.msgId || ''))
283
+ continue;
284
+ if ((maxAgeMs !== undefined && entry.ts < now - maxAgeMs) || entry.ts > now)
285
+ continue;
286
+ if (!matchThread(entry, input.thread))
287
+ continue;
288
+ if (entry.msgType === 'command' || entry.groupAttention === 'command' || /^\s*\//.test(entry.content)
289
+ || !['text', 'quote', 'voice', 'image', 'video', 'file', 'link'].includes(entry.msgType))
290
+ continue;
291
+ if (/\[EvolCore\.(receive|reply|agent)\]/.test(entry.content))
292
+ continue;
293
+ if (!entry.content.trim())
294
+ continue;
295
+ const content = maxMessageChars !== undefined && entry.content.length > maxMessageChars
296
+ ? `${entry.content.slice(0, Math.floor(maxMessageChars * 0.75))}\n[...truncated...]\n${entry.content.slice(-Math.floor(maxMessageChars * 0.25))}`
297
+ : entry.content;
298
+ const decision = entry.attentionDecision?.decision
299
+ ?? (entry.groupAttention === 'auto-ignore' ? 'ignore'
300
+ : entry.groupAttention === 'auto-defer' ? 'defer'
301
+ : entry.groupAttention === 'auto-interrupt' ? 'interrupt'
302
+ : entry.groupAttention === 'auto-fallback' ? 'ignore'
303
+ : null);
304
+ const lifecycle = decision === 'defer' ? 'pending' : 'consumed';
305
+ const candidate = {
306
+ source: 'background',
307
+ message_id: entry.msgId,
308
+ ...(entry.sessionId ? { session_id: entry.sessionId } : {}),
309
+ sender: entry.from,
310
+ role: entry.permMode || 'unknown',
311
+ timestamp: new Date(entry.ts).toISOString(),
312
+ content,
313
+ lifecycle,
314
+ decision,
315
+ msg_type: entry.msgType,
316
+ };
317
+ if (collectAll) {
318
+ candidates.push(candidate);
319
+ }
320
+ else {
321
+ const insertAt = candidates.findIndex(previous => previous.timestamp > candidate.timestamp);
322
+ if (insertAt < 0)
323
+ candidates.push(candidate);
324
+ else
325
+ candidates.splice(insertAt, 0, candidate);
326
+ if (candidates.length > maxMessages)
327
+ candidates.shift();
328
+ }
329
+ }
330
+ if (collectAll)
331
+ candidates.sort((left, right) => (Date.parse(left.timestamp) - Date.parse(right.timestamp)));
332
+ const selected = [];
333
+ let chars = 0;
334
+ for (let index = candidates.length - 1; index >= 0 && selected.length < maxMessages; index--) {
335
+ const candidate = candidates[index];
336
+ const candidateChars = Buffer.byteLength(JSON.stringify(candidate));
337
+ if (chars + candidateChars > maxChars)
338
+ break;
339
+ selected.push(candidate);
340
+ chars += candidateChars;
341
+ }
342
+ return selected.reverse();
343
+ }
@@ -9,7 +9,7 @@ import { daemonTaskSendIpcTimeoutMs, resolveAunMessageEncrypt } from '../msg/p2p
9
9
  import { checkGroupIndex, getGroupIndex } from './index-rpc.js';
10
10
  import { appendMessageLog, buildOutboundEntry, classifyAunPayloadForLog } from '../../core/message/message-log.js';
11
11
  import { chatDirPath } from '../../core/session/session-fs-store.js';
12
- import { normalizeCodexTextPayload, readBestTaskRuntimeContext } from '../../cli/task-context.js';
12
+ import { readBestTaskRuntimeContext } from '../../cli/task-context.js';
13
13
  import { ipcQuery, IpcTransportError } from '../../ipc.js';
14
14
  import { resolvePaths } from '../../paths.js';
15
15
  import { readDelegationCredential } from '../../core/auth/agent-delegation.js';
@@ -173,7 +173,6 @@ export async function groupSend(args) {
173
173
  const runtimeContext = await readBestTaskRuntimeContext();
174
174
  if (args.body.mode !== 'file') {
175
175
  const payload = buildGroupPayload(args.body);
176
- normalizeCodexTextPayload(payload, runtimeContext);
177
176
  applyGroupRoutingPayloadFields(payload, args, runtimeContext);
178
177
  const daemonResult = await tryDaemonGroupSend(args, payload, runtimeContext);
179
178
  if (daemonResult)
@@ -215,7 +214,6 @@ export async function groupSend(args) {
215
214
  break;
216
215
  }
217
216
  }
218
- normalizeCodexTextPayload(payload, runtimeContext);
219
217
  const payloadMentions = Object.prototype.hasOwnProperty.call(payload, 'mentions')
220
218
  ? normalizeAunMentionEntries(payload.mentions)
221
219
  : undefined;
@@ -8,7 +8,9 @@ function parseSdkVersion(value) {
8
8
  return parts.every(Number.isSafeInteger) ? parts : undefined;
9
9
  }
10
10
  /** Volatile cursors and device targeting require the SDK's 0.5.17 contract. */
11
- export function supportsMenuVolatileTransport(value) {
11
+ export function supportsMenuVolatileTransport(value, capability) {
12
+ if (capability === true || capability === '1')
13
+ return true;
12
14
  const version = parseSdkVersion(value);
13
15
  if (!version)
14
16
  return false;
@@ -30,9 +32,9 @@ export function menuTransportParams(method, params) {
30
32
  return params;
31
33
  if (params.payload.type !== 'menu.response')
32
34
  return { ...params, ttl: MENU_MESSAGE_TTL_SECONDS };
33
- const { to_device_id: target, menu_client_sdk_version: sdkVersion, ...rest } = params;
35
+ const { to_device_id: target, menu_client_sdk_version: sdkVersion, menu_client_volatile_v1: capability, ...rest } = params;
34
36
  delete rest.device_id;
35
- if (!supportsMenuVolatileTransport(sdkVersion ?? params.protected_headers?.sdk_version)) {
37
+ if (!supportsMenuVolatileTransport(sdkVersion ?? params.protected_headers?.sdk_version, capability)) {
36
38
  // Evol App versions backed by fastaun < 0.5.17 do not consume the
37
39
  // volatile cursor or understand device-targeted Menu responses.
38
40
  return rest;
@@ -9,6 +9,8 @@ import { isValidAid } from '../aid/validation.js';
9
9
  import { tryParseChannelKey } from '../../core/channel-loader.js';
10
10
  import { downloadGroupFsBytes } from '../group/fs-download.js';
11
11
  import { groupBan, groupBanlist, groupBlock, groupUnblock, groupBlocklist, groupReportStatus, groupMemberStatuses, groupCreate, groupDissolve, groupInfo, groupInvite, groupJoin, groupKick, groupLeave, groupList, groupMembers, groupOnline, groupPull, groupAck, groupAcceptInvite, groupInvites, groupRejectInvite, groupRevokeInvite, groupResume, groupRules, groupRulesFileGet, groupRulesFilePublish, groupRulesFileSet, groupSetRole, groupSuspend, groupTransferOwner, groupUnban, groupUpdate, groupUpdateRules, } from '../group/operations.js';
12
+ import { handleGroupMenu, GroupMenuError } from '../../core/command/group-menu.js';
13
+ import { readRolesConfig } from '../../config/roles.js';
12
14
  /** Rules uploaded by a managed task are carried as bounded UTF-8 content. */
13
15
  export const MAX_MANAGED_GROUP_RULES_BYTES = 4 * 1024;
14
16
  const RELATION_GROUP_READ_KINDS = new Set([
@@ -20,6 +22,7 @@ const RELATION_GROUP_READ_KINDS = new Set([
20
22
  'member-statuses',
21
23
  'rules.get',
22
24
  'rules.get-legacy',
25
+ 'mention',
23
26
  ]);
24
27
  /** Read operations that a user role may perform against its current group relation. */
25
28
  export function isManagedGroupRelationRead(command) {
@@ -146,6 +149,31 @@ export function resolveManagedGroupOperation(argv) {
146
149
  if (!('positionals' in parsed))
147
150
  return parsed;
148
151
  const { positionals, values } = parsed;
152
+ if (sub === 'mention') {
153
+ const flagsError = onlyFlags(values, ['--clear']);
154
+ if (flagsError)
155
+ return flagsError;
156
+ const from = requiredAid(positionals[0], 'from AID');
157
+ if (typeof from !== 'string')
158
+ return from;
159
+ const groupId = requiredGroup(positionals[1], from);
160
+ if (typeof groupId !== 'string')
161
+ return groupId;
162
+ const mode = positionals[2];
163
+ if (positionals.length > 3)
164
+ return failGroup('INVALID_ARGUMENT', 'mention accepts <from> <group-aid> [<mode>]');
165
+ const clear = values.has('--clear');
166
+ if (mode !== undefined && clear)
167
+ return failGroup('INVALID_ARGUMENT', 'mention <mode> and --clear are mutually exclusive');
168
+ if (mode !== undefined && !['mention-only', 'disabled', 'auto'].includes(mode)) {
169
+ return failGroup('INVALID_ARGUMENT', `invalid mention mode: ${mode}`);
170
+ }
171
+ return command(sub, 'ec.group.mention', ['group', sub, from, groupId, ...(mode ? [mode] : []), ...(clear ? ['--clear'] : [])], from, {
172
+ groupId,
173
+ ...(mode ? { mode } : {}),
174
+ clear,
175
+ });
176
+ }
149
177
  if (sub === 'invites') {
150
178
  const flagsError = onlyFlags(values, ['--page', '--size']);
151
179
  if (flagsError)
@@ -297,6 +325,9 @@ export function resolveManagedGroupOperation(argv) {
297
325
  if (onlineStatus === undefined || workStatus === undefined) {
298
326
  return failGroup('INVALID_ARGUMENT', 'report-status requires --online-status and --work-status');
299
327
  }
328
+ if (![onlineStatus, workStatus].every(status => /^[a-z][a-z0-9_-]{0,63}$/i.test(status))) {
329
+ return failGroup('INVALID_ARGUMENT', 'report-status values must be single-line status identifiers');
330
+ }
300
331
  Object.assign(extra, { onlineStatus, workStatus });
301
332
  }
302
333
  else if (sub === 'members') {
@@ -510,6 +541,45 @@ export async function executeManagedGroupOperation(command, rulesContent, histor
510
541
  case 'rules.get-legacy':
511
542
  result = await groupRules({ from: command.from, groupId: command.groupId, ...opts });
512
543
  break;
544
+ case 'mention': {
545
+ try {
546
+ const menuContext = {
547
+ self: command.from,
548
+ role: readRolesConfig(command.from).defaultRoles?.group ?? 'member',
549
+ isDaemonOwner: false,
550
+ currentGroupId: command.groupId,
551
+ roleContext: {
552
+ self: command.from,
553
+ actorRole: readRolesConfig(command.from).defaultRoles?.group ?? 'member',
554
+ aunAvailable: true,
555
+ },
556
+ };
557
+ const mode = command.mode;
558
+ const clear = command.clear === true;
559
+ if (mode === undefined && !clear) {
560
+ const query = await handleGroupMenu({ kind: 'query', args: { view: 'mentionMode', scope: 'relation', groupId: command.groupId } }, menuContext);
561
+ result = { ok: true, ...(query && typeof query === 'object' ? query : {}) };
562
+ }
563
+ else {
564
+ const current = await handleGroupMenu({ kind: 'query', args: { view: 'mentionMode', scope: 'relation', groupId: command.groupId } }, menuContext);
565
+ const revision = typeof current?.revision === 'string' ? current.revision : '';
566
+ if (!revision)
567
+ throw new ManagedGroupOperationError('INVALID_REVISION', 'unable to resolve current mentionMode revision');
568
+ const updated = await handleGroupMenu({
569
+ kind: 'update',
570
+ args: { resource: 'mentionMode', scope: 'relation', groupId: command.groupId, expectedRevision: revision },
571
+ value: clear ? 'clear' : mode,
572
+ }, menuContext);
573
+ result = { ok: true, ...(updated && typeof updated === 'object' ? updated : {}) };
574
+ }
575
+ }
576
+ catch (error) {
577
+ if (error instanceof GroupMenuError)
578
+ throw new ManagedGroupOperationError(error.code, error.message);
579
+ throw error;
580
+ }
581
+ break;
582
+ }
513
583
  default: throw new ManagedGroupOperationError('NOT_ALLOWED', `unsupported managed group operation: ${command.kind}`);
514
584
  }
515
585
  if (!result || result.ok !== true) {
@@ -4,7 +4,7 @@ import { createShortConnection } from '../rpc/index.js';
4
4
  import { getAidStore, SLOT } from '../aid/store.js';
5
5
  import { uploadFileAndBuildPayload } from './upload.js';
6
6
  import { appendMessageLog, buildOutboundEntry, classifyAunPayloadForLog } from '../../core/message/message-log.js';
7
- import { normalizeCodexTextPayload, readBestTaskRuntimeContext, runtimeRefMessageIdForMsgSend } from '../../cli/task-context.js';
7
+ import { readBestTaskRuntimeContext, runtimeRefMessageIdForMsgSend } from '../../cli/task-context.js';
8
8
  import { chatDirPath } from '../../core/session/session-fs-store.js';
9
9
  import { resolvePaths } from '../../paths.js';
10
10
  import { ipcQuery } from '../../ipc.js';
@@ -229,7 +229,6 @@ export async function msgSend(args) {
229
229
  let payload;
230
230
  if (args.body.mode !== 'file') {
231
231
  payload = withAunAttention(buildSimplePayload(args.body));
232
- normalizeCodexTextPayload(payload, runtimeContext);
233
232
  if (Object.prototype.hasOwnProperty.call(payload, 'mentions')) {
234
233
  payload.mentions = normalizeAunMentionEntries(payload.mentions);
235
234
  }
@@ -286,7 +285,6 @@ export async function msgSend(args) {
286
285
  // 3. 构建 payload
287
286
  if (!payload) {
288
287
  payload = buildSimplePayload(args.body);
289
- normalizeCodexTextPayload(payload, runtimeContext);
290
288
  }
291
289
  // 4. 写入 payload.chatmode
292
290
  payload.chatmode = chatmode;
@@ -27,8 +27,8 @@ export function inferPayloadType(filename) {
27
27
  export function isValidPayloadType(value) {
28
28
  return value === 'image' || value === 'video' || value === 'voice' || value === 'file';
29
29
  }
30
- /** 单次上传最大大小(与 daemon sendFile 一致)。 */
31
- export const MAX_AUN_ATTACHMENT_SIZE = 10 * 1024 * 1024;
30
+ /** 单次上传最大大小(与 daemon sendFile 和 Menu file fetch 一致)。 */
31
+ export const MAX_AUN_ATTACHMENT_SIZE = 256 * 1024 * 1024;
32
32
  /**
33
33
  * 上传内存中的二进制内容并构造发送用的标准附件 payload。
34
34
  */