zencode-cli 0.2.1 → 0.2.3
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/LICENSE +21 -0
- package/README.md +117 -287
- package/dist/bin/zencode.js +1440 -1545
- package/dist/bin/zencode.js.map +1 -1
- package/package.json +56 -56
- package/DESIGN.md +0 -613
package/dist/bin/zencode.js
CHANGED
|
@@ -9,129 +9,16 @@ var __export = (target, all) => {
|
|
|
9
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
// src/config/defaults.ts
|
|
13
|
-
var DEFAULT_CONFIG;
|
|
14
|
-
var init_defaults = __esm({
|
|
15
|
-
"src/config/defaults.ts"() {
|
|
16
|
-
"use strict";
|
|
17
|
-
DEFAULT_CONFIG = {
|
|
18
|
-
model: "deepseek-chat",
|
|
19
|
-
api_key: "",
|
|
20
|
-
base_url: "https://api.deepseek.com/v1",
|
|
21
|
-
temperature: 0.7,
|
|
22
|
-
max_tokens: 8192,
|
|
23
|
-
agent_mode: "dual",
|
|
24
|
-
collaboration: "delegated",
|
|
25
|
-
dual_agent: {},
|
|
26
|
-
features: {
|
|
27
|
-
git: "auto",
|
|
28
|
-
mcp: "off",
|
|
29
|
-
planning_layer: "on",
|
|
30
|
-
parallel_agents: "on",
|
|
31
|
-
todo: "on"
|
|
32
|
-
},
|
|
33
|
-
permissions: {
|
|
34
|
-
auto_approve: ["read-file", "glob", "grep", "spawn-agents", "todo", "memo"],
|
|
35
|
-
require_approval: ["write-file", "edit-file", "bash", "git"]
|
|
36
|
-
},
|
|
37
|
-
mcp_servers: [],
|
|
38
|
-
prompts: [],
|
|
39
|
-
max_tool_output: 3e4
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
// src/config/loader.ts
|
|
45
|
-
import * as fs from "fs";
|
|
46
|
-
import * as path from "path";
|
|
47
|
-
import * as os from "os";
|
|
48
|
-
import { parse as parseYaml } from "yaml";
|
|
49
|
-
function deepMerge(target, source) {
|
|
50
|
-
const result = { ...target };
|
|
51
|
-
for (const key of Object.keys(source)) {
|
|
52
|
-
const sourceVal = source[key];
|
|
53
|
-
const targetVal = target[key];
|
|
54
|
-
if (sourceVal !== void 0 && sourceVal !== null && typeof sourceVal === "object" && !Array.isArray(sourceVal) && typeof targetVal === "object" && !Array.isArray(targetVal) && targetVal !== null) {
|
|
55
|
-
result[key] = deepMerge(targetVal, sourceVal);
|
|
56
|
-
} else if (sourceVal !== void 0) {
|
|
57
|
-
result[key] = sourceVal;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return result;
|
|
61
|
-
}
|
|
62
|
-
function loadYamlFile(filePath) {
|
|
63
|
-
try {
|
|
64
|
-
const content = fs.readFileSync(filePath, "utf-8");
|
|
65
|
-
return parseYaml(content) || {};
|
|
66
|
-
} catch {
|
|
67
|
-
return {};
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
function loadEnvConfig() {
|
|
71
|
-
const config = {};
|
|
72
|
-
if (process.env["ZENCODE_API_KEY"]) {
|
|
73
|
-
config.api_key = process.env["ZENCODE_API_KEY"];
|
|
74
|
-
}
|
|
75
|
-
if (process.env["ZENCODE_MODEL"]) {
|
|
76
|
-
config.model = process.env["ZENCODE_MODEL"];
|
|
77
|
-
}
|
|
78
|
-
if (process.env["ZENCODE_BASE_URL"]) {
|
|
79
|
-
config.base_url = process.env["ZENCODE_BASE_URL"];
|
|
80
|
-
}
|
|
81
|
-
if (process.env["ZENCODE_MODE"]) {
|
|
82
|
-
const mode = process.env["ZENCODE_MODE"];
|
|
83
|
-
if (mode === "dual" || mode === "single") {
|
|
84
|
-
config.agent_mode = mode;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return config;
|
|
88
|
-
}
|
|
89
|
-
function loadCliConfig(opts) {
|
|
90
|
-
const config = {};
|
|
91
|
-
if (opts.model) config.model = opts.model;
|
|
92
|
-
if (opts.apiKey) config.api_key = opts.apiKey;
|
|
93
|
-
if (opts.baseUrl) config.base_url = opts.baseUrl;
|
|
94
|
-
if (opts.single) config.agent_mode = "single";
|
|
95
|
-
if (opts.dual) config.agent_mode = "dual";
|
|
96
|
-
if (opts.mode) {
|
|
97
|
-
const m = opts.mode;
|
|
98
|
-
if (m === "delegated" || m === "autonomous" || m === "controlled") {
|
|
99
|
-
config.collaboration = m;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
return config;
|
|
103
|
-
}
|
|
104
|
-
function loadConfig(cliOpts = {}) {
|
|
105
|
-
const globalConfigPath = path.join(os.homedir(), ".zencode", "config.yaml");
|
|
106
|
-
const projectDirConfigPath = path.resolve(".zencode", "config.yaml");
|
|
107
|
-
const projectFileConfigPath = path.resolve(".zencode.yaml");
|
|
108
|
-
let config = { ...DEFAULT_CONFIG };
|
|
109
|
-
config = deepMerge(config, loadYamlFile(globalConfigPath));
|
|
110
|
-
config = deepMerge(config, loadYamlFile(projectDirConfigPath));
|
|
111
|
-
config = deepMerge(config, loadYamlFile(projectFileConfigPath));
|
|
112
|
-
config = deepMerge(config, loadEnvConfig());
|
|
113
|
-
config = deepMerge(config, loadCliConfig(cliOpts));
|
|
114
|
-
return config;
|
|
115
|
-
}
|
|
116
|
-
function resolveModelConfig(config, role) {
|
|
117
|
-
const roleConfig = config.dual_agent[role] || {};
|
|
118
|
-
return {
|
|
119
|
-
model: roleConfig.model || config.model,
|
|
120
|
-
api_key: roleConfig.api_key || config.api_key,
|
|
121
|
-
base_url: roleConfig.base_url || config.base_url,
|
|
122
|
-
temperature: roleConfig.temperature ?? config.temperature,
|
|
123
|
-
max_tokens: roleConfig.max_tokens ?? config.max_tokens
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
var init_loader = __esm({
|
|
127
|
-
"src/config/loader.ts"() {
|
|
128
|
-
"use strict";
|
|
129
|
-
init_defaults();
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
|
|
133
12
|
// src/llm/client.ts
|
|
134
13
|
import OpenAI from "openai";
|
|
14
|
+
function isAbortError(error) {
|
|
15
|
+
if (!error) return false;
|
|
16
|
+
const err = error;
|
|
17
|
+
const name = err.name || err.cause?.name || "";
|
|
18
|
+
const code = err.code || err.cause?.code || "";
|
|
19
|
+
const message = err.message || "";
|
|
20
|
+
return name === "AbortError" || name === "APIUserAbortError" || code === "ABORT_ERR" || /abort|aborted|cancel|cancelled|canceled|interrupted/i.test(message);
|
|
21
|
+
}
|
|
135
22
|
function createLLMClient(options) {
|
|
136
23
|
return new LLMClient(options);
|
|
137
24
|
}
|
|
@@ -144,6 +31,7 @@ var init_client = __esm({
|
|
|
144
31
|
model;
|
|
145
32
|
temperature;
|
|
146
33
|
maxTokens;
|
|
34
|
+
activeAbortController = null;
|
|
147
35
|
constructor(options) {
|
|
148
36
|
this.client = new OpenAI({
|
|
149
37
|
apiKey: options.apiKey,
|
|
@@ -168,15 +56,44 @@ var init_client = __esm({
|
|
|
168
56
|
params.tools = tools;
|
|
169
57
|
params.tool_choice = "auto";
|
|
170
58
|
}
|
|
59
|
+
params.stream_options = { include_usage: true };
|
|
60
|
+
const abortController = new AbortController();
|
|
61
|
+
this.activeAbortController = abortController;
|
|
171
62
|
try {
|
|
172
|
-
const stream = await this.client.chat.completions.create(params
|
|
63
|
+
const stream = await this.client.chat.completions.create(params, {
|
|
64
|
+
signal: abortController.signal
|
|
65
|
+
});
|
|
173
66
|
let contentParts = [];
|
|
67
|
+
let reasoningParts = [];
|
|
68
|
+
let reasoningStarted = false;
|
|
69
|
+
let reasoningEnded = false;
|
|
174
70
|
const toolCallMap = /* @__PURE__ */ new Map();
|
|
71
|
+
let usageInfo = null;
|
|
175
72
|
for await (const chunk of stream) {
|
|
73
|
+
const chunkUsage = chunk.usage;
|
|
74
|
+
if (chunkUsage && chunkUsage.total_tokens) {
|
|
75
|
+
usageInfo = {
|
|
76
|
+
prompt_tokens: chunkUsage.prompt_tokens ?? 0,
|
|
77
|
+
completion_tokens: chunkUsage.completion_tokens ?? 0,
|
|
78
|
+
total_tokens: chunkUsage.total_tokens
|
|
79
|
+
};
|
|
80
|
+
}
|
|
176
81
|
const choice = chunk.choices[0];
|
|
177
82
|
if (!choice) continue;
|
|
178
83
|
const delta = choice.delta;
|
|
84
|
+
if (delta.reasoning_content) {
|
|
85
|
+
reasoningParts.push(delta.reasoning_content);
|
|
86
|
+
if (!reasoningStarted) {
|
|
87
|
+
reasoningStarted = true;
|
|
88
|
+
callbacks.onContent?.("<think>");
|
|
89
|
+
}
|
|
90
|
+
callbacks.onContent?.(delta.reasoning_content);
|
|
91
|
+
}
|
|
179
92
|
if (delta.content) {
|
|
93
|
+
if (reasoningStarted && !reasoningEnded) {
|
|
94
|
+
reasoningEnded = true;
|
|
95
|
+
callbacks.onContent?.("</think>");
|
|
96
|
+
}
|
|
180
97
|
contentParts.push(delta.content);
|
|
181
98
|
callbacks.onContent?.(delta.content);
|
|
182
99
|
}
|
|
@@ -204,6 +121,10 @@ var init_client = __esm({
|
|
|
204
121
|
}
|
|
205
122
|
}
|
|
206
123
|
}
|
|
124
|
+
if (reasoningStarted && !reasoningEnded) {
|
|
125
|
+
reasoningEnded = true;
|
|
126
|
+
callbacks.onContent?.("</think>");
|
|
127
|
+
}
|
|
207
128
|
const toolCalls = [];
|
|
208
129
|
for (const [, tc] of [...toolCallMap.entries()].sort(([a], [b]) => a - b)) {
|
|
209
130
|
const toolCall = {
|
|
@@ -218,19 +139,32 @@ var init_client = __esm({
|
|
|
218
139
|
callbacks.onToolCall?.(toolCall);
|
|
219
140
|
}
|
|
220
141
|
const fullContent = contentParts.join("");
|
|
142
|
+
const fullReasoning = reasoningParts.join("");
|
|
221
143
|
const assistantMessage = {
|
|
222
144
|
role: "assistant",
|
|
223
145
|
content: fullContent || null
|
|
224
146
|
};
|
|
147
|
+
if (fullReasoning) {
|
|
148
|
+
assistantMessage.reasoning_content = fullReasoning;
|
|
149
|
+
}
|
|
225
150
|
if (toolCalls.length > 0) {
|
|
226
151
|
assistantMessage.tool_calls = toolCalls;
|
|
227
152
|
}
|
|
153
|
+
if (usageInfo) {
|
|
154
|
+
assistantMessage.usage = usageInfo;
|
|
155
|
+
}
|
|
228
156
|
callbacks.onFinish?.(assistantMessage);
|
|
229
157
|
return assistantMessage;
|
|
230
158
|
} catch (error) {
|
|
231
159
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
232
|
-
|
|
160
|
+
if (!isAbortError(err)) {
|
|
161
|
+
callbacks.onError?.(err);
|
|
162
|
+
}
|
|
233
163
|
throw err;
|
|
164
|
+
} finally {
|
|
165
|
+
if (this.activeAbortController === abortController) {
|
|
166
|
+
this.activeAbortController = null;
|
|
167
|
+
}
|
|
234
168
|
}
|
|
235
169
|
}
|
|
236
170
|
/**
|
|
@@ -258,6 +192,10 @@ var init_client = __esm({
|
|
|
258
192
|
role: "assistant",
|
|
259
193
|
content: msg.content
|
|
260
194
|
};
|
|
195
|
+
const reasoning = msg.reasoning_content;
|
|
196
|
+
if (reasoning && typeof reasoning === "string") {
|
|
197
|
+
result.reasoning_content = reasoning;
|
|
198
|
+
}
|
|
261
199
|
if (msg.tool_calls && msg.tool_calls.length > 0) {
|
|
262
200
|
result.tool_calls = msg.tool_calls.map((tc) => ({
|
|
263
201
|
id: tc.id,
|
|
@@ -268,11 +206,24 @@ var init_client = __esm({
|
|
|
268
206
|
}
|
|
269
207
|
}));
|
|
270
208
|
}
|
|
209
|
+
if (response.usage) {
|
|
210
|
+
result.usage = {
|
|
211
|
+
prompt_tokens: response.usage.prompt_tokens,
|
|
212
|
+
completion_tokens: response.usage.completion_tokens,
|
|
213
|
+
total_tokens: response.usage.total_tokens
|
|
214
|
+
};
|
|
215
|
+
}
|
|
271
216
|
return result;
|
|
272
217
|
}
|
|
273
218
|
get modelName() {
|
|
274
219
|
return this.model;
|
|
275
220
|
}
|
|
221
|
+
abortActiveStream() {
|
|
222
|
+
if (this.activeAbortController) {
|
|
223
|
+
this.activeAbortController.abort();
|
|
224
|
+
this.activeAbortController = null;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
276
227
|
};
|
|
277
228
|
}
|
|
278
229
|
});
|
|
@@ -542,14 +493,24 @@ var init_edit_file = __esm({
|
|
|
542
493
|
|
|
543
494
|
// src/tools/bash.ts
|
|
544
495
|
import { exec } from "child_process";
|
|
545
|
-
|
|
496
|
+
function decodeBuffer(buf) {
|
|
497
|
+
if (!IS_WIN) return buf.toString("utf-8");
|
|
498
|
+
try {
|
|
499
|
+
const decoder = new TextDecoder("gbk");
|
|
500
|
+
return decoder.decode(buf);
|
|
501
|
+
} catch {
|
|
502
|
+
return buf.toString("utf-8");
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
var DEFAULT_TIMEOUT, IS_WIN, bashTool;
|
|
546
506
|
var init_bash = __esm({
|
|
547
507
|
"src/tools/bash.ts"() {
|
|
548
508
|
"use strict";
|
|
549
509
|
DEFAULT_TIMEOUT = 12e4;
|
|
510
|
+
IS_WIN = process.platform === "win32";
|
|
550
511
|
bashTool = {
|
|
551
512
|
name: "bash",
|
|
552
|
-
description: "\u6267\u884C shell \u547D\u4EE4\u3002\u7528\u4E8E\u8FD0\u884C\u6784\u5EFA\u3001\u6D4B\u8BD5\u3001git \u64CD\u4F5C\u7B49\u7CFB\u7EDF\u547D\u4EE4\u3002\u4E0D\u8981\u7528 bash \u505A\u6587\u4EF6\u8BFB\u5199\uFF08\u7528 read-file/edit-file/write-file\uFF09\u6216\u641C\u7D22\uFF08\u7528 glob/grep\uFF09\u3002",
|
|
513
|
+
description: IS_WIN ? "\u6267\u884C\u7CFB\u7EDF\u547D\u4EE4\uFF08shell: cmd.exe\uFF09\u3002\u7528\u4E8E\u8FD0\u884C\u6784\u5EFA\u3001\u6D4B\u8BD5\u3001git \u7B49\u3002Windows \u73AF\u5883\u8BF7\u4F7F\u7528 Windows \u547D\u4EE4\uFF08dir\u3001type\u3001copy\uFF09\u6216 Python \u8DE8\u5E73\u53F0\u547D\u4EE4\uFF0C\u4E0D\u8981\u4F7F\u7528 Unix \u547D\u4EE4\uFF08ls\u3001cat\u3001cp\uFF09\u3002\u4E0D\u8981\u7528 bash \u505A\u6587\u4EF6\u8BFB\u5199\uFF08\u7528 read-file/edit-file/write-file\uFF09\u6216\u641C\u7D22\uFF08\u7528 glob/grep\uFF09\u3002" : "\u6267\u884C shell \u547D\u4EE4\uFF08shell: /bin/bash\uFF09\u3002\u7528\u4E8E\u8FD0\u884C\u6784\u5EFA\u3001\u6D4B\u8BD5\u3001git \u64CD\u4F5C\u7B49\u7CFB\u7EDF\u547D\u4EE4\u3002\u4E0D\u8981\u7528 bash \u505A\u6587\u4EF6\u8BFB\u5199\uFF08\u7528 read-file/edit-file/write-file\uFF09\u6216\u641C\u7D22\uFF08\u7528 glob/grep\uFF09\u3002",
|
|
553
514
|
parameters: {
|
|
554
515
|
type: "object",
|
|
555
516
|
properties: {
|
|
@@ -568,7 +529,7 @@ var init_bash = __esm({
|
|
|
568
529
|
async execute(params) {
|
|
569
530
|
const command = params["command"];
|
|
570
531
|
const timeout = params["timeout"] || DEFAULT_TIMEOUT;
|
|
571
|
-
return new Promise((
|
|
532
|
+
return new Promise((resolve10) => {
|
|
572
533
|
exec(
|
|
573
534
|
command,
|
|
574
535
|
{
|
|
@@ -576,9 +537,12 @@ var init_bash = __esm({
|
|
|
576
537
|
timeout,
|
|
577
538
|
maxBuffer: 1024 * 1024 * 10,
|
|
578
539
|
// 10MB
|
|
579
|
-
shell: process.platform === "win32" ? "cmd.exe" : "/bin/bash"
|
|
540
|
+
shell: process.platform === "win32" ? "cmd.exe" : "/bin/bash",
|
|
541
|
+
encoding: "buffer"
|
|
580
542
|
},
|
|
581
|
-
(error,
|
|
543
|
+
(error, stdoutBuf, stderrBuf) => {
|
|
544
|
+
const stdout = stdoutBuf ? decodeBuffer(stdoutBuf) : "";
|
|
545
|
+
const stderr = stderrBuf ? decodeBuffer(stderrBuf) : "";
|
|
582
546
|
let output = "";
|
|
583
547
|
if (stdout) output += stdout;
|
|
584
548
|
if (stderr) output += (output ? "\n" : "") + `[stderr]
|
|
@@ -589,7 +553,7 @@ ${stderr}`;
|
|
|
589
553
|
} else if (error && !stdout && !stderr) {
|
|
590
554
|
output = `\u547D\u4EE4\u6267\u884C\u5931\u8D25\uFF1A${error.message}`;
|
|
591
555
|
}
|
|
592
|
-
|
|
556
|
+
resolve10({ content: output || "\uFF08\u65E0\u8F93\u51FA\uFF09" });
|
|
593
557
|
}
|
|
594
558
|
);
|
|
595
559
|
});
|
|
@@ -900,6 +864,18 @@ var init_conversation = __esm({
|
|
|
900
864
|
content
|
|
901
865
|
});
|
|
902
866
|
}
|
|
867
|
+
/**
|
|
868
|
+
* 清除历史消息中的 reasoning_content(新一轮对话开始时调用)
|
|
869
|
+
* deepseek-reasoner 要求同一轮 tool call 循环内保留 reasoning_content,
|
|
870
|
+
* 但新一轮用户问题开始时应清除以节省带宽,API 也会忽略旧的 reasoning_content
|
|
871
|
+
*/
|
|
872
|
+
clearReasoningContent() {
|
|
873
|
+
for (const msg of this.messages) {
|
|
874
|
+
if (msg.reasoning_content !== void 0) {
|
|
875
|
+
msg.reasoning_content = void 0;
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
}
|
|
903
879
|
/**
|
|
904
880
|
* 获取完整的消息列表(包含系统提示词)
|
|
905
881
|
*/
|
|
@@ -933,67 +909,6 @@ var init_conversation = __esm({
|
|
|
933
909
|
}
|
|
934
910
|
});
|
|
935
911
|
|
|
936
|
-
// src/core/auto-memo.ts
|
|
937
|
-
function extractExports(code) {
|
|
938
|
-
const names = [];
|
|
939
|
-
for (const m of code.matchAll(/export\s+(?:default\s+)?(?:function|class|const|let|var)\s+(\w+)/g)) {
|
|
940
|
-
names.push(m[1]);
|
|
941
|
-
}
|
|
942
|
-
for (const m of code.matchAll(/^(?:async\s+)?function\s+(\w+)/gm)) {
|
|
943
|
-
if (!names.includes(m[1])) names.push(m[1]);
|
|
944
|
-
}
|
|
945
|
-
for (const m of code.matchAll(/(?:^|\n)\s{0,4}(?:async\s+)?function\s+(\w+)/g)) {
|
|
946
|
-
if (!names.includes(m[1])) names.push(m[1]);
|
|
947
|
-
}
|
|
948
|
-
return [...new Set(names)].slice(0, 8).join(", ");
|
|
949
|
-
}
|
|
950
|
-
function autoMemoForTool(memoStore, toolName, params, result) {
|
|
951
|
-
if (!memoStore) return;
|
|
952
|
-
if (toolName === "write-file") {
|
|
953
|
-
const path8 = params["path"];
|
|
954
|
-
const content = params["content"] || "";
|
|
955
|
-
const lines = content.split("\n").length;
|
|
956
|
-
const exports = extractExports(content);
|
|
957
|
-
memoStore.write(
|
|
958
|
-
`file:${path8}`,
|
|
959
|
-
content,
|
|
960
|
-
"auto",
|
|
961
|
-
`\u65B0\u5EFA ${lines}\u884C${exports ? " | \u5BFC\u51FA: " + exports : ""}`
|
|
962
|
-
);
|
|
963
|
-
}
|
|
964
|
-
if (toolName === "edit-file") {
|
|
965
|
-
const path8 = params["path"];
|
|
966
|
-
const newStr = params["new_string"] || "";
|
|
967
|
-
const oldStr = params["old_string"] || "";
|
|
968
|
-
const changeLines = newStr.split("\n").length;
|
|
969
|
-
memoStore.write(
|
|
970
|
-
`file:${path8}`,
|
|
971
|
-
`--- \u65E7 ---
|
|
972
|
-
${oldStr}
|
|
973
|
-
--- \u65B0 ---
|
|
974
|
-
${newStr}`,
|
|
975
|
-
"auto",
|
|
976
|
-
`\u7F16\u8F91 ${changeLines}\u884C\u53D8\u66F4`
|
|
977
|
-
);
|
|
978
|
-
}
|
|
979
|
-
if (toolName === "read-file") {
|
|
980
|
-
const path8 = params["path"];
|
|
981
|
-
const lines = result.split("\n").length;
|
|
982
|
-
const exports = extractExports(result);
|
|
983
|
-
memoStore.write(
|
|
984
|
-
`file:${path8}`,
|
|
985
|
-
result,
|
|
986
|
-
"auto",
|
|
987
|
-
`\u5DF2\u8BFB ${lines}\u884C${exports ? " | \u5BFC\u51FA: " + exports : ""}`
|
|
988
|
-
);
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
var init_auto_memo = __esm({
|
|
992
|
-
"src/core/auto-memo.ts"() {
|
|
993
|
-
"use strict";
|
|
994
|
-
}
|
|
995
|
-
});
|
|
996
|
-
|
|
997
912
|
// src/core/read-tracker.ts
|
|
998
913
|
import * as fs6 from "fs";
|
|
999
914
|
import * as path6 from "path";
|
|
@@ -1042,7 +957,6 @@ var init_agent = __esm({
|
|
|
1042
957
|
"use strict";
|
|
1043
958
|
init_permission();
|
|
1044
959
|
init_conversation();
|
|
1045
|
-
init_auto_memo();
|
|
1046
960
|
init_read_tracker();
|
|
1047
961
|
Agent = class {
|
|
1048
962
|
conversation;
|
|
@@ -1050,24 +964,26 @@ var init_agent = __esm({
|
|
|
1050
964
|
registry;
|
|
1051
965
|
config;
|
|
1052
966
|
fixedTools;
|
|
1053
|
-
memoStore;
|
|
1054
967
|
readTracker = new ReadTracker();
|
|
1055
|
-
|
|
968
|
+
interrupted = false;
|
|
969
|
+
constructor(client, registry, config, systemPrompt, tools) {
|
|
1056
970
|
this.client = client;
|
|
1057
971
|
this.registry = registry;
|
|
1058
972
|
this.config = config;
|
|
1059
973
|
this.conversation = new Conversation();
|
|
1060
974
|
this.conversation.setSystemPrompt(systemPrompt);
|
|
1061
975
|
this.fixedTools = tools;
|
|
1062
|
-
this.memoStore = memoStore;
|
|
1063
976
|
}
|
|
1064
977
|
/**
|
|
1065
978
|
* 执行一轮完整的 agent 循环
|
|
1066
979
|
*/
|
|
1067
980
|
async run(userMessage, callbacks = {}) {
|
|
981
|
+
this.interrupted = false;
|
|
982
|
+
this.conversation.clearReasoningContent();
|
|
1068
983
|
this.conversation.addUserMessage(userMessage);
|
|
1069
984
|
let lastContent = "";
|
|
1070
985
|
while (true) {
|
|
986
|
+
if (this.interrupted) break;
|
|
1071
987
|
const tools = this.fixedTools || this.registry.toToolDefinitions();
|
|
1072
988
|
const assistantMsg = await this.client.chatStream(
|
|
1073
989
|
this.conversation.getMessages(),
|
|
@@ -1080,6 +996,7 @@ var init_agent = __esm({
|
|
|
1080
996
|
break;
|
|
1081
997
|
}
|
|
1082
998
|
for (const toolCall of assistantMsg.tool_calls) {
|
|
999
|
+
if (this.interrupted) break;
|
|
1083
1000
|
const toolName = toolCall.function.name;
|
|
1084
1001
|
let params;
|
|
1085
1002
|
try {
|
|
@@ -1129,7 +1046,6 @@ var init_agent = __esm({
|
|
|
1129
1046
|
}
|
|
1130
1047
|
const result = await this.registry.execute(toolName, params, this.config.max_tool_output);
|
|
1131
1048
|
callbacks.onToolResult?.(toolName, result.content, result.truncated ?? false);
|
|
1132
|
-
autoMemoForTool(this.memoStore, toolName, params, result.content);
|
|
1133
1049
|
if (toolName === "read-file") {
|
|
1134
1050
|
this.readTracker.markRead(params["path"]);
|
|
1135
1051
|
} else if (toolName === "write-file") {
|
|
@@ -1147,6 +1063,10 @@ var init_agent = __esm({
|
|
|
1147
1063
|
}
|
|
1148
1064
|
return lastContent;
|
|
1149
1065
|
}
|
|
1066
|
+
interrupt() {
|
|
1067
|
+
this.interrupted = true;
|
|
1068
|
+
this.client.abortActiveStream();
|
|
1069
|
+
}
|
|
1150
1070
|
/**
|
|
1151
1071
|
* 获取对话历史
|
|
1152
1072
|
*/
|
|
@@ -1157,487 +1077,39 @@ var init_agent = __esm({
|
|
|
1157
1077
|
}
|
|
1158
1078
|
});
|
|
1159
1079
|
|
|
1160
|
-
// src/core/
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
"
|
|
1164
|
-
|
|
1165
|
-
init_conversation();
|
|
1166
|
-
init_permission();
|
|
1167
|
-
init_auto_memo();
|
|
1168
|
-
init_read_tracker();
|
|
1169
|
-
Coder = class {
|
|
1170
|
-
client;
|
|
1171
|
-
registry;
|
|
1172
|
-
config;
|
|
1173
|
-
systemPrompt;
|
|
1174
|
-
tools;
|
|
1175
|
-
memoStore;
|
|
1176
|
-
constructor(client, registry, config, systemPrompt, tools, memoStore) {
|
|
1177
|
-
this.client = client;
|
|
1178
|
-
this.registry = registry;
|
|
1179
|
-
this.config = config;
|
|
1180
|
-
this.systemPrompt = systemPrompt;
|
|
1181
|
-
this.tools = tools;
|
|
1182
|
-
this.memoStore = memoStore;
|
|
1183
|
-
}
|
|
1184
|
-
/**
|
|
1185
|
-
* 执行编码任务(短生命周期,每次新建会话)
|
|
1186
|
-
*
|
|
1187
|
-
* @param taskMessage 调度者发来的任务描述(作为 user message)
|
|
1188
|
-
* @param callbacks 回调
|
|
1189
|
-
* @returns 编码者的最终响应
|
|
1190
|
-
*/
|
|
1191
|
-
async execute(taskMessage, callbacks = {}) {
|
|
1192
|
-
const conversation = new Conversation();
|
|
1193
|
-
conversation.setSystemPrompt(this.systemPrompt);
|
|
1194
|
-
conversation.addUserMessage(taskMessage);
|
|
1195
|
-
const readTracker = new ReadTracker();
|
|
1196
|
-
let lastContent = "";
|
|
1197
|
-
while (true) {
|
|
1198
|
-
const assistantMsg = await this.client.chatStream(
|
|
1199
|
-
conversation.getMessages(),
|
|
1200
|
-
this.tools.length > 0 ? this.tools : void 0,
|
|
1201
|
-
callbacks
|
|
1202
|
-
);
|
|
1203
|
-
conversation.addAssistantMessage(assistantMsg);
|
|
1204
|
-
if (!assistantMsg.tool_calls || assistantMsg.tool_calls.length === 0) {
|
|
1205
|
-
lastContent = assistantMsg.content || "";
|
|
1206
|
-
break;
|
|
1207
|
-
}
|
|
1208
|
-
for (const toolCall of assistantMsg.tool_calls) {
|
|
1209
|
-
const toolName = toolCall.function.name;
|
|
1210
|
-
let params;
|
|
1211
|
-
try {
|
|
1212
|
-
params = JSON.parse(toolCall.function.arguments);
|
|
1213
|
-
} catch {
|
|
1214
|
-
conversation.addToolResult(toolCall.id, "\u53C2\u6570\u89E3\u6790\u5931\u8D25\uFF1A\u65E0\u6548\u7684 JSON");
|
|
1215
|
-
continue;
|
|
1216
|
-
}
|
|
1217
|
-
try {
|
|
1218
|
-
if (toolName === "edit-file") {
|
|
1219
|
-
const editPath = params["path"];
|
|
1220
|
-
if (!readTracker.hasRead(editPath)) {
|
|
1221
|
-
conversation.addToolResult(
|
|
1222
|
-
toolCall.id,
|
|
1223
|
-
`\u26A0 \u7981\u6B62\u7F16\u8F91\u672A\u8BFB\u53D6\u7684\u6587\u4EF6\u3002\u8BF7\u5148 read-file "${editPath}" \u4E86\u89E3\u5F53\u524D\u5185\u5BB9\uFF0C\u518D edit-file\u3002`
|
|
1224
|
-
);
|
|
1225
|
-
continue;
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
|
-
if (toolName === "write-file") {
|
|
1229
|
-
const warn = readTracker.checkWriteOverwrite(
|
|
1230
|
-
params["path"],
|
|
1231
|
-
params["overwrite"]
|
|
1232
|
-
);
|
|
1233
|
-
if (warn) {
|
|
1234
|
-
conversation.addToolResult(toolCall.id, warn);
|
|
1235
|
-
continue;
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1238
|
-
const permLevel = this.registry.getPermissionLevel(toolName);
|
|
1239
|
-
if (permLevel === "deny") {
|
|
1240
|
-
callbacks.onDenied?.(toolName);
|
|
1241
|
-
conversation.addToolResult(toolCall.id, `\u5DE5\u5177 "${toolName}" \u5DF2\u88AB\u7981\u6B62\u6267\u884C`);
|
|
1242
|
-
continue;
|
|
1243
|
-
}
|
|
1244
|
-
if (permLevel === "auto") {
|
|
1245
|
-
callbacks.onToolExecuting?.(toolName, params);
|
|
1246
|
-
}
|
|
1247
|
-
if (permLevel === "confirm") {
|
|
1248
|
-
const confirmResult = await confirmExecution(toolName, params);
|
|
1249
|
-
if (!confirmResult.approved) {
|
|
1250
|
-
callbacks.onDenied?.(toolName, confirmResult.feedback);
|
|
1251
|
-
const denyMsg = confirmResult.feedback ? `\u7528\u6237\u62D2\u7EDD\u4E86\u6B64\u64CD\u4F5C\uFF0C\u7528\u6237\u53CD\u9988: ${confirmResult.feedback}` : "\u7528\u6237\u62D2\u7EDD\u4E86\u6B64\u64CD\u4F5C";
|
|
1252
|
-
conversation.addToolResult(toolCall.id, denyMsg);
|
|
1253
|
-
continue;
|
|
1254
|
-
}
|
|
1255
|
-
}
|
|
1256
|
-
const result = await this.registry.execute(toolName, params, this.config.max_tool_output);
|
|
1257
|
-
callbacks.onToolResult?.(toolName, result.content, result.truncated ?? false);
|
|
1258
|
-
autoMemoForTool(this.memoStore, toolName, params, result.content);
|
|
1259
|
-
if (toolName === "read-file") {
|
|
1260
|
-
readTracker.markRead(params["path"]);
|
|
1261
|
-
} else if (toolName === "write-file") {
|
|
1262
|
-
readTracker.markWritten(params["path"]);
|
|
1263
|
-
}
|
|
1264
|
-
conversation.addToolResult(toolCall.id, result.content);
|
|
1265
|
-
} catch (err) {
|
|
1266
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1267
|
-
conversation.addToolResult(toolCall.id, `\u5DE5\u5177\u6267\u884C\u5F02\u5E38\uFF1A${msg}`);
|
|
1268
|
-
}
|
|
1269
|
-
}
|
|
1270
|
-
if (assistantMsg.content) {
|
|
1271
|
-
lastContent = assistantMsg.content;
|
|
1272
|
-
}
|
|
1273
|
-
}
|
|
1274
|
-
return lastContent;
|
|
1275
|
-
}
|
|
1276
|
-
};
|
|
1277
|
-
}
|
|
1278
|
-
});
|
|
1080
|
+
// src/core/prompt/layers/core.ts
|
|
1081
|
+
import * as os2 from "os";
|
|
1082
|
+
function buildCorePrompt() {
|
|
1083
|
+
const shellInfo = IS_WIN2 ? 'cmd.exe\uFF08Windows\uFF09\u3002\u8BF7\u4F7F\u7528 Windows \u547D\u4EE4\uFF08dir\u3001type\u3001copy \u7B49\uFF09\u6216 Python \u8DE8\u5E73\u53F0\u547D\u4EE4\uFF08python -c "..."\uFF09\uFF0C\u4E0D\u8981\u4F7F\u7528 Unix \u547D\u4EE4\uFF08ls\u3001cat\u3001cp \u7B49\uFF09' : "/bin/bash";
|
|
1084
|
+
return `\u4F60\u662F ZenCode\uFF0C\u4E00\u4E2A CLI \u73AF\u5883\u4E0B\u7684 AI \u7F16\u7A0B\u52A9\u624B\u3002\u4F60\u5E2E\u52A9\u7528\u6237\u5B8C\u6210\u8F6F\u4EF6\u5DE5\u7A0B\u4EFB\u52A1\uFF1A\u4FEEbug\u3001\u52A0\u529F\u80FD\u3001\u91CD\u6784\u4EE3\u7801\u3001\u89E3\u91CA\u4EE3\u7801\u7B49\u3002
|
|
1279
1085
|
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
switch (name) {
|
|
1283
|
-
case "delegated":
|
|
1284
|
-
return DELEGATED_MODE;
|
|
1285
|
-
case "autonomous":
|
|
1286
|
-
return AUTONOMOUS_MODE;
|
|
1287
|
-
case "controlled":
|
|
1288
|
-
return CONTROLLED_MODE;
|
|
1289
|
-
default:
|
|
1290
|
-
return DELEGATED_MODE;
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1293
|
-
var CODER_IDENTITY, DELEGATED_MODE, AUTONOMOUS_MODE, CONTROLLED_MODE;
|
|
1294
|
-
var init_modes = __esm({
|
|
1295
|
-
"src/core/dual-agent/modes.ts"() {
|
|
1296
|
-
"use strict";
|
|
1297
|
-
CODER_IDENTITY = `\u4F60\u662F\u7F16\u7801\u5B50 Agent\u3002
|
|
1086
|
+
\u5DE5\u4F5C\u76EE\u5F55\uFF1A${process.cwd()}
|
|
1087
|
+
\u7CFB\u7EDF\uFF1A${os2.platform()} ${os2.arch()}
|
|
1298
1088
|
|
|
1299
|
-
|
|
1300
|
-
1. \u67E5\u770B\u4EFB\u52A1\u672B\u5C3E\u7684 [\u5171\u4EAB\u5907\u5FD8\u5F55] \u4E86\u89E3\u5DF2\u6709\u6587\u4EF6\u548C\u51FD\u6570
|
|
1301
|
-
- \u6BCF\u884C\u683C\u5F0F\uFF1A[file:\u8DEF\u5F84] \u6458\u8981 | \u5BFC\u51FA: \u51FD\u6570\u540D\u5217\u8868
|
|
1302
|
-
- \u9700\u8981\u5B8C\u6574\u6587\u4EF6\u5185\u5BB9\u6216 diff \u8BE6\u60C5 \u2192 memo read <key>
|
|
1303
|
-
2. \u7F16\u7801\uFF1A
|
|
1304
|
-
- \u65B0\u5EFA\u6587\u4EF6 \u2192 write-file
|
|
1305
|
-
- \u4FEE\u6539\u6587\u4EF6 \u2192 \u5FC5\u987B\u5148 read-file \u9605\u8BFB \u2192 \u518D edit-file \u4FEE\u6539
|
|
1306
|
-
- \u26A0\uFE0F \u7CFB\u7EDF\u5F3A\u5236\uFF1A\u672A read-file \u7684\u6587\u4EF6\u65E0\u6CD5 edit-file\uFF0C\u4F1A\u88AB\u62E6\u622A
|
|
1307
|
-
- \u5F15\u7528\u5DF2\u6709\u6587\u4EF6\u7684\u51FD\u6570\u65F6\uFF0C\u52A1\u5FC5\u4F7F\u7528 memo \u4E2D\u5217\u51FA\u7684\u51C6\u786E\u540D\u79F0
|
|
1308
|
-
3. \u4E00\u53E5\u8BDD\u8BF4\u660E\u7ED3\u679C
|
|
1089
|
+
# \u5DE5\u5177\u4F7F\u7528\u539F\u5219
|
|
1309
1090
|
|
|
1310
|
-
\
|
|
1311
|
-
\u5982\u6709\u9700\u8981\uFF0C\u4ECD\u53EF memo write \u8BB0\u5F55\u975E\u6587\u4EF6\u7C7B\u4FE1\u606F\uFF08\u67B6\u6784\u51B3\u7B56\u3001\u6CE8\u610F\u4E8B\u9879\u7B49\uFF09\u3002
|
|
1091
|
+
\u4F60\u6709\u4EE5\u4E0B\u5DE5\u5177\u53EF\u7528\uFF0C\u8BF7\u6839\u636E\u4EFB\u52A1\u9009\u62E9\u6700\u5408\u9002\u7684\u5DE5\u5177\uFF1A
|
|
1312
1092
|
|
|
1313
|
-
|
|
1314
|
-
-
|
|
1315
|
-
-
|
|
1316
|
-
- \u7F29\u8FDB\u3001\u7A7A\u683C\u3001\u6362\u884C\u7B26\
|
|
1317
|
-
- \
|
|
1093
|
+
- **read-file**\uFF1A\u8BFB\u53D6\u6587\u4EF6\u5185\u5BB9\u3002\u4FEE\u6539\u4EE3\u7801\u524D\u5FC5\u987B\u5148\u8BFB\u53D6\u76EE\u6807\u6587\u4EF6\u3002\u652F\u6301 offset/limit \u8BFB\u53D6\u5927\u6587\u4EF6\u7684\u7279\u5B9A\u90E8\u5206\u3002
|
|
1094
|
+
- **edit-file**\uFF1A\u901A\u8FC7\u5B57\u7B26\u4E32\u66FF\u6362\u7F16\u8F91\u6587\u4EF6\u3002\u4F18\u5148\u4F7F\u7528 edit-file \u800C\u975E write-file \u4FEE\u6539\u5DF2\u6709\u6587\u4EF6\u2014\u2014\u5B83\u66F4\u7CBE\u786E\u3001\u66F4\u5B89\u5168\u3002
|
|
1095
|
+
- \u26A0\uFE0F \u7CFB\u7EDF\u5F3A\u5236\uFF1A\u672A\u7528 read-file \u8BFB\u53D6\u8FC7\u7684\u6587\u4EF6\u65E0\u6CD5 edit-file\uFF0C\u4F1A\u88AB\u62E6\u622A
|
|
1096
|
+
- old_string \u5FC5\u987B\u4E0E\u6587\u4EF6\u4E2D\u7684\u5185\u5BB9**\u5B8C\u5168\u4E00\u81F4**\uFF08\u5305\u62EC\u7F29\u8FDB\u3001\u7A7A\u683C\u3001\u6362\u884C\u7B26\uFF09
|
|
1097
|
+
- old_string \u4E0D\u552F\u4E00\u65F6\uFF0C\u5305\u542B\u66F4\u591A\u4E0A\u4E0B\u6587\u884C\uFF08\u5EFA\u8BAE 3-5 \u884C\uFF09\u4F7F\u5176\u552F\u4E00
|
|
1098
|
+
- \u4E0D\u8981\u51ED\u8BB0\u5FC6\u731C\u6D4B\u6587\u4EF6\u5185\u5BB9\uFF0C\u5FC5\u987B\u57FA\u4E8E read-file \u7684\u5B9E\u9645\u8FD4\u56DE\u503C
|
|
1099
|
+
- **write-file**\uFF1A\u521B\u5EFA\u65B0\u6587\u4EF6\u6216\u5B8C\u6574\u91CD\u5199\u6587\u4EF6\u3002\u4EC5\u5728\u521B\u5EFA\u65B0\u6587\u4EF6\u6216\u9700\u8981\u5927\u5E45\u91CD\u5199\u65F6\u4F7F\u7528\u3002
|
|
1100
|
+
- **glob**\uFF1A\u6309\u6A21\u5F0F\u641C\u7D22\u6587\u4EF6\u8DEF\u5F84\u3002\u7528\u4E8E\u67E5\u627E\u6587\u4EF6\u4F4D\u7F6E\uFF08\u5982 \`**/*.ts\`\u3001\`src/**/config.*\`\uFF09\u3002
|
|
1101
|
+
- **grep**\uFF1A\u5728\u6587\u4EF6\u5185\u5BB9\u4E2D\u641C\u7D22\u6B63\u5219\u8868\u8FBE\u5F0F\u3002\u7528\u4E8E\u67E5\u627E\u51FD\u6570\u5B9A\u4E49\u3001\u5F15\u7528\u3001\u7279\u5B9A\u4EE3\u7801\u6A21\u5F0F\u3002
|
|
1102
|
+
- **bash**\uFF1A\u6267\u884C\u7CFB\u7EDF\u547D\u4EE4\uFF0C\u5F53\u524D shell\uFF1A${shellInfo}\u3002\u7528\u4E8E\u8FD0\u884C\u6784\u5EFA\u3001\u6D4B\u8BD5\u3001git \u64CD\u4F5C\u7B49\u3002\u4E0D\u8981\u7528 bash \u505A\u80FD\u7528\u4E0A\u8FF0\u5DE5\u5177\u5B8C\u6210\u7684\u4E8B\uFF08\u6587\u4EF6\u8BFB\u5199\u7528 read-file/edit-file/write-file\uFF0C\u641C\u7D22\u7528 glob/grep\uFF09\u3002
|
|
1318
1103
|
|
|
1319
|
-
|
|
1320
|
-
- \
|
|
1321
|
-
- \
|
|
1322
|
-
- \
|
|
1323
|
-
- \
|
|
1324
|
-
- \
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
coderHasTools: true,
|
|
1329
|
-
coderToolNames: ["read-file", "write-file", "edit-file", "bash", "glob", "grep", "memo"],
|
|
1330
|
-
coderSystemPrompt: `${CODER_IDENTITY}`
|
|
1331
|
-
};
|
|
1332
|
-
AUTONOMOUS_MODE = {
|
|
1333
|
-
name: "autonomous",
|
|
1334
|
-
description: "A\u89C4\u5212\uFF0CB\u81EA\u4E3B\u6267\u884C\uFF08\u9002\u5408\u80FD\u529B\u5F3A\u7684\u6A21\u578B\uFF09",
|
|
1335
|
-
coderHasTools: true,
|
|
1336
|
-
coderToolNames: ["read-file", "write-file", "edit-file", "bash", "glob", "grep", "memo"],
|
|
1337
|
-
coderSystemPrompt: `${CODER_IDENTITY}`
|
|
1338
|
-
};
|
|
1339
|
-
CONTROLLED_MODE = {
|
|
1340
|
-
name: "controlled",
|
|
1341
|
-
description: "A\u5168\u6743\u7BA1\u7406\uFF0CB\u53EA\u8FD4\u56DE\u4EE3\u7801",
|
|
1342
|
-
coderHasTools: false,
|
|
1343
|
-
coderSystemPrompt: "\u4F60\u662F\u88AB\u8C03\u5EA6\u8005\u6D3E\u51FA\u7684\u5B50 Agent\u3002\u6839\u636E\u63D0\u4F9B\u7684\u4EE3\u7801\u548C\u9700\u6C42\uFF0C\u53EA\u8FD4\u56DE\u4FEE\u6539\u540E\u7684\u4EE3\u7801\u3002\u4E0D\u8981\u81EA\u884C\u64CD\u4F5C\u6587\u4EF6\uFF0C\u4E0D\u8981\u505A\u989D\u5916\u6539\u52A8\u3002"
|
|
1344
|
-
};
|
|
1345
|
-
}
|
|
1346
|
-
});
|
|
1347
|
-
|
|
1348
|
-
// src/core/dual-agent/orchestrator.ts
|
|
1349
|
-
var SEND_TO_CODER_TOOL, Orchestrator;
|
|
1350
|
-
var init_orchestrator = __esm({
|
|
1351
|
-
"src/core/dual-agent/orchestrator.ts"() {
|
|
1352
|
-
"use strict";
|
|
1353
|
-
init_client();
|
|
1354
|
-
init_loader();
|
|
1355
|
-
init_conversation();
|
|
1356
|
-
init_permission();
|
|
1357
|
-
init_coder();
|
|
1358
|
-
init_modes();
|
|
1359
|
-
init_auto_memo();
|
|
1360
|
-
init_read_tracker();
|
|
1361
|
-
SEND_TO_CODER_TOOL = {
|
|
1362
|
-
type: "function",
|
|
1363
|
-
function: {
|
|
1364
|
-
name: "send-to-coder",
|
|
1365
|
-
description: "\u5C06\u7F16\u7801\u4EFB\u52A1\u53D1\u9001\u7ED9\u7F16\u7801 Agent\u3002\u4F20\u5165\u4EFB\u52A1\u63CF\u8FF0\u548C\u76F8\u5173\u4EE3\u7801\u4E0A\u4E0B\u6587\uFF0C\u7F16\u7801 Agent \u5C06\u8FD4\u56DE\u4EE3\u7801\u6216\u89E3\u51B3\u65B9\u6848\u3002",
|
|
1366
|
-
parameters: {
|
|
1367
|
-
type: "object",
|
|
1368
|
-
properties: {
|
|
1369
|
-
task: {
|
|
1370
|
-
type: "string",
|
|
1371
|
-
description: "\u8981\u53D1\u9001\u7ED9\u7F16\u7801 Agent \u7684\u4EFB\u52A1\u63CF\u8FF0\uFF0C\u5305\u542B\u76F8\u5173\u4EE3\u7801\u4E0A\u4E0B\u6587"
|
|
1372
|
-
}
|
|
1373
|
-
},
|
|
1374
|
-
required: ["task"]
|
|
1375
|
-
}
|
|
1376
|
-
}
|
|
1377
|
-
};
|
|
1378
|
-
Orchestrator = class {
|
|
1379
|
-
conversation;
|
|
1380
|
-
orchestratorClient;
|
|
1381
|
-
coderClient;
|
|
1382
|
-
registry;
|
|
1383
|
-
config;
|
|
1384
|
-
mode;
|
|
1385
|
-
baseSystemPrompt;
|
|
1386
|
-
memoStore;
|
|
1387
|
-
readTracker = new ReadTracker();
|
|
1388
|
-
constructor(registry, config, systemPrompt, memoStore) {
|
|
1389
|
-
this.registry = registry;
|
|
1390
|
-
this.config = config;
|
|
1391
|
-
this.mode = config.collaboration;
|
|
1392
|
-
this.baseSystemPrompt = systemPrompt;
|
|
1393
|
-
this.memoStore = memoStore;
|
|
1394
|
-
const orchConfig = resolveModelConfig(config, "orchestrator");
|
|
1395
|
-
this.orchestratorClient = createLLMClient({
|
|
1396
|
-
apiKey: orchConfig.api_key,
|
|
1397
|
-
baseURL: orchConfig.base_url,
|
|
1398
|
-
model: orchConfig.model,
|
|
1399
|
-
temperature: orchConfig.temperature,
|
|
1400
|
-
maxTokens: orchConfig.max_tokens
|
|
1401
|
-
});
|
|
1402
|
-
const coderConfig = resolveModelConfig(config, "coder");
|
|
1403
|
-
this.coderClient = createLLMClient({
|
|
1404
|
-
apiKey: coderConfig.api_key,
|
|
1405
|
-
baseURL: coderConfig.base_url,
|
|
1406
|
-
model: coderConfig.model,
|
|
1407
|
-
temperature: coderConfig.temperature,
|
|
1408
|
-
maxTokens: coderConfig.max_tokens
|
|
1409
|
-
});
|
|
1410
|
-
this.conversation = new Conversation();
|
|
1411
|
-
this.conversation.setSystemPrompt(this.buildSystemPrompt());
|
|
1412
|
-
}
|
|
1413
|
-
/**
|
|
1414
|
-
* 动态获取调度者的工具列表:注册表工具 + send-to-coder
|
|
1415
|
-
*/
|
|
1416
|
-
getTools() {
|
|
1417
|
-
return [
|
|
1418
|
-
...this.registry.toToolDefinitions(),
|
|
1419
|
-
SEND_TO_CODER_TOOL
|
|
1420
|
-
];
|
|
1421
|
-
}
|
|
1422
|
-
/**
|
|
1423
|
-
* 构建调度者的系统提示词(包含当前协作模式)
|
|
1424
|
-
*/
|
|
1425
|
-
buildSystemPrompt() {
|
|
1426
|
-
const modeInfo = getMode(this.mode);
|
|
1427
|
-
return `${this.baseSystemPrompt}
|
|
1428
|
-
|
|
1429
|
-
# \u4F60\u662F\u8C03\u5EA6\u8005 Agent
|
|
1430
|
-
|
|
1431
|
-
\u534F\u4F5C\u6A21\u5F0F\uFF1A${modeInfo.name} - ${modeInfo.description}
|
|
1432
|
-
|
|
1433
|
-
\u4F60\u662F\u4FA6\u5BDF\u5175 + \u6307\u6325\u5B98\u3002\u4F60\u81EA\u5DF1\u4E0D\u5199\u4EE3\u7801\uFF0C\u4F60\u7684\u804C\u8D23\u662F\uFF1A\u6536\u96C6\u60C5\u62A5 \u2192 \u59D4\u6D3E\u7F16\u7801 Agent\u3002
|
|
1434
|
-
|
|
1435
|
-
## \u6838\u5FC3\u6D41\u7A0B
|
|
1436
|
-
|
|
1437
|
-
1. **\u8BC4\u4F30**\uFF1A\u4EFB\u52A1\u662F\u5426\u9700\u8981\u4E86\u89E3\u73B0\u6709\u4EE3\u7801\uFF1F
|
|
1438
|
-
- \u9700\u8981\u4FEE\u6539\u73B0\u6709\u6587\u4EF6\u3001\u9700\u8981\u7406\u89E3\u4F9D\u8D56\u5173\u7CFB \u2192 \u5148\u6536\u96C6
|
|
1439
|
-
- \u9700\u6C42\u81EA\u5305\u542B\u3001\u76EE\u6807\u8DEF\u5F84\u660E\u786E \u2192 \u8DF3\u5230\u7B2C 3 \u6B65
|
|
1440
|
-
|
|
1441
|
-
2. **\u6536\u96C6\u4E0A\u4E0B\u6587**\uFF08\u9AD8\u6548\uFF0C\u4E0D\u91CD\u590D\uFF09
|
|
1442
|
-
- \u7528 glob/grep \u5B9A\u4F4D + read-file \u6216 spawn-agents \u5E76\u884C\u8BFB\u53D6
|
|
1443
|
-
- \u6587\u4EF6\u64CD\u4F5C\uFF08read-file \u7B49\uFF09\u4F1A\u81EA\u52A8\u8BB0\u5F55\u5230 memo\uFF0C\u4E0D\u9700\u8981\u624B\u52A8 memo write \u6587\u4EF6\u5185\u5BB9
|
|
1444
|
-
- \u4ECD\u53EF memo write \u8BB0\u5F55\u5206\u6790\u7ED3\u8BBA\u3001\u67B6\u6784\u51B3\u7B56\u7B49\u975E\u6587\u4EF6\u4FE1\u606F
|
|
1445
|
-
|
|
1446
|
-
3. **\u59D4\u6D3E\u7F16\u7801**\uFF1Asend-to-coder
|
|
1447
|
-
- task \u4E2D\u5199\u6E05\uFF1A\u505A\u4EC0\u4E48 + \u76EE\u6807\u8DEF\u5F84
|
|
1448
|
-
- \u7F16\u7801 Agent \u4F1A\u81EA\u52A8\u770B\u5230 memo \u4E2D\u7684\u6587\u4EF6\u7D22\u5F15\uFF08\u5305\u62EC\u5DF2\u521B\u5EFA\u7684\u6587\u4EF6\u548C\u5BFC\u51FA\u7684\u51FD\u6570\u540D\uFF09
|
|
1449
|
-
- \u6BCF\u6B21\u53EA\u53D1\u4E00\u4E2A\u5177\u4F53\u6B65\u9AA4
|
|
1450
|
-
|
|
1451
|
-
4. **\u8FED\u4EE3/\u9A8C\u8BC1**\uFF1A\u9700\u8981\u65F6\u7EE7\u7EED\u59D4\u6D3E\u6216\u7528 bash \u8FD0\u884C\u9A8C\u8BC1
|
|
1452
|
-
|
|
1453
|
-
## \u91CD\u8981
|
|
1454
|
-
|
|
1455
|
-
- memo \u7D22\u5F15\u4F1A\u81EA\u52A8\u6CE8\u5165\u7ED9\u7F16\u7801 Agent\uFF0C\u5305\u542B\u6240\u6709\u5DF2\u64CD\u4F5C\u6587\u4EF6\u7684\u6458\u8981\u548C\u5BFC\u51FA\u51FD\u6570\u540D
|
|
1456
|
-
- \u4E0D\u8981\u91CD\u590D\u63A2\u7D22\uFF08glob \u67E5\u8FC7\u5C31\u4E0D\u8981\u518D bash ls \u540C\u4E00\u76EE\u5F55\uFF09
|
|
1457
|
-
- bash \u7528\u4E8E\u6267\u884C\u547D\u4EE4\uFF08\u6784\u5EFA\u3001\u6D4B\u8BD5\uFF09\uFF0C\u4E0D\u9700\u8981\u59D4\u6D3E
|
|
1458
|
-
- \u5B8C\u6210\u6240\u6709\u6B65\u9AA4\u540E\u7B80\u8981\u544A\u77E5\u7528\u6237\u7ED3\u679C`;
|
|
1459
|
-
}
|
|
1460
|
-
/**
|
|
1461
|
-
* 切换协作模式
|
|
1462
|
-
*/
|
|
1463
|
-
setMode(mode) {
|
|
1464
|
-
this.mode = mode;
|
|
1465
|
-
this.conversation.setSystemPrompt(this.buildSystemPrompt());
|
|
1466
|
-
}
|
|
1467
|
-
/**
|
|
1468
|
-
* 执行用户请求
|
|
1469
|
-
*/
|
|
1470
|
-
async run(userMessage, callbacks = {}) {
|
|
1471
|
-
callbacks.onModeInfo?.(this.mode);
|
|
1472
|
-
this.conversation.addUserMessage(userMessage);
|
|
1473
|
-
let lastContent = "";
|
|
1474
|
-
while (true) {
|
|
1475
|
-
const tools = this.getTools();
|
|
1476
|
-
const assistantMsg = await this.orchestratorClient.chatStream(
|
|
1477
|
-
this.conversation.getMessages(),
|
|
1478
|
-
tools,
|
|
1479
|
-
callbacks
|
|
1480
|
-
);
|
|
1481
|
-
this.conversation.addAssistantMessage(assistantMsg);
|
|
1482
|
-
if (!assistantMsg.tool_calls || assistantMsg.tool_calls.length === 0) {
|
|
1483
|
-
lastContent = assistantMsg.content || "";
|
|
1484
|
-
break;
|
|
1485
|
-
}
|
|
1486
|
-
for (const toolCall of assistantMsg.tool_calls) {
|
|
1487
|
-
const toolName = toolCall.function.name;
|
|
1488
|
-
let params;
|
|
1489
|
-
try {
|
|
1490
|
-
params = JSON.parse(toolCall.function.arguments);
|
|
1491
|
-
} catch {
|
|
1492
|
-
this.conversation.addToolResult(toolCall.id, "\u53C2\u6570\u89E3\u6790\u5931\u8D25\uFF1A\u65E0\u6548\u7684 JSON");
|
|
1493
|
-
continue;
|
|
1494
|
-
}
|
|
1495
|
-
try {
|
|
1496
|
-
if (toolName === "send-to-coder") {
|
|
1497
|
-
const task = params["task"];
|
|
1498
|
-
const coderResponse = await this.invokeCoder(task, callbacks);
|
|
1499
|
-
this.conversation.addToolResult(toolCall.id, coderResponse);
|
|
1500
|
-
continue;
|
|
1501
|
-
}
|
|
1502
|
-
if (toolName === "edit-file") {
|
|
1503
|
-
const editPath = params["path"];
|
|
1504
|
-
if (!this.readTracker.hasRead(editPath)) {
|
|
1505
|
-
this.conversation.addToolResult(
|
|
1506
|
-
toolCall.id,
|
|
1507
|
-
`\u26A0 \u7981\u6B62\u7F16\u8F91\u672A\u8BFB\u53D6\u7684\u6587\u4EF6\u3002\u8BF7\u5148 read-file "${editPath}" \u4E86\u89E3\u5F53\u524D\u5185\u5BB9\uFF0C\u518D edit-file\u3002`
|
|
1508
|
-
);
|
|
1509
|
-
continue;
|
|
1510
|
-
}
|
|
1511
|
-
}
|
|
1512
|
-
if (toolName === "write-file") {
|
|
1513
|
-
const warn = this.readTracker.checkWriteOverwrite(
|
|
1514
|
-
params["path"],
|
|
1515
|
-
params["overwrite"]
|
|
1516
|
-
);
|
|
1517
|
-
if (warn) {
|
|
1518
|
-
this.conversation.addToolResult(toolCall.id, warn);
|
|
1519
|
-
continue;
|
|
1520
|
-
}
|
|
1521
|
-
}
|
|
1522
|
-
const permLevel = this.registry.getPermissionLevel(toolName);
|
|
1523
|
-
if (permLevel === "deny") {
|
|
1524
|
-
callbacks.onDenied?.(toolName);
|
|
1525
|
-
this.conversation.addToolResult(toolCall.id, `\u5DE5\u5177 "${toolName}" \u5DF2\u88AB\u7981\u6B62\u6267\u884C`);
|
|
1526
|
-
continue;
|
|
1527
|
-
}
|
|
1528
|
-
if (permLevel === "auto") {
|
|
1529
|
-
callbacks.onToolExecuting?.(toolName, params);
|
|
1530
|
-
}
|
|
1531
|
-
if (permLevel === "confirm") {
|
|
1532
|
-
const confirmResult = await confirmExecution(toolName, params);
|
|
1533
|
-
if (!confirmResult.approved) {
|
|
1534
|
-
callbacks.onDenied?.(toolName, confirmResult.feedback);
|
|
1535
|
-
const denyMsg = confirmResult.feedback ? `\u7528\u6237\u62D2\u7EDD\u4E86\u6B64\u64CD\u4F5C\uFF0C\u7528\u6237\u53CD\u9988: ${confirmResult.feedback}` : "\u7528\u6237\u62D2\u7EDD\u4E86\u6B64\u64CD\u4F5C";
|
|
1536
|
-
this.conversation.addToolResult(toolCall.id, denyMsg);
|
|
1537
|
-
continue;
|
|
1538
|
-
}
|
|
1539
|
-
}
|
|
1540
|
-
const result = await this.registry.execute(toolName, params, this.config.max_tool_output);
|
|
1541
|
-
callbacks.onToolResult?.(toolName, result.content, result.truncated ?? false);
|
|
1542
|
-
autoMemoForTool(this.memoStore, toolName, params, result.content);
|
|
1543
|
-
if (toolName === "read-file") {
|
|
1544
|
-
this.readTracker.markRead(params["path"]);
|
|
1545
|
-
} else if (toolName === "write-file") {
|
|
1546
|
-
this.readTracker.markWritten(params["path"]);
|
|
1547
|
-
}
|
|
1548
|
-
this.conversation.addToolResult(toolCall.id, result.content);
|
|
1549
|
-
} catch (err) {
|
|
1550
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1551
|
-
this.conversation.addToolResult(toolCall.id, `\u5DE5\u5177\u6267\u884C\u5F02\u5E38\uFF1A${msg}`);
|
|
1552
|
-
}
|
|
1553
|
-
}
|
|
1554
|
-
if (assistantMsg.content) {
|
|
1555
|
-
lastContent = assistantMsg.content;
|
|
1556
|
-
}
|
|
1557
|
-
}
|
|
1558
|
-
return lastContent;
|
|
1559
|
-
}
|
|
1560
|
-
/**
|
|
1561
|
-
* 调用编码者 Agent
|
|
1562
|
-
*/
|
|
1563
|
-
async invokeCoder(task, callbacks) {
|
|
1564
|
-
callbacks.onCoderStart?.();
|
|
1565
|
-
const modeInfo = getMode(this.mode);
|
|
1566
|
-
let coderTools = [];
|
|
1567
|
-
if (modeInfo.coderHasTools && modeInfo.coderToolNames) {
|
|
1568
|
-
coderTools = this.registry.toToolDefinitions(modeInfo.coderToolNames);
|
|
1569
|
-
}
|
|
1570
|
-
let taskWithMemo = task;
|
|
1571
|
-
if (this.memoStore) {
|
|
1572
|
-
const index = this.memoStore.buildIndex();
|
|
1573
|
-
if (index) {
|
|
1574
|
-
taskWithMemo += `
|
|
1575
|
-
|
|
1576
|
-
[\u5171\u4EAB\u5907\u5FD8\u5F55]
|
|
1577
|
-
${index}`;
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
|
-
taskWithMemo += "\n\n[\u91CD\u8981\uFF1A\u7ACB\u5373\u5F00\u59CB\u7F16\u7801\uFF0C\u4E0D\u8981\u63A2\u7D22\uFF0C\u4E0D\u8981\u8F93\u51FA\u5206\u6790]";
|
|
1581
|
-
const coder = new Coder(
|
|
1582
|
-
this.coderClient,
|
|
1583
|
-
this.registry,
|
|
1584
|
-
this.config,
|
|
1585
|
-
modeInfo.coderSystemPrompt,
|
|
1586
|
-
coderTools,
|
|
1587
|
-
this.memoStore
|
|
1588
|
-
);
|
|
1589
|
-
const response = await coder.execute(taskWithMemo, {
|
|
1590
|
-
onContent: callbacks.onContent,
|
|
1591
|
-
onToolCallStreaming: callbacks.onToolCallStreaming,
|
|
1592
|
-
onToolExecuting: callbacks.onToolExecuting,
|
|
1593
|
-
onToolResult: callbacks.onToolResult,
|
|
1594
|
-
onDenied: callbacks.onDenied
|
|
1595
|
-
});
|
|
1596
|
-
callbacks.onCoderEnd?.(response);
|
|
1597
|
-
return response;
|
|
1598
|
-
}
|
|
1599
|
-
/**
|
|
1600
|
-
* 获取对话历史
|
|
1601
|
-
*/
|
|
1602
|
-
getConversation() {
|
|
1603
|
-
return this.conversation;
|
|
1604
|
-
}
|
|
1605
|
-
};
|
|
1606
|
-
}
|
|
1607
|
-
});
|
|
1608
|
-
|
|
1609
|
-
// src/core/prompt/layers/core.ts
|
|
1610
|
-
import * as os2 from "os";
|
|
1611
|
-
function buildCorePrompt() {
|
|
1612
|
-
return `\u4F60\u662F ZenCode\uFF0C\u4E00\u4E2A CLI \u73AF\u5883\u4E0B\u7684 AI \u7F16\u7A0B\u52A9\u624B\u3002\u4F60\u5E2E\u52A9\u7528\u6237\u5B8C\u6210\u8F6F\u4EF6\u5DE5\u7A0B\u4EFB\u52A1\uFF1A\u4FEEbug\u3001\u52A0\u529F\u80FD\u3001\u91CD\u6784\u4EE3\u7801\u3001\u89E3\u91CA\u4EE3\u7801\u7B49\u3002
|
|
1613
|
-
|
|
1614
|
-
\u5DE5\u4F5C\u76EE\u5F55\uFF1A${process.cwd()}
|
|
1615
|
-
\u7CFB\u7EDF\uFF1A${os2.platform()} ${os2.arch()}
|
|
1616
|
-
|
|
1617
|
-
# \u5DE5\u5177\u4F7F\u7528\u539F\u5219
|
|
1618
|
-
|
|
1619
|
-
\u4F60\u6709\u4EE5\u4E0B\u5DE5\u5177\u53EF\u7528\uFF0C\u8BF7\u6839\u636E\u4EFB\u52A1\u9009\u62E9\u6700\u5408\u9002\u7684\u5DE5\u5177\uFF1A
|
|
1620
|
-
|
|
1621
|
-
- **read-file**\uFF1A\u8BFB\u53D6\u6587\u4EF6\u5185\u5BB9\u3002\u4FEE\u6539\u4EE3\u7801\u524D\u5FC5\u987B\u5148\u8BFB\u53D6\u76EE\u6807\u6587\u4EF6\u3002\u652F\u6301 offset/limit \u8BFB\u53D6\u5927\u6587\u4EF6\u7684\u7279\u5B9A\u90E8\u5206\u3002
|
|
1622
|
-
- **edit-file**\uFF1A\u901A\u8FC7\u5B57\u7B26\u4E32\u66FF\u6362\u7F16\u8F91\u6587\u4EF6\u3002\u4F18\u5148\u4F7F\u7528 edit-file \u800C\u975E write-file \u4FEE\u6539\u5DF2\u6709\u6587\u4EF6\u2014\u2014\u5B83\u66F4\u7CBE\u786E\u3001\u66F4\u5B89\u5168\u3002
|
|
1623
|
-
- \u26A0\uFE0F \u7CFB\u7EDF\u5F3A\u5236\uFF1A\u672A\u7528 read-file \u8BFB\u53D6\u8FC7\u7684\u6587\u4EF6\u65E0\u6CD5 edit-file\uFF0C\u4F1A\u88AB\u62E6\u622A
|
|
1624
|
-
- old_string \u5FC5\u987B\u4E0E\u6587\u4EF6\u4E2D\u7684\u5185\u5BB9**\u5B8C\u5168\u4E00\u81F4**\uFF08\u5305\u62EC\u7F29\u8FDB\u3001\u7A7A\u683C\u3001\u6362\u884C\u7B26\uFF09
|
|
1625
|
-
- old_string \u4E0D\u552F\u4E00\u65F6\uFF0C\u5305\u542B\u66F4\u591A\u4E0A\u4E0B\u6587\u884C\uFF08\u5EFA\u8BAE 3-5 \u884C\uFF09\u4F7F\u5176\u552F\u4E00
|
|
1626
|
-
- \u4E0D\u8981\u51ED\u8BB0\u5FC6\u731C\u6D4B\u6587\u4EF6\u5185\u5BB9\uFF0C\u5FC5\u987B\u57FA\u4E8E read-file \u7684\u5B9E\u9645\u8FD4\u56DE\u503C
|
|
1627
|
-
- **write-file**\uFF1A\u521B\u5EFA\u65B0\u6587\u4EF6\u6216\u5B8C\u6574\u91CD\u5199\u6587\u4EF6\u3002\u4EC5\u5728\u521B\u5EFA\u65B0\u6587\u4EF6\u6216\u9700\u8981\u5927\u5E45\u91CD\u5199\u65F6\u4F7F\u7528\u3002
|
|
1628
|
-
- **glob**\uFF1A\u6309\u6A21\u5F0F\u641C\u7D22\u6587\u4EF6\u8DEF\u5F84\u3002\u7528\u4E8E\u67E5\u627E\u6587\u4EF6\u4F4D\u7F6E\uFF08\u5982 \`**/*.ts\`\u3001\`src/**/config.*\`\uFF09\u3002
|
|
1629
|
-
- **grep**\uFF1A\u5728\u6587\u4EF6\u5185\u5BB9\u4E2D\u641C\u7D22\u6B63\u5219\u8868\u8FBE\u5F0F\u3002\u7528\u4E8E\u67E5\u627E\u51FD\u6570\u5B9A\u4E49\u3001\u5F15\u7528\u3001\u7279\u5B9A\u4EE3\u7801\u6A21\u5F0F\u3002
|
|
1630
|
-
- **bash**\uFF1A\u6267\u884C shell \u547D\u4EE4\u3002\u7528\u4E8E\u8FD0\u884C\u6784\u5EFA\u3001\u6D4B\u8BD5\u3001git \u64CD\u4F5C\u7B49\u3002\u4E0D\u8981\u7528 bash \u505A\u80FD\u7528\u4E0A\u8FF0\u5DE5\u5177\u5B8C\u6210\u7684\u4E8B\uFF08\u5982\u4E0D\u8981\u7528 cat \u8BFB\u6587\u4EF6\u3001\u4E0D\u8981\u7528 sed \u7F16\u8F91\u6587\u4EF6\u3001\u4E0D\u8981\u7528 find \u641C\u7D22\u6587\u4EF6\uFF09\u3002
|
|
1631
|
-
|
|
1632
|
-
\u5173\u952E\u89C4\u5219\uFF1A
|
|
1633
|
-
- **\u5148\u8BFB\u540E\u6539**\uFF1A\u4FEE\u6539\u6587\u4EF6\u524D\u5FC5\u987B read-file \u8BFB\u53D6\u8BE5\u6587\u4EF6\uFF08\u7CFB\u7EDF\u4F1A\u62E6\u622A\u672A\u8BFB\u53D6\u5C31 edit \u7684\u64CD\u4F5C\uFF09
|
|
1634
|
-
- edit-file \u7684 old_string \u5FC5\u987B\u4ECE read-file \u8FD4\u56DE\u7684\u5185\u5BB9\u4E2D\u7CBE\u786E\u590D\u5236\uFF0C\u4E0D\u8981\u624B\u52A8\u8F93\u5165\u6216\u51ED\u8BB0\u5FC6
|
|
1635
|
-
- \u4F18\u5148 edit-file \u7F16\u8F91\u5DF2\u6709\u6587\u4EF6\uFF0C\u800C\u975E write-file \u91CD\u5199
|
|
1636
|
-
- \u4E0D\u8981\u521B\u5EFA\u4E0D\u5FC5\u8981\u7684\u65B0\u6587\u4EF6\uFF0C\u4F18\u5148\u5728\u73B0\u6709\u6587\u4EF6\u4E2D\u4FEE\u6539
|
|
1637
|
-
- \u53EA\u505A\u5FC5\u8981\u7684\u6700\u5C0F\u6539\u52A8\uFF0C\u4E0D\u505A\u989D\u5916"\u6539\u8FDB"
|
|
1638
|
-
- \u4E0D\u8981\u6DFB\u52A0\u7528\u6237\u672A\u8981\u6C42\u7684\u6CE8\u91CA\u3001\u6587\u6863\u3001\u7C7B\u578B\u6CE8\u89E3
|
|
1639
|
-
- \u4E0D\u8981\u5F15\u5165\u5B89\u5168\u6F0F\u6D1E\uFF08\u6CE8\u5165\u3001XSS\u3001SQL \u6CE8\u5165\u7B49 OWASP Top 10\uFF09
|
|
1640
|
-
- \u5F15\u7528\u4EE3\u7801\u65F6\u4F7F\u7528 \`\u6587\u4EF6\u8DEF\u5F84:\u884C\u53F7\` \u683C\u5F0F\uFF08\u5982 \`src/app.ts:42\`\uFF09\uFF0C\u65B9\u4FBF\u7528\u6237\u8DF3\u8F6C
|
|
1104
|
+
\u5173\u952E\u89C4\u5219\uFF1A
|
|
1105
|
+
- **\u5148\u8BFB\u540E\u6539**\uFF1A\u4FEE\u6539\u6587\u4EF6\u524D\u5FC5\u987B read-file \u8BFB\u53D6\u8BE5\u6587\u4EF6\uFF08\u7CFB\u7EDF\u4F1A\u62E6\u622A\u672A\u8BFB\u53D6\u5C31 edit \u7684\u64CD\u4F5C\uFF09
|
|
1106
|
+
- edit-file \u7684 old_string \u5FC5\u987B\u4ECE read-file \u8FD4\u56DE\u7684\u5185\u5BB9\u4E2D\u7CBE\u786E\u590D\u5236\uFF0C\u4E0D\u8981\u624B\u52A8\u8F93\u5165\u6216\u51ED\u8BB0\u5FC6
|
|
1107
|
+
- \u4F18\u5148 edit-file \u7F16\u8F91\u5DF2\u6709\u6587\u4EF6\uFF0C\u800C\u975E write-file \u91CD\u5199
|
|
1108
|
+
- \u4E0D\u8981\u521B\u5EFA\u4E0D\u5FC5\u8981\u7684\u65B0\u6587\u4EF6\uFF0C\u4F18\u5148\u5728\u73B0\u6709\u6587\u4EF6\u4E2D\u4FEE\u6539
|
|
1109
|
+
- \u53EA\u505A\u5FC5\u8981\u7684\u6700\u5C0F\u6539\u52A8\uFF0C\u4E0D\u505A\u989D\u5916"\u6539\u8FDB"
|
|
1110
|
+
- \u4E0D\u8981\u6DFB\u52A0\u7528\u6237\u672A\u8981\u6C42\u7684\u6CE8\u91CA\u3001\u6587\u6863\u3001\u7C7B\u578B\u6CE8\u89E3
|
|
1111
|
+
- \u4E0D\u8981\u5F15\u5165\u5B89\u5168\u6F0F\u6D1E\uFF08\u6CE8\u5165\u3001XSS\u3001SQL \u6CE8\u5165\u7B49 OWASP Top 10\uFF09
|
|
1112
|
+
- \u5F15\u7528\u4EE3\u7801\u65F6\u4F7F\u7528 \`\u6587\u4EF6\u8DEF\u5F84:\u884C\u53F7\` \u683C\u5F0F\uFF08\u5982 \`src/app.ts:42\`\uFF09\uFF0C\u65B9\u4FBF\u7528\u6237\u8DF3\u8F6C
|
|
1641
1113
|
|
|
1642
1114
|
# \u4EA4\u4E92\u98CE\u683C
|
|
1643
1115
|
|
|
@@ -1646,9 +1118,11 @@ function buildCorePrompt() {
|
|
|
1646
1118
|
- \u4E0D\u8981\u7ED9\u51FA\u65F6\u95F4\u9884\u4F30\uFF08"\u5927\u6982\u9700\u8981\u51E0\u5206\u949F"\u4E4B\u7C7B\uFF09
|
|
1647
1119
|
- \u56DE\u590D\u7B80\u6D01\uFF0C\u76F4\u63A5\u7ED9\u7ED3\u679C`;
|
|
1648
1120
|
}
|
|
1121
|
+
var IS_WIN2;
|
|
1649
1122
|
var init_core = __esm({
|
|
1650
1123
|
"src/core/prompt/layers/core.ts"() {
|
|
1651
1124
|
"use strict";
|
|
1125
|
+
IS_WIN2 = os2.platform() === "win32";
|
|
1652
1126
|
}
|
|
1653
1127
|
});
|
|
1654
1128
|
|
|
@@ -1658,8 +1132,8 @@ function buildPlanningPrompt() {
|
|
|
1658
1132
|
|
|
1659
1133
|
\u5904\u7406\u7F16\u7A0B\u4EFB\u52A1\u65F6\uFF1A
|
|
1660
1134
|
1. \u5148\u7528 read-file / grep / glob \u9605\u8BFB\u76F8\u5173\u4EE3\u7801\uFF0C\u7406\u89E3\u73B0\u6709\u903B\u8F91\u548C\u4E0A\u4E0B\u6587
|
|
1661
|
-
2. \
|
|
1662
|
-
3. \
|
|
1135
|
+
2. \u5224\u65AD\u4EFB\u52A1\u7684\u590D\u6742\u7A0B\u5EA6\uFF0C\u7B80\u5355\u4EFB\u52A1\u76F4\u63A5\u6267\u884C\uFF0C\u590D\u6742\u4EFB\u52A1\u5148\u505A\u8BA1\u5212
|
|
1136
|
+
3. \u5982\u679C\u7528\u6237\u7684\u8981\u6C42\u4E0D\u6E05\u6670\uFF0C\u4E00\u5B9A\u8981\u8BE2\u95EE\u7528\u6237\uFF0C\u786E\u5B9A\u7EC6\u8282
|
|
1663
1137
|
|
|
1664
1138
|
\u591A\u6B65\u4EFB\u52A1\u7BA1\u7406\uFF1A
|
|
1665
1139
|
- \u5BF9\u4E8E 3 \u4E2A\u4EE5\u4E0A\u6B65\u9AA4\u7684\u4EFB\u52A1\uFF0C\u4F7F\u7528 todo \u5DE5\u5177\u521B\u5EFA\u8BA1\u5212\u518D\u9010\u6B65\u6267\u884C
|
|
@@ -1667,11 +1141,8 @@ function buildPlanningPrompt() {
|
|
|
1667
1141
|
- \u6BCF\u6B65\u5B8C\u6210\u540E\u68C0\u67E5\u8BA1\u5212\uFF0C\u51B3\u5B9A\u4E0B\u4E00\u6B65
|
|
1668
1142
|
|
|
1669
1143
|
\u4EE3\u7801\u8D28\u91CF\uFF1A
|
|
1670
|
-
- \u4E0D\u8981\u5199\u4E0D\u5FC5\u8981\u7684\u6D4B\u8BD5\uFF1B\u5982\u679C\u8981\u5199\u6D4B\u8BD5\uFF0C\u5148\u786E\u8BA4\u9879\u76EE\u7684\u6D4B\u8BD5\u6846\u67B6\u548C\u4F9D\u8D56\uFF0C\u786E\u4FDD\u80FD\u5B9E\u9645\u8FD0\u884C
|
|
1671
|
-
- \u4E0D\u8981\u8FC7\u5EA6\u5DE5\u7A0B\u5316\uFF1A\u4E0D\u8981\u4E3A\u4E00\u6B21\u6027\u64CD\u4F5C\u521B\u5EFA\u62BD\u8C61\u3001\u4E0D\u8981\u4E3A\u5047\u8BBE\u7684\u672A\u6765\u9700\u6C42\u8BBE\u8BA1
|
|
1672
|
-
- \u4E0D\u8981\u4E3A\u4E0D\u53EF\u80FD\u53D1\u751F\u7684\u573A\u666F\u6DFB\u52A0\u9519\u8BEF\u5904\u7406\uFF1B\u53EA\u5728\u7CFB\u7EDF\u8FB9\u754C\uFF08\u7528\u6237\u8F93\u5165\u3001\u5916\u90E8 API\uFF09\u505A\u6821\u9A8C
|
|
1673
1144
|
- \u5982\u679C\u5220\u9664\u4E86\u4EE3\u7801\uFF0C\u5C31\u5F7B\u5E95\u5220\u9664\uFF0C\u4E0D\u8981\u7559\u6CE8\u91CA\u8BF4"\u5DF2\u79FB\u9664"\uFF0C\u4E0D\u8981\u4FDD\u7559\u672A\u4F7F\u7528\u7684\u517C\u5BB9\u6027\u53D8\u91CF
|
|
1674
|
-
- \
|
|
1145
|
+
- \u4E0D\u8981\u7559\u4E0BTODO\u7136\u540E\u653E\u7740\u4E0D\u7BA1`;
|
|
1675
1146
|
}
|
|
1676
1147
|
var init_planning = __esm({
|
|
1677
1148
|
"src/core/prompt/layers/planning.ts"() {
|
|
@@ -1695,7 +1166,7 @@ function buildParallelPrompt() {
|
|
|
1695
1166
|
\u6B63\u786E\uFF1Aspawn-agents \u540C\u65F6\u8BFB auth controller\u3001auth service\u3001auth middleware\u3001auth types
|
|
1696
1167
|
\u9519\u8BEF\uFF1A\u5148 read-file controller\uFF0C\u518D read-file service\uFF0C\u518D read-file middleware...
|
|
1697
1168
|
|
|
1698
|
-
\u6BCF\u4E2A\u5B50 Agent \u6709\u72EC\u7ACB\u5BF9\u8BDD\uFF0C\u9ED8\u8BA4\u53EF\u7528 read-file\u3001glob\u3001grep\
|
|
1169
|
+
\u6BCF\u4E2A\u5B50 Agent \u6709\u72EC\u7ACB\u5BF9\u8BDD\uFF0C\u9ED8\u8BA4\u53EF\u7528 read-file\u3001glob\u3001grep\u3002`;
|
|
1699
1170
|
}
|
|
1700
1171
|
var init_parallel = __esm({
|
|
1701
1172
|
"src/core/prompt/layers/parallel.ts"() {
|
|
@@ -1728,6 +1199,24 @@ var init_git = __esm({
|
|
|
1728
1199
|
}
|
|
1729
1200
|
});
|
|
1730
1201
|
|
|
1202
|
+
// src/core/prompt/layers/agents.ts
|
|
1203
|
+
function buildAgentsPrompt(agents) {
|
|
1204
|
+
if (agents.length === 0) return null;
|
|
1205
|
+
const agentList = agents.map((a) => `- **${a.name}**\uFF1A${a.description}`).join("\n");
|
|
1206
|
+
return `# \u5B50 Agent
|
|
1207
|
+
|
|
1208
|
+
\u4F60\u53EF\u4EE5\u901A\u8FC7 dispatch \u5DE5\u5177\u8C03\u5EA6\u4EE5\u4E0B\u4E13\u7528\u5B50 Agent\uFF1A
|
|
1209
|
+
|
|
1210
|
+
${agentList}
|
|
1211
|
+
|
|
1212
|
+
\u4F7F\u7528\u573A\u666F\uFF1A\u5F53\u4EFB\u52A1\u9700\u8981\u4E13\u4E1A\u89D2\u8272\uFF08\u5982\u4EE3\u7801\u5BA1\u67E5\u3001\u67B6\u6784\u5206\u6790\uFF09\u4E14\u5B50 Agent \u7684\u80FD\u529B\u6BD4\u4F60\u76F4\u63A5\u505A\u66F4\u5408\u9002\u65F6\uFF0C\u4F7F\u7528 dispatch \u59D4\u6D3E\u3002`;
|
|
1213
|
+
}
|
|
1214
|
+
var init_agents = __esm({
|
|
1215
|
+
"src/core/prompt/layers/agents.ts"() {
|
|
1216
|
+
"use strict";
|
|
1217
|
+
}
|
|
1218
|
+
});
|
|
1219
|
+
|
|
1731
1220
|
// src/core/prompt/layers/project.ts
|
|
1732
1221
|
import * as fs7 from "fs/promises";
|
|
1733
1222
|
import * as path7 from "path";
|
|
@@ -1769,7 +1258,7 @@ function isGitRepo() {
|
|
|
1769
1258
|
return false;
|
|
1770
1259
|
}
|
|
1771
1260
|
}
|
|
1772
|
-
async function buildPrompt(config) {
|
|
1261
|
+
async function buildPrompt(config, agents) {
|
|
1773
1262
|
const layers = [];
|
|
1774
1263
|
layers.push(buildCorePrompt());
|
|
1775
1264
|
if (config.features.planning_layer === "on") {
|
|
@@ -1782,6 +1271,12 @@ async function buildPrompt(config) {
|
|
|
1782
1271
|
if (config.features.parallel_agents === "on") {
|
|
1783
1272
|
layers.push(buildParallelPrompt());
|
|
1784
1273
|
}
|
|
1274
|
+
if (agents && agents.length > 0) {
|
|
1275
|
+
const agentsPrompt = buildAgentsPrompt(agents);
|
|
1276
|
+
if (agentsPrompt) {
|
|
1277
|
+
layers.push(agentsPrompt);
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1785
1280
|
const projectPrompt = await buildProjectPrompt();
|
|
1786
1281
|
if (projectPrompt) {
|
|
1787
1282
|
layers.push(projectPrompt);
|
|
@@ -1800,6 +1295,7 @@ var init_builder = __esm({
|
|
|
1800
1295
|
init_planning();
|
|
1801
1296
|
init_parallel();
|
|
1802
1297
|
init_git();
|
|
1298
|
+
init_agents();
|
|
1803
1299
|
init_project();
|
|
1804
1300
|
}
|
|
1805
1301
|
});
|
|
@@ -1854,74 +1350,266 @@ var init_todo_store = __esm({
|
|
|
1854
1350
|
}
|
|
1855
1351
|
});
|
|
1856
1352
|
|
|
1857
|
-
// src/core/
|
|
1858
|
-
var
|
|
1859
|
-
var
|
|
1860
|
-
"src/core/
|
|
1353
|
+
// src/core/sub-agents/registry.ts
|
|
1354
|
+
var SubAgentConfigRegistry;
|
|
1355
|
+
var init_registry2 = __esm({
|
|
1356
|
+
"src/core/sub-agents/registry.ts"() {
|
|
1861
1357
|
"use strict";
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
write(key, content, author = "agent", summary) {
|
|
1867
|
-
const trimmed = content.slice(0, MAX_CONTENT_LENGTH);
|
|
1868
|
-
const entry = {
|
|
1869
|
-
key,
|
|
1870
|
-
summary: summary || content.slice(0, 80).replace(/\n/g, " "),
|
|
1871
|
-
content: trimmed,
|
|
1872
|
-
author,
|
|
1873
|
-
updatedAt: Date.now()
|
|
1874
|
-
};
|
|
1875
|
-
if (!this.entries.has(key) && this.entries.size >= MAX_ENTRIES) {
|
|
1876
|
-
let oldest = null;
|
|
1877
|
-
let oldestTime = Infinity;
|
|
1878
|
-
for (const [k, v] of this.entries) {
|
|
1879
|
-
if (v.updatedAt < oldestTime) {
|
|
1880
|
-
oldestTime = v.updatedAt;
|
|
1881
|
-
oldest = k;
|
|
1882
|
-
}
|
|
1883
|
-
}
|
|
1884
|
-
if (oldest) this.entries.delete(oldest);
|
|
1885
|
-
}
|
|
1886
|
-
this.entries.set(key, entry);
|
|
1887
|
-
return entry;
|
|
1358
|
+
SubAgentConfigRegistry = class {
|
|
1359
|
+
configs = /* @__PURE__ */ new Map();
|
|
1360
|
+
register(config) {
|
|
1361
|
+
this.configs.set(config.name, config);
|
|
1888
1362
|
}
|
|
1889
|
-
|
|
1890
|
-
return this.
|
|
1363
|
+
get(name) {
|
|
1364
|
+
return this.configs.get(name);
|
|
1365
|
+
}
|
|
1366
|
+
has(name) {
|
|
1367
|
+
return this.configs.has(name);
|
|
1891
1368
|
}
|
|
1892
1369
|
list() {
|
|
1893
|
-
return [...this.
|
|
1894
|
-
key: e.key,
|
|
1895
|
-
author: e.author,
|
|
1896
|
-
summary: e.summary
|
|
1897
|
-
}));
|
|
1370
|
+
return [...this.configs.values()];
|
|
1898
1371
|
}
|
|
1899
|
-
|
|
1900
|
-
return this.
|
|
1372
|
+
listNames() {
|
|
1373
|
+
return [...this.configs.keys()];
|
|
1901
1374
|
}
|
|
1902
|
-
|
|
1903
|
-
|
|
1375
|
+
/**
|
|
1376
|
+
* 生成子 Agent 列表描述(用于 dispatch 工具的说明)
|
|
1377
|
+
*/
|
|
1378
|
+
buildAgentListDescription() {
|
|
1379
|
+
if (this.configs.size === 0) return "\u6682\u65E0\u53EF\u7528\u5B50 Agent";
|
|
1380
|
+
return [...this.configs.values()].map((s) => `- ${s.name}: ${s.description}`).join("\n");
|
|
1381
|
+
}
|
|
1382
|
+
};
|
|
1383
|
+
}
|
|
1384
|
+
});
|
|
1385
|
+
|
|
1386
|
+
// src/core/sub-agents/presets.ts
|
|
1387
|
+
var presetAgents;
|
|
1388
|
+
var init_presets = __esm({
|
|
1389
|
+
"src/core/sub-agents/presets.ts"() {
|
|
1390
|
+
"use strict";
|
|
1391
|
+
presetAgents = [
|
|
1392
|
+
{
|
|
1393
|
+
name: "reviewer",
|
|
1394
|
+
description: "\u4EE3\u7801\u5BA1\u67E5\uFF1A\u53D1\u73B0 bug\u3001\u5B89\u5168\u6F0F\u6D1E\u3001\u6027\u80FD\u95EE\u9898",
|
|
1395
|
+
prompt: `\u4F60\u662F\u4EE3\u7801\u5BA1\u67E5\u4E13\u5BB6\u3002\u5BA1\u67E5\u7528\u6237\u6307\u5B9A\u7684\u4EE3\u7801\uFF0C\u8F93\u51FA\u53D1\u73B0\u7684\u95EE\u9898\u3002
|
|
1396
|
+
|
|
1397
|
+
\u5BA1\u67E5\u7EF4\u5EA6\uFF08\u6309\u4F18\u5148\u7EA7\uFF09\uFF1A
|
|
1398
|
+
1. \u6B63\u786E\u6027\uFF1A\u903B\u8F91\u9519\u8BEF\u3001\u8FB9\u754C\u6761\u4EF6\u3001\u7A7A\u503C/\u672A\u5B9A\u4E49\u5904\u7406
|
|
1399
|
+
2. \u5B89\u5168\uFF1A\u6CE8\u5165\u3001XSS\u3001\u654F\u611F\u4FE1\u606F\u6CC4\u9732\u3001\u6743\u9650\u68C0\u67E5
|
|
1400
|
+
3. \u6027\u80FD\uFF1A\u4E0D\u5FC5\u8981\u7684\u5FAA\u73AF\u3001\u5185\u5B58\u6CC4\u6F0F\u3001N+1 \u67E5\u8BE2
|
|
1401
|
+
4. \u53EF\u7EF4\u62A4\u6027\uFF1A\u547D\u540D\u3001\u91CD\u590D\u4EE3\u7801\u3001\u8FC7\u5EA6\u590D\u6742
|
|
1402
|
+
|
|
1403
|
+
\u8F93\u51FA\u683C\u5F0F\uFF1A
|
|
1404
|
+
- \u6BCF\u4E2A\u95EE\u9898\uFF1A\u6587\u4EF6\u8DEF\u5F84:\u884C\u53F7 + \u95EE\u9898\u63CF\u8FF0 + \u5EFA\u8BAE\u4FEE\u590D
|
|
1405
|
+
- \u6CA1\u6709\u95EE\u9898\u5C31\u8BF4\u6CA1\u6709\u95EE\u9898\uFF0C\u4E0D\u8981\u786C\u51D1
|
|
1406
|
+
- \u4E0D\u8981\u91CD\u5199\u4EE3\u7801\uFF0C\u53EA\u6307\u51FA\u95EE\u9898\u548C\u4FEE\u590D\u65B9\u5411`,
|
|
1407
|
+
tools: ["read-file", "glob", "grep"],
|
|
1408
|
+
max_turns: 10,
|
|
1409
|
+
timeout: 60
|
|
1410
|
+
},
|
|
1411
|
+
{
|
|
1412
|
+
name: "researcher",
|
|
1413
|
+
description: "\u4EE3\u7801\u5E93\u7814\u7A76\uFF1A\u6DF1\u5EA6\u5206\u6790\u67B6\u6784\u3001\u4F9D\u8D56\u548C\u5B9E\u73B0\u7EC6\u8282",
|
|
1414
|
+
prompt: `\u4F60\u662F\u4EE3\u7801\u5E93\u7814\u7A76\u5458\u3002\u6DF1\u5165\u5206\u6790\u7528\u6237\u6307\u5B9A\u7684\u4EE3\u7801\u5E93\u6216\u6A21\u5757\uFF0C\u8F93\u51FA\u7ED3\u6784\u5316\u7684\u5206\u6790\u62A5\u544A\u3002
|
|
1415
|
+
|
|
1416
|
+
\u5206\u6790\u65B9\u6CD5\uFF1A
|
|
1417
|
+
1. \u5148 glob \u4E86\u89E3\u6587\u4EF6\u7ED3\u6784
|
|
1418
|
+
2. grep \u641C\u7D22\u5173\u952E\u5165\u53E3\u70B9\u3001\u5BFC\u51FA\u3001\u4F9D\u8D56
|
|
1419
|
+
3. read-file \u9605\u8BFB\u6838\u5FC3\u6587\u4EF6
|
|
1420
|
+
|
|
1421
|
+
\u8F93\u51FA\u5185\u5BB9\uFF1A
|
|
1422
|
+
- \u6A21\u5757\u804C\u8D23\u548C\u8FB9\u754C
|
|
1423
|
+
- \u5173\u952E\u6587\u4EF6\u53CA\u5176\u4F5C\u7528
|
|
1424
|
+
- \u6570\u636E\u6D41\u548C\u8C03\u7528\u94FE
|
|
1425
|
+
- \u5916\u90E8\u4F9D\u8D56
|
|
1426
|
+
- \u5982\u6709\u7528\u6237\u5177\u4F53\u95EE\u9898\uFF0C\u9488\u5BF9\u6027\u56DE\u7B54`,
|
|
1427
|
+
tools: ["read-file", "glob", "grep"],
|
|
1428
|
+
max_turns: 15,
|
|
1429
|
+
timeout: 120
|
|
1430
|
+
},
|
|
1431
|
+
{
|
|
1432
|
+
name: "refactor",
|
|
1433
|
+
description: "\u91CD\u6784\u4E13\u5BB6\uFF1A\u5206\u6790\u4EE3\u7801\u7ED3\u6784\u5E76\u5B9E\u65BD\u91CD\u6784",
|
|
1434
|
+
prompt: `\u4F60\u662F\u91CD\u6784\u4E13\u5BB6\u3002\u5206\u6790\u7528\u6237\u6307\u5B9A\u7684\u4EE3\u7801\uFF0C\u627E\u51FA\u53EF\u91CD\u6784\u7684\u70B9\u5E76\u5B9E\u65BD\u91CD\u6784\u3002
|
|
1435
|
+
|
|
1436
|
+
\u91CD\u6784\u539F\u5219\uFF1A
|
|
1437
|
+
- \u53EA\u505A\u6709\u660E\u786E\u6536\u76CA\u7684\u91CD\u6784\uFF08\u6D88\u9664\u91CD\u590D\u3001\u964D\u4F4E\u590D\u6742\u5EA6\u3001\u6539\u5584\u547D\u540D\uFF09
|
|
1438
|
+
- \u4FDD\u6301\u884C\u4E3A\u4E0D\u53D8\uFF0C\u4E0D\u6DFB\u52A0\u65B0\u529F\u80FD
|
|
1439
|
+
- \u6BCF\u6B21\u53EA\u505A\u4E00\u4E2A\u91CD\u6784\uFF0C\u4E0D\u8981\u540C\u65F6\u6539\u592A\u591A
|
|
1440
|
+
- \u4FEE\u6539\u524D\u5FC5\u987B read-file \u786E\u8BA4\u5F53\u524D\u5185\u5BB9
|
|
1441
|
+
|
|
1442
|
+
\u5E38\u89C1\u91CD\u6784\uFF1A
|
|
1443
|
+
- \u63D0\u53D6\u91CD\u590D\u4EE3\u7801\u4E3A\u51FD\u6570
|
|
1444
|
+
- \u7B80\u5316\u8FC7\u6DF1\u7684\u5D4C\u5957\uFF08\u63D0\u524D\u8FD4\u56DE\uFF09
|
|
1445
|
+
- \u62C6\u5206\u8FC7\u5927\u7684\u51FD\u6570
|
|
1446
|
+
- \u6539\u5584\u547D\u540D\u4F7F\u610F\u56FE\u66F4\u6E05\u6670`,
|
|
1447
|
+
tools: ["read-file", "write-file", "edit-file", "glob", "grep"],
|
|
1448
|
+
max_turns: 15,
|
|
1449
|
+
timeout: 120
|
|
1450
|
+
}
|
|
1451
|
+
];
|
|
1452
|
+
}
|
|
1453
|
+
});
|
|
1454
|
+
|
|
1455
|
+
// src/core/sub-agents/loader.ts
|
|
1456
|
+
import * as fs9 from "fs";
|
|
1457
|
+
import * as path8 from "path";
|
|
1458
|
+
import * as os3 from "os";
|
|
1459
|
+
import { parse as parseYaml2 } from "yaml";
|
|
1460
|
+
function loadAgentYaml(filePath) {
|
|
1461
|
+
try {
|
|
1462
|
+
const content = fs9.readFileSync(filePath, "utf-8");
|
|
1463
|
+
const parsed = parseYaml2(content);
|
|
1464
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
1465
|
+
if (!parsed["name"] || !parsed["prompt"] || !parsed["tools"]) return null;
|
|
1466
|
+
return {
|
|
1467
|
+
name: parsed["name"],
|
|
1468
|
+
description: parsed["description"] || "",
|
|
1469
|
+
prompt: parsed["prompt"],
|
|
1470
|
+
tools: parsed["tools"],
|
|
1471
|
+
max_turns: parsed["max_turns"],
|
|
1472
|
+
timeout: parsed["timeout"],
|
|
1473
|
+
model: parsed["model"]
|
|
1474
|
+
};
|
|
1475
|
+
} catch {
|
|
1476
|
+
return null;
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
function loadAgentsFromDir(dir) {
|
|
1480
|
+
try {
|
|
1481
|
+
if (!fs9.existsSync(dir)) return [];
|
|
1482
|
+
const files = fs9.readdirSync(dir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
|
|
1483
|
+
const agents = [];
|
|
1484
|
+
for (const file of files) {
|
|
1485
|
+
const agent = loadAgentYaml(path8.join(dir, file));
|
|
1486
|
+
if (agent) agents.push(agent);
|
|
1487
|
+
}
|
|
1488
|
+
return agents;
|
|
1489
|
+
} catch {
|
|
1490
|
+
return [];
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
function loadAllAgentConfigs() {
|
|
1494
|
+
const configMap = /* @__PURE__ */ new Map();
|
|
1495
|
+
for (const agent of presetAgents) {
|
|
1496
|
+
configMap.set(agent.name, agent);
|
|
1497
|
+
}
|
|
1498
|
+
const globalDir = path8.join(os3.homedir(), ".zencode", "agents");
|
|
1499
|
+
for (const agent of loadAgentsFromDir(globalDir)) {
|
|
1500
|
+
configMap.set(agent.name, agent);
|
|
1501
|
+
}
|
|
1502
|
+
const projectDir = path8.resolve(".zencode", "agents");
|
|
1503
|
+
for (const agent of loadAgentsFromDir(projectDir)) {
|
|
1504
|
+
configMap.set(agent.name, agent);
|
|
1505
|
+
}
|
|
1506
|
+
return [...configMap.values()];
|
|
1507
|
+
}
|
|
1508
|
+
var init_loader = __esm({
|
|
1509
|
+
"src/core/sub-agents/loader.ts"() {
|
|
1510
|
+
"use strict";
|
|
1511
|
+
init_presets();
|
|
1512
|
+
}
|
|
1513
|
+
});
|
|
1514
|
+
|
|
1515
|
+
// src/core/skills/registry.ts
|
|
1516
|
+
var SkillRegistry;
|
|
1517
|
+
var init_registry3 = __esm({
|
|
1518
|
+
"src/core/skills/registry.ts"() {
|
|
1519
|
+
"use strict";
|
|
1520
|
+
SkillRegistry = class {
|
|
1521
|
+
skills = /* @__PURE__ */ new Map();
|
|
1522
|
+
register(skill) {
|
|
1523
|
+
this.skills.set(skill.name, skill);
|
|
1524
|
+
}
|
|
1525
|
+
get(name) {
|
|
1526
|
+
return this.skills.get(name);
|
|
1527
|
+
}
|
|
1528
|
+
has(name) {
|
|
1529
|
+
return this.skills.has(name);
|
|
1530
|
+
}
|
|
1531
|
+
list() {
|
|
1532
|
+
return [...this.skills.values()];
|
|
1533
|
+
}
|
|
1534
|
+
listNames() {
|
|
1535
|
+
return [...this.skills.keys()];
|
|
1904
1536
|
}
|
|
1905
1537
|
/**
|
|
1906
|
-
*
|
|
1907
|
-
* 只输出 key + summary,清爽紧凑
|
|
1908
|
-
* Coder 需要详情时用 memo read <key> 获取完整内容
|
|
1538
|
+
* 展开 skill 的 prompt 模板,替换 $ARGS 为用户参数
|
|
1909
1539
|
*/
|
|
1910
|
-
|
|
1911
|
-
if (
|
|
1912
|
-
|
|
1540
|
+
expandPrompt(skill, args) {
|
|
1541
|
+
if (args && skill.prompt.includes("$ARGS")) {
|
|
1542
|
+
return skill.prompt.replace(/\$ARGS/g, args);
|
|
1543
|
+
}
|
|
1544
|
+
if (args) {
|
|
1545
|
+
return `${skill.prompt}
|
|
1546
|
+
|
|
1547
|
+
\u7528\u6237\u8865\u5145: ${args}`;
|
|
1548
|
+
}
|
|
1549
|
+
return skill.prompt;
|
|
1913
1550
|
}
|
|
1914
1551
|
};
|
|
1915
1552
|
}
|
|
1916
1553
|
});
|
|
1917
1554
|
|
|
1555
|
+
// src/core/skills/loader.ts
|
|
1556
|
+
import * as fs10 from "fs";
|
|
1557
|
+
import * as path9 from "path";
|
|
1558
|
+
import * as os4 from "os";
|
|
1559
|
+
import { parse as parseYaml3 } from "yaml";
|
|
1560
|
+
function loadSkillYaml(filePath) {
|
|
1561
|
+
try {
|
|
1562
|
+
const content = fs10.readFileSync(filePath, "utf-8");
|
|
1563
|
+
const parsed = parseYaml3(content);
|
|
1564
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
1565
|
+
if (!parsed["name"] || !parsed["prompt"]) return null;
|
|
1566
|
+
return {
|
|
1567
|
+
name: parsed["name"],
|
|
1568
|
+
description: parsed["description"] || "",
|
|
1569
|
+
prompt: parsed["prompt"]
|
|
1570
|
+
};
|
|
1571
|
+
} catch {
|
|
1572
|
+
return null;
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
function loadSkillsFromDir(dir) {
|
|
1576
|
+
try {
|
|
1577
|
+
if (!fs10.existsSync(dir)) return [];
|
|
1578
|
+
const files = fs10.readdirSync(dir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
|
|
1579
|
+
const skills = [];
|
|
1580
|
+
for (const file of files) {
|
|
1581
|
+
const skill = loadSkillYaml(path9.join(dir, file));
|
|
1582
|
+
if (skill) skills.push(skill);
|
|
1583
|
+
}
|
|
1584
|
+
return skills;
|
|
1585
|
+
} catch {
|
|
1586
|
+
return [];
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
function loadAllSkills() {
|
|
1590
|
+
const skillMap = /* @__PURE__ */ new Map();
|
|
1591
|
+
const globalDir = path9.join(os4.homedir(), ".zencode", "skills");
|
|
1592
|
+
for (const skill of loadSkillsFromDir(globalDir)) {
|
|
1593
|
+
skillMap.set(skill.name, skill);
|
|
1594
|
+
}
|
|
1595
|
+
const projectDir = path9.resolve(".zencode", "skills");
|
|
1596
|
+
for (const skill of loadSkillsFromDir(projectDir)) {
|
|
1597
|
+
skillMap.set(skill.name, skill);
|
|
1598
|
+
}
|
|
1599
|
+
return [...skillMap.values()];
|
|
1600
|
+
}
|
|
1601
|
+
var init_loader2 = __esm({
|
|
1602
|
+
"src/core/skills/loader.ts"() {
|
|
1603
|
+
"use strict";
|
|
1604
|
+
}
|
|
1605
|
+
});
|
|
1606
|
+
|
|
1918
1607
|
// src/core/sub-agent.ts
|
|
1919
1608
|
var DEFAULT_TIMEOUT_MS, SubAgent;
|
|
1920
1609
|
var init_sub_agent = __esm({
|
|
1921
1610
|
"src/core/sub-agent.ts"() {
|
|
1922
1611
|
"use strict";
|
|
1923
1612
|
init_conversation();
|
|
1924
|
-
init_auto_memo();
|
|
1925
1613
|
init_read_tracker();
|
|
1926
1614
|
DEFAULT_TIMEOUT_MS = 12e4;
|
|
1927
1615
|
SubAgent = class {
|
|
@@ -1932,8 +1620,8 @@ var init_sub_agent = __esm({
|
|
|
1932
1620
|
allowedTools;
|
|
1933
1621
|
maxTurns;
|
|
1934
1622
|
timeoutMs;
|
|
1935
|
-
|
|
1936
|
-
constructor(client, registry, config, task, allowedTools = ["read-file", "glob", "grep"
|
|
1623
|
+
tracker;
|
|
1624
|
+
constructor(client, registry, config, task, allowedTools = ["read-file", "glob", "grep"], maxTurns = 10, timeoutMs = DEFAULT_TIMEOUT_MS, tracker) {
|
|
1937
1625
|
this.client = client;
|
|
1938
1626
|
this.registry = registry;
|
|
1939
1627
|
this.config = config;
|
|
@@ -1941,7 +1629,7 @@ var init_sub_agent = __esm({
|
|
|
1941
1629
|
this.allowedTools = allowedTools.filter((t) => t !== "spawn-agents" && t !== "todo");
|
|
1942
1630
|
this.maxTurns = Math.min(maxTurns, 15);
|
|
1943
1631
|
this.timeoutMs = timeoutMs;
|
|
1944
|
-
this.
|
|
1632
|
+
this.tracker = tracker;
|
|
1945
1633
|
}
|
|
1946
1634
|
async run() {
|
|
1947
1635
|
return Promise.race([
|
|
@@ -1960,17 +1648,8 @@ var init_sub_agent = __esm({
|
|
|
1960
1648
|
async execute() {
|
|
1961
1649
|
const conversation = new Conversation();
|
|
1962
1650
|
const readTracker = new ReadTracker();
|
|
1963
|
-
|
|
1651
|
+
const systemPrompt = `\u4F60\u662F ZenCode \u5B50 Agent\u3002\u4F60\u7684\u4EFB\u52A1\uFF1A${this.task}
|
|
1964
1652
|
\u5B8C\u6210\u540E\u76F4\u63A5\u8FD4\u56DE\u7ED3\u679C\uFF0C\u4E0D\u8981\u591A\u4F59\u89E3\u91CA\u3002`;
|
|
1965
|
-
if (this.memoStore) {
|
|
1966
|
-
const index = this.memoStore.buildIndex();
|
|
1967
|
-
if (index) {
|
|
1968
|
-
systemPrompt += `
|
|
1969
|
-
|
|
1970
|
-
[\u5171\u4EAB\u5907\u5FD8\u5F55 - \u53EF\u7528 memo read \u8BFB\u53D6\u8BE6\u60C5]
|
|
1971
|
-
${index}`;
|
|
1972
|
-
}
|
|
1973
|
-
}
|
|
1974
1653
|
conversation.setSystemPrompt(systemPrompt);
|
|
1975
1654
|
conversation.addUserMessage(this.task);
|
|
1976
1655
|
const tools = this.registry.toToolDefinitions(this.allowedTools);
|
|
@@ -1980,6 +1659,9 @@ ${index}`;
|
|
|
1980
1659
|
conversation.getMessages(),
|
|
1981
1660
|
tools.length > 0 ? tools : void 0
|
|
1982
1661
|
);
|
|
1662
|
+
if (assistantMsg.usage && this.tracker) {
|
|
1663
|
+
this.tracker.addTokens(assistantMsg.usage.total_tokens);
|
|
1664
|
+
}
|
|
1983
1665
|
conversation.addAssistantMessage(assistantMsg);
|
|
1984
1666
|
if (!assistantMsg.tool_calls || assistantMsg.tool_calls.length === 0) {
|
|
1985
1667
|
lastContent = assistantMsg.content || "";
|
|
@@ -2027,7 +1709,6 @@ ${index}`;
|
|
|
2027
1709
|
params,
|
|
2028
1710
|
this.config.max_tool_output
|
|
2029
1711
|
);
|
|
2030
|
-
autoMemoForTool(this.memoStore, toolName, params, result.content);
|
|
2031
1712
|
if (toolName === "read-file") {
|
|
2032
1713
|
readTracker.markRead(params["path"]);
|
|
2033
1714
|
} else if (toolName === "write-file") {
|
|
@@ -2050,7 +1731,7 @@ ${index}`;
|
|
|
2050
1731
|
});
|
|
2051
1732
|
|
|
2052
1733
|
// src/tools/spawn-agents.ts
|
|
2053
|
-
function createSpawnAgentsTool(client, registry, config, tracker
|
|
1734
|
+
function createSpawnAgentsTool(client, registry, config, tracker) {
|
|
2054
1735
|
return {
|
|
2055
1736
|
name: "spawn-agents",
|
|
2056
1737
|
description: "\u5E76\u884C\u542F\u52A8\u591A\u4E2A\u5B50 Agent \u6267\u884C\u4EFB\u52A1\u3002\u6BCF\u4E2A\u5B50 Agent \u6709\u72EC\u7ACB\u5BF9\u8BDD\uFF0C\u9ED8\u8BA4\u53EA\u80FD\u7528\u53EA\u8BFB\u5DE5\u5177 (read-file, glob, grep)\u3002\u9002\u7528\u4E8E\u540C\u65F6\u8BFB\u53D6\u548C\u5206\u6790\u591A\u4E2A\u6587\u4EF6\u3001\u641C\u7D22\u591A\u4E2A\u6A21\u5F0F\u7B49\u573A\u666F\u3002",
|
|
@@ -2104,7 +1785,7 @@ function createSpawnAgentsTool(client, registry, config, tracker, memoStore) {
|
|
|
2104
1785
|
if (tools.length === 0) {
|
|
2105
1786
|
tools = DEFAULT_TOOLS.filter((t) => autoTools.includes(t));
|
|
2106
1787
|
}
|
|
2107
|
-
return new SubAgent(client, registry, config, task.description, tools, maxTurns, void 0,
|
|
1788
|
+
return new SubAgent(client, registry, config, task.description, tools, maxTurns, void 0, tracker);
|
|
2108
1789
|
});
|
|
2109
1790
|
const wrappedRuns = agents.map(
|
|
2110
1791
|
(agent) => agent.run().then(
|
|
@@ -2145,7 +1826,7 @@ var init_spawn_agents = __esm({
|
|
|
2145
1826
|
"src/tools/spawn-agents.ts"() {
|
|
2146
1827
|
"use strict";
|
|
2147
1828
|
init_sub_agent();
|
|
2148
|
-
DEFAULT_TOOLS = ["read-file", "glob", "grep"
|
|
1829
|
+
DEFAULT_TOOLS = ["read-file", "glob", "grep"];
|
|
2149
1830
|
MAX_CONCURRENT = 10;
|
|
2150
1831
|
MAX_TURNS_LIMIT = 15;
|
|
2151
1832
|
}
|
|
@@ -2219,141 +1900,310 @@ ${lines.join("\n")}`
|
|
|
2219
1900
|
if (!item) {
|
|
2220
1901
|
return { content: `\u9519\u8BEF\uFF1A\u672A\u627E\u5230\u6761\u76EE "${id}"` };
|
|
2221
1902
|
}
|
|
2222
|
-
const icon = item.status === "completed" ? "\u25CF" : item.status === "in-progress" ? "\u25D0" : "\u25CB";
|
|
2223
|
-
return {
|
|
2224
|
-
content: `\u5DF2\u66F4\u65B0\uFF1A${icon} [${item.id}] ${item.title} \u2192 ${item.status}`
|
|
2225
|
-
};
|
|
2226
|
-
}
|
|
2227
|
-
case "list": {
|
|
2228
|
-
const plan = store.list();
|
|
2229
|
-
if (!plan) {
|
|
2230
|
-
return { content: "\u5F53\u524D\u6CA1\u6709\u8BA1\u5212" };
|
|
1903
|
+
const icon = item.status === "completed" ? "\u25CF" : item.status === "in-progress" ? "\u25D0" : "\u25CB";
|
|
1904
|
+
return {
|
|
1905
|
+
content: `\u5DF2\u66F4\u65B0\uFF1A${icon} [${item.id}] ${item.title} \u2192 ${item.status}`
|
|
1906
|
+
};
|
|
1907
|
+
}
|
|
1908
|
+
case "list": {
|
|
1909
|
+
const plan = store.list();
|
|
1910
|
+
if (!plan) {
|
|
1911
|
+
return { content: "\u5F53\u524D\u6CA1\u6709\u8BA1\u5212" };
|
|
1912
|
+
}
|
|
1913
|
+
const completed = plan.items.filter(
|
|
1914
|
+
(i) => i.status === "completed"
|
|
1915
|
+
).length;
|
|
1916
|
+
const lines = plan.items.map((item) => {
|
|
1917
|
+
const icon = item.status === "completed" ? "\u25CF" : item.status === "in-progress" ? "\u25D0" : "\u25CB";
|
|
1918
|
+
return `${icon} [${item.id}] ${item.title}`;
|
|
1919
|
+
});
|
|
1920
|
+
return {
|
|
1921
|
+
content: `\u8BA1\u5212\u8FDB\u5EA6 ${completed}/${plan.items.length}\uFF1A
|
|
1922
|
+
${lines.join("\n")}`
|
|
1923
|
+
};
|
|
1924
|
+
}
|
|
1925
|
+
case "clear": {
|
|
1926
|
+
store.clear();
|
|
1927
|
+
return { content: "\u8BA1\u5212\u5DF2\u6E05\u7A7A" };
|
|
1928
|
+
}
|
|
1929
|
+
default:
|
|
1930
|
+
return {
|
|
1931
|
+
content: `\u9519\u8BEF\uFF1A\u672A\u77E5\u64CD\u4F5C "${action}"\u3002\u652F\u6301: create, update, list, clear`
|
|
1932
|
+
};
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
};
|
|
1936
|
+
}
|
|
1937
|
+
var init_todo = __esm({
|
|
1938
|
+
"src/tools/todo.ts"() {
|
|
1939
|
+
"use strict";
|
|
1940
|
+
}
|
|
1941
|
+
});
|
|
1942
|
+
|
|
1943
|
+
// src/core/sub-agents/runner.ts
|
|
1944
|
+
var DEFAULT_MAX_TURNS, SubAgentRunner;
|
|
1945
|
+
var init_runner = __esm({
|
|
1946
|
+
"src/core/sub-agents/runner.ts"() {
|
|
1947
|
+
"use strict";
|
|
1948
|
+
init_conversation();
|
|
1949
|
+
init_permission();
|
|
1950
|
+
init_read_tracker();
|
|
1951
|
+
DEFAULT_MAX_TURNS = 15;
|
|
1952
|
+
SubAgentRunner = class {
|
|
1953
|
+
client;
|
|
1954
|
+
registry;
|
|
1955
|
+
config;
|
|
1956
|
+
agentConfig;
|
|
1957
|
+
tracker;
|
|
1958
|
+
constructor(client, registry, config, agentConfig, tracker) {
|
|
1959
|
+
this.client = client;
|
|
1960
|
+
this.registry = registry;
|
|
1961
|
+
this.config = config;
|
|
1962
|
+
this.agentConfig = agentConfig;
|
|
1963
|
+
this.tracker = tracker;
|
|
1964
|
+
}
|
|
1965
|
+
/**
|
|
1966
|
+
* 执行子 Agent 任务
|
|
1967
|
+
*/
|
|
1968
|
+
async execute(task, context, callbacks = {}) {
|
|
1969
|
+
const timeoutMs = (this.agentConfig.timeout ?? 120) * 1e3;
|
|
1970
|
+
const maxTurns = this.agentConfig.max_turns ?? DEFAULT_MAX_TURNS;
|
|
1971
|
+
return Promise.race([
|
|
1972
|
+
this.run(task, context, maxTurns, callbacks),
|
|
1973
|
+
this.timeout(timeoutMs)
|
|
1974
|
+
]);
|
|
1975
|
+
}
|
|
1976
|
+
timeout(ms) {
|
|
1977
|
+
return new Promise((_, reject) => {
|
|
1978
|
+
setTimeout(
|
|
1979
|
+
() => reject(new Error(`\u5B50 Agent "${this.agentConfig.name}" \u8D85\u65F6\uFF08${ms / 1e3}s\uFF09`)),
|
|
1980
|
+
ms
|
|
1981
|
+
);
|
|
1982
|
+
});
|
|
1983
|
+
}
|
|
1984
|
+
async run(task, context, maxTurns, callbacks) {
|
|
1985
|
+
const conversation = new Conversation();
|
|
1986
|
+
const readTracker = new ReadTracker();
|
|
1987
|
+
conversation.setSystemPrompt(this.agentConfig.prompt);
|
|
1988
|
+
let taskMessage = task;
|
|
1989
|
+
if (context) {
|
|
1990
|
+
taskMessage += `
|
|
1991
|
+
|
|
1992
|
+
[\u4E0A\u4E0B\u6587]
|
|
1993
|
+
${context}`;
|
|
1994
|
+
}
|
|
1995
|
+
conversation.addUserMessage(taskMessage);
|
|
1996
|
+
const tools = this.registry.toToolDefinitions(this.agentConfig.tools);
|
|
1997
|
+
let lastContent = "";
|
|
1998
|
+
for (let turn = 0; turn < maxTurns; turn++) {
|
|
1999
|
+
const assistantMsg = await this.client.chatStream(
|
|
2000
|
+
conversation.getMessages(),
|
|
2001
|
+
tools.length > 0 ? tools : void 0,
|
|
2002
|
+
callbacks
|
|
2003
|
+
);
|
|
2004
|
+
if (assistantMsg.usage && this.tracker) {
|
|
2005
|
+
this.tracker.addTokens(assistantMsg.usage.total_tokens);
|
|
2006
|
+
}
|
|
2007
|
+
conversation.addAssistantMessage(assistantMsg);
|
|
2008
|
+
if (!assistantMsg.tool_calls || assistantMsg.tool_calls.length === 0) {
|
|
2009
|
+
lastContent = assistantMsg.content || "";
|
|
2010
|
+
break;
|
|
2011
|
+
}
|
|
2012
|
+
for (const toolCall of assistantMsg.tool_calls) {
|
|
2013
|
+
const toolName = toolCall.function.name;
|
|
2014
|
+
if (!this.agentConfig.tools.includes(toolName)) {
|
|
2015
|
+
conversation.addToolResult(
|
|
2016
|
+
toolCall.id,
|
|
2017
|
+
`\u5B50 Agent "${this.agentConfig.name}" \u4E0D\u5141\u8BB8\u4F7F\u7528\u5DE5\u5177 "${toolName}"`
|
|
2018
|
+
);
|
|
2019
|
+
continue;
|
|
2020
|
+
}
|
|
2021
|
+
let params;
|
|
2022
|
+
try {
|
|
2023
|
+
params = JSON.parse(toolCall.function.arguments);
|
|
2024
|
+
} catch {
|
|
2025
|
+
conversation.addToolResult(toolCall.id, "\u53C2\u6570\u89E3\u6790\u5931\u8D25\uFF1A\u65E0\u6548\u7684 JSON");
|
|
2026
|
+
continue;
|
|
2027
|
+
}
|
|
2028
|
+
try {
|
|
2029
|
+
if (toolName === "edit-file") {
|
|
2030
|
+
const editPath = params["path"];
|
|
2031
|
+
if (!readTracker.hasRead(editPath)) {
|
|
2032
|
+
conversation.addToolResult(
|
|
2033
|
+
toolCall.id,
|
|
2034
|
+
`\u26A0 \u7981\u6B62\u7F16\u8F91\u672A\u8BFB\u53D6\u7684\u6587\u4EF6\u3002\u8BF7\u5148 read-file "${editPath}" \u4E86\u89E3\u5F53\u524D\u5185\u5BB9\uFF0C\u518D edit-file\u3002`
|
|
2035
|
+
);
|
|
2036
|
+
continue;
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
if (toolName === "write-file") {
|
|
2040
|
+
const warn = readTracker.checkWriteOverwrite(
|
|
2041
|
+
params["path"],
|
|
2042
|
+
params["overwrite"]
|
|
2043
|
+
);
|
|
2044
|
+
if (warn) {
|
|
2045
|
+
conversation.addToolResult(toolCall.id, warn);
|
|
2046
|
+
continue;
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
const permLevel = this.registry.getPermissionLevel(toolName);
|
|
2050
|
+
if (permLevel === "deny") {
|
|
2051
|
+
callbacks.onDenied?.(toolName);
|
|
2052
|
+
conversation.addToolResult(toolCall.id, `\u5DE5\u5177 "${toolName}" \u5DF2\u88AB\u7981\u6B62\u6267\u884C`);
|
|
2053
|
+
continue;
|
|
2054
|
+
}
|
|
2055
|
+
if (permLevel === "auto") {
|
|
2056
|
+
callbacks.onToolExecuting?.(toolName, params);
|
|
2057
|
+
}
|
|
2058
|
+
if (permLevel === "confirm") {
|
|
2059
|
+
const confirmResult = await confirmExecution(toolName, params);
|
|
2060
|
+
if (!confirmResult.approved) {
|
|
2061
|
+
callbacks.onDenied?.(toolName, confirmResult.feedback);
|
|
2062
|
+
const denyMsg = confirmResult.feedback ? `\u7528\u6237\u62D2\u7EDD\u4E86\u6B64\u64CD\u4F5C\uFF0C\u7528\u6237\u53CD\u9988: ${confirmResult.feedback}` : "\u7528\u6237\u62D2\u7EDD\u4E86\u6B64\u64CD\u4F5C";
|
|
2063
|
+
conversation.addToolResult(toolCall.id, denyMsg);
|
|
2064
|
+
continue;
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
const result = await this.registry.execute(toolName, params, this.config.max_tool_output);
|
|
2068
|
+
callbacks.onToolResult?.(toolName, result.content, result.truncated ?? false);
|
|
2069
|
+
if (toolName === "read-file") {
|
|
2070
|
+
readTracker.markRead(params["path"]);
|
|
2071
|
+
} else if (toolName === "write-file") {
|
|
2072
|
+
readTracker.markWritten(params["path"]);
|
|
2073
|
+
}
|
|
2074
|
+
conversation.addToolResult(toolCall.id, result.content);
|
|
2075
|
+
} catch (err) {
|
|
2076
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2077
|
+
conversation.addToolResult(toolCall.id, `\u5DE5\u5177\u6267\u884C\u5F02\u5E38\uFF1A${msg}`);
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
if (assistantMsg.content) {
|
|
2081
|
+
lastContent = assistantMsg.content;
|
|
2231
2082
|
}
|
|
2232
|
-
const completed = plan.items.filter(
|
|
2233
|
-
(i) => i.status === "completed"
|
|
2234
|
-
).length;
|
|
2235
|
-
const lines = plan.items.map((item) => {
|
|
2236
|
-
const icon = item.status === "completed" ? "\u25CF" : item.status === "in-progress" ? "\u25D0" : "\u25CB";
|
|
2237
|
-
return `${icon} [${item.id}] ${item.title}`;
|
|
2238
|
-
});
|
|
2239
|
-
return {
|
|
2240
|
-
content: `\u8BA1\u5212\u8FDB\u5EA6 ${completed}/${plan.items.length}\uFF1A
|
|
2241
|
-
${lines.join("\n")}`
|
|
2242
|
-
};
|
|
2243
|
-
}
|
|
2244
|
-
case "clear": {
|
|
2245
|
-
store.clear();
|
|
2246
|
-
return { content: "\u8BA1\u5212\u5DF2\u6E05\u7A7A" };
|
|
2247
2083
|
}
|
|
2248
|
-
|
|
2249
|
-
return {
|
|
2250
|
-
content: `\u9519\u8BEF\uFF1A\u672A\u77E5\u64CD\u4F5C "${action}"\u3002\u652F\u6301: create, update, list, clear`
|
|
2251
|
-
};
|
|
2084
|
+
return lastContent;
|
|
2252
2085
|
}
|
|
2253
|
-
}
|
|
2254
|
-
};
|
|
2255
|
-
}
|
|
2256
|
-
var init_todo = __esm({
|
|
2257
|
-
"src/tools/todo.ts"() {
|
|
2258
|
-
"use strict";
|
|
2086
|
+
};
|
|
2259
2087
|
}
|
|
2260
2088
|
});
|
|
2261
2089
|
|
|
2262
|
-
// src/tools/
|
|
2263
|
-
function
|
|
2090
|
+
// src/tools/dispatch.ts
|
|
2091
|
+
function createDispatchTool(defaultClient, toolRegistry, config, agentRegistry, tracker, callbacks) {
|
|
2264
2092
|
return {
|
|
2265
|
-
name: "
|
|
2266
|
-
description: "\
|
|
2093
|
+
name: "dispatch",
|
|
2094
|
+
description: "\u8C03\u5EA6\u5B50 Agent \u6267\u884C\u4E13\u95E8\u4EFB\u52A1\u3002\u5B50 Agent \u6709\u72EC\u7ACB\u5BF9\u8BDD\u548C\u4E13\u5C5E\u7CFB\u7EDF\u63D0\u793A\u8BCD\uFF0C\u9002\u7528\u4E8E\u9700\u8981\u4E13\u4E1A\u89D2\u8272\u7684\u573A\u666F\u3002",
|
|
2267
2095
|
parameters: {
|
|
2268
2096
|
type: "object",
|
|
2269
2097
|
properties: {
|
|
2270
|
-
|
|
2271
|
-
type: "string",
|
|
2272
|
-
description: "\u64CD\u4F5C\u7C7B\u578B",
|
|
2273
|
-
enum: ["write", "read", "list", "delete", "clear"]
|
|
2274
|
-
},
|
|
2275
|
-
key: {
|
|
2098
|
+
agent: {
|
|
2276
2099
|
type: "string",
|
|
2277
|
-
description:
|
|
2100
|
+
description: `\u5B50 Agent \u540D\u79F0\u3002\u53EF\u9009: ${agentRegistry.listNames().join(", ")}`,
|
|
2101
|
+
enum: agentRegistry.listNames()
|
|
2278
2102
|
},
|
|
2279
|
-
|
|
2103
|
+
task: {
|
|
2280
2104
|
type: "string",
|
|
2281
|
-
description: "
|
|
2105
|
+
description: "\u8981\u6267\u884C\u7684\u5177\u4F53\u4EFB\u52A1\u63CF\u8FF0"
|
|
2282
2106
|
},
|
|
2283
|
-
|
|
2107
|
+
context: {
|
|
2284
2108
|
type: "string",
|
|
2285
|
-
description: "
|
|
2109
|
+
description: "\u53EF\u9009\u7684\u989D\u5916\u4E0A\u4E0B\u6587\u4FE1\u606F\uFF08\u5982\u53C2\u8003\u6587\u4EF6\u8DEF\u5F84\u3001\u4F9D\u8D56\u5173\u7CFB\u7B49\uFF09"
|
|
2286
2110
|
}
|
|
2287
2111
|
},
|
|
2288
|
-
required: ["
|
|
2112
|
+
required: ["agent", "task"]
|
|
2289
2113
|
},
|
|
2290
2114
|
permissionLevel: "auto",
|
|
2291
2115
|
async execute(params) {
|
|
2292
|
-
const
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
return { content: "\u5907\u5FD8\u5F55\u4E3A\u7A7A" };
|
|
2320
|
-
}
|
|
2321
|
-
const lines = items.map(
|
|
2322
|
-
(item) => `[${item.key}] (${item.author}) ${item.summary}`
|
|
2323
|
-
);
|
|
2324
|
-
return { content: `\u5171 ${items.length} \u6761\u5907\u5FD8\u5F55\uFF1A
|
|
2325
|
-
${lines.join("\n")}` };
|
|
2326
|
-
}
|
|
2327
|
-
case "delete": {
|
|
2328
|
-
const key = params["key"];
|
|
2329
|
-
if (!key) {
|
|
2330
|
-
return { content: "\u9519\u8BEF\uFF1Adelete \u9700\u8981\u63D0\u4F9B key" };
|
|
2331
|
-
}
|
|
2332
|
-
const ok = store.delete(key);
|
|
2333
|
-
return { content: ok ? `\u5DF2\u5220\u9664 memo [${key}]` : `memo [${key}] \u4E0D\u5B58\u5728` };
|
|
2334
|
-
}
|
|
2335
|
-
case "clear": {
|
|
2336
|
-
store.clear();
|
|
2337
|
-
return { content: "\u5907\u5FD8\u5F55\u5DF2\u6E05\u7A7A" };
|
|
2338
|
-
}
|
|
2339
|
-
default:
|
|
2340
|
-
return { content: `\u9519\u8BEF\uFF1A\u672A\u77E5\u64CD\u4F5C "${action}"\u3002\u652F\u6301: write, read, list, delete, clear` };
|
|
2116
|
+
const agentName = params["agent"];
|
|
2117
|
+
const task = params["task"];
|
|
2118
|
+
const context = params["context"];
|
|
2119
|
+
const agentConfig = agentRegistry.get(agentName);
|
|
2120
|
+
if (!agentConfig) {
|
|
2121
|
+
return { content: `\u9519\u8BEF\uFF1A\u672A\u627E\u5230\u5B50 Agent "${agentName}"\u3002\u53EF\u7528: ${agentRegistry.listNames().join(", ")}` };
|
|
2122
|
+
}
|
|
2123
|
+
let client = defaultClient;
|
|
2124
|
+
if (agentConfig.model) {
|
|
2125
|
+
client = createLLMClient({
|
|
2126
|
+
apiKey: agentConfig.model.api_key || config.api_key,
|
|
2127
|
+
baseURL: agentConfig.model.base_url || config.base_url,
|
|
2128
|
+
model: agentConfig.model.model || config.model,
|
|
2129
|
+
temperature: config.temperature,
|
|
2130
|
+
maxTokens: config.max_tokens
|
|
2131
|
+
});
|
|
2132
|
+
}
|
|
2133
|
+
const runner = new SubAgentRunner(client, toolRegistry, config, agentConfig, tracker);
|
|
2134
|
+
tracker?.start([`${agentName}: ${task.slice(0, 60)}`]);
|
|
2135
|
+
try {
|
|
2136
|
+
const result = await runner.execute(task, context, callbacks?.() ?? {});
|
|
2137
|
+
tracker?.finish();
|
|
2138
|
+
return { content: result || "\uFF08\u5B50 Agent \u6267\u884C\u5B8C\u6210\uFF0C\u65E0\u8F93\u51FA\uFF09" };
|
|
2139
|
+
} catch (err) {
|
|
2140
|
+
tracker?.finish();
|
|
2141
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2142
|
+
return { content: `\u5B50 Agent \u6267\u884C\u9519\u8BEF\uFF1A${msg}` };
|
|
2341
2143
|
}
|
|
2342
2144
|
}
|
|
2343
2145
|
};
|
|
2344
2146
|
}
|
|
2345
|
-
var
|
|
2346
|
-
"src/tools/
|
|
2147
|
+
var init_dispatch = __esm({
|
|
2148
|
+
"src/tools/dispatch.ts"() {
|
|
2347
2149
|
"use strict";
|
|
2150
|
+
init_runner();
|
|
2151
|
+
init_client();
|
|
2348
2152
|
}
|
|
2349
2153
|
});
|
|
2350
2154
|
|
|
2351
2155
|
// src/cli/tui/bridge.ts
|
|
2156
|
+
function gray(text) {
|
|
2157
|
+
return `${ANSI_GRAY}${text}${ANSI_RESET}`;
|
|
2158
|
+
}
|
|
2352
2159
|
function createThinkFilter() {
|
|
2353
2160
|
let inThink = false;
|
|
2354
2161
|
let tagBuffer = "";
|
|
2355
|
-
let
|
|
2162
|
+
let thinkLineBuffer = "";
|
|
2163
|
+
let thinkHasVisibleContent = false;
|
|
2164
|
+
let thinkLastEmittedBlank = false;
|
|
2356
2165
|
let postThink = false;
|
|
2166
|
+
function flushThinkLine(rawLine) {
|
|
2167
|
+
const normalized = rawLine.trim();
|
|
2168
|
+
if (!thinkHasVisibleContent && normalized.length === 0) return "";
|
|
2169
|
+
if (normalized.length === 0) {
|
|
2170
|
+
if (thinkLastEmittedBlank) return "";
|
|
2171
|
+
thinkLastEmittedBlank = true;
|
|
2172
|
+
return "\n";
|
|
2173
|
+
}
|
|
2174
|
+
thinkHasVisibleContent = true;
|
|
2175
|
+
thinkLastEmittedBlank = false;
|
|
2176
|
+
return `${gray(` ${normalized}`)}
|
|
2177
|
+
`;
|
|
2178
|
+
}
|
|
2179
|
+
function appendOutsideText(current, text) {
|
|
2180
|
+
if (!postThink) return current + text;
|
|
2181
|
+
let result = current;
|
|
2182
|
+
for (let i = 0; i < text.length; i++) {
|
|
2183
|
+
const ch = text[i];
|
|
2184
|
+
if (postThink && (ch === "\n" || ch === "\r" || ch === " " || ch === " ")) {
|
|
2185
|
+
continue;
|
|
2186
|
+
}
|
|
2187
|
+
postThink = false;
|
|
2188
|
+
result += text.slice(i);
|
|
2189
|
+
break;
|
|
2190
|
+
}
|
|
2191
|
+
return result;
|
|
2192
|
+
}
|
|
2193
|
+
function appendThinkText(current, text) {
|
|
2194
|
+
let result = current;
|
|
2195
|
+
for (let i = 0; i < text.length; i++) {
|
|
2196
|
+
const ch = text[i];
|
|
2197
|
+
if (ch === "\r") continue;
|
|
2198
|
+
if (ch === "\n") {
|
|
2199
|
+
result += flushThinkLine(thinkLineBuffer);
|
|
2200
|
+
thinkLineBuffer = "";
|
|
2201
|
+
} else {
|
|
2202
|
+
thinkLineBuffer += ch;
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
return result;
|
|
2206
|
+
}
|
|
2357
2207
|
return function filter(text) {
|
|
2358
2208
|
let result = "";
|
|
2359
2209
|
for (let i = 0; i < text.length; i++) {
|
|
@@ -2362,24 +2212,25 @@ function createThinkFilter() {
|
|
|
2362
2212
|
tagBuffer += ch;
|
|
2363
2213
|
if (tagBuffer === "<think>") {
|
|
2364
2214
|
inThink = true;
|
|
2365
|
-
|
|
2215
|
+
thinkLineBuffer = "";
|
|
2216
|
+
thinkHasVisibleContent = false;
|
|
2217
|
+
thinkLastEmittedBlank = false;
|
|
2366
2218
|
tagBuffer = "";
|
|
2367
|
-
result +=
|
|
2219
|
+
result += `${gray("Thinking")}
|
|
2368
2220
|
`;
|
|
2369
2221
|
} else if (tagBuffer === "</think>") {
|
|
2370
2222
|
inThink = false;
|
|
2371
2223
|
postThink = true;
|
|
2372
2224
|
tagBuffer = "";
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
`;
|
|
2377
|
-
} else if (!"<think>".startsWith(tagBuffer) && !"</think>".startsWith(tagBuffer)) {
|
|
2378
|
-
if (inThink && lineStart) {
|
|
2379
|
-
result += "\u2502 ";
|
|
2380
|
-
lineStart = false;
|
|
2225
|
+
if (thinkLineBuffer.length > 0) {
|
|
2226
|
+
result += flushThinkLine(thinkLineBuffer);
|
|
2227
|
+
thinkLineBuffer = "";
|
|
2381
2228
|
}
|
|
2382
|
-
result
|
|
2229
|
+
while (result.endsWith("\n\n\n")) result = result.slice(0, -1);
|
|
2230
|
+
result += "\n";
|
|
2231
|
+
} else if (!"<think>".startsWith(tagBuffer) && !"</think>".startsWith(tagBuffer)) {
|
|
2232
|
+
if (inThink) result = appendThinkText(result, tagBuffer);
|
|
2233
|
+
else result = appendOutsideText(result, tagBuffer);
|
|
2383
2234
|
tagBuffer = "";
|
|
2384
2235
|
}
|
|
2385
2236
|
continue;
|
|
@@ -2388,34 +2239,20 @@ function createThinkFilter() {
|
|
|
2388
2239
|
tagBuffer = "<";
|
|
2389
2240
|
continue;
|
|
2390
2241
|
}
|
|
2391
|
-
if (inThink)
|
|
2392
|
-
|
|
2393
|
-
result += "\u2502 ";
|
|
2394
|
-
lineStart = false;
|
|
2395
|
-
}
|
|
2396
|
-
result += ch;
|
|
2397
|
-
if (ch === "\n") {
|
|
2398
|
-
lineStart = true;
|
|
2399
|
-
}
|
|
2400
|
-
} else {
|
|
2401
|
-
if (postThink) {
|
|
2402
|
-
if (ch === "\n" || ch === "\r" || ch === " " || ch === " ") continue;
|
|
2403
|
-
postThink = false;
|
|
2404
|
-
}
|
|
2405
|
-
result += ch;
|
|
2406
|
-
}
|
|
2242
|
+
if (inThink) result = appendThinkText(result, ch);
|
|
2243
|
+
else result = appendOutsideText(result, ch);
|
|
2407
2244
|
}
|
|
2408
2245
|
return result;
|
|
2409
2246
|
};
|
|
2410
2247
|
}
|
|
2411
|
-
function createTokenBatcher(dispatch) {
|
|
2248
|
+
function createTokenBatcher(dispatch, type) {
|
|
2412
2249
|
let buffer = "";
|
|
2413
2250
|
let timer = null;
|
|
2414
2251
|
function flush() {
|
|
2415
2252
|
if (buffer.length > 0) {
|
|
2416
2253
|
const text = buffer;
|
|
2417
2254
|
buffer = "";
|
|
2418
|
-
dispatch({ type
|
|
2255
|
+
dispatch({ type, text });
|
|
2419
2256
|
}
|
|
2420
2257
|
}
|
|
2421
2258
|
function start() {
|
|
@@ -2460,8 +2297,10 @@ function registerConfirmToolId(toolName, id) {
|
|
|
2460
2297
|
activeToolIds.set(toolName, id);
|
|
2461
2298
|
}
|
|
2462
2299
|
function createBridgeCallbacks(dispatch) {
|
|
2463
|
-
const
|
|
2464
|
-
const
|
|
2300
|
+
const contentBatcher = createTokenBatcher(dispatch, "APPEND_CONTENT");
|
|
2301
|
+
const thoughtBatcher = createTokenBatcher(dispatch, "APPEND_THOUGHT");
|
|
2302
|
+
let inThink = false;
|
|
2303
|
+
let tagBuffer = "";
|
|
2465
2304
|
activeToolIds = /* @__PURE__ */ new Map();
|
|
2466
2305
|
streamingToolIds = /* @__PURE__ */ new Map();
|
|
2467
2306
|
lastStreamingArgs = /* @__PURE__ */ new Map();
|
|
@@ -2480,12 +2319,41 @@ function createBridgeCallbacks(dispatch) {
|
|
|
2480
2319
|
}
|
|
2481
2320
|
return {
|
|
2482
2321
|
onContent: (text) => {
|
|
2483
|
-
|
|
2484
|
-
|
|
2322
|
+
for (let i = 0; i < text.length; i++) {
|
|
2323
|
+
const ch = text[i];
|
|
2324
|
+
if (tagBuffer.length > 0 || ch === "<") {
|
|
2325
|
+
tagBuffer += ch;
|
|
2326
|
+
if (tagBuffer === "<think>") {
|
|
2327
|
+
contentBatcher.flush();
|
|
2328
|
+
inThink = true;
|
|
2329
|
+
tagBuffer = "";
|
|
2330
|
+
continue;
|
|
2331
|
+
} else if (tagBuffer === "</think>") {
|
|
2332
|
+
thoughtBatcher.flush();
|
|
2333
|
+
inThink = false;
|
|
2334
|
+
tagBuffer = "";
|
|
2335
|
+
continue;
|
|
2336
|
+
} else if (!"<think>".startsWith(tagBuffer) && !"</think>".startsWith(tagBuffer)) {
|
|
2337
|
+
if (inThink) {
|
|
2338
|
+
thoughtBatcher.append(tagBuffer);
|
|
2339
|
+
} else {
|
|
2340
|
+
contentBatcher.append(tagBuffer);
|
|
2341
|
+
}
|
|
2342
|
+
tagBuffer = "";
|
|
2343
|
+
}
|
|
2344
|
+
continue;
|
|
2345
|
+
}
|
|
2346
|
+
if (inThink) {
|
|
2347
|
+
thoughtBatcher.append(ch);
|
|
2348
|
+
} else {
|
|
2349
|
+
contentBatcher.append(ch);
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2485
2352
|
},
|
|
2486
2353
|
onToolCallStreaming: (index, name, accumulatedArgs) => {
|
|
2487
2354
|
if (!streamingToolIds.has(index)) {
|
|
2488
|
-
|
|
2355
|
+
contentBatcher.flush();
|
|
2356
|
+
thoughtBatcher.flush();
|
|
2489
2357
|
const id2 = `tool-${++toolCallCounter}`;
|
|
2490
2358
|
streamingToolIds.set(index, id2);
|
|
2491
2359
|
activeToolIds.set(name, id2);
|
|
@@ -2505,8 +2373,10 @@ function createBridgeCallbacks(dispatch) {
|
|
|
2505
2373
|
},
|
|
2506
2374
|
onToolExecuting: (name, params) => {
|
|
2507
2375
|
flushStreamingUpdate();
|
|
2508
|
-
|
|
2509
|
-
|
|
2376
|
+
contentBatcher.flush();
|
|
2377
|
+
thoughtBatcher.flush();
|
|
2378
|
+
contentBatcher.pause();
|
|
2379
|
+
thoughtBatcher.pause();
|
|
2510
2380
|
const existingId = activeToolIds.get(name);
|
|
2511
2381
|
const id = existingId || `tool-${++toolCallCounter}`;
|
|
2512
2382
|
activeToolIds.set(name, id);
|
|
@@ -2520,41 +2390,36 @@ function createBridgeCallbacks(dispatch) {
|
|
|
2520
2390
|
if (isWriteTool) {
|
|
2521
2391
|
const code = extractCodeFromArgs(name, lastStreamingArgs.get(id) || "");
|
|
2522
2392
|
const codeLines = code ? code.split("\n").length : 0;
|
|
2523
|
-
summary = codeLines > 0 ? `${codeLines}
|
|
2393
|
+
summary = codeLines > 0 ? `${codeLines} lines` : truncated ? "truncated" : `${lines.length} lines`;
|
|
2524
2394
|
} else {
|
|
2525
|
-
summary = truncated ?
|
|
2395
|
+
summary = truncated ? `truncated` : `${lines.length} lines`;
|
|
2526
2396
|
}
|
|
2527
2397
|
const preview = lines.slice(0, 5).join("\n").slice(0, 200);
|
|
2528
2398
|
const resultContent = lines.length > 5 || preview.length >= 200 ? preview + "..." : preview;
|
|
2529
2399
|
dispatch({ type: "TOOL_RESULT", id, resultSummary: summary, resultContent });
|
|
2530
2400
|
},
|
|
2531
|
-
onCoderStart: () => {
|
|
2532
|
-
batcher.flush();
|
|
2533
|
-
dispatch({ type: "CODER_START" });
|
|
2534
|
-
},
|
|
2535
|
-
onCoderEnd: () => {
|
|
2536
|
-
dispatch({ type: "CODER_END" });
|
|
2537
|
-
},
|
|
2538
2401
|
onDenied: (toolName, feedback) => {
|
|
2539
2402
|
const id = activeToolIds.get(toolName) || `tool-${++toolCallCounter}`;
|
|
2540
2403
|
dispatch({ type: "TOOL_DENIED", id, feedback });
|
|
2541
2404
|
},
|
|
2542
2405
|
onError: (err) => {
|
|
2543
|
-
|
|
2406
|
+
contentBatcher.stop();
|
|
2407
|
+
thoughtBatcher.stop();
|
|
2544
2408
|
dispatch({ type: "SET_ERROR", error: err.message });
|
|
2545
2409
|
},
|
|
2546
|
-
// Called internally when streaming is complete
|
|
2547
2410
|
_stopBatcher: () => {
|
|
2548
|
-
|
|
2411
|
+
contentBatcher.stop();
|
|
2412
|
+
thoughtBatcher.stop();
|
|
2549
2413
|
}
|
|
2550
2414
|
};
|
|
2551
2415
|
}
|
|
2552
|
-
var BATCH_INTERVAL_MS,
|
|
2416
|
+
var BATCH_INTERVAL_MS, ANSI_GRAY, ANSI_RESET, toolCallCounter, activeToolIds, streamingToolIds, lastStreamingArgs;
|
|
2553
2417
|
var init_bridge = __esm({
|
|
2554
2418
|
"src/cli/tui/bridge.ts"() {
|
|
2555
2419
|
"use strict";
|
|
2556
2420
|
BATCH_INTERVAL_MS = 64;
|
|
2557
|
-
|
|
2421
|
+
ANSI_GRAY = "\x1B[90m";
|
|
2422
|
+
ANSI_RESET = "\x1B[0m";
|
|
2558
2423
|
toolCallCounter = 0;
|
|
2559
2424
|
activeToolIds = /* @__PURE__ */ new Map();
|
|
2560
2425
|
streamingToolIds = /* @__PURE__ */ new Map();
|
|
@@ -2575,7 +2440,8 @@ var init_sub_agent_tracker = __esm({
|
|
|
2575
2440
|
total: descriptions.length,
|
|
2576
2441
|
completed: 0,
|
|
2577
2442
|
failed: 0,
|
|
2578
|
-
descriptions
|
|
2443
|
+
descriptions,
|
|
2444
|
+
tokens: 0
|
|
2579
2445
|
};
|
|
2580
2446
|
this.notify();
|
|
2581
2447
|
}
|
|
@@ -2589,6 +2455,11 @@ var init_sub_agent_tracker = __esm({
|
|
|
2589
2455
|
this.progress = { ...this.progress, failed: this.progress.failed + 1 };
|
|
2590
2456
|
this.notify();
|
|
2591
2457
|
}
|
|
2458
|
+
addTokens(count) {
|
|
2459
|
+
if (!this.progress) return;
|
|
2460
|
+
this.progress = { ...this.progress, tokens: this.progress.tokens + count };
|
|
2461
|
+
this.notify();
|
|
2462
|
+
}
|
|
2592
2463
|
finish() {
|
|
2593
2464
|
this.progress = null;
|
|
2594
2465
|
this.notify();
|
|
@@ -2613,15 +2484,12 @@ var init_sub_agent_tracker = __esm({
|
|
|
2613
2484
|
});
|
|
2614
2485
|
|
|
2615
2486
|
// src/cli/tui/state.ts
|
|
2616
|
-
function createInitialState(modelName
|
|
2487
|
+
function createInitialState(modelName) {
|
|
2617
2488
|
return {
|
|
2618
2489
|
messages: [],
|
|
2619
2490
|
isRunning: false,
|
|
2620
2491
|
error: void 0,
|
|
2621
|
-
coderWorking: false,
|
|
2622
2492
|
modelName,
|
|
2623
|
-
agentMode,
|
|
2624
|
-
collaboration,
|
|
2625
2493
|
todoPlan: null,
|
|
2626
2494
|
subAgentProgress: null
|
|
2627
2495
|
};
|
|
@@ -2691,6 +2559,21 @@ function tuiReducer(state, action) {
|
|
|
2691
2559
|
})
|
|
2692
2560
|
};
|
|
2693
2561
|
}
|
|
2562
|
+
case "APPEND_THOUGHT": {
|
|
2563
|
+
return {
|
|
2564
|
+
...state,
|
|
2565
|
+
messages: updateLastAssistant(state.messages, (msg) => {
|
|
2566
|
+
const blocks = [...msg.blocks];
|
|
2567
|
+
const last = blocks[blocks.length - 1];
|
|
2568
|
+
if (last && last.type === "thought") {
|
|
2569
|
+
blocks[blocks.length - 1] = { type: "thought", text: last.text + action.text };
|
|
2570
|
+
} else {
|
|
2571
|
+
blocks.push({ type: "thought", text: action.text });
|
|
2572
|
+
}
|
|
2573
|
+
return { ...msg, blocks };
|
|
2574
|
+
})
|
|
2575
|
+
};
|
|
2576
|
+
}
|
|
2694
2577
|
case "TOOL_EXECUTING": {
|
|
2695
2578
|
return {
|
|
2696
2579
|
...state,
|
|
@@ -2836,14 +2719,8 @@ function tuiReducer(state, action) {
|
|
|
2836
2719
|
return { ...state, error: action.error, isRunning: false };
|
|
2837
2720
|
case "CLEAR_ERROR":
|
|
2838
2721
|
return { ...state, error: void 0 };
|
|
2839
|
-
case "CODER_START":
|
|
2840
|
-
return { ...state, coderWorking: true };
|
|
2841
|
-
case "CODER_END":
|
|
2842
|
-
return { ...state, coderWorking: false };
|
|
2843
2722
|
case "CLEAR_MESSAGES":
|
|
2844
2723
|
return { ...state, messages: [] };
|
|
2845
|
-
case "SET_MODE":
|
|
2846
|
-
return { ...state, agentMode: action.agentMode, collaboration: action.collaboration };
|
|
2847
2724
|
case "SET_TODO_PLAN":
|
|
2848
2725
|
return { ...state, todoPlan: action.plan };
|
|
2849
2726
|
case "SET_SUB_AGENT_PROGRESS":
|
|
@@ -2866,136 +2743,90 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
2866
2743
|
function getToolParamSummary(name, params) {
|
|
2867
2744
|
switch (name) {
|
|
2868
2745
|
case "bash":
|
|
2869
|
-
return String(params["command"] || "").slice(0,
|
|
2746
|
+
return String(params["command"] || "").slice(0, 100);
|
|
2870
2747
|
case "read-file":
|
|
2871
2748
|
case "write-file":
|
|
2872
2749
|
case "edit-file":
|
|
2873
2750
|
return String(params["path"] || "");
|
|
2874
2751
|
case "glob":
|
|
2875
|
-
return String(params["pattern"] || "");
|
|
2876
2752
|
case "grep":
|
|
2877
2753
|
return String(params["pattern"] || "");
|
|
2878
|
-
case "send-to-coder":
|
|
2879
|
-
return String(params["task"] || "").slice(0, 40);
|
|
2880
2754
|
case "spawn-agents": {
|
|
2881
2755
|
const tasks = params["tasks"];
|
|
2882
|
-
if (!tasks) return "";
|
|
2883
|
-
|
|
2884
|
-
return tasks.map((t) => t.description.slice(0, 30)).join(", ");
|
|
2885
|
-
}
|
|
2886
|
-
return `${tasks.length} \u4E2A\u5E76\u884C\u4EFB\u52A1`;
|
|
2887
|
-
}
|
|
2888
|
-
case "todo": {
|
|
2889
|
-
const action = String(params["action"] || "");
|
|
2890
|
-
const id = params["id"] ? ` [${params["id"]}]` : "";
|
|
2891
|
-
return `${action}${id}`;
|
|
2892
|
-
}
|
|
2893
|
-
case "memo": {
|
|
2894
|
-
const action = String(params["action"] || "");
|
|
2895
|
-
const key = params["key"] ? ` [${params["key"]}]` : "";
|
|
2896
|
-
return `${action}${key}`;
|
|
2756
|
+
if (!tasks || tasks.length === 0) return "";
|
|
2757
|
+
return `${tasks.length} tasks: ${tasks.map((t) => t.description || "?").join(" | ").slice(0, 100)}`;
|
|
2897
2758
|
}
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
if (keys.length > 0 && keys[0]) {
|
|
2901
|
-
return String(params[keys[0]] || "").slice(0, 40);
|
|
2902
|
-
}
|
|
2903
|
-
return "";
|
|
2904
|
-
}
|
|
2905
|
-
}
|
|
2906
|
-
}
|
|
2907
|
-
function getToolIcon(name) {
|
|
2908
|
-
switch (name) {
|
|
2909
|
-
case "bash":
|
|
2910
|
-
return "$";
|
|
2911
|
-
case "write-file":
|
|
2912
|
-
return "+";
|
|
2913
|
-
case "edit-file":
|
|
2914
|
-
return "\xB1";
|
|
2915
|
-
case "read-file":
|
|
2916
|
-
return "\u{1F4C4}";
|
|
2917
|
-
case "glob":
|
|
2918
|
-
return "\u{1F50D}";
|
|
2919
|
-
case "grep":
|
|
2920
|
-
return "\u{1F50D}";
|
|
2921
|
-
case "spawn-agents":
|
|
2922
|
-
return "\u26A1";
|
|
2923
|
-
case "todo":
|
|
2924
|
-
return "\u{1F4CB}";
|
|
2925
|
-
case "memo":
|
|
2926
|
-
return "\u{1F4DD}";
|
|
2759
|
+
case "dispatch":
|
|
2760
|
+
return `${params["agent"] || "?"}: ${String(params["task"] || "").slice(0, 80)}`;
|
|
2927
2761
|
default:
|
|
2928
|
-
|
|
2762
|
+
const keys = Object.keys(params);
|
|
2763
|
+
if (keys.length === 0) return "";
|
|
2764
|
+
const val = params[keys[0]];
|
|
2765
|
+
if (val === null || val === void 0) return "";
|
|
2766
|
+
if (typeof val === "string") return val.slice(0, 60);
|
|
2767
|
+
if (typeof val === "number" || typeof val === "boolean") return String(val);
|
|
2768
|
+
return JSON.stringify(val).slice(0, 60);
|
|
2929
2769
|
}
|
|
2930
2770
|
}
|
|
2931
|
-
function
|
|
2932
|
-
|
|
2933
|
-
|
|
2771
|
+
function truncateContent(text, maxLines) {
|
|
2772
|
+
const lines = text.split("\n");
|
|
2773
|
+
const result = [];
|
|
2774
|
+
for (let line of lines) {
|
|
2775
|
+
if (result.length >= maxLines) break;
|
|
2776
|
+
result.push(line.replace(/\r/g, "").replace(/\t/g, " "));
|
|
2934
2777
|
}
|
|
2935
|
-
if (
|
|
2936
|
-
|
|
2778
|
+
if (lines.length > maxLines) {
|
|
2779
|
+
result.push(`... (and ${lines.length - maxLines} more lines)`);
|
|
2937
2780
|
}
|
|
2938
|
-
return
|
|
2939
|
-
}
|
|
2940
|
-
function truncateCode(code, maxLines) {
|
|
2941
|
-
const lines = code.split("\n");
|
|
2942
|
-
if (lines.length <= maxLines) return code;
|
|
2943
|
-
return lines.slice(0, maxLines).join("\n") + `
|
|
2944
|
-
... (\u5171 ${lines.length} \u884C)`;
|
|
2781
|
+
return result;
|
|
2945
2782
|
}
|
|
2946
2783
|
function ToolCallLine({ toolCall }) {
|
|
2947
|
-
const { name, params, status,
|
|
2784
|
+
const { name, params, status, resultContent, denyFeedback } = toolCall;
|
|
2948
2785
|
const summary = getToolParamSummary(name, params);
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
let
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
statusText = "[y/N]";
|
|
2969
|
-
break;
|
|
2786
|
+
let borderColor = "#504945";
|
|
2787
|
+
let titleColor = "#fabd2f";
|
|
2788
|
+
let statusColor = "#fabd2f";
|
|
2789
|
+
let statusText = "RUNNING";
|
|
2790
|
+
if (status === "done") {
|
|
2791
|
+
borderColor = "#b8bb26";
|
|
2792
|
+
titleColor = "#b8bb26";
|
|
2793
|
+
statusColor = "#b8bb26";
|
|
2794
|
+
statusText = "DONE";
|
|
2795
|
+
} else if (status === "denied") {
|
|
2796
|
+
borderColor = "#fb4934";
|
|
2797
|
+
titleColor = "#fb4934";
|
|
2798
|
+
statusColor = "#fb4934";
|
|
2799
|
+
statusText = "DENIED";
|
|
2800
|
+
} else if (status === "confirming") {
|
|
2801
|
+
borderColor = "#fe8019";
|
|
2802
|
+
titleColor = "#fe8019";
|
|
2803
|
+
statusColor = "#fe8019";
|
|
2804
|
+
statusText = "CONFIRM";
|
|
2970
2805
|
}
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
marginTop: 0,
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
/* @__PURE__ */ jsx(Text, { color: "red", children: "\u53CD\u9988:" }),
|
|
2996
|
-
/* @__PURE__ */ jsx(Text, { children: denyFeedback })
|
|
2997
|
-
] })
|
|
2998
|
-
] });
|
|
2806
|
+
const contentLines = resultContent ? truncateContent(resultContent, 15) : [];
|
|
2807
|
+
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: 0, marginBottom: 1, width: "100%", children: /* @__PURE__ */ jsxs(
|
|
2808
|
+
Box,
|
|
2809
|
+
{
|
|
2810
|
+
flexDirection: "column",
|
|
2811
|
+
borderStyle: "round",
|
|
2812
|
+
borderColor,
|
|
2813
|
+
paddingX: 1,
|
|
2814
|
+
width: "100%",
|
|
2815
|
+
children: [
|
|
2816
|
+
/* @__PURE__ */ jsxs(Box, { gap: 1, children: [
|
|
2817
|
+
/* @__PURE__ */ jsx(Box, { backgroundColor: statusColor, width: 9, justifyContent: "center", children: /* @__PURE__ */ jsx(Text, { color: "#282828", bold: true, children: statusText }) }),
|
|
2818
|
+
/* @__PURE__ */ jsx(Text, { color: titleColor, bold: true, children: name.toUpperCase() }),
|
|
2819
|
+
/* @__PURE__ */ jsx(Text, { color: "#ebdbb2", dimColor: true, italic: true, wrap: "truncate-end", children: summary })
|
|
2820
|
+
] }),
|
|
2821
|
+
status === "done" && contentLines.length > 0 && /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: 1, children: contentLines.map((line, i) => /* @__PURE__ */ jsx(Text, { color: "#ebdbb2", wrap: "truncate-end", children: line }, i)) }),
|
|
2822
|
+
status === "denied" && denyFeedback && /* @__PURE__ */ jsxs(Box, { gap: 1, marginTop: 0, children: [
|
|
2823
|
+
/* @__PURE__ */ jsx(Text, { color: "#fb4934", bold: true, children: "REASON:" }),
|
|
2824
|
+
/* @__PURE__ */ jsx(Text, { color: "#ebdbb2", children: denyFeedback })
|
|
2825
|
+
] }),
|
|
2826
|
+
status === "confirming" && /* @__PURE__ */ jsx(Box, { marginTop: 0, children: /* @__PURE__ */ jsx(Text, { color: "#fe8019", italic: true, children: "Waiting for your permission..." }) })
|
|
2827
|
+
]
|
|
2828
|
+
}
|
|
2829
|
+
) });
|
|
2999
2830
|
}
|
|
3000
2831
|
var init_ToolCallLine = __esm({
|
|
3001
2832
|
"src/cli/tui/components/ToolCallLine.tsx"() {
|
|
@@ -3014,209 +2845,94 @@ var init_MessageBubble = __esm({
|
|
|
3014
2845
|
init_ToolCallLine();
|
|
3015
2846
|
MessageBubble = React.memo(function MessageBubble2({ message }) {
|
|
3016
2847
|
const { role, blocks, isStreaming } = message;
|
|
3017
|
-
const
|
|
3018
|
-
const
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
2848
|
+
const isUser = role === "user";
|
|
2849
|
+
const label = isUser ? "USER" : "AI";
|
|
2850
|
+
const labelBg = isUser ? "#b8bb26" : "#83a598";
|
|
2851
|
+
const labelFg = "#282828";
|
|
2852
|
+
const contentColor = isUser ? "#ebdbb2" : "#ebdbb2";
|
|
2853
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: 1, width: "100%", children: [
|
|
2854
|
+
/* @__PURE__ */ jsxs2(Box2, { marginBottom: 0, children: [
|
|
2855
|
+
/* @__PURE__ */ jsx2(Box2, { backgroundColor: labelBg, width: 9, paddingLeft: 1, marginRight: 1, children: /* @__PURE__ */ jsx2(Text2, { color: labelFg, bold: true, children: label }) }),
|
|
2856
|
+
isStreaming && /* @__PURE__ */ jsx2(Text2, { color: "#83a598", dimColor: true, italic: true, children: "typing..." })
|
|
2857
|
+
] }),
|
|
2858
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: "100%", children: [
|
|
2859
|
+
blocks.map((block, i) => {
|
|
2860
|
+
if (block.type === "text") {
|
|
2861
|
+
const text = block.text.trim();
|
|
2862
|
+
if (!text && !isStreaming) return null;
|
|
2863
|
+
return /* @__PURE__ */ jsx2(Box2, { paddingLeft: 2, marginBottom: i < blocks.length - 1 ? 1 : 0, width: "100%", children: /* @__PURE__ */ jsx2(Text2, { color: contentColor, children: text }) }, `text-${i}`);
|
|
2864
|
+
} else if (block.type === "thought") {
|
|
2865
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: 0, width: "100%", children: [
|
|
2866
|
+
/* @__PURE__ */ jsx2(Box2, { gap: 1, marginBottom: 0, children: /* @__PURE__ */ jsx2(Box2, { backgroundColor: "#504945", width: 9, justifyContent: "center", children: /* @__PURE__ */ jsx2(Text2, { color: "#a89984", bold: true, children: "THINK" }) }) }),
|
|
2867
|
+
/* @__PURE__ */ jsx2(Box2, { paddingLeft: 2, marginBottom: 0, children: /* @__PURE__ */ jsx2(Text2, { color: "#928374", italic: true, children: block.text.trim() }) })
|
|
2868
|
+
] }, `thought-${i}`);
|
|
2869
|
+
} else {
|
|
2870
|
+
return /* @__PURE__ */ jsx2(Box2, { paddingLeft: 2, width: "100%", children: /* @__PURE__ */ jsx2(ToolCallLine, { toolCall: block.toolCall }) }, block.toolCall.id);
|
|
2871
|
+
}
|
|
2872
|
+
}),
|
|
2873
|
+
blocks.length === 0 && isStreaming && /* @__PURE__ */ jsx2(Box2, { paddingLeft: 2, children: /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "..." }) })
|
|
3039
2874
|
] })
|
|
3040
|
-
] });
|
|
3041
|
-
});
|
|
3042
|
-
}
|
|
3043
|
-
});
|
|
3044
|
-
|
|
3045
|
-
// src/cli/tui/components/ChatArea.tsx
|
|
3046
|
-
import React2, { useRef } from "react";
|
|
3047
|
-
import { Static, Box as Box3, Text as Text3 } from "ink";
|
|
3048
|
-
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
3049
|
-
function splitMessages(messages) {
|
|
3050
|
-
let staticEnd = 0;
|
|
3051
|
-
for (let i = 0; i < messages.length; i++) {
|
|
3052
|
-
if (messages[i].isStreaming || messages[i].confirmPending) break;
|
|
3053
|
-
staticEnd = i + 1;
|
|
3054
|
-
}
|
|
3055
|
-
const dynamicMsgs = messages.slice(staticEnd);
|
|
3056
|
-
const streamingMsg = dynamicMsgs.find((m) => m.isStreaming) || null;
|
|
3057
|
-
const otherDynamic = dynamicMsgs.filter((m) => m !== streamingMsg);
|
|
3058
|
-
return {
|
|
3059
|
-
staticMsgs: messages.slice(0, staticEnd),
|
|
3060
|
-
streamingMsg,
|
|
3061
|
-
otherDynamic
|
|
3062
|
-
};
|
|
3063
|
-
}
|
|
3064
|
-
function extractFromBlocks(msg, staticItems, dynamicNodes, isStreaming) {
|
|
3065
|
-
let lineIdx = 0;
|
|
3066
|
-
for (let bi = 0; bi < msg.blocks.length; bi++) {
|
|
3067
|
-
const block = msg.blocks[bi];
|
|
3068
|
-
const isLastBlock = bi === msg.blocks.length - 1;
|
|
3069
|
-
if (block.type === "text") {
|
|
3070
|
-
const lines = block.text.split("\n");
|
|
3071
|
-
if (isStreaming && isLastBlock) {
|
|
3072
|
-
const partial = lines.pop() || "";
|
|
3073
|
-
for (const line of lines) {
|
|
3074
|
-
staticItems.push({
|
|
3075
|
-
id: `${msg.id}-L${lineIdx}`,
|
|
3076
|
-
type: "line",
|
|
3077
|
-
text: line,
|
|
3078
|
-
isFirstLine: lineIdx === 0
|
|
3079
|
-
});
|
|
3080
|
-
lineIdx++;
|
|
3081
|
-
}
|
|
3082
|
-
if (partial || lineIdx === 0) {
|
|
3083
|
-
dynamicNodes.push(
|
|
3084
|
-
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
3085
|
-
/* @__PURE__ */ jsx3(Text3, { color: "cyan", bold: true, children: lineIdx === 0 ? "\u25C6 " : " " }),
|
|
3086
|
-
/* @__PURE__ */ jsx3(Box3, { flexGrow: 1, children: /* @__PURE__ */ jsx3(Text3, { children: partial }) })
|
|
3087
|
-
] }, "partial")
|
|
3088
|
-
);
|
|
3089
|
-
}
|
|
3090
|
-
} else {
|
|
3091
|
-
for (const line of lines) {
|
|
3092
|
-
staticItems.push({
|
|
3093
|
-
id: `${msg.id}-L${lineIdx}`,
|
|
3094
|
-
type: "line",
|
|
3095
|
-
text: line,
|
|
3096
|
-
isFirstLine: lineIdx === 0
|
|
3097
|
-
});
|
|
3098
|
-
lineIdx++;
|
|
3099
|
-
}
|
|
3100
|
-
}
|
|
3101
|
-
} else if (block.type === "tool") {
|
|
3102
|
-
const tc = block.toolCall;
|
|
3103
|
-
const isWriteTool = tc.name === "write-file" || tc.name === "edit-file";
|
|
3104
|
-
staticItems.push({
|
|
3105
|
-
id: `${msg.id}-TH${tc.id}`,
|
|
3106
|
-
type: "tool-header",
|
|
3107
|
-
toolCall: tc
|
|
3108
|
-
});
|
|
3109
|
-
if (isWriteTool && tc.status === "running" && isStreaming) {
|
|
3110
|
-
const lineCount = parseInt(tc.streamingContent || "0", 10);
|
|
3111
|
-
dynamicNodes.push(
|
|
3112
|
-
/* @__PURE__ */ jsx3(Box3, { paddingX: 1, marginLeft: 4, children: /* @__PURE__ */ jsxs3(Text3, { color: "cyan", children: [
|
|
3113
|
-
"\u270E \u751F\u6210\u4E2D...",
|
|
3114
|
-
lineCount > 0 ? ` ${lineCount} \u884C` : ""
|
|
3115
|
-
] }) }, `tool-progress-${tc.id}`)
|
|
3116
|
-
);
|
|
3117
|
-
}
|
|
3118
|
-
if (tc.status === "done" || tc.status === "denied") {
|
|
3119
|
-
staticItems.push({
|
|
3120
|
-
id: `${msg.id}-TD${tc.id}`,
|
|
3121
|
-
type: "tool-done",
|
|
3122
|
-
toolCall: tc
|
|
3123
|
-
});
|
|
3124
|
-
}
|
|
3125
|
-
}
|
|
3126
|
-
}
|
|
3127
|
-
}
|
|
3128
|
-
function renderStaticItem(item) {
|
|
3129
|
-
if (item.type === "message") {
|
|
3130
|
-
return /* @__PURE__ */ jsx3(Box3, { paddingX: 1, children: /* @__PURE__ */ jsx3(MessageBubble, { message: item.message }) }, item.id);
|
|
3131
|
-
}
|
|
3132
|
-
if (item.type === "line") {
|
|
3133
|
-
return /* @__PURE__ */ jsxs3(Box3, { paddingX: 1, children: [
|
|
3134
|
-
/* @__PURE__ */ jsx3(Text3, { color: "cyan", bold: true, children: item.isFirstLine ? "\u25C6 " : " " }),
|
|
3135
|
-
/* @__PURE__ */ jsx3(Text3, { children: item.text })
|
|
3136
|
-
] }, item.id);
|
|
3137
|
-
}
|
|
3138
|
-
if (item.type === "tool-header") {
|
|
3139
|
-
const tc = item.toolCall;
|
|
3140
|
-
const icon = getToolIcon(tc.name);
|
|
3141
|
-
const summary = getToolParamSummary(tc.name, tc.params);
|
|
3142
|
-
return /* @__PURE__ */ jsx3(Box3, { paddingX: 1, marginLeft: 2, children: /* @__PURE__ */ jsxs3(Box3, { gap: 1, children: [
|
|
3143
|
-
/* @__PURE__ */ jsx3(Text3, { color: "yellow", children: "\u23F3" }),
|
|
3144
|
-
/* @__PURE__ */ jsxs3(Text3, { color: "yellow", bold: true, children: [
|
|
3145
|
-
icon,
|
|
3146
|
-
" ",
|
|
3147
|
-
tc.name
|
|
3148
|
-
] }),
|
|
3149
|
-
summary ? /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: summary }) : null
|
|
3150
|
-
] }) }, item.id);
|
|
3151
|
-
}
|
|
3152
|
-
if (item.type === "tool-done") {
|
|
3153
|
-
return /* @__PURE__ */ jsx3(Box3, { paddingX: 1, marginLeft: 0, children: /* @__PURE__ */ jsx3(ToolCallLine, { toolCall: item.toolCall }) }, item.id);
|
|
2875
|
+
] });
|
|
2876
|
+
});
|
|
3154
2877
|
}
|
|
3155
|
-
|
|
3156
|
-
|
|
2878
|
+
});
|
|
2879
|
+
|
|
2880
|
+
// src/cli/tui/components/ChatArea.tsx
|
|
2881
|
+
import React2 from "react";
|
|
2882
|
+
import { Box as Box3 } from "ink";
|
|
2883
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
3157
2884
|
var ChatArea;
|
|
3158
2885
|
var init_ChatArea = __esm({
|
|
3159
2886
|
"src/cli/tui/components/ChatArea.tsx"() {
|
|
3160
2887
|
"use strict";
|
|
3161
2888
|
init_MessageBubble();
|
|
3162
|
-
init_ToolCallLine();
|
|
3163
2889
|
ChatArea = React2.memo(function ChatArea2({ messages }) {
|
|
3164
|
-
|
|
3165
|
-
const streamedIds = useRef(/* @__PURE__ */ new Set());
|
|
3166
|
-
if (streamingMsg) {
|
|
3167
|
-
streamedIds.current.add(streamingMsg.id);
|
|
3168
|
-
}
|
|
3169
|
-
const currentItems = [];
|
|
3170
|
-
for (const msg of staticMsgs) {
|
|
3171
|
-
if (streamedIds.current.has(msg.id)) {
|
|
3172
|
-
const ignore = [];
|
|
3173
|
-
extractFromBlocks(msg, currentItems, ignore, false);
|
|
3174
|
-
} else {
|
|
3175
|
-
currentItems.push({ id: msg.id, type: "message", message: msg });
|
|
3176
|
-
}
|
|
3177
|
-
}
|
|
3178
|
-
const dynamicNodes = [];
|
|
3179
|
-
if (streamingMsg) {
|
|
3180
|
-
extractFromBlocks(streamingMsg, currentItems, dynamicNodes, true);
|
|
3181
|
-
}
|
|
3182
|
-
const seenIds = useRef(/* @__PURE__ */ new Set());
|
|
3183
|
-
const accumulated = useRef([]);
|
|
3184
|
-
let hasNew = false;
|
|
3185
|
-
for (const item of currentItems) {
|
|
3186
|
-
if (!seenIds.current.has(item.id)) {
|
|
3187
|
-
seenIds.current.add(item.id);
|
|
3188
|
-
accumulated.current.push(item);
|
|
3189
|
-
hasNew = true;
|
|
3190
|
-
}
|
|
3191
|
-
}
|
|
3192
|
-
if (hasNew) {
|
|
3193
|
-
accumulated.current = [...accumulated.current];
|
|
3194
|
-
}
|
|
3195
|
-
const staticItems = accumulated.current;
|
|
3196
|
-
const showPlaceholder = streamingMsg && streamingMsg.blocks.length === 0;
|
|
3197
|
-
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
3198
|
-
staticItems.length > 0 && /* @__PURE__ */ jsx3(Static, { items: staticItems, children: renderStaticItem }),
|
|
3199
|
-
(dynamicNodes.length > 0 || showPlaceholder) && /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", paddingX: 1, children: [
|
|
3200
|
-
showPlaceholder && /* @__PURE__ */ jsxs3(Box3, { children: [
|
|
3201
|
-
/* @__PURE__ */ jsx3(Text3, { color: "cyan", bold: true, children: "\u25C6 " }),
|
|
3202
|
-
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "..." })
|
|
3203
|
-
] }),
|
|
3204
|
-
dynamicNodes
|
|
3205
|
-
] }),
|
|
3206
|
-
otherDynamic.map((msg) => /* @__PURE__ */ jsx3(Box3, { paddingX: 1, children: /* @__PURE__ */ jsx3(MessageBubble, { message: msg }) }, msg.id))
|
|
3207
|
-
] });
|
|
2890
|
+
return /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", width: "100%", children: messages.map((msg) => /* @__PURE__ */ jsx3(MessageBubble, { message: msg }, msg.id)) });
|
|
3208
2891
|
});
|
|
3209
2892
|
}
|
|
3210
2893
|
});
|
|
3211
2894
|
|
|
3212
2895
|
// src/cli/tui/components/InputArea.tsx
|
|
3213
|
-
import { useState } from "react";
|
|
3214
|
-
import { Box as Box4, Text as
|
|
3215
|
-
import { Fragment, jsx as jsx4, jsxs as
|
|
3216
|
-
function CleanTextInput({
|
|
2896
|
+
import { useRef, useState } from "react";
|
|
2897
|
+
import { Box as Box4, Text as Text3, useInput } from "ink";
|
|
2898
|
+
import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
2899
|
+
function CleanTextInput({
|
|
2900
|
+
onSubmit,
|
|
2901
|
+
placeholder,
|
|
2902
|
+
onExitRequest,
|
|
2903
|
+
onScroll
|
|
2904
|
+
}) {
|
|
3217
2905
|
const [value, setValue] = useState("");
|
|
3218
2906
|
const [cursor, setCursor] = useState(0);
|
|
2907
|
+
const lastCtrlCAtRef = useRef(0);
|
|
3219
2908
|
useInput((input, key) => {
|
|
2909
|
+
if (onScroll) {
|
|
2910
|
+
if (key.pageUp || key.upArrow && key.shift) {
|
|
2911
|
+
onScroll("up");
|
|
2912
|
+
return;
|
|
2913
|
+
}
|
|
2914
|
+
if (key.pageDown || key.downArrow && key.shift) {
|
|
2915
|
+
onScroll("down");
|
|
2916
|
+
return;
|
|
2917
|
+
}
|
|
2918
|
+
}
|
|
2919
|
+
if (input === "c" && key.ctrl) {
|
|
2920
|
+
if (value.length > 0) {
|
|
2921
|
+
setValue("");
|
|
2922
|
+
setCursor(0);
|
|
2923
|
+
lastCtrlCAtRef.current = 0;
|
|
2924
|
+
return;
|
|
2925
|
+
}
|
|
2926
|
+
const now = Date.now();
|
|
2927
|
+
const isDoublePress = now - lastCtrlCAtRef.current <= 1200;
|
|
2928
|
+
if (isDoublePress) {
|
|
2929
|
+
onExitRequest();
|
|
2930
|
+
lastCtrlCAtRef.current = 0;
|
|
2931
|
+
} else {
|
|
2932
|
+
lastCtrlCAtRef.current = now;
|
|
2933
|
+
}
|
|
2934
|
+
return;
|
|
2935
|
+
}
|
|
3220
2936
|
if (key.return) {
|
|
3221
2937
|
const trimmed = value.trim();
|
|
3222
2938
|
if (trimmed) {
|
|
@@ -3269,30 +2985,33 @@ function CleanTextInput({ onSubmit, placeholder }) {
|
|
|
3269
2985
|
setCursor((prev) => prev + input.length);
|
|
3270
2986
|
});
|
|
3271
2987
|
if (value.length === 0) {
|
|
3272
|
-
return /* @__PURE__ */
|
|
3273
|
-
/* @__PURE__ */ jsx4(
|
|
3274
|
-
/* @__PURE__ */ jsx4(
|
|
2988
|
+
return /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
2989
|
+
/* @__PURE__ */ jsx4(Text3, { inverse: true, children: " " }),
|
|
2990
|
+
/* @__PURE__ */ jsx4(Text3, { dimColor: true, children: placeholder || "" })
|
|
3275
2991
|
] });
|
|
3276
2992
|
}
|
|
3277
2993
|
const before = value.slice(0, cursor);
|
|
3278
2994
|
const at = cursor < value.length ? value[cursor] : " ";
|
|
3279
2995
|
const after = cursor < value.length ? value.slice(cursor + 1) : "";
|
|
3280
|
-
return /* @__PURE__ */
|
|
3281
|
-
/* @__PURE__ */ jsx4(
|
|
3282
|
-
/* @__PURE__ */ jsx4(
|
|
3283
|
-
/* @__PURE__ */ jsx4(
|
|
2996
|
+
return /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
2997
|
+
/* @__PURE__ */ jsx4(Text3, { children: before }),
|
|
2998
|
+
/* @__PURE__ */ jsx4(Text3, { inverse: true, children: at }),
|
|
2999
|
+
/* @__PURE__ */ jsx4(Text3, { children: after })
|
|
3284
3000
|
] });
|
|
3285
3001
|
}
|
|
3286
|
-
function InputArea({ onSubmit, isRunning }) {
|
|
3287
|
-
return /* @__PURE__ */
|
|
3288
|
-
/* @__PURE__ */ jsx4(
|
|
3289
|
-
|
|
3002
|
+
function InputArea({ onSubmit, isRunning, onExitRequest, onScroll }) {
|
|
3003
|
+
return /* @__PURE__ */ jsxs3(Box4, { paddingX: 0, children: [
|
|
3004
|
+
/* @__PURE__ */ jsx4(Box4, { backgroundColor: isRunning ? "#504945" : "#b8bb26", paddingX: 1, marginRight: 1, children: /* @__PURE__ */ jsx4(Text3, { color: "#282828", bold: true, children: isRunning ? " WAIT " : " INPUT " }) }),
|
|
3005
|
+
/* @__PURE__ */ jsx4(Box4, { flexGrow: 1, children: isRunning ? /* @__PURE__ */ jsx4(Box4, { flexGrow: 1, onWheel: (event) => {
|
|
3006
|
+
}, children: /* @__PURE__ */ jsx4(Text3, { color: "#a89984", italic: true, children: "Thinking..." }) }) : /* @__PURE__ */ jsx4(
|
|
3290
3007
|
CleanTextInput,
|
|
3291
3008
|
{
|
|
3292
3009
|
onSubmit,
|
|
3293
|
-
placeholder: "
|
|
3010
|
+
placeholder: "Type a message or /command...",
|
|
3011
|
+
onExitRequest,
|
|
3012
|
+
onScroll
|
|
3294
3013
|
}
|
|
3295
|
-
)
|
|
3014
|
+
) })
|
|
3296
3015
|
] });
|
|
3297
3016
|
}
|
|
3298
3017
|
var init_InputArea = __esm({
|
|
@@ -3302,43 +3021,46 @@ var init_InputArea = __esm({
|
|
|
3302
3021
|
});
|
|
3303
3022
|
|
|
3304
3023
|
// src/cli/tui/components/StatusBar.tsx
|
|
3305
|
-
import { Box as Box5, Text as
|
|
3306
|
-
import { Fragment as Fragment2, jsx as jsx5, jsxs as
|
|
3307
|
-
function
|
|
3308
|
-
|
|
3024
|
+
import { Box as Box5, Text as Text4 } from "ink";
|
|
3025
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
3026
|
+
function formatTokens(n) {
|
|
3027
|
+
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
3028
|
+
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
|
|
3029
|
+
return String(n);
|
|
3030
|
+
}
|
|
3031
|
+
function StatusBar({ isRunning, modelName, todoPlan, subAgentProgress }) {
|
|
3309
3032
|
const todoProgress = todoPlan ? `${todoPlan.items.filter((i) => i.status === "completed").length}/${todoPlan.items.length}` : null;
|
|
3310
|
-
return /* @__PURE__ */
|
|
3311
|
-
/* @__PURE__ */ jsx5(
|
|
3312
|
-
/* @__PURE__ */
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
"\
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
] })
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3033
|
+
return /* @__PURE__ */ jsxs4(Box5, { marginTop: 1, children: [
|
|
3034
|
+
/* @__PURE__ */ jsx5(Box5, { backgroundColor: "#3c3836", paddingX: 1, children: /* @__PURE__ */ jsx5(Text4, { color: "#ebdbb2", bold: true, children: " ZENCODE " }) }),
|
|
3035
|
+
/* @__PURE__ */ jsxs4(Box5, { backgroundColor: "#504945", paddingX: 1, flexGrow: 1, children: [
|
|
3036
|
+
/* @__PURE__ */ jsx5(Text4, { color: "#ebdbb2", children: modelName }),
|
|
3037
|
+
isRunning && !subAgentProgress && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
3038
|
+
/* @__PURE__ */ jsx5(Text4, { color: "#ebdbb2", children: " \u2502 " }),
|
|
3039
|
+
/* @__PURE__ */ jsx5(Text4, { color: "#8ec07c", children: "\u25CF thinking..." })
|
|
3040
|
+
] }),
|
|
3041
|
+
subAgentProgress && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
3042
|
+
/* @__PURE__ */ jsx5(Text4, { color: "#ebdbb2", children: " \u2502 " }),
|
|
3043
|
+
/* @__PURE__ */ jsxs4(Text4, { color: "#b8bb26", children: [
|
|
3044
|
+
"Agents: ",
|
|
3045
|
+
subAgentProgress.completed + subAgentProgress.failed,
|
|
3046
|
+
"/",
|
|
3047
|
+
subAgentProgress.total
|
|
3048
|
+
] }),
|
|
3049
|
+
/* @__PURE__ */ jsx5(Text4, { color: "#ebdbb2", children: " \u2502 " }),
|
|
3050
|
+
/* @__PURE__ */ jsxs4(Text4, { color: "#83a598", children: [
|
|
3051
|
+
"tokens: ",
|
|
3052
|
+
formatTokens(subAgentProgress.tokens)
|
|
3053
|
+
] })
|
|
3054
|
+
] }),
|
|
3055
|
+
todoProgress && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
3056
|
+
/* @__PURE__ */ jsx5(Text4, { color: "#ebdbb2", children: " \u2502 " }),
|
|
3057
|
+
/* @__PURE__ */ jsxs4(Text4, { color: "#fabd2f", children: [
|
|
3058
|
+
"Plan: ",
|
|
3059
|
+
todoProgress
|
|
3060
|
+
] })
|
|
3338
3061
|
] })
|
|
3339
3062
|
] }),
|
|
3340
|
-
/* @__PURE__ */ jsx5(
|
|
3341
|
-
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "/help" })
|
|
3063
|
+
/* @__PURE__ */ jsx5(Box5, { backgroundColor: "#3c3836", paddingX: 1, children: /* @__PURE__ */ jsx5(Text4, { color: "#ebdbb2", children: "/help" }) })
|
|
3342
3064
|
] });
|
|
3343
3065
|
}
|
|
3344
3066
|
var init_StatusBar = __esm({
|
|
@@ -3349,8 +3071,8 @@ var init_StatusBar = __esm({
|
|
|
3349
3071
|
|
|
3350
3072
|
// src/cli/tui/components/ConfirmPrompt.tsx
|
|
3351
3073
|
import { useState as useState2 } from "react";
|
|
3352
|
-
import { Box as Box6, Text as
|
|
3353
|
-
import { Fragment as Fragment3, jsx as jsx6, jsxs as
|
|
3074
|
+
import { Box as Box6, Text as Text5, useInput as useInput2 } from "ink";
|
|
3075
|
+
import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
3354
3076
|
function getToolDetails(toolName, params) {
|
|
3355
3077
|
const lines = [];
|
|
3356
3078
|
let label = toolName;
|
|
@@ -3436,18 +3158,18 @@ function FeedbackInput({ onSubmit }) {
|
|
|
3436
3158
|
setCursor((prev) => prev + input.length);
|
|
3437
3159
|
});
|
|
3438
3160
|
if (value.length === 0) {
|
|
3439
|
-
return /* @__PURE__ */
|
|
3440
|
-
/* @__PURE__ */ jsx6(
|
|
3441
|
-
/* @__PURE__ */ jsx6(
|
|
3161
|
+
return /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
3162
|
+
/* @__PURE__ */ jsx6(Text5, { inverse: true, children: " " }),
|
|
3163
|
+
/* @__PURE__ */ jsx6(Text5, { dimColor: true, children: "\u8F93\u5165\u53CD\u9988\u6307\u4EE4\u7ED9 AI..." })
|
|
3442
3164
|
] });
|
|
3443
3165
|
}
|
|
3444
3166
|
const before = value.slice(0, cursor);
|
|
3445
3167
|
const at = cursor < value.length ? value[cursor] : " ";
|
|
3446
3168
|
const after = cursor < value.length ? value.slice(cursor + 1) : "";
|
|
3447
|
-
return /* @__PURE__ */
|
|
3448
|
-
/* @__PURE__ */ jsx6(
|
|
3449
|
-
/* @__PURE__ */ jsx6(
|
|
3450
|
-
/* @__PURE__ */ jsx6(
|
|
3169
|
+
return /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
3170
|
+
/* @__PURE__ */ jsx6(Text5, { children: before }),
|
|
3171
|
+
/* @__PURE__ */ jsx6(Text5, { inverse: true, children: at }),
|
|
3172
|
+
/* @__PURE__ */ jsx6(Text5, { children: after })
|
|
3451
3173
|
] });
|
|
3452
3174
|
}
|
|
3453
3175
|
function ConfirmPrompt({ confirm, onRespond }) {
|
|
@@ -3475,66 +3197,69 @@ function ConfirmPrompt({ confirm, onRespond }) {
|
|
|
3475
3197
|
}, { isActive: !feedbackMode });
|
|
3476
3198
|
const { lines, label } = getToolDetails(confirm.toolName, confirm.params);
|
|
3477
3199
|
if (feedbackMode) {
|
|
3478
|
-
return /* @__PURE__ */
|
|
3479
|
-
/* @__PURE__ */
|
|
3200
|
+
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", paddingX: 0, marginY: 1, children: [
|
|
3201
|
+
/* @__PURE__ */ jsx6(Box6, { backgroundColor: "#fe8019", paddingX: 1, children: /* @__PURE__ */ jsx6(Text5, { color: "#282828", bold: true, children: " FEEDBACK " }) }),
|
|
3202
|
+
/* @__PURE__ */ jsxs5(
|
|
3480
3203
|
Box6,
|
|
3481
3204
|
{
|
|
3482
3205
|
flexDirection: "column",
|
|
3483
3206
|
borderStyle: "round",
|
|
3484
|
-
borderColor: "
|
|
3207
|
+
borderColor: "#fe8019",
|
|
3485
3208
|
paddingX: 1,
|
|
3486
3209
|
children: [
|
|
3487
|
-
/* @__PURE__ */ jsx6(
|
|
3488
|
-
lines.map((line, i) => /* @__PURE__ */ jsx6(
|
|
3210
|
+
/* @__PURE__ */ jsx6(Text5, { bold: true, color: "#fabd2f", children: label }),
|
|
3211
|
+
lines.map((line, i) => /* @__PURE__ */ jsx6(Text5, { color: "#a89984", children: line }, i)),
|
|
3212
|
+
/* @__PURE__ */ jsxs5(Box6, { marginTop: 1, gap: 1, children: [
|
|
3213
|
+
/* @__PURE__ */ jsx6(Text5, { color: "#83a598", bold: true, children: "Reason: " }),
|
|
3214
|
+
/* @__PURE__ */ jsx6(
|
|
3215
|
+
FeedbackInput,
|
|
3216
|
+
{
|
|
3217
|
+
onSubmit: (text) => {
|
|
3218
|
+
const trimmed = text.trim();
|
|
3219
|
+
if (trimmed) {
|
|
3220
|
+
onRespond({ feedback: trimmed });
|
|
3221
|
+
} else {
|
|
3222
|
+
setFeedbackMode(false);
|
|
3223
|
+
}
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
)
|
|
3227
|
+
] })
|
|
3489
3228
|
]
|
|
3490
3229
|
}
|
|
3491
3230
|
),
|
|
3492
|
-
/* @__PURE__ */
|
|
3493
|
-
/* @__PURE__ */ jsx6(Text6, { color: "cyan", children: "\u53CD\u9988: " }),
|
|
3494
|
-
/* @__PURE__ */ jsx6(
|
|
3495
|
-
FeedbackInput,
|
|
3496
|
-
{
|
|
3497
|
-
onSubmit: (text) => {
|
|
3498
|
-
const trimmed = text.trim();
|
|
3499
|
-
if (trimmed) {
|
|
3500
|
-
onRespond({ feedback: trimmed });
|
|
3501
|
-
} else {
|
|
3502
|
-
setFeedbackMode(false);
|
|
3503
|
-
}
|
|
3504
|
-
}
|
|
3505
|
-
}
|
|
3506
|
-
)
|
|
3507
|
-
] }),
|
|
3508
|
-
/* @__PURE__ */ jsx6(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Enter \u53D1\u9001 Esc \u8FD4\u56DE\u9009\u62E9" }) })
|
|
3231
|
+
/* @__PURE__ */ jsx6(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx6(Text5, { dimColor: true, children: "Enter to send \xB7 Esc to cancel" }) })
|
|
3509
3232
|
] });
|
|
3510
3233
|
}
|
|
3511
|
-
return /* @__PURE__ */
|
|
3512
|
-
/* @__PURE__ */
|
|
3234
|
+
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", paddingX: 0, marginY: 1, children: [
|
|
3235
|
+
/* @__PURE__ */ jsx6(Box6, { backgroundColor: "#fe8019", paddingX: 1, children: /* @__PURE__ */ jsx6(Text5, { color: "#282828", bold: true, children: " CONFIRMATION REQUIRED " }) }),
|
|
3236
|
+
/* @__PURE__ */ jsxs5(
|
|
3513
3237
|
Box6,
|
|
3514
3238
|
{
|
|
3515
3239
|
flexDirection: "column",
|
|
3516
3240
|
borderStyle: "round",
|
|
3517
|
-
borderColor: "
|
|
3241
|
+
borderColor: "#fe8019",
|
|
3518
3242
|
paddingX: 1,
|
|
3519
3243
|
children: [
|
|
3520
|
-
/* @__PURE__ */ jsx6(
|
|
3521
|
-
lines.map((line, i) => /* @__PURE__ */ jsx6(
|
|
3244
|
+
/* @__PURE__ */ jsx6(Text5, { bold: true, color: "#fabd2f", children: label }),
|
|
3245
|
+
lines.map((line, i) => /* @__PURE__ */ jsx6(Text5, { color: "#a89984", children: line }, i)),
|
|
3246
|
+
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, gap: 2, justifyContent: "center", children: OPTIONS.map((opt, i) => {
|
|
3247
|
+
const isSelected = i === selected;
|
|
3248
|
+
const optColor = opt.key === "deny" ? "#fb4934" : opt.key === "allow" ? "#b8bb26" : "#8ec07c";
|
|
3249
|
+
return /* @__PURE__ */ jsx6(Box6, { children: isSelected ? /* @__PURE__ */ jsxs5(Text5, { bold: true, backgroundColor: optColor, color: "#282828", children: [
|
|
3250
|
+
" ",
|
|
3251
|
+
opt.label,
|
|
3252
|
+
" "
|
|
3253
|
+
] }) : /* @__PURE__ */ jsxs5(Text5, { color: optColor, children: [
|
|
3254
|
+
" ",
|
|
3255
|
+
opt.label,
|
|
3256
|
+
" "
|
|
3257
|
+
] }) }, opt.key);
|
|
3258
|
+
}) })
|
|
3522
3259
|
]
|
|
3523
3260
|
}
|
|
3524
3261
|
),
|
|
3525
|
-
/* @__PURE__ */ jsx6(Box6, {
|
|
3526
|
-
const isSelected = i === selected;
|
|
3527
|
-
return /* @__PURE__ */ jsx6(Box6, { children: isSelected ? /* @__PURE__ */ jsxs6(Text6, { bold: true, color: "cyan", inverse: true, children: [
|
|
3528
|
-
" ",
|
|
3529
|
-
opt.label,
|
|
3530
|
-
" "
|
|
3531
|
-
] }) : /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
3532
|
-
" ",
|
|
3533
|
-
opt.label,
|
|
3534
|
-
" "
|
|
3535
|
-
] }) }, opt.key);
|
|
3536
|
-
}) }),
|
|
3537
|
-
/* @__PURE__ */ jsx6(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "\u2190 \u2192 \u9009\u62E9 Enter \u786E\u8BA4 y \u5141\u8BB8 n \u62D2\u7EDD a \u59CB\u7EC8 Tab \u53CD\u9988" }) })
|
|
3262
|
+
/* @__PURE__ */ jsx6(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx6(Text5, { dimColor: true, children: "\u2190 \u2192 select \xB7 Enter confirm \xB7 y/n shortcuts \xB7 Tab feedback" }) })
|
|
3538
3263
|
] });
|
|
3539
3264
|
}
|
|
3540
3265
|
var OPTIONS;
|
|
@@ -3550,55 +3275,57 @@ var init_ConfirmPrompt = __esm({
|
|
|
3550
3275
|
});
|
|
3551
3276
|
|
|
3552
3277
|
// src/cli/tui/components/TodoPanel.tsx
|
|
3553
|
-
import { Box as Box7, Text as
|
|
3554
|
-
import { jsx as jsx7, jsxs as
|
|
3278
|
+
import { Box as Box7, Text as Text6 } from "ink";
|
|
3279
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
3555
3280
|
function getStatusIcon(status) {
|
|
3556
3281
|
switch (status) {
|
|
3557
3282
|
case "completed":
|
|
3558
|
-
return "\
|
|
3283
|
+
return "\u2705";
|
|
3559
3284
|
case "in-progress":
|
|
3560
|
-
return "\
|
|
3285
|
+
return "\u23F3";
|
|
3561
3286
|
default:
|
|
3562
|
-
return "\
|
|
3287
|
+
return "\u26AA";
|
|
3563
3288
|
}
|
|
3564
3289
|
}
|
|
3565
3290
|
function getStatusColor(status) {
|
|
3566
3291
|
switch (status) {
|
|
3567
3292
|
case "completed":
|
|
3568
|
-
return "
|
|
3293
|
+
return "#b8bb26";
|
|
3569
3294
|
case "in-progress":
|
|
3570
|
-
return "
|
|
3295
|
+
return "#fabd2f";
|
|
3571
3296
|
default:
|
|
3572
|
-
return "
|
|
3297
|
+
return "#928374";
|
|
3573
3298
|
}
|
|
3574
3299
|
}
|
|
3575
3300
|
function TodoPanel({ plan }) {
|
|
3576
3301
|
const completed = plan.items.filter((i) => i.status === "completed").length;
|
|
3577
3302
|
const total = plan.items.length;
|
|
3578
|
-
return /* @__PURE__ */
|
|
3303
|
+
return /* @__PURE__ */ jsxs6(
|
|
3579
3304
|
Box7,
|
|
3580
3305
|
{
|
|
3581
3306
|
flexDirection: "column",
|
|
3582
3307
|
borderStyle: "round",
|
|
3583
|
-
borderColor: "
|
|
3308
|
+
borderColor: "#83a598",
|
|
3584
3309
|
paddingX: 1,
|
|
3585
|
-
|
|
3310
|
+
marginY: 1,
|
|
3586
3311
|
children: [
|
|
3587
|
-
/* @__PURE__ */
|
|
3588
|
-
/* @__PURE__ */ jsx7(
|
|
3589
|
-
/* @__PURE__ */
|
|
3312
|
+
/* @__PURE__ */ jsxs6(Box7, { justifyContent: "space-between", marginBottom: 1, children: [
|
|
3313
|
+
/* @__PURE__ */ jsx7(Text6, { bold: true, color: "#83a598", children: "\u{1F4CB} PROJECT PLAN" }),
|
|
3314
|
+
/* @__PURE__ */ jsxs6(Text6, { color: "#ebdbb2", children: [
|
|
3590
3315
|
completed,
|
|
3591
3316
|
"/",
|
|
3592
|
-
total
|
|
3317
|
+
total,
|
|
3318
|
+
" tasks"
|
|
3593
3319
|
] })
|
|
3594
3320
|
] }),
|
|
3595
|
-
plan.items.map((item) => /* @__PURE__ */
|
|
3596
|
-
/* @__PURE__ */ jsx7(
|
|
3321
|
+
plan.items.map((item) => /* @__PURE__ */ jsxs6(Box7, { gap: 1, children: [
|
|
3322
|
+
/* @__PURE__ */ jsx7(Text6, { color: getStatusColor(item.status), children: getStatusIcon(item.status) }),
|
|
3597
3323
|
/* @__PURE__ */ jsx7(
|
|
3598
|
-
|
|
3324
|
+
Text6,
|
|
3599
3325
|
{
|
|
3600
|
-
color: item.status === "completed" ? "
|
|
3326
|
+
color: item.status === "completed" ? "#b8bb26" : item.status === "in-progress" ? "#fabd2f" : "#ebdbb2",
|
|
3601
3327
|
dimColor: item.status === "pending",
|
|
3328
|
+
strikethrough: item.status === "completed",
|
|
3602
3329
|
children: item.title
|
|
3603
3330
|
}
|
|
3604
3331
|
)
|
|
@@ -3613,26 +3340,62 @@ var init_TodoPanel = __esm({
|
|
|
3613
3340
|
}
|
|
3614
3341
|
});
|
|
3615
3342
|
|
|
3343
|
+
// src/cli/tui/components/Header.tsx
|
|
3344
|
+
import { Box as Box8, Text as Text7 } from "ink";
|
|
3345
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
3346
|
+
function Header({ modelName }) {
|
|
3347
|
+
return /* @__PURE__ */ jsx8(
|
|
3348
|
+
Box8,
|
|
3349
|
+
{
|
|
3350
|
+
flexDirection: "column",
|
|
3351
|
+
width: "100%",
|
|
3352
|
+
borderStyle: "round",
|
|
3353
|
+
borderColor: "#504945",
|
|
3354
|
+
paddingX: 1,
|
|
3355
|
+
marginTop: 0,
|
|
3356
|
+
marginBottom: 1,
|
|
3357
|
+
children: /* @__PURE__ */ jsxs7(Box8, { justifyContent: "space-between", children: [
|
|
3358
|
+
/* @__PURE__ */ jsxs7(Box8, { gap: 1, children: [
|
|
3359
|
+
/* @__PURE__ */ jsx8(Text7, { bold: true, color: "#fe8019", children: "ZEN CODE" }),
|
|
3360
|
+
/* @__PURE__ */ jsx8(Text7, { color: "#a89984", dimColor: true, children: "v0.2.1" })
|
|
3361
|
+
] }),
|
|
3362
|
+
/* @__PURE__ */ jsx8(Text7, { color: "#83a598", bold: true, children: modelName })
|
|
3363
|
+
] })
|
|
3364
|
+
}
|
|
3365
|
+
);
|
|
3366
|
+
}
|
|
3367
|
+
var init_Header = __esm({
|
|
3368
|
+
"src/cli/tui/components/Header.tsx"() {
|
|
3369
|
+
"use strict";
|
|
3370
|
+
}
|
|
3371
|
+
});
|
|
3372
|
+
|
|
3616
3373
|
// src/cli/tui/App.tsx
|
|
3617
3374
|
import { useReducer, useCallback, useEffect, useRef as useRef2, useState as useState3 } from "react";
|
|
3618
|
-
import { Box as
|
|
3619
|
-
import { jsx as
|
|
3620
|
-
function App({ config, client, agent,
|
|
3375
|
+
import { Box as Box9, Text as Text8, useInput as useInput3, useStdout } from "ink";
|
|
3376
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
3377
|
+
function App({ config, client, agent, registry, todoStore, subAgentTracker, agentRegistry, skillRegistry }) {
|
|
3378
|
+
const { stdout } = useStdout();
|
|
3379
|
+
const [, setWidth] = useState3(stdout.columns);
|
|
3380
|
+
useEffect(() => {
|
|
3381
|
+
const onResize = () => {
|
|
3382
|
+
setWidth(stdout.columns);
|
|
3383
|
+
};
|
|
3384
|
+
stdout.on("resize", onResize);
|
|
3385
|
+
return () => {
|
|
3386
|
+
stdout.off("resize", onResize);
|
|
3387
|
+
};
|
|
3388
|
+
}, [stdout]);
|
|
3621
3389
|
const [state, dispatch] = useReducer(
|
|
3622
3390
|
tuiReducer,
|
|
3623
|
-
createInitialState(
|
|
3624
|
-
config.model,
|
|
3625
|
-
config.agent_mode,
|
|
3626
|
-
config.collaboration
|
|
3627
|
-
)
|
|
3391
|
+
createInitialState(config.model)
|
|
3628
3392
|
);
|
|
3629
3393
|
const [resetKey, setResetKey] = useState3(0);
|
|
3394
|
+
const currentCallbacksRef = useRef2(null);
|
|
3630
3395
|
const agentRef = useRef2(agent);
|
|
3631
|
-
const orchestratorRef = useRef2(orchestrator);
|
|
3632
3396
|
const todoStoreRef = useRef2(todoStore);
|
|
3633
3397
|
const subAgentTrackerRef = useRef2(subAgentTracker);
|
|
3634
3398
|
agentRef.current = agent;
|
|
3635
|
-
orchestratorRef.current = orchestrator;
|
|
3636
3399
|
todoStoreRef.current = todoStore;
|
|
3637
3400
|
subAgentTrackerRef.current = subAgentTracker;
|
|
3638
3401
|
useEffect(() => {
|
|
@@ -3672,7 +3435,7 @@ function App({ config, client, agent, orchestrator, registry, todoStore, memoSto
|
|
|
3672
3435
|
);
|
|
3673
3436
|
useEffect(() => {
|
|
3674
3437
|
setStructuredConfirmHandler((toolName, params) => {
|
|
3675
|
-
return new Promise((
|
|
3438
|
+
return new Promise((resolve10) => {
|
|
3676
3439
|
const id = `confirm-${Date.now()}`;
|
|
3677
3440
|
registerConfirmToolId(toolName, id);
|
|
3678
3441
|
dispatch({
|
|
@@ -3683,13 +3446,13 @@ function App({ config, client, agent, orchestrator, registry, todoStore, memoSto
|
|
|
3683
3446
|
resolve: (result) => {
|
|
3684
3447
|
if (result === "always") {
|
|
3685
3448
|
registry.addAutoApprove(toolName);
|
|
3686
|
-
|
|
3449
|
+
resolve10({ approved: true });
|
|
3687
3450
|
} else if (result === "allow") {
|
|
3688
|
-
|
|
3451
|
+
resolve10({ approved: true });
|
|
3689
3452
|
} else if (result === "deny") {
|
|
3690
|
-
|
|
3453
|
+
resolve10({ approved: false });
|
|
3691
3454
|
} else {
|
|
3692
|
-
|
|
3455
|
+
resolve10({ approved: false, feedback: result.feedback });
|
|
3693
3456
|
}
|
|
3694
3457
|
}
|
|
3695
3458
|
});
|
|
@@ -3699,39 +3462,53 @@ function App({ config, client, agent, orchestrator, registry, todoStore, memoSto
|
|
|
3699
3462
|
setStructuredConfirmHandler(null);
|
|
3700
3463
|
};
|
|
3701
3464
|
}, [registry]);
|
|
3465
|
+
const runAgent = useCallback(async (text) => {
|
|
3466
|
+
dispatch({ type: "ADD_USER_MESSAGE", text });
|
|
3467
|
+
dispatch({ type: "START_ASSISTANT" });
|
|
3468
|
+
dispatch({ type: "SET_RUNNING", running: true });
|
|
3469
|
+
const callbacks = createBridgeCallbacks(dispatch);
|
|
3470
|
+
currentCallbacksRef.current = callbacks;
|
|
3471
|
+
try {
|
|
3472
|
+
await agentRef.current.run(text, callbacks);
|
|
3473
|
+
} catch (err) {
|
|
3474
|
+
if (!isAbortError(err)) {
|
|
3475
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3476
|
+
dispatch({ type: "SET_ERROR", error: msg });
|
|
3477
|
+
}
|
|
3478
|
+
} finally {
|
|
3479
|
+
currentCallbacksRef.current = null;
|
|
3480
|
+
}
|
|
3481
|
+
callbacks._stopBatcher?.();
|
|
3482
|
+
dispatch({ type: "FINISH_STREAMING" });
|
|
3483
|
+
dispatch({ type: "SET_RUNNING", running: false });
|
|
3484
|
+
}, []);
|
|
3702
3485
|
const handleSubmit = useCallback(async (text) => {
|
|
3703
3486
|
if (text.startsWith("/")) {
|
|
3487
|
+
const parts = text.slice(1).split(/\s+/);
|
|
3488
|
+
const skillName = parts[0];
|
|
3489
|
+
const skillArgs = parts.slice(1).join(" ");
|
|
3490
|
+
const skill = skillRegistry.get(skillName);
|
|
3491
|
+
if (skill) {
|
|
3492
|
+
const expandedPrompt = skillRegistry.expandPrompt(skill, skillArgs);
|
|
3493
|
+
await runAgent(expandedPrompt);
|
|
3494
|
+
return;
|
|
3495
|
+
}
|
|
3704
3496
|
handleSlashCommand2(text, {
|
|
3705
3497
|
config,
|
|
3706
|
-
|
|
3498
|
+
agent: agentRef.current,
|
|
3707
3499
|
registry,
|
|
3708
3500
|
dispatch,
|
|
3709
3501
|
setResetKey,
|
|
3710
3502
|
client,
|
|
3711
3503
|
todoStore,
|
|
3712
|
-
|
|
3713
|
-
|
|
3504
|
+
subAgentTracker,
|
|
3505
|
+
agentRegistry,
|
|
3506
|
+
skillRegistry
|
|
3714
3507
|
});
|
|
3715
3508
|
return;
|
|
3716
3509
|
}
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
dispatch({ type: "SET_RUNNING", running: true });
|
|
3720
|
-
const callbacks = createBridgeCallbacks(dispatch);
|
|
3721
|
-
try {
|
|
3722
|
-
if (config.agent_mode === "single") {
|
|
3723
|
-
await agentRef.current.run(text, callbacks);
|
|
3724
|
-
} else {
|
|
3725
|
-
await orchestratorRef.current.run(text, callbacks);
|
|
3726
|
-
}
|
|
3727
|
-
} catch (err) {
|
|
3728
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
3729
|
-
dispatch({ type: "SET_ERROR", error: msg });
|
|
3730
|
-
}
|
|
3731
|
-
callbacks._stopBatcher?.();
|
|
3732
|
-
dispatch({ type: "FINISH_STREAMING" });
|
|
3733
|
-
dispatch({ type: "SET_RUNNING", running: false });
|
|
3734
|
-
}, [config]);
|
|
3510
|
+
await runAgent(text);
|
|
3511
|
+
}, [config, runAgent]);
|
|
3735
3512
|
const handleConfirmResponse = useCallback((result) => {
|
|
3736
3513
|
if (confirmPending) {
|
|
3737
3514
|
confirmPending.resolve(result);
|
|
@@ -3749,114 +3526,118 @@ function App({ config, client, agent, orchestrator, registry, todoStore, memoSto
|
|
|
3749
3526
|
}
|
|
3750
3527
|
}, [confirmPending, state.messages]);
|
|
3751
3528
|
useInput3((input, key) => {
|
|
3752
|
-
if (
|
|
3529
|
+
if (key.escape) {
|
|
3753
3530
|
if (state.isRunning) {
|
|
3531
|
+
agentRef.current.interrupt();
|
|
3532
|
+
currentCallbacksRef.current?._stopBatcher?.();
|
|
3754
3533
|
dispatch({ type: "SET_RUNNING", running: false });
|
|
3755
3534
|
dispatch({ type: "FINISH_STREAMING" });
|
|
3756
|
-
} else {
|
|
3757
|
-
process.exit(0);
|
|
3758
3535
|
}
|
|
3759
3536
|
return;
|
|
3760
3537
|
}
|
|
3538
|
+
if (input === "c" && key.ctrl) {
|
|
3539
|
+
if (state.isRunning) {
|
|
3540
|
+
agentRef.current.interrupt();
|
|
3541
|
+
currentCallbacksRef.current?._stopBatcher?.();
|
|
3542
|
+
dispatch({ type: "SET_RUNNING", running: false });
|
|
3543
|
+
dispatch({ type: "FINISH_STREAMING" });
|
|
3544
|
+
return;
|
|
3545
|
+
}
|
|
3546
|
+
}
|
|
3761
3547
|
if (input === "d" && key.ctrl) {
|
|
3762
3548
|
process.exit(0);
|
|
3763
3549
|
}
|
|
3764
3550
|
});
|
|
3765
|
-
return /* @__PURE__ */ jsxs8(
|
|
3766
|
-
/* @__PURE__ */
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
]
|
|
3778
|
-
}
|
|
3779
|
-
),
|
|
3780
|
-
confirmPending && /* @__PURE__ */ jsx8(
|
|
3781
|
-
ConfirmPrompt,
|
|
3782
|
-
{
|
|
3783
|
-
confirm: confirmPending,
|
|
3784
|
-
onRespond: handleConfirmResponse
|
|
3785
|
-
}
|
|
3786
|
-
),
|
|
3787
|
-
state.todoPlan && /* @__PURE__ */ jsx8(TodoPanel, { plan: state.todoPlan }),
|
|
3788
|
-
/* @__PURE__ */ jsx8(
|
|
3551
|
+
return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", paddingX: 0, width: "100%", children: [
|
|
3552
|
+
/* @__PURE__ */ jsxs8(Box9, { paddingX: 1, flexDirection: "column", children: [
|
|
3553
|
+
/* @__PURE__ */ jsx9(Header, { modelName: state.modelName }),
|
|
3554
|
+
/* @__PURE__ */ jsx9(ChatArea, { messages: state.messages })
|
|
3555
|
+
] }),
|
|
3556
|
+
state.error && /* @__PURE__ */ jsxs8(Box9, { borderStyle: "round", borderColor: "#fb4934", paddingX: 1, marginBottom: 1, children: [
|
|
3557
|
+
/* @__PURE__ */ jsx9(Text8, { color: "#fb4934", bold: true, children: "ERROR: " }),
|
|
3558
|
+
/* @__PURE__ */ jsx9(Text8, { color: "#fb4934", children: state.error })
|
|
3559
|
+
] }),
|
|
3560
|
+
confirmPending && /* @__PURE__ */ jsx9(ConfirmPrompt, { confirm: confirmPending, onRespond: handleConfirmResponse }),
|
|
3561
|
+
state.todoPlan && /* @__PURE__ */ jsx9(TodoPanel, { plan: state.todoPlan }),
|
|
3562
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(
|
|
3789
3563
|
InputArea,
|
|
3790
3564
|
{
|
|
3791
3565
|
onSubmit: handleSubmit,
|
|
3792
|
-
isRunning: state.isRunning || !!confirmPending
|
|
3566
|
+
isRunning: state.isRunning || !!confirmPending,
|
|
3567
|
+
onExitRequest: () => process.exit(0)
|
|
3793
3568
|
}
|
|
3794
|
-
),
|
|
3795
|
-
/* @__PURE__ */
|
|
3569
|
+
) }),
|
|
3570
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 0, children: /* @__PURE__ */ jsx9(
|
|
3796
3571
|
StatusBar,
|
|
3797
3572
|
{
|
|
3798
|
-
agentMode: state.agentMode,
|
|
3799
|
-
collaboration: state.collaboration,
|
|
3800
|
-
coderWorking: state.coderWorking,
|
|
3801
3573
|
isRunning: state.isRunning,
|
|
3802
3574
|
modelName: state.modelName,
|
|
3803
3575
|
todoPlan: state.todoPlan,
|
|
3804
3576
|
subAgentProgress: state.subAgentProgress
|
|
3805
3577
|
}
|
|
3806
|
-
)
|
|
3578
|
+
) })
|
|
3807
3579
|
] }, resetKey);
|
|
3808
3580
|
}
|
|
3809
3581
|
function handleSlashCommand2(input, ctx) {
|
|
3810
|
-
const { config,
|
|
3582
|
+
const { config, agent, registry, dispatch, setResetKey, client, todoStore, subAgentTracker, agentRegistry, skillRegistry } = ctx;
|
|
3811
3583
|
const parts = input.trim().split(/\s+/);
|
|
3812
3584
|
const command = parts[0];
|
|
3813
3585
|
switch (command) {
|
|
3814
3586
|
case "/help":
|
|
3815
3587
|
dispatch({ type: "ADD_USER_MESSAGE", text: input });
|
|
3816
3588
|
dispatch({ type: "START_ASSISTANT" });
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
text: `\u53EF\u7528\u547D\u4EE4:
|
|
3589
|
+
{
|
|
3590
|
+
let helpText = `\u53EF\u7528\u547D\u4EE4:
|
|
3820
3591
|
/help \u663E\u793A\u6B64\u5E2E\u52A9\u4FE1\u606F
|
|
3821
|
-
/
|
|
3822
|
-
/
|
|
3823
|
-
/dual \u5207\u6362\u5230\u53CC Agent \u6A21\u5F0F
|
|
3592
|
+
/skills \u5217\u51FA\u6240\u6709\u53EF\u7528\u6280\u80FD
|
|
3593
|
+
/agents \u5217\u51FA\u6240\u6709\u53EF\u7528\u5B50 Agent
|
|
3824
3594
|
/parallel \u5207\u6362\u5E76\u884C\u5B50 Agent \u529F\u80FD on/off
|
|
3825
3595
|
/todo \u5207\u6362 todo \u8BA1\u5212\u529F\u80FD on/off
|
|
3826
3596
|
/clear \u6E05\u7A7A\u5BF9\u8BDD\u5386\u53F2
|
|
3827
3597
|
/info \u663E\u793A\u5F53\u524D\u914D\u7F6E
|
|
3828
3598
|
Ctrl+C \u53D6\u6D88\u5F53\u524D\u8BF7\u6C42 / \u9000\u51FA
|
|
3829
|
-
Ctrl+D \u9000\u51FA
|
|
3830
|
-
|
|
3599
|
+
Ctrl+D \u9000\u51FA`;
|
|
3600
|
+
const skills = skillRegistry.list();
|
|
3601
|
+
if (skills.length > 0) {
|
|
3602
|
+
helpText += `
|
|
3603
|
+
|
|
3604
|
+
\u53EF\u7528\u6280\u80FD:
|
|
3605
|
+
${skills.map((s) => ` /${s.name} ${s.description}`).join("\n")}`;
|
|
3606
|
+
}
|
|
3607
|
+
dispatch({ type: "APPEND_CONTENT", text: helpText });
|
|
3608
|
+
}
|
|
3831
3609
|
dispatch({ type: "FINISH_STREAMING" });
|
|
3832
3610
|
break;
|
|
3833
|
-
case "/
|
|
3834
|
-
const
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
dispatch({
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
dispatch({ type: "FINISH_STREAMING" });
|
|
3844
|
-
} else if (["delegated", "autonomous", "controlled"].includes(mode)) {
|
|
3845
|
-
config.collaboration = mode;
|
|
3846
|
-
orchestrator?.setMode(mode);
|
|
3847
|
-
dispatch({ type: "SET_MODE", agentMode: config.agent_mode, collaboration: mode });
|
|
3611
|
+
case "/skills": {
|
|
3612
|
+
const skills = skillRegistry.list();
|
|
3613
|
+
dispatch({ type: "ADD_USER_MESSAGE", text: input });
|
|
3614
|
+
dispatch({ type: "START_ASSISTANT" });
|
|
3615
|
+
if (skills.length === 0) {
|
|
3616
|
+
dispatch({ type: "APPEND_CONTENT", text: "\u6682\u65E0\u53EF\u7528\u6280\u80FD\u3002\u5728 ~/.zencode/skills/ \u6216 .zencode/skills/ \u653E\u7F6E YAML \u6587\u4EF6\u6DFB\u52A0\u6280\u80FD\u3002" });
|
|
3617
|
+
} else {
|
|
3618
|
+
const lines = skills.map((s) => ` /${s.name}: ${s.description}`);
|
|
3619
|
+
dispatch({ type: "APPEND_CONTENT", text: `\u53EF\u7528\u6280\u80FD (${skills.length}):
|
|
3620
|
+
${lines.join("\n")}` });
|
|
3848
3621
|
}
|
|
3622
|
+
dispatch({ type: "FINISH_STREAMING" });
|
|
3849
3623
|
break;
|
|
3850
3624
|
}
|
|
3851
|
-
case "/
|
|
3852
|
-
|
|
3853
|
-
dispatch({ type: "
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3625
|
+
case "/agents": {
|
|
3626
|
+
const agents = agentRegistry.list();
|
|
3627
|
+
dispatch({ type: "ADD_USER_MESSAGE", text: input });
|
|
3628
|
+
dispatch({ type: "START_ASSISTANT" });
|
|
3629
|
+
if (agents.length === 0) {
|
|
3630
|
+
dispatch({ type: "APPEND_CONTENT", text: "\u6682\u65E0\u53EF\u7528\u5B50 Agent\u3002" });
|
|
3631
|
+
} else {
|
|
3632
|
+
const lines = agents.map((a) => ` ${a.name}: ${a.description} [tools: ${a.tools.join(", ")}]`);
|
|
3633
|
+
dispatch({ type: "APPEND_CONTENT", text: `\u53EF\u7528\u5B50 Agent (${agents.length}):
|
|
3634
|
+
${lines.join("\n")}` });
|
|
3635
|
+
}
|
|
3636
|
+
dispatch({ type: "FINISH_STREAMING" });
|
|
3858
3637
|
break;
|
|
3638
|
+
}
|
|
3859
3639
|
case "/clear":
|
|
3640
|
+
agent.getConversation().clear();
|
|
3860
3641
|
dispatch({ type: "CLEAR_MESSAGES" });
|
|
3861
3642
|
setResetKey((prev) => prev + 1);
|
|
3862
3643
|
break;
|
|
@@ -3867,7 +3648,7 @@ function handleSlashCommand2(input, ctx) {
|
|
|
3867
3648
|
if (next === "off") {
|
|
3868
3649
|
registry.unregister("spawn-agents");
|
|
3869
3650
|
} else if (!registry.has("spawn-agents")) {
|
|
3870
|
-
registry.register(createSpawnAgentsTool(client, registry, config, subAgentTracker
|
|
3651
|
+
registry.register(createSpawnAgentsTool(client, registry, config, subAgentTracker));
|
|
3871
3652
|
}
|
|
3872
3653
|
dispatch({ type: "ADD_USER_MESSAGE", text: input });
|
|
3873
3654
|
dispatch({ type: "START_ASSISTANT" });
|
|
@@ -3896,9 +3677,9 @@ function handleSlashCommand2(input, ctx) {
|
|
|
3896
3677
|
dispatch({
|
|
3897
3678
|
type: "APPEND_CONTENT",
|
|
3898
3679
|
text: `\u6A21\u578B: ${config.model}
|
|
3899
|
-
|
|
3900
|
-
\
|
|
3901
|
-
\
|
|
3680
|
+
\u57FA\u7840 URL: ${config.base_url}
|
|
3681
|
+
\u5B50 Agent: ${agentRegistry.listNames().join(", ") || "\u65E0"}
|
|
3682
|
+
\u6280\u80FD: ${skillRegistry.listNames().map((n) => "/" + n).join(", ") || "\u65E0"}`
|
|
3902
3683
|
});
|
|
3903
3684
|
dispatch({ type: "FINISH_STREAMING" });
|
|
3904
3685
|
break;
|
|
@@ -3916,6 +3697,7 @@ Agent \u6A21\u5F0F: ${config.agent_mode}
|
|
|
3916
3697
|
var init_App = __esm({
|
|
3917
3698
|
"src/cli/tui/App.tsx"() {
|
|
3918
3699
|
"use strict";
|
|
3700
|
+
init_client();
|
|
3919
3701
|
init_permission();
|
|
3920
3702
|
init_state();
|
|
3921
3703
|
init_bridge();
|
|
@@ -3926,6 +3708,7 @@ var init_App = __esm({
|
|
|
3926
3708
|
init_StatusBar();
|
|
3927
3709
|
init_ConfirmPrompt();
|
|
3928
3710
|
init_TodoPanel();
|
|
3711
|
+
init_Header();
|
|
3929
3712
|
}
|
|
3930
3713
|
});
|
|
3931
3714
|
|
|
@@ -3935,10 +3718,14 @@ __export(tui_exports, {
|
|
|
3935
3718
|
startTui: () => startTui
|
|
3936
3719
|
});
|
|
3937
3720
|
import { render } from "ink";
|
|
3938
|
-
import { jsx as
|
|
3721
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
3939
3722
|
async function startTui(options) {
|
|
3940
3723
|
const { config } = options;
|
|
3941
|
-
const
|
|
3724
|
+
const agentRegistry = new SubAgentConfigRegistry();
|
|
3725
|
+
for (const agentConfig of loadAllAgentConfigs()) {
|
|
3726
|
+
agentRegistry.register(agentConfig);
|
|
3727
|
+
}
|
|
3728
|
+
const { systemPrompt } = await buildPrompt(config, agentRegistry.list());
|
|
3942
3729
|
const registry = new ToolRegistry();
|
|
3943
3730
|
registerCoreTools(registry);
|
|
3944
3731
|
registry.setPermissions(config.permissions);
|
|
@@ -3950,32 +3737,37 @@ async function startTui(options) {
|
|
|
3950
3737
|
maxTokens: config.max_tokens
|
|
3951
3738
|
});
|
|
3952
3739
|
const todoStore = new TodoStore();
|
|
3953
|
-
const memoStore = new MemoStore();
|
|
3954
3740
|
const subAgentTracker = new SubAgentTracker();
|
|
3741
|
+
const skillRegistry = new SkillRegistry();
|
|
3742
|
+
for (const skill of loadAllSkills()) {
|
|
3743
|
+
skillRegistry.register(skill);
|
|
3744
|
+
}
|
|
3955
3745
|
if (config.features.parallel_agents === "on") {
|
|
3956
|
-
registry.register(createSpawnAgentsTool(client, registry, config, subAgentTracker
|
|
3746
|
+
registry.register(createSpawnAgentsTool(client, registry, config, subAgentTracker));
|
|
3957
3747
|
}
|
|
3958
3748
|
if (config.features.todo === "on") {
|
|
3959
3749
|
registry.register(createTodoTool(todoStore));
|
|
3960
3750
|
}
|
|
3961
|
-
registry.register(
|
|
3962
|
-
const agent = new Agent(client, registry, config, systemPrompt
|
|
3963
|
-
const orchestrator = new Orchestrator(registry, config, systemPrompt, memoStore);
|
|
3751
|
+
registry.register(createDispatchTool(client, registry, config, agentRegistry, subAgentTracker));
|
|
3752
|
+
const agent = new Agent(client, registry, config, systemPrompt);
|
|
3964
3753
|
const { waitUntilExit } = render(
|
|
3965
|
-
/* @__PURE__ */
|
|
3754
|
+
/* @__PURE__ */ jsx10(
|
|
3966
3755
|
App,
|
|
3967
3756
|
{
|
|
3968
3757
|
config,
|
|
3969
3758
|
client,
|
|
3970
3759
|
agent,
|
|
3971
|
-
orchestrator,
|
|
3972
3760
|
registry,
|
|
3973
3761
|
todoStore,
|
|
3974
|
-
|
|
3975
|
-
|
|
3762
|
+
subAgentTracker,
|
|
3763
|
+
agentRegistry,
|
|
3764
|
+
skillRegistry
|
|
3976
3765
|
}
|
|
3977
3766
|
),
|
|
3978
|
-
{
|
|
3767
|
+
{
|
|
3768
|
+
patchConsole: true,
|
|
3769
|
+
exitOnCtrlC: false
|
|
3770
|
+
}
|
|
3979
3771
|
);
|
|
3980
3772
|
await waitUntilExit();
|
|
3981
3773
|
}
|
|
@@ -3986,41 +3778,129 @@ var init_tui = __esm({
|
|
|
3986
3778
|
init_registry();
|
|
3987
3779
|
init_register();
|
|
3988
3780
|
init_agent();
|
|
3989
|
-
init_orchestrator();
|
|
3990
3781
|
init_builder();
|
|
3991
3782
|
init_todo_store();
|
|
3992
|
-
init_memo_store();
|
|
3993
3783
|
init_sub_agent_tracker();
|
|
3784
|
+
init_registry2();
|
|
3785
|
+
init_loader();
|
|
3786
|
+
init_registry3();
|
|
3787
|
+
init_loader2();
|
|
3994
3788
|
init_spawn_agents();
|
|
3995
3789
|
init_todo();
|
|
3996
|
-
|
|
3790
|
+
init_dispatch();
|
|
3997
3791
|
init_App();
|
|
3998
3792
|
}
|
|
3999
3793
|
});
|
|
4000
3794
|
|
|
4001
3795
|
// src/cli/index.ts
|
|
4002
|
-
|
|
3796
|
+
import { Command } from "commander";
|
|
3797
|
+
|
|
3798
|
+
// src/config/loader.ts
|
|
3799
|
+
import * as fs from "fs";
|
|
3800
|
+
import * as path from "path";
|
|
3801
|
+
import * as os from "os";
|
|
3802
|
+
import { parse as parseYaml } from "yaml";
|
|
3803
|
+
|
|
3804
|
+
// src/config/defaults.ts
|
|
3805
|
+
var DEFAULT_CONFIG = {
|
|
3806
|
+
model: "deepseek-chat",
|
|
3807
|
+
api_key: "",
|
|
3808
|
+
base_url: "https://api.deepseek.com/v1",
|
|
3809
|
+
temperature: 0.7,
|
|
3810
|
+
max_tokens: 8192,
|
|
3811
|
+
features: {
|
|
3812
|
+
git: "auto",
|
|
3813
|
+
mcp: "off",
|
|
3814
|
+
planning_layer: "on",
|
|
3815
|
+
parallel_agents: "on",
|
|
3816
|
+
todo: "on"
|
|
3817
|
+
},
|
|
3818
|
+
permissions: {
|
|
3819
|
+
auto_approve: ["read-file", "glob", "grep", "spawn-agents", "todo", "dispatch"],
|
|
3820
|
+
require_approval: ["write-file", "edit-file", "bash", "git"]
|
|
3821
|
+
},
|
|
3822
|
+
mcp_servers: [],
|
|
3823
|
+
prompts: [],
|
|
3824
|
+
max_tool_output: 3e4
|
|
3825
|
+
};
|
|
3826
|
+
|
|
3827
|
+
// src/config/loader.ts
|
|
3828
|
+
function deepMerge(target, source) {
|
|
3829
|
+
const result = { ...target };
|
|
3830
|
+
for (const key of Object.keys(source)) {
|
|
3831
|
+
const sourceVal = source[key];
|
|
3832
|
+
const targetVal = target[key];
|
|
3833
|
+
if (sourceVal !== void 0 && sourceVal !== null && typeof sourceVal === "object" && !Array.isArray(sourceVal) && typeof targetVal === "object" && !Array.isArray(targetVal) && targetVal !== null) {
|
|
3834
|
+
result[key] = deepMerge(targetVal, sourceVal);
|
|
3835
|
+
} else if (sourceVal !== void 0) {
|
|
3836
|
+
result[key] = sourceVal;
|
|
3837
|
+
}
|
|
3838
|
+
}
|
|
3839
|
+
return result;
|
|
3840
|
+
}
|
|
3841
|
+
function loadYamlFile(filePath) {
|
|
3842
|
+
try {
|
|
3843
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
3844
|
+
return parseYaml(content) || {};
|
|
3845
|
+
} catch {
|
|
3846
|
+
return {};
|
|
3847
|
+
}
|
|
3848
|
+
}
|
|
3849
|
+
function loadEnvConfig() {
|
|
3850
|
+
const config = {};
|
|
3851
|
+
if (process.env["ZENCODE_API_KEY"]) {
|
|
3852
|
+
config.api_key = process.env["ZENCODE_API_KEY"];
|
|
3853
|
+
}
|
|
3854
|
+
if (process.env["ZENCODE_MODEL"]) {
|
|
3855
|
+
config.model = process.env["ZENCODE_MODEL"];
|
|
3856
|
+
}
|
|
3857
|
+
if (process.env["ZENCODE_BASE_URL"]) {
|
|
3858
|
+
config.base_url = process.env["ZENCODE_BASE_URL"];
|
|
3859
|
+
}
|
|
3860
|
+
return config;
|
|
3861
|
+
}
|
|
3862
|
+
function loadCliConfig(opts) {
|
|
3863
|
+
const config = {};
|
|
3864
|
+
if (opts.model) config.model = opts.model;
|
|
3865
|
+
if (opts.apiKey) config.api_key = opts.apiKey;
|
|
3866
|
+
if (opts.baseUrl) config.base_url = opts.baseUrl;
|
|
3867
|
+
return config;
|
|
3868
|
+
}
|
|
3869
|
+
function loadConfig(cliOpts = {}) {
|
|
3870
|
+
const globalConfigPath = path.join(os.homedir(), ".zencode", "config.yaml");
|
|
3871
|
+
const projectDirConfigPath = path.resolve(".zencode", "config.yaml");
|
|
3872
|
+
const projectFileConfigPath = path.resolve(".zencode.yaml");
|
|
3873
|
+
let config = { ...DEFAULT_CONFIG };
|
|
3874
|
+
config = deepMerge(config, loadYamlFile(globalConfigPath));
|
|
3875
|
+
config = deepMerge(config, loadYamlFile(projectDirConfigPath));
|
|
3876
|
+
config = deepMerge(config, loadYamlFile(projectFileConfigPath));
|
|
3877
|
+
config = deepMerge(config, loadEnvConfig());
|
|
3878
|
+
config = deepMerge(config, loadCliConfig(cliOpts));
|
|
3879
|
+
return config;
|
|
3880
|
+
}
|
|
3881
|
+
|
|
3882
|
+
// src/cli/index.ts
|
|
4003
3883
|
init_client();
|
|
4004
3884
|
init_registry();
|
|
4005
3885
|
init_register();
|
|
4006
3886
|
init_agent();
|
|
4007
|
-
init_orchestrator();
|
|
4008
3887
|
init_builder();
|
|
4009
|
-
import { Command } from "commander";
|
|
4010
3888
|
|
|
4011
3889
|
// src/cli/repl.ts
|
|
4012
3890
|
init_client();
|
|
4013
3891
|
init_registry();
|
|
4014
3892
|
init_agent();
|
|
4015
|
-
init_orchestrator();
|
|
4016
3893
|
init_builder();
|
|
4017
3894
|
init_register();
|
|
4018
3895
|
init_permission();
|
|
4019
3896
|
init_todo_store();
|
|
4020
|
-
|
|
3897
|
+
init_registry2();
|
|
3898
|
+
init_loader();
|
|
3899
|
+
init_registry3();
|
|
3900
|
+
init_loader2();
|
|
4021
3901
|
init_spawn_agents();
|
|
4022
3902
|
init_todo();
|
|
4023
|
-
|
|
3903
|
+
init_dispatch();
|
|
4024
3904
|
init_bridge();
|
|
4025
3905
|
import * as readline from "readline";
|
|
4026
3906
|
|
|
@@ -4052,12 +3932,8 @@ function printToolCall(toolName, params) {
|
|
|
4052
3932
|
const action = String(params["action"]);
|
|
4053
3933
|
const id = params["id"] ? ` [${params["id"]}]` : "";
|
|
4054
3934
|
detail = ` ${chalk2.dim(`${action}${id}`)}`;
|
|
4055
|
-
} else if (toolName === "memo" && params["action"]) {
|
|
4056
|
-
const action = String(params["action"]);
|
|
4057
|
-
const key = params["key"] ? ` [${params["key"]}]` : "";
|
|
4058
|
-
detail = ` ${chalk2.dim(`${action}${key}`)}`;
|
|
4059
3935
|
}
|
|
4060
|
-
const icon = toolName === "spawn-agents" ? "\u26A1" : toolName === "todo" ? "\u{1F4CB}" :
|
|
3936
|
+
const icon = toolName === "spawn-agents" ? "\u26A1" : toolName === "todo" ? "\u{1F4CB}" : "\u2699";
|
|
4061
3937
|
console.log(chalk2.yellow(` ${icon} ${toolName}`) + detail);
|
|
4062
3938
|
}
|
|
4063
3939
|
function printToolResult(toolName, result, truncated) {
|
|
@@ -4080,10 +3956,9 @@ function printWarning(message) {
|
|
|
4080
3956
|
function printSuccess(message) {
|
|
4081
3957
|
console.log(chalk2.green(`\u2713 ${message}`));
|
|
4082
3958
|
}
|
|
4083
|
-
function printWelcome(modelName
|
|
3959
|
+
function printWelcome(modelName) {
|
|
4084
3960
|
console.log(chalk2.bold.cyan("\n ZenCode") + chalk2.dim(" - \u6781\u7B80 AI \u7F16\u7A0B\u52A9\u624B\n"));
|
|
4085
3961
|
console.log(chalk2.dim(` \u6A21\u578B: ${modelName}`));
|
|
4086
|
-
console.log(chalk2.dim(` \u6A21\u5F0F: ${mode}`));
|
|
4087
3962
|
console.log(chalk2.dim(` \u8F93\u5165 /help \u67E5\u770B\u547D\u4EE4\uFF0CCtrl+C \u9000\u51FA
|
|
4088
3963
|
`));
|
|
4089
3964
|
}
|
|
@@ -4097,9 +3972,8 @@ function handleSlashCommand(input, context) {
|
|
|
4097
3972
|
console.log(`
|
|
4098
3973
|
\u53EF\u7528\u547D\u4EE4:
|
|
4099
3974
|
/help \u663E\u793A\u6B64\u5E2E\u52A9\u4FE1\u606F
|
|
4100
|
-
/
|
|
4101
|
-
/
|
|
4102
|
-
/dual \u5207\u6362\u5230\u53CC Agent \u6A21\u5F0F
|
|
3975
|
+
/skills \u5217\u51FA\u6240\u6709\u53EF\u7528\u6280\u80FD\uFF08\u7528\u6237\u81EA\u5B9A\u4E49\u659C\u6760\u547D\u4EE4\uFF09
|
|
3976
|
+
/agents \u5217\u51FA\u6240\u6709\u53EF\u7528\u5B50 Agent
|
|
4103
3977
|
/parallel \u5207\u6362\u5E76\u884C\u5B50 Agent \u529F\u80FD on/off
|
|
4104
3978
|
/todo \u5207\u6362 todo \u8BA1\u5212\u529F\u80FD on/off
|
|
4105
3979
|
/clear \u6E05\u7A7A\u5BF9\u8BDD\u5386\u53F2
|
|
@@ -4107,33 +3981,33 @@ function handleSlashCommand(input, context) {
|
|
|
4107
3981
|
/exit \u9000\u51FA
|
|
4108
3982
|
`);
|
|
4109
3983
|
return true;
|
|
4110
|
-
case "/
|
|
4111
|
-
const
|
|
4112
|
-
if (
|
|
4113
|
-
printInfo(
|
|
4114
|
-
printInfo("\u53EF\u9009: delegated, autonomous, controlled");
|
|
4115
|
-
return true;
|
|
4116
|
-
}
|
|
4117
|
-
if (["delegated", "autonomous", "controlled"].includes(mode)) {
|
|
4118
|
-
context.config.collaboration = mode;
|
|
4119
|
-
context.setMode?.(mode);
|
|
4120
|
-
printSuccess(`\u5DF2\u5207\u6362\u5230 ${mode} \u6A21\u5F0F`);
|
|
3984
|
+
case "/skills": {
|
|
3985
|
+
const skills = context.skillRegistry.list();
|
|
3986
|
+
if (skills.length === 0) {
|
|
3987
|
+
printInfo("\u6682\u65E0\u53EF\u7528\u6280\u80FD\u3002\u5728 ~/.zencode/skills/ \u6216 .zencode/skills/ \u4E2D\u6DFB\u52A0 YAML \u6587\u4EF6\u5B9A\u4E49\u6280\u80FD\u3002");
|
|
4121
3988
|
} else {
|
|
4122
|
-
|
|
3989
|
+
printInfo(`\u53EF\u7528\u6280\u80FD (${skills.length}):`);
|
|
3990
|
+
for (const s of skills) {
|
|
3991
|
+
printInfo(` /${s.name}: ${s.description}`);
|
|
3992
|
+
}
|
|
4123
3993
|
}
|
|
4124
3994
|
return true;
|
|
4125
3995
|
}
|
|
4126
|
-
case "/
|
|
4127
|
-
context.
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
3996
|
+
case "/agents": {
|
|
3997
|
+
const agents = context.agentRegistry.list();
|
|
3998
|
+
if (agents.length === 0) {
|
|
3999
|
+
printInfo("\u6682\u65E0\u53EF\u7528\u5B50 Agent");
|
|
4000
|
+
} else {
|
|
4001
|
+
printInfo(`\u53EF\u7528\u5B50 Agent (${agents.length}):`);
|
|
4002
|
+
for (const a of agents) {
|
|
4003
|
+
printInfo(` ${a.name}: ${a.description}`);
|
|
4004
|
+
}
|
|
4005
|
+
}
|
|
4133
4006
|
return true;
|
|
4007
|
+
}
|
|
4134
4008
|
case "/clear":
|
|
4135
|
-
printSuccess("\u5BF9\u8BDD\u5386\u53F2\u5DF2\u6E05\u7A7A
|
|
4136
|
-
return
|
|
4009
|
+
printSuccess("\u5BF9\u8BDD\u5386\u53F2\u5DF2\u6E05\u7A7A");
|
|
4010
|
+
return "clear";
|
|
4137
4011
|
case "/parallel": {
|
|
4138
4012
|
const current = context.config.features.parallel_agents;
|
|
4139
4013
|
const next = current === "on" ? "off" : "on";
|
|
@@ -4142,7 +4016,7 @@ function handleSlashCommand(input, context) {
|
|
|
4142
4016
|
context.registry.unregister("spawn-agents");
|
|
4143
4017
|
} else if (!context.registry.has("spawn-agents")) {
|
|
4144
4018
|
context.registry.register(
|
|
4145
|
-
createSpawnAgentsTool(context.client, context.registry, context.config
|
|
4019
|
+
createSpawnAgentsTool(context.client, context.registry, context.config)
|
|
4146
4020
|
);
|
|
4147
4021
|
}
|
|
4148
4022
|
printSuccess(`\u5E76\u884C\u5B50 Agent \u529F\u80FD\u5DF2${next === "on" ? "\u5F00\u542F" : "\u5173\u95ED"}`);
|
|
@@ -4162,9 +4036,9 @@ function handleSlashCommand(input, context) {
|
|
|
4162
4036
|
}
|
|
4163
4037
|
case "/info":
|
|
4164
4038
|
printInfo(`\u6A21\u578B: ${context.config.model}`);
|
|
4165
|
-
printInfo(`Agent \u6A21\u5F0F: ${context.config.agent_mode}`);
|
|
4166
|
-
printInfo(`\u534F\u4F5C\u6A21\u5F0F: ${context.config.collaboration}`);
|
|
4167
4039
|
printInfo(`\u57FA\u7840 URL: ${context.config.base_url}`);
|
|
4040
|
+
printInfo(`\u5B50 Agent: ${context.agentRegistry.listNames().join(", ") || "\u65E0"}`);
|
|
4041
|
+
printInfo(`\u6280\u80FD: ${context.skillRegistry.listNames().join(", ") || "\u65E0"}`);
|
|
4168
4042
|
return true;
|
|
4169
4043
|
case "/exit":
|
|
4170
4044
|
process.exit(0);
|
|
@@ -4175,7 +4049,11 @@ function handleSlashCommand(input, context) {
|
|
|
4175
4049
|
}
|
|
4176
4050
|
async function startRepl(options) {
|
|
4177
4051
|
const { config } = options;
|
|
4178
|
-
const
|
|
4052
|
+
const agentRegistry = new SubAgentConfigRegistry();
|
|
4053
|
+
for (const agentConfig of loadAllAgentConfigs()) {
|
|
4054
|
+
agentRegistry.register(agentConfig);
|
|
4055
|
+
}
|
|
4056
|
+
const { systemPrompt } = await buildPrompt(config, agentRegistry.list());
|
|
4179
4057
|
const registry = new ToolRegistry();
|
|
4180
4058
|
registerCoreTools(registry);
|
|
4181
4059
|
registry.setPermissions(config.permissions);
|
|
@@ -4187,23 +4065,19 @@ async function startRepl(options) {
|
|
|
4187
4065
|
maxTokens: config.max_tokens
|
|
4188
4066
|
});
|
|
4189
4067
|
const todoStore = new TodoStore();
|
|
4190
|
-
const memoStore = new MemoStore();
|
|
4191
4068
|
if (config.features.parallel_agents === "on") {
|
|
4192
|
-
registry.register(createSpawnAgentsTool(client, registry, config
|
|
4069
|
+
registry.register(createSpawnAgentsTool(client, registry, config));
|
|
4193
4070
|
}
|
|
4194
4071
|
if (config.features.todo === "on") {
|
|
4195
4072
|
registry.register(createTodoTool(todoStore));
|
|
4196
4073
|
}
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
if (config.agent_mode === "single") {
|
|
4201
|
-
singleAgent = new Agent(client, registry, config, systemPrompt);
|
|
4202
|
-
} else {
|
|
4203
|
-
orchestrator = new Orchestrator(registry, config, systemPrompt, memoStore);
|
|
4074
|
+
const skillRegistry = new SkillRegistry();
|
|
4075
|
+
for (const skill of loadAllSkills()) {
|
|
4076
|
+
skillRegistry.register(skill);
|
|
4204
4077
|
}
|
|
4205
|
-
|
|
4206
|
-
|
|
4078
|
+
registry.register(createDispatchTool(client, registry, config, agentRegistry));
|
|
4079
|
+
let agent = new Agent(client, registry, config, systemPrompt);
|
|
4080
|
+
printWelcome(config.model);
|
|
4207
4081
|
const rl = readline.createInterface({
|
|
4208
4082
|
input: process.stdin,
|
|
4209
4083
|
output: process.stdout,
|
|
@@ -4211,13 +4085,13 @@ async function startRepl(options) {
|
|
|
4211
4085
|
historySize: 100
|
|
4212
4086
|
});
|
|
4213
4087
|
setConfirmHandler(async (promptText) => {
|
|
4214
|
-
return new Promise((
|
|
4088
|
+
return new Promise((resolve10) => {
|
|
4215
4089
|
process.stdout.write(promptText);
|
|
4216
4090
|
const onData = (data) => {
|
|
4217
4091
|
process.stdin.removeListener("data", onData);
|
|
4218
4092
|
process.stdin.pause();
|
|
4219
4093
|
const answer = data.toString().trim().toLowerCase();
|
|
4220
|
-
|
|
4094
|
+
resolve10(answer === "y");
|
|
4221
4095
|
};
|
|
4222
4096
|
process.stdin.resume();
|
|
4223
4097
|
process.stdin.once("data", onData);
|
|
@@ -4231,29 +4105,75 @@ async function startRepl(options) {
|
|
|
4231
4105
|
return;
|
|
4232
4106
|
}
|
|
4233
4107
|
if (input.startsWith("/")) {
|
|
4108
|
+
const slashParts = input.slice(1).split(/\s+/);
|
|
4109
|
+
const skillName = slashParts[0] ?? "";
|
|
4110
|
+
const skillArgs = slashParts.slice(1).join(" ");
|
|
4111
|
+
const skill = skillRegistry.get(skillName);
|
|
4112
|
+
if (skill) {
|
|
4113
|
+
const expandedPrompt = skillRegistry.expandPrompt(skill, skillArgs);
|
|
4114
|
+
rl.pause();
|
|
4115
|
+
let isStreaming2 = false;
|
|
4116
|
+
const thinkFilter2 = createThinkFilter();
|
|
4117
|
+
const callbacks2 = {
|
|
4118
|
+
onContent: (text) => {
|
|
4119
|
+
const filtered = thinkFilter2(text);
|
|
4120
|
+
if (!filtered) return;
|
|
4121
|
+
if (!isStreaming2) {
|
|
4122
|
+
isStreaming2 = true;
|
|
4123
|
+
process.stdout.write("\n");
|
|
4124
|
+
}
|
|
4125
|
+
printStream(filtered);
|
|
4126
|
+
},
|
|
4127
|
+
onToolExecuting: (name, params) => {
|
|
4128
|
+
if (isStreaming2) {
|
|
4129
|
+
process.stdout.write("\n");
|
|
4130
|
+
isStreaming2 = false;
|
|
4131
|
+
}
|
|
4132
|
+
printToolCall(name, params);
|
|
4133
|
+
},
|
|
4134
|
+
onToolResult: (name, result, truncated) => {
|
|
4135
|
+
printToolResult(name, result, truncated);
|
|
4136
|
+
},
|
|
4137
|
+
onError: (err) => {
|
|
4138
|
+
if (isStreaming2) {
|
|
4139
|
+
process.stdout.write("\n");
|
|
4140
|
+
isStreaming2 = false;
|
|
4141
|
+
}
|
|
4142
|
+
printError(err.message);
|
|
4143
|
+
}
|
|
4144
|
+
};
|
|
4145
|
+
try {
|
|
4146
|
+
await agent.run(expandedPrompt, callbacks2);
|
|
4147
|
+
} catch (err) {
|
|
4148
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4149
|
+
printError(msg);
|
|
4150
|
+
}
|
|
4151
|
+
if (isStreaming2) {
|
|
4152
|
+
process.stdout.write("\n");
|
|
4153
|
+
}
|
|
4154
|
+
console.log();
|
|
4155
|
+
rl.resume();
|
|
4156
|
+
rl.prompt();
|
|
4157
|
+
return;
|
|
4158
|
+
}
|
|
4234
4159
|
const handled = handleSlashCommand(input, {
|
|
4235
4160
|
config,
|
|
4236
|
-
orchestrator,
|
|
4237
4161
|
registry,
|
|
4238
4162
|
client,
|
|
4239
4163
|
todoStore,
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
orchestrator?.setMode(mode);
|
|
4243
|
-
}
|
|
4164
|
+
agentRegistry,
|
|
4165
|
+
skillRegistry
|
|
4244
4166
|
});
|
|
4167
|
+
if (handled === "clear") {
|
|
4168
|
+
agent = new Agent(client, registry, config, systemPrompt);
|
|
4169
|
+
rl.prompt();
|
|
4170
|
+
return;
|
|
4171
|
+
}
|
|
4245
4172
|
if (handled) {
|
|
4246
4173
|
rl.prompt();
|
|
4247
4174
|
return;
|
|
4248
4175
|
}
|
|
4249
4176
|
}
|
|
4250
|
-
if (config.agent_mode === "single" && !singleAgent) {
|
|
4251
|
-
singleAgent = new Agent(client, registry, config, systemPrompt);
|
|
4252
|
-
orchestrator = void 0;
|
|
4253
|
-
} else if (config.agent_mode === "dual" && !orchestrator) {
|
|
4254
|
-
orchestrator = new Orchestrator(registry, config, systemPrompt, memoStore);
|
|
4255
|
-
singleAgent = void 0;
|
|
4256
|
-
}
|
|
4257
4177
|
rl.pause();
|
|
4258
4178
|
let isStreaming = false;
|
|
4259
4179
|
const thinkFilter = createThinkFilter();
|
|
@@ -4277,16 +4197,6 @@ async function startRepl(options) {
|
|
|
4277
4197
|
onToolResult: (name, result, truncated) => {
|
|
4278
4198
|
printToolResult(name, result, truncated);
|
|
4279
4199
|
},
|
|
4280
|
-
onCoderStart: () => {
|
|
4281
|
-
if (isStreaming) {
|
|
4282
|
-
process.stdout.write("\n");
|
|
4283
|
-
isStreaming = false;
|
|
4284
|
-
}
|
|
4285
|
-
printInfo("\u7F16\u7801 Agent \u5F00\u59CB\u5DE5\u4F5C...");
|
|
4286
|
-
},
|
|
4287
|
-
onCoderEnd: () => {
|
|
4288
|
-
printInfo("\u7F16\u7801 Agent \u5B8C\u6210");
|
|
4289
|
-
},
|
|
4290
4200
|
onError: (err) => {
|
|
4291
4201
|
if (isStreaming) {
|
|
4292
4202
|
process.stdout.write("\n");
|
|
@@ -4296,11 +4206,7 @@ async function startRepl(options) {
|
|
|
4296
4206
|
}
|
|
4297
4207
|
};
|
|
4298
4208
|
try {
|
|
4299
|
-
|
|
4300
|
-
await singleAgent.run(input, callbacks);
|
|
4301
|
-
} else if (orchestrator) {
|
|
4302
|
-
await orchestrator.run(input, callbacks);
|
|
4303
|
-
}
|
|
4209
|
+
await agent.run(input, callbacks);
|
|
4304
4210
|
} catch (err) {
|
|
4305
4211
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4306
4212
|
printError(msg);
|
|
@@ -4320,13 +4226,18 @@ async function startRepl(options) {
|
|
|
4320
4226
|
|
|
4321
4227
|
// src/cli/index.ts
|
|
4322
4228
|
init_todo_store();
|
|
4323
|
-
|
|
4229
|
+
init_registry2();
|
|
4230
|
+
init_loader();
|
|
4324
4231
|
init_spawn_agents();
|
|
4325
4232
|
init_todo();
|
|
4326
|
-
|
|
4233
|
+
init_dispatch();
|
|
4327
4234
|
init_bridge();
|
|
4328
4235
|
async function runOnce(prompt, config) {
|
|
4329
|
-
const
|
|
4236
|
+
const agentRegistry = new SubAgentConfigRegistry();
|
|
4237
|
+
for (const agentConfig of loadAllAgentConfigs()) {
|
|
4238
|
+
agentRegistry.register(agentConfig);
|
|
4239
|
+
}
|
|
4240
|
+
const { systemPrompt } = await buildPrompt(config, agentRegistry.list());
|
|
4330
4241
|
const registry = new ToolRegistry();
|
|
4331
4242
|
registerCoreTools(registry);
|
|
4332
4243
|
registry.setPermissions(config.permissions);
|
|
@@ -4338,14 +4249,13 @@ async function runOnce(prompt, config) {
|
|
|
4338
4249
|
maxTokens: config.max_tokens
|
|
4339
4250
|
});
|
|
4340
4251
|
const todoStore = new TodoStore();
|
|
4341
|
-
const memoStore = new MemoStore();
|
|
4342
4252
|
if (config.features.parallel_agents === "on") {
|
|
4343
|
-
registry.register(createSpawnAgentsTool(client, registry, config
|
|
4253
|
+
registry.register(createSpawnAgentsTool(client, registry, config));
|
|
4344
4254
|
}
|
|
4345
4255
|
if (config.features.todo === "on") {
|
|
4346
4256
|
registry.register(createTodoTool(todoStore));
|
|
4347
4257
|
}
|
|
4348
|
-
registry.register(
|
|
4258
|
+
registry.register(createDispatchTool(client, registry, config, agentRegistry));
|
|
4349
4259
|
let isStreaming = false;
|
|
4350
4260
|
const thinkFilter = createThinkFilter();
|
|
4351
4261
|
const callbacks = {
|
|
@@ -4367,28 +4277,13 @@ async function runOnce(prompt, config) {
|
|
|
4367
4277
|
onToolResult: (name, result, truncated) => {
|
|
4368
4278
|
printToolResult(name, result, truncated);
|
|
4369
4279
|
},
|
|
4370
|
-
onCoderStart: () => {
|
|
4371
|
-
if (isStreaming) {
|
|
4372
|
-
process.stdout.write("\n");
|
|
4373
|
-
isStreaming = false;
|
|
4374
|
-
}
|
|
4375
|
-
printInfo("\u7F16\u7801 Agent \u5F00\u59CB\u5DE5\u4F5C...");
|
|
4376
|
-
},
|
|
4377
|
-
onCoderEnd: () => {
|
|
4378
|
-
printInfo("\u7F16\u7801 Agent \u5B8C\u6210");
|
|
4379
|
-
},
|
|
4380
4280
|
onError: (err) => {
|
|
4381
4281
|
printError(err.message);
|
|
4382
4282
|
}
|
|
4383
4283
|
};
|
|
4384
4284
|
try {
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
await agent.run(prompt, callbacks);
|
|
4388
|
-
} else {
|
|
4389
|
-
const orchestrator = new Orchestrator(registry, config, systemPrompt, memoStore);
|
|
4390
|
-
await orchestrator.run(prompt, callbacks);
|
|
4391
|
-
}
|
|
4285
|
+
const agent = new Agent(client, registry, config, systemPrompt);
|
|
4286
|
+
await agent.run(prompt, callbacks);
|
|
4392
4287
|
} catch (err) {
|
|
4393
4288
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4394
4289
|
printError(msg);
|
|
@@ -4400,7 +4295,7 @@ async function runOnce(prompt, config) {
|
|
|
4400
4295
|
}
|
|
4401
4296
|
function createCli() {
|
|
4402
4297
|
const program2 = new Command();
|
|
4403
|
-
program2.name("zencode").description("\u6781\u7B80 CLI AI \u7F16\u7A0B\u5DE5\u5177").version("0.2.
|
|
4298
|
+
program2.name("zencode").description("\u6781\u7B80 CLI AI \u7F16\u7A0B\u5DE5\u5177").version("0.2.3").option("-m, --model <model>", "\u6307\u5B9A\u6A21\u578B\u540D\u79F0").option("-k, --api-key <key>", "API \u5BC6\u94A5").option("-u, --base-url <url>", "API \u57FA\u7840 URL").option("--simple", "\u4F7F\u7528\u7B80\u5355 REPL \u6A21\u5F0F\uFF08\u975E\u5168\u5C4F TUI\uFF09").argument("[prompt...]", "\u76F4\u63A5\u6267\u884C\u7684\u63D0\u793A\u8BCD\uFF08\u975E\u4EA4\u4E92\u5F0F\uFF09").action(async (promptParts, opts) => {
|
|
4404
4299
|
const config = loadConfig(opts);
|
|
4405
4300
|
if (!config.api_key) {
|
|
4406
4301
|
printError("\u672A\u8BBE\u7F6E API \u5BC6\u94A5\u3002\u8BF7\u901A\u8FC7\u4EE5\u4E0B\u65B9\u5F0F\u4E4B\u4E00\u8BBE\u7F6E\uFF1A");
|