chatccc 0.2.223 → 0.2.224
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/config.sample.json +40 -39
- package/package.json +1 -1
- package/src/__tests__/builtin-chat-session.test.ts +33 -0
- package/src/__tests__/builtin-config.test.ts +1 -0
- package/src/__tests__/ccc-adapter.test.ts +20 -0
- package/src/__tests__/config-reload.test.ts +52 -52
- package/src/__tests__/config-sample.test.ts +41 -40
- package/src/__tests__/orchestrator.test.ts +836 -803
- package/src/__tests__/session.test.ts +1183 -1173
- package/src/adapters/ccc-adapter.ts +1 -0
- package/src/builtin/cli.ts +4 -0
- package/src/builtin/index.ts +10 -0
- package/src/config.ts +203 -192
- package/src/session.ts +5 -2
- package/src/web-ui.ts +610 -607
|
@@ -41,6 +41,7 @@ export function createCccAdapter(options: CccAdapterOptions = {}): ToolAdapter {
|
|
|
41
41
|
...(options.apiKey !== undefined ? { apiKey: options.apiKey } : {}),
|
|
42
42
|
...(options.baseURL !== undefined ? { baseURL: options.baseURL } : {}),
|
|
43
43
|
...(options.model !== undefined ? { model: options.model } : {}),
|
|
44
|
+
...(options.effort !== undefined ? { effort: options.effort } : {}),
|
|
44
45
|
};
|
|
45
46
|
|
|
46
47
|
return {
|
package/src/builtin/cli.ts
CHANGED
|
@@ -67,6 +67,9 @@ function parseArgs(argv = process.argv.slice(2)): ParsedArgs {
|
|
|
67
67
|
if (arg === "--model" && next !== undefined) {
|
|
68
68
|
config.model = next;
|
|
69
69
|
i++;
|
|
70
|
+
} else if (arg === "--effort" && next !== undefined) {
|
|
71
|
+
config.effort = next;
|
|
72
|
+
i++;
|
|
70
73
|
} else if (arg === "--base-url" && next !== undefined) {
|
|
71
74
|
config.baseURL = next;
|
|
72
75
|
i++;
|
|
@@ -117,6 +120,7 @@ function printHelp(appConfig: RuntimeDeps["appConfig"]): void {
|
|
|
117
120
|
"",
|
|
118
121
|
"Options:",
|
|
119
122
|
` --model <name> Model name (overrides config.ccc.model, current default ${appConfig.ccc.model})`,
|
|
123
|
+
` --effort <level> Reasoning effort: none/minimal/low/medium/high/xhigh/max (overrides config.ccc.effort)`,
|
|
120
124
|
` --base-url <url> API base URL (current default ${appConfig.ccc.DEEPSEEK_BASE_URL})`,
|
|
121
125
|
" --api-key <key> API key (overrides config.ccc.DEEPSEEK_API_KEY)",
|
|
122
126
|
" --cwd <path> Working directory",
|
package/src/builtin/index.ts
CHANGED
|
@@ -100,6 +100,11 @@ export interface ChatSessionConfig {
|
|
|
100
100
|
apiKey?: string;
|
|
101
101
|
/** 模型名称;传入时覆盖 config.ccc.model */
|
|
102
102
|
model?: string;
|
|
103
|
+
/**
|
|
104
|
+
* Reasoning effort(none/minimal/low/medium/high/xhigh/max);
|
|
105
|
+
* 传入时覆盖 config.ccc.effort,留空不传 reasoning_effort 请求字段。
|
|
106
|
+
*/
|
|
107
|
+
effort?: string;
|
|
103
108
|
}
|
|
104
109
|
|
|
105
110
|
export interface ChatSessionOptions {
|
|
@@ -156,6 +161,7 @@ export class ChatSession {
|
|
|
156
161
|
private cwd: string;
|
|
157
162
|
private context: BuiltinContextManager;
|
|
158
163
|
private maxSteps?: number;
|
|
164
|
+
private effort: string;
|
|
159
165
|
|
|
160
166
|
constructor(
|
|
161
167
|
overrides: ChatSessionConfig = {},
|
|
@@ -170,6 +176,7 @@ export class ChatSession {
|
|
|
170
176
|
|
|
171
177
|
const baseURL = overrides.baseURL ?? appConfig.ccc.DEEPSEEK_BASE_URL;
|
|
172
178
|
const modelId = overrides.model ?? appConfig.ccc.model;
|
|
179
|
+
this.effort = (overrides.effort ?? appConfig.ccc.effort ?? "").trim();
|
|
173
180
|
|
|
174
181
|
const provider = createOpenAICompatible({
|
|
175
182
|
name: "deepseek",
|
|
@@ -260,6 +267,9 @@ export class ChatSession {
|
|
|
260
267
|
tools: createBuiltinFileTools(this.cwd),
|
|
261
268
|
stopWhen: maxSteps !== undefined ? stepCountIs(maxSteps) : isLoopFinished(),
|
|
262
269
|
abortSignal: signal,
|
|
270
|
+
// DeepSeek OpenAI 兼容接口:providerOptions.deepseek.reasoningEffort
|
|
271
|
+
// 由 @ai-sdk/openai-compatible 自动映射为请求体 reasoning_effort 字段
|
|
272
|
+
...(this.effort ? { providerOptions: { deepseek: { reasoningEffort: this.effort } } } : {}),
|
|
263
273
|
});
|
|
264
274
|
|
|
265
275
|
const stream = result.fullStream ?? textStreamToFullStream(result.textStream);
|
package/src/config.ts
CHANGED
|
@@ -88,40 +88,45 @@ export interface CursorConfig {
|
|
|
88
88
|
onDemandMonthlyBudget: number;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
export interface CodexConfig {
|
|
92
|
-
/** 是否启用 Codex Agent;缺省时按"有任意字段非空"自动判定(向后兼容) */
|
|
93
|
-
enabled: boolean;
|
|
91
|
+
export interface CodexConfig {
|
|
92
|
+
/** 是否启用 Codex Agent;缺省时按"有任意字段非空"自动判定(向后兼容) */
|
|
93
|
+
enabled: boolean;
|
|
94
94
|
/** 是否作为 /new 未指定工具时使用的默认 Agent */
|
|
95
95
|
defaultAgent: boolean;
|
|
96
96
|
/** Codex CLI 可执行文件绝对路径;留空时退回到 PATH 中的 `codex` */
|
|
97
97
|
path: string;
|
|
98
98
|
model: string;
|
|
99
99
|
/** /model 可切换的单个备选模型;留空则不加入候选列表 */
|
|
100
|
-
alternativeModel: string;
|
|
101
|
-
effort: string;
|
|
102
|
-
/** Codex Priority service tier. False explicitly forces the standard tier. */
|
|
103
|
-
fastMode: boolean;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export interface CccConfig {
|
|
107
|
-
/** Whether the built-in CCC Agent is available for new sessions. */
|
|
108
|
-
enabled: boolean;
|
|
109
|
-
/** Whether /new without an explicit tool should use CCC Agent. */
|
|
110
|
-
defaultAgent: boolean;
|
|
111
|
-
/** DeepSeek API Key for the ChatCCC self-developed agent. */
|
|
112
|
-
DEEPSEEK_API_KEY: string;
|
|
113
|
-
/** DeepSeek-compatible API Base URL for the ChatCCC self-developed agent. */
|
|
114
|
-
DEEPSEEK_BASE_URL: string;
|
|
115
|
-
/** Model used by the ChatCCC self-developed agent. */
|
|
116
|
-
model: string;
|
|
117
|
-
/** Optional model exposed through /model for manual per-session switching. */
|
|
118
|
-
alternativeModel: string;
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
100
|
+
alternativeModel: string;
|
|
101
|
+
effort: string;
|
|
102
|
+
/** Codex Priority service tier. False explicitly forces the standard tier. */
|
|
103
|
+
fastMode: boolean;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface CccConfig {
|
|
107
|
+
/** Whether the built-in CCC Agent is available for new sessions. */
|
|
108
|
+
enabled: boolean;
|
|
109
|
+
/** Whether /new without an explicit tool should use CCC Agent. */
|
|
110
|
+
defaultAgent: boolean;
|
|
111
|
+
/** DeepSeek API Key for the ChatCCC self-developed agent. */
|
|
112
|
+
DEEPSEEK_API_KEY: string;
|
|
113
|
+
/** DeepSeek-compatible API Base URL for the ChatCCC self-developed agent. */
|
|
114
|
+
DEEPSEEK_BASE_URL: string;
|
|
115
|
+
/** Model used by the ChatCCC self-developed agent. */
|
|
116
|
+
model: string;
|
|
117
|
+
/** Optional model exposed through /model for manual per-session switching. */
|
|
118
|
+
alternativeModel: string;
|
|
119
|
+
/**
|
|
120
|
+
* Reasoning effort (none/minimal/low/medium/high/xhigh/max),透传到
|
|
121
|
+
* DeepSeek reasoning_effort 请求字段;留空表示不传(服务端默认 medium)。
|
|
122
|
+
*/
|
|
123
|
+
effort: string;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface FeishuConfig {
|
|
127
|
+
appId: string;
|
|
128
|
+
appSecret: string;
|
|
129
|
+
}
|
|
125
130
|
|
|
126
131
|
export interface PlatformConfig {
|
|
127
132
|
enabled: boolean;
|
|
@@ -151,39 +156,39 @@ export interface RawStreamAgentLogConfig {
|
|
|
151
156
|
keepCompleted: boolean;
|
|
152
157
|
}
|
|
153
158
|
|
|
154
|
-
export interface RawStreamLogsConfig {
|
|
155
|
-
claude: RawStreamAgentLogConfig;
|
|
156
|
-
cursor: RawStreamAgentLogConfig;
|
|
157
|
-
codex: RawStreamAgentLogConfig;
|
|
158
|
-
ccc: RawStreamAgentLogConfig;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export interface AppConfig {
|
|
162
|
-
feishu: FeishuConfig;
|
|
163
|
-
platforms: PlatformsConfig;
|
|
164
|
-
webUi: { openOnStart: boolean };
|
|
165
|
-
chromeDevtools: ChromeDevtoolsConfig;
|
|
159
|
+
export interface RawStreamLogsConfig {
|
|
160
|
+
claude: RawStreamAgentLogConfig;
|
|
161
|
+
cursor: RawStreamAgentLogConfig;
|
|
162
|
+
codex: RawStreamAgentLogConfig;
|
|
163
|
+
ccc: RawStreamAgentLogConfig;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export interface AppConfig {
|
|
167
|
+
feishu: FeishuConfig;
|
|
168
|
+
platforms: PlatformsConfig;
|
|
169
|
+
webUi: { openOnStart: boolean };
|
|
170
|
+
chromeDevtools: ChromeDevtoolsConfig;
|
|
166
171
|
port: number;
|
|
167
172
|
gitTimeoutSeconds: number;
|
|
168
173
|
/** 若为 false,AI 生成过程中用户发送消息不会打断,须先点「停止」再发送新消息 */
|
|
169
174
|
allowInterrupt: boolean;
|
|
170
175
|
rawStreamLogs: RawStreamLogsConfig;
|
|
171
|
-
claude: ClaudeConfig;
|
|
172
|
-
cursor: CursorConfig;
|
|
173
|
-
codex: CodexConfig;
|
|
174
|
-
ccc: CccConfig;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
export type AgentTool = "claude" | "cursor" | "codex" | "ccc";
|
|
178
|
-
export const AGENT_TOOLS: AgentTool[] = ["claude", "cursor", "codex", "ccc"];
|
|
176
|
+
claude: ClaudeConfig;
|
|
177
|
+
cursor: CursorConfig;
|
|
178
|
+
codex: CodexConfig;
|
|
179
|
+
ccc: CccConfig;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export type AgentTool = "claude" | "cursor" | "codex" | "ccc";
|
|
183
|
+
export const AGENT_TOOLS: AgentTool[] = ["claude", "cursor", "codex", "ccc"];
|
|
179
184
|
export type CursorAvatarBatteryMode = "apiPercent" | "onDemandUse";
|
|
180
185
|
|
|
181
186
|
/** 获取指定 agent 配置中所有模型相关的值(最多 100 个,去重) */
|
|
182
|
-
export function getAllModelsForTool(tool: string, cfg: AppConfig = config): string[] {
|
|
183
|
-
const seen = new Set<string>();
|
|
184
|
-
const collect = (v: unknown) => {
|
|
185
|
-
if (typeof v === "string" && v.trim()) seen.add(v.trim());
|
|
186
|
-
};
|
|
187
|
+
export function getAllModelsForTool(tool: string, cfg: AppConfig = config): string[] {
|
|
188
|
+
const seen = new Set<string>();
|
|
189
|
+
const collect = (v: unknown) => {
|
|
190
|
+
if (typeof v === "string" && v.trim()) seen.add(v.trim());
|
|
191
|
+
};
|
|
187
192
|
|
|
188
193
|
if (tool === "claude") {
|
|
189
194
|
collect(cfg.claude.model);
|
|
@@ -191,36 +196,39 @@ export function getAllModelsForTool(tool: string, cfg: AppConfig = config): stri
|
|
|
191
196
|
} else if (tool === "cursor") {
|
|
192
197
|
collect(cfg.cursor.model);
|
|
193
198
|
collect(cfg.cursor.alternativeModel);
|
|
194
|
-
} else if (tool === "codex") {
|
|
195
|
-
collect(cfg.codex.model);
|
|
196
|
-
collect(cfg.codex.alternativeModel);
|
|
197
|
-
} else if (tool === "ccc") {
|
|
198
|
-
collect(cfg.ccc.model);
|
|
199
|
-
collect(cfg.ccc.alternativeModel);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
return Array.from(seen).slice(0, 100);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
export const CLAUDE_EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"] as const;
|
|
206
|
-
export const CODEX_EFFORT_LEVELS = ["minimal", "low", "medium", "high", "xhigh"] as const;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if (tool === "
|
|
211
|
-
return [];
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
199
|
+
} else if (tool === "codex") {
|
|
200
|
+
collect(cfg.codex.model);
|
|
201
|
+
collect(cfg.codex.alternativeModel);
|
|
202
|
+
} else if (tool === "ccc") {
|
|
203
|
+
collect(cfg.ccc.model);
|
|
204
|
+
collect(cfg.ccc.alternativeModel);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return Array.from(seen).slice(0, 100);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export const CLAUDE_EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"] as const;
|
|
211
|
+
export const CODEX_EFFORT_LEVELS = ["minimal", "low", "medium", "high", "xhigh"] as const;
|
|
212
|
+
export const CCC_EFFORT_LEVELS = ["none", "minimal", "low", "medium", "high", "xhigh", "max"] as const;
|
|
213
|
+
|
|
214
|
+
export function getAllEffortsForTool(tool: string): string[] {
|
|
215
|
+
if (tool === "claude") return [...CLAUDE_EFFORT_LEVELS];
|
|
216
|
+
if (tool === "codex") return [...CODEX_EFFORT_LEVELS];
|
|
217
|
+
if (tool === "ccc") return [...CCC_EFFORT_LEVELS];
|
|
218
|
+
return [];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function getDefaultEffortForTool(tool: AgentTool, cfg: AppConfig = config): string {
|
|
222
|
+
if (tool === "claude") return cfg.claude.effort;
|
|
223
|
+
if (tool === "codex") return cfg.codex.effort;
|
|
224
|
+
if (tool === "ccc") return cfg.ccc.effort;
|
|
225
|
+
return "";
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const CONFIG_FILE = join(USER_DATA_DIR, "config.json");
|
|
229
|
+
const CONFIG_SAMPLE_FILE = join(PROJECT_ROOT, "config.sample.json");
|
|
230
|
+
export const DEFAULT_CCC_DEEPSEEK_BASE_URL = "https://api.deepseek.com/v1";
|
|
231
|
+
export const DEFAULT_CCC_MODEL = "deepseek-v4-pro";
|
|
224
232
|
|
|
225
233
|
/**
|
|
226
234
|
* 将旧位置(PROJECT_ROOT)的持久化数据一次性迁移到 USER_DATA_DIR。
|
|
@@ -428,20 +436,20 @@ function normalizeRawStreamAgentLogConfig(raw: unknown): RawStreamAgentLogConfig
|
|
|
428
436
|
|
|
429
437
|
function loadConfig(): AppConfig {
|
|
430
438
|
const defaults: AppConfig = {
|
|
431
|
-
feishu: { appId: "", appSecret: "" },
|
|
432
|
-
platforms: { feishu: { enabled: true }, ilink: { enabled: true } },
|
|
433
|
-
webUi: { openOnStart: true },
|
|
434
|
-
chromeDevtools: { enabled: false, port: 15166, chromePath: "" },
|
|
439
|
+
feishu: { appId: "", appSecret: "" },
|
|
440
|
+
platforms: { feishu: { enabled: true }, ilink: { enabled: true } },
|
|
441
|
+
webUi: { openOnStart: true },
|
|
442
|
+
chromeDevtools: { enabled: false, port: 15166, chromePath: "" },
|
|
435
443
|
port: 18080,
|
|
436
444
|
gitTimeoutSeconds: 180,
|
|
437
445
|
allowInterrupt: false,
|
|
438
|
-
rawStreamLogs: {
|
|
439
|
-
claude: { enabled: false, maxBytesPerTurn: 50 * 1024 * 1024, retentionDays: 7, keepCompleted: false },
|
|
440
|
-
cursor: { enabled: false, maxBytesPerTurn: 50 * 1024 * 1024, retentionDays: 7, keepCompleted: false },
|
|
441
|
-
codex: { enabled: false, maxBytesPerTurn: 50 * 1024 * 1024, retentionDays: 7, keepCompleted: false },
|
|
442
|
-
ccc: { enabled: false, maxBytesPerTurn: 50 * 1024 * 1024, retentionDays: 7, keepCompleted: false },
|
|
443
|
-
},
|
|
444
|
-
claude: { enabled: false, defaultAgent: true, model: "", subagentModel: "", effort: "", apiKey: "", baseUrl: "", maxTurn: 0 },
|
|
446
|
+
rawStreamLogs: {
|
|
447
|
+
claude: { enabled: false, maxBytesPerTurn: 50 * 1024 * 1024, retentionDays: 7, keepCompleted: false },
|
|
448
|
+
cursor: { enabled: false, maxBytesPerTurn: 50 * 1024 * 1024, retentionDays: 7, keepCompleted: false },
|
|
449
|
+
codex: { enabled: false, maxBytesPerTurn: 50 * 1024 * 1024, retentionDays: 7, keepCompleted: false },
|
|
450
|
+
ccc: { enabled: false, maxBytesPerTurn: 50 * 1024 * 1024, retentionDays: 7, keepCompleted: false },
|
|
451
|
+
},
|
|
452
|
+
claude: { enabled: false, defaultAgent: true, model: "", subagentModel: "", effort: "", apiKey: "", baseUrl: "", maxTurn: 0 },
|
|
445
453
|
cursor: {
|
|
446
454
|
enabled: false,
|
|
447
455
|
defaultAgent: false,
|
|
@@ -450,17 +458,18 @@ function loadConfig(): AppConfig {
|
|
|
450
458
|
alternativeModel: "",
|
|
451
459
|
avatarBatteryMode: "apiPercent",
|
|
452
460
|
onDemandMonthlyBudget: 1000,
|
|
453
|
-
},
|
|
454
|
-
codex: { enabled: false, defaultAgent: false, path: "", model: "", alternativeModel: "", effort: "", fastMode: false },
|
|
455
|
-
ccc: {
|
|
456
|
-
enabled: false,
|
|
457
|
-
defaultAgent: false,
|
|
458
|
-
DEEPSEEK_API_KEY: "",
|
|
459
|
-
DEEPSEEK_BASE_URL: DEFAULT_CCC_DEEPSEEK_BASE_URL,
|
|
460
|
-
model: DEFAULT_CCC_MODEL,
|
|
461
|
-
alternativeModel: "",
|
|
462
|
-
|
|
463
|
-
|
|
461
|
+
},
|
|
462
|
+
codex: { enabled: false, defaultAgent: false, path: "", model: "", alternativeModel: "", effort: "", fastMode: false },
|
|
463
|
+
ccc: {
|
|
464
|
+
enabled: false,
|
|
465
|
+
defaultAgent: false,
|
|
466
|
+
DEEPSEEK_API_KEY: "",
|
|
467
|
+
DEEPSEEK_BASE_URL: DEFAULT_CCC_DEEPSEEK_BASE_URL,
|
|
468
|
+
model: DEFAULT_CCC_MODEL,
|
|
469
|
+
alternativeModel: "",
|
|
470
|
+
effort: "",
|
|
471
|
+
},
|
|
472
|
+
};
|
|
464
473
|
|
|
465
474
|
if (!IS_TEST_ENV) {
|
|
466
475
|
migrateLegacyData();
|
|
@@ -510,20 +519,21 @@ function loadConfig(): AppConfig {
|
|
|
510
519
|
alternativeModel?: unknown;
|
|
511
520
|
avatarBatteryMode?: unknown;
|
|
512
521
|
onDemandMonthlyBudget?: unknown;
|
|
513
|
-
};
|
|
514
|
-
codex?: { enabled?: unknown; defaultAgent?: unknown; path?: unknown; command?: unknown; model?: unknown; alternativeModel?: unknown; effort?: unknown; fastMode?: unknown };
|
|
515
|
-
ccc?: {
|
|
516
|
-
enabled?: unknown;
|
|
517
|
-
defaultAgent?: unknown;
|
|
518
|
-
DEEPSEEK_API_KEY?: unknown;
|
|
519
|
-
DEEPSEEK_BASE_URL?: unknown;
|
|
520
|
-
model?: unknown;
|
|
521
|
-
alternativeModel?: unknown;
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
522
|
+
};
|
|
523
|
+
codex?: { enabled?: unknown; defaultAgent?: unknown; path?: unknown; command?: unknown; model?: unknown; alternativeModel?: unknown; effort?: unknown; fastMode?: unknown };
|
|
524
|
+
ccc?: {
|
|
525
|
+
enabled?: unknown;
|
|
526
|
+
defaultAgent?: unknown;
|
|
527
|
+
DEEPSEEK_API_KEY?: unknown;
|
|
528
|
+
DEEPSEEK_BASE_URL?: unknown;
|
|
529
|
+
model?: unknown;
|
|
530
|
+
alternativeModel?: unknown;
|
|
531
|
+
effort?: unknown;
|
|
532
|
+
};
|
|
533
|
+
webUi?: { openOnStart?: unknown };
|
|
534
|
+
chromeDevtools?: { enabled?: unknown; port?: unknown; chromePath?: unknown };
|
|
535
|
+
rawStreamLogs?: unknown;
|
|
536
|
+
};
|
|
527
537
|
try {
|
|
528
538
|
parsed = JSON.parse(raw);
|
|
529
539
|
} catch (err) {
|
|
@@ -533,11 +543,11 @@ function loadConfig(): AppConfig {
|
|
|
533
543
|
|
|
534
544
|
const feishu = parsed.feishu ?? { appId: "", appSecret: "" };
|
|
535
545
|
const claude = parsed.claude ?? {} as Partial<ClaudeConfig>;
|
|
536
|
-
const cursorRaw = (parsed.cursor ?? {}) as NonNullable<typeof parsed.cursor>;
|
|
537
|
-
const codexRaw = (parsed.codex ?? {}) as NonNullable<typeof parsed.codex>;
|
|
538
|
-
const cccRaw = (parsed.ccc ?? {}) as NonNullable<typeof parsed.ccc>;
|
|
539
|
-
const webUiRaw = (parsed.webUi ?? {}) as NonNullable<typeof parsed.webUi>;
|
|
540
|
-
const chromeDevtoolsRaw = (parsed.chromeDevtools ?? {}) as NonNullable<typeof parsed.chromeDevtools>;
|
|
546
|
+
const cursorRaw = (parsed.cursor ?? {}) as NonNullable<typeof parsed.cursor>;
|
|
547
|
+
const codexRaw = (parsed.codex ?? {}) as NonNullable<typeof parsed.codex>;
|
|
548
|
+
const cccRaw = (parsed.ccc ?? {}) as NonNullable<typeof parsed.ccc>;
|
|
549
|
+
const webUiRaw = (parsed.webUi ?? {}) as NonNullable<typeof parsed.webUi>;
|
|
550
|
+
const chromeDevtoolsRaw = (parsed.chromeDevtools ?? {}) as NonNullable<typeof parsed.chromeDevtools>;
|
|
541
551
|
const rawStreamLogsRaw = typeof parsed.rawStreamLogs === "object" && parsed.rawStreamLogs !== null
|
|
542
552
|
? parsed.rawStreamLogs as unknown as Record<string, unknown>
|
|
543
553
|
: {};
|
|
@@ -574,36 +584,36 @@ function loadConfig(): AppConfig {
|
|
|
574
584
|
(typeof cursorRaw.model === "string" && (cursorRaw.model as string).trim()) ||
|
|
575
585
|
(typeof cursorRaw.alternativeModel === "string" && (cursorRaw.alternativeModel as string).trim()),
|
|
576
586
|
);
|
|
577
|
-
const codexNonEmpty = (): boolean =>
|
|
587
|
+
const codexNonEmpty = (): boolean =>
|
|
578
588
|
Boolean(
|
|
579
589
|
(typeof codexRaw.path === "string" && codexRaw.path.trim()) ||
|
|
580
590
|
(typeof codexRaw.command === "string" && (codexRaw.command as string).trim()) ||
|
|
581
|
-
(typeof codexRaw.model === "string" && (codexRaw.model as string).trim()) ||
|
|
582
|
-
(typeof codexRaw.alternativeModel === "string" && (codexRaw.alternativeModel as string).trim()) ||
|
|
583
|
-
(typeof codexRaw.effort === "string" && (codexRaw.effort as string).trim()) ||
|
|
584
|
-
codexRaw.fastMode === true,
|
|
585
|
-
);
|
|
586
|
-
// 旧版 ccc 配置没有 enabled。只用 API Key 推断启用,避免 sample 中自带的
|
|
587
|
-
// 默认 Base URL / model 让升级用户在未配置凭证时意外启用 CCC Agent。
|
|
588
|
-
const cccNonEmpty = (): boolean =>
|
|
589
|
-
Boolean(typeof cccRaw.DEEPSEEK_API_KEY === "string" && cccRaw.DEEPSEEK_API_KEY.trim());
|
|
591
|
+
(typeof codexRaw.model === "string" && (codexRaw.model as string).trim()) ||
|
|
592
|
+
(typeof codexRaw.alternativeModel === "string" && (codexRaw.alternativeModel as string).trim()) ||
|
|
593
|
+
(typeof codexRaw.effort === "string" && (codexRaw.effort as string).trim()) ||
|
|
594
|
+
codexRaw.fastMode === true,
|
|
595
|
+
);
|
|
596
|
+
// 旧版 ccc 配置没有 enabled。只用 API Key 推断启用,避免 sample 中自带的
|
|
597
|
+
// 默认 Base URL / model 让升级用户在未配置凭证时意外启用 CCC Agent。
|
|
598
|
+
const cccNonEmpty = (): boolean =>
|
|
599
|
+
Boolean(typeof cccRaw.DEEPSEEK_API_KEY === "string" && cccRaw.DEEPSEEK_API_KEY.trim());
|
|
590
600
|
|
|
591
601
|
const claudeEnabled = resolveEnabled(claude.enabled, claudeNonEmpty);
|
|
592
602
|
const cursorEnabled = resolveEnabled(cursorRaw.enabled, cursorNonEmpty);
|
|
593
|
-
const codexEnabled = resolveEnabled(codexRaw.enabled, codexNonEmpty);
|
|
594
|
-
const cccEnabled = resolveEnabled(cccRaw.enabled, cccNonEmpty);
|
|
603
|
+
const codexEnabled = resolveEnabled(codexRaw.enabled, codexNonEmpty);
|
|
604
|
+
const cccEnabled = resolveEnabled(cccRaw.enabled, cccNonEmpty);
|
|
595
605
|
const chromeDevtoolsPort = Number(chromeDevtoolsRaw.port);
|
|
596
606
|
const explicitDefaultTool: AgentTool | null =
|
|
597
607
|
typeof claude.defaultAgent === "boolean" && claude.defaultAgent && claudeEnabled ? "claude" :
|
|
598
608
|
typeof cursorRaw.defaultAgent === "boolean" && cursorRaw.defaultAgent && cursorEnabled ? "cursor" :
|
|
599
|
-
typeof codexRaw.defaultAgent === "boolean" && codexRaw.defaultAgent && codexEnabled ? "codex" :
|
|
600
|
-
typeof cccRaw.defaultAgent === "boolean" && cccRaw.defaultAgent && cccEnabled ? "ccc" :
|
|
609
|
+
typeof codexRaw.defaultAgent === "boolean" && codexRaw.defaultAgent && codexEnabled ? "codex" :
|
|
610
|
+
typeof cccRaw.defaultAgent === "boolean" && cccRaw.defaultAgent && cccEnabled ? "ccc" :
|
|
601
611
|
null;
|
|
602
612
|
const fallbackDefaultTool: AgentTool =
|
|
603
613
|
claudeEnabled ? "claude" :
|
|
604
614
|
cursorEnabled ? "cursor" :
|
|
605
|
-
codexEnabled ? "codex" :
|
|
606
|
-
cccEnabled ? "ccc" :
|
|
615
|
+
codexEnabled ? "codex" :
|
|
616
|
+
cccEnabled ? "ccc" :
|
|
607
617
|
"claude";
|
|
608
618
|
const defaultTool = explicitDefaultTool ?? fallbackDefaultTool;
|
|
609
619
|
|
|
@@ -612,7 +622,7 @@ function loadConfig(): AppConfig {
|
|
|
612
622
|
appId: feishu.appId ?? "",
|
|
613
623
|
appSecret: feishu.appSecret ?? "",
|
|
614
624
|
},
|
|
615
|
-
platforms: {
|
|
625
|
+
platforms: {
|
|
616
626
|
feishu: {
|
|
617
627
|
enabled: typeof (parsed.platforms as unknown as Record<string, unknown> | undefined)?.feishu === "object"
|
|
618
628
|
? Boolean(((parsed.platforms as unknown as Record<string, unknown>).feishu as Record<string, unknown>).enabled ?? true)
|
|
@@ -630,13 +640,13 @@ function loadConfig(): AppConfig {
|
|
|
630
640
|
reuseTokenOnStart: typeof (parsed.platforms as unknown as Record<string, unknown> | undefined)?.ilink === "object"
|
|
631
641
|
? Boolean(((parsed.platforms as unknown as Record<string, unknown>).ilink as Record<string, unknown>).reuseTokenOnStart ?? true)
|
|
632
642
|
: true,
|
|
633
|
-
},
|
|
634
|
-
},
|
|
635
|
-
webUi: {
|
|
636
|
-
// 兼容升级前没有 webUi 字段的 config.json:缺省仍按“自动打开”处理。
|
|
637
|
-
openOnStart: typeof webUiRaw.openOnStart === "boolean" ? webUiRaw.openOnStart : true,
|
|
638
|
-
},
|
|
639
|
-
chromeDevtools: {
|
|
643
|
+
},
|
|
644
|
+
},
|
|
645
|
+
webUi: {
|
|
646
|
+
// 兼容升级前没有 webUi 字段的 config.json:缺省仍按“自动打开”处理。
|
|
647
|
+
openOnStart: typeof webUiRaw.openOnStart === "boolean" ? webUiRaw.openOnStart : true,
|
|
648
|
+
},
|
|
649
|
+
chromeDevtools: {
|
|
640
650
|
enabled: typeof chromeDevtoolsRaw.enabled === "boolean" ? chromeDevtoolsRaw.enabled : false,
|
|
641
651
|
port: Number.isInteger(chromeDevtoolsPort) && chromeDevtoolsPort >= 1 && chromeDevtoolsPort <= 65535
|
|
642
652
|
? chromeDevtoolsPort
|
|
@@ -646,12 +656,12 @@ function loadConfig(): AppConfig {
|
|
|
646
656
|
port: typeof parsed.port === "number" ? parsed.port : 18080,
|
|
647
657
|
gitTimeoutSeconds: typeof parsed.gitTimeoutSeconds === "number" ? parsed.gitTimeoutSeconds : 180,
|
|
648
658
|
allowInterrupt: typeof parsed.allowInterrupt === "boolean" ? parsed.allowInterrupt : false,
|
|
649
|
-
rawStreamLogs: {
|
|
650
|
-
claude: normalizeRawStreamAgentLogConfig(rawStreamLogsRaw.claude),
|
|
651
|
-
cursor: normalizeRawStreamAgentLogConfig(rawStreamLogsRaw.cursor),
|
|
652
|
-
codex: normalizeRawStreamAgentLogConfig(rawStreamLogsRaw.codex),
|
|
653
|
-
ccc: normalizeRawStreamAgentLogConfig(rawStreamLogsRaw.ccc),
|
|
654
|
-
},
|
|
659
|
+
rawStreamLogs: {
|
|
660
|
+
claude: normalizeRawStreamAgentLogConfig(rawStreamLogsRaw.claude),
|
|
661
|
+
cursor: normalizeRawStreamAgentLogConfig(rawStreamLogsRaw.cursor),
|
|
662
|
+
codex: normalizeRawStreamAgentLogConfig(rawStreamLogsRaw.codex),
|
|
663
|
+
ccc: normalizeRawStreamAgentLogConfig(rawStreamLogsRaw.ccc),
|
|
664
|
+
},
|
|
655
665
|
claude: {
|
|
656
666
|
enabled: claudeEnabled,
|
|
657
667
|
defaultAgent: defaultTool === "claude",
|
|
@@ -673,28 +683,29 @@ function loadConfig(): AppConfig {
|
|
|
673
683
|
avatarBatteryMode: normalizeCursorAvatarBatteryMode(cursorRaw.avatarBatteryMode),
|
|
674
684
|
onDemandMonthlyBudget: normalizeCursorOnDemandMonthlyBudget(cursorRaw.onDemandMonthlyBudget),
|
|
675
685
|
},
|
|
676
|
-
codex: {
|
|
677
|
-
enabled: codexEnabled,
|
|
678
|
-
defaultAgent: defaultTool === "codex",
|
|
679
|
-
path: readToolCliPath(codexRaw, { label: "codex", onLegacyField }),
|
|
680
|
-
model: normalizeOptionalConfigField(codexRaw.model, { label: "codex.model" }),
|
|
681
|
-
alternativeModel: normalizeOptionalConfigField(codexRaw.alternativeModel, { label: "codex.alternativeModel" }),
|
|
682
|
-
effort: normalizeOptionalConfigField(codexRaw.effort, { label: "codex.effort" }),
|
|
683
|
-
fastMode: codexRaw.fastMode === true,
|
|
684
|
-
},
|
|
685
|
-
ccc: {
|
|
686
|
-
enabled: cccEnabled,
|
|
687
|
-
defaultAgent: defaultTool === "ccc",
|
|
688
|
-
DEEPSEEK_API_KEY: normalizeOptionalConfigField(cccRaw.DEEPSEEK_API_KEY, { label: "ccc.DEEPSEEK_API_KEY" }),
|
|
689
|
-
DEEPSEEK_BASE_URL: normalizeOptionalConfigField(cccRaw.DEEPSEEK_BASE_URL, {
|
|
690
|
-
label: "ccc.DEEPSEEK_BASE_URL",
|
|
691
|
-
fallback: DEFAULT_CCC_DEEPSEEK_BASE_URL,
|
|
692
|
-
}),
|
|
693
|
-
model: normalizeOptionalConfigField(cccRaw.model, { label: "ccc.model", fallback: DEFAULT_CCC_MODEL }),
|
|
694
|
-
alternativeModel: normalizeOptionalConfigField(cccRaw.alternativeModel, { label: "ccc.alternativeModel" }),
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
}
|
|
686
|
+
codex: {
|
|
687
|
+
enabled: codexEnabled,
|
|
688
|
+
defaultAgent: defaultTool === "codex",
|
|
689
|
+
path: readToolCliPath(codexRaw, { label: "codex", onLegacyField }),
|
|
690
|
+
model: normalizeOptionalConfigField(codexRaw.model, { label: "codex.model" }),
|
|
691
|
+
alternativeModel: normalizeOptionalConfigField(codexRaw.alternativeModel, { label: "codex.alternativeModel" }),
|
|
692
|
+
effort: normalizeOptionalConfigField(codexRaw.effort, { label: "codex.effort" }),
|
|
693
|
+
fastMode: codexRaw.fastMode === true,
|
|
694
|
+
},
|
|
695
|
+
ccc: {
|
|
696
|
+
enabled: cccEnabled,
|
|
697
|
+
defaultAgent: defaultTool === "ccc",
|
|
698
|
+
DEEPSEEK_API_KEY: normalizeOptionalConfigField(cccRaw.DEEPSEEK_API_KEY, { label: "ccc.DEEPSEEK_API_KEY" }),
|
|
699
|
+
DEEPSEEK_BASE_URL: normalizeOptionalConfigField(cccRaw.DEEPSEEK_BASE_URL, {
|
|
700
|
+
label: "ccc.DEEPSEEK_BASE_URL",
|
|
701
|
+
fallback: DEFAULT_CCC_DEEPSEEK_BASE_URL,
|
|
702
|
+
}),
|
|
703
|
+
model: normalizeOptionalConfigField(cccRaw.model, { label: "ccc.model", fallback: DEFAULT_CCC_MODEL }),
|
|
704
|
+
alternativeModel: normalizeOptionalConfigField(cccRaw.alternativeModel, { label: "ccc.alternativeModel" }),
|
|
705
|
+
effort: normalizeOptionalConfigField(cccRaw.effort, { label: "ccc.effort" }),
|
|
706
|
+
},
|
|
707
|
+
};
|
|
708
|
+
}
|
|
698
709
|
|
|
699
710
|
/**
|
|
700
711
|
* 全局可变 config 对象。
|
|
@@ -1010,26 +1021,26 @@ export function explainMissingFeishuCredentialsAndExit(): never {
|
|
|
1010
1021
|
export const CLAUDE_SESSION_PREFIX = "Claude Code Session:";
|
|
1011
1022
|
/** 群描述中用于识别 Cursor 会话的前缀 */
|
|
1012
1023
|
export const CURSOR_SESSION_PREFIX = "Cursor Session:";
|
|
1013
|
-
/** 群描述中用于识别 Codex 会话的前缀 */
|
|
1014
|
-
export const CODEX_SESSION_PREFIX = "Codex Session:";
|
|
1015
|
-
/** 群描述中用于识别 CCC Agent 会话的前缀 */
|
|
1016
|
-
export const CCC_SESSION_PREFIX = "CCC Session:";
|
|
1024
|
+
/** 群描述中用于识别 Codex 会话的前缀 */
|
|
1025
|
+
export const CODEX_SESSION_PREFIX = "Codex Session:";
|
|
1026
|
+
/** 群描述中用于识别 CCC Agent 会话的前缀 */
|
|
1027
|
+
export const CCC_SESSION_PREFIX = "CCC Session:";
|
|
1017
1028
|
|
|
1018
1029
|
/** 根据 tool 名称返回对应的群描述前缀 */
|
|
1019
|
-
export function sessionPrefixForTool(tool: string): string {
|
|
1020
|
-
if (tool === "cursor") return CURSOR_SESSION_PREFIX;
|
|
1021
|
-
if (tool === "codex") return CODEX_SESSION_PREFIX;
|
|
1022
|
-
if (tool === "ccc") return CCC_SESSION_PREFIX;
|
|
1023
|
-
return CLAUDE_SESSION_PREFIX;
|
|
1024
|
-
}
|
|
1030
|
+
export function sessionPrefixForTool(tool: string): string {
|
|
1031
|
+
if (tool === "cursor") return CURSOR_SESSION_PREFIX;
|
|
1032
|
+
if (tool === "codex") return CODEX_SESSION_PREFIX;
|
|
1033
|
+
if (tool === "ccc") return CCC_SESSION_PREFIX;
|
|
1034
|
+
return CLAUDE_SESSION_PREFIX;
|
|
1035
|
+
}
|
|
1025
1036
|
|
|
1026
1037
|
/** 根据 tool 名称返回用于状态展示的标签 */
|
|
1027
|
-
export function toolDisplayName(tool: string): string {
|
|
1028
|
-
if (tool === "cursor") return "Cursor";
|
|
1029
|
-
if (tool === "codex") return "Codex";
|
|
1030
|
-
if (tool === "ccc") return "CCC Agent";
|
|
1031
|
-
return "Claude Code";
|
|
1032
|
-
}
|
|
1038
|
+
export function toolDisplayName(tool: string): string {
|
|
1039
|
+
if (tool === "cursor") return "Cursor";
|
|
1040
|
+
if (tool === "codex") return "Codex";
|
|
1041
|
+
if (tool === "ccc") return "CCC Agent";
|
|
1042
|
+
return "Claude Code";
|
|
1043
|
+
}
|
|
1033
1044
|
|
|
1034
1045
|
/** 解析 /new 未指定工具时使用的默认 Agent。旧配置缺省 defaultAgent 时保持 Claude 优先。 */
|
|
1035
1046
|
export function resolveDefaultAgentTool(cfg: AppConfig = config): AgentTool {
|
package/src/session.ts
CHANGED
|
@@ -526,7 +526,7 @@ export function getEffectiveEffortForTool(tool: string, sessionId?: string): str
|
|
|
526
526
|
const override = sessionEffortOverrides.get(sessionId);
|
|
527
527
|
if (override) return override;
|
|
528
528
|
}
|
|
529
|
-
if (tool === "claude" || tool === "codex") {
|
|
529
|
+
if (tool === "claude" || tool === "codex" || tool === "ccc") {
|
|
530
530
|
return getDefaultEffortForTool(tool);
|
|
531
531
|
}
|
|
532
532
|
return "";
|
|
@@ -597,7 +597,10 @@ export function getAdapterForTool(tool: string, sessionId?: string): ToolAdapter
|
|
|
597
597
|
fastMode: effectiveFastMode,
|
|
598
598
|
});
|
|
599
599
|
} else if (tool === "ccc") {
|
|
600
|
-
adapter = createCccAdapter({
|
|
600
|
+
adapter = createCccAdapter({
|
|
601
|
+
model: effectiveModel || undefined,
|
|
602
|
+
effort: effectiveEffort || undefined,
|
|
603
|
+
});
|
|
601
604
|
} else {
|
|
602
605
|
adapter = createClaudeAdapter({
|
|
603
606
|
model: effectiveModel,
|