evolclaw 3.0.0 → 3.1.1
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/README.md +1 -1
- package/bin/ec.js +29 -0
- package/dist/agents/baseagent-normalize.js +19 -0
- package/dist/agents/claude-runner.js +47 -12
- package/dist/agents/codex-runner.js +2 -0
- package/dist/agents/gemini-runner.js +9 -9
- package/dist/agents/kit-renderer.js +281 -0
- package/dist/aun/aid/identity.js +28 -0
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/aid/lifecycle-log.js +33 -0
- package/dist/aun/msg/group.js +3 -1
- package/dist/aun/msg/p2p.js +42 -1
- package/dist/channels/aun.js +427 -146
- package/dist/channels/dingtalk.js +3 -1
- package/dist/channels/feishu.js +128 -7
- package/dist/channels/qqbot.js +3 -1
- package/dist/channels/wechat.js +4 -1
- package/dist/channels/wecom.js +3 -1
- package/dist/cli/bench.js +1219 -0
- package/dist/cli/index.js +418 -40
- package/dist/cli/init.js +3 -4
- package/dist/cli/link-rules.js +245 -0
- package/dist/cli/net-check.js +640 -0
- package/dist/cli/watch-msg.js +666 -0
- package/dist/config-store.js +82 -5
- package/dist/core/channel-loader.js +23 -10
- package/dist/core/command-handler.js +127 -99
- package/dist/core/evolagent.js +5 -10
- package/dist/core/message/im-renderer.js +93 -48
- package/dist/core/message/items-formatter.js +11 -4
- package/dist/core/message/message-bridge.js +11 -2
- package/dist/core/message/message-log.js +8 -1
- package/dist/core/message/message-processor.js +194 -127
- package/dist/core/message/message-queue.js +10 -3
- package/dist/core/permission.js +95 -3
- package/dist/core/relation/peer-identity.js +161 -0
- package/dist/core/session/session-manager.js +103 -65
- package/dist/core/trigger/manager.js +16 -0
- package/dist/core/trigger/parser.js +110 -0
- package/dist/core/trigger/scheduler.js +7 -1
- package/dist/data/error-dict.json +118 -0
- package/dist/eck/baseagent-caps.js +18 -0
- package/dist/eck/detect.js +47 -0
- package/dist/eck/init.js +77 -0
- package/dist/eck/rules-loader.js +28 -0
- package/dist/index.js +186 -19
- package/dist/net-check.js +640 -0
- package/dist/paths.js +31 -40
- package/dist/utils/aid-lifecycle-log.js +33 -0
- package/dist/utils/atomic-write.js +10 -0
- package/dist/utils/cross-platform.js +17 -8
- package/dist/utils/error-utils.js +27 -15
- package/dist/utils/instance-registry.js +6 -5
- package/dist/utils/log-writer.js +2 -1
- package/dist/utils/logger.js +10 -0
- package/dist/utils/npm-ops.js +35 -3
- package/dist/utils/process-introspect.js +16 -38
- package/dist/utils/stats.js +216 -2
- package/dist/watch-msg.js +26 -11
- package/evolclaw-install-aun.md +14 -2
- package/kits/docs/GUIDE.md +20 -0
- package/kits/docs/INDEX.md +52 -0
- package/kits/docs/aun/CHEATSHEET.md +17 -0
- package/kits/docs/aun/SYNC_PROTOCOL.md +15 -0
- package/kits/docs/channels/feishu.md +27 -0
- package/kits/docs/eck_templates/GUIDE.template.md +22 -0
- package/kits/docs/eck_templates/INDEX.template.md +28 -0
- package/kits/docs/eck_templates/path-registry.template.md +33 -0
- package/kits/docs/eck_templates/runtime.template.md +19 -0
- package/kits/docs/evolclaw/MSG_GROUP.md +30 -0
- package/kits/docs/evolclaw/MSG_PRIVATE.md +72 -0
- package/kits/docs/identity/AID_PROFILE_SPEC.md +27 -0
- package/kits/docs/identity/PATH_OPS.md +16 -0
- package/kits/docs/identity/ROLE_DETAIL.md +20 -0
- package/kits/docs/path-registry.md +43 -0
- package/kits/eck_manifest.json +95 -0
- package/kits/rules/01-overview.md +120 -0
- package/kits/rules/02-navigation.md +75 -0
- package/kits/rules/03-identity.md +34 -0
- package/kits/rules/04-relation.md +49 -0
- package/kits/rules/05-venue.md +45 -0
- package/kits/rules/06-channel.md +73 -0
- package/kits/templates/system-fragments/baseagent.md +2 -0
- package/kits/templates/system-fragments/channel.md +10 -0
- package/kits/templates/system-fragments/identity.md +12 -0
- package/kits/templates/system-fragments/relation.md +9 -0
- package/kits/templates/system-fragments/runtime.md +19 -0
- package/kits/templates/system-fragments/venue.md +5 -0
- package/package.json +7 -5
- package/dist/agents/templates.js +0 -122
- package/dist/data/prompts.md +0 -137
- package/kits/aun/meta.md +0 -25
- package/kits/aun/role.md +0 -25
- package/kits/templates/group.md +0 -20
- package/kits/templates/private.md +0 -9
- package/kits/templates/system-fragments/personal-context.md +0 -3
- package/kits/templates/system-fragments/self-intro.md +0 -5
- package/kits/templates/system-fragments/speaker-intro.md +0 -5
- package/kits/templates/system-fragments/venue-intro.md +0 -5
- /package/kits/{channels → docs/channels}/aun.md +0 -0
- /package/kits/{evolclaw/commands.md → docs/evolclaw/AGENT_CMD.md} +0 -0
- /package/kits/{evolclaw → docs/evolclaw}/self-summary.md +0 -0
- /package/kits/{evolclaw → docs/evolclaw}/tools.md +0 -0
- /package/kits/{evolclaw → docs/identity}/identity-tools.md +0 -0
package/dist/config-store.js
CHANGED
|
@@ -75,8 +75,62 @@ export function loadDefaults() {
|
|
|
75
75
|
return expandEnvRefs(raw);
|
|
76
76
|
}
|
|
77
77
|
export function saveDefaults(value) {
|
|
78
|
+
backupDefaults(resolvePaths().defaultsConfig);
|
|
78
79
|
atomicWriteJson(resolvePaths().defaultsConfig, value);
|
|
79
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* 备份 defaults.json 为 defaults_YYYYMMDDhhmmss.json。文件不存在时为 no-op。
|
|
83
|
+
* 同秒重复调用会被覆盖(同一秒内的内容相同,可接受)。
|
|
84
|
+
*/
|
|
85
|
+
function backupDefaults(filePath) {
|
|
86
|
+
if (!fs.existsSync(filePath))
|
|
87
|
+
return;
|
|
88
|
+
const now = new Date();
|
|
89
|
+
const ts = now.getFullYear().toString()
|
|
90
|
+
+ String(now.getMonth() + 1).padStart(2, '0')
|
|
91
|
+
+ String(now.getDate()).padStart(2, '0')
|
|
92
|
+
+ String(now.getHours()).padStart(2, '0')
|
|
93
|
+
+ String(now.getMinutes()).padStart(2, '0')
|
|
94
|
+
+ String(now.getSeconds()).padStart(2, '0');
|
|
95
|
+
const backupPath = path.join(path.dirname(filePath), `defaults_${ts}.json`);
|
|
96
|
+
try {
|
|
97
|
+
fs.copyFileSync(filePath, backupPath);
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
logger.warn(`[config] backup failed: ${backupPath}: ${e}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* 安全写入 defaults.json:备份现有文件 → 深合并 patch → 原子写入。
|
|
105
|
+
*
|
|
106
|
+
* 与 saveDefaults() 不同,本函数保留现有字段,仅覆盖 patch 中显式指定的字段。
|
|
107
|
+
* 适用场景:evolclaw init 仅修改 active_baseagent/baseagents 时,不应丢失 chatmode/projects 等其它字段。
|
|
108
|
+
*/
|
|
109
|
+
export function saveDefaultsSafe(patch) {
|
|
110
|
+
const p = resolvePaths().defaultsConfig;
|
|
111
|
+
let existing = null;
|
|
112
|
+
try {
|
|
113
|
+
existing = atomicReadJson(p);
|
|
114
|
+
}
|
|
115
|
+
catch (e) {
|
|
116
|
+
logger.warn(`[config] existing defaults.json unparsable, will be backed up and replaced: ${e}`);
|
|
117
|
+
}
|
|
118
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
119
|
+
backupDefaults(p);
|
|
120
|
+
const merged = existing
|
|
121
|
+
? deepMergeObject(existing, patch)
|
|
122
|
+
: { $schema_version: CONFIG_SCHEMA_VERSION, ...patch };
|
|
123
|
+
atomicWriteJson(p, merged);
|
|
124
|
+
}
|
|
125
|
+
export function loadProcessConfig() {
|
|
126
|
+
const raw = atomicReadJson(resolvePaths().processConfig);
|
|
127
|
+
if (raw === null)
|
|
128
|
+
return {};
|
|
129
|
+
return expandEnvRefs(raw);
|
|
130
|
+
}
|
|
131
|
+
export function saveProcessConfig(value) {
|
|
132
|
+
atomicWriteJson(resolvePaths().processConfig, value);
|
|
133
|
+
}
|
|
80
134
|
// ── 自动迁移 ───────────────────────────────────────────────────────────
|
|
81
135
|
/**
|
|
82
136
|
* 启动时自动迁移:如果 agents/defaults.json 不存在但 data/evolclaw.json 存在,
|
|
@@ -455,21 +509,44 @@ export function ensureAgentDirSkeleton(aid) {
|
|
|
455
509
|
const root = agentDir(aid);
|
|
456
510
|
const subs = [
|
|
457
511
|
'personal',
|
|
458
|
-
'
|
|
459
|
-
'
|
|
460
|
-
'
|
|
461
|
-
'
|
|
462
|
-
'
|
|
512
|
+
'personal/memory',
|
|
513
|
+
'personal/skills',
|
|
514
|
+
'relations/contacts',
|
|
515
|
+
'relations/_observed',
|
|
516
|
+
'relations/_observed/_index',
|
|
517
|
+
'relations/_index',
|
|
518
|
+
'relations/_trash',
|
|
463
519
|
'venues',
|
|
464
520
|
'venues/_index',
|
|
465
521
|
'venues/_trash',
|
|
466
522
|
'sessions',
|
|
467
523
|
'data/cache',
|
|
524
|
+
'index',
|
|
468
525
|
];
|
|
469
526
|
for (const sub of subs) {
|
|
470
527
|
fs.mkdirSync(path.join(root, sub), { recursive: true });
|
|
471
528
|
}
|
|
472
529
|
}
|
|
530
|
+
// ── identities → relations 迁移 ──────────────────────────────────────
|
|
531
|
+
/**
|
|
532
|
+
* 启动时自动迁移:如果 agents/<aid>/identities/ 存在但 relations/ 不存在,
|
|
533
|
+
* 将 identities/ 重命名为 relations/。幂等。
|
|
534
|
+
*/
|
|
535
|
+
export function migrateIdentitiesIfNeeded() {
|
|
536
|
+
const agentsDir = resolvePaths().agentsDir;
|
|
537
|
+
if (!fs.existsSync(agentsDir))
|
|
538
|
+
return;
|
|
539
|
+
for (const entry of fs.readdirSync(agentsDir, { withFileTypes: true })) {
|
|
540
|
+
if (!entry.isDirectory())
|
|
541
|
+
continue;
|
|
542
|
+
const oldDir = path.join(agentsDir, entry.name, 'identities');
|
|
543
|
+
const newDir = path.join(agentsDir, entry.name, 'relations');
|
|
544
|
+
if (fs.existsSync(oldDir) && !fs.existsSync(newDir)) {
|
|
545
|
+
fs.renameSync(oldDir, newDir);
|
|
546
|
+
logger.info(`[migrate] Renamed agents/${entry.name}/identities/ → relations/`);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
473
550
|
// ── Project Migration ────────────────────────────────────────────────────────
|
|
474
551
|
import os from 'os';
|
|
475
552
|
/** 将绝对路径编码为 Claude Code 的目录名格式(/ \ . 替换为 -) */
|
|
@@ -89,23 +89,36 @@ export class ChannelLoader {
|
|
|
89
89
|
}
|
|
90
90
|
return instances;
|
|
91
91
|
}
|
|
92
|
-
async connectAll(instances,
|
|
92
|
+
async connectAll(instances, { concurrency = 3, intervalMs = 50 } = {}) {
|
|
93
93
|
const connected = [];
|
|
94
94
|
const failed = [];
|
|
95
|
+
const inflight = new Set();
|
|
95
96
|
for (const inst of instances) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
catch (e) {
|
|
101
|
-
failed.push(e);
|
|
97
|
+
// 等待并发数降到 concurrency 以下
|
|
98
|
+
while (inflight.size >= concurrency) {
|
|
99
|
+
await Promise.race(inflight);
|
|
102
100
|
}
|
|
103
|
-
|
|
104
|
-
|
|
101
|
+
const task = (async () => {
|
|
102
|
+
try {
|
|
103
|
+
await inst.connect();
|
|
104
|
+
connected.push(inst.adapter.channelName);
|
|
105
|
+
}
|
|
106
|
+
catch (e) {
|
|
107
|
+
failed.push({ name: inst.adapter.channelName, error: e });
|
|
108
|
+
logger.warn(`[connectAll] ${inst.adapter.channelName} connect failed: ${e}`);
|
|
109
|
+
}
|
|
110
|
+
})();
|
|
111
|
+
const tracked = task.then(() => { inflight.delete(tracked); });
|
|
112
|
+
inflight.add(tracked);
|
|
113
|
+
// 间隔发起,避免瞬间并发冲击网关
|
|
114
|
+
if (intervalMs > 0) {
|
|
115
|
+
await new Promise(r => setTimeout(r, intervalMs));
|
|
105
116
|
}
|
|
106
117
|
}
|
|
118
|
+
// 等待所有剩余任务完成
|
|
119
|
+
await Promise.allSettled(inflight);
|
|
107
120
|
if (failed.length > 0) {
|
|
108
|
-
logger.warn(`
|
|
121
|
+
logger.warn(`[connectAll] ${failed.length} channel(s) failed initial connect (will retry in background): ${failed.map(f => f.name).join(', ')}`);
|
|
109
122
|
}
|
|
110
123
|
return connected;
|
|
111
124
|
}
|
|
@@ -8,8 +8,9 @@ import crypto from 'crypto';
|
|
|
8
8
|
import path from 'path';
|
|
9
9
|
import fs from 'fs';
|
|
10
10
|
import os from 'os';
|
|
11
|
-
import { parseTriggerSet } from './trigger/parser.js';
|
|
11
|
+
import { parseTriggerSet, parseTriggerUpdate } from './trigger/parser.js';
|
|
12
12
|
import { calcNextFireAt } from './trigger/scheduler.js';
|
|
13
|
+
import { checkLatestVersion, getLocalVersion, isLinkedInstall, compareVersions } from '../utils/npm-ops.js';
|
|
13
14
|
const allEfforts = ['low', 'medium', 'high', 'max'];
|
|
14
15
|
const nonMaxEfforts = allEfforts.filter(e => e !== 'max');
|
|
15
16
|
function getAvailableEfforts(agent, model) {
|
|
@@ -23,17 +24,8 @@ function getAvailableEfforts(agent, model) {
|
|
|
23
24
|
}
|
|
24
25
|
return [];
|
|
25
26
|
}
|
|
26
|
-
function formatModelUsage(
|
|
27
|
-
|
|
28
|
-
const lines = [
|
|
29
|
-
'用法:',
|
|
30
|
-
' /model <模型> 切换模型',
|
|
31
|
-
];
|
|
32
|
-
if (efforts.length > 0) {
|
|
33
|
-
lines.push(' /model <模型> <强度> 切换模型+推理强度');
|
|
34
|
-
lines.push(' /effort [level] 查看或切换推理强度');
|
|
35
|
-
}
|
|
36
|
-
return lines.join('\n');
|
|
27
|
+
function formatModelUsage(_agent, _model) {
|
|
28
|
+
return '用法: /model <模型>';
|
|
37
29
|
}
|
|
38
30
|
/**
|
|
39
31
|
* 写入用户级 ~/.claude/settings.json(与 Claude CLI 行为一致)
|
|
@@ -108,7 +100,7 @@ function formatIdleTime(ms) {
|
|
|
108
100
|
return '刚刚';
|
|
109
101
|
}
|
|
110
102
|
// 支持的命令列表
|
|
111
|
-
const commands = ['/new', '/pwd', '/plist', '/project', '/bind', '/help', '/evolhelp', '/status', '/restart', '/model', '/setmodel', '/effort', '/agent', '/slist', '/session', '/rename', '/stop', '/clear', '/compact', '/repair', '/safe', '/fork', '/del', '/perm', '/file', '/check', '/rewind', '/activity', '/chatmode', '/dispatch', '/ask', '/resume', '/aid', '/rpc', '/storage', '/trigger'];
|
|
103
|
+
const commands = ['/new', '/pwd', '/plist', '/project', '/bind', '/help', '/evolhelp', '/status', '/restart', '/model', '/setmodel', '/effort', '/agent', '/slist', '/session', '/rename', '/stop', '/clear', '/compact', '/repair', '/safe', '/fork', '/del', '/perm', '/file', '/check', '/rewind', '/activity', '/chatmode', '/dispatch', '/ask', '/resume', '/aid', '/rpc', '/storage', '/trigger', '/upgrade'];
|
|
112
104
|
// 命令别名映射
|
|
113
105
|
const aliases = {
|
|
114
106
|
'/p': '/project',
|
|
@@ -117,7 +109,7 @@ const aliases = {
|
|
|
117
109
|
'/rw': '/rewind'
|
|
118
110
|
};
|
|
119
111
|
// 命令快速路径前缀(所有命令都不进入消息队列)
|
|
120
|
-
const quickCommandPrefixes = ['/new', '/pwd', '/plist', '/project', '/bind', '/help', '/evolhelp', '/status', '/restart', '/model', '/setmodel', '/effort', '/agent', '/slist', '/session', '/rename', '/repair', '/fork', '/stop', '/clear', '/compact', '/safe', '/del', '/perm', '/file', '/check', '/p ', '/s ', '/name', '/rewind', '/rw', '/rw ', '/activity', '/chatmode', '/dispatch', '/ask', '/resume', '/aid', '/rpc', '/storage', '/trigger'];
|
|
112
|
+
const quickCommandPrefixes = ['/new', '/pwd', '/plist', '/project', '/bind', '/help', '/evolhelp', '/status', '/restart', '/model', '/setmodel', '/effort', '/agent', '/slist', '/session', '/rename', '/repair', '/fork', '/stop', '/clear', '/compact', '/safe', '/del', '/perm', '/file', '/check', '/p ', '/s ', '/name', '/rewind', '/rw', '/rw ', '/activity', '/chatmode', '/dispatch', '/ask', '/resume', '/aid', '/rpc', '/storage', '/trigger', '/upgrade'];
|
|
121
113
|
export class CommandHandler {
|
|
122
114
|
sessionManager;
|
|
123
115
|
messageCache;
|
|
@@ -340,9 +332,6 @@ export class CommandHandler {
|
|
|
340
332
|
return renderCommandCardAsText(card);
|
|
341
333
|
if (!adapter?.send)
|
|
342
334
|
return renderCommandCardAsText(card);
|
|
343
|
-
// session 忙碌时降级到文本,避免并发触发带参写操作
|
|
344
|
-
if (this.isSessionBusy(opts.interaction.sessionId))
|
|
345
|
-
return renderCommandCardAsText(card);
|
|
346
335
|
try {
|
|
347
336
|
const envelope = buildEnvelope({
|
|
348
337
|
channel: opts.channel,
|
|
@@ -382,14 +371,6 @@ export class CommandHandler {
|
|
|
382
371
|
});
|
|
383
372
|
return { matched: true, result: '✓ 已回答' };
|
|
384
373
|
}
|
|
385
|
-
/** 判断指定 session 是否有活跃流(用于 idle 守卫和卡片降级) */
|
|
386
|
-
isSessionBusy(sessionId) {
|
|
387
|
-
for (const agent of this.agentMap.values()) {
|
|
388
|
-
if (agent.hasActiveStream(sessionId))
|
|
389
|
-
return true;
|
|
390
|
-
}
|
|
391
|
-
return false;
|
|
392
|
-
}
|
|
393
374
|
/** 获取活跃会话,无会话时自动创建(话题除外) */
|
|
394
375
|
async ensureSession(channel, channelId, threadId, chatType) {
|
|
395
376
|
if (threadId) {
|
|
@@ -526,7 +507,7 @@ export class CommandHandler {
|
|
|
526
507
|
] } },
|
|
527
508
|
{ cmd: '/dispatch', label: '切换分发模式', desc: '控制群聊消息过滤(仅@提及或广播响应)', next: { type: 'select', items: [
|
|
528
509
|
{ value: 'mention', label: '@ 提及', desc: '仅在被 @ 提及时响应' },
|
|
529
|
-
{ value: '
|
|
510
|
+
{ value: 'broadcast', label: '广播', desc: '响应群内所有消息' },
|
|
530
511
|
] } },
|
|
531
512
|
]
|
|
532
513
|
});
|
|
@@ -694,14 +675,14 @@ export class CommandHandler {
|
|
|
694
675
|
return { data: { mode: arg } };
|
|
695
676
|
}
|
|
696
677
|
if (cmdBase === '/dispatch') {
|
|
697
|
-
const currentMode = session.metadata?.dispatchMode
|
|
678
|
+
const currentMode = session.metadata?.dispatchMode;
|
|
698
679
|
if (mode === 'query') {
|
|
699
|
-
return { data: { mode: currentMode } };
|
|
680
|
+
return { data: { mode: currentMode ?? null } };
|
|
700
681
|
}
|
|
701
682
|
// update
|
|
702
683
|
if (!arg)
|
|
703
684
|
return { error: '缺少目标模式' };
|
|
704
|
-
if (arg !== 'mention' && arg !== '
|
|
685
|
+
if (arg !== 'mention' && arg !== 'broadcast')
|
|
705
686
|
return { error: `无效模式: ${arg}` };
|
|
706
687
|
const identity = this.sessionManager.resolveIdentity(channel, userId);
|
|
707
688
|
const chatType = session.chatType || 'private';
|
|
@@ -893,7 +874,7 @@ export class CommandHandler {
|
|
|
893
874
|
'💬 聊天设置:',
|
|
894
875
|
' /activity [all|dm|owner|none] - 查看/控制中间输出显示模式',
|
|
895
876
|
' /chatmode [interactive|proactive] - 查看/切换会话模式(被动响应或主动推进)',
|
|
896
|
-
' /dispatch [mention|
|
|
877
|
+
' /dispatch [mention|broadcast] - 查看/切换群聊分发模式(仅@响应或广播响应,仅群聊)',
|
|
897
878
|
'',
|
|
898
879
|
'🔐 权限管理:',
|
|
899
880
|
' /perm - 查看当前权限模式',
|
|
@@ -970,7 +951,7 @@ export class CommandHandler {
|
|
|
970
951
|
// 聊天设置
|
|
971
952
|
if (isAdmin) {
|
|
972
953
|
cmds.push({ command: '/chatmode', args: '[interactive|proactive]', description: '查看/切换会话模式(被动响应或主动推进)', category: '聊天设置', roles: ['admin', 'owner'] });
|
|
973
|
-
cmds.push({ command: '/dispatch', args: '[mention|
|
|
954
|
+
cmds.push({ command: '/dispatch', args: '[mention|broadcast]', description: '查看/切换群聊分发模式(仅@响应或广播响应)', category: '聊天设置', roles: ['admin', 'owner'] });
|
|
974
955
|
}
|
|
975
956
|
// 交互
|
|
976
957
|
cmds.push({ command: '/ask', args: '<选项>', description: '回答 Agent 的交互式问题', category: '运维', roles: ['guest', 'admin', 'owner'] });
|
|
@@ -1029,9 +1010,9 @@ export class CommandHandler {
|
|
|
1029
1010
|
return ` ${prefix} ${m.key} (${m.nameZh}) - ${m.description}${suffix}`;
|
|
1030
1011
|
}).join('\n');
|
|
1031
1012
|
if (isOwner) {
|
|
1032
|
-
return { kind: 'command.result', text:
|
|
1013
|
+
return { kind: 'command.result', text: `权限模式: ${currentMode}\n\n${modeList}\n\n用法: /perm <模式> 或 allow|always|deny` };
|
|
1033
1014
|
}
|
|
1034
|
-
return { kind: 'command.result', text:
|
|
1015
|
+
return { kind: 'command.result', text: `当前权限模式: ${currentMode}` };
|
|
1035
1016
|
}
|
|
1036
1017
|
const parts = args.split(/\s+/);
|
|
1037
1018
|
// /perm <mode> 或 /perm allow|always|deny:切换模式 / 快捷审批
|
|
@@ -1235,7 +1216,7 @@ export class CommandHandler {
|
|
|
1235
1216
|
const list = available.map(a => `${a === currentAgent ? ' ✓' : ' '} ${a}`).join('\n');
|
|
1236
1217
|
const canSwitchAgent = activeChatType === 'group' ? isOwner : isAdmin;
|
|
1237
1218
|
if (canSwitchAgent) {
|
|
1238
|
-
return { kind: 'command.result', text: `当前 Agent: ${currentAgent}\n\n可用:\n${list}\n
|
|
1219
|
+
return { kind: 'command.result', text: `当前 Agent: ${currentAgent}\n\n可用:\n${list}\n用法: /agent <name>` };
|
|
1239
1220
|
}
|
|
1240
1221
|
return { kind: 'command.result', text: `当前 Agent: ${currentAgent}` };
|
|
1241
1222
|
}
|
|
@@ -1373,7 +1354,7 @@ export class CommandHandler {
|
|
|
1373
1354
|
? `\n推理强度: ${currentEffort === 'auto' ? 'auto (SDK默认)' : currentEffort} (使用 /effort 调整)`
|
|
1374
1355
|
: '';
|
|
1375
1356
|
if (isAdmin) {
|
|
1376
|
-
return { kind: 'command.result', text: `当前模型: ${currentModel}${effortHint}\n\n可用模型:\n${modelList}\n\n
|
|
1357
|
+
return { kind: 'command.result', text: `当前模型: ${currentModel}${effortHint}\n\n可用模型:\n${modelList}\n\n用法: /model <模型>` };
|
|
1377
1358
|
}
|
|
1378
1359
|
return { kind: 'command.result', text: `当前模型: ${currentModel}${effortHint}` };
|
|
1379
1360
|
}
|
|
@@ -1494,12 +1475,11 @@ export class CommandHandler {
|
|
|
1494
1475
|
}
|
|
1495
1476
|
// 降级:文本
|
|
1496
1477
|
const effortDisplay = currentEffort === 'auto' ? 'auto (SDK默认)' : currentEffort;
|
|
1497
|
-
const
|
|
1498
|
-
const effortList = allItems.map(e => ` ${e === currentEffort ? '✓' : ' '} ${e}${e === 'auto' ? ' (SDK默认)' : ''}`).join('\n');
|
|
1478
|
+
const effortOptions = [...efforts, 'auto'].join(' / ');
|
|
1499
1479
|
if (isAdmin) {
|
|
1500
|
-
return { kind: 'command.result', text:
|
|
1480
|
+
return { kind: 'command.result', text: `推理强度: ${effortDisplay} 可选: ${effortOptions} 用法: /effort <level>` };
|
|
1501
1481
|
}
|
|
1502
|
-
return { kind: 'command.result', text:
|
|
1482
|
+
return { kind: 'command.result', text: `推理强度: ${effortDisplay}` };
|
|
1503
1483
|
}
|
|
1504
1484
|
// 带参(切换)需 admin+;无参查询已在上方返回
|
|
1505
1485
|
if (!isAdmin)
|
|
@@ -1534,9 +1514,7 @@ export class CommandHandler {
|
|
|
1534
1514
|
return { kind: 'command.error', text: '❌ 无权限:此命令仅限 owner 使用' };
|
|
1535
1515
|
// 无参数时返回用法说明
|
|
1536
1516
|
if (normalizedContent === '/aid') {
|
|
1537
|
-
return { kind: 'command.result', text:
|
|
1538
|
-
|
|
1539
|
-
用法:
|
|
1517
|
+
return { kind: 'command.result', text: `用法:
|
|
1540
1518
|
/aid list 列出本地所有 AID
|
|
1541
1519
|
/aid show <aid> 查看 AID 详情
|
|
1542
1520
|
/aid new <aid> 创建新 AID
|
|
@@ -1546,27 +1524,16 @@ export class CommandHandler {
|
|
|
1546
1524
|
/aid agentmd get <aid> 下载并验签 agent.md` };
|
|
1547
1525
|
}
|
|
1548
1526
|
if (normalizedContent === '/rpc') {
|
|
1549
|
-
return { kind: 'command.result', text:
|
|
1550
|
-
|
|
1551
|
-
用法:
|
|
1552
|
-
/rpc --as <aid> --params <json>
|
|
1553
|
-
|
|
1554
|
-
参数格式:
|
|
1555
|
-
单行 JSON 单次调用
|
|
1556
|
-
多行 JSONL 逐行执行,失败即停
|
|
1557
|
-
|
|
1558
|
-
示例:
|
|
1559
|
-
/rpc --as myaid.agentid.pub --params {"method":"meta.ping","params":{}}` };
|
|
1527
|
+
return { kind: 'command.result', text: `用法: /rpc --as <aid> --params <json>
|
|
1528
|
+
示例: /rpc --as myaid.agentid.pub --params {"method":"meta.ping","params":{}}` };
|
|
1560
1529
|
}
|
|
1561
1530
|
if (normalizedContent === '/storage') {
|
|
1562
|
-
return { kind: 'command.result', text:
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
/storage
|
|
1566
|
-
/storage
|
|
1567
|
-
/storage
|
|
1568
|
-
/storage rm <aid> <path> 删文件
|
|
1569
|
-
/storage quota <aid> 查配额` };
|
|
1531
|
+
return { kind: 'command.result', text: `用法:
|
|
1532
|
+
/storage upload <aid> <file> <path> [--public]
|
|
1533
|
+
/storage download <aid> <url> [local-path]
|
|
1534
|
+
/storage ls <aid> [prefix]
|
|
1535
|
+
/storage rm <aid> <path>
|
|
1536
|
+
/storage quota <aid>` };
|
|
1570
1537
|
}
|
|
1571
1538
|
const cliArgs = normalizedContent.slice(1); // strip leading /
|
|
1572
1539
|
try {
|
|
@@ -1644,9 +1611,9 @@ export class CommandHandler {
|
|
|
1644
1611
|
return ` ${prefix} ${m.key} — ${m.label}`;
|
|
1645
1612
|
}).join('\n');
|
|
1646
1613
|
if (isOwner) {
|
|
1647
|
-
return { kind: 'command.result', text:
|
|
1614
|
+
return { kind: 'command.result', text: `中间输出: ${currentMode} 用法: /activity <all|dm|owner|none>` };
|
|
1648
1615
|
}
|
|
1649
|
-
return { kind: 'command.result', text:
|
|
1616
|
+
return { kind: 'command.result', text: `中间输出: ${currentMode}` };
|
|
1650
1617
|
}
|
|
1651
1618
|
const newMode = modeMap[activityArg];
|
|
1652
1619
|
if (!newMode) {
|
|
@@ -1678,8 +1645,12 @@ export class CommandHandler {
|
|
|
1678
1645
|
const arg = normalizedContent.slice(9).trim();
|
|
1679
1646
|
const currentMode = chatmodeSession.sessionMode || 'interactive';
|
|
1680
1647
|
const chatmodeChatType = chatmodeSession.chatType || activeChatType;
|
|
1681
|
-
const
|
|
1648
|
+
const isGroup = chatmodeChatType === 'group';
|
|
1649
|
+
const canSwitch = !isGroup;
|
|
1682
1650
|
if (!arg) {
|
|
1651
|
+
if (isGroup) {
|
|
1652
|
+
return { kind: 'command.result', text: `📋 会话模式: proactive(群聊强制)` };
|
|
1653
|
+
}
|
|
1683
1654
|
// 尝试发送 CommandCard 卡片
|
|
1684
1655
|
if (canSwitch) {
|
|
1685
1656
|
const modes = [
|
|
@@ -1712,23 +1683,16 @@ export class CommandHandler {
|
|
|
1712
1683
|
}
|
|
1713
1684
|
// 降级:文本
|
|
1714
1685
|
if (canSwitch) {
|
|
1715
|
-
return { kind: 'command.result', text:
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
'模式说明:',
|
|
1719
|
-
' • interactive — 交互模式:收到消息时才回复,回复直接显示',
|
|
1720
|
-
' • proactive — 主动模式:流式输出静默,由 Agent 自调 ctl send 发声',
|
|
1721
|
-
'',
|
|
1722
|
-
'用法: /chatmode <interactive|proactive>',
|
|
1723
|
-
].join('\n') };
|
|
1724
|
-
}
|
|
1725
|
-
return { kind: 'command.result', text: `📋 会话模式: ${currentMode}` };
|
|
1686
|
+
return { kind: 'command.result', text: `会话模式: ${currentMode} 用法: /chatmode <interactive|proactive>` };
|
|
1687
|
+
}
|
|
1688
|
+
return { kind: 'command.result', text: `会话模式: ${currentMode}` };
|
|
1726
1689
|
}
|
|
1727
1690
|
if (arg !== 'interactive' && arg !== 'proactive') {
|
|
1728
1691
|
return { kind: 'command.error', text: `❌ 无效模式: ${arg}\n可选: interactive / proactive` };
|
|
1729
1692
|
}
|
|
1730
|
-
|
|
1731
|
-
|
|
1693
|
+
// 群聊强制 proactive,不可切换
|
|
1694
|
+
if ((chatmodeSession.chatType || activeChatType) === 'group') {
|
|
1695
|
+
return { kind: 'command.error', text: '❌ 群聊强制 proactive 模式,不可切换' };
|
|
1732
1696
|
}
|
|
1733
1697
|
if (arg === currentMode) {
|
|
1734
1698
|
return { kind: 'command.result', text: `📋 当前会话模式已是 ${arg}` };
|
|
@@ -1750,7 +1714,7 @@ export class CommandHandler {
|
|
|
1750
1714
|
this.eventBus.publish({ type: 'session:chat-mode-changed', sessionId: chatmodeSession.id, mode: arg, timestamp: Date.now() });
|
|
1751
1715
|
return { kind: 'command.result', text: `✅ 会话模式已切换: ${arg}` };
|
|
1752
1716
|
}
|
|
1753
|
-
// /dispatch 命令:查看/切换群聊分发模式(mention |
|
|
1717
|
+
// /dispatch 命令:查看/切换群聊分发模式(mention | broadcast)
|
|
1754
1718
|
// 仅群聊可用;群聊中设置需管理员权限
|
|
1755
1719
|
if (normalizedContent === '/dispatch' || normalizedContent.startsWith('/dispatch ')) {
|
|
1756
1720
|
const dispatchResult = await this.ensureSession(channel, channelId, threadId, chatType);
|
|
@@ -1762,13 +1726,14 @@ export class CommandHandler {
|
|
|
1762
1726
|
return { kind: 'command.error', text: '❌ /dispatch 仅在群聊中可用' };
|
|
1763
1727
|
}
|
|
1764
1728
|
const arg = normalizedContent.slice(9).trim();
|
|
1765
|
-
const currentMode = dispatchSession.metadata?.dispatchMode
|
|
1729
|
+
const currentMode = dispatchSession.metadata?.dispatchMode;
|
|
1766
1730
|
if (!arg) {
|
|
1731
|
+
const displayMode = currentMode ?? '未设置(跟随群设置)';
|
|
1767
1732
|
// 尝试发送 CommandCard 卡片
|
|
1768
1733
|
if (isAdmin) {
|
|
1769
1734
|
const modes = [
|
|
1770
1735
|
{ key: 'mention', name: '提及模式', desc: '仅当被 @ 提及(含 @all)时响应群消息' },
|
|
1771
|
-
{ key: '
|
|
1736
|
+
{ key: 'broadcast', name: '广播模式', desc: '群内所有消息都触发响应' },
|
|
1772
1737
|
];
|
|
1773
1738
|
const interaction = {
|
|
1774
1739
|
type: 'interaction',
|
|
@@ -1795,31 +1760,24 @@ export class CommandHandler {
|
|
|
1795
1760
|
// 卡片降级:fall through 到下方文本输出
|
|
1796
1761
|
}
|
|
1797
1762
|
// 降级:文本
|
|
1798
|
-
const lines = [];
|
|
1799
|
-
lines.push(`📋 分发模式: ${currentMode}`);
|
|
1800
|
-
lines.push('');
|
|
1801
|
-
lines.push('模式说明:');
|
|
1802
|
-
lines.push(' • mention — 提及模式:仅当被@提及时响应群消息(含@all)');
|
|
1803
|
-
lines.push(' • all — 广播模式:群内所有消息都触发响应');
|
|
1804
1763
|
if (isAdmin) {
|
|
1805
|
-
|
|
1806
|
-
lines.push('用法: /dispatch <mention|all>');
|
|
1764
|
+
return { kind: 'command.result', text: `分发模式: ${displayMode} 用法: /dispatch <mention|broadcast>` };
|
|
1807
1765
|
}
|
|
1808
|
-
return { kind: 'command.result', text:
|
|
1766
|
+
return { kind: 'command.result', text: `分发模式: ${displayMode}` };
|
|
1809
1767
|
}
|
|
1810
|
-
if (arg !== 'mention' && arg !== '
|
|
1811
|
-
return { kind: 'command.error', text: `❌ 无效模式: ${arg}\n可选: mention /
|
|
1768
|
+
if (arg !== 'mention' && arg !== 'broadcast') {
|
|
1769
|
+
return { kind: 'command.error', text: `❌ 无效模式: ${arg}\n可选: mention / broadcast\n用法: /dispatch <模式>` };
|
|
1812
1770
|
}
|
|
1813
1771
|
if (!isAdmin) {
|
|
1814
1772
|
return { kind: 'command.error', text: '❌ 无权限:群聊中切换分发模式仅限管理员使用' };
|
|
1815
1773
|
}
|
|
1816
1774
|
if (arg === currentMode) {
|
|
1817
|
-
return { kind: 'command.result', text:
|
|
1775
|
+
return { kind: 'command.result', text: `当前已是 ${arg}` };
|
|
1818
1776
|
}
|
|
1819
1777
|
const metadata = { ...(dispatchSession.metadata || {}), dispatchMode: arg };
|
|
1820
1778
|
await this.sessionManager.updateSession(dispatchSession.id, { metadata });
|
|
1821
1779
|
this.eventBus.publish({ type: 'session:dispatch-mode-changed', sessionId: dispatchSession.id, mode: arg, timestamp: Date.now() });
|
|
1822
|
-
return { kind: 'command.result', text: `✅ 分发模式已切换: ${currentMode} → ${arg}` };
|
|
1780
|
+
return { kind: 'command.result', text: `✅ 分发模式已切换: ${currentMode ?? '未设置'} → ${arg}` };
|
|
1823
1781
|
}
|
|
1824
1782
|
// /stop 命令:中断当前任务
|
|
1825
1783
|
if (normalizedContent === '/stop') {
|
|
@@ -1970,7 +1928,7 @@ export class CommandHandler {
|
|
|
1970
1928
|
}
|
|
1971
1929
|
const lines = [];
|
|
1972
1930
|
const sessionMode = session.sessionMode || 'interactive';
|
|
1973
|
-
const dispatchMode = session.metadata?.dispatchMode
|
|
1931
|
+
const dispatchMode = session.metadata?.dispatchMode ?? '未设置(跟随群设置)';
|
|
1974
1932
|
const chatModeLine = `会话模式: ${sessionMode}`;
|
|
1975
1933
|
const dispatchModeLine = session.chatType === 'group' ? `分发模式: ${dispatchMode}` : null;
|
|
1976
1934
|
if (isAdmin) {
|
|
@@ -2218,6 +2176,23 @@ export class CommandHandler {
|
|
|
2218
2176
|
await executeRestart();
|
|
2219
2177
|
return { kind: 'command.result', text: '🔄 服务正在重启,请稍候...(约 5 秒后恢复)' };
|
|
2220
2178
|
}
|
|
2179
|
+
// /upgrade 命令:检查版本更新,提示用户手动重启
|
|
2180
|
+
if (normalizedContent === '/upgrade') {
|
|
2181
|
+
if (!isAdmin)
|
|
2182
|
+
return { kind: 'command.error', text: '❌ 无权限:升级检查仅限管理员使用' };
|
|
2183
|
+
if (isLinkedInstall()) {
|
|
2184
|
+
return { kind: 'command.result', text: '⏭ 开发模式,跳过升级检查' };
|
|
2185
|
+
}
|
|
2186
|
+
const localVer = getLocalVersion();
|
|
2187
|
+
const remoteVer = await checkLatestVersion();
|
|
2188
|
+
if (!remoteVer) {
|
|
2189
|
+
return { kind: 'command.result', text: `⚠️ 无法连接 npm registry(当前版本 ${localVer})` };
|
|
2190
|
+
}
|
|
2191
|
+
if (compareVersions(localVer, remoteVer) >= 0) {
|
|
2192
|
+
return { kind: 'command.result', text: `✓ 已是最新版本 (${localVer})` };
|
|
2193
|
+
}
|
|
2194
|
+
return { kind: 'command.result', text: `📦 发现新版本 ${localVer} → ${remoteVer}\n执行 /restart 升级` };
|
|
2195
|
+
}
|
|
2221
2196
|
// /pwd 命令:显示当前项目路径
|
|
2222
2197
|
if (normalizedContent === '/pwd') {
|
|
2223
2198
|
// session 现在总是存在(上面已自动创建)
|
|
@@ -3093,6 +3068,45 @@ export class CommandHandler {
|
|
|
3093
3068
|
this.eventBus.publish({ type: 'trigger:cancelled', triggerId: trigger.id, by: peerId });
|
|
3094
3069
|
return `✅ 触发器已取消:**${trigger.name}**`;
|
|
3095
3070
|
}
|
|
3071
|
+
// /trigger update <name|id> [--参数...]
|
|
3072
|
+
if (sub.startsWith('update ')) {
|
|
3073
|
+
if (!manager || !scheduler)
|
|
3074
|
+
return '⚠️ 触发器功能未启用';
|
|
3075
|
+
const args = sub.slice('update '.length);
|
|
3076
|
+
const result = parseTriggerUpdate(args);
|
|
3077
|
+
if (!result.ok)
|
|
3078
|
+
return `❌ ${result.error}`;
|
|
3079
|
+
const { nameOrId, value: patch } = result;
|
|
3080
|
+
// Find trigger: non-admin lookup is scoped
|
|
3081
|
+
let trigger;
|
|
3082
|
+
if (isAdmin) {
|
|
3083
|
+
trigger = manager.getByName(nameOrId) ?? manager.getById(nameOrId);
|
|
3084
|
+
}
|
|
3085
|
+
else {
|
|
3086
|
+
trigger = manager.getByNameScoped(nameOrId, peerId, channel)
|
|
3087
|
+
?? manager.getByIdScoped(nameOrId, peerId, channel);
|
|
3088
|
+
}
|
|
3089
|
+
if (!trigger) {
|
|
3090
|
+
return isAdmin
|
|
3091
|
+
? `❌ 未找到触发器:${nameOrId}`
|
|
3092
|
+
: `❌ 未找到触发器 "${nameOrId}",或无权限修改`;
|
|
3093
|
+
}
|
|
3094
|
+
// If schedule changed, recalculate nextFireAt
|
|
3095
|
+
if (patch.scheduleType && patch.scheduleValue) {
|
|
3096
|
+
const now = Date.now();
|
|
3097
|
+
patch.nextFireAt = calcNextFireAt(patch.scheduleType, patch.scheduleValue, now);
|
|
3098
|
+
}
|
|
3099
|
+
let updated;
|
|
3100
|
+
try {
|
|
3101
|
+
updated = manager.update(trigger.id, patch);
|
|
3102
|
+
scheduler.update(updated);
|
|
3103
|
+
}
|
|
3104
|
+
catch (err) {
|
|
3105
|
+
return `❌ 更新失败:${err.message}`;
|
|
3106
|
+
}
|
|
3107
|
+
const nextStr = new Date(updated.nextFireAt).toLocaleString();
|
|
3108
|
+
return `✅ 触发器已更新:**${updated.name}**\n下次触发:${nextStr}`;
|
|
3109
|
+
}
|
|
3096
3110
|
// /trigger set ...
|
|
3097
3111
|
if (sub.startsWith('set ')) {
|
|
3098
3112
|
if (!manager || !scheduler)
|
|
@@ -3137,7 +3151,7 @@ export class CommandHandler {
|
|
|
3137
3151
|
const nextStr = new Date(nextFireAt).toLocaleString();
|
|
3138
3152
|
return `✅ 触发器已注册:**${name}**\n下次触发:${nextStr}`;
|
|
3139
3153
|
}
|
|
3140
|
-
return `❌ 未知子命令。用法:\n/trigger — 查看活跃触发器\n/trigger list — 查看所有触发器\n/trigger set <参数> — 注册触发器\n/trigger cancel <名称> — 取消触发器`;
|
|
3154
|
+
return `❌ 未知子命令。用法:\n/trigger — 查看活跃触发器\n/trigger list — 查看所有触发器\n/trigger set <参数> — 注册触发器\n/trigger update <名称|ID> <参数> — 修改触发器\n/trigger cancel <名称> — 取消触发器`;
|
|
3141
3155
|
}
|
|
3142
3156
|
// ── /rewind helpers ──
|
|
3143
3157
|
async handleRewindList(session, agent) {
|
|
@@ -3256,7 +3270,8 @@ export class CommandHandler {
|
|
|
3256
3270
|
'/help', '/status', '/check', '/pwd',
|
|
3257
3271
|
'/model', '/effort', '/perm', '/agent',
|
|
3258
3272
|
'/compact', '/file', '/send', '/restart', '/bind', '/aid', '/rpc', '/storage',
|
|
3259
|
-
'/rename', '/name', '/evolagent',
|
|
3273
|
+
'/rename', '/name', '/evolagent', '/trigger',
|
|
3274
|
+
'/chatmode', '/dispatch', '/activity',
|
|
3260
3275
|
];
|
|
3261
3276
|
/** ctl 中仅允许查询形态的指令;写形态(带参)一律拒绝 */
|
|
3262
3277
|
static CTL_READONLY = new Set(['/agent']);
|
|
@@ -3278,6 +3293,8 @@ export class CommandHandler {
|
|
|
3278
3293
|
const taskId = this.sessionManager.getActiveTaskId(session.id);
|
|
3279
3294
|
const chatmode = session.sessionMode || 'interactive';
|
|
3280
3295
|
const encrypted = this.sessionManager.getSessionEncrypt(session.id);
|
|
3296
|
+
// 诊断日志:记录 task_id 解析结果
|
|
3297
|
+
logger.info(`[CommandHandler] buildCtlReplyContext: sessionId=${session.id} taskId=${taskId ?? 'none'} chatmode=${chatmode} threadId=${ctx.threadId ?? 'none'}`);
|
|
3281
3298
|
if (taskId || chatmode !== 'interactive' || encrypted != null) {
|
|
3282
3299
|
ctx.metadata = {};
|
|
3283
3300
|
if (taskId)
|
|
@@ -3294,6 +3311,7 @@ export class CommandHandler {
|
|
|
3294
3311
|
* 复用现有 slash cmd 逻辑,权限继承 session 用户角色
|
|
3295
3312
|
*/
|
|
3296
3313
|
async handleCtl(cmd, sessionId) {
|
|
3314
|
+
logger.info(`[ctl] cmd="${cmd}" sessionId=${sessionId}`);
|
|
3297
3315
|
// 1. 白名单检查
|
|
3298
3316
|
const inputCmd = cmd.split(' ')[0];
|
|
3299
3317
|
if (!CommandHandler.CTL_COMMANDS.includes(inputCmd)) {
|
|
@@ -3354,7 +3372,10 @@ export class CommandHandler {
|
|
|
3354
3372
|
}
|
|
3355
3373
|
// 4. /send 文本消息:直接通过 adapter 主动发送,不走 handle()
|
|
3356
3374
|
if (cmd.startsWith('/send ') || cmd === '/send') {
|
|
3357
|
-
|
|
3375
|
+
// 解析 --encrypt 标志和消息文本
|
|
3376
|
+
const raw = cmd.startsWith('/send ') ? cmd.slice(6).trim() : '';
|
|
3377
|
+
const forceEncrypt = raw.startsWith('--encrypt ');
|
|
3378
|
+
const text = forceEncrypt ? raw.slice(10).trim() : raw;
|
|
3358
3379
|
if (!text)
|
|
3359
3380
|
return { ok: false, error: '消息内容不能为空' };
|
|
3360
3381
|
const adapter = this.adapters.get(session.channel);
|
|
@@ -3362,8 +3383,15 @@ export class CommandHandler {
|
|
|
3362
3383
|
return { ok: false, error: `adapter 未找到: ${session.channel}` };
|
|
3363
3384
|
try {
|
|
3364
3385
|
const replyContext = this.buildCtlReplyContext(session);
|
|
3365
|
-
|
|
3366
|
-
|
|
3386
|
+
const taskId = replyContext?.metadata?.taskId;
|
|
3387
|
+
const chatmode = replyContext?.metadata?.chatmode ?? 'interactive';
|
|
3388
|
+
// --encrypt 覆盖 session 加密状态
|
|
3389
|
+
const enrichedReplyContext = forceEncrypt
|
|
3390
|
+
? { ...(replyContext ?? {}), metadata: { ...(replyContext?.metadata ?? {}), encrypted: true } }
|
|
3391
|
+
: replyContext;
|
|
3392
|
+
await adapter.send(buildEnvelope({ taskId, channel: adapter.channelName, channelId: session.channelId, chatmode, replyContext: enrichedReplyContext }), { kind: 'result.text', text, isFinal: true });
|
|
3393
|
+
// 出方向 jsonl 写入已下沉到 aun.ts:deliverTextEntry,message.send 成功后统一写入。
|
|
3394
|
+
return { ok: true, result: 'ok' };
|
|
3367
3395
|
}
|
|
3368
3396
|
catch (err) {
|
|
3369
3397
|
return { ok: false, error: err.message || String(err) };
|