evolclaw 2.6.3 → 2.7.0
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/data/evolclaw.sample.json +3 -4
- package/dist/agents/claude-runner.js +15 -6
- package/dist/agents/codex-runner.js +0 -1
- package/dist/agents/gemini-runner.js +3 -1
- package/dist/channels/aun.js +99 -28
- package/dist/channels/feishu.js +2 -0
- package/dist/cli.js +29 -1
- package/dist/config.js +66 -40
- package/dist/core/command-handler.js +51 -41
- package/dist/core/message/message-processor.js +93 -72
- package/dist/core/message/thought-emitter.js +1 -0
- package/dist/core/session/session-manager.js +23 -2
- package/dist/index.js +24 -25
- package/dist/prompts/templates.js +122 -0
- package/dist/templates/prompts.md +103 -0
- package/dist/utils/channel-fingerprint.js +59 -0
- package/dist/utils/init.js +1 -1
- package/dist/utils/logger.js +15 -3
- package/package.json +1 -1
package/dist/config.js
CHANGED
|
@@ -51,7 +51,7 @@ function loadCodexSettings() {
|
|
|
51
51
|
export function resolveAnthropicConfig(config) {
|
|
52
52
|
const settings = loadClaudeSettings();
|
|
53
53
|
// 过滤占位符,视为未配置
|
|
54
|
-
const configApiKey = config.agents?.
|
|
54
|
+
const configApiKey = config.agents?.claude?.apiKey;
|
|
55
55
|
const isPlaceholderKey = !configApiKey ||
|
|
56
56
|
configApiKey.includes('your-') ||
|
|
57
57
|
configApiKey.includes('placeholder');
|
|
@@ -59,21 +59,21 @@ export function resolveAnthropicConfig(config) {
|
|
|
59
59
|
|| process.env.ANTHROPIC_AUTH_TOKEN
|
|
60
60
|
|| settings.env?.ANTHROPIC_AUTH_TOKEN;
|
|
61
61
|
if (!apiKey) {
|
|
62
|
-
throw new Error('No API key found. Set one of: agents.
|
|
62
|
+
throw new Error('No API key found. Set one of: agents.claude.apiKey, env ANTHROPIC_AUTH_TOKEN, or ~/.claude/settings.json env.ANTHROPIC_AUTH_TOKEN');
|
|
63
63
|
}
|
|
64
64
|
// baseUrl 也过滤占位符
|
|
65
|
-
const configBaseUrl = config.agents?.
|
|
65
|
+
const configBaseUrl = config.agents?.claude?.baseUrl;
|
|
66
66
|
const isPlaceholderUrl = configBaseUrl?.includes('api.anthropic.com');
|
|
67
67
|
const baseUrl = (isPlaceholderUrl ? null : configBaseUrl)
|
|
68
68
|
|| process.env.ANTHROPIC_BASE_URL
|
|
69
69
|
|| settings.env?.ANTHROPIC_BASE_URL;
|
|
70
|
-
const model = config.agents?.
|
|
70
|
+
const model = config.agents?.claude?.model
|
|
71
71
|
|| settings.model
|
|
72
72
|
|| 'sonnet';
|
|
73
|
-
const effort = config.agents?.
|
|
73
|
+
const effort = config.agents?.claude?.effort
|
|
74
74
|
|| settings.effortLevel
|
|
75
75
|
|| undefined;
|
|
76
|
-
const configExecPath = config.agents?.
|
|
76
|
+
const configExecPath = config.agents?.claude?.pathToClaudeCodeExecutable;
|
|
77
77
|
const isPlaceholderExec = !configExecPath || configExecPath.includes('your-') || configExecPath.includes('placeholder');
|
|
78
78
|
const pathToClaudeCodeExecutable = isPlaceholderExec ? undefined : configExecPath;
|
|
79
79
|
return { apiKey, baseUrl, model, effort, pathToClaudeCodeExecutable };
|
|
@@ -81,7 +81,7 @@ export function resolveAnthropicConfig(config) {
|
|
|
81
81
|
export function resolveOpenaiConfig(config) {
|
|
82
82
|
const codexSettings = loadCodexSettings();
|
|
83
83
|
// 过滤占位符,视为未配置
|
|
84
|
-
const configApiKey = config.agents?.
|
|
84
|
+
const configApiKey = config.agents?.codex?.apiKey;
|
|
85
85
|
const isPlaceholderKey = !configApiKey ||
|
|
86
86
|
configApiKey.includes('your-') ||
|
|
87
87
|
configApiKey.includes('placeholder');
|
|
@@ -89,23 +89,23 @@ export function resolveOpenaiConfig(config) {
|
|
|
89
89
|
|| process.env.OPENAI_API_KEY
|
|
90
90
|
|| codexSettings.apiKey;
|
|
91
91
|
if (!apiKey) {
|
|
92
|
-
throw new Error('No OpenAI API key found. Set one of: agents.
|
|
92
|
+
throw new Error('No OpenAI API key found. Set one of: agents.codex.apiKey, env OPENAI_API_KEY, or ~/.codex/auth.json');
|
|
93
93
|
}
|
|
94
94
|
// baseUrl 也过滤占位符(与 anthropic 保持一致:只检查默认域名)
|
|
95
|
-
const configBaseUrl = config.agents?.
|
|
95
|
+
const configBaseUrl = config.agents?.codex?.baseUrl;
|
|
96
96
|
const isPlaceholderUrl = configBaseUrl?.includes('api.openai.com');
|
|
97
97
|
const baseUrl = (isPlaceholderUrl ? null : configBaseUrl)
|
|
98
98
|
|| process.env.OPENAI_BASE_URL
|
|
99
99
|
|| codexSettings.baseUrl
|
|
100
100
|
|| undefined;
|
|
101
|
-
const model = config.agents?.
|
|
101
|
+
const model = config.agents?.codex?.model
|
|
102
102
|
|| codexSettings.model
|
|
103
103
|
|| 'gpt-5.2-codex';
|
|
104
|
-
const effort = config.agents?.
|
|
104
|
+
const effort = config.agents?.codex?.effort || config.agents?.codex?.reasoning || undefined;
|
|
105
105
|
return { apiKey, baseUrl, model, effort };
|
|
106
106
|
}
|
|
107
107
|
export function resolveGoogleConfig(config) {
|
|
108
|
-
const googleCfg = config.agents?.
|
|
108
|
+
const googleCfg = config.agents?.gemini;
|
|
109
109
|
// CLI path: config → which gemini
|
|
110
110
|
let cliPath = googleCfg?.cliPath || '';
|
|
111
111
|
if (!cliPath) {
|
|
@@ -170,9 +170,41 @@ export function loadConfig(configPath = resolvePaths().config) {
|
|
|
170
170
|
}
|
|
171
171
|
const content = fs.readFileSync(configPath, 'utf-8');
|
|
172
172
|
const config = JSON.parse(content);
|
|
173
|
+
if (migrateAgentsKeys(config)) {
|
|
174
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
175
|
+
logger.warn(`Config migrated: agents.{anthropic,openai,google} → {claude,codex,gemini} in ${configPath}`);
|
|
176
|
+
}
|
|
173
177
|
validateConfig(config);
|
|
174
178
|
return config;
|
|
175
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* Rename legacy agent config keys to runner names.
|
|
182
|
+
* Returns true if any rename happened.
|
|
183
|
+
*/
|
|
184
|
+
function migrateAgentsKeys(config) {
|
|
185
|
+
const agents = config?.agents;
|
|
186
|
+
if (!agents || typeof agents !== 'object')
|
|
187
|
+
return false;
|
|
188
|
+
const renames = [
|
|
189
|
+
['anthropic', 'claude'],
|
|
190
|
+
['openai', 'codex'],
|
|
191
|
+
['google', 'gemini'],
|
|
192
|
+
];
|
|
193
|
+
let changed = false;
|
|
194
|
+
for (const [oldKey, newKey] of renames) {
|
|
195
|
+
if (agents[oldKey] === undefined)
|
|
196
|
+
continue;
|
|
197
|
+
if (agents[newKey] === undefined) {
|
|
198
|
+
agents[newKey] = agents[oldKey];
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
logger.warn(`Config has both agents.${oldKey} and agents.${newKey}; keeping new key, dropping legacy one`);
|
|
202
|
+
}
|
|
203
|
+
delete agents[oldKey];
|
|
204
|
+
changed = true;
|
|
205
|
+
}
|
|
206
|
+
return changed;
|
|
207
|
+
}
|
|
176
208
|
export function saveConfig(config, configPath = resolvePaths().config) {
|
|
177
209
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
178
210
|
}
|
|
@@ -309,26 +341,16 @@ export function setChannelShowActivities(config, instanceName, mode) {
|
|
|
309
341
|
}
|
|
310
342
|
}
|
|
311
343
|
/**
|
|
312
|
-
*
|
|
313
|
-
*
|
|
344
|
+
* 读取全局 chatmode 配置的默认 sessionMode
|
|
345
|
+
* 按 chatType 返回对应模式,未配置时返回 undefined(由 session-manager 回退到 'interactive')
|
|
314
346
|
*/
|
|
315
|
-
export function
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
if (inst)
|
|
323
|
-
return inst.sessionMode;
|
|
324
|
-
}
|
|
325
|
-
else {
|
|
326
|
-
const effectiveName = raw.name ?? type;
|
|
327
|
-
if (effectiveName === instanceName)
|
|
328
|
-
return raw.sessionMode;
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
return undefined;
|
|
347
|
+
export function getDefaultSessionMode(config, chatType) {
|
|
348
|
+
const cm = config.chatmode;
|
|
349
|
+
if (!cm)
|
|
350
|
+
return undefined;
|
|
351
|
+
if (chatType === 'group')
|
|
352
|
+
return cm.group;
|
|
353
|
+
return cm.private;
|
|
332
354
|
}
|
|
333
355
|
export function isOwner(config, channelOrType, userId) {
|
|
334
356
|
// 按实例名精确匹配
|
|
@@ -441,29 +463,33 @@ export function ensureDir(dirPath) {
|
|
|
441
463
|
fs.mkdirSync(dirPath, { recursive: true });
|
|
442
464
|
}
|
|
443
465
|
}
|
|
444
|
-
// agents.defaultAgent → config key 映射
|
|
445
|
-
const agentKeyMap = { claude: 'anthropic', codex: 'openai', gemini: 'google' };
|
|
446
466
|
/**
|
|
447
467
|
* 配置结构完整性校验(不校验凭据有效性)。
|
|
448
468
|
* 要求 agents/channels/projects 三段同时具备必要的锚点字段。
|
|
449
469
|
*/
|
|
450
470
|
export function validateConfigIntegrity(config) {
|
|
451
471
|
const reasons = [];
|
|
452
|
-
// agents
|
|
472
|
+
// agents — 单 agent 时自动推断,无需显式 defaultAgent
|
|
453
473
|
const defaultAgent = config.agents?.defaultAgent;
|
|
454
474
|
if (!defaultAgent) {
|
|
455
|
-
|
|
475
|
+
const agentKeys = Object.keys(config.agents || {}).filter(k => k !== 'defaultAgent');
|
|
476
|
+
const configuredAgents = agentKeys.filter(k => config.agents?.[k]);
|
|
477
|
+
if (configuredAgents.length === 0 && agentKeys.length !== 1) {
|
|
478
|
+
reasons.push('Missing agents.defaultAgent (multiple or no agents configured)');
|
|
479
|
+
}
|
|
456
480
|
}
|
|
457
481
|
else {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
reasons.push(`agents.defaultAgent='${defaultAgent}' but agents.${key} does not exist`);
|
|
482
|
+
if (!config.agents?.[defaultAgent]) {
|
|
483
|
+
reasons.push(`agents.defaultAgent='${defaultAgent}' but agents.${defaultAgent} does not exist`);
|
|
461
484
|
}
|
|
462
485
|
}
|
|
463
|
-
// channels
|
|
486
|
+
// channels — 单通道时自动推断,无需显式 defaultChannel
|
|
464
487
|
const defaultChannel = config.channels?.defaultChannel;
|
|
465
488
|
if (!defaultChannel) {
|
|
466
|
-
|
|
489
|
+
const channelKeys = channelTypes.filter(t => config.channels?.[t]);
|
|
490
|
+
if (channelKeys.length === 0) {
|
|
491
|
+
reasons.push('Missing channels.defaultChannel (no channels configured)');
|
|
492
|
+
}
|
|
467
493
|
}
|
|
468
494
|
else {
|
|
469
495
|
if (!config.channels?.[defaultChannel]) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { hasModelSwitcher, hasPermissionController } from '../agents/claude-runner.js';
|
|
2
|
-
import { saveConfig, resolvePaths, getPackageRoot, getOwner, getChannelShowActivities, setChannelShowActivities
|
|
2
|
+
import { saveConfig, resolvePaths, getPackageRoot, getOwner, getChannelShowActivities, setChannelShowActivities } from '../config.js';
|
|
3
3
|
import { logger } from '../utils/logger.js';
|
|
4
4
|
import crypto from 'crypto';
|
|
5
5
|
import path from 'path';
|
|
@@ -112,7 +112,7 @@ const aliases = {
|
|
|
112
112
|
'/rw': '/rewind'
|
|
113
113
|
};
|
|
114
114
|
// 命令快速路径前缀(所有命令都不进入消息队列)
|
|
115
|
-
const quickCommandPrefixes = ['/new', '/pwd', '/plist', '/project', '/bind', '/help', '/status', '/restart', '/model', '/effort', '/agent', '/slist', '/session', '/rename', '/repair', '/fork', '/stop', '/clear', '/compact', '/safe', '/del', '/perm', '/file', '/check', '/p ', '/s ', '/name', '/rewind', '/rw', '/rw ', '/activity', '/chatmode'];
|
|
115
|
+
const quickCommandPrefixes = ['/new', '/pwd', '/plist', '/project', '/bind', '/help', '/status', '/restart', '/model', '/effort', '/agent', '/slist', '/session', '/rename', '/repair', '/fork', '/stop', '/clear', '/compact', '/safe', '/del', '/perm', '/file', '/check', '/p ', '/s ', '/name', '/rewind', '/rw', '/rw ', '/activity', '/chatmode', '/aid', '/agentmd'];
|
|
116
116
|
export class CommandHandler {
|
|
117
117
|
sessionManager;
|
|
118
118
|
config;
|
|
@@ -150,9 +150,15 @@ export class CommandHandler {
|
|
|
150
150
|
this.defaultAgentId = agentRunnerOrMap.name;
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
|
-
/**
|
|
153
|
+
/** 项目列表快捷访问(list 缺失时用 defaultPath 作为唯一项目) */
|
|
154
154
|
get projects() {
|
|
155
|
-
|
|
155
|
+
const list = this.config.projects?.list;
|
|
156
|
+
if (list && Object.keys(list).length > 0)
|
|
157
|
+
return list;
|
|
158
|
+
const dp = this.config.projects?.defaultPath;
|
|
159
|
+
if (dp)
|
|
160
|
+
return { [path.basename(dp)]: dp };
|
|
161
|
+
return {};
|
|
156
162
|
}
|
|
157
163
|
/** 根据项目路径查找配置中的项目名称 */
|
|
158
164
|
getConfiguredProjectName(projectPath) {
|
|
@@ -532,6 +538,7 @@ export class CommandHandler {
|
|
|
532
538
|
if (normalizedContent !== content) {
|
|
533
539
|
logger.debug(`[CommandHandler] normalized: "${content}" -> "${normalizedContent}"`);
|
|
534
540
|
}
|
|
541
|
+
logger.info(`[CommandHandler] handle: channel=${channel} channelId=${channelId} cmd="${normalizedContent.split(' ')[0]}" user=${userId ?? 'n/a'} role=${identity?.role ?? 'n/a'}`);
|
|
535
542
|
// 话题内禁用部分命令
|
|
536
543
|
if (threadId) {
|
|
537
544
|
const threadBlocked = ['/new', '/slist', '/plist', '/bind', '/s', '/session', '/project', '/p', '/fork', '/del', '/agent'];
|
|
@@ -701,7 +708,7 @@ export class CommandHandler {
|
|
|
701
708
|
if (!hasPermissionController(permAgent)) {
|
|
702
709
|
return '❌ 权限控制不可用';
|
|
703
710
|
}
|
|
704
|
-
const defaultPermMode = identity.role === 'owner' ? 'bypass' : '
|
|
711
|
+
const defaultPermMode = identity.role === 'owner' ? 'bypass' : 'auto';
|
|
705
712
|
const currentMode = permSession.metadata?.permissionMode ?? defaultPermMode;
|
|
706
713
|
const modes = permAgent.listModes();
|
|
707
714
|
// 尝试发送交互卡片
|
|
@@ -737,6 +744,10 @@ export class CommandHandler {
|
|
|
737
744
|
const adapter = this.adapters.get(channel);
|
|
738
745
|
adapter?.sendText(channelId, result, replyCtx);
|
|
739
746
|
}
|
|
747
|
+
else {
|
|
748
|
+
// 切换成功后重新发新卡片(会自动 invalidate 旧卡片)
|
|
749
|
+
await this.handle('/perm', channel, channelId, undefined, userId, threadId);
|
|
750
|
+
}
|
|
740
751
|
}
|
|
741
752
|
},
|
|
742
753
|
});
|
|
@@ -1008,14 +1019,14 @@ export class CommandHandler {
|
|
|
1008
1019
|
// evolclaw.json 配了 → 写 evolclaw.json
|
|
1009
1020
|
// evolclaw.json 没配 → 写 agent 全局配置
|
|
1010
1021
|
if (isCodexAgent) {
|
|
1011
|
-
const configuredInEvolclaw = !!(this.config.agents?.
|
|
1022
|
+
const configuredInEvolclaw = !!(this.config.agents?.codex?.model || this.config.agents?.codex?.reasoning);
|
|
1012
1023
|
if (configuredInEvolclaw) {
|
|
1013
|
-
if (!this.config.agents.
|
|
1014
|
-
this.config.agents.
|
|
1024
|
+
if (!this.config.agents.codex)
|
|
1025
|
+
this.config.agents.codex = {};
|
|
1015
1026
|
if (newModel)
|
|
1016
|
-
this.config.agents.
|
|
1027
|
+
this.config.agents.codex.model = newModel;
|
|
1017
1028
|
if (newEffort)
|
|
1018
|
-
this.config.agents.
|
|
1029
|
+
this.config.agents.codex.reasoning = newEffort;
|
|
1019
1030
|
try {
|
|
1020
1031
|
saveConfig(this.config);
|
|
1021
1032
|
}
|
|
@@ -1025,12 +1036,12 @@ export class CommandHandler {
|
|
|
1025
1036
|
}
|
|
1026
1037
|
else {
|
|
1027
1038
|
// Codex 全局配置(~/.codex/config.toml)目前不支持写入,回退到 evolclaw.json
|
|
1028
|
-
if (!this.config.agents.
|
|
1029
|
-
this.config.agents.
|
|
1039
|
+
if (!this.config.agents.codex)
|
|
1040
|
+
this.config.agents.codex = {};
|
|
1030
1041
|
if (newModel)
|
|
1031
|
-
this.config.agents.
|
|
1042
|
+
this.config.agents.codex.model = newModel;
|
|
1032
1043
|
if (newEffort)
|
|
1033
|
-
this.config.agents.
|
|
1044
|
+
this.config.agents.codex.reasoning = newEffort;
|
|
1034
1045
|
try {
|
|
1035
1046
|
saveConfig(this.config);
|
|
1036
1047
|
}
|
|
@@ -1040,14 +1051,14 @@ export class CommandHandler {
|
|
|
1040
1051
|
}
|
|
1041
1052
|
}
|
|
1042
1053
|
else {
|
|
1043
|
-
const configuredInEvolclaw = !!(this.config.agents?.
|
|
1054
|
+
const configuredInEvolclaw = !!(this.config.agents?.claude?.model || this.config.agents?.claude?.effort);
|
|
1044
1055
|
if (configuredInEvolclaw) {
|
|
1045
|
-
if (!this.config.agents.
|
|
1046
|
-
this.config.agents.
|
|
1056
|
+
if (!this.config.agents.claude)
|
|
1057
|
+
this.config.agents.claude = {};
|
|
1047
1058
|
if (newModel)
|
|
1048
|
-
this.config.agents.
|
|
1059
|
+
this.config.agents.claude.model = newModel;
|
|
1049
1060
|
if (newEffort)
|
|
1050
|
-
this.config.agents.
|
|
1061
|
+
this.config.agents.claude.effort = newEffort;
|
|
1051
1062
|
try {
|
|
1052
1063
|
saveConfig(this.config);
|
|
1053
1064
|
}
|
|
@@ -1146,8 +1157,8 @@ export class CommandHandler {
|
|
|
1146
1157
|
effortAgent.setEffort?.(undefined);
|
|
1147
1158
|
const isCodex = effortAgent.name === 'codex';
|
|
1148
1159
|
if (isCodex) {
|
|
1149
|
-
if (this.config.agents?.
|
|
1150
|
-
delete this.config.agents.
|
|
1160
|
+
if (this.config.agents?.codex?.reasoning) {
|
|
1161
|
+
delete this.config.agents.codex.reasoning;
|
|
1151
1162
|
try {
|
|
1152
1163
|
saveConfig(this.config);
|
|
1153
1164
|
}
|
|
@@ -1155,9 +1166,9 @@ export class CommandHandler {
|
|
|
1155
1166
|
}
|
|
1156
1167
|
}
|
|
1157
1168
|
else {
|
|
1158
|
-
const configuredInEvolclaw = !!this.config.agents?.
|
|
1169
|
+
const configuredInEvolclaw = !!this.config.agents?.claude?.effort;
|
|
1159
1170
|
if (configuredInEvolclaw) {
|
|
1160
|
-
delete this.config.agents.
|
|
1171
|
+
delete this.config.agents.claude.effort;
|
|
1161
1172
|
try {
|
|
1162
1173
|
saveConfig(this.config);
|
|
1163
1174
|
}
|
|
@@ -1183,20 +1194,20 @@ export class CommandHandler {
|
|
|
1183
1194
|
this.config.agents = {};
|
|
1184
1195
|
const isCodex = effortAgent.name === 'codex';
|
|
1185
1196
|
if (isCodex) {
|
|
1186
|
-
if (!this.config.agents.
|
|
1187
|
-
this.config.agents.
|
|
1188
|
-
this.config.agents.
|
|
1197
|
+
if (!this.config.agents.codex)
|
|
1198
|
+
this.config.agents.codex = {};
|
|
1199
|
+
this.config.agents.codex.reasoning = newEffort;
|
|
1189
1200
|
try {
|
|
1190
1201
|
saveConfig(this.config);
|
|
1191
1202
|
}
|
|
1192
1203
|
catch { }
|
|
1193
1204
|
}
|
|
1194
1205
|
else {
|
|
1195
|
-
const configuredInEvolclaw = !!(this.config.agents?.
|
|
1206
|
+
const configuredInEvolclaw = !!(this.config.agents?.claude?.model || this.config.agents?.claude?.effort);
|
|
1196
1207
|
if (configuredInEvolclaw) {
|
|
1197
|
-
if (!this.config.agents.
|
|
1198
|
-
this.config.agents.
|
|
1199
|
-
this.config.agents.
|
|
1208
|
+
if (!this.config.agents.claude)
|
|
1209
|
+
this.config.agents.claude = {};
|
|
1210
|
+
this.config.agents.claude.effort = newEffort;
|
|
1200
1211
|
try {
|
|
1201
1212
|
saveConfig(this.config);
|
|
1202
1213
|
}
|
|
@@ -1456,16 +1467,14 @@ export class CommandHandler {
|
|
|
1456
1467
|
if (normalizedContent === '/chatmode' || normalizedContent.startsWith('/chatmode ')) {
|
|
1457
1468
|
if (!activeSession)
|
|
1458
1469
|
return '❌ 当前无活跃会话';
|
|
1459
|
-
const lockedMode = getChannelSessionMode(this.config, channel);
|
|
1460
1470
|
const arg = normalizedContent.slice(9).trim();
|
|
1461
1471
|
const currentMode = activeSession.sessionMode || 'interactive';
|
|
1462
1472
|
if (!arg) {
|
|
1463
|
-
const lockHint = lockedMode ? `(由通道配置锁定为 ${lockedMode})` : '';
|
|
1464
1473
|
const canSwitch = activeChatType !== 'group' || isAdmin;
|
|
1465
|
-
if (canSwitch
|
|
1466
|
-
return `📋 当前会话模式: ${currentMode}
|
|
1474
|
+
if (canSwitch) {
|
|
1475
|
+
return `📋 当前会话模式: ${currentMode}\n可选: interactive / proactive\n用法: /chatmode <模式>`;
|
|
1467
1476
|
}
|
|
1468
|
-
return `📋 当前会话模式: ${currentMode}
|
|
1477
|
+
return `📋 当前会话模式: ${currentMode}`;
|
|
1469
1478
|
}
|
|
1470
1479
|
if (arg !== 'interactive' && arg !== 'proactive') {
|
|
1471
1480
|
return `❌ 无效模式: ${arg}\n可选: interactive / proactive`;
|
|
@@ -1473,9 +1482,6 @@ export class CommandHandler {
|
|
|
1473
1482
|
if (activeChatType === 'group' && !isAdmin) {
|
|
1474
1483
|
return '❌ 无权限:群聊中切换会话模式仅限管理员使用';
|
|
1475
1484
|
}
|
|
1476
|
-
if (lockedMode) {
|
|
1477
|
-
return `❌ 会话模式由通道配置锁定为 ${lockedMode},无法切换`;
|
|
1478
|
-
}
|
|
1479
1485
|
if (arg === currentMode) {
|
|
1480
1486
|
return `📋 当前会话模式已是 ${arg}`;
|
|
1481
1487
|
}
|
|
@@ -1637,8 +1643,7 @@ export class CommandHandler {
|
|
|
1637
1643
|
}
|
|
1638
1644
|
const lines = [];
|
|
1639
1645
|
const sessionMode = session.sessionMode || 'interactive';
|
|
1640
|
-
const
|
|
1641
|
-
const chatModeLine = `会话模式: ${sessionMode}${lockedMode ? '(通道锁定)' : ''}`;
|
|
1646
|
+
const chatModeLine = `会话模式: ${sessionMode}`;
|
|
1642
1647
|
if (isAdmin) {
|
|
1643
1648
|
lines.push(`📊 ${isThread ? '话题' : '会话'}状态:`, `渠道: ${this.resolveChannelType(channel)} / 项目: ${projectName} / 会话: ${session.name || '(未命名)'}`, `会话ID: ${session.id}`, `项目路径: ${session.projectPath}`, `会话状态: ${sessionStatus}`, chatModeLine, `会话轮数: ${sessionTurns}`);
|
|
1644
1649
|
if (health.consecutiveErrors > 0) {
|
|
@@ -2175,7 +2180,12 @@ export class CommandHandler {
|
|
|
2175
2180
|
return '❌ 项目路径必须是绝对路径';
|
|
2176
2181
|
}
|
|
2177
2182
|
if (!fs.existsSync(projectPath)) {
|
|
2178
|
-
|
|
2183
|
+
if (this.config.projects?.autoCreate) {
|
|
2184
|
+
fs.mkdirSync(projectPath, { recursive: true });
|
|
2185
|
+
}
|
|
2186
|
+
else {
|
|
2187
|
+
return `❌ 路径不存在: ${projectPath}`;
|
|
2188
|
+
}
|
|
2179
2189
|
}
|
|
2180
2190
|
// 生成项目名称(使用目录名)
|
|
2181
2191
|
const projectName = path.basename(projectPath);
|