deepccc 0.1.27 → 0.1.28
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 +41 -11
- package/dist/cli.js +5 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.js +24 -0
- package/dist/file-tools.d.ts +10 -1
- package/dist/file-tools.js +11 -4
- package/dist/index.d.ts +6 -0
- package/dist/index.js +26 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,8 +46,10 @@ deepccc 的本地缓存优化实测命中率 **96.7%**,有效降低重复请
|
|
|
46
46
|
```bash
|
|
47
47
|
export DEEPCCC_API_KEY="sk-..."
|
|
48
48
|
export DEEPCCC_BASE_URL="https://api.deepseek.com/v1"
|
|
49
|
-
export DEEPCCC_MODEL="deepseek-v4-pro"
|
|
50
|
-
export
|
|
49
|
+
export DEEPCCC_MODEL="deepseek-v4-pro"
|
|
50
|
+
export DEEPCCC_EFFORT="high"
|
|
51
|
+
export DEEPCCC_MAX_OUTPUT_TOKENS="32768"
|
|
52
|
+
export DEEPCCC_STREAMING="true"
|
|
51
53
|
```
|
|
52
54
|
|
|
53
55
|
Windows PowerShell:
|
|
@@ -56,8 +58,10 @@ Windows PowerShell:
|
|
|
56
58
|
$env:DEEPCCC_API_KEY="sk-..."
|
|
57
59
|
$env:DEEPCCC_PROVIDER="openai"
|
|
58
60
|
$env:DEEPCCC_BASE_URL="https://api.deepseek.com/v1"
|
|
59
|
-
$env:DEEPCCC_MODEL="deepseek-v4-pro"
|
|
60
|
-
$env:
|
|
61
|
+
$env:DEEPCCC_MODEL="deepseek-v4-pro"
|
|
62
|
+
$env:DEEPCCC_EFFORT="high"
|
|
63
|
+
$env:DEEPCCC_MAX_OUTPUT_TOKENS="32768"
|
|
64
|
+
$env:DEEPCCC_STREAMING="true"
|
|
61
65
|
```
|
|
62
66
|
|
|
63
67
|
也兼容这些 DeepSeek 别名:
|
|
@@ -75,10 +79,18 @@ $env:DEEPCCC_STREAMING="true"
|
|
|
75
79
|
"apiKey": "sk-...",
|
|
76
80
|
"baseURL": "https://api.deepseek.com/v1",
|
|
77
81
|
"model": "deepseek-v4-pro",
|
|
78
|
-
"subModel": "",
|
|
79
|
-
"effort": "",
|
|
80
|
-
"
|
|
82
|
+
"subModel": "",
|
|
83
|
+
"effort": "",
|
|
84
|
+
"maxOutputTokens": null,
|
|
85
|
+
"streaming": true,
|
|
81
86
|
"contextWindow": 1048576,
|
|
87
|
+
"git": {
|
|
88
|
+
"coAuthor": {
|
|
89
|
+
"enabled": true,
|
|
90
|
+
"name": "DeepCCC",
|
|
91
|
+
"email": "20184052+wzj998@users.noreply.github.com"
|
|
92
|
+
}
|
|
93
|
+
},
|
|
82
94
|
"rawStreamLogs": {
|
|
83
95
|
"enabled": true,
|
|
84
96
|
"maxBytesPerTurn": 1048576,
|
|
@@ -88,14 +100,24 @@ $env:DEEPCCC_STREAMING="true"
|
|
|
88
100
|
}
|
|
89
101
|
```
|
|
90
102
|
|
|
103
|
+
`git.coAuthor.enabled` 默认开启。DeepCCC 通过 `run_command` 创建 Git 提交时会保留用户为
|
|
104
|
+
主 Author,并追加 `Co-authored-by: DeepCCC <20184052+wzj998@users.noreply.github.com>`。
|
|
105
|
+
可设为 `false` 或用 `DEEPCCC_GIT_COAUTHOR=false` 全局关闭。ChatCCC 的
|
|
106
|
+
`ccc.gitCoAuthor` 是三态 override:`null`/缺失跟随这里,`true` 强制开启,`false` 强制关闭。
|
|
107
|
+
|
|
91
108
|
`provider` 可选 `openai` 或 `anthropic`,默认 `openai`,也可以通过
|
|
92
109
|
`DEEPCCC_PROVIDER` 或命令行 `--provider` 覆盖:
|
|
93
110
|
|
|
94
111
|
- `openai` 使用 OpenAI-compatible Chat Completions 协议,兼容 DeepSeek、OpenAI、LiteLLM、vLLM 等服务。
|
|
95
|
-
- `anthropic` 使用 Anthropic Messages 协议。配置中的 `baseURL` **完全按填写值使用,不自动补 `/v1`**,请填写到完整版本化地址,例如 DeepSeek 官方 Anthropic 端点为 `https://api.deepseek.com/anthropic/v1`(官方 Anthropic SDK 使用的 `https://api.deepseek.com/anthropic` 基址会拼接为 `.../anthropic/v1/messages`)。`effort`
|
|
112
|
+
- `anthropic` 使用 Anthropic Messages 协议。配置中的 `baseURL` **完全按填写值使用,不自动补 `/v1`**,请填写到完整版本化地址,例如 DeepSeek 官方 Anthropic 端点为 `https://api.deepseek.com/anthropic/v1`(官方 Anthropic SDK 使用的 `https://api.deepseek.com/anthropic` 基址会拼接为 `.../anthropic/v1/messages`)。`effort` 在 OpenAI-compatible 模式映射为 `reasoning_effort`,在 Anthropic 模式映射为 `output_config.effort`;目标服务不支持时应留空。
|
|
96
113
|
|
|
97
|
-
`streaming` 控制主对话是否使用流式请求,默认 `true`;也可以通过
|
|
98
|
-
`DEEPCCC_STREAMING=true|false` 覆盖。关闭后,终端会在整条模型响应完成后一次性显示结果。
|
|
114
|
+
`streaming` 控制主对话是否使用流式请求,默认 `true`;也可以通过
|
|
115
|
+
`DEEPCCC_STREAMING=true|false` 覆盖。关闭后,终端会在整条模型响应完成后一次性显示结果。
|
|
116
|
+
|
|
117
|
+
`maxOutputTokens` 限制主对话单次最大输出 token,默认不配置(`null`/缺失),此时不向
|
|
118
|
+
Provider 发送 `max_tokens`,使用模型服务端默认值。可通过 `DEEPCCC_MAX_OUTPUT_TOKENS`
|
|
119
|
+
或命令行 `--max-output-tokens` 覆盖;只接受正整数。该限制会同时覆盖模型思考内容、
|
|
120
|
+
工具参数和最终回答,设置过小可能导致工具调用或长回复被截断。
|
|
99
121
|
|
|
100
122
|
`contextWindow` 是模型上下文窗口(token),默认 `1048576`(1M,DeepSeek V4 Pro/Flash
|
|
101
123
|
原生规格);上下文压缩阈值自动 = `contextWindow × 0.8`(超出即把较早消息压缩为摘要)。
|
|
@@ -166,7 +188,15 @@ deepccc --max-steps 20
|
|
|
166
188
|
deepccc --effort high
|
|
167
189
|
```
|
|
168
190
|
|
|
169
|
-
可选值:`none` / `minimal` / `low` / `medium` / `high` / `xhigh` / `max`(留空则不传 `reasoning_effort` 请求字段)。
|
|
191
|
+
可选值:`none` / `minimal` / `low` / `medium` / `high` / `xhigh` / `max`(留空则不传 `reasoning_effort` 请求字段)。
|
|
192
|
+
|
|
193
|
+
限制主对话最大输出 token:
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
deepccc --max-output-tokens 8192
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
不设置时使用 Provider 默认值。
|
|
170
200
|
|
|
171
201
|
## 权限机制
|
|
172
202
|
|
package/dist/cli.js
CHANGED
|
@@ -65,6 +65,10 @@ function parseArgs(argv = process.argv.slice(2)) {
|
|
|
65
65
|
config.effort = next;
|
|
66
66
|
i++;
|
|
67
67
|
}
|
|
68
|
+
else if (arg === "--max-output-tokens" && next !== undefined) {
|
|
69
|
+
config.maxOutputTokens = parsePositiveIntegerOption("--max-output-tokens", next);
|
|
70
|
+
i++;
|
|
71
|
+
}
|
|
68
72
|
else if (arg === "--base-url" && next !== undefined) {
|
|
69
73
|
config.baseURL = next;
|
|
70
74
|
i++;
|
|
@@ -131,6 +135,7 @@ function printHelp(appConfig) {
|
|
|
131
135
|
` --model <name> Model name (current default ${appConfig.model})`,
|
|
132
136
|
` --sub-model <name> Sub-model for lightweight steps (compaction/task; empty = follow main model)`,
|
|
133
137
|
` --effort <level> Reasoning effort: none/minimal/low/medium/high/xhigh/max (overrides config.effort)`,
|
|
138
|
+
` --max-output-tokens <n> Maximum output tokens (unset = Provider default)`,
|
|
134
139
|
` --base-url <url> Provider API base URL (current default ${appConfig.baseURL})`,
|
|
135
140
|
" --api-key <key> API key",
|
|
136
141
|
" --cwd <path> Working directory",
|
package/dist/config.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export interface DeepCccConfig {
|
|
|
12
12
|
subModel: string;
|
|
13
13
|
/** Reasoning effort(none/minimal/low/medium/high/xhigh/max),留空不传 reasoning_effort */
|
|
14
14
|
effort: string;
|
|
15
|
+
/** 主对话最大输出 token;未配置时不传,使用 Provider 默认值。 */
|
|
16
|
+
maxOutputTokens?: number;
|
|
15
17
|
/** 主对话是否使用流式请求;默认开启 */
|
|
16
18
|
streaming: boolean;
|
|
17
19
|
/**
|
|
@@ -19,6 +21,13 @@ export interface DeepCccConfig {
|
|
|
19
21
|
* 上下文压缩阈值自动 = contextWindow × 0.8;超过模型/服务端实际上限会被 API 拒绝。
|
|
20
22
|
*/
|
|
21
23
|
contextWindow: number;
|
|
24
|
+
git: {
|
|
25
|
+
coAuthor: {
|
|
26
|
+
enabled: boolean;
|
|
27
|
+
name: string;
|
|
28
|
+
email: string;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
22
31
|
rawStreamLogs: {
|
|
23
32
|
enabled: boolean;
|
|
24
33
|
maxBytesPerTurn: number;
|
package/dist/config.js
CHANGED
|
@@ -16,6 +16,13 @@ export const DEFAULT_CONFIG = {
|
|
|
16
16
|
effort: "",
|
|
17
17
|
streaming: true,
|
|
18
18
|
contextWindow: 1_048_576,
|
|
19
|
+
git: {
|
|
20
|
+
coAuthor: {
|
|
21
|
+
enabled: true,
|
|
22
|
+
name: "DeepCCC",
|
|
23
|
+
email: "20184052+wzj998@users.noreply.github.com",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
19
26
|
rawStreamLogs: {
|
|
20
27
|
// 默认开启:压缩后可通过 session_search(include_raw_logs=true)找回原文。
|
|
21
28
|
// 如需关闭,在 ~/.deepccc/config.json 中设置 rawStreamLogs.enabled=false。
|
|
@@ -49,6 +56,12 @@ function numberEnv(name) {
|
|
|
49
56
|
const value = Number(env(name));
|
|
50
57
|
return Number.isFinite(value) && value >= 0 ? value : undefined;
|
|
51
58
|
}
|
|
59
|
+
function optionalPositiveInteger(value) {
|
|
60
|
+
if (value === undefined || value === null || value === "")
|
|
61
|
+
return undefined;
|
|
62
|
+
const parsed = Number(value);
|
|
63
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : undefined;
|
|
64
|
+
}
|
|
52
65
|
export function normalizeDeepCccProvider(value) {
|
|
53
66
|
if (value === undefined || value === null || String(value).trim() === "")
|
|
54
67
|
return "openai";
|
|
@@ -62,6 +75,8 @@ function loadConfig() {
|
|
|
62
75
|
const rawLogs = file.rawStreamLogs && typeof file.rawStreamLogs === "object"
|
|
63
76
|
? file.rawStreamLogs
|
|
64
77
|
: {};
|
|
78
|
+
const git = file.git && typeof file.git === "object" ? file.git : {};
|
|
79
|
+
const coAuthor = git.coAuthor && typeof git.coAuthor === "object" ? git.coAuthor : {};
|
|
65
80
|
return {
|
|
66
81
|
provider: normalizeDeepCccProvider(env("DEEPCCC_PROVIDER") ?? file.provider ?? DEFAULT_CONFIG.provider),
|
|
67
82
|
apiKey: env("DEEPCCC_API_KEY") ?? env("DEEPSEEK_API_KEY") ?? file.apiKey ?? DEFAULT_CONFIG.apiKey,
|
|
@@ -69,8 +84,17 @@ function loadConfig() {
|
|
|
69
84
|
model: env("DEEPCCC_MODEL") ?? env("DEEPSEEK_MODEL") ?? file.model ?? DEFAULT_CONFIG.model,
|
|
70
85
|
subModel: env("DEEPCCC_SUB_MODEL") ?? file.subModel ?? DEFAULT_CONFIG.subModel,
|
|
71
86
|
effort: env("DEEPCCC_EFFORT") ?? env("DEEPSEEK_EFFORT") ?? file.effort ?? DEFAULT_CONFIG.effort,
|
|
87
|
+
maxOutputTokens: optionalPositiveInteger(env("DEEPCCC_MAX_OUTPUT_TOKENS"))
|
|
88
|
+
?? optionalPositiveInteger(file.maxOutputTokens),
|
|
72
89
|
streaming: boolEnv("DEEPCCC_STREAMING") ?? file.streaming ?? DEFAULT_CONFIG.streaming,
|
|
73
90
|
contextWindow: numberEnv("DEEPCCC_CONTEXT_WINDOW") ?? file.contextWindow ?? DEFAULT_CONFIG.contextWindow,
|
|
91
|
+
git: {
|
|
92
|
+
coAuthor: {
|
|
93
|
+
enabled: boolEnv("DEEPCCC_GIT_COAUTHOR") ?? coAuthor.enabled ?? DEFAULT_CONFIG.git.coAuthor.enabled,
|
|
94
|
+
name: coAuthor.name?.trim() || DEFAULT_CONFIG.git.coAuthor.name,
|
|
95
|
+
email: coAuthor.email?.trim() || DEFAULT_CONFIG.git.coAuthor.email,
|
|
96
|
+
},
|
|
97
|
+
},
|
|
74
98
|
rawStreamLogs: {
|
|
75
99
|
enabled: boolEnv("DEEPCCC_RAW_STREAM_LOGS") ?? rawLogs.enabled ?? DEFAULT_CONFIG.rawStreamLogs.enabled,
|
|
76
100
|
maxBytesPerTurn: numberEnv("DEEPCCC_RAW_STREAM_MAX_BYTES") ?? rawLogs.maxBytesPerTurn ?? DEFAULT_CONFIG.rawStreamLogs.maxBytesPerTurn,
|
package/dist/file-tools.d.ts
CHANGED
|
@@ -71,6 +71,13 @@ export interface RunCommandOutput {
|
|
|
71
71
|
truncated: boolean;
|
|
72
72
|
durationMs: number;
|
|
73
73
|
}
|
|
74
|
+
export interface GitCoAuthorOptions {
|
|
75
|
+
enabled: boolean;
|
|
76
|
+
name: string;
|
|
77
|
+
email: string;
|
|
78
|
+
}
|
|
79
|
+
/** Adds DeepCCC attribution to git commit commands without replacing the user's author. */
|
|
80
|
+
export declare function withGitCoAuthor(command: string, coAuthor?: GitCoAuthorOptions): string;
|
|
74
81
|
export interface FileEdit {
|
|
75
82
|
oldText: string;
|
|
76
83
|
newText: string;
|
|
@@ -143,7 +150,7 @@ export declare function expandHomePath(value: string): string;
|
|
|
143
150
|
export declare function readFileForTool(cwd: string, input: ReadFileInput): Promise<ReadFileOutput>;
|
|
144
151
|
export declare function listDirForTool(cwd: string, input?: ListDirInput): Promise<ListDirOutput>;
|
|
145
152
|
export declare function searchCodeForTool(cwd: string, input: SearchCodeInput, signal?: AbortSignal, runtimeOptions?: SearchCodeRuntimeOptions): Promise<SearchCodeOutput>;
|
|
146
|
-
export declare function runCommandForTool(cwd: string, input: RunCommandInput, abortSignal?: AbortSignal): Promise<RunCommandOutput>;
|
|
153
|
+
export declare function runCommandForTool(cwd: string, input: RunCommandInput, abortSignal?: AbortSignal, coAuthor?: GitCoAuthorOptions): Promise<RunCommandOutput>;
|
|
147
154
|
export declare function editFileForTool(cwd: string, input: EditFileInput): Promise<EditFileOutput>;
|
|
148
155
|
export declare function createFileForTool(cwd: string, input: CreateFileInput): Promise<FileWriteOutput>;
|
|
149
156
|
export declare function deleteFileForTool(cwd: string, input: DeleteFileInput): Promise<DeleteFileOutput>;
|
|
@@ -152,6 +159,8 @@ export declare function applyPatchForTool(cwd: string, input: ApplyPatchInput):
|
|
|
152
159
|
export interface BuiltinFileToolsOptions {
|
|
153
160
|
/** 权限门控:副作用工具(run_command/文件写操作)执行前会先经过 gate.check */
|
|
154
161
|
permissionGate?: PermissionGate;
|
|
162
|
+
/** Git commits created by DeepCCC receive this Co-authored-by trailer when enabled. */
|
|
163
|
+
gitCoAuthor?: GitCoAuthorOptions;
|
|
155
164
|
/** session_search 工具的会话/原始日志目录(默认 ~/.deepccc/sessions 与 raw-stream-logs;测试可注入) */
|
|
156
165
|
sessionSearch?: {
|
|
157
166
|
contextDir?: string;
|
package/dist/file-tools.js
CHANGED
|
@@ -26,6 +26,13 @@ const MAX_COMMAND_TIMEOUT_MS = 900_000;
|
|
|
26
26
|
export const MAX_TASK_OUTPUT_CHARS = 32_000;
|
|
27
27
|
const requireFromHere = createRequire(import.meta.url);
|
|
28
28
|
const FALLBACK_SKIPPED_DIRECTORIES = new Set([".git", "node_modules"]);
|
|
29
|
+
/** Adds DeepCCC attribution to git commit commands without replacing the user's author. */
|
|
30
|
+
export function withGitCoAuthor(command, coAuthor) {
|
|
31
|
+
if (!coAuthor?.enabled || command.includes(coAuthor.email))
|
|
32
|
+
return command;
|
|
33
|
+
const trailer = ` --trailer "Co-authored-by: ${coAuthor.name} <${coAuthor.email}>"`;
|
|
34
|
+
return command.replace(/(^|(?:&&|\|\||;|\|)\s*)(git\s+commit)\b/g, (_match, prefix, gitCommit) => `${prefix}${gitCommit}${trailer}`);
|
|
35
|
+
}
|
|
29
36
|
/**
|
|
30
37
|
* 把 `~` / `~/x`(含反斜杠 `~\\x`)展开为用户主目录绝对路径。
|
|
31
38
|
* `~user/x` 形式不展开(Node 无内置支持),保持原样交给后续解析。
|
|
@@ -676,8 +683,8 @@ export async function searchCodeForTool(cwd, input, signal, runtimeOptions = {})
|
|
|
676
683
|
: fallback.truncated,
|
|
677
684
|
};
|
|
678
685
|
}
|
|
679
|
-
export async function runCommandForTool(cwd, input, abortSignal) {
|
|
680
|
-
const command = input.command?.trim();
|
|
686
|
+
export async function runCommandForTool(cwd, input, abortSignal, coAuthor) {
|
|
687
|
+
const command = withGitCoAuthor(input.command?.trim(), coAuthor);
|
|
681
688
|
if (!command)
|
|
682
689
|
throw new Error("command is required");
|
|
683
690
|
const commandCwd = resolveToolPath(cwd, input.cwd);
|
|
@@ -1016,14 +1023,14 @@ export function createBuiltinFileTools(cwd, options = {}) {
|
|
|
1016
1023
|
},
|
|
1017
1024
|
required: ["command"],
|
|
1018
1025
|
}),
|
|
1019
|
-
execute: async (input,
|
|
1026
|
+
execute: async (input, execOptions) => {
|
|
1020
1027
|
await guard({
|
|
1021
1028
|
tool: "run_command",
|
|
1022
1029
|
action: input.command,
|
|
1023
1030
|
reason: isDangerousCommand(input.command) ? "high-risk" : "rule",
|
|
1024
1031
|
detail: `运行命令: ${input.command}`,
|
|
1025
1032
|
});
|
|
1026
|
-
return runCommandForTool(cwd, input, options.
|
|
1033
|
+
return runCommandForTool(cwd, input, execOptions.abortSignal, options.gitCoAuthor);
|
|
1027
1034
|
},
|
|
1028
1035
|
}),
|
|
1029
1036
|
task: tool({
|
package/dist/index.d.ts
CHANGED
|
@@ -37,6 +37,8 @@ export interface ChatSessionConfig {
|
|
|
37
37
|
* overrides config.effort; empty omits the reasoning_effort request field.
|
|
38
38
|
*/
|
|
39
39
|
effort?: string;
|
|
40
|
+
/** Maximum output tokens for the main conversation; unset uses the Provider default. */
|
|
41
|
+
maxOutputTokens?: number;
|
|
40
42
|
}
|
|
41
43
|
export interface ChatSessionOptions {
|
|
42
44
|
/** Session working directory. */
|
|
@@ -78,6 +80,8 @@ export interface ChatSessionOptions {
|
|
|
78
80
|
* 调用)自动拒绝高危命令,常规文件操作与低危命令不受影响。
|
|
79
81
|
*/
|
|
80
82
|
permissionResolver?: PermissionResolver;
|
|
83
|
+
/** Overrides ~/.deepccc/config.json git.coAuthor.enabled for this integration. */
|
|
84
|
+
gitCoAuthor?: boolean;
|
|
81
85
|
}
|
|
82
86
|
/**
|
|
83
87
|
* 流式响应事件
|
|
@@ -136,9 +140,11 @@ export declare class ChatSession {
|
|
|
136
140
|
private compactionTimeoutMs;
|
|
137
141
|
private maxSteps?;
|
|
138
142
|
private effort;
|
|
143
|
+
private maxOutputTokens?;
|
|
139
144
|
private permissionMode;
|
|
140
145
|
private permissionResolver?;
|
|
141
146
|
private permissionGate;
|
|
147
|
+
private gitCoAuthorEnabled;
|
|
142
148
|
private skillDirs;
|
|
143
149
|
private customSystemPrompt;
|
|
144
150
|
/** 最近一次 chat() 使用的 system prompt(供 history 等读取) */
|
package/dist/index.js
CHANGED
|
@@ -77,6 +77,7 @@ const COMPACTION_RECOVERY_HINT_DISABLED = [
|
|
|
77
77
|
].join("\n");
|
|
78
78
|
export const DEFAULT_COMPACTION_TIMEOUT_MS = 5 * 60 * 1000;
|
|
79
79
|
const MAX_COMPACTION_OUTPUT_TOKENS = 16_384;
|
|
80
|
+
const OPENAI_COMPATIBLE_PROVIDER_NAME = "deepccc";
|
|
80
81
|
/** task 子代理工具:子代理单轮对话的最大工具步数(防失控循环,结果收敛后即结束) */
|
|
81
82
|
const TASK_MAX_STEPS = 20;
|
|
82
83
|
/** 子代理最终输出截断:保留前 MAX_TASK_OUTPUT_CHARS 字符,尾部注明截断信息 */
|
|
@@ -219,6 +220,14 @@ function normalizeMaxSteps(value) {
|
|
|
219
220
|
}
|
|
220
221
|
return value;
|
|
221
222
|
}
|
|
223
|
+
function normalizeMaxOutputTokens(value) {
|
|
224
|
+
if (value === undefined)
|
|
225
|
+
return undefined;
|
|
226
|
+
if (!Number.isFinite(value) || !Number.isInteger(value) || value <= 0) {
|
|
227
|
+
throw new Error("maxOutputTokens must be a positive integer when provided");
|
|
228
|
+
}
|
|
229
|
+
return value;
|
|
230
|
+
}
|
|
222
231
|
function normalizeAnthropicBaseURL(baseURL) {
|
|
223
232
|
// 完全按用户填写的地址使用,不自动补 /v1(AI SDK 仅对官方 api.anthropic.com
|
|
224
233
|
// 特判补一次 /v1,其他地址原样拼接 /messages)。
|
|
@@ -239,9 +248,11 @@ export class ChatSession {
|
|
|
239
248
|
compactionTimeoutMs;
|
|
240
249
|
maxSteps;
|
|
241
250
|
effort;
|
|
251
|
+
maxOutputTokens;
|
|
242
252
|
permissionMode;
|
|
243
253
|
permissionResolver;
|
|
244
254
|
permissionGate;
|
|
255
|
+
gitCoAuthorEnabled;
|
|
245
256
|
skillDirs;
|
|
246
257
|
customSystemPrompt;
|
|
247
258
|
/** 最近一次 chat() 使用的 system prompt(供 history 等读取) */
|
|
@@ -266,6 +277,7 @@ export class ChatSession {
|
|
|
266
277
|
baseURL: this.baseURL,
|
|
267
278
|
model: this.subModelId || this.modelId,
|
|
268
279
|
effort: this.effort,
|
|
280
|
+
maxOutputTokens: this.maxOutputTokens,
|
|
269
281
|
}, {
|
|
270
282
|
cwd: taskCwd,
|
|
271
283
|
persist: false,
|
|
@@ -303,6 +315,7 @@ export class ChatSession {
|
|
|
303
315
|
this.subModelId = (overrides.subModel ?? appConfig.subModel ?? "").trim();
|
|
304
316
|
this.provider = normalizeDeepCccProvider(overrides.provider ?? appConfig.provider);
|
|
305
317
|
this.effort = (overrides.effort ?? appConfig.effort ?? "").trim();
|
|
318
|
+
this.maxOutputTokens = normalizeMaxOutputTokens(overrides.maxOutputTokens ?? appConfig.maxOutputTokens);
|
|
306
319
|
this.apiKey = apiKey;
|
|
307
320
|
this.baseURL = baseURL;
|
|
308
321
|
const provider = this.provider === "anthropic"
|
|
@@ -311,7 +324,7 @@ export class ChatSession {
|
|
|
311
324
|
apiKey,
|
|
312
325
|
})
|
|
313
326
|
: createOpenAICompatible({
|
|
314
|
-
name:
|
|
327
|
+
name: OPENAI_COMPATIBLE_PROVIDER_NAME,
|
|
315
328
|
baseURL,
|
|
316
329
|
apiKey,
|
|
317
330
|
includeUsage: true,
|
|
@@ -325,6 +338,7 @@ export class ChatSession {
|
|
|
325
338
|
this.customSystemPrompt = options.systemPrompt ?? "";
|
|
326
339
|
this.permissionMode = options.permissionMode ?? "ask";
|
|
327
340
|
this.permissionResolver = options.permissionResolver;
|
|
341
|
+
this.gitCoAuthorEnabled = options.gitCoAuthor ?? appConfig.git.coAuthor.enabled;
|
|
328
342
|
// 技能目录在构造时确定;技能内容在每次 chat() 前重新扫描(mtime 热加载),
|
|
329
343
|
// 因此创建/修改技能后下一次对话自动生效,无需重启。
|
|
330
344
|
this.skillDirs =
|
|
@@ -413,14 +427,14 @@ export class ChatSession {
|
|
|
413
427
|
? addAnthropicToolJsonCompatibilityNote(hintedMessages)
|
|
414
428
|
: hintedMessages;
|
|
415
429
|
// effort 按协议映射:
|
|
416
|
-
// - OpenAI 兼容:providerOptions.
|
|
417
|
-
//
|
|
430
|
+
// - OpenAI 兼容:providerOptions 的 key 必须与 createOpenAICompatible.name 一致;
|
|
431
|
+
// reasoningEffort 由 SDK 映射为请求体 reasoning_effort 字段;
|
|
418
432
|
// - Anthropic:providerOptions.anthropic.effort 由 @ai-sdk/anthropic 组装为请求体
|
|
419
433
|
// output_config.effort(官方 Effort API,见 platform.claude.com/docs/en/build-with-claude/effort)
|
|
420
434
|
let effortProviderOptions;
|
|
421
435
|
if (this.effort) {
|
|
422
436
|
effortProviderOptions = this.provider === "openai"
|
|
423
|
-
? {
|
|
437
|
+
? { [OPENAI_COMPATIBLE_PROVIDER_NAME]: { reasoningEffort: this.effort } }
|
|
424
438
|
: { anthropic: { effort: this.effort } };
|
|
425
439
|
}
|
|
426
440
|
const baseGenerationOptions = {
|
|
@@ -429,9 +443,16 @@ export class ChatSession {
|
|
|
429
443
|
tools: createBuiltinFileTools(this.cwd, {
|
|
430
444
|
permissionGate: this.permissionGate,
|
|
431
445
|
runTask: this.runTask,
|
|
446
|
+
gitCoAuthor: {
|
|
447
|
+
...appConfig.git.coAuthor,
|
|
448
|
+
enabled: this.gitCoAuthorEnabled,
|
|
449
|
+
},
|
|
432
450
|
}),
|
|
433
451
|
stopWhen: maxSteps !== undefined ? stepCountIs(maxSteps) : isLoopFinished(),
|
|
434
452
|
abortSignal: signal,
|
|
453
|
+
...(this.maxOutputTokens !== undefined
|
|
454
|
+
? { maxOutputTokens: this.maxOutputTokens }
|
|
455
|
+
: {}),
|
|
435
456
|
...(effortProviderOptions ? { providerOptions: effortProviderOptions } : {}),
|
|
436
457
|
};
|
|
437
458
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
@@ -624,7 +645,7 @@ export class ChatSession {
|
|
|
624
645
|
// output_config.effort=low),避免继承主对话的高 effort 拖慢"压缩上下文中"阶段。
|
|
625
646
|
maxOutputTokens: MAX_COMPACTION_OUTPUT_TOKENS,
|
|
626
647
|
providerOptions: this.provider === "openai"
|
|
627
|
-
? {
|
|
648
|
+
? { [OPENAI_COMPATIBLE_PROVIDER_NAME]: { reasoningEffort: "none" } }
|
|
628
649
|
: { anthropic: { effort: "low" } },
|
|
629
650
|
});
|
|
630
651
|
if (!result.text.trim()) {
|