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.
- package/CHANGELOG.md +12 -0
- package/bin/ec-safe-output.js +270 -54
- package/dist/agents/baseagent-availability.js +37 -1
- package/dist/agents/baseagent.js +19 -9
- package/dist/agents/claude-runner.js +171 -13
- package/dist/agents/codex/app-server-client.js +6 -2
- package/dist/agents/codex/diagnostics.js +4 -3
- package/dist/agents/codex-runner.js +396 -95
- package/dist/agents/gemini-runner.js +35 -17
- package/dist/aun/group/history.js +130 -90
- package/dist/aun/group/operations.js +1 -3
- package/dist/aun/menu/transport.js +5 -3
- package/dist/aun/msg/managed-operation.js +70 -0
- package/dist/aun/msg/p2p.js +1 -3
- package/dist/aun/msg/upload.js +2 -2
- package/dist/aun/outbox.js +1 -37
- package/dist/channels/aun.js +249 -89
- package/dist/channels/daemon.js +18 -1
- package/dist/channels/feishu.js +1 -2
- package/dist/cli/agent-command.js +23 -3
- package/dist/cli/agent-create.js +68 -2
- package/dist/cli/agent.js +12 -2
- package/dist/cli/aun-commands.js +87 -13
- package/dist/cli/cli-argv.js +26 -0
- package/dist/cli/daemon-commands.js +40 -10
- package/dist/cli/data-command.js +4 -4
- package/dist/cli/handoff-command.js +2 -0
- package/dist/cli/index.js +18 -1
- package/dist/cli/managed-command-guard.js +10 -0
- package/dist/cli/memory-command.js +441 -0
- package/dist/cli/restart-monitor.js +62 -6
- package/dist/cli/skill-command.js +41 -0
- package/dist/cli/task-context.js +2 -23
- package/dist/cli/trigger-command.js +9 -1
- package/dist/cli/watch-msg-diag.js +209 -0
- package/dist/config/config-field-policy.js +52 -3
- package/dist/config/config-manager.js +17 -4
- package/dist/config/contact-book-store.js +2 -2
- package/dist/config/mention-mode.js +7 -2
- package/dist/config/merge.js +28 -9
- package/dist/config/resolved-config-op.js +1 -1
- package/dist/config/runtime-env.js +137 -0
- package/dist/config/schema-registry.js +11 -1
- package/dist/config-store.js +10 -10
- package/dist/core/audit/event-key.js +3 -1
- package/dist/core/audit/log-integrity.js +4 -0
- package/dist/core/auth/agent-delegation.js +2 -1
- package/dist/core/auth/auth-gateway.js +13 -6
- package/dist/core/auth/authorization-audit.js +54 -19
- package/dist/core/auth/operation-authorizer.js +17 -0
- package/dist/core/auth/operation-catalog.js +9 -0
- package/dist/core/causation/context.js +5 -1
- package/dist/core/command/agent-control.js +1 -2
- package/dist/core/command/command-handler.js +51 -23
- package/dist/core/command/memory-slash.js +270 -0
- package/dist/core/command/menu-catalog.js +1 -0
- package/dist/core/command/menu-handler.js +315 -60
- package/dist/core/command/menu-protocol.js +42 -4
- package/dist/core/command/slash-gate.js +10 -10
- package/dist/core/command/slash-handler.js +206 -64
- package/dist/core/data-migration.js +282 -505
- package/dist/core/event-catalog.js +26 -0
- package/dist/core/evolagent-registry.js +6 -0
- package/dist/core/evolagent.js +0 -10
- package/dist/core/handoff/store.js +1 -20
- package/dist/core/inference/text-inference.js +57 -1
- package/dist/core/memory/consolidation-context.js +505 -0
- package/dist/core/memory/consolidation-ops-records.js +124 -0
- package/dist/core/memory/consolidation-trigger.js +394 -0
- package/dist/core/memory/index-manager.js +308 -0
- package/dist/core/memory/launch-consolidation.js +395 -0
- package/dist/core/memory/memory-admin.js +234 -0
- package/dist/core/memory/memory-document.js +100 -0
- package/dist/core/memory/memory-session-policy.js +41 -0
- package/dist/core/memory/memory-signature.js +257 -0
- package/dist/core/memory/memory-writer.js +432 -0
- package/dist/core/memory/staleness.js +117 -0
- package/dist/core/message/auto-mention-gate.js +454 -0
- package/dist/core/message/im-renderer.js +69 -16
- package/dist/core/message/message-bridge.js +287 -21
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/message-queue.js +114 -27
- package/dist/core/message/pending-hints.js +329 -161
- package/dist/core/message/response-engine.js +567 -80
- package/dist/core/permission/ec-command-parser.js +1029 -80
- package/dist/core/permission/protected-paths.js +376 -126
- package/dist/core/permission/sandbox-runtime.js +40 -12
- package/dist/core/permission/tool-error-code.js +96 -13
- package/dist/core/permission/tool-policy.js +276 -23
- package/dist/core/role/runtime-policy.js +3 -7
- package/dist/core/session/{runner-task-history.js → runner-event-summary.js} +1 -1
- package/dist/core/session/session-fs-store.js +0 -76
- package/dist/core/session/session-idle-monitor.js +355 -0
- package/dist/core/session/session-manager.js +478 -17
- package/dist/core/session/session-mapper.js +33 -16
- package/dist/core/session/session-renew.js +217 -155
- package/dist/core/session/{session-turns.js → session-transcript.js} +35 -6
- package/dist/core/skill/global-watcher.js +282 -0
- package/dist/core/skill/index-generator.js +84 -0
- package/dist/core/skill/startup.js +73 -0
- package/dist/core/skill/switch-resolver.js +115 -0
- package/dist/core/task/task-activity-store.js +219 -0
- package/dist/eck/kit-renderer.js +30 -3
- package/dist/eck/manifest-engine.js +26 -4
- package/dist/index.js +708 -18
- package/dist/ipc.js +564 -2
- package/dist/paths.js +30 -9
- package/dist/response-system/engines/v1/proactive-flow.js +116 -15
- package/dist/response-system/modes/single-session/index.js +3 -0
- package/dist/trigger/feedback.js +1 -0
- package/dist/trigger/manager.js +0 -10
- package/dist/trigger/scheduler.js +11 -0
- package/dist/trigger/validation.js +22 -0
- package/dist/utils/avatar-upload.js +59 -18
- package/dist/utils/error-dict.json +6 -3
- package/dist/utils/error-utils.js +27 -0
- package/dist/utils/logger.js +32 -10
- package/dist/utils/media-cache.js +84 -0
- package/dist/utils/msg-diag.js +97 -0
- package/dist/utils/welcome.js +37 -8
- package/kits/docs/AUTHORING.md +314 -0
- package/kits/docs/GUIDE.md +2 -0
- package/kits/docs/INDEX.md +2 -0
- package/kits/docs/aun/CHEATSHEET.md +4 -0
- package/kits/docs/aun/SDK.md +60 -0
- package/kits/docs/context-assembly.md +10 -6
- package/kits/docs/evolcore/INDEX.md +17 -3
- package/kits/docs/evolcore/agent-create.md +24 -0
- package/kits/docs/evolcore/agent.md +5 -2
- package/kits/docs/evolcore/config.md +1 -1
- package/kits/docs/evolcore/ec-capabilities.md +364 -0
- package/kits/docs/evolcore/group.md +34 -18
- package/kits/docs/evolcore/memory-architecture.md +78 -0
- package/kits/docs/evolcore/memory-consolidation-batches.md +133 -0
- package/kits/docs/evolcore/memory-consolidation.md +263 -0
- package/kits/docs/evolcore/memory-tags.md +63 -0
- package/kits/docs/evolcore/memory.md +110 -0
- package/kits/docs/evolcore/menu-client.md +27 -11
- package/kits/{schemas → docs/evolcore}/menu-exec-schema-commands.md +12 -6
- package/kits/docs/evolcore/menu-protocol.md +213 -0
- package/kits/docs/evolcore/msg.md +2 -0
- package/kits/docs/evolcore/trigger.md +7 -5
- package/kits/docs/path-registry.md +6 -8
- package/kits/docs/prompt-loading-architecture.md +5 -3
- package/kits/docs/venues/group.md +5 -4
- package/kits/eck_manifest.json +294 -88
- package/kits/eck_manifest.memory.json +257 -0
- package/kits/rules/03-identity.md +8 -8
- package/kits/rules/05-venue.md +4 -2
- package/kits/rules/06-channel.md +1 -1
- package/kits/schemas/_meta.json +276 -47
- package/kits/schemas/agent-config.schema.12.json +38 -2
- package/kits/schemas/agent-config.schema.13.json +591 -0
- package/kits/schemas/daemon.schema.6.json +9 -9
- package/kits/schemas/defaults.schema.5.json +29 -0
- package/kits/schemas/relation-config.schema.8.json +14 -1
- package/kits/schemas/relation-config.schema.9.json +129 -0
- package/kits/schemas/role-config.schema.1.json +3 -3
- package/kits/skills/.switches.json +3 -0
- package/kits/skills/ec-skill/SKILL.md +217 -0
- package/kits/skills/ec-skill/scripts/cli.mjs +128 -0
- package/kits/skills/ec-skill/scripts/deps.mjs +107 -0
- package/kits/skills/ec-skill/scripts/frontmatter.mjs +104 -0
- package/kits/skills/ec-skill/scripts/skill.mjs +629 -0
- package/kits/skills/ec-skill/scripts/template/SKILL.md.tmpl +30 -0
- package/kits/templates/decision/auto-mention.md +25 -0
- package/kits/templates/decision/session-renew.md +22 -0
- package/kits/templates/memory-consolidation/forget.md +104 -0
- package/kits/templates/memory-consolidation/generalize.md +76 -0
- package/kits/templates/memory-consolidation/notes.md +133 -0
- package/kits/templates/memory-consolidation/profile.md +58 -0
- package/kits/templates/memory-consolidation/promote.md +72 -0
- package/kits/templates/memory-consolidation/workflow.md +75 -0
- package/kits/templates/system-fragments/bootstrap.md +14 -5
- package/kits/templates/system-fragments/commands.md +1 -0
- package/kits/templates/system-fragments/group-history.md +6 -0
- package/kits/templates/system-fragments/identity.md +0 -3
- package/kits/templates/system-fragments/memory.md +79 -0
- package/kits/templates/system-fragments/reply.md +1 -0
- package/kits/templates/system-fragments/venue.md +1 -1
- package/package.json +4 -6
- package/dist/trigger/legacy-session-history.js +0 -19
- 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 `
|
|
107
|
-
String.raw `
|
|
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
|
|
123
|
-
String.raw
|
|
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
|
|
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
|
|
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 {
|
|
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
|
|
30
|
-
|
|
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 (
|
|
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,
|
|
82
|
-
limit, direction, before,
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
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
|
-
|
|
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
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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:
|
|
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 {
|
|
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) {
|
package/dist/aun/msg/p2p.js
CHANGED
|
@@ -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 {
|
|
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;
|
package/dist/aun/msg/upload.js
CHANGED
|
@@ -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 =
|
|
30
|
+
/** 单次上传最大大小(与 daemon sendFile 和 Menu file fetch 一致)。 */
|
|
31
|
+
export const MAX_AUN_ATTACHMENT_SIZE = 256 * 1024 * 1024;
|
|
32
32
|
/**
|
|
33
33
|
* 上传内存中的二进制内容并构造发送用的标准附件 payload。
|
|
34
34
|
*/
|