deepccc 0.1.25 → 0.1.26
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 +11 -0
- package/dist/cli.js +5 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.js +2 -0
- package/dist/file-tools.d.ts +18 -0
- package/dist/file-tools.js +22 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +84 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -75,6 +75,7 @@ $env:DEEPCCC_STREAMING="true"
|
|
|
75
75
|
"apiKey": "sk-...",
|
|
76
76
|
"baseURL": "https://api.deepseek.com/v1",
|
|
77
77
|
"model": "deepseek-v4-pro",
|
|
78
|
+
"subModel": "",
|
|
78
79
|
"effort": "",
|
|
79
80
|
"streaming": true,
|
|
80
81
|
"contextWindow": 1048576,
|
|
@@ -102,6 +103,16 @@ $env:DEEPCCC_STREAMING="true"
|
|
|
102
103
|
API 拒绝(context length exceeded),实际窗口以模型与所用服务端为准(如 litellm 的
|
|
103
104
|
`max_input_tokens`)。
|
|
104
105
|
|
|
106
|
+
`subModel` 是子模型(选填),默认 `""`(留空跟随主模型)。配置后,DeepCCC 内部的轻量
|
|
107
|
+
环节——上下文压缩摘要生成、`task` 子代理任务——使用子模型执行,主对话仍用主模型。
|
|
108
|
+
典型用法:主模型用 pro 承担复杂推理,子模型用 flash 做高频廉价的摘要与子任务。
|
|
109
|
+
可通过 `DEEPCCC_SUB_MODEL` 环境变量或命令行 `--sub-model` 覆盖。
|
|
110
|
+
|
|
111
|
+
`task` 子代理工具:主模型可把边界清晰的独立子任务(仓库调研、长文档阅读、独立模块生成)
|
|
112
|
+
委派给子代理执行——子代理使用子模型、独立上下文,不污染主对话上下文;结果截断回传。
|
|
113
|
+
子代理**不能再次委派**(禁止嵌套),单轮最多 20 个工具步,超时与主会话压缩超时一致。
|
|
114
|
+
仅在配置了子模型时建议使用(未配置时子代理跟随主模型,节省有限)。
|
|
115
|
+
|
|
105
116
|
`rawStreamLogs.enabled` 默认 `true`,通过 `DEEPCCC_RAW_STREAM_LOGS` 环境变量或配置 JSON 关闭。
|
|
106
117
|
开启时,每次对话的原始流按 gzip JSONL 落到 `~/.deepccc/raw-stream-logs/`,供
|
|
107
118
|
`session_search` 工具在会话被压缩后找回被压缩消息的精确原文(检索时设置
|
package/dist/cli.js
CHANGED
|
@@ -57,6 +57,10 @@ function parseArgs(argv = process.argv.slice(2)) {
|
|
|
57
57
|
config.model = next;
|
|
58
58
|
i++;
|
|
59
59
|
}
|
|
60
|
+
else if (arg === "--sub-model" && next !== undefined) {
|
|
61
|
+
config.subModel = next;
|
|
62
|
+
i++;
|
|
63
|
+
}
|
|
60
64
|
else if (arg === "--effort" && next !== undefined) {
|
|
61
65
|
config.effort = next;
|
|
62
66
|
i++;
|
|
@@ -125,6 +129,7 @@ function printHelp(appConfig) {
|
|
|
125
129
|
"Options:",
|
|
126
130
|
` --provider <name> API protocol: openai or anthropic (current default ${appConfig.provider})`,
|
|
127
131
|
` --model <name> Model name (current default ${appConfig.model})`,
|
|
132
|
+
` --sub-model <name> Sub-model for lightweight steps (compaction/task; empty = follow main model)`,
|
|
128
133
|
` --effort <level> Reasoning effort: none/minimal/low/medium/high/xhigh/max (overrides config.effort)`,
|
|
129
134
|
` --base-url <url> Provider API base URL (current default ${appConfig.baseURL})`,
|
|
130
135
|
" --api-key <key> API key",
|
package/dist/config.d.ts
CHANGED
|
@@ -5,6 +5,11 @@ export interface DeepCccConfig {
|
|
|
5
5
|
apiKey: string;
|
|
6
6
|
baseURL: string;
|
|
7
7
|
model: string;
|
|
8
|
+
/**
|
|
9
|
+
* 子模型(可选):用于内部轻量环节(上下文压缩摘要生成、task 子代理任务)。
|
|
10
|
+
* 留空("")时跟随主模型,行为与旧版完全一致。
|
|
11
|
+
*/
|
|
12
|
+
subModel: string;
|
|
8
13
|
/** Reasoning effort(none/minimal/low/medium/high/xhigh/max),留空不传 reasoning_effort */
|
|
9
14
|
effort: string;
|
|
10
15
|
/** 主对话是否使用流式请求;默认开启 */
|
package/dist/config.js
CHANGED
|
@@ -12,6 +12,7 @@ export const DEFAULT_CONFIG = {
|
|
|
12
12
|
apiKey: "",
|
|
13
13
|
baseURL: "https://api.deepseek.com/v1",
|
|
14
14
|
model: "deepseek-v4-pro",
|
|
15
|
+
subModel: "",
|
|
15
16
|
effort: "",
|
|
16
17
|
streaming: true,
|
|
17
18
|
contextWindow: 1_048_576,
|
|
@@ -66,6 +67,7 @@ function loadConfig() {
|
|
|
66
67
|
apiKey: env("DEEPCCC_API_KEY") ?? env("DEEPSEEK_API_KEY") ?? file.apiKey ?? DEFAULT_CONFIG.apiKey,
|
|
67
68
|
baseURL: env("DEEPCCC_BASE_URL") ?? env("DEEPSEEK_BASE_URL") ?? file.baseURL ?? DEFAULT_CONFIG.baseURL,
|
|
68
69
|
model: env("DEEPCCC_MODEL") ?? env("DEEPSEEK_MODEL") ?? file.model ?? DEFAULT_CONFIG.model,
|
|
70
|
+
subModel: env("DEEPCCC_SUB_MODEL") ?? file.subModel ?? DEFAULT_CONFIG.subModel,
|
|
69
71
|
effort: env("DEEPCCC_EFFORT") ?? env("DEEPSEEK_EFFORT") ?? file.effort ?? DEFAULT_CONFIG.effort,
|
|
70
72
|
streaming: boolEnv("DEEPCCC_STREAMING") ?? file.streaming ?? DEFAULT_CONFIG.streaming,
|
|
71
73
|
contextWindow: numberEnv("DEEPCCC_CONTEXT_WINDOW") ?? file.contextWindow ?? DEFAULT_CONFIG.contextWindow,
|
package/dist/file-tools.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type ToolSet } from "ai";
|
|
2
2
|
import { type PermissionGate } from "./permissions.js";
|
|
3
|
+
/** task 子代理工具:子任务结果回传主会话前的最大字符数(防止子代理长输出撑爆主上下文) */
|
|
4
|
+
export declare const MAX_TASK_OUTPUT_CHARS = 32000;
|
|
3
5
|
export interface ReadFileInput {
|
|
4
6
|
path: string;
|
|
5
7
|
startLine?: number;
|
|
@@ -155,5 +157,21 @@ export interface BuiltinFileToolsOptions {
|
|
|
155
157
|
contextDir?: string;
|
|
156
158
|
rawLogsDir?: string;
|
|
157
159
|
};
|
|
160
|
+
/**
|
|
161
|
+
* task 子代理工具的执行回调:把独立子任务委派给子会话(使用子模型、独立上下文)。
|
|
162
|
+
* 未提供时 task 工具不可用(抛出明确错误),主会话之外的工具集不会意外开启子代理。
|
|
163
|
+
*/
|
|
164
|
+
runTask?: TaskRunner;
|
|
165
|
+
}
|
|
166
|
+
export interface TaskRunnerInput {
|
|
167
|
+
/** 子任务描述:包含目标、约束与交付物,子代理将以此作为唯一指令。 */
|
|
168
|
+
description: string;
|
|
169
|
+
/** 可选子任务工作目录(相对主会话 cwd 或绝对路径),默认继承主会话工作目录。 */
|
|
170
|
+
cwd?: string;
|
|
171
|
+
}
|
|
172
|
+
export interface TaskRunnerOutput {
|
|
173
|
+
/** 子代理最终文本回复(已截断到 MAX_TASK_OUTPUT_CHARS,已过隐私替换)。 */
|
|
174
|
+
result: string;
|
|
158
175
|
}
|
|
176
|
+
export type TaskRunner = (input: TaskRunnerInput, signal?: AbortSignal) => Promise<string>;
|
|
159
177
|
export declare function createBuiltinFileTools(cwd: string, options?: BuiltinFileToolsOptions): ToolSet;
|
package/dist/file-tools.js
CHANGED
|
@@ -22,6 +22,8 @@ const SEARCH_TIMEOUT_MS = 15_000;
|
|
|
22
22
|
const MAX_COMMAND_OUTPUT_BYTES = 256 * 1024;
|
|
23
23
|
const DEFAULT_COMMAND_TIMEOUT_MS = 120_000;
|
|
24
24
|
const MAX_COMMAND_TIMEOUT_MS = 900_000;
|
|
25
|
+
/** task 子代理工具:子任务结果回传主会话前的最大字符数(防止子代理长输出撑爆主上下文) */
|
|
26
|
+
export const MAX_TASK_OUTPUT_CHARS = 32_000;
|
|
25
27
|
const requireFromHere = createRequire(import.meta.url);
|
|
26
28
|
const FALLBACK_SKIPPED_DIRECTORIES = new Set([".git", "node_modules"]);
|
|
27
29
|
/**
|
|
@@ -939,6 +941,7 @@ export async function applyPatchForTool(cwd, input) {
|
|
|
939
941
|
}
|
|
940
942
|
export function createBuiltinFileTools(cwd, options = {}) {
|
|
941
943
|
const gate = options.permissionGate;
|
|
944
|
+
const runTask = options.runTask;
|
|
942
945
|
/** 文件路径的候选匹配键:原始路径 + 绝对路径 + 相对 cwd 路径(正/反斜杠双版本,规则可任选其一) */
|
|
943
946
|
const pathKeys = (p) => {
|
|
944
947
|
const abs = resolveToolPath(cwd, p);
|
|
@@ -1023,6 +1026,25 @@ export function createBuiltinFileTools(cwd, options = {}) {
|
|
|
1023
1026
|
return runCommandForTool(cwd, input, options.abortSignal);
|
|
1024
1027
|
},
|
|
1025
1028
|
}),
|
|
1029
|
+
task: tool({
|
|
1030
|
+
description: "把独立子任务委派给子代理执行:子代理使用子模型、拥有独立上下文,不污染主对话上下文。适合边界清晰、可独立交付的调研/代码生成子任务(如扫描整个仓库、阅读长文档、生成独立模块)。子代理不能再次委派任务(禁止嵌套),结果会截断回传。",
|
|
1031
|
+
inputSchema: jsonSchema({
|
|
1032
|
+
type: "object",
|
|
1033
|
+
additionalProperties: false,
|
|
1034
|
+
properties: {
|
|
1035
|
+
description: { type: "string", description: "子任务描述:目标、约束与交付物。请写清楚子代理需要返回什么。" },
|
|
1036
|
+
cwd: { type: "string", description: "可选子任务工作目录(绝对路径或相对主会话 cwd),默认继承主会话工作目录。" },
|
|
1037
|
+
},
|
|
1038
|
+
required: ["description"],
|
|
1039
|
+
}),
|
|
1040
|
+
execute: async (input, execOptions) => {
|
|
1041
|
+
if (!runTask) {
|
|
1042
|
+
throw new Error("task 工具不可用:当前环境未启用子代理执行器");
|
|
1043
|
+
}
|
|
1044
|
+
const result = await runTask(input, execOptions.abortSignal);
|
|
1045
|
+
return { result };
|
|
1046
|
+
},
|
|
1047
|
+
}),
|
|
1026
1048
|
edit_file: tool({
|
|
1027
1049
|
description: "通过精确的 oldText -> newText 替换编辑现有 UTF-8 文本文件。可行时使用 SHA-256 前置条件以避免覆盖并发编辑。",
|
|
1028
1050
|
inputSchema: jsonSchema({
|
package/dist/index.d.ts
CHANGED
|
@@ -28,6 +28,10 @@ export interface ChatSessionConfig {
|
|
|
28
28
|
apiKey?: string;
|
|
29
29
|
/** Model id. Defaults to DEEPCCC_MODEL/config. */
|
|
30
30
|
model?: string;
|
|
31
|
+
/**
|
|
32
|
+
* 子模型 id(可选):用于压缩摘要生成与 task 子代理任务。留空跟随主模型。
|
|
33
|
+
*/
|
|
34
|
+
subModel?: string;
|
|
31
35
|
/**
|
|
32
36
|
* Reasoning effort (none/minimal/low/medium/high/xhigh/max);
|
|
33
37
|
* overrides config.effort; empty omits the reasoning_effort request field.
|
|
@@ -115,17 +119,30 @@ interface ChatMessage {
|
|
|
115
119
|
}
|
|
116
120
|
export declare class ChatSession {
|
|
117
121
|
private model;
|
|
122
|
+
/** 子模型实例;未配置 subModel 时与主模型同一实例 */
|
|
123
|
+
private subModel;
|
|
118
124
|
private provider;
|
|
125
|
+
private apiKey;
|
|
126
|
+
private baseURL;
|
|
127
|
+
private modelId;
|
|
128
|
+
private subModelId;
|
|
119
129
|
private cwd;
|
|
120
130
|
private context;
|
|
121
131
|
private compactionTimeoutMs;
|
|
122
132
|
private maxSteps?;
|
|
123
133
|
private effort;
|
|
134
|
+
private permissionMode;
|
|
135
|
+
private permissionResolver?;
|
|
124
136
|
private permissionGate;
|
|
125
137
|
private skillDirs;
|
|
126
138
|
private customSystemPrompt;
|
|
127
139
|
/** 最近一次 chat() 使用的 system prompt(供 history 等读取) */
|
|
128
140
|
private systemPrompt;
|
|
141
|
+
/**
|
|
142
|
+
* task 子代理工具执行器:用子模型开独立子会话(独立上下文、独立 cwd)执行子任务,
|
|
143
|
+
* 回传最终文本(截断 + 隐私替换)。单层:子会话的工具集不包含 runTask,天然禁止嵌套。
|
|
144
|
+
*/
|
|
145
|
+
private runTask;
|
|
129
146
|
constructor(overrides?: ChatSessionConfig, options?: ChatSessionOptions);
|
|
130
147
|
/**
|
|
131
148
|
* 组装系统提示词。顺序遵循“稳定性优先”原则(缓存命中友好):
|
package/dist/index.js
CHANGED
|
@@ -8,12 +8,12 @@ import { createAnthropic } from "@ai-sdk/anthropic";
|
|
|
8
8
|
import { generateText, isLoopFinished, stepCountIs, streamText } from "ai";
|
|
9
9
|
import { existsSync, readFileSync } from "node:fs";
|
|
10
10
|
import { homedir } from "node:os";
|
|
11
|
-
import { join } from "node:path";
|
|
11
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
13
|
import { config as appConfig, normalizeDeepCccProvider, RAW_STREAM_LOGS_DIR, } from "./config.js";
|
|
14
14
|
import { createRawStreamLog, } from "./raw-stream-log.js";
|
|
15
15
|
import { buildPersistedAssistantMessage, buildSummaryPrompt, BuiltinContextManager, defaultBuiltinSessionId, } from "./context.js";
|
|
16
|
-
import { createBuiltinFileTools } from "./file-tools.js";
|
|
16
|
+
import { createBuiltinFileTools, MAX_TASK_OUTPUT_CHARS } from "./file-tools.js";
|
|
17
17
|
import { PermissionGate } from "./permissions.js";
|
|
18
18
|
import { buildDefaultSkillDirs, buildSkillsIndexPrompt, scanSkillsDirs, } from "./skills.js";
|
|
19
19
|
import { applyPrivacy, applyPrivacyToJson } from "./privacy.js";
|
|
@@ -76,6 +76,15 @@ const COMPACTION_RECOVERY_HINT_DISABLED = [
|
|
|
76
76
|
].join("\n");
|
|
77
77
|
export const DEFAULT_COMPACTION_TIMEOUT_MS = 5 * 60 * 1000;
|
|
78
78
|
const MAX_COMPACTION_OUTPUT_TOKENS = 16_384;
|
|
79
|
+
/** task 子代理工具:子代理单轮对话的最大工具步数(防失控循环,结果收敛后即结束) */
|
|
80
|
+
const TASK_MAX_STEPS = 20;
|
|
81
|
+
/** 子代理最终输出截断:保留前 MAX_TASK_OUTPUT_CHARS 字符,尾部注明截断信息 */
|
|
82
|
+
function truncateTaskOutput(text) {
|
|
83
|
+
if (text.length <= MAX_TASK_OUTPUT_CHARS)
|
|
84
|
+
return text;
|
|
85
|
+
return (text.slice(0, MAX_TASK_OUTPUT_CHARS) +
|
|
86
|
+
`\n…[子代理输出已截断,共 ${text.length} 字符,仅保留前 ${MAX_TASK_OUTPUT_CHARS} 字符]`);
|
|
87
|
+
}
|
|
79
88
|
const ANTHROPIC_TOOL_JSON_COMPATIBILITY_NOTE = [
|
|
80
89
|
"[Protocol compatibility note]",
|
|
81
90
|
"tool-call arguments use JSON encoding; the final reply does not need to be JSON unless the user requests it.",
|
|
@@ -217,17 +226,71 @@ function normalizeAnthropicBaseURL(baseURL) {
|
|
|
217
226
|
}
|
|
218
227
|
export class ChatSession {
|
|
219
228
|
model;
|
|
229
|
+
/** 子模型实例;未配置 subModel 时与主模型同一实例 */
|
|
230
|
+
subModel;
|
|
220
231
|
provider;
|
|
232
|
+
apiKey;
|
|
233
|
+
baseURL;
|
|
234
|
+
modelId;
|
|
235
|
+
subModelId;
|
|
221
236
|
cwd;
|
|
222
237
|
context;
|
|
223
238
|
compactionTimeoutMs;
|
|
224
239
|
maxSteps;
|
|
225
240
|
effort;
|
|
241
|
+
permissionMode;
|
|
242
|
+
permissionResolver;
|
|
226
243
|
permissionGate;
|
|
227
244
|
skillDirs;
|
|
228
245
|
customSystemPrompt;
|
|
229
246
|
/** 最近一次 chat() 使用的 system prompt(供 history 等读取) */
|
|
230
247
|
systemPrompt = "";
|
|
248
|
+
/**
|
|
249
|
+
* task 子代理工具执行器:用子模型开独立子会话(独立上下文、独立 cwd)执行子任务,
|
|
250
|
+
* 回传最终文本(截断 + 隐私替换)。单层:子会话的工具集不包含 runTask,天然禁止嵌套。
|
|
251
|
+
*/
|
|
252
|
+
runTask = async (input, signal) => {
|
|
253
|
+
const rawCwd = input.cwd?.trim();
|
|
254
|
+
const taskCwd = rawCwd ? (isAbsolute(rawCwd) ? rawCwd : resolve(this.cwd, rawCwd)) : this.cwd;
|
|
255
|
+
const timeoutController = new AbortController();
|
|
256
|
+
const timeout = setTimeout(() => timeoutController.abort(), this.compactionTimeoutMs);
|
|
257
|
+
timeout.unref?.();
|
|
258
|
+
const taskSignal = signal
|
|
259
|
+
? AbortSignal.any([signal, timeoutController.signal])
|
|
260
|
+
: timeoutController.signal;
|
|
261
|
+
try {
|
|
262
|
+
const child = new ChatSession({
|
|
263
|
+
provider: this.provider,
|
|
264
|
+
apiKey: this.apiKey,
|
|
265
|
+
baseURL: this.baseURL,
|
|
266
|
+
model: this.subModelId || this.modelId,
|
|
267
|
+
effort: this.effort,
|
|
268
|
+
}, {
|
|
269
|
+
cwd: taskCwd,
|
|
270
|
+
persist: false,
|
|
271
|
+
permissionMode: this.permissionMode,
|
|
272
|
+
permissionResolver: this.permissionResolver,
|
|
273
|
+
maxSteps: TASK_MAX_STEPS,
|
|
274
|
+
compactionTimeoutMs: this.compactionTimeoutMs,
|
|
275
|
+
skillsDirs: this.skillDirs.map((s) => s.dir),
|
|
276
|
+
});
|
|
277
|
+
let full = "";
|
|
278
|
+
for await (const event of child.chat(input.description, taskSignal)) {
|
|
279
|
+
if (event.type === "text") {
|
|
280
|
+
full += event.text;
|
|
281
|
+
}
|
|
282
|
+
else if (event.type === "error") {
|
|
283
|
+
throw new Error(`task 子代理执行失败: ${event.message}`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
if (!full.trim())
|
|
287
|
+
return "(子代理未返回文本内容)";
|
|
288
|
+
return applyPrivacy(truncateTaskOutput(full));
|
|
289
|
+
}
|
|
290
|
+
finally {
|
|
291
|
+
clearTimeout(timeout);
|
|
292
|
+
}
|
|
293
|
+
};
|
|
231
294
|
constructor(overrides = {}, options = {}) {
|
|
232
295
|
const apiKey = overrides.apiKey ?? appConfig.apiKey;
|
|
233
296
|
if (!apiKey) {
|
|
@@ -235,28 +298,32 @@ export class ChatSession {
|
|
|
235
298
|
}
|
|
236
299
|
const baseURL = overrides.baseURL ?? appConfig.baseURL;
|
|
237
300
|
const modelId = overrides.model ?? appConfig.model;
|
|
301
|
+
this.modelId = modelId;
|
|
302
|
+
this.subModelId = (overrides.subModel ?? appConfig.subModel ?? "").trim();
|
|
238
303
|
this.provider = normalizeDeepCccProvider(overrides.provider ?? appConfig.provider);
|
|
239
304
|
this.effort = (overrides.effort ?? appConfig.effort ?? "").trim();
|
|
240
|
-
|
|
241
|
-
|
|
305
|
+
this.apiKey = apiKey;
|
|
306
|
+
this.baseURL = baseURL;
|
|
307
|
+
const provider = this.provider === "anthropic"
|
|
308
|
+
? createAnthropic({
|
|
242
309
|
baseURL: normalizeAnthropicBaseURL(baseURL),
|
|
243
310
|
apiKey,
|
|
244
|
-
})
|
|
245
|
-
|
|
246
|
-
}
|
|
247
|
-
else {
|
|
248
|
-
const provider = createOpenAICompatible({
|
|
311
|
+
})
|
|
312
|
+
: createOpenAICompatible({
|
|
249
313
|
name: "deepccc",
|
|
250
314
|
baseURL,
|
|
251
315
|
apiKey,
|
|
252
316
|
includeUsage: true,
|
|
253
317
|
});
|
|
254
|
-
|
|
255
|
-
|
|
318
|
+
this.model = provider(modelId);
|
|
319
|
+
// 子模型:留空时与主模型共用同一实例(行为与旧版完全一致,零开销)
|
|
320
|
+
this.subModel = this.subModelId ? provider(this.subModelId) : this.model;
|
|
256
321
|
this.cwd = options.cwd ?? process.cwd();
|
|
257
322
|
this.maxSteps = normalizeMaxSteps(options.maxSteps);
|
|
258
323
|
this.compactionTimeoutMs = Math.max(1, options.compactionTimeoutMs ?? DEFAULT_COMPACTION_TIMEOUT_MS);
|
|
259
324
|
this.customSystemPrompt = options.systemPrompt ?? "";
|
|
325
|
+
this.permissionMode = options.permissionMode ?? "ask";
|
|
326
|
+
this.permissionResolver = options.permissionResolver;
|
|
260
327
|
// 技能目录在构造时确定;技能内容在每次 chat() 前重新扫描(mtime 热加载),
|
|
261
328
|
// 因此创建/修改技能后下一次对话自动生效,无需重启。
|
|
262
329
|
this.skillDirs =
|
|
@@ -271,7 +338,7 @@ export class ChatSession {
|
|
|
271
338
|
compactAtTokens: options.compactAtTokens,
|
|
272
339
|
keepRecentMessages: options.keepRecentMessages,
|
|
273
340
|
});
|
|
274
|
-
this.permissionGate = new PermissionGate(
|
|
341
|
+
this.permissionGate = new PermissionGate(this.permissionMode, this.permissionResolver);
|
|
275
342
|
}
|
|
276
343
|
/**
|
|
277
344
|
* 组装系统提示词。顺序遵循“稳定性优先”原则(缓存命中友好):
|
|
@@ -360,7 +427,10 @@ export class ChatSession {
|
|
|
360
427
|
model: this.model,
|
|
361
428
|
system,
|
|
362
429
|
messages: modelMessages,
|
|
363
|
-
tools: createBuiltinFileTools(this.cwd, {
|
|
430
|
+
tools: createBuiltinFileTools(this.cwd, {
|
|
431
|
+
permissionGate: this.permissionGate,
|
|
432
|
+
runTask: this.runTask,
|
|
433
|
+
}),
|
|
364
434
|
stopWhen: maxSteps !== undefined ? stepCountIs(maxSteps) : isLoopFinished(),
|
|
365
435
|
abortSignal: signal,
|
|
366
436
|
...(effortProviderOptions ? { providerOptions: effortProviderOptions } : {}),
|
|
@@ -500,7 +570,7 @@ export class ChatSession {
|
|
|
500
570
|
if (!plan)
|
|
501
571
|
return 0;
|
|
502
572
|
const result = await generateText({
|
|
503
|
-
model: this.
|
|
573
|
+
model: this.subModel,
|
|
504
574
|
system: SUMMARY_SYSTEM_PROMPT,
|
|
505
575
|
messages: [{ role: "user", content: buildSummaryPrompt(plan) }],
|
|
506
576
|
abortSignal: compactionSignal,
|