zencode-cli 0.1.0 → 0.2.2
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 +230 -0
- package/dist/bin/zencode.js +1483 -1397
- package/dist/bin/zencode.js.map +1 -1
- package/package.json +56 -54
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,34 @@ var init_client = __esm({
|
|
|
168
56
|
params.tools = tools;
|
|
169
57
|
params.tool_choice = "auto";
|
|
170
58
|
}
|
|
59
|
+
const abortController = new AbortController();
|
|
60
|
+
this.activeAbortController = abortController;
|
|
171
61
|
try {
|
|
172
|
-
const stream = await this.client.chat.completions.create(params
|
|
62
|
+
const stream = await this.client.chat.completions.create(params, {
|
|
63
|
+
signal: abortController.signal
|
|
64
|
+
});
|
|
173
65
|
let contentParts = [];
|
|
66
|
+
let reasoningParts = [];
|
|
67
|
+
let reasoningStarted = false;
|
|
68
|
+
let reasoningEnded = false;
|
|
174
69
|
const toolCallMap = /* @__PURE__ */ new Map();
|
|
175
70
|
for await (const chunk of stream) {
|
|
176
71
|
const choice = chunk.choices[0];
|
|
177
72
|
if (!choice) continue;
|
|
178
73
|
const delta = choice.delta;
|
|
74
|
+
if (delta.reasoning_content) {
|
|
75
|
+
reasoningParts.push(delta.reasoning_content);
|
|
76
|
+
if (!reasoningStarted) {
|
|
77
|
+
reasoningStarted = true;
|
|
78
|
+
callbacks.onContent?.("<think>");
|
|
79
|
+
}
|
|
80
|
+
callbacks.onContent?.(delta.reasoning_content);
|
|
81
|
+
}
|
|
179
82
|
if (delta.content) {
|
|
83
|
+
if (reasoningStarted && !reasoningEnded) {
|
|
84
|
+
reasoningEnded = true;
|
|
85
|
+
callbacks.onContent?.("</think>");
|
|
86
|
+
}
|
|
180
87
|
contentParts.push(delta.content);
|
|
181
88
|
callbacks.onContent?.(delta.content);
|
|
182
89
|
}
|
|
@@ -204,6 +111,10 @@ var init_client = __esm({
|
|
|
204
111
|
}
|
|
205
112
|
}
|
|
206
113
|
}
|
|
114
|
+
if (reasoningStarted && !reasoningEnded) {
|
|
115
|
+
reasoningEnded = true;
|
|
116
|
+
callbacks.onContent?.("</think>");
|
|
117
|
+
}
|
|
207
118
|
const toolCalls = [];
|
|
208
119
|
for (const [, tc] of [...toolCallMap.entries()].sort(([a], [b]) => a - b)) {
|
|
209
120
|
const toolCall = {
|
|
@@ -218,10 +129,14 @@ var init_client = __esm({
|
|
|
218
129
|
callbacks.onToolCall?.(toolCall);
|
|
219
130
|
}
|
|
220
131
|
const fullContent = contentParts.join("");
|
|
132
|
+
const fullReasoning = reasoningParts.join("");
|
|
221
133
|
const assistantMessage = {
|
|
222
134
|
role: "assistant",
|
|
223
135
|
content: fullContent || null
|
|
224
136
|
};
|
|
137
|
+
if (fullReasoning) {
|
|
138
|
+
assistantMessage.reasoning_content = fullReasoning;
|
|
139
|
+
}
|
|
225
140
|
if (toolCalls.length > 0) {
|
|
226
141
|
assistantMessage.tool_calls = toolCalls;
|
|
227
142
|
}
|
|
@@ -229,8 +144,14 @@ var init_client = __esm({
|
|
|
229
144
|
return assistantMessage;
|
|
230
145
|
} catch (error) {
|
|
231
146
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
232
|
-
|
|
147
|
+
if (!isAbortError(err)) {
|
|
148
|
+
callbacks.onError?.(err);
|
|
149
|
+
}
|
|
233
150
|
throw err;
|
|
151
|
+
} finally {
|
|
152
|
+
if (this.activeAbortController === abortController) {
|
|
153
|
+
this.activeAbortController = null;
|
|
154
|
+
}
|
|
234
155
|
}
|
|
235
156
|
}
|
|
236
157
|
/**
|
|
@@ -258,6 +179,10 @@ var init_client = __esm({
|
|
|
258
179
|
role: "assistant",
|
|
259
180
|
content: msg.content
|
|
260
181
|
};
|
|
182
|
+
const reasoning = msg.reasoning_content;
|
|
183
|
+
if (reasoning && typeof reasoning === "string") {
|
|
184
|
+
result.reasoning_content = reasoning;
|
|
185
|
+
}
|
|
261
186
|
if (msg.tool_calls && msg.tool_calls.length > 0) {
|
|
262
187
|
result.tool_calls = msg.tool_calls.map((tc) => ({
|
|
263
188
|
id: tc.id,
|
|
@@ -273,6 +198,12 @@ var init_client = __esm({
|
|
|
273
198
|
get modelName() {
|
|
274
199
|
return this.model;
|
|
275
200
|
}
|
|
201
|
+
abortActiveStream() {
|
|
202
|
+
if (this.activeAbortController) {
|
|
203
|
+
this.activeAbortController.abort();
|
|
204
|
+
this.activeAbortController = null;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
276
207
|
};
|
|
277
208
|
}
|
|
278
209
|
});
|
|
@@ -446,6 +377,10 @@ var init_write_file = __esm({
|
|
|
446
377
|
content: {
|
|
447
378
|
type: "string",
|
|
448
379
|
description: "\u8981\u5199\u5165\u7684\u6587\u4EF6\u5185\u5BB9"
|
|
380
|
+
},
|
|
381
|
+
overwrite: {
|
|
382
|
+
type: "boolean",
|
|
383
|
+
description: "\u6587\u4EF6\u5DF2\u5B58\u5728\u65F6\u662F\u5426\u786E\u8BA4\u8986\u76D6\uFF0C\u9ED8\u8BA4 false"
|
|
449
384
|
}
|
|
450
385
|
},
|
|
451
386
|
required: ["path", "content"]
|
|
@@ -538,14 +473,24 @@ var init_edit_file = __esm({
|
|
|
538
473
|
|
|
539
474
|
// src/tools/bash.ts
|
|
540
475
|
import { exec } from "child_process";
|
|
541
|
-
|
|
476
|
+
function decodeBuffer(buf) {
|
|
477
|
+
if (!IS_WIN) return buf.toString("utf-8");
|
|
478
|
+
try {
|
|
479
|
+
const decoder = new TextDecoder("gbk");
|
|
480
|
+
return decoder.decode(buf);
|
|
481
|
+
} catch {
|
|
482
|
+
return buf.toString("utf-8");
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
var DEFAULT_TIMEOUT, IS_WIN, bashTool;
|
|
542
486
|
var init_bash = __esm({
|
|
543
487
|
"src/tools/bash.ts"() {
|
|
544
488
|
"use strict";
|
|
545
489
|
DEFAULT_TIMEOUT = 12e4;
|
|
490
|
+
IS_WIN = process.platform === "win32";
|
|
546
491
|
bashTool = {
|
|
547
492
|
name: "bash",
|
|
548
|
-
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",
|
|
493
|
+
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",
|
|
549
494
|
parameters: {
|
|
550
495
|
type: "object",
|
|
551
496
|
properties: {
|
|
@@ -564,7 +509,7 @@ var init_bash = __esm({
|
|
|
564
509
|
async execute(params) {
|
|
565
510
|
const command = params["command"];
|
|
566
511
|
const timeout = params["timeout"] || DEFAULT_TIMEOUT;
|
|
567
|
-
return new Promise((
|
|
512
|
+
return new Promise((resolve10) => {
|
|
568
513
|
exec(
|
|
569
514
|
command,
|
|
570
515
|
{
|
|
@@ -572,9 +517,12 @@ var init_bash = __esm({
|
|
|
572
517
|
timeout,
|
|
573
518
|
maxBuffer: 1024 * 1024 * 10,
|
|
574
519
|
// 10MB
|
|
575
|
-
shell: process.platform === "win32" ? "cmd.exe" : "/bin/bash"
|
|
520
|
+
shell: process.platform === "win32" ? "cmd.exe" : "/bin/bash",
|
|
521
|
+
encoding: "buffer"
|
|
576
522
|
},
|
|
577
|
-
(error,
|
|
523
|
+
(error, stdoutBuf, stderrBuf) => {
|
|
524
|
+
const stdout = stdoutBuf ? decodeBuffer(stdoutBuf) : "";
|
|
525
|
+
const stderr = stderrBuf ? decodeBuffer(stderrBuf) : "";
|
|
578
526
|
let output = "";
|
|
579
527
|
if (stdout) output += stdout;
|
|
580
528
|
if (stderr) output += (output ? "\n" : "") + `[stderr]
|
|
@@ -585,7 +533,7 @@ ${stderr}`;
|
|
|
585
533
|
} else if (error && !stdout && !stderr) {
|
|
586
534
|
output = `\u547D\u4EE4\u6267\u884C\u5931\u8D25\uFF1A${error.message}`;
|
|
587
535
|
}
|
|
588
|
-
|
|
536
|
+
resolve10({ content: output || "\uFF08\u65E0\u8F93\u51FA\uFF09" });
|
|
589
537
|
}
|
|
590
538
|
);
|
|
591
539
|
});
|
|
@@ -896,6 +844,18 @@ var init_conversation = __esm({
|
|
|
896
844
|
content
|
|
897
845
|
});
|
|
898
846
|
}
|
|
847
|
+
/**
|
|
848
|
+
* 清除历史消息中的 reasoning_content(新一轮对话开始时调用)
|
|
849
|
+
* deepseek-reasoner 要求同一轮 tool call 循环内保留 reasoning_content,
|
|
850
|
+
* 但新一轮用户问题开始时应清除以节省带宽,API 也会忽略旧的 reasoning_content
|
|
851
|
+
*/
|
|
852
|
+
clearReasoningContent() {
|
|
853
|
+
for (const msg of this.messages) {
|
|
854
|
+
if (msg.reasoning_content !== void 0) {
|
|
855
|
+
msg.reasoning_content = void 0;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
}
|
|
899
859
|
/**
|
|
900
860
|
* 获取完整的消息列表(包含系统提示词)
|
|
901
861
|
*/
|
|
@@ -929,6 +889,47 @@ var init_conversation = __esm({
|
|
|
929
889
|
}
|
|
930
890
|
});
|
|
931
891
|
|
|
892
|
+
// src/core/read-tracker.ts
|
|
893
|
+
import * as fs6 from "fs";
|
|
894
|
+
import * as path6 from "path";
|
|
895
|
+
var ReadTracker;
|
|
896
|
+
var init_read_tracker = __esm({
|
|
897
|
+
"src/core/read-tracker.ts"() {
|
|
898
|
+
"use strict";
|
|
899
|
+
ReadTracker = class {
|
|
900
|
+
files = /* @__PURE__ */ new Set();
|
|
901
|
+
/** 标记文件已被读取 */
|
|
902
|
+
markRead(filePath) {
|
|
903
|
+
this.files.add(this.normalize(filePath));
|
|
904
|
+
}
|
|
905
|
+
/** 标记文件已被写入(新建/重写,agent 已知内容) */
|
|
906
|
+
markWritten(filePath) {
|
|
907
|
+
this.files.add(this.normalize(filePath));
|
|
908
|
+
}
|
|
909
|
+
/** 检查文件是否已读取 */
|
|
910
|
+
hasRead(filePath) {
|
|
911
|
+
return this.files.has(this.normalize(filePath));
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* 检查 write-file 是否会覆盖已有文件
|
|
915
|
+
* @returns 警告消息(需要拦截),或 null(可以继续执行)
|
|
916
|
+
*/
|
|
917
|
+
checkWriteOverwrite(filePath, overwrite) {
|
|
918
|
+
const resolved = path6.resolve(filePath);
|
|
919
|
+
if (!overwrite && fs6.existsSync(resolved)) {
|
|
920
|
+
return `\u26A0 \u6587\u4EF6\u5DF2\u5B58\u5728\uFF1A${filePath}
|
|
921
|
+
\u4FEE\u6539\u5DF2\u6709\u6587\u4EF6\u8BF7\u7528 read-file + edit-file\uFF08\u66F4\u7CBE\u786E\u5B89\u5168\uFF09\u3002
|
|
922
|
+
\u5982\u786E\u9700\u5B8C\u6574\u91CD\u5199\uFF0C\u8BF7\u91CD\u65B0\u8C03\u7528 write-file \u5E76\u8BBE\u7F6E overwrite: true\u3002`;
|
|
923
|
+
}
|
|
924
|
+
return null;
|
|
925
|
+
}
|
|
926
|
+
normalize(filePath) {
|
|
927
|
+
return filePath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
928
|
+
}
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
});
|
|
932
|
+
|
|
932
933
|
// src/core/agent.ts
|
|
933
934
|
var Agent;
|
|
934
935
|
var init_agent = __esm({
|
|
@@ -936,12 +937,15 @@ var init_agent = __esm({
|
|
|
936
937
|
"use strict";
|
|
937
938
|
init_permission();
|
|
938
939
|
init_conversation();
|
|
940
|
+
init_read_tracker();
|
|
939
941
|
Agent = class {
|
|
940
942
|
conversation;
|
|
941
943
|
client;
|
|
942
944
|
registry;
|
|
943
945
|
config;
|
|
944
946
|
fixedTools;
|
|
947
|
+
readTracker = new ReadTracker();
|
|
948
|
+
interrupted = false;
|
|
945
949
|
constructor(client, registry, config, systemPrompt, tools) {
|
|
946
950
|
this.client = client;
|
|
947
951
|
this.registry = registry;
|
|
@@ -954,9 +958,12 @@ var init_agent = __esm({
|
|
|
954
958
|
* 执行一轮完整的 agent 循环
|
|
955
959
|
*/
|
|
956
960
|
async run(userMessage, callbacks = {}) {
|
|
961
|
+
this.interrupted = false;
|
|
962
|
+
this.conversation.clearReasoningContent();
|
|
957
963
|
this.conversation.addUserMessage(userMessage);
|
|
958
964
|
let lastContent = "";
|
|
959
965
|
while (true) {
|
|
966
|
+
if (this.interrupted) break;
|
|
960
967
|
const tools = this.fixedTools || this.registry.toToolDefinitions();
|
|
961
968
|
const assistantMsg = await this.client.chatStream(
|
|
962
969
|
this.conversation.getMessages(),
|
|
@@ -969,6 +976,7 @@ var init_agent = __esm({
|
|
|
969
976
|
break;
|
|
970
977
|
}
|
|
971
978
|
for (const toolCall of assistantMsg.tool_calls) {
|
|
979
|
+
if (this.interrupted) break;
|
|
972
980
|
const toolName = toolCall.function.name;
|
|
973
981
|
let params;
|
|
974
982
|
try {
|
|
@@ -978,6 +986,26 @@ var init_agent = __esm({
|
|
|
978
986
|
continue;
|
|
979
987
|
}
|
|
980
988
|
try {
|
|
989
|
+
if (toolName === "edit-file") {
|
|
990
|
+
const editPath = params["path"];
|
|
991
|
+
if (!this.readTracker.hasRead(editPath)) {
|
|
992
|
+
this.conversation.addToolResult(
|
|
993
|
+
toolCall.id,
|
|
994
|
+
`\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`
|
|
995
|
+
);
|
|
996
|
+
continue;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
if (toolName === "write-file") {
|
|
1000
|
+
const warn = this.readTracker.checkWriteOverwrite(
|
|
1001
|
+
params["path"],
|
|
1002
|
+
params["overwrite"]
|
|
1003
|
+
);
|
|
1004
|
+
if (warn) {
|
|
1005
|
+
this.conversation.addToolResult(toolCall.id, warn);
|
|
1006
|
+
continue;
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
981
1009
|
const permLevel = this.registry.getPermissionLevel(toolName);
|
|
982
1010
|
if (permLevel === "deny") {
|
|
983
1011
|
callbacks.onDenied?.(toolName);
|
|
@@ -998,6 +1026,11 @@ var init_agent = __esm({
|
|
|
998
1026
|
}
|
|
999
1027
|
const result = await this.registry.execute(toolName, params, this.config.max_tool_output);
|
|
1000
1028
|
callbacks.onToolResult?.(toolName, result.content, result.truncated ?? false);
|
|
1029
|
+
if (toolName === "read-file") {
|
|
1030
|
+
this.readTracker.markRead(params["path"]);
|
|
1031
|
+
} else if (toolName === "write-file") {
|
|
1032
|
+
this.readTracker.markWritten(params["path"]);
|
|
1033
|
+
}
|
|
1001
1034
|
this.conversation.addToolResult(toolCall.id, result.content);
|
|
1002
1035
|
} catch (err) {
|
|
1003
1036
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -1010,6 +1043,10 @@ var init_agent = __esm({
|
|
|
1010
1043
|
}
|
|
1011
1044
|
return lastContent;
|
|
1012
1045
|
}
|
|
1046
|
+
interrupt() {
|
|
1047
|
+
this.interrupted = true;
|
|
1048
|
+
this.client.abortActiveStream();
|
|
1049
|
+
}
|
|
1013
1050
|
/**
|
|
1014
1051
|
* 获取对话历史
|
|
1015
1052
|
*/
|
|
@@ -1020,448 +1057,76 @@ var init_agent = __esm({
|
|
|
1020
1057
|
}
|
|
1021
1058
|
});
|
|
1022
1059
|
|
|
1023
|
-
// src/core/
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
"
|
|
1060
|
+
// src/core/prompt/layers/core.ts
|
|
1061
|
+
import * as os2 from "os";
|
|
1062
|
+
function buildCorePrompt() {
|
|
1063
|
+
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";
|
|
1064
|
+
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
|
|
1065
|
+
|
|
1066
|
+
\u5DE5\u4F5C\u76EE\u5F55\uFF1A${process.cwd()}
|
|
1067
|
+
\u7CFB\u7EDF\uFF1A${os2.platform()} ${os2.arch()}
|
|
1068
|
+
|
|
1069
|
+
# \u5DE5\u5177\u4F7F\u7528\u539F\u5219
|
|
1070
|
+
|
|
1071
|
+
\u4F60\u6709\u4EE5\u4E0B\u5DE5\u5177\u53EF\u7528\uFF0C\u8BF7\u6839\u636E\u4EFB\u52A1\u9009\u62E9\u6700\u5408\u9002\u7684\u5DE5\u5177\uFF1A
|
|
1072
|
+
|
|
1073
|
+
- **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
|
|
1074
|
+
- **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
|
|
1075
|
+
- \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
|
|
1076
|
+
- 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
|
|
1077
|
+
- 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
|
|
1078
|
+
- \u4E0D\u8981\u51ED\u8BB0\u5FC6\u731C\u6D4B\u6587\u4EF6\u5185\u5BB9\uFF0C\u5FC5\u987B\u57FA\u4E8E read-file \u7684\u5B9E\u9645\u8FD4\u56DE\u503C
|
|
1079
|
+
- **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
|
|
1080
|
+
- **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
|
|
1081
|
+
- **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
|
|
1082
|
+
- **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
|
|
1083
|
+
|
|
1084
|
+
\u5173\u952E\u89C4\u5219\uFF1A
|
|
1085
|
+
- **\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
|
|
1086
|
+
- 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
|
|
1087
|
+
- \u4F18\u5148 edit-file \u7F16\u8F91\u5DF2\u6709\u6587\u4EF6\uFF0C\u800C\u975E write-file \u91CD\u5199
|
|
1088
|
+
- \u4E0D\u8981\u521B\u5EFA\u4E0D\u5FC5\u8981\u7684\u65B0\u6587\u4EF6\uFF0C\u4F18\u5148\u5728\u73B0\u6709\u6587\u4EF6\u4E2D\u4FEE\u6539
|
|
1089
|
+
- \u53EA\u505A\u5FC5\u8981\u7684\u6700\u5C0F\u6539\u52A8\uFF0C\u4E0D\u505A\u989D\u5916"\u6539\u8FDB"
|
|
1090
|
+
- \u4E0D\u8981\u6DFB\u52A0\u7528\u6237\u672A\u8981\u6C42\u7684\u6CE8\u91CA\u3001\u6587\u6863\u3001\u7C7B\u578B\u6CE8\u89E3
|
|
1091
|
+
- \u4E0D\u8981\u5F15\u5165\u5B89\u5168\u6F0F\u6D1E\uFF08\u6CE8\u5165\u3001XSS\u3001SQL \u6CE8\u5165\u7B49 OWASP Top 10\uFF09
|
|
1092
|
+
- \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
|
|
1093
|
+
|
|
1094
|
+
# \u4EA4\u4E92\u98CE\u683C
|
|
1095
|
+
|
|
1096
|
+
- \u4FDD\u6301\u6280\u672F\u5BA2\u89C2\u6027\uFF0C\u57FA\u4E8E\u4E8B\u5B9E\u56DE\u7B54\uFF0C\u4E0D\u8FC7\u5EA6\u8D5E\u540C\u6216\u606D\u7EF4\u7528\u6237\uFF0C\u5FC5\u8981\u65F6\u76F4\u63A5\u6307\u51FA\u95EE\u9898
|
|
1097
|
+
- \u4E0D\u786E\u5B9A\u65F6\u5148\u8C03\u67E5\u9A8C\u8BC1\uFF0C\u800C\u975E\u76F4\u89C9\u6027\u5730\u786E\u8BA4\u7528\u6237\u7684\u5047\u8BBE
|
|
1098
|
+
- \u4E0D\u8981\u7ED9\u51FA\u65F6\u95F4\u9884\u4F30\uFF08"\u5927\u6982\u9700\u8981\u51E0\u5206\u949F"\u4E4B\u7C7B\uFF09
|
|
1099
|
+
- \u56DE\u590D\u7B80\u6D01\uFF0C\u76F4\u63A5\u7ED9\u7ED3\u679C`;
|
|
1100
|
+
}
|
|
1101
|
+
var IS_WIN2;
|
|
1102
|
+
var init_core = __esm({
|
|
1103
|
+
"src/core/prompt/layers/core.ts"() {
|
|
1027
1104
|
"use strict";
|
|
1028
|
-
|
|
1029
|
-
init_permission();
|
|
1030
|
-
Coder = class {
|
|
1031
|
-
client;
|
|
1032
|
-
registry;
|
|
1033
|
-
config;
|
|
1034
|
-
systemPrompt;
|
|
1035
|
-
tools;
|
|
1036
|
-
constructor(client, registry, config, systemPrompt, tools) {
|
|
1037
|
-
this.client = client;
|
|
1038
|
-
this.registry = registry;
|
|
1039
|
-
this.config = config;
|
|
1040
|
-
this.systemPrompt = systemPrompt;
|
|
1041
|
-
this.tools = tools;
|
|
1042
|
-
}
|
|
1043
|
-
/**
|
|
1044
|
-
* 执行编码任务(短生命周期,每次新建会话)
|
|
1045
|
-
*
|
|
1046
|
-
* @param taskMessage 调度者发来的任务描述(作为 user message)
|
|
1047
|
-
* @param callbacks 回调
|
|
1048
|
-
* @returns 编码者的最终响应
|
|
1049
|
-
*/
|
|
1050
|
-
async execute(taskMessage, callbacks = {}) {
|
|
1051
|
-
const conversation = new Conversation();
|
|
1052
|
-
conversation.setSystemPrompt(this.systemPrompt);
|
|
1053
|
-
conversation.addUserMessage(taskMessage);
|
|
1054
|
-
let lastContent = "";
|
|
1055
|
-
while (true) {
|
|
1056
|
-
const assistantMsg = await this.client.chatStream(
|
|
1057
|
-
conversation.getMessages(),
|
|
1058
|
-
this.tools.length > 0 ? this.tools : void 0,
|
|
1059
|
-
callbacks
|
|
1060
|
-
);
|
|
1061
|
-
conversation.addAssistantMessage(assistantMsg);
|
|
1062
|
-
if (!assistantMsg.tool_calls || assistantMsg.tool_calls.length === 0) {
|
|
1063
|
-
lastContent = assistantMsg.content || "";
|
|
1064
|
-
break;
|
|
1065
|
-
}
|
|
1066
|
-
for (const toolCall of assistantMsg.tool_calls) {
|
|
1067
|
-
const toolName = toolCall.function.name;
|
|
1068
|
-
let params;
|
|
1069
|
-
try {
|
|
1070
|
-
params = JSON.parse(toolCall.function.arguments);
|
|
1071
|
-
} catch {
|
|
1072
|
-
conversation.addToolResult(toolCall.id, "\u53C2\u6570\u89E3\u6790\u5931\u8D25\uFF1A\u65E0\u6548\u7684 JSON");
|
|
1073
|
-
continue;
|
|
1074
|
-
}
|
|
1075
|
-
try {
|
|
1076
|
-
const permLevel = this.registry.getPermissionLevel(toolName);
|
|
1077
|
-
if (permLevel === "deny") {
|
|
1078
|
-
callbacks.onDenied?.(toolName);
|
|
1079
|
-
conversation.addToolResult(toolCall.id, `\u5DE5\u5177 "${toolName}" \u5DF2\u88AB\u7981\u6B62\u6267\u884C`);
|
|
1080
|
-
continue;
|
|
1081
|
-
}
|
|
1082
|
-
if (permLevel === "auto") {
|
|
1083
|
-
callbacks.onToolExecuting?.(toolName, params);
|
|
1084
|
-
}
|
|
1085
|
-
if (permLevel === "confirm") {
|
|
1086
|
-
const confirmResult = await confirmExecution(toolName, params);
|
|
1087
|
-
if (!confirmResult.approved) {
|
|
1088
|
-
callbacks.onDenied?.(toolName, confirmResult.feedback);
|
|
1089
|
-
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";
|
|
1090
|
-
conversation.addToolResult(toolCall.id, denyMsg);
|
|
1091
|
-
continue;
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
const result = await this.registry.execute(toolName, params, this.config.max_tool_output);
|
|
1095
|
-
callbacks.onToolResult?.(toolName, result.content, result.truncated ?? false);
|
|
1096
|
-
conversation.addToolResult(toolCall.id, result.content);
|
|
1097
|
-
} catch (err) {
|
|
1098
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1099
|
-
conversation.addToolResult(toolCall.id, `\u5DE5\u5177\u6267\u884C\u5F02\u5E38\uFF1A${msg}`);
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
if (assistantMsg.content) {
|
|
1103
|
-
lastContent = assistantMsg.content;
|
|
1104
|
-
}
|
|
1105
|
-
}
|
|
1106
|
-
return lastContent;
|
|
1107
|
-
}
|
|
1108
|
-
};
|
|
1105
|
+
IS_WIN2 = os2.platform() === "win32";
|
|
1109
1106
|
}
|
|
1110
1107
|
});
|
|
1111
1108
|
|
|
1112
|
-
// src/core/
|
|
1113
|
-
function
|
|
1114
|
-
|
|
1115
|
-
case "delegated":
|
|
1116
|
-
return DELEGATED_MODE;
|
|
1117
|
-
case "autonomous":
|
|
1118
|
-
return AUTONOMOUS_MODE;
|
|
1119
|
-
case "controlled":
|
|
1120
|
-
return CONTROLLED_MODE;
|
|
1121
|
-
default:
|
|
1122
|
-
return DELEGATED_MODE;
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
var CODER_IDENTITY, DELEGATED_MODE, AUTONOMOUS_MODE, CONTROLLED_MODE;
|
|
1126
|
-
var init_modes = __esm({
|
|
1127
|
-
"src/core/dual-agent/modes.ts"() {
|
|
1128
|
-
"use strict";
|
|
1129
|
-
CODER_IDENTITY = `\u4F60\u662F\u7F16\u7801\u5B50 Agent\u3002\u6309\u4EE5\u4E0B\u987A\u5E8F\u5DE5\u4F5C\uFF1A
|
|
1109
|
+
// src/core/prompt/layers/planning.ts
|
|
1110
|
+
function buildPlanningPrompt() {
|
|
1111
|
+
return `# \u5DE5\u4F5C\u65B9\u6CD5
|
|
1130
1112
|
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
3. **\u5199 memo**\uFF1A\u5B8C\u6210\u540E memo write \u8BB0\u5F55\u6539\u52A8\u6458\u8981\uFF08key \u5982 "done-xxx"\uFF09\uFF0C\u4FBF\u4E8E\u8C03\u5EA6\u8005\u4E86\u89E3\u8FDB\u5C55
|
|
1136
|
-
4. **\u56DE\u590D**\uFF1A\u4E00\u53E5\u8BDD\u8BF4\u660E\u7ED3\u679C
|
|
1113
|
+
\u5904\u7406\u7F16\u7A0B\u4EFB\u52A1\u65F6\uFF1A
|
|
1114
|
+
1. \u5148\u7528 read-file / grep / glob \u9605\u8BFB\u76F8\u5173\u4EE3\u7801\uFF0C\u7406\u89E3\u73B0\u6709\u903B\u8F91\u548C\u4E0A\u4E0B\u6587
|
|
1115
|
+
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
|
|
1116
|
+
3. \u5982\u679C\u7528\u6237\u7684\u8981\u6C42\u4E0D\u6E05\u6670\uFF0C\u4E00\u5B9A\u8981\u8BE2\u95EE\u7528\u6237\uFF0C\u786E\u5B9A\u7EC6\u8282
|
|
1137
1117
|
|
|
1138
|
-
|
|
1139
|
-
- \
|
|
1140
|
-
- \
|
|
1141
|
-
- \
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
};
|
|
1151
|
-
AUTONOMOUS_MODE = {
|
|
1152
|
-
name: "autonomous",
|
|
1153
|
-
description: "A\u89C4\u5212\uFF0CB\u81EA\u4E3B\u6267\u884C\uFF08\u9002\u5408\u80FD\u529B\u5F3A\u7684\u6A21\u578B\uFF09",
|
|
1154
|
-
coderHasTools: true,
|
|
1155
|
-
coderToolNames: ["read-file", "write-file", "edit-file", "bash", "glob", "grep", "memo"],
|
|
1156
|
-
coderSystemPrompt: `${CODER_IDENTITY}`
|
|
1157
|
-
};
|
|
1158
|
-
CONTROLLED_MODE = {
|
|
1159
|
-
name: "controlled",
|
|
1160
|
-
description: "A\u5168\u6743\u7BA1\u7406\uFF0CB\u53EA\u8FD4\u56DE\u4EE3\u7801",
|
|
1161
|
-
coderHasTools: false,
|
|
1162
|
-
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"
|
|
1163
|
-
};
|
|
1164
|
-
}
|
|
1165
|
-
});
|
|
1166
|
-
|
|
1167
|
-
// src/core/dual-agent/orchestrator.ts
|
|
1168
|
-
var SEND_TO_CODER_TOOL, Orchestrator;
|
|
1169
|
-
var init_orchestrator = __esm({
|
|
1170
|
-
"src/core/dual-agent/orchestrator.ts"() {
|
|
1171
|
-
"use strict";
|
|
1172
|
-
init_client();
|
|
1173
|
-
init_loader();
|
|
1174
|
-
init_conversation();
|
|
1175
|
-
init_permission();
|
|
1176
|
-
init_coder();
|
|
1177
|
-
init_modes();
|
|
1178
|
-
SEND_TO_CODER_TOOL = {
|
|
1179
|
-
type: "function",
|
|
1180
|
-
function: {
|
|
1181
|
-
name: "send-to-coder",
|
|
1182
|
-
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",
|
|
1183
|
-
parameters: {
|
|
1184
|
-
type: "object",
|
|
1185
|
-
properties: {
|
|
1186
|
-
task: {
|
|
1187
|
-
type: "string",
|
|
1188
|
-
description: "\u8981\u53D1\u9001\u7ED9\u7F16\u7801 Agent \u7684\u4EFB\u52A1\u63CF\u8FF0\uFF0C\u5305\u542B\u76F8\u5173\u4EE3\u7801\u4E0A\u4E0B\u6587"
|
|
1189
|
-
}
|
|
1190
|
-
},
|
|
1191
|
-
required: ["task"]
|
|
1192
|
-
}
|
|
1193
|
-
}
|
|
1194
|
-
};
|
|
1195
|
-
Orchestrator = class {
|
|
1196
|
-
conversation;
|
|
1197
|
-
orchestratorClient;
|
|
1198
|
-
coderClient;
|
|
1199
|
-
registry;
|
|
1200
|
-
config;
|
|
1201
|
-
mode;
|
|
1202
|
-
baseSystemPrompt;
|
|
1203
|
-
memoStore;
|
|
1204
|
-
constructor(registry, config, systemPrompt, memoStore) {
|
|
1205
|
-
this.registry = registry;
|
|
1206
|
-
this.config = config;
|
|
1207
|
-
this.mode = config.collaboration;
|
|
1208
|
-
this.baseSystemPrompt = systemPrompt;
|
|
1209
|
-
this.memoStore = memoStore;
|
|
1210
|
-
const orchConfig = resolveModelConfig(config, "orchestrator");
|
|
1211
|
-
this.orchestratorClient = createLLMClient({
|
|
1212
|
-
apiKey: orchConfig.api_key,
|
|
1213
|
-
baseURL: orchConfig.base_url,
|
|
1214
|
-
model: orchConfig.model,
|
|
1215
|
-
temperature: orchConfig.temperature,
|
|
1216
|
-
maxTokens: orchConfig.max_tokens
|
|
1217
|
-
});
|
|
1218
|
-
const coderConfig = resolveModelConfig(config, "coder");
|
|
1219
|
-
this.coderClient = createLLMClient({
|
|
1220
|
-
apiKey: coderConfig.api_key,
|
|
1221
|
-
baseURL: coderConfig.base_url,
|
|
1222
|
-
model: coderConfig.model,
|
|
1223
|
-
temperature: coderConfig.temperature,
|
|
1224
|
-
maxTokens: coderConfig.max_tokens
|
|
1225
|
-
});
|
|
1226
|
-
this.conversation = new Conversation();
|
|
1227
|
-
this.conversation.setSystemPrompt(this.buildSystemPrompt());
|
|
1228
|
-
}
|
|
1229
|
-
/**
|
|
1230
|
-
* 动态获取调度者的工具列表:注册表工具 + send-to-coder
|
|
1231
|
-
*/
|
|
1232
|
-
getTools() {
|
|
1233
|
-
return [
|
|
1234
|
-
...this.registry.toToolDefinitions(),
|
|
1235
|
-
SEND_TO_CODER_TOOL
|
|
1236
|
-
];
|
|
1237
|
-
}
|
|
1238
|
-
/**
|
|
1239
|
-
* 构建调度者的系统提示词(包含当前协作模式)
|
|
1240
|
-
*/
|
|
1241
|
-
buildSystemPrompt() {
|
|
1242
|
-
const modeInfo = getMode(this.mode);
|
|
1243
|
-
return `${this.baseSystemPrompt}
|
|
1244
|
-
|
|
1245
|
-
# \u4F60\u662F\u8C03\u5EA6\u8005 Agent
|
|
1246
|
-
|
|
1247
|
-
\u534F\u4F5C\u6A21\u5F0F\uFF1A${modeInfo.name} - ${modeInfo.description}
|
|
1248
|
-
|
|
1249
|
-
\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 \u8BB0\u5165 memo \u2192 \u59D4\u6D3E\u7F16\u7801 Agent\u3002
|
|
1250
|
-
|
|
1251
|
-
## \u6838\u5FC3\u6D41\u7A0B
|
|
1252
|
-
|
|
1253
|
-
1. **\u8BC4\u4F30**\uFF1A\u4EFB\u52A1\u662F\u5426\u9700\u8981\u4E86\u89E3\u73B0\u6709\u4EE3\u7801\uFF1F
|
|
1254
|
-
- \u9700\u8981\u4FEE\u6539\u73B0\u6709\u6587\u4EF6\u3001\u9700\u8981\u4E0E\u73B0\u6709\u4EE3\u7801\u98CE\u683C\u4E00\u81F4\u3001\u9700\u8981\u7406\u89E3\u4F9D\u8D56\u5173\u7CFB \u2192 \u5148\u6536\u96C6
|
|
1255
|
-
- \u9700\u6C42\u5B8C\u5168\u81EA\u5305\u542B\u3001\u76EE\u6807\u8DEF\u5F84\u5DF2\u660E\u786E \u2192 \u8DF3\u5230\u7B2C 3 \u6B65
|
|
1256
|
-
|
|
1257
|
-
2. **\u6536\u96C6\u4E0A\u4E0B\u6587**\uFF08\u9AD8\u6548\uFF0C\u4E0D\u91CD\u590D\uFF09
|
|
1258
|
-
- \u7528 glob/grep \u5B9A\u4F4D\uFF08\u4E00\u6B21\u5C31\u591F\uFF0C\u4E0D\u8981\u540C\u4E00\u76EE\u6807\u67E5\u591A\u6B21\uFF09
|
|
1259
|
-
- \u7528 read-file \u6216 spawn-agents \u5E76\u884C\u8BFB\u53D6\u5173\u952E\u6587\u4EF6
|
|
1260
|
-
- **\u5FC5\u987B memo write \u8BB0\u5F55\u53D1\u73B0**\uFF0Ckey \u7528\u6709\u610F\u4E49\u7684\u540D\u79F0
|
|
1261
|
-
\u4F8B\uFF1Amemo write key="demo-structure" value="demo/ \u4E0B\u6709 weather.html\uFF08\u5929\u6C14\u7EC4\u4EF6\u793A\u4F8B\uFF09\uFF0C\u4F7F\u7528\u7EAF HTML+CSS+JS \u5355\u6587\u4EF6\u7ED3\u6784"
|
|
1262
|
-
- \u7CBE\u70BC\u6458\u8981\uFF0C\u4E0D\u8981\u590D\u5236\u6574\u4E2A\u6587\u4EF6\u5185\u5BB9
|
|
1263
|
-
|
|
1264
|
-
3. **\u59D4\u6D3E\u7F16\u7801**\uFF1Asend-to-coder
|
|
1265
|
-
- task \u4E2D\u5199\u6E05\uFF1A\u505A\u4EC0\u4E48 + \u76EE\u6807\u8DEF\u5F84 + \u5F15\u7528 memo key
|
|
1266
|
-
\u4F8B\uFF1Atask="\u5728 demo/newyear.html \u521B\u5EFA\u9A6C\u5E74\u6625\u8282\u795D\u798F\u7F51\u9875\u3002\u9879\u76EE\u73B0\u6709\u7ED3\u6784\u89C1 memo key 'demo-structure'\u3002\u8981\u6C42\uFF1A\u5355\u6587\u4EF6 HTML\uFF0C\u5305\u542B\u5185\u8054 CSS \u548C JS\uFF0C\u4E2D\u6587\u5185\u5BB9\u3002"
|
|
1267
|
-
- \u6BCF\u6B21\u53EA\u53D1\u4E00\u4E2A\u5177\u4F53\u6B65\u9AA4
|
|
1268
|
-
|
|
1269
|
-
4. **\u8FED\u4EE3/\u9A8C\u8BC1**\uFF1A\u9700\u8981\u65F6\u7EE7\u7EED\u59D4\u6D3E\u6216\u7528 bash \u8FD0\u884C\u9A8C\u8BC1
|
|
1270
|
-
|
|
1271
|
-
## \u91CD\u8981
|
|
1272
|
-
|
|
1273
|
-
- memo \u662F\u4F60\u4E0E\u7F16\u7801 Agent \u7684\u5171\u4EAB\u8BB0\u5FC6\uFF0C\u662F\u534F\u4F5C\u7684\u6838\u5FC3\u6865\u6881
|
|
1274
|
-
- \u4E0D\u8981\u91CD\u590D\u63A2\u7D22\uFF08glob \u67E5\u8FC7\u5C31\u4E0D\u8981\u518D bash ls \u540C\u4E00\u76EE\u5F55\uFF09
|
|
1275
|
-
- bash \u7528\u4E8E\u6267\u884C\u547D\u4EE4\uFF08\u6784\u5EFA\u3001\u6D4B\u8BD5\uFF09\uFF0C\u4E0D\u9700\u8981\u59D4\u6D3E
|
|
1276
|
-
- \u5B8C\u6210\u6240\u6709\u6B65\u9AA4\u540E\u7B80\u8981\u544A\u77E5\u7528\u6237\u7ED3\u679C`;
|
|
1277
|
-
}
|
|
1278
|
-
/**
|
|
1279
|
-
* 切换协作模式
|
|
1280
|
-
*/
|
|
1281
|
-
setMode(mode) {
|
|
1282
|
-
this.mode = mode;
|
|
1283
|
-
this.conversation.setSystemPrompt(this.buildSystemPrompt());
|
|
1284
|
-
}
|
|
1285
|
-
/**
|
|
1286
|
-
* 执行用户请求
|
|
1287
|
-
*/
|
|
1288
|
-
async run(userMessage, callbacks = {}) {
|
|
1289
|
-
callbacks.onModeInfo?.(this.mode);
|
|
1290
|
-
this.conversation.addUserMessage(userMessage);
|
|
1291
|
-
let lastContent = "";
|
|
1292
|
-
while (true) {
|
|
1293
|
-
const tools = this.getTools();
|
|
1294
|
-
const assistantMsg = await this.orchestratorClient.chatStream(
|
|
1295
|
-
this.conversation.getMessages(),
|
|
1296
|
-
tools,
|
|
1297
|
-
callbacks
|
|
1298
|
-
);
|
|
1299
|
-
this.conversation.addAssistantMessage(assistantMsg);
|
|
1300
|
-
if (!assistantMsg.tool_calls || assistantMsg.tool_calls.length === 0) {
|
|
1301
|
-
lastContent = assistantMsg.content || "";
|
|
1302
|
-
break;
|
|
1303
|
-
}
|
|
1304
|
-
for (const toolCall of assistantMsg.tool_calls) {
|
|
1305
|
-
const toolName = toolCall.function.name;
|
|
1306
|
-
let params;
|
|
1307
|
-
try {
|
|
1308
|
-
params = JSON.parse(toolCall.function.arguments);
|
|
1309
|
-
} catch {
|
|
1310
|
-
this.conversation.addToolResult(toolCall.id, "\u53C2\u6570\u89E3\u6790\u5931\u8D25\uFF1A\u65E0\u6548\u7684 JSON");
|
|
1311
|
-
continue;
|
|
1312
|
-
}
|
|
1313
|
-
try {
|
|
1314
|
-
if (toolName === "send-to-coder") {
|
|
1315
|
-
const task = params["task"];
|
|
1316
|
-
const coderResponse = await this.invokeCoder(task, callbacks);
|
|
1317
|
-
this.conversation.addToolResult(toolCall.id, coderResponse);
|
|
1318
|
-
continue;
|
|
1319
|
-
}
|
|
1320
|
-
const permLevel = this.registry.getPermissionLevel(toolName);
|
|
1321
|
-
if (permLevel === "deny") {
|
|
1322
|
-
callbacks.onDenied?.(toolName);
|
|
1323
|
-
this.conversation.addToolResult(toolCall.id, `\u5DE5\u5177 "${toolName}" \u5DF2\u88AB\u7981\u6B62\u6267\u884C`);
|
|
1324
|
-
continue;
|
|
1325
|
-
}
|
|
1326
|
-
if (permLevel === "auto") {
|
|
1327
|
-
callbacks.onToolExecuting?.(toolName, params);
|
|
1328
|
-
}
|
|
1329
|
-
if (permLevel === "confirm") {
|
|
1330
|
-
const confirmResult = await confirmExecution(toolName, params);
|
|
1331
|
-
if (!confirmResult.approved) {
|
|
1332
|
-
callbacks.onDenied?.(toolName, confirmResult.feedback);
|
|
1333
|
-
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";
|
|
1334
|
-
this.conversation.addToolResult(toolCall.id, denyMsg);
|
|
1335
|
-
continue;
|
|
1336
|
-
}
|
|
1337
|
-
}
|
|
1338
|
-
const result = await this.registry.execute(toolName, params, this.config.max_tool_output);
|
|
1339
|
-
callbacks.onToolResult?.(toolName, result.content, result.truncated ?? false);
|
|
1340
|
-
this.conversation.addToolResult(toolCall.id, result.content);
|
|
1341
|
-
} catch (err) {
|
|
1342
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1343
|
-
this.conversation.addToolResult(toolCall.id, `\u5DE5\u5177\u6267\u884C\u5F02\u5E38\uFF1A${msg}`);
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
if (assistantMsg.content) {
|
|
1347
|
-
lastContent = assistantMsg.content;
|
|
1348
|
-
}
|
|
1349
|
-
}
|
|
1350
|
-
return lastContent;
|
|
1351
|
-
}
|
|
1352
|
-
/**
|
|
1353
|
-
* 调用编码者 Agent
|
|
1354
|
-
*/
|
|
1355
|
-
async invokeCoder(task, callbacks) {
|
|
1356
|
-
callbacks.onCoderStart?.();
|
|
1357
|
-
const modeInfo = getMode(this.mode);
|
|
1358
|
-
let coderTools = [];
|
|
1359
|
-
if (modeInfo.coderHasTools && modeInfo.coderToolNames) {
|
|
1360
|
-
coderTools = this.registry.toToolDefinitions(modeInfo.coderToolNames);
|
|
1361
|
-
}
|
|
1362
|
-
let taskWithMemo = task;
|
|
1363
|
-
if (this.memoStore) {
|
|
1364
|
-
const index = this.memoStore.buildIndex();
|
|
1365
|
-
if (index) {
|
|
1366
|
-
taskWithMemo += `
|
|
1367
|
-
|
|
1368
|
-
[\u5171\u4EAB\u5907\u5FD8\u5F55]
|
|
1369
|
-
${index}`;
|
|
1370
|
-
}
|
|
1371
|
-
}
|
|
1372
|
-
taskWithMemo += "\n\n[\u91CD\u8981\uFF1A\u7ACB\u5373\u5F00\u59CB\u7F16\u7801\uFF0C\u4E0D\u8981\u63A2\u7D22\uFF0C\u4E0D\u8981\u8F93\u51FA\u5206\u6790]";
|
|
1373
|
-
const coder = new Coder(
|
|
1374
|
-
this.coderClient,
|
|
1375
|
-
this.registry,
|
|
1376
|
-
this.config,
|
|
1377
|
-
modeInfo.coderSystemPrompt,
|
|
1378
|
-
coderTools
|
|
1379
|
-
);
|
|
1380
|
-
const response = await coder.execute(taskWithMemo, {
|
|
1381
|
-
onContent: callbacks.onContent,
|
|
1382
|
-
onToolCallStreaming: callbacks.onToolCallStreaming,
|
|
1383
|
-
onToolExecuting: callbacks.onToolExecuting,
|
|
1384
|
-
onToolResult: callbacks.onToolResult,
|
|
1385
|
-
onDenied: callbacks.onDenied
|
|
1386
|
-
});
|
|
1387
|
-
callbacks.onCoderEnd?.(response);
|
|
1388
|
-
return response;
|
|
1389
|
-
}
|
|
1390
|
-
/**
|
|
1391
|
-
* 获取对话历史
|
|
1392
|
-
*/
|
|
1393
|
-
getConversation() {
|
|
1394
|
-
return this.conversation;
|
|
1395
|
-
}
|
|
1396
|
-
};
|
|
1397
|
-
}
|
|
1398
|
-
});
|
|
1399
|
-
|
|
1400
|
-
// src/core/prompt/layers/core.ts
|
|
1401
|
-
import * as os2 from "os";
|
|
1402
|
-
function buildCorePrompt() {
|
|
1403
|
-
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
|
|
1404
|
-
|
|
1405
|
-
\u5DE5\u4F5C\u76EE\u5F55\uFF1A${process.cwd()}
|
|
1406
|
-
\u7CFB\u7EDF\uFF1A${os2.platform()} ${os2.arch()}
|
|
1407
|
-
|
|
1408
|
-
# \u5DE5\u5177\u4F7F\u7528\u539F\u5219
|
|
1409
|
-
|
|
1410
|
-
\u4F60\u6709\u4EE5\u4E0B\u5DE5\u5177\u53EF\u7528\uFF0C\u8BF7\u6839\u636E\u4EFB\u52A1\u9009\u62E9\u6700\u5408\u9002\u7684\u5DE5\u5177\uFF1A
|
|
1411
|
-
|
|
1412
|
-
- **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
|
|
1413
|
-
- **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\u3002old_string \u5FC5\u987B\u552F\u4E00\u5339\u914D\uFF0C\u5339\u914D\u5931\u8D25\u65F6\u63D0\u4F9B\u66F4\u591A\u4E0A\u4E0B\u6587\u4F7F\u5176\u552F\u4E00\u3002
|
|
1414
|
-
- **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
|
|
1415
|
-
- **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
|
|
1416
|
-
- **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
|
|
1417
|
-
- **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
|
|
1418
|
-
|
|
1419
|
-
\u5173\u952E\u89C4\u5219\uFF1A
|
|
1420
|
-
- \u4FEE\u6539\u4EE3\u7801\u524D\u5148\u7528 read-file \u9605\u8BFB\u76F8\u5173\u6587\u4EF6\uFF0C\u7406\u89E3\u73B0\u6709\u903B\u8F91
|
|
1421
|
-
- \u4F18\u5148 edit-file \u7F16\u8F91\u5DF2\u6709\u6587\u4EF6\uFF0C\u800C\u975E write-file \u91CD\u5199
|
|
1422
|
-
- \u4E0D\u8981\u521B\u5EFA\u4E0D\u5FC5\u8981\u7684\u65B0\u6587\u4EF6\uFF0C\u4F18\u5148\u5728\u73B0\u6709\u6587\u4EF6\u4E2D\u4FEE\u6539
|
|
1423
|
-
- \u53EA\u505A\u5FC5\u8981\u7684\u6700\u5C0F\u6539\u52A8\uFF0C\u4E0D\u505A\u989D\u5916"\u6539\u8FDB"
|
|
1424
|
-
- \u4E0D\u8981\u6DFB\u52A0\u7528\u6237\u672A\u8981\u6C42\u7684\u6CE8\u91CA\u3001\u6587\u6863\u3001\u7C7B\u578B\u6CE8\u89E3
|
|
1425
|
-
- \u4E0D\u8981\u5F15\u5165\u5B89\u5168\u6F0F\u6D1E\uFF08\u6CE8\u5165\u3001XSS\u3001SQL \u6CE8\u5165\u7B49 OWASP Top 10\uFF09
|
|
1426
|
-
- \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
|
|
1427
|
-
|
|
1428
|
-
# \u4EA4\u4E92\u98CE\u683C
|
|
1429
|
-
|
|
1430
|
-
- \u4FDD\u6301\u6280\u672F\u5BA2\u89C2\u6027\uFF0C\u57FA\u4E8E\u4E8B\u5B9E\u56DE\u7B54\uFF0C\u4E0D\u8FC7\u5EA6\u8D5E\u540C\u6216\u606D\u7EF4\u7528\u6237\uFF0C\u5FC5\u8981\u65F6\u76F4\u63A5\u6307\u51FA\u95EE\u9898
|
|
1431
|
-
- \u4E0D\u786E\u5B9A\u65F6\u5148\u8C03\u67E5\u9A8C\u8BC1\uFF0C\u800C\u975E\u76F4\u89C9\u6027\u5730\u786E\u8BA4\u7528\u6237\u7684\u5047\u8BBE
|
|
1432
|
-
- \u4E0D\u8981\u7ED9\u51FA\u65F6\u95F4\u9884\u4F30\uFF08"\u5927\u6982\u9700\u8981\u51E0\u5206\u949F"\u4E4B\u7C7B\uFF09
|
|
1433
|
-
- \u56DE\u590D\u7B80\u6D01\uFF0C\u76F4\u63A5\u7ED9\u7ED3\u679C`;
|
|
1434
|
-
}
|
|
1435
|
-
var init_core = __esm({
|
|
1436
|
-
"src/core/prompt/layers/core.ts"() {
|
|
1437
|
-
"use strict";
|
|
1438
|
-
}
|
|
1439
|
-
});
|
|
1440
|
-
|
|
1441
|
-
// src/core/prompt/layers/planning.ts
|
|
1442
|
-
function buildPlanningPrompt() {
|
|
1443
|
-
return `# \u5DE5\u4F5C\u65B9\u6CD5
|
|
1444
|
-
|
|
1445
|
-
\u5904\u7406\u7F16\u7A0B\u4EFB\u52A1\u65F6\uFF1A
|
|
1446
|
-
1. \u5148\u7528 read-file / grep / glob \u9605\u8BFB\u76F8\u5173\u4EE3\u7801\uFF0C\u7406\u89E3\u73B0\u6709\u903B\u8F91\u548C\u4E0A\u4E0B\u6587
|
|
1447
|
-
2. \u786E\u5B9A\u65B9\u6848\u540E\u76F4\u63A5\u5B9E\u65BD\uFF0C\u4E0D\u8981\u957F\u7BC7\u89E3\u91CA\u8BA1\u5212
|
|
1448
|
-
3. \u6539\u5B8C\u5373\u6B62\uFF0C\u4E0D\u8981\u81EA\u884C"\u9A8C\u8BC1"\u6216"\u6D4B\u8BD5"
|
|
1449
|
-
|
|
1450
|
-
\u591A\u6B65\u4EFB\u52A1\u7BA1\u7406\uFF1A
|
|
1451
|
-
- \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
|
|
1452
|
-
- \u5F00\u59CB\u6B65\u9AA4\u524D\u6807\u8BB0 in-progress\uFF0C\u5B8C\u6210\u540E\u6807\u8BB0 completed
|
|
1453
|
-
- \u6BCF\u6B65\u5B8C\u6210\u540E\u68C0\u67E5\u8BA1\u5212\uFF0C\u51B3\u5B9A\u4E0B\u4E00\u6B65
|
|
1454
|
-
|
|
1455
|
-
\u4EE3\u7801\u8D28\u91CF\uFF1A
|
|
1456
|
-
- \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
|
|
1457
|
-
- \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
|
|
1458
|
-
- \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
|
|
1459
|
-
- \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
|
|
1460
|
-
- \u4E09\u884C\u76F8\u4F3C\u4EE3\u7801\u4F18\u4E8E\u4E00\u4E2A\u8FC7\u65E9\u7684\u62BD\u8C61`;
|
|
1461
|
-
}
|
|
1462
|
-
var init_planning = __esm({
|
|
1463
|
-
"src/core/prompt/layers/planning.ts"() {
|
|
1464
|
-
"use strict";
|
|
1118
|
+
\u591A\u6B65\u4EFB\u52A1\u7BA1\u7406\uFF1A
|
|
1119
|
+
- \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
|
|
1120
|
+
- \u5F00\u59CB\u6B65\u9AA4\u524D\u6807\u8BB0 in-progress\uFF0C\u5B8C\u6210\u540E\u6807\u8BB0 completed
|
|
1121
|
+
- \u6BCF\u6B65\u5B8C\u6210\u540E\u68C0\u67E5\u8BA1\u5212\uFF0C\u51B3\u5B9A\u4E0B\u4E00\u6B65
|
|
1122
|
+
|
|
1123
|
+
\u4EE3\u7801\u8D28\u91CF\uFF1A
|
|
1124
|
+
- \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
|
|
1125
|
+
- \u4E0D\u8981\u7559\u4E0BTODO\u7136\u540E\u653E\u7740\u4E0D\u7BA1`;
|
|
1126
|
+
}
|
|
1127
|
+
var init_planning = __esm({
|
|
1128
|
+
"src/core/prompt/layers/planning.ts"() {
|
|
1129
|
+
"use strict";
|
|
1465
1130
|
}
|
|
1466
1131
|
});
|
|
1467
1132
|
|
|
@@ -1481,7 +1146,7 @@ function buildParallelPrompt() {
|
|
|
1481
1146
|
\u6B63\u786E\uFF1Aspawn-agents \u540C\u65F6\u8BFB auth controller\u3001auth service\u3001auth middleware\u3001auth types
|
|
1482
1147
|
\u9519\u8BEF\uFF1A\u5148 read-file controller\uFF0C\u518D read-file service\uFF0C\u518D read-file middleware...
|
|
1483
1148
|
|
|
1484
|
-
\u6BCF\u4E2A\u5B50 Agent \u6709\u72EC\u7ACB\u5BF9\u8BDD\uFF0C\u9ED8\u8BA4\u53EF\u7528 read-file\u3001glob\u3001grep\
|
|
1149
|
+
\u6BCF\u4E2A\u5B50 Agent \u6709\u72EC\u7ACB\u5BF9\u8BDD\uFF0C\u9ED8\u8BA4\u53EF\u7528 read-file\u3001glob\u3001grep\u3002`;
|
|
1485
1150
|
}
|
|
1486
1151
|
var init_parallel = __esm({
|
|
1487
1152
|
"src/core/prompt/layers/parallel.ts"() {
|
|
@@ -1514,12 +1179,30 @@ var init_git = __esm({
|
|
|
1514
1179
|
}
|
|
1515
1180
|
});
|
|
1516
1181
|
|
|
1182
|
+
// src/core/prompt/layers/agents.ts
|
|
1183
|
+
function buildAgentsPrompt(agents) {
|
|
1184
|
+
if (agents.length === 0) return null;
|
|
1185
|
+
const agentList = agents.map((a) => `- **${a.name}**\uFF1A${a.description}`).join("\n");
|
|
1186
|
+
return `# \u5B50 Agent
|
|
1187
|
+
|
|
1188
|
+
\u4F60\u53EF\u4EE5\u901A\u8FC7 dispatch \u5DE5\u5177\u8C03\u5EA6\u4EE5\u4E0B\u4E13\u7528\u5B50 Agent\uFF1A
|
|
1189
|
+
|
|
1190
|
+
${agentList}
|
|
1191
|
+
|
|
1192
|
+
\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`;
|
|
1193
|
+
}
|
|
1194
|
+
var init_agents = __esm({
|
|
1195
|
+
"src/core/prompt/layers/agents.ts"() {
|
|
1196
|
+
"use strict";
|
|
1197
|
+
}
|
|
1198
|
+
});
|
|
1199
|
+
|
|
1517
1200
|
// src/core/prompt/layers/project.ts
|
|
1518
|
-
import * as
|
|
1519
|
-
import * as
|
|
1201
|
+
import * as fs7 from "fs/promises";
|
|
1202
|
+
import * as path7 from "path";
|
|
1520
1203
|
async function buildProjectPrompt() {
|
|
1521
1204
|
try {
|
|
1522
|
-
const content = await
|
|
1205
|
+
const content = await fs7.readFile(path7.resolve("ZENCODE.md"), "utf-8");
|
|
1523
1206
|
return content.trim() || null;
|
|
1524
1207
|
} catch {
|
|
1525
1208
|
return null;
|
|
@@ -1529,8 +1212,8 @@ async function loadUserPrompts(paths) {
|
|
|
1529
1212
|
const prompts = [];
|
|
1530
1213
|
for (const p of paths) {
|
|
1531
1214
|
try {
|
|
1532
|
-
const resolved = p.startsWith("~") ?
|
|
1533
|
-
const content = await
|
|
1215
|
+
const resolved = p.startsWith("~") ? path7.join(process.env["HOME"] || process.env["USERPROFILE"] || "", p.slice(1)) : path7.resolve(p);
|
|
1216
|
+
const content = await fs7.readFile(resolved, "utf-8");
|
|
1534
1217
|
if (content.trim()) {
|
|
1535
1218
|
prompts.push(content.trim());
|
|
1536
1219
|
}
|
|
@@ -1546,16 +1229,16 @@ var init_project = __esm({
|
|
|
1546
1229
|
});
|
|
1547
1230
|
|
|
1548
1231
|
// src/core/prompt/builder.ts
|
|
1549
|
-
import * as
|
|
1232
|
+
import * as fs8 from "fs";
|
|
1550
1233
|
function isGitRepo() {
|
|
1551
1234
|
try {
|
|
1552
|
-
|
|
1235
|
+
fs8.statSync(".git");
|
|
1553
1236
|
return true;
|
|
1554
1237
|
} catch {
|
|
1555
1238
|
return false;
|
|
1556
1239
|
}
|
|
1557
1240
|
}
|
|
1558
|
-
async function buildPrompt(config) {
|
|
1241
|
+
async function buildPrompt(config, agents) {
|
|
1559
1242
|
const layers = [];
|
|
1560
1243
|
layers.push(buildCorePrompt());
|
|
1561
1244
|
if (config.features.planning_layer === "on") {
|
|
@@ -1568,6 +1251,12 @@ async function buildPrompt(config) {
|
|
|
1568
1251
|
if (config.features.parallel_agents === "on") {
|
|
1569
1252
|
layers.push(buildParallelPrompt());
|
|
1570
1253
|
}
|
|
1254
|
+
if (agents && agents.length > 0) {
|
|
1255
|
+
const agentsPrompt = buildAgentsPrompt(agents);
|
|
1256
|
+
if (agentsPrompt) {
|
|
1257
|
+
layers.push(agentsPrompt);
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1571
1260
|
const projectPrompt = await buildProjectPrompt();
|
|
1572
1261
|
if (projectPrompt) {
|
|
1573
1262
|
layers.push(projectPrompt);
|
|
@@ -1586,6 +1275,7 @@ var init_builder = __esm({
|
|
|
1586
1275
|
init_planning();
|
|
1587
1276
|
init_parallel();
|
|
1588
1277
|
init_git();
|
|
1278
|
+
init_agents();
|
|
1589
1279
|
init_project();
|
|
1590
1280
|
}
|
|
1591
1281
|
});
|
|
@@ -1640,69 +1330,267 @@ var init_todo_store = __esm({
|
|
|
1640
1330
|
}
|
|
1641
1331
|
});
|
|
1642
1332
|
|
|
1643
|
-
// src/core/
|
|
1644
|
-
var
|
|
1645
|
-
var
|
|
1646
|
-
"src/core/
|
|
1333
|
+
// src/core/sub-agents/registry.ts
|
|
1334
|
+
var SubAgentConfigRegistry;
|
|
1335
|
+
var init_registry2 = __esm({
|
|
1336
|
+
"src/core/sub-agents/registry.ts"() {
|
|
1647
1337
|
"use strict";
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
write(key, content, author = "agent") {
|
|
1653
|
-
const trimmed = content.slice(0, MAX_CONTENT_LENGTH);
|
|
1654
|
-
const entry = { key, content: trimmed, author, updatedAt: Date.now() };
|
|
1655
|
-
if (!this.entries.has(key) && this.entries.size >= MAX_ENTRIES) {
|
|
1656
|
-
let oldest = null;
|
|
1657
|
-
let oldestTime = Infinity;
|
|
1658
|
-
for (const [k, v] of this.entries) {
|
|
1659
|
-
if (v.updatedAt < oldestTime) {
|
|
1660
|
-
oldestTime = v.updatedAt;
|
|
1661
|
-
oldest = k;
|
|
1662
|
-
}
|
|
1663
|
-
}
|
|
1664
|
-
if (oldest) this.entries.delete(oldest);
|
|
1665
|
-
}
|
|
1666
|
-
this.entries.set(key, entry);
|
|
1667
|
-
return entry;
|
|
1338
|
+
SubAgentConfigRegistry = class {
|
|
1339
|
+
configs = /* @__PURE__ */ new Map();
|
|
1340
|
+
register(config) {
|
|
1341
|
+
this.configs.set(config.name, config);
|
|
1668
1342
|
}
|
|
1669
|
-
|
|
1670
|
-
return this.
|
|
1343
|
+
get(name) {
|
|
1344
|
+
return this.configs.get(name);
|
|
1345
|
+
}
|
|
1346
|
+
has(name) {
|
|
1347
|
+
return this.configs.has(name);
|
|
1671
1348
|
}
|
|
1672
1349
|
list() {
|
|
1673
|
-
return [...this.
|
|
1674
|
-
key: e.key,
|
|
1675
|
-
author: e.author,
|
|
1676
|
-
preview: e.content.slice(0, 80)
|
|
1677
|
-
}));
|
|
1350
|
+
return [...this.configs.values()];
|
|
1678
1351
|
}
|
|
1679
|
-
|
|
1680
|
-
return this.
|
|
1352
|
+
listNames() {
|
|
1353
|
+
return [...this.configs.keys()];
|
|
1681
1354
|
}
|
|
1682
|
-
|
|
1683
|
-
|
|
1355
|
+
/**
|
|
1356
|
+
* 生成子 Agent 列表描述(用于 dispatch 工具的说明)
|
|
1357
|
+
*/
|
|
1358
|
+
buildAgentListDescription() {
|
|
1359
|
+
if (this.configs.size === 0) return "\u6682\u65E0\u53EF\u7528\u5B50 Agent";
|
|
1360
|
+
return [...this.configs.values()].map((s) => `- ${s.name}: ${s.description}`).join("\n");
|
|
1361
|
+
}
|
|
1362
|
+
};
|
|
1363
|
+
}
|
|
1364
|
+
});
|
|
1365
|
+
|
|
1366
|
+
// src/core/sub-agents/presets.ts
|
|
1367
|
+
var presetAgents;
|
|
1368
|
+
var init_presets = __esm({
|
|
1369
|
+
"src/core/sub-agents/presets.ts"() {
|
|
1370
|
+
"use strict";
|
|
1371
|
+
presetAgents = [
|
|
1372
|
+
{
|
|
1373
|
+
name: "reviewer",
|
|
1374
|
+
description: "\u4EE3\u7801\u5BA1\u67E5\uFF1A\u53D1\u73B0 bug\u3001\u5B89\u5168\u6F0F\u6D1E\u3001\u6027\u80FD\u95EE\u9898",
|
|
1375
|
+
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
|
|
1376
|
+
|
|
1377
|
+
\u5BA1\u67E5\u7EF4\u5EA6\uFF08\u6309\u4F18\u5148\u7EA7\uFF09\uFF1A
|
|
1378
|
+
1. \u6B63\u786E\u6027\uFF1A\u903B\u8F91\u9519\u8BEF\u3001\u8FB9\u754C\u6761\u4EF6\u3001\u7A7A\u503C/\u672A\u5B9A\u4E49\u5904\u7406
|
|
1379
|
+
2. \u5B89\u5168\uFF1A\u6CE8\u5165\u3001XSS\u3001\u654F\u611F\u4FE1\u606F\u6CC4\u9732\u3001\u6743\u9650\u68C0\u67E5
|
|
1380
|
+
3. \u6027\u80FD\uFF1A\u4E0D\u5FC5\u8981\u7684\u5FAA\u73AF\u3001\u5185\u5B58\u6CC4\u6F0F\u3001N+1 \u67E5\u8BE2
|
|
1381
|
+
4. \u53EF\u7EF4\u62A4\u6027\uFF1A\u547D\u540D\u3001\u91CD\u590D\u4EE3\u7801\u3001\u8FC7\u5EA6\u590D\u6742
|
|
1382
|
+
|
|
1383
|
+
\u8F93\u51FA\u683C\u5F0F\uFF1A
|
|
1384
|
+
- \u6BCF\u4E2A\u95EE\u9898\uFF1A\u6587\u4EF6\u8DEF\u5F84:\u884C\u53F7 + \u95EE\u9898\u63CF\u8FF0 + \u5EFA\u8BAE\u4FEE\u590D
|
|
1385
|
+
- \u6CA1\u6709\u95EE\u9898\u5C31\u8BF4\u6CA1\u6709\u95EE\u9898\uFF0C\u4E0D\u8981\u786C\u51D1
|
|
1386
|
+
- \u4E0D\u8981\u91CD\u5199\u4EE3\u7801\uFF0C\u53EA\u6307\u51FA\u95EE\u9898\u548C\u4FEE\u590D\u65B9\u5411`,
|
|
1387
|
+
tools: ["read-file", "glob", "grep"],
|
|
1388
|
+
max_turns: 10,
|
|
1389
|
+
timeout: 60
|
|
1390
|
+
},
|
|
1391
|
+
{
|
|
1392
|
+
name: "researcher",
|
|
1393
|
+
description: "\u4EE3\u7801\u5E93\u7814\u7A76\uFF1A\u6DF1\u5EA6\u5206\u6790\u67B6\u6784\u3001\u4F9D\u8D56\u548C\u5B9E\u73B0\u7EC6\u8282",
|
|
1394
|
+
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
|
|
1395
|
+
|
|
1396
|
+
\u5206\u6790\u65B9\u6CD5\uFF1A
|
|
1397
|
+
1. \u5148 glob \u4E86\u89E3\u6587\u4EF6\u7ED3\u6784
|
|
1398
|
+
2. grep \u641C\u7D22\u5173\u952E\u5165\u53E3\u70B9\u3001\u5BFC\u51FA\u3001\u4F9D\u8D56
|
|
1399
|
+
3. read-file \u9605\u8BFB\u6838\u5FC3\u6587\u4EF6
|
|
1400
|
+
|
|
1401
|
+
\u8F93\u51FA\u5185\u5BB9\uFF1A
|
|
1402
|
+
- \u6A21\u5757\u804C\u8D23\u548C\u8FB9\u754C
|
|
1403
|
+
- \u5173\u952E\u6587\u4EF6\u53CA\u5176\u4F5C\u7528
|
|
1404
|
+
- \u6570\u636E\u6D41\u548C\u8C03\u7528\u94FE
|
|
1405
|
+
- \u5916\u90E8\u4F9D\u8D56
|
|
1406
|
+
- \u5982\u6709\u7528\u6237\u5177\u4F53\u95EE\u9898\uFF0C\u9488\u5BF9\u6027\u56DE\u7B54`,
|
|
1407
|
+
tools: ["read-file", "glob", "grep"],
|
|
1408
|
+
max_turns: 15,
|
|
1409
|
+
timeout: 120
|
|
1410
|
+
},
|
|
1411
|
+
{
|
|
1412
|
+
name: "refactor",
|
|
1413
|
+
description: "\u91CD\u6784\u4E13\u5BB6\uFF1A\u5206\u6790\u4EE3\u7801\u7ED3\u6784\u5E76\u5B9E\u65BD\u91CD\u6784",
|
|
1414
|
+
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
|
|
1415
|
+
|
|
1416
|
+
\u91CD\u6784\u539F\u5219\uFF1A
|
|
1417
|
+
- \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
|
|
1418
|
+
- \u4FDD\u6301\u884C\u4E3A\u4E0D\u53D8\uFF0C\u4E0D\u6DFB\u52A0\u65B0\u529F\u80FD
|
|
1419
|
+
- \u6BCF\u6B21\u53EA\u505A\u4E00\u4E2A\u91CD\u6784\uFF0C\u4E0D\u8981\u540C\u65F6\u6539\u592A\u591A
|
|
1420
|
+
- \u4FEE\u6539\u524D\u5FC5\u987B read-file \u786E\u8BA4\u5F53\u524D\u5185\u5BB9
|
|
1421
|
+
|
|
1422
|
+
\u5E38\u89C1\u91CD\u6784\uFF1A
|
|
1423
|
+
- \u63D0\u53D6\u91CD\u590D\u4EE3\u7801\u4E3A\u51FD\u6570
|
|
1424
|
+
- \u7B80\u5316\u8FC7\u6DF1\u7684\u5D4C\u5957\uFF08\u63D0\u524D\u8FD4\u56DE\uFF09
|
|
1425
|
+
- \u62C6\u5206\u8FC7\u5927\u7684\u51FD\u6570
|
|
1426
|
+
- \u6539\u5584\u547D\u540D\u4F7F\u610F\u56FE\u66F4\u6E05\u6670`,
|
|
1427
|
+
tools: ["read-file", "write-file", "edit-file", "glob", "grep"],
|
|
1428
|
+
max_turns: 15,
|
|
1429
|
+
timeout: 120
|
|
1430
|
+
}
|
|
1431
|
+
];
|
|
1432
|
+
}
|
|
1433
|
+
});
|
|
1434
|
+
|
|
1435
|
+
// src/core/sub-agents/loader.ts
|
|
1436
|
+
import * as fs9 from "fs";
|
|
1437
|
+
import * as path8 from "path";
|
|
1438
|
+
import * as os3 from "os";
|
|
1439
|
+
import { parse as parseYaml2 } from "yaml";
|
|
1440
|
+
function loadAgentYaml(filePath) {
|
|
1441
|
+
try {
|
|
1442
|
+
const content = fs9.readFileSync(filePath, "utf-8");
|
|
1443
|
+
const parsed = parseYaml2(content);
|
|
1444
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
1445
|
+
if (!parsed["name"] || !parsed["prompt"] || !parsed["tools"]) return null;
|
|
1446
|
+
return {
|
|
1447
|
+
name: parsed["name"],
|
|
1448
|
+
description: parsed["description"] || "",
|
|
1449
|
+
prompt: parsed["prompt"],
|
|
1450
|
+
tools: parsed["tools"],
|
|
1451
|
+
max_turns: parsed["max_turns"],
|
|
1452
|
+
timeout: parsed["timeout"],
|
|
1453
|
+
model: parsed["model"]
|
|
1454
|
+
};
|
|
1455
|
+
} catch {
|
|
1456
|
+
return null;
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
function loadAgentsFromDir(dir) {
|
|
1460
|
+
try {
|
|
1461
|
+
if (!fs9.existsSync(dir)) return [];
|
|
1462
|
+
const files = fs9.readdirSync(dir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
|
|
1463
|
+
const agents = [];
|
|
1464
|
+
for (const file of files) {
|
|
1465
|
+
const agent = loadAgentYaml(path8.join(dir, file));
|
|
1466
|
+
if (agent) agents.push(agent);
|
|
1467
|
+
}
|
|
1468
|
+
return agents;
|
|
1469
|
+
} catch {
|
|
1470
|
+
return [];
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
function loadAllAgentConfigs() {
|
|
1474
|
+
const configMap = /* @__PURE__ */ new Map();
|
|
1475
|
+
for (const agent of presetAgents) {
|
|
1476
|
+
configMap.set(agent.name, agent);
|
|
1477
|
+
}
|
|
1478
|
+
const globalDir = path8.join(os3.homedir(), ".zencode", "agents");
|
|
1479
|
+
for (const agent of loadAgentsFromDir(globalDir)) {
|
|
1480
|
+
configMap.set(agent.name, agent);
|
|
1481
|
+
}
|
|
1482
|
+
const projectDir = path8.resolve(".zencode", "agents");
|
|
1483
|
+
for (const agent of loadAgentsFromDir(projectDir)) {
|
|
1484
|
+
configMap.set(agent.name, agent);
|
|
1485
|
+
}
|
|
1486
|
+
return [...configMap.values()];
|
|
1487
|
+
}
|
|
1488
|
+
var init_loader = __esm({
|
|
1489
|
+
"src/core/sub-agents/loader.ts"() {
|
|
1490
|
+
"use strict";
|
|
1491
|
+
init_presets();
|
|
1492
|
+
}
|
|
1493
|
+
});
|
|
1494
|
+
|
|
1495
|
+
// src/core/skills/registry.ts
|
|
1496
|
+
var SkillRegistry;
|
|
1497
|
+
var init_registry3 = __esm({
|
|
1498
|
+
"src/core/skills/registry.ts"() {
|
|
1499
|
+
"use strict";
|
|
1500
|
+
SkillRegistry = class {
|
|
1501
|
+
skills = /* @__PURE__ */ new Map();
|
|
1502
|
+
register(skill) {
|
|
1503
|
+
this.skills.set(skill.name, skill);
|
|
1504
|
+
}
|
|
1505
|
+
get(name) {
|
|
1506
|
+
return this.skills.get(name);
|
|
1507
|
+
}
|
|
1508
|
+
has(name) {
|
|
1509
|
+
return this.skills.has(name);
|
|
1510
|
+
}
|
|
1511
|
+
list() {
|
|
1512
|
+
return [...this.skills.values()];
|
|
1513
|
+
}
|
|
1514
|
+
listNames() {
|
|
1515
|
+
return [...this.skills.keys()];
|
|
1684
1516
|
}
|
|
1685
1517
|
/**
|
|
1686
|
-
*
|
|
1687
|
-
* 仅包含 key 列表和简短预览,占用极少 token
|
|
1518
|
+
* 展开 skill 的 prompt 模板,替换 $ARGS 为用户参数
|
|
1688
1519
|
*/
|
|
1689
|
-
|
|
1690
|
-
if (
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
)
|
|
1694
|
-
|
|
1520
|
+
expandPrompt(skill, args) {
|
|
1521
|
+
if (args && skill.prompt.includes("$ARGS")) {
|
|
1522
|
+
return skill.prompt.replace(/\$ARGS/g, args);
|
|
1523
|
+
}
|
|
1524
|
+
if (args) {
|
|
1525
|
+
return `${skill.prompt}
|
|
1526
|
+
|
|
1527
|
+
\u7528\u6237\u8865\u5145: ${args}`;
|
|
1528
|
+
}
|
|
1529
|
+
return skill.prompt;
|
|
1695
1530
|
}
|
|
1696
1531
|
};
|
|
1697
1532
|
}
|
|
1698
1533
|
});
|
|
1699
1534
|
|
|
1535
|
+
// src/core/skills/loader.ts
|
|
1536
|
+
import * as fs10 from "fs";
|
|
1537
|
+
import * as path9 from "path";
|
|
1538
|
+
import * as os4 from "os";
|
|
1539
|
+
import { parse as parseYaml3 } from "yaml";
|
|
1540
|
+
function loadSkillYaml(filePath) {
|
|
1541
|
+
try {
|
|
1542
|
+
const content = fs10.readFileSync(filePath, "utf-8");
|
|
1543
|
+
const parsed = parseYaml3(content);
|
|
1544
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
1545
|
+
if (!parsed["name"] || !parsed["prompt"]) return null;
|
|
1546
|
+
return {
|
|
1547
|
+
name: parsed["name"],
|
|
1548
|
+
description: parsed["description"] || "",
|
|
1549
|
+
prompt: parsed["prompt"]
|
|
1550
|
+
};
|
|
1551
|
+
} catch {
|
|
1552
|
+
return null;
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
function loadSkillsFromDir(dir) {
|
|
1556
|
+
try {
|
|
1557
|
+
if (!fs10.existsSync(dir)) return [];
|
|
1558
|
+
const files = fs10.readdirSync(dir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
|
|
1559
|
+
const skills = [];
|
|
1560
|
+
for (const file of files) {
|
|
1561
|
+
const skill = loadSkillYaml(path9.join(dir, file));
|
|
1562
|
+
if (skill) skills.push(skill);
|
|
1563
|
+
}
|
|
1564
|
+
return skills;
|
|
1565
|
+
} catch {
|
|
1566
|
+
return [];
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
function loadAllSkills() {
|
|
1570
|
+
const skillMap = /* @__PURE__ */ new Map();
|
|
1571
|
+
const globalDir = path9.join(os4.homedir(), ".zencode", "skills");
|
|
1572
|
+
for (const skill of loadSkillsFromDir(globalDir)) {
|
|
1573
|
+
skillMap.set(skill.name, skill);
|
|
1574
|
+
}
|
|
1575
|
+
const projectDir = path9.resolve(".zencode", "skills");
|
|
1576
|
+
for (const skill of loadSkillsFromDir(projectDir)) {
|
|
1577
|
+
skillMap.set(skill.name, skill);
|
|
1578
|
+
}
|
|
1579
|
+
return [...skillMap.values()];
|
|
1580
|
+
}
|
|
1581
|
+
var init_loader2 = __esm({
|
|
1582
|
+
"src/core/skills/loader.ts"() {
|
|
1583
|
+
"use strict";
|
|
1584
|
+
}
|
|
1585
|
+
});
|
|
1586
|
+
|
|
1700
1587
|
// src/core/sub-agent.ts
|
|
1701
1588
|
var DEFAULT_TIMEOUT_MS, SubAgent;
|
|
1702
1589
|
var init_sub_agent = __esm({
|
|
1703
1590
|
"src/core/sub-agent.ts"() {
|
|
1704
1591
|
"use strict";
|
|
1705
1592
|
init_conversation();
|
|
1593
|
+
init_read_tracker();
|
|
1706
1594
|
DEFAULT_TIMEOUT_MS = 12e4;
|
|
1707
1595
|
SubAgent = class {
|
|
1708
1596
|
client;
|
|
@@ -1712,8 +1600,7 @@ var init_sub_agent = __esm({
|
|
|
1712
1600
|
allowedTools;
|
|
1713
1601
|
maxTurns;
|
|
1714
1602
|
timeoutMs;
|
|
1715
|
-
|
|
1716
|
-
constructor(client, registry, config, task, allowedTools = ["read-file", "glob", "grep", "memo"], maxTurns = 10, timeoutMs = DEFAULT_TIMEOUT_MS, memoStore) {
|
|
1603
|
+
constructor(client, registry, config, task, allowedTools = ["read-file", "glob", "grep"], maxTurns = 10, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
1717
1604
|
this.client = client;
|
|
1718
1605
|
this.registry = registry;
|
|
1719
1606
|
this.config = config;
|
|
@@ -1721,7 +1608,6 @@ var init_sub_agent = __esm({
|
|
|
1721
1608
|
this.allowedTools = allowedTools.filter((t) => t !== "spawn-agents" && t !== "todo");
|
|
1722
1609
|
this.maxTurns = Math.min(maxTurns, 15);
|
|
1723
1610
|
this.timeoutMs = timeoutMs;
|
|
1724
|
-
this.memoStore = memoStore;
|
|
1725
1611
|
}
|
|
1726
1612
|
async run() {
|
|
1727
1613
|
return Promise.race([
|
|
@@ -1739,17 +1625,9 @@ var init_sub_agent = __esm({
|
|
|
1739
1625
|
}
|
|
1740
1626
|
async execute() {
|
|
1741
1627
|
const conversation = new Conversation();
|
|
1742
|
-
|
|
1628
|
+
const readTracker = new ReadTracker();
|
|
1629
|
+
const systemPrompt = `\u4F60\u662F ZenCode \u5B50 Agent\u3002\u4F60\u7684\u4EFB\u52A1\uFF1A${this.task}
|
|
1743
1630
|
\u5B8C\u6210\u540E\u76F4\u63A5\u8FD4\u56DE\u7ED3\u679C\uFF0C\u4E0D\u8981\u591A\u4F59\u89E3\u91CA\u3002`;
|
|
1744
|
-
if (this.memoStore) {
|
|
1745
|
-
const index = this.memoStore.buildIndex();
|
|
1746
|
-
if (index) {
|
|
1747
|
-
systemPrompt += `
|
|
1748
|
-
|
|
1749
|
-
[\u5171\u4EAB\u5907\u5FD8\u5F55 - \u53EF\u7528 memo read \u8BFB\u53D6\u8BE6\u60C5]
|
|
1750
|
-
${index}`;
|
|
1751
|
-
}
|
|
1752
|
-
}
|
|
1753
1631
|
conversation.setSystemPrompt(systemPrompt);
|
|
1754
1632
|
conversation.addUserMessage(this.task);
|
|
1755
1633
|
const tools = this.registry.toToolDefinitions(this.allowedTools);
|
|
@@ -1781,11 +1659,36 @@ ${index}`;
|
|
|
1781
1659
|
continue;
|
|
1782
1660
|
}
|
|
1783
1661
|
try {
|
|
1662
|
+
if (toolName === "edit-file") {
|
|
1663
|
+
const editPath = params["path"];
|
|
1664
|
+
if (!readTracker.hasRead(editPath)) {
|
|
1665
|
+
conversation.addToolResult(
|
|
1666
|
+
toolCall.id,
|
|
1667
|
+
`\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`
|
|
1668
|
+
);
|
|
1669
|
+
continue;
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
if (toolName === "write-file") {
|
|
1673
|
+
const warn = readTracker.checkWriteOverwrite(
|
|
1674
|
+
params["path"],
|
|
1675
|
+
params["overwrite"]
|
|
1676
|
+
);
|
|
1677
|
+
if (warn) {
|
|
1678
|
+
conversation.addToolResult(toolCall.id, warn);
|
|
1679
|
+
continue;
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1784
1682
|
const result = await this.registry.execute(
|
|
1785
1683
|
toolName,
|
|
1786
1684
|
params,
|
|
1787
1685
|
this.config.max_tool_output
|
|
1788
1686
|
);
|
|
1687
|
+
if (toolName === "read-file") {
|
|
1688
|
+
readTracker.markRead(params["path"]);
|
|
1689
|
+
} else if (toolName === "write-file") {
|
|
1690
|
+
readTracker.markWritten(params["path"]);
|
|
1691
|
+
}
|
|
1789
1692
|
conversation.addToolResult(toolCall.id, result.content);
|
|
1790
1693
|
} catch (err) {
|
|
1791
1694
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -1803,7 +1706,7 @@ ${index}`;
|
|
|
1803
1706
|
});
|
|
1804
1707
|
|
|
1805
1708
|
// src/tools/spawn-agents.ts
|
|
1806
|
-
function createSpawnAgentsTool(client, registry, config, tracker
|
|
1709
|
+
function createSpawnAgentsTool(client, registry, config, tracker) {
|
|
1807
1710
|
return {
|
|
1808
1711
|
name: "spawn-agents",
|
|
1809
1712
|
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",
|
|
@@ -1857,7 +1760,7 @@ function createSpawnAgentsTool(client, registry, config, tracker, memoStore) {
|
|
|
1857
1760
|
if (tools.length === 0) {
|
|
1858
1761
|
tools = DEFAULT_TOOLS.filter((t) => autoTools.includes(t));
|
|
1859
1762
|
}
|
|
1860
|
-
return new SubAgent(client, registry, config, task.description, tools, maxTurns
|
|
1763
|
+
return new SubAgent(client, registry, config, task.description, tools, maxTurns);
|
|
1861
1764
|
});
|
|
1862
1765
|
const wrappedRuns = agents.map(
|
|
1863
1766
|
(agent) => agent.run().then(
|
|
@@ -1898,7 +1801,7 @@ var init_spawn_agents = __esm({
|
|
|
1898
1801
|
"src/tools/spawn-agents.ts"() {
|
|
1899
1802
|
"use strict";
|
|
1900
1803
|
init_sub_agent();
|
|
1901
|
-
DEFAULT_TOOLS = ["read-file", "glob", "grep"
|
|
1804
|
+
DEFAULT_TOOLS = ["read-file", "glob", "grep"];
|
|
1902
1805
|
MAX_CONCURRENT = 10;
|
|
1903
1806
|
MAX_TURNS_LIMIT = 15;
|
|
1904
1807
|
}
|
|
@@ -2012,96 +1915,262 @@ var init_todo = __esm({
|
|
|
2012
1915
|
}
|
|
2013
1916
|
});
|
|
2014
1917
|
|
|
2015
|
-
// src/
|
|
2016
|
-
|
|
1918
|
+
// src/core/sub-agents/runner.ts
|
|
1919
|
+
var DEFAULT_MAX_TURNS, SubAgentRunner;
|
|
1920
|
+
var init_runner = __esm({
|
|
1921
|
+
"src/core/sub-agents/runner.ts"() {
|
|
1922
|
+
"use strict";
|
|
1923
|
+
init_conversation();
|
|
1924
|
+
init_permission();
|
|
1925
|
+
init_read_tracker();
|
|
1926
|
+
DEFAULT_MAX_TURNS = 15;
|
|
1927
|
+
SubAgentRunner = class {
|
|
1928
|
+
client;
|
|
1929
|
+
registry;
|
|
1930
|
+
config;
|
|
1931
|
+
agentConfig;
|
|
1932
|
+
constructor(client, registry, config, agentConfig) {
|
|
1933
|
+
this.client = client;
|
|
1934
|
+
this.registry = registry;
|
|
1935
|
+
this.config = config;
|
|
1936
|
+
this.agentConfig = agentConfig;
|
|
1937
|
+
}
|
|
1938
|
+
/**
|
|
1939
|
+
* 执行子 Agent 任务
|
|
1940
|
+
*/
|
|
1941
|
+
async execute(task, context, callbacks = {}) {
|
|
1942
|
+
const timeoutMs = (this.agentConfig.timeout ?? 120) * 1e3;
|
|
1943
|
+
const maxTurns = this.agentConfig.max_turns ?? DEFAULT_MAX_TURNS;
|
|
1944
|
+
return Promise.race([
|
|
1945
|
+
this.run(task, context, maxTurns, callbacks),
|
|
1946
|
+
this.timeout(timeoutMs)
|
|
1947
|
+
]);
|
|
1948
|
+
}
|
|
1949
|
+
timeout(ms) {
|
|
1950
|
+
return new Promise((_, reject) => {
|
|
1951
|
+
setTimeout(
|
|
1952
|
+
() => reject(new Error(`\u5B50 Agent "${this.agentConfig.name}" \u8D85\u65F6\uFF08${ms / 1e3}s\uFF09`)),
|
|
1953
|
+
ms
|
|
1954
|
+
);
|
|
1955
|
+
});
|
|
1956
|
+
}
|
|
1957
|
+
async run(task, context, maxTurns, callbacks) {
|
|
1958
|
+
const conversation = new Conversation();
|
|
1959
|
+
const readTracker = new ReadTracker();
|
|
1960
|
+
conversation.setSystemPrompt(this.agentConfig.prompt);
|
|
1961
|
+
let taskMessage = task;
|
|
1962
|
+
if (context) {
|
|
1963
|
+
taskMessage += `
|
|
1964
|
+
|
|
1965
|
+
[\u4E0A\u4E0B\u6587]
|
|
1966
|
+
${context}`;
|
|
1967
|
+
}
|
|
1968
|
+
conversation.addUserMessage(taskMessage);
|
|
1969
|
+
const tools = this.registry.toToolDefinitions(this.agentConfig.tools);
|
|
1970
|
+
let lastContent = "";
|
|
1971
|
+
for (let turn = 0; turn < maxTurns; turn++) {
|
|
1972
|
+
const assistantMsg = await this.client.chatStream(
|
|
1973
|
+
conversation.getMessages(),
|
|
1974
|
+
tools.length > 0 ? tools : void 0,
|
|
1975
|
+
callbacks
|
|
1976
|
+
);
|
|
1977
|
+
conversation.addAssistantMessage(assistantMsg);
|
|
1978
|
+
if (!assistantMsg.tool_calls || assistantMsg.tool_calls.length === 0) {
|
|
1979
|
+
lastContent = assistantMsg.content || "";
|
|
1980
|
+
break;
|
|
1981
|
+
}
|
|
1982
|
+
for (const toolCall of assistantMsg.tool_calls) {
|
|
1983
|
+
const toolName = toolCall.function.name;
|
|
1984
|
+
if (!this.agentConfig.tools.includes(toolName)) {
|
|
1985
|
+
conversation.addToolResult(
|
|
1986
|
+
toolCall.id,
|
|
1987
|
+
`\u5B50 Agent "${this.agentConfig.name}" \u4E0D\u5141\u8BB8\u4F7F\u7528\u5DE5\u5177 "${toolName}"`
|
|
1988
|
+
);
|
|
1989
|
+
continue;
|
|
1990
|
+
}
|
|
1991
|
+
let params;
|
|
1992
|
+
try {
|
|
1993
|
+
params = JSON.parse(toolCall.function.arguments);
|
|
1994
|
+
} catch {
|
|
1995
|
+
conversation.addToolResult(toolCall.id, "\u53C2\u6570\u89E3\u6790\u5931\u8D25\uFF1A\u65E0\u6548\u7684 JSON");
|
|
1996
|
+
continue;
|
|
1997
|
+
}
|
|
1998
|
+
try {
|
|
1999
|
+
if (toolName === "edit-file") {
|
|
2000
|
+
const editPath = params["path"];
|
|
2001
|
+
if (!readTracker.hasRead(editPath)) {
|
|
2002
|
+
conversation.addToolResult(
|
|
2003
|
+
toolCall.id,
|
|
2004
|
+
`\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`
|
|
2005
|
+
);
|
|
2006
|
+
continue;
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
if (toolName === "write-file") {
|
|
2010
|
+
const warn = readTracker.checkWriteOverwrite(
|
|
2011
|
+
params["path"],
|
|
2012
|
+
params["overwrite"]
|
|
2013
|
+
);
|
|
2014
|
+
if (warn) {
|
|
2015
|
+
conversation.addToolResult(toolCall.id, warn);
|
|
2016
|
+
continue;
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
const permLevel = this.registry.getPermissionLevel(toolName);
|
|
2020
|
+
if (permLevel === "deny") {
|
|
2021
|
+
callbacks.onDenied?.(toolName);
|
|
2022
|
+
conversation.addToolResult(toolCall.id, `\u5DE5\u5177 "${toolName}" \u5DF2\u88AB\u7981\u6B62\u6267\u884C`);
|
|
2023
|
+
continue;
|
|
2024
|
+
}
|
|
2025
|
+
if (permLevel === "auto") {
|
|
2026
|
+
callbacks.onToolExecuting?.(toolName, params);
|
|
2027
|
+
}
|
|
2028
|
+
if (permLevel === "confirm") {
|
|
2029
|
+
const confirmResult = await confirmExecution(toolName, params);
|
|
2030
|
+
if (!confirmResult.approved) {
|
|
2031
|
+
callbacks.onDenied?.(toolName, confirmResult.feedback);
|
|
2032
|
+
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";
|
|
2033
|
+
conversation.addToolResult(toolCall.id, denyMsg);
|
|
2034
|
+
continue;
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
const result = await this.registry.execute(toolName, params, this.config.max_tool_output);
|
|
2038
|
+
callbacks.onToolResult?.(toolName, result.content, result.truncated ?? false);
|
|
2039
|
+
if (toolName === "read-file") {
|
|
2040
|
+
readTracker.markRead(params["path"]);
|
|
2041
|
+
} else if (toolName === "write-file") {
|
|
2042
|
+
readTracker.markWritten(params["path"]);
|
|
2043
|
+
}
|
|
2044
|
+
conversation.addToolResult(toolCall.id, result.content);
|
|
2045
|
+
} catch (err) {
|
|
2046
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2047
|
+
conversation.addToolResult(toolCall.id, `\u5DE5\u5177\u6267\u884C\u5F02\u5E38\uFF1A${msg}`);
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
if (assistantMsg.content) {
|
|
2051
|
+
lastContent = assistantMsg.content;
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
return lastContent;
|
|
2055
|
+
}
|
|
2056
|
+
};
|
|
2057
|
+
}
|
|
2058
|
+
});
|
|
2059
|
+
|
|
2060
|
+
// src/tools/dispatch.ts
|
|
2061
|
+
function createDispatchTool(defaultClient, toolRegistry, config, agentRegistry, callbacks) {
|
|
2017
2062
|
return {
|
|
2018
|
-
name: "
|
|
2019
|
-
description: "\
|
|
2063
|
+
name: "dispatch",
|
|
2064
|
+
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",
|
|
2020
2065
|
parameters: {
|
|
2021
2066
|
type: "object",
|
|
2022
2067
|
properties: {
|
|
2023
|
-
|
|
2068
|
+
agent: {
|
|
2024
2069
|
type: "string",
|
|
2025
|
-
description:
|
|
2026
|
-
enum:
|
|
2070
|
+
description: `\u5B50 Agent \u540D\u79F0\u3002\u53EF\u9009: ${agentRegistry.listNames().join(", ")}`,
|
|
2071
|
+
enum: agentRegistry.listNames()
|
|
2027
2072
|
},
|
|
2028
|
-
|
|
2073
|
+
task: {
|
|
2029
2074
|
type: "string",
|
|
2030
|
-
description: "
|
|
2075
|
+
description: "\u8981\u6267\u884C\u7684\u5177\u4F53\u4EFB\u52A1\u63CF\u8FF0"
|
|
2031
2076
|
},
|
|
2032
|
-
|
|
2077
|
+
context: {
|
|
2033
2078
|
type: "string",
|
|
2034
|
-
description: "
|
|
2079
|
+
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"
|
|
2035
2080
|
}
|
|
2036
2081
|
},
|
|
2037
|
-
required: ["
|
|
2082
|
+
required: ["agent", "task"]
|
|
2038
2083
|
},
|
|
2039
2084
|
permissionLevel: "auto",
|
|
2040
2085
|
async execute(params) {
|
|
2041
|
-
const
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
const items = store.list();
|
|
2066
|
-
if (items.length === 0) {
|
|
2067
|
-
return { content: "\u5907\u5FD8\u5F55\u4E3A\u7A7A" };
|
|
2068
|
-
}
|
|
2069
|
-
const lines = items.map(
|
|
2070
|
-
(item) => `[${item.key}] (${item.author}) ${item.preview}`
|
|
2071
|
-
);
|
|
2072
|
-
return { content: `\u5171 ${items.length} \u6761\u5907\u5FD8\u5F55\uFF1A
|
|
2073
|
-
${lines.join("\n")}` };
|
|
2074
|
-
}
|
|
2075
|
-
case "delete": {
|
|
2076
|
-
const key = params["key"];
|
|
2077
|
-
if (!key) {
|
|
2078
|
-
return { content: "\u9519\u8BEF\uFF1Adelete \u9700\u8981\u63D0\u4F9B key" };
|
|
2079
|
-
}
|
|
2080
|
-
const ok = store.delete(key);
|
|
2081
|
-
return { content: ok ? `\u5DF2\u5220\u9664 memo [${key}]` : `memo [${key}] \u4E0D\u5B58\u5728` };
|
|
2082
|
-
}
|
|
2083
|
-
case "clear": {
|
|
2084
|
-
store.clear();
|
|
2085
|
-
return { content: "\u5907\u5FD8\u5F55\u5DF2\u6E05\u7A7A" };
|
|
2086
|
-
}
|
|
2087
|
-
default:
|
|
2088
|
-
return { content: `\u9519\u8BEF\uFF1A\u672A\u77E5\u64CD\u4F5C "${action}"\u3002\u652F\u6301: write, read, list, delete, clear` };
|
|
2086
|
+
const agentName = params["agent"];
|
|
2087
|
+
const task = params["task"];
|
|
2088
|
+
const context = params["context"];
|
|
2089
|
+
const agentConfig = agentRegistry.get(agentName);
|
|
2090
|
+
if (!agentConfig) {
|
|
2091
|
+
return { content: `\u9519\u8BEF\uFF1A\u672A\u627E\u5230\u5B50 Agent "${agentName}"\u3002\u53EF\u7528: ${agentRegistry.listNames().join(", ")}` };
|
|
2092
|
+
}
|
|
2093
|
+
let client = defaultClient;
|
|
2094
|
+
if (agentConfig.model) {
|
|
2095
|
+
client = createLLMClient({
|
|
2096
|
+
apiKey: agentConfig.model.api_key || config.api_key,
|
|
2097
|
+
baseURL: agentConfig.model.base_url || config.base_url,
|
|
2098
|
+
model: agentConfig.model.model || config.model,
|
|
2099
|
+
temperature: config.temperature,
|
|
2100
|
+
maxTokens: config.max_tokens
|
|
2101
|
+
});
|
|
2102
|
+
}
|
|
2103
|
+
const runner = new SubAgentRunner(client, toolRegistry, config, agentConfig);
|
|
2104
|
+
try {
|
|
2105
|
+
const result = await runner.execute(task, context, callbacks?.() ?? {});
|
|
2106
|
+
return { content: result || "\uFF08\u5B50 Agent \u6267\u884C\u5B8C\u6210\uFF0C\u65E0\u8F93\u51FA\uFF09" };
|
|
2107
|
+
} catch (err) {
|
|
2108
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2109
|
+
return { content: `\u5B50 Agent \u6267\u884C\u9519\u8BEF\uFF1A${msg}` };
|
|
2089
2110
|
}
|
|
2090
2111
|
}
|
|
2091
2112
|
};
|
|
2092
2113
|
}
|
|
2093
|
-
var
|
|
2094
|
-
"src/tools/
|
|
2114
|
+
var init_dispatch = __esm({
|
|
2115
|
+
"src/tools/dispatch.ts"() {
|
|
2095
2116
|
"use strict";
|
|
2117
|
+
init_runner();
|
|
2118
|
+
init_client();
|
|
2096
2119
|
}
|
|
2097
2120
|
});
|
|
2098
2121
|
|
|
2099
2122
|
// src/cli/tui/bridge.ts
|
|
2123
|
+
function gray(text) {
|
|
2124
|
+
return `${ANSI_GRAY}${text}${ANSI_RESET}`;
|
|
2125
|
+
}
|
|
2100
2126
|
function createThinkFilter() {
|
|
2101
2127
|
let inThink = false;
|
|
2102
2128
|
let tagBuffer = "";
|
|
2103
|
-
let
|
|
2129
|
+
let thinkLineBuffer = "";
|
|
2130
|
+
let thinkHasVisibleContent = false;
|
|
2131
|
+
let thinkLastEmittedBlank = false;
|
|
2104
2132
|
let postThink = false;
|
|
2133
|
+
function flushThinkLine(rawLine) {
|
|
2134
|
+
const normalized = rawLine.trim();
|
|
2135
|
+
if (!thinkHasVisibleContent && normalized.length === 0) return "";
|
|
2136
|
+
if (normalized.length === 0) {
|
|
2137
|
+
if (thinkLastEmittedBlank) return "";
|
|
2138
|
+
thinkLastEmittedBlank = true;
|
|
2139
|
+
return "\n";
|
|
2140
|
+
}
|
|
2141
|
+
thinkHasVisibleContent = true;
|
|
2142
|
+
thinkLastEmittedBlank = false;
|
|
2143
|
+
return `${gray(` ${normalized}`)}
|
|
2144
|
+
`;
|
|
2145
|
+
}
|
|
2146
|
+
function appendOutsideText(current, text) {
|
|
2147
|
+
if (!postThink) return current + text;
|
|
2148
|
+
let result = current;
|
|
2149
|
+
for (let i = 0; i < text.length; i++) {
|
|
2150
|
+
const ch = text[i];
|
|
2151
|
+
if (postThink && (ch === "\n" || ch === "\r" || ch === " " || ch === " ")) {
|
|
2152
|
+
continue;
|
|
2153
|
+
}
|
|
2154
|
+
postThink = false;
|
|
2155
|
+
result += text.slice(i);
|
|
2156
|
+
break;
|
|
2157
|
+
}
|
|
2158
|
+
return result;
|
|
2159
|
+
}
|
|
2160
|
+
function appendThinkText(current, text) {
|
|
2161
|
+
let result = current;
|
|
2162
|
+
for (let i = 0; i < text.length; i++) {
|
|
2163
|
+
const ch = text[i];
|
|
2164
|
+
if (ch === "\r") continue;
|
|
2165
|
+
if (ch === "\n") {
|
|
2166
|
+
result += flushThinkLine(thinkLineBuffer);
|
|
2167
|
+
thinkLineBuffer = "";
|
|
2168
|
+
} else {
|
|
2169
|
+
thinkLineBuffer += ch;
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
return result;
|
|
2173
|
+
}
|
|
2105
2174
|
return function filter(text) {
|
|
2106
2175
|
let result = "";
|
|
2107
2176
|
for (let i = 0; i < text.length; i++) {
|
|
@@ -2110,24 +2179,25 @@ function createThinkFilter() {
|
|
|
2110
2179
|
tagBuffer += ch;
|
|
2111
2180
|
if (tagBuffer === "<think>") {
|
|
2112
2181
|
inThink = true;
|
|
2113
|
-
|
|
2182
|
+
thinkLineBuffer = "";
|
|
2183
|
+
thinkHasVisibleContent = false;
|
|
2184
|
+
thinkLastEmittedBlank = false;
|
|
2114
2185
|
tagBuffer = "";
|
|
2115
|
-
result +=
|
|
2186
|
+
result += `${gray("Thinking")}
|
|
2116
2187
|
`;
|
|
2117
2188
|
} else if (tagBuffer === "</think>") {
|
|
2118
2189
|
inThink = false;
|
|
2119
2190
|
postThink = true;
|
|
2120
2191
|
tagBuffer = "";
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
`;
|
|
2125
|
-
} else if (!"<think>".startsWith(tagBuffer) && !"</think>".startsWith(tagBuffer)) {
|
|
2126
|
-
if (inThink && lineStart) {
|
|
2127
|
-
result += "\u2502 ";
|
|
2128
|
-
lineStart = false;
|
|
2192
|
+
if (thinkLineBuffer.length > 0) {
|
|
2193
|
+
result += flushThinkLine(thinkLineBuffer);
|
|
2194
|
+
thinkLineBuffer = "";
|
|
2129
2195
|
}
|
|
2130
|
-
result
|
|
2196
|
+
while (result.endsWith("\n\n\n")) result = result.slice(0, -1);
|
|
2197
|
+
result += "\n";
|
|
2198
|
+
} else if (!"<think>".startsWith(tagBuffer) && !"</think>".startsWith(tagBuffer)) {
|
|
2199
|
+
if (inThink) result = appendThinkText(result, tagBuffer);
|
|
2200
|
+
else result = appendOutsideText(result, tagBuffer);
|
|
2131
2201
|
tagBuffer = "";
|
|
2132
2202
|
}
|
|
2133
2203
|
continue;
|
|
@@ -2136,34 +2206,20 @@ function createThinkFilter() {
|
|
|
2136
2206
|
tagBuffer = "<";
|
|
2137
2207
|
continue;
|
|
2138
2208
|
}
|
|
2139
|
-
if (inThink)
|
|
2140
|
-
|
|
2141
|
-
result += "\u2502 ";
|
|
2142
|
-
lineStart = false;
|
|
2143
|
-
}
|
|
2144
|
-
result += ch;
|
|
2145
|
-
if (ch === "\n") {
|
|
2146
|
-
lineStart = true;
|
|
2147
|
-
}
|
|
2148
|
-
} else {
|
|
2149
|
-
if (postThink) {
|
|
2150
|
-
if (ch === "\n" || ch === "\r" || ch === " " || ch === " ") continue;
|
|
2151
|
-
postThink = false;
|
|
2152
|
-
}
|
|
2153
|
-
result += ch;
|
|
2154
|
-
}
|
|
2209
|
+
if (inThink) result = appendThinkText(result, ch);
|
|
2210
|
+
else result = appendOutsideText(result, ch);
|
|
2155
2211
|
}
|
|
2156
2212
|
return result;
|
|
2157
2213
|
};
|
|
2158
2214
|
}
|
|
2159
|
-
function createTokenBatcher(dispatch) {
|
|
2215
|
+
function createTokenBatcher(dispatch, type) {
|
|
2160
2216
|
let buffer = "";
|
|
2161
2217
|
let timer = null;
|
|
2162
2218
|
function flush() {
|
|
2163
2219
|
if (buffer.length > 0) {
|
|
2164
2220
|
const text = buffer;
|
|
2165
2221
|
buffer = "";
|
|
2166
|
-
dispatch({ type
|
|
2222
|
+
dispatch({ type, text });
|
|
2167
2223
|
}
|
|
2168
2224
|
}
|
|
2169
2225
|
function start() {
|
|
@@ -2208,8 +2264,10 @@ function registerConfirmToolId(toolName, id) {
|
|
|
2208
2264
|
activeToolIds.set(toolName, id);
|
|
2209
2265
|
}
|
|
2210
2266
|
function createBridgeCallbacks(dispatch) {
|
|
2211
|
-
const
|
|
2212
|
-
const
|
|
2267
|
+
const contentBatcher = createTokenBatcher(dispatch, "APPEND_CONTENT");
|
|
2268
|
+
const thoughtBatcher = createTokenBatcher(dispatch, "APPEND_THOUGHT");
|
|
2269
|
+
let inThink = false;
|
|
2270
|
+
let tagBuffer = "";
|
|
2213
2271
|
activeToolIds = /* @__PURE__ */ new Map();
|
|
2214
2272
|
streamingToolIds = /* @__PURE__ */ new Map();
|
|
2215
2273
|
lastStreamingArgs = /* @__PURE__ */ new Map();
|
|
@@ -2228,12 +2286,41 @@ function createBridgeCallbacks(dispatch) {
|
|
|
2228
2286
|
}
|
|
2229
2287
|
return {
|
|
2230
2288
|
onContent: (text) => {
|
|
2231
|
-
|
|
2232
|
-
|
|
2289
|
+
for (let i = 0; i < text.length; i++) {
|
|
2290
|
+
const ch = text[i];
|
|
2291
|
+
if (tagBuffer.length > 0 || ch === "<") {
|
|
2292
|
+
tagBuffer += ch;
|
|
2293
|
+
if (tagBuffer === "<think>") {
|
|
2294
|
+
contentBatcher.flush();
|
|
2295
|
+
inThink = true;
|
|
2296
|
+
tagBuffer = "";
|
|
2297
|
+
continue;
|
|
2298
|
+
} else if (tagBuffer === "</think>") {
|
|
2299
|
+
thoughtBatcher.flush();
|
|
2300
|
+
inThink = false;
|
|
2301
|
+
tagBuffer = "";
|
|
2302
|
+
continue;
|
|
2303
|
+
} else if (!"<think>".startsWith(tagBuffer) && !"</think>".startsWith(tagBuffer)) {
|
|
2304
|
+
if (inThink) {
|
|
2305
|
+
thoughtBatcher.append(tagBuffer);
|
|
2306
|
+
} else {
|
|
2307
|
+
contentBatcher.append(tagBuffer);
|
|
2308
|
+
}
|
|
2309
|
+
tagBuffer = "";
|
|
2310
|
+
}
|
|
2311
|
+
continue;
|
|
2312
|
+
}
|
|
2313
|
+
if (inThink) {
|
|
2314
|
+
thoughtBatcher.append(ch);
|
|
2315
|
+
} else {
|
|
2316
|
+
contentBatcher.append(ch);
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2233
2319
|
},
|
|
2234
2320
|
onToolCallStreaming: (index, name, accumulatedArgs) => {
|
|
2235
2321
|
if (!streamingToolIds.has(index)) {
|
|
2236
|
-
|
|
2322
|
+
contentBatcher.flush();
|
|
2323
|
+
thoughtBatcher.flush();
|
|
2237
2324
|
const id2 = `tool-${++toolCallCounter}`;
|
|
2238
2325
|
streamingToolIds.set(index, id2);
|
|
2239
2326
|
activeToolIds.set(name, id2);
|
|
@@ -2253,8 +2340,10 @@ function createBridgeCallbacks(dispatch) {
|
|
|
2253
2340
|
},
|
|
2254
2341
|
onToolExecuting: (name, params) => {
|
|
2255
2342
|
flushStreamingUpdate();
|
|
2256
|
-
|
|
2257
|
-
|
|
2343
|
+
contentBatcher.flush();
|
|
2344
|
+
thoughtBatcher.flush();
|
|
2345
|
+
contentBatcher.pause();
|
|
2346
|
+
thoughtBatcher.pause();
|
|
2258
2347
|
const existingId = activeToolIds.get(name);
|
|
2259
2348
|
const id = existingId || `tool-${++toolCallCounter}`;
|
|
2260
2349
|
activeToolIds.set(name, id);
|
|
@@ -2268,41 +2357,36 @@ function createBridgeCallbacks(dispatch) {
|
|
|
2268
2357
|
if (isWriteTool) {
|
|
2269
2358
|
const code = extractCodeFromArgs(name, lastStreamingArgs.get(id) || "");
|
|
2270
2359
|
const codeLines = code ? code.split("\n").length : 0;
|
|
2271
|
-
summary = codeLines > 0 ? `${codeLines}
|
|
2360
|
+
summary = codeLines > 0 ? `${codeLines} lines` : truncated ? "truncated" : `${lines.length} lines`;
|
|
2272
2361
|
} else {
|
|
2273
|
-
summary = truncated ?
|
|
2362
|
+
summary = truncated ? `truncated` : `${lines.length} lines`;
|
|
2274
2363
|
}
|
|
2275
2364
|
const preview = lines.slice(0, 5).join("\n").slice(0, 200);
|
|
2276
2365
|
const resultContent = lines.length > 5 || preview.length >= 200 ? preview + "..." : preview;
|
|
2277
2366
|
dispatch({ type: "TOOL_RESULT", id, resultSummary: summary, resultContent });
|
|
2278
2367
|
},
|
|
2279
|
-
onCoderStart: () => {
|
|
2280
|
-
batcher.flush();
|
|
2281
|
-
dispatch({ type: "CODER_START" });
|
|
2282
|
-
},
|
|
2283
|
-
onCoderEnd: () => {
|
|
2284
|
-
dispatch({ type: "CODER_END" });
|
|
2285
|
-
},
|
|
2286
2368
|
onDenied: (toolName, feedback) => {
|
|
2287
2369
|
const id = activeToolIds.get(toolName) || `tool-${++toolCallCounter}`;
|
|
2288
2370
|
dispatch({ type: "TOOL_DENIED", id, feedback });
|
|
2289
2371
|
},
|
|
2290
2372
|
onError: (err) => {
|
|
2291
|
-
|
|
2373
|
+
contentBatcher.stop();
|
|
2374
|
+
thoughtBatcher.stop();
|
|
2292
2375
|
dispatch({ type: "SET_ERROR", error: err.message });
|
|
2293
2376
|
},
|
|
2294
|
-
// Called internally when streaming is complete
|
|
2295
2377
|
_stopBatcher: () => {
|
|
2296
|
-
|
|
2378
|
+
contentBatcher.stop();
|
|
2379
|
+
thoughtBatcher.stop();
|
|
2297
2380
|
}
|
|
2298
2381
|
};
|
|
2299
2382
|
}
|
|
2300
|
-
var BATCH_INTERVAL_MS,
|
|
2383
|
+
var BATCH_INTERVAL_MS, ANSI_GRAY, ANSI_RESET, toolCallCounter, activeToolIds, streamingToolIds, lastStreamingArgs;
|
|
2301
2384
|
var init_bridge = __esm({
|
|
2302
2385
|
"src/cli/tui/bridge.ts"() {
|
|
2303
2386
|
"use strict";
|
|
2304
2387
|
BATCH_INTERVAL_MS = 64;
|
|
2305
|
-
|
|
2388
|
+
ANSI_GRAY = "\x1B[90m";
|
|
2389
|
+
ANSI_RESET = "\x1B[0m";
|
|
2306
2390
|
toolCallCounter = 0;
|
|
2307
2391
|
activeToolIds = /* @__PURE__ */ new Map();
|
|
2308
2392
|
streamingToolIds = /* @__PURE__ */ new Map();
|
|
@@ -2361,15 +2445,12 @@ var init_sub_agent_tracker = __esm({
|
|
|
2361
2445
|
});
|
|
2362
2446
|
|
|
2363
2447
|
// src/cli/tui/state.ts
|
|
2364
|
-
function createInitialState(modelName
|
|
2448
|
+
function createInitialState(modelName) {
|
|
2365
2449
|
return {
|
|
2366
2450
|
messages: [],
|
|
2367
2451
|
isRunning: false,
|
|
2368
2452
|
error: void 0,
|
|
2369
|
-
coderWorking: false,
|
|
2370
2453
|
modelName,
|
|
2371
|
-
agentMode,
|
|
2372
|
-
collaboration,
|
|
2373
2454
|
todoPlan: null,
|
|
2374
2455
|
subAgentProgress: null
|
|
2375
2456
|
};
|
|
@@ -2439,6 +2520,21 @@ function tuiReducer(state, action) {
|
|
|
2439
2520
|
})
|
|
2440
2521
|
};
|
|
2441
2522
|
}
|
|
2523
|
+
case "APPEND_THOUGHT": {
|
|
2524
|
+
return {
|
|
2525
|
+
...state,
|
|
2526
|
+
messages: updateLastAssistant(state.messages, (msg) => {
|
|
2527
|
+
const blocks = [...msg.blocks];
|
|
2528
|
+
const last = blocks[blocks.length - 1];
|
|
2529
|
+
if (last && last.type === "thought") {
|
|
2530
|
+
blocks[blocks.length - 1] = { type: "thought", text: last.text + action.text };
|
|
2531
|
+
} else {
|
|
2532
|
+
blocks.push({ type: "thought", text: action.text });
|
|
2533
|
+
}
|
|
2534
|
+
return { ...msg, blocks };
|
|
2535
|
+
})
|
|
2536
|
+
};
|
|
2537
|
+
}
|
|
2442
2538
|
case "TOOL_EXECUTING": {
|
|
2443
2539
|
return {
|
|
2444
2540
|
...state,
|
|
@@ -2584,14 +2680,8 @@ function tuiReducer(state, action) {
|
|
|
2584
2680
|
return { ...state, error: action.error, isRunning: false };
|
|
2585
2681
|
case "CLEAR_ERROR":
|
|
2586
2682
|
return { ...state, error: void 0 };
|
|
2587
|
-
case "CODER_START":
|
|
2588
|
-
return { ...state, coderWorking: true };
|
|
2589
|
-
case "CODER_END":
|
|
2590
|
-
return { ...state, coderWorking: false };
|
|
2591
2683
|
case "CLEAR_MESSAGES":
|
|
2592
2684
|
return { ...state, messages: [] };
|
|
2593
|
-
case "SET_MODE":
|
|
2594
|
-
return { ...state, agentMode: action.agentMode, collaboration: action.collaboration };
|
|
2595
2685
|
case "SET_TODO_PLAN":
|
|
2596
2686
|
return { ...state, todoPlan: action.plan };
|
|
2597
2687
|
case "SET_SUB_AGENT_PROGRESS":
|
|
@@ -2614,136 +2704,78 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
2614
2704
|
function getToolParamSummary(name, params) {
|
|
2615
2705
|
switch (name) {
|
|
2616
2706
|
case "bash":
|
|
2617
|
-
return String(params["command"] || "").slice(0,
|
|
2707
|
+
return String(params["command"] || "").slice(0, 100);
|
|
2618
2708
|
case "read-file":
|
|
2619
2709
|
case "write-file":
|
|
2620
2710
|
case "edit-file":
|
|
2621
2711
|
return String(params["path"] || "");
|
|
2622
2712
|
case "glob":
|
|
2623
|
-
return String(params["pattern"] || "");
|
|
2624
2713
|
case "grep":
|
|
2625
2714
|
return String(params["pattern"] || "");
|
|
2626
|
-
case "send-to-coder":
|
|
2627
|
-
return String(params["task"] || "").slice(0, 40);
|
|
2628
|
-
case "spawn-agents": {
|
|
2629
|
-
const tasks = params["tasks"];
|
|
2630
|
-
if (!tasks) return "";
|
|
2631
|
-
if (tasks.length <= 2) {
|
|
2632
|
-
return tasks.map((t) => t.description.slice(0, 30)).join(", ");
|
|
2633
|
-
}
|
|
2634
|
-
return `${tasks.length} \u4E2A\u5E76\u884C\u4EFB\u52A1`;
|
|
2635
|
-
}
|
|
2636
|
-
case "todo": {
|
|
2637
|
-
const action = String(params["action"] || "");
|
|
2638
|
-
const id = params["id"] ? ` [${params["id"]}]` : "";
|
|
2639
|
-
return `${action}${id}`;
|
|
2640
|
-
}
|
|
2641
|
-
case "memo": {
|
|
2642
|
-
const action = String(params["action"] || "");
|
|
2643
|
-
const key = params["key"] ? ` [${params["key"]}]` : "";
|
|
2644
|
-
return `${action}${key}`;
|
|
2645
|
-
}
|
|
2646
|
-
default: {
|
|
2647
|
-
const keys = Object.keys(params);
|
|
2648
|
-
if (keys.length > 0 && keys[0]) {
|
|
2649
|
-
return String(params[keys[0]] || "").slice(0, 40);
|
|
2650
|
-
}
|
|
2651
|
-
return "";
|
|
2652
|
-
}
|
|
2653
|
-
}
|
|
2654
|
-
}
|
|
2655
|
-
function getToolIcon(name) {
|
|
2656
|
-
switch (name) {
|
|
2657
|
-
case "bash":
|
|
2658
|
-
return "$";
|
|
2659
|
-
case "write-file":
|
|
2660
|
-
return "+";
|
|
2661
|
-
case "edit-file":
|
|
2662
|
-
return "\xB1";
|
|
2663
|
-
case "read-file":
|
|
2664
|
-
return "\u{1F4C4}";
|
|
2665
|
-
case "glob":
|
|
2666
|
-
return "\u{1F50D}";
|
|
2667
|
-
case "grep":
|
|
2668
|
-
return "\u{1F50D}";
|
|
2669
|
-
case "spawn-agents":
|
|
2670
|
-
return "\u26A1";
|
|
2671
|
-
case "todo":
|
|
2672
|
-
return "\u{1F4CB}";
|
|
2673
|
-
case "memo":
|
|
2674
|
-
return "\u{1F4DD}";
|
|
2675
2715
|
default:
|
|
2676
|
-
|
|
2716
|
+
const keys = Object.keys(params);
|
|
2717
|
+
return keys.length > 0 ? String(params[keys[0]] || "").slice(0, 60) : "";
|
|
2677
2718
|
}
|
|
2678
2719
|
}
|
|
2679
|
-
function
|
|
2680
|
-
|
|
2681
|
-
|
|
2720
|
+
function truncateContent(text, maxLines) {
|
|
2721
|
+
const lines = text.split("\n");
|
|
2722
|
+
const result = [];
|
|
2723
|
+
for (let line of lines) {
|
|
2724
|
+
if (result.length >= maxLines) break;
|
|
2725
|
+
result.push(line.replace(/\r/g, "").replace(/\t/g, " "));
|
|
2682
2726
|
}
|
|
2683
|
-
if (
|
|
2684
|
-
|
|
2727
|
+
if (lines.length > maxLines) {
|
|
2728
|
+
result.push(`... (and ${lines.length - maxLines} more lines)`);
|
|
2685
2729
|
}
|
|
2686
|
-
return
|
|
2687
|
-
}
|
|
2688
|
-
function truncateCode(code, maxLines) {
|
|
2689
|
-
const lines = code.split("\n");
|
|
2690
|
-
if (lines.length <= maxLines) return code;
|
|
2691
|
-
return lines.slice(0, maxLines).join("\n") + `
|
|
2692
|
-
... (\u5171 ${lines.length} \u884C)`;
|
|
2730
|
+
return result;
|
|
2693
2731
|
}
|
|
2694
2732
|
function ToolCallLine({ toolCall }) {
|
|
2695
|
-
const { name, params, status,
|
|
2733
|
+
const { name, params, status, resultContent, denyFeedback } = toolCall;
|
|
2696
2734
|
const summary = getToolParamSummary(name, params);
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
let
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
statusText = "[y/N]";
|
|
2717
|
-
break;
|
|
2735
|
+
let borderColor = "#504945";
|
|
2736
|
+
let titleColor = "#fabd2f";
|
|
2737
|
+
let statusColor = "#fabd2f";
|
|
2738
|
+
let statusText = "RUNNING";
|
|
2739
|
+
if (status === "done") {
|
|
2740
|
+
borderColor = "#b8bb26";
|
|
2741
|
+
titleColor = "#b8bb26";
|
|
2742
|
+
statusColor = "#b8bb26";
|
|
2743
|
+
statusText = "DONE";
|
|
2744
|
+
} else if (status === "denied") {
|
|
2745
|
+
borderColor = "#fb4934";
|
|
2746
|
+
titleColor = "#fb4934";
|
|
2747
|
+
statusColor = "#fb4934";
|
|
2748
|
+
statusText = "DENIED";
|
|
2749
|
+
} else if (status === "confirming") {
|
|
2750
|
+
borderColor = "#fe8019";
|
|
2751
|
+
titleColor = "#fe8019";
|
|
2752
|
+
statusColor = "#fe8019";
|
|
2753
|
+
statusText = "CONFIRM";
|
|
2718
2754
|
}
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
marginTop: 0,
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
/* @__PURE__ */ jsx(Text, { color: "red", children: "\u53CD\u9988:" }),
|
|
2744
|
-
/* @__PURE__ */ jsx(Text, { children: denyFeedback })
|
|
2745
|
-
] })
|
|
2746
|
-
] });
|
|
2755
|
+
const contentLines = resultContent ? truncateContent(resultContent, 15) : [];
|
|
2756
|
+
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: 0, marginBottom: 1, width: "100%", children: /* @__PURE__ */ jsxs(
|
|
2757
|
+
Box,
|
|
2758
|
+
{
|
|
2759
|
+
flexDirection: "column",
|
|
2760
|
+
borderStyle: "round",
|
|
2761
|
+
borderColor,
|
|
2762
|
+
paddingX: 1,
|
|
2763
|
+
width: "100%",
|
|
2764
|
+
children: [
|
|
2765
|
+
/* @__PURE__ */ jsxs(Box, { gap: 1, children: [
|
|
2766
|
+
/* @__PURE__ */ jsx(Box, { backgroundColor: statusColor, width: 9, justifyContent: "center", children: /* @__PURE__ */ jsx(Text, { color: "#282828", bold: true, children: statusText }) }),
|
|
2767
|
+
/* @__PURE__ */ jsx(Text, { color: titleColor, bold: true, children: name.toUpperCase() }),
|
|
2768
|
+
/* @__PURE__ */ jsx(Text, { color: "#ebdbb2", dimColor: true, italic: true, wrap: "truncate-end", children: summary })
|
|
2769
|
+
] }),
|
|
2770
|
+
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)) }),
|
|
2771
|
+
status === "denied" && denyFeedback && /* @__PURE__ */ jsxs(Box, { gap: 1, marginTop: 0, children: [
|
|
2772
|
+
/* @__PURE__ */ jsx(Text, { color: "#fb4934", bold: true, children: "REASON:" }),
|
|
2773
|
+
/* @__PURE__ */ jsx(Text, { color: "#ebdbb2", children: denyFeedback })
|
|
2774
|
+
] }),
|
|
2775
|
+
status === "confirming" && /* @__PURE__ */ jsx(Box, { marginTop: 0, children: /* @__PURE__ */ jsx(Text, { color: "#fe8019", italic: true, children: "Waiting for your permission..." }) })
|
|
2776
|
+
]
|
|
2777
|
+
}
|
|
2778
|
+
) });
|
|
2747
2779
|
}
|
|
2748
2780
|
var init_ToolCallLine = __esm({
|
|
2749
2781
|
"src/cli/tui/components/ToolCallLine.tsx"() {
|
|
@@ -2762,209 +2794,94 @@ var init_MessageBubble = __esm({
|
|
|
2762
2794
|
init_ToolCallLine();
|
|
2763
2795
|
MessageBubble = React.memo(function MessageBubble2({ message }) {
|
|
2764
2796
|
const { role, blocks, isStreaming } = message;
|
|
2765
|
-
const
|
|
2766
|
-
const
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
})
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
import React2, { useRef } from "react";
|
|
2795
|
-
import { Static, Box as Box3, Text as Text3 } from "ink";
|
|
2796
|
-
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
2797
|
-
function splitMessages(messages) {
|
|
2798
|
-
let staticEnd = 0;
|
|
2799
|
-
for (let i = 0; i < messages.length; i++) {
|
|
2800
|
-
if (messages[i].isStreaming || messages[i].confirmPending) break;
|
|
2801
|
-
staticEnd = i + 1;
|
|
2802
|
-
}
|
|
2803
|
-
const dynamicMsgs = messages.slice(staticEnd);
|
|
2804
|
-
const streamingMsg = dynamicMsgs.find((m) => m.isStreaming) || null;
|
|
2805
|
-
const otherDynamic = dynamicMsgs.filter((m) => m !== streamingMsg);
|
|
2806
|
-
return {
|
|
2807
|
-
staticMsgs: messages.slice(0, staticEnd),
|
|
2808
|
-
streamingMsg,
|
|
2809
|
-
otherDynamic
|
|
2810
|
-
};
|
|
2811
|
-
}
|
|
2812
|
-
function extractFromBlocks(msg, staticItems, dynamicNodes, isStreaming) {
|
|
2813
|
-
let lineIdx = 0;
|
|
2814
|
-
for (let bi = 0; bi < msg.blocks.length; bi++) {
|
|
2815
|
-
const block = msg.blocks[bi];
|
|
2816
|
-
const isLastBlock = bi === msg.blocks.length - 1;
|
|
2817
|
-
if (block.type === "text") {
|
|
2818
|
-
const lines = block.text.split("\n");
|
|
2819
|
-
if (isStreaming && isLastBlock) {
|
|
2820
|
-
const partial = lines.pop() || "";
|
|
2821
|
-
for (const line of lines) {
|
|
2822
|
-
staticItems.push({
|
|
2823
|
-
id: `${msg.id}-L${lineIdx}`,
|
|
2824
|
-
type: "line",
|
|
2825
|
-
text: line,
|
|
2826
|
-
isFirstLine: lineIdx === 0
|
|
2827
|
-
});
|
|
2828
|
-
lineIdx++;
|
|
2829
|
-
}
|
|
2830
|
-
if (partial || lineIdx === 0) {
|
|
2831
|
-
dynamicNodes.push(
|
|
2832
|
-
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
2833
|
-
/* @__PURE__ */ jsx3(Text3, { color: "cyan", bold: true, children: lineIdx === 0 ? "\u25C6 " : " " }),
|
|
2834
|
-
/* @__PURE__ */ jsx3(Box3, { flexGrow: 1, children: /* @__PURE__ */ jsx3(Text3, { children: partial }) })
|
|
2835
|
-
] }, "partial")
|
|
2836
|
-
);
|
|
2837
|
-
}
|
|
2838
|
-
} else {
|
|
2839
|
-
for (const line of lines) {
|
|
2840
|
-
staticItems.push({
|
|
2841
|
-
id: `${msg.id}-L${lineIdx}`,
|
|
2842
|
-
type: "line",
|
|
2843
|
-
text: line,
|
|
2844
|
-
isFirstLine: lineIdx === 0
|
|
2845
|
-
});
|
|
2846
|
-
lineIdx++;
|
|
2847
|
-
}
|
|
2848
|
-
}
|
|
2849
|
-
} else if (block.type === "tool") {
|
|
2850
|
-
const tc = block.toolCall;
|
|
2851
|
-
const isWriteTool = tc.name === "write-file" || tc.name === "edit-file";
|
|
2852
|
-
staticItems.push({
|
|
2853
|
-
id: `${msg.id}-TH${tc.id}`,
|
|
2854
|
-
type: "tool-header",
|
|
2855
|
-
toolCall: tc
|
|
2856
|
-
});
|
|
2857
|
-
if (isWriteTool && tc.status === "running" && isStreaming) {
|
|
2858
|
-
const lineCount = parseInt(tc.streamingContent || "0", 10);
|
|
2859
|
-
dynamicNodes.push(
|
|
2860
|
-
/* @__PURE__ */ jsx3(Box3, { paddingX: 1, marginLeft: 4, children: /* @__PURE__ */ jsxs3(Text3, { color: "cyan", children: [
|
|
2861
|
-
"\u270E \u751F\u6210\u4E2D...",
|
|
2862
|
-
lineCount > 0 ? ` ${lineCount} \u884C` : ""
|
|
2863
|
-
] }) }, `tool-progress-${tc.id}`)
|
|
2864
|
-
);
|
|
2865
|
-
}
|
|
2866
|
-
if (tc.status === "done" || tc.status === "denied") {
|
|
2867
|
-
staticItems.push({
|
|
2868
|
-
id: `${msg.id}-TD${tc.id}`,
|
|
2869
|
-
type: "tool-done",
|
|
2870
|
-
toolCall: tc
|
|
2871
|
-
});
|
|
2872
|
-
}
|
|
2873
|
-
}
|
|
2874
|
-
}
|
|
2875
|
-
}
|
|
2876
|
-
function renderStaticItem(item) {
|
|
2877
|
-
if (item.type === "message") {
|
|
2878
|
-
return /* @__PURE__ */ jsx3(Box3, { paddingX: 1, children: /* @__PURE__ */ jsx3(MessageBubble, { message: item.message }) }, item.id);
|
|
2879
|
-
}
|
|
2880
|
-
if (item.type === "line") {
|
|
2881
|
-
return /* @__PURE__ */ jsxs3(Box3, { paddingX: 1, children: [
|
|
2882
|
-
/* @__PURE__ */ jsx3(Text3, { color: "cyan", bold: true, children: item.isFirstLine ? "\u25C6 " : " " }),
|
|
2883
|
-
/* @__PURE__ */ jsx3(Text3, { children: item.text })
|
|
2884
|
-
] }, item.id);
|
|
2885
|
-
}
|
|
2886
|
-
if (item.type === "tool-header") {
|
|
2887
|
-
const tc = item.toolCall;
|
|
2888
|
-
const icon = getToolIcon(tc.name);
|
|
2889
|
-
const summary = getToolParamSummary(tc.name, tc.params);
|
|
2890
|
-
return /* @__PURE__ */ jsx3(Box3, { paddingX: 1, marginLeft: 2, children: /* @__PURE__ */ jsxs3(Box3, { gap: 1, children: [
|
|
2891
|
-
/* @__PURE__ */ jsx3(Text3, { color: "yellow", children: "\u23F3" }),
|
|
2892
|
-
/* @__PURE__ */ jsxs3(Text3, { color: "yellow", bold: true, children: [
|
|
2893
|
-
icon,
|
|
2894
|
-
" ",
|
|
2895
|
-
tc.name
|
|
2896
|
-
] }),
|
|
2897
|
-
summary ? /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: summary }) : null
|
|
2898
|
-
] }) }, item.id);
|
|
2899
|
-
}
|
|
2900
|
-
if (item.type === "tool-done") {
|
|
2901
|
-
return /* @__PURE__ */ jsx3(Box3, { paddingX: 1, marginLeft: 0, children: /* @__PURE__ */ jsx3(ToolCallLine, { toolCall: item.toolCall }) }, item.id);
|
|
2797
|
+
const isUser = role === "user";
|
|
2798
|
+
const label = isUser ? "USER" : "AI";
|
|
2799
|
+
const labelBg = isUser ? "#b8bb26" : "#83a598";
|
|
2800
|
+
const labelFg = "#282828";
|
|
2801
|
+
const contentColor = isUser ? "#ebdbb2" : "#ebdbb2";
|
|
2802
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: 1, width: "100%", children: [
|
|
2803
|
+
/* @__PURE__ */ jsxs2(Box2, { marginBottom: 0, children: [
|
|
2804
|
+
/* @__PURE__ */ jsx2(Box2, { backgroundColor: labelBg, width: 9, paddingLeft: 1, marginRight: 1, children: /* @__PURE__ */ jsx2(Text2, { color: labelFg, bold: true, children: label }) }),
|
|
2805
|
+
isStreaming && /* @__PURE__ */ jsx2(Text2, { color: "#83a598", dimColor: true, italic: true, children: "typing..." })
|
|
2806
|
+
] }),
|
|
2807
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: "100%", children: [
|
|
2808
|
+
blocks.map((block, i) => {
|
|
2809
|
+
if (block.type === "text") {
|
|
2810
|
+
const text = block.text.trim();
|
|
2811
|
+
if (!text && !isStreaming) return null;
|
|
2812
|
+
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}`);
|
|
2813
|
+
} else if (block.type === "thought") {
|
|
2814
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: 0, width: "100%", children: [
|
|
2815
|
+
/* @__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" }) }) }),
|
|
2816
|
+
/* @__PURE__ */ jsx2(Box2, { paddingLeft: 2, marginBottom: 0, children: /* @__PURE__ */ jsx2(Text2, { color: "#928374", italic: true, children: block.text.trim() }) })
|
|
2817
|
+
] }, `thought-${i}`);
|
|
2818
|
+
} else {
|
|
2819
|
+
return /* @__PURE__ */ jsx2(Box2, { paddingLeft: 2, width: "100%", children: /* @__PURE__ */ jsx2(ToolCallLine, { toolCall: block.toolCall }) }, block.toolCall.id);
|
|
2820
|
+
}
|
|
2821
|
+
}),
|
|
2822
|
+
blocks.length === 0 && isStreaming && /* @__PURE__ */ jsx2(Box2, { paddingLeft: 2, children: /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "..." }) })
|
|
2823
|
+
] })
|
|
2824
|
+
] });
|
|
2825
|
+
});
|
|
2902
2826
|
}
|
|
2903
|
-
|
|
2904
|
-
|
|
2827
|
+
});
|
|
2828
|
+
|
|
2829
|
+
// src/cli/tui/components/ChatArea.tsx
|
|
2830
|
+
import React2 from "react";
|
|
2831
|
+
import { Box as Box3 } from "ink";
|
|
2832
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
2905
2833
|
var ChatArea;
|
|
2906
2834
|
var init_ChatArea = __esm({
|
|
2907
2835
|
"src/cli/tui/components/ChatArea.tsx"() {
|
|
2908
2836
|
"use strict";
|
|
2909
2837
|
init_MessageBubble();
|
|
2910
|
-
init_ToolCallLine();
|
|
2911
2838
|
ChatArea = React2.memo(function ChatArea2({ messages }) {
|
|
2912
|
-
|
|
2913
|
-
const streamedIds = useRef(/* @__PURE__ */ new Set());
|
|
2914
|
-
if (streamingMsg) {
|
|
2915
|
-
streamedIds.current.add(streamingMsg.id);
|
|
2916
|
-
}
|
|
2917
|
-
const currentItems = [];
|
|
2918
|
-
for (const msg of staticMsgs) {
|
|
2919
|
-
if (streamedIds.current.has(msg.id)) {
|
|
2920
|
-
const ignore = [];
|
|
2921
|
-
extractFromBlocks(msg, currentItems, ignore, false);
|
|
2922
|
-
} else {
|
|
2923
|
-
currentItems.push({ id: msg.id, type: "message", message: msg });
|
|
2924
|
-
}
|
|
2925
|
-
}
|
|
2926
|
-
const dynamicNodes = [];
|
|
2927
|
-
if (streamingMsg) {
|
|
2928
|
-
extractFromBlocks(streamingMsg, currentItems, dynamicNodes, true);
|
|
2929
|
-
}
|
|
2930
|
-
const seenIds = useRef(/* @__PURE__ */ new Set());
|
|
2931
|
-
const accumulated = useRef([]);
|
|
2932
|
-
let hasNew = false;
|
|
2933
|
-
for (const item of currentItems) {
|
|
2934
|
-
if (!seenIds.current.has(item.id)) {
|
|
2935
|
-
seenIds.current.add(item.id);
|
|
2936
|
-
accumulated.current.push(item);
|
|
2937
|
-
hasNew = true;
|
|
2938
|
-
}
|
|
2939
|
-
}
|
|
2940
|
-
if (hasNew) {
|
|
2941
|
-
accumulated.current = [...accumulated.current];
|
|
2942
|
-
}
|
|
2943
|
-
const staticItems = accumulated.current;
|
|
2944
|
-
const showPlaceholder = streamingMsg && streamingMsg.blocks.length === 0;
|
|
2945
|
-
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
2946
|
-
staticItems.length > 0 && /* @__PURE__ */ jsx3(Static, { items: staticItems, children: renderStaticItem }),
|
|
2947
|
-
(dynamicNodes.length > 0 || showPlaceholder) && /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", paddingX: 1, children: [
|
|
2948
|
-
showPlaceholder && /* @__PURE__ */ jsxs3(Box3, { children: [
|
|
2949
|
-
/* @__PURE__ */ jsx3(Text3, { color: "cyan", bold: true, children: "\u25C6 " }),
|
|
2950
|
-
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "..." })
|
|
2951
|
-
] }),
|
|
2952
|
-
dynamicNodes
|
|
2953
|
-
] }),
|
|
2954
|
-
otherDynamic.map((msg) => /* @__PURE__ */ jsx3(Box3, { paddingX: 1, children: /* @__PURE__ */ jsx3(MessageBubble, { message: msg }) }, msg.id))
|
|
2955
|
-
] });
|
|
2839
|
+
return /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", width: "100%", children: messages.map((msg) => /* @__PURE__ */ jsx3(MessageBubble, { message: msg }, msg.id)) });
|
|
2956
2840
|
});
|
|
2957
2841
|
}
|
|
2958
2842
|
});
|
|
2959
2843
|
|
|
2960
2844
|
// src/cli/tui/components/InputArea.tsx
|
|
2961
|
-
import { useState } from "react";
|
|
2962
|
-
import { Box as Box4, Text as
|
|
2963
|
-
import { Fragment, jsx as jsx4, jsxs as
|
|
2964
|
-
function CleanTextInput({
|
|
2845
|
+
import { useRef, useState } from "react";
|
|
2846
|
+
import { Box as Box4, Text as Text3, useInput } from "ink";
|
|
2847
|
+
import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
2848
|
+
function CleanTextInput({
|
|
2849
|
+
onSubmit,
|
|
2850
|
+
placeholder,
|
|
2851
|
+
onExitRequest,
|
|
2852
|
+
onScroll
|
|
2853
|
+
}) {
|
|
2965
2854
|
const [value, setValue] = useState("");
|
|
2966
2855
|
const [cursor, setCursor] = useState(0);
|
|
2856
|
+
const lastCtrlCAtRef = useRef(0);
|
|
2967
2857
|
useInput((input, key) => {
|
|
2858
|
+
if (onScroll) {
|
|
2859
|
+
if (key.pageUp || key.upArrow && key.shift) {
|
|
2860
|
+
onScroll("up");
|
|
2861
|
+
return;
|
|
2862
|
+
}
|
|
2863
|
+
if (key.pageDown || key.downArrow && key.shift) {
|
|
2864
|
+
onScroll("down");
|
|
2865
|
+
return;
|
|
2866
|
+
}
|
|
2867
|
+
}
|
|
2868
|
+
if (input === "c" && key.ctrl) {
|
|
2869
|
+
if (value.length > 0) {
|
|
2870
|
+
setValue("");
|
|
2871
|
+
setCursor(0);
|
|
2872
|
+
lastCtrlCAtRef.current = 0;
|
|
2873
|
+
return;
|
|
2874
|
+
}
|
|
2875
|
+
const now = Date.now();
|
|
2876
|
+
const isDoublePress = now - lastCtrlCAtRef.current <= 1200;
|
|
2877
|
+
if (isDoublePress) {
|
|
2878
|
+
onExitRequest();
|
|
2879
|
+
lastCtrlCAtRef.current = 0;
|
|
2880
|
+
} else {
|
|
2881
|
+
lastCtrlCAtRef.current = now;
|
|
2882
|
+
}
|
|
2883
|
+
return;
|
|
2884
|
+
}
|
|
2968
2885
|
if (key.return) {
|
|
2969
2886
|
const trimmed = value.trim();
|
|
2970
2887
|
if (trimmed) {
|
|
@@ -3017,30 +2934,33 @@ function CleanTextInput({ onSubmit, placeholder }) {
|
|
|
3017
2934
|
setCursor((prev) => prev + input.length);
|
|
3018
2935
|
});
|
|
3019
2936
|
if (value.length === 0) {
|
|
3020
|
-
return /* @__PURE__ */
|
|
3021
|
-
/* @__PURE__ */ jsx4(
|
|
3022
|
-
/* @__PURE__ */ jsx4(
|
|
2937
|
+
return /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
2938
|
+
/* @__PURE__ */ jsx4(Text3, { inverse: true, children: " " }),
|
|
2939
|
+
/* @__PURE__ */ jsx4(Text3, { dimColor: true, children: placeholder || "" })
|
|
3023
2940
|
] });
|
|
3024
2941
|
}
|
|
3025
2942
|
const before = value.slice(0, cursor);
|
|
3026
2943
|
const at = cursor < value.length ? value[cursor] : " ";
|
|
3027
2944
|
const after = cursor < value.length ? value.slice(cursor + 1) : "";
|
|
3028
|
-
return /* @__PURE__ */
|
|
3029
|
-
/* @__PURE__ */ jsx4(
|
|
3030
|
-
/* @__PURE__ */ jsx4(
|
|
3031
|
-
/* @__PURE__ */ jsx4(
|
|
2945
|
+
return /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
2946
|
+
/* @__PURE__ */ jsx4(Text3, { children: before }),
|
|
2947
|
+
/* @__PURE__ */ jsx4(Text3, { inverse: true, children: at }),
|
|
2948
|
+
/* @__PURE__ */ jsx4(Text3, { children: after })
|
|
3032
2949
|
] });
|
|
3033
2950
|
}
|
|
3034
|
-
function InputArea({ onSubmit, isRunning }) {
|
|
3035
|
-
return /* @__PURE__ */
|
|
3036
|
-
/* @__PURE__ */ jsx4(
|
|
3037
|
-
|
|
2951
|
+
function InputArea({ onSubmit, isRunning, onExitRequest, onScroll }) {
|
|
2952
|
+
return /* @__PURE__ */ jsxs3(Box4, { paddingX: 0, children: [
|
|
2953
|
+
/* @__PURE__ */ jsx4(Box4, { backgroundColor: isRunning ? "#504945" : "#b8bb26", paddingX: 1, marginRight: 1, children: /* @__PURE__ */ jsx4(Text3, { color: "#282828", bold: true, children: isRunning ? " WAIT " : " INPUT " }) }),
|
|
2954
|
+
/* @__PURE__ */ jsx4(Box4, { flexGrow: 1, children: isRunning ? /* @__PURE__ */ jsx4(Box4, { flexGrow: 1, onWheel: (event) => {
|
|
2955
|
+
}, children: /* @__PURE__ */ jsx4(Text3, { color: "#a89984", italic: true, children: "Thinking..." }) }) : /* @__PURE__ */ jsx4(
|
|
3038
2956
|
CleanTextInput,
|
|
3039
2957
|
{
|
|
3040
2958
|
onSubmit,
|
|
3041
|
-
placeholder: "
|
|
2959
|
+
placeholder: "Type a message or /command...",
|
|
2960
|
+
onExitRequest,
|
|
2961
|
+
onScroll
|
|
3042
2962
|
}
|
|
3043
|
-
)
|
|
2963
|
+
) })
|
|
3044
2964
|
] });
|
|
3045
2965
|
}
|
|
3046
2966
|
var init_InputArea = __esm({
|
|
@@ -3050,43 +2970,36 @@ var init_InputArea = __esm({
|
|
|
3050
2970
|
});
|
|
3051
2971
|
|
|
3052
2972
|
// src/cli/tui/components/StatusBar.tsx
|
|
3053
|
-
import { Box as Box5, Text as
|
|
3054
|
-
import { Fragment as Fragment2, jsx as jsx5, jsxs as
|
|
3055
|
-
function StatusBar({
|
|
3056
|
-
const modeLabel = agentMode === "dual" ? `dual(${collaboration})` : "single";
|
|
2973
|
+
import { Box as Box5, Text as Text4 } from "ink";
|
|
2974
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2975
|
+
function StatusBar({ isRunning, modelName, todoPlan, subAgentProgress }) {
|
|
3057
2976
|
const todoProgress = todoPlan ? `${todoPlan.items.filter((i) => i.status === "completed").length}/${todoPlan.items.length}` : null;
|
|
3058
|
-
return /* @__PURE__ */
|
|
3059
|
-
/* @__PURE__ */ jsx5(
|
|
3060
|
-
/* @__PURE__ */
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
"
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
todoProgress && /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
3082
|
-
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "\u2502" }),
|
|
3083
|
-
/* @__PURE__ */ jsxs5(Text5, { color: "cyan", children: [
|
|
3084
|
-
"plan ",
|
|
3085
|
-
todoProgress
|
|
2977
|
+
return /* @__PURE__ */ jsxs4(Box5, { marginTop: 1, children: [
|
|
2978
|
+
/* @__PURE__ */ jsx5(Box5, { backgroundColor: "#3c3836", paddingX: 1, children: /* @__PURE__ */ jsx5(Text4, { color: "#ebdbb2", bold: true, children: " ZENCODE " }) }),
|
|
2979
|
+
/* @__PURE__ */ jsxs4(Box5, { backgroundColor: "#504945", paddingX: 1, flexGrow: 1, children: [
|
|
2980
|
+
/* @__PURE__ */ jsx5(Text4, { color: "#ebdbb2", children: modelName }),
|
|
2981
|
+
isRunning && !subAgentProgress && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
2982
|
+
/* @__PURE__ */ jsx5(Text4, { color: "#ebdbb2", children: " \u2502 " }),
|
|
2983
|
+
/* @__PURE__ */ jsx5(Text4, { color: "#8ec07c", children: "\u25CF thinking..." })
|
|
2984
|
+
] }),
|
|
2985
|
+
subAgentProgress && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
2986
|
+
/* @__PURE__ */ jsx5(Text4, { color: "#ebdbb2", children: " \u2502 " }),
|
|
2987
|
+
/* @__PURE__ */ jsxs4(Text4, { color: "#b8bb26", children: [
|
|
2988
|
+
"\u{1F916} Agents: ",
|
|
2989
|
+
subAgentProgress.completed + subAgentProgress.failed,
|
|
2990
|
+
"/",
|
|
2991
|
+
subAgentProgress.total
|
|
2992
|
+
] })
|
|
2993
|
+
] }),
|
|
2994
|
+
todoProgress && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
2995
|
+
/* @__PURE__ */ jsx5(Text4, { color: "#ebdbb2", children: " \u2502 " }),
|
|
2996
|
+
/* @__PURE__ */ jsxs4(Text4, { color: "#fabd2f", children: [
|
|
2997
|
+
"\u{1F4CB} Plan: ",
|
|
2998
|
+
todoProgress
|
|
2999
|
+
] })
|
|
3086
3000
|
] })
|
|
3087
3001
|
] }),
|
|
3088
|
-
/* @__PURE__ */ jsx5(
|
|
3089
|
-
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "/help" })
|
|
3002
|
+
/* @__PURE__ */ jsx5(Box5, { backgroundColor: "#3c3836", paddingX: 1, children: /* @__PURE__ */ jsx5(Text4, { color: "#ebdbb2", children: "/help" }) })
|
|
3090
3003
|
] });
|
|
3091
3004
|
}
|
|
3092
3005
|
var init_StatusBar = __esm({
|
|
@@ -3097,8 +3010,8 @@ var init_StatusBar = __esm({
|
|
|
3097
3010
|
|
|
3098
3011
|
// src/cli/tui/components/ConfirmPrompt.tsx
|
|
3099
3012
|
import { useState as useState2 } from "react";
|
|
3100
|
-
import { Box as Box6, Text as
|
|
3101
|
-
import { Fragment as Fragment3, jsx as jsx6, jsxs as
|
|
3013
|
+
import { Box as Box6, Text as Text5, useInput as useInput2 } from "ink";
|
|
3014
|
+
import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
3102
3015
|
function getToolDetails(toolName, params) {
|
|
3103
3016
|
const lines = [];
|
|
3104
3017
|
let label = toolName;
|
|
@@ -3184,18 +3097,18 @@ function FeedbackInput({ onSubmit }) {
|
|
|
3184
3097
|
setCursor((prev) => prev + input.length);
|
|
3185
3098
|
});
|
|
3186
3099
|
if (value.length === 0) {
|
|
3187
|
-
return /* @__PURE__ */
|
|
3188
|
-
/* @__PURE__ */ jsx6(
|
|
3189
|
-
/* @__PURE__ */ jsx6(
|
|
3100
|
+
return /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
3101
|
+
/* @__PURE__ */ jsx6(Text5, { inverse: true, children: " " }),
|
|
3102
|
+
/* @__PURE__ */ jsx6(Text5, { dimColor: true, children: "\u8F93\u5165\u53CD\u9988\u6307\u4EE4\u7ED9 AI..." })
|
|
3190
3103
|
] });
|
|
3191
3104
|
}
|
|
3192
3105
|
const before = value.slice(0, cursor);
|
|
3193
3106
|
const at = cursor < value.length ? value[cursor] : " ";
|
|
3194
3107
|
const after = cursor < value.length ? value.slice(cursor + 1) : "";
|
|
3195
|
-
return /* @__PURE__ */
|
|
3196
|
-
/* @__PURE__ */ jsx6(
|
|
3197
|
-
/* @__PURE__ */ jsx6(
|
|
3198
|
-
/* @__PURE__ */ jsx6(
|
|
3108
|
+
return /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
3109
|
+
/* @__PURE__ */ jsx6(Text5, { children: before }),
|
|
3110
|
+
/* @__PURE__ */ jsx6(Text5, { inverse: true, children: at }),
|
|
3111
|
+
/* @__PURE__ */ jsx6(Text5, { children: after })
|
|
3199
3112
|
] });
|
|
3200
3113
|
}
|
|
3201
3114
|
function ConfirmPrompt({ confirm, onRespond }) {
|
|
@@ -3223,66 +3136,69 @@ function ConfirmPrompt({ confirm, onRespond }) {
|
|
|
3223
3136
|
}, { isActive: !feedbackMode });
|
|
3224
3137
|
const { lines, label } = getToolDetails(confirm.toolName, confirm.params);
|
|
3225
3138
|
if (feedbackMode) {
|
|
3226
|
-
return /* @__PURE__ */
|
|
3227
|
-
/* @__PURE__ */
|
|
3139
|
+
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", paddingX: 0, marginY: 1, children: [
|
|
3140
|
+
/* @__PURE__ */ jsx6(Box6, { backgroundColor: "#fe8019", paddingX: 1, children: /* @__PURE__ */ jsx6(Text5, { color: "#282828", bold: true, children: " FEEDBACK " }) }),
|
|
3141
|
+
/* @__PURE__ */ jsxs5(
|
|
3228
3142
|
Box6,
|
|
3229
3143
|
{
|
|
3230
3144
|
flexDirection: "column",
|
|
3231
3145
|
borderStyle: "round",
|
|
3232
|
-
borderColor: "
|
|
3146
|
+
borderColor: "#fe8019",
|
|
3233
3147
|
paddingX: 1,
|
|
3234
3148
|
children: [
|
|
3235
|
-
/* @__PURE__ */ jsx6(
|
|
3236
|
-
lines.map((line, i) => /* @__PURE__ */ jsx6(
|
|
3149
|
+
/* @__PURE__ */ jsx6(Text5, { bold: true, color: "#fabd2f", children: label }),
|
|
3150
|
+
lines.map((line, i) => /* @__PURE__ */ jsx6(Text5, { color: "#a89984", children: line }, i)),
|
|
3151
|
+
/* @__PURE__ */ jsxs5(Box6, { marginTop: 1, gap: 1, children: [
|
|
3152
|
+
/* @__PURE__ */ jsx6(Text5, { color: "#83a598", bold: true, children: "Reason: " }),
|
|
3153
|
+
/* @__PURE__ */ jsx6(
|
|
3154
|
+
FeedbackInput,
|
|
3155
|
+
{
|
|
3156
|
+
onSubmit: (text) => {
|
|
3157
|
+
const trimmed = text.trim();
|
|
3158
|
+
if (trimmed) {
|
|
3159
|
+
onRespond({ feedback: trimmed });
|
|
3160
|
+
} else {
|
|
3161
|
+
setFeedbackMode(false);
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
)
|
|
3166
|
+
] })
|
|
3237
3167
|
]
|
|
3238
3168
|
}
|
|
3239
3169
|
),
|
|
3240
|
-
/* @__PURE__ */
|
|
3241
|
-
/* @__PURE__ */ jsx6(Text6, { color: "cyan", children: "\u53CD\u9988: " }),
|
|
3242
|
-
/* @__PURE__ */ jsx6(
|
|
3243
|
-
FeedbackInput,
|
|
3244
|
-
{
|
|
3245
|
-
onSubmit: (text) => {
|
|
3246
|
-
const trimmed = text.trim();
|
|
3247
|
-
if (trimmed) {
|
|
3248
|
-
onRespond({ feedback: trimmed });
|
|
3249
|
-
} else {
|
|
3250
|
-
setFeedbackMode(false);
|
|
3251
|
-
}
|
|
3252
|
-
}
|
|
3253
|
-
}
|
|
3254
|
-
)
|
|
3255
|
-
] }),
|
|
3256
|
-
/* @__PURE__ */ jsx6(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Enter \u53D1\u9001 Esc \u8FD4\u56DE\u9009\u62E9" }) })
|
|
3170
|
+
/* @__PURE__ */ jsx6(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx6(Text5, { dimColor: true, children: "Enter to send \xB7 Esc to cancel" }) })
|
|
3257
3171
|
] });
|
|
3258
3172
|
}
|
|
3259
|
-
return /* @__PURE__ */
|
|
3260
|
-
/* @__PURE__ */
|
|
3173
|
+
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", paddingX: 0, marginY: 1, children: [
|
|
3174
|
+
/* @__PURE__ */ jsx6(Box6, { backgroundColor: "#fe8019", paddingX: 1, children: /* @__PURE__ */ jsx6(Text5, { color: "#282828", bold: true, children: " CONFIRMATION REQUIRED " }) }),
|
|
3175
|
+
/* @__PURE__ */ jsxs5(
|
|
3261
3176
|
Box6,
|
|
3262
3177
|
{
|
|
3263
3178
|
flexDirection: "column",
|
|
3264
3179
|
borderStyle: "round",
|
|
3265
|
-
borderColor: "
|
|
3180
|
+
borderColor: "#fe8019",
|
|
3266
3181
|
paddingX: 1,
|
|
3267
3182
|
children: [
|
|
3268
|
-
/* @__PURE__ */ jsx6(
|
|
3269
|
-
lines.map((line, i) => /* @__PURE__ */ jsx6(
|
|
3183
|
+
/* @__PURE__ */ jsx6(Text5, { bold: true, color: "#fabd2f", children: label }),
|
|
3184
|
+
lines.map((line, i) => /* @__PURE__ */ jsx6(Text5, { color: "#a89984", children: line }, i)),
|
|
3185
|
+
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, gap: 2, justifyContent: "center", children: OPTIONS.map((opt, i) => {
|
|
3186
|
+
const isSelected = i === selected;
|
|
3187
|
+
const optColor = opt.key === "deny" ? "#fb4934" : opt.key === "allow" ? "#b8bb26" : "#8ec07c";
|
|
3188
|
+
return /* @__PURE__ */ jsx6(Box6, { children: isSelected ? /* @__PURE__ */ jsxs5(Text5, { bold: true, backgroundColor: optColor, color: "#282828", children: [
|
|
3189
|
+
" ",
|
|
3190
|
+
opt.label,
|
|
3191
|
+
" "
|
|
3192
|
+
] }) : /* @__PURE__ */ jsxs5(Text5, { color: optColor, children: [
|
|
3193
|
+
" ",
|
|
3194
|
+
opt.label,
|
|
3195
|
+
" "
|
|
3196
|
+
] }) }, opt.key);
|
|
3197
|
+
}) })
|
|
3270
3198
|
]
|
|
3271
3199
|
}
|
|
3272
3200
|
),
|
|
3273
|
-
/* @__PURE__ */ jsx6(Box6, {
|
|
3274
|
-
const isSelected = i === selected;
|
|
3275
|
-
return /* @__PURE__ */ jsx6(Box6, { children: isSelected ? /* @__PURE__ */ jsxs6(Text6, { bold: true, color: "cyan", inverse: true, children: [
|
|
3276
|
-
" ",
|
|
3277
|
-
opt.label,
|
|
3278
|
-
" "
|
|
3279
|
-
] }) : /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
3280
|
-
" ",
|
|
3281
|
-
opt.label,
|
|
3282
|
-
" "
|
|
3283
|
-
] }) }, opt.key);
|
|
3284
|
-
}) }),
|
|
3285
|
-
/* @__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" }) })
|
|
3201
|
+
/* @__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" }) })
|
|
3286
3202
|
] });
|
|
3287
3203
|
}
|
|
3288
3204
|
var OPTIONS;
|
|
@@ -3298,55 +3214,57 @@ var init_ConfirmPrompt = __esm({
|
|
|
3298
3214
|
});
|
|
3299
3215
|
|
|
3300
3216
|
// src/cli/tui/components/TodoPanel.tsx
|
|
3301
|
-
import { Box as Box7, Text as
|
|
3302
|
-
import { jsx as jsx7, jsxs as
|
|
3217
|
+
import { Box as Box7, Text as Text6 } from "ink";
|
|
3218
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
3303
3219
|
function getStatusIcon(status) {
|
|
3304
3220
|
switch (status) {
|
|
3305
3221
|
case "completed":
|
|
3306
|
-
return "\
|
|
3222
|
+
return "\u2705";
|
|
3307
3223
|
case "in-progress":
|
|
3308
|
-
return "\
|
|
3224
|
+
return "\u23F3";
|
|
3309
3225
|
default:
|
|
3310
|
-
return "\
|
|
3226
|
+
return "\u26AA";
|
|
3311
3227
|
}
|
|
3312
3228
|
}
|
|
3313
3229
|
function getStatusColor(status) {
|
|
3314
3230
|
switch (status) {
|
|
3315
3231
|
case "completed":
|
|
3316
|
-
return "
|
|
3232
|
+
return "#b8bb26";
|
|
3317
3233
|
case "in-progress":
|
|
3318
|
-
return "
|
|
3234
|
+
return "#fabd2f";
|
|
3319
3235
|
default:
|
|
3320
|
-
return "
|
|
3236
|
+
return "#928374";
|
|
3321
3237
|
}
|
|
3322
3238
|
}
|
|
3323
3239
|
function TodoPanel({ plan }) {
|
|
3324
3240
|
const completed = plan.items.filter((i) => i.status === "completed").length;
|
|
3325
3241
|
const total = plan.items.length;
|
|
3326
|
-
return /* @__PURE__ */
|
|
3242
|
+
return /* @__PURE__ */ jsxs6(
|
|
3327
3243
|
Box7,
|
|
3328
3244
|
{
|
|
3329
3245
|
flexDirection: "column",
|
|
3330
3246
|
borderStyle: "round",
|
|
3331
|
-
borderColor: "
|
|
3247
|
+
borderColor: "#83a598",
|
|
3332
3248
|
paddingX: 1,
|
|
3333
|
-
|
|
3249
|
+
marginY: 1,
|
|
3334
3250
|
children: [
|
|
3335
|
-
/* @__PURE__ */
|
|
3336
|
-
/* @__PURE__ */ jsx7(
|
|
3337
|
-
/* @__PURE__ */
|
|
3251
|
+
/* @__PURE__ */ jsxs6(Box7, { justifyContent: "space-between", marginBottom: 1, children: [
|
|
3252
|
+
/* @__PURE__ */ jsx7(Text6, { bold: true, color: "#83a598", children: "\u{1F4CB} PROJECT PLAN" }),
|
|
3253
|
+
/* @__PURE__ */ jsxs6(Text6, { color: "#ebdbb2", children: [
|
|
3338
3254
|
completed,
|
|
3339
3255
|
"/",
|
|
3340
|
-
total
|
|
3256
|
+
total,
|
|
3257
|
+
" tasks"
|
|
3341
3258
|
] })
|
|
3342
3259
|
] }),
|
|
3343
|
-
plan.items.map((item) => /* @__PURE__ */
|
|
3344
|
-
/* @__PURE__ */ jsx7(
|
|
3260
|
+
plan.items.map((item) => /* @__PURE__ */ jsxs6(Box7, { gap: 1, children: [
|
|
3261
|
+
/* @__PURE__ */ jsx7(Text6, { color: getStatusColor(item.status), children: getStatusIcon(item.status) }),
|
|
3345
3262
|
/* @__PURE__ */ jsx7(
|
|
3346
|
-
|
|
3263
|
+
Text6,
|
|
3347
3264
|
{
|
|
3348
|
-
color: item.status === "completed" ? "
|
|
3265
|
+
color: item.status === "completed" ? "#b8bb26" : item.status === "in-progress" ? "#fabd2f" : "#ebdbb2",
|
|
3349
3266
|
dimColor: item.status === "pending",
|
|
3267
|
+
strikethrough: item.status === "completed",
|
|
3350
3268
|
children: item.title
|
|
3351
3269
|
}
|
|
3352
3270
|
)
|
|
@@ -3361,26 +3279,62 @@ var init_TodoPanel = __esm({
|
|
|
3361
3279
|
}
|
|
3362
3280
|
});
|
|
3363
3281
|
|
|
3282
|
+
// src/cli/tui/components/Header.tsx
|
|
3283
|
+
import { Box as Box8, Text as Text7 } from "ink";
|
|
3284
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
3285
|
+
function Header({ modelName }) {
|
|
3286
|
+
return /* @__PURE__ */ jsx8(
|
|
3287
|
+
Box8,
|
|
3288
|
+
{
|
|
3289
|
+
flexDirection: "column",
|
|
3290
|
+
width: "100%",
|
|
3291
|
+
borderStyle: "round",
|
|
3292
|
+
borderColor: "#504945",
|
|
3293
|
+
paddingX: 1,
|
|
3294
|
+
marginTop: 0,
|
|
3295
|
+
marginBottom: 1,
|
|
3296
|
+
children: /* @__PURE__ */ jsxs7(Box8, { justifyContent: "space-between", children: [
|
|
3297
|
+
/* @__PURE__ */ jsxs7(Box8, { gap: 1, children: [
|
|
3298
|
+
/* @__PURE__ */ jsx8(Text7, { bold: true, color: "#fe8019", children: "ZEN CODE" }),
|
|
3299
|
+
/* @__PURE__ */ jsx8(Text7, { color: "#a89984", dimColor: true, children: "v0.2.1" })
|
|
3300
|
+
] }),
|
|
3301
|
+
/* @__PURE__ */ jsx8(Text7, { color: "#83a598", bold: true, children: modelName })
|
|
3302
|
+
] })
|
|
3303
|
+
}
|
|
3304
|
+
);
|
|
3305
|
+
}
|
|
3306
|
+
var init_Header = __esm({
|
|
3307
|
+
"src/cli/tui/components/Header.tsx"() {
|
|
3308
|
+
"use strict";
|
|
3309
|
+
}
|
|
3310
|
+
});
|
|
3311
|
+
|
|
3364
3312
|
// src/cli/tui/App.tsx
|
|
3365
3313
|
import { useReducer, useCallback, useEffect, useRef as useRef2, useState as useState3 } from "react";
|
|
3366
|
-
import { Box as
|
|
3367
|
-
import { jsx as
|
|
3368
|
-
function App({ config, client, agent,
|
|
3314
|
+
import { Box as Box9, Text as Text8, useInput as useInput3, useStdout } from "ink";
|
|
3315
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
3316
|
+
function App({ config, client, agent, registry, todoStore, subAgentTracker, agentRegistry, skillRegistry }) {
|
|
3317
|
+
const { stdout } = useStdout();
|
|
3318
|
+
const [, setWidth] = useState3(stdout.columns);
|
|
3319
|
+
useEffect(() => {
|
|
3320
|
+
const onResize = () => {
|
|
3321
|
+
setWidth(stdout.columns);
|
|
3322
|
+
};
|
|
3323
|
+
stdout.on("resize", onResize);
|
|
3324
|
+
return () => {
|
|
3325
|
+
stdout.off("resize", onResize);
|
|
3326
|
+
};
|
|
3327
|
+
}, [stdout]);
|
|
3369
3328
|
const [state, dispatch] = useReducer(
|
|
3370
3329
|
tuiReducer,
|
|
3371
|
-
createInitialState(
|
|
3372
|
-
config.model,
|
|
3373
|
-
config.agent_mode,
|
|
3374
|
-
config.collaboration
|
|
3375
|
-
)
|
|
3330
|
+
createInitialState(config.model)
|
|
3376
3331
|
);
|
|
3377
3332
|
const [resetKey, setResetKey] = useState3(0);
|
|
3333
|
+
const currentCallbacksRef = useRef2(null);
|
|
3378
3334
|
const agentRef = useRef2(agent);
|
|
3379
|
-
const orchestratorRef = useRef2(orchestrator);
|
|
3380
3335
|
const todoStoreRef = useRef2(todoStore);
|
|
3381
3336
|
const subAgentTrackerRef = useRef2(subAgentTracker);
|
|
3382
3337
|
agentRef.current = agent;
|
|
3383
|
-
orchestratorRef.current = orchestrator;
|
|
3384
3338
|
todoStoreRef.current = todoStore;
|
|
3385
3339
|
subAgentTrackerRef.current = subAgentTracker;
|
|
3386
3340
|
useEffect(() => {
|
|
@@ -3420,7 +3374,7 @@ function App({ config, client, agent, orchestrator, registry, todoStore, memoSto
|
|
|
3420
3374
|
);
|
|
3421
3375
|
useEffect(() => {
|
|
3422
3376
|
setStructuredConfirmHandler((toolName, params) => {
|
|
3423
|
-
return new Promise((
|
|
3377
|
+
return new Promise((resolve10) => {
|
|
3424
3378
|
const id = `confirm-${Date.now()}`;
|
|
3425
3379
|
registerConfirmToolId(toolName, id);
|
|
3426
3380
|
dispatch({
|
|
@@ -3431,13 +3385,13 @@ function App({ config, client, agent, orchestrator, registry, todoStore, memoSto
|
|
|
3431
3385
|
resolve: (result) => {
|
|
3432
3386
|
if (result === "always") {
|
|
3433
3387
|
registry.addAutoApprove(toolName);
|
|
3434
|
-
|
|
3388
|
+
resolve10({ approved: true });
|
|
3435
3389
|
} else if (result === "allow") {
|
|
3436
|
-
|
|
3390
|
+
resolve10({ approved: true });
|
|
3437
3391
|
} else if (result === "deny") {
|
|
3438
|
-
|
|
3392
|
+
resolve10({ approved: false });
|
|
3439
3393
|
} else {
|
|
3440
|
-
|
|
3394
|
+
resolve10({ approved: false, feedback: result.feedback });
|
|
3441
3395
|
}
|
|
3442
3396
|
}
|
|
3443
3397
|
});
|
|
@@ -3447,39 +3401,53 @@ function App({ config, client, agent, orchestrator, registry, todoStore, memoSto
|
|
|
3447
3401
|
setStructuredConfirmHandler(null);
|
|
3448
3402
|
};
|
|
3449
3403
|
}, [registry]);
|
|
3404
|
+
const runAgent = useCallback(async (text) => {
|
|
3405
|
+
dispatch({ type: "ADD_USER_MESSAGE", text });
|
|
3406
|
+
dispatch({ type: "START_ASSISTANT" });
|
|
3407
|
+
dispatch({ type: "SET_RUNNING", running: true });
|
|
3408
|
+
const callbacks = createBridgeCallbacks(dispatch);
|
|
3409
|
+
currentCallbacksRef.current = callbacks;
|
|
3410
|
+
try {
|
|
3411
|
+
await agentRef.current.run(text, callbacks);
|
|
3412
|
+
} catch (err) {
|
|
3413
|
+
if (!isAbortError(err)) {
|
|
3414
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3415
|
+
dispatch({ type: "SET_ERROR", error: msg });
|
|
3416
|
+
}
|
|
3417
|
+
} finally {
|
|
3418
|
+
currentCallbacksRef.current = null;
|
|
3419
|
+
}
|
|
3420
|
+
callbacks._stopBatcher?.();
|
|
3421
|
+
dispatch({ type: "FINISH_STREAMING" });
|
|
3422
|
+
dispatch({ type: "SET_RUNNING", running: false });
|
|
3423
|
+
}, []);
|
|
3450
3424
|
const handleSubmit = useCallback(async (text) => {
|
|
3451
3425
|
if (text.startsWith("/")) {
|
|
3426
|
+
const parts = text.slice(1).split(/\s+/);
|
|
3427
|
+
const skillName = parts[0];
|
|
3428
|
+
const skillArgs = parts.slice(1).join(" ");
|
|
3429
|
+
const skill = skillRegistry.get(skillName);
|
|
3430
|
+
if (skill) {
|
|
3431
|
+
const expandedPrompt = skillRegistry.expandPrompt(skill, skillArgs);
|
|
3432
|
+
await runAgent(expandedPrompt);
|
|
3433
|
+
return;
|
|
3434
|
+
}
|
|
3452
3435
|
handleSlashCommand2(text, {
|
|
3453
3436
|
config,
|
|
3454
|
-
|
|
3437
|
+
agent: agentRef.current,
|
|
3455
3438
|
registry,
|
|
3456
3439
|
dispatch,
|
|
3457
3440
|
setResetKey,
|
|
3458
3441
|
client,
|
|
3459
3442
|
todoStore,
|
|
3460
|
-
|
|
3461
|
-
|
|
3443
|
+
subAgentTracker,
|
|
3444
|
+
agentRegistry,
|
|
3445
|
+
skillRegistry
|
|
3462
3446
|
});
|
|
3463
3447
|
return;
|
|
3464
3448
|
}
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
dispatch({ type: "SET_RUNNING", running: true });
|
|
3468
|
-
const callbacks = createBridgeCallbacks(dispatch);
|
|
3469
|
-
try {
|
|
3470
|
-
if (config.agent_mode === "single") {
|
|
3471
|
-
await agentRef.current.run(text, callbacks);
|
|
3472
|
-
} else {
|
|
3473
|
-
await orchestratorRef.current.run(text, callbacks);
|
|
3474
|
-
}
|
|
3475
|
-
} catch (err) {
|
|
3476
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
3477
|
-
dispatch({ type: "SET_ERROR", error: msg });
|
|
3478
|
-
}
|
|
3479
|
-
callbacks._stopBatcher?.();
|
|
3480
|
-
dispatch({ type: "FINISH_STREAMING" });
|
|
3481
|
-
dispatch({ type: "SET_RUNNING", running: false });
|
|
3482
|
-
}, [config]);
|
|
3449
|
+
await runAgent(text);
|
|
3450
|
+
}, [config, runAgent]);
|
|
3483
3451
|
const handleConfirmResponse = useCallback((result) => {
|
|
3484
3452
|
if (confirmPending) {
|
|
3485
3453
|
confirmPending.resolve(result);
|
|
@@ -3497,114 +3465,118 @@ function App({ config, client, agent, orchestrator, registry, todoStore, memoSto
|
|
|
3497
3465
|
}
|
|
3498
3466
|
}, [confirmPending, state.messages]);
|
|
3499
3467
|
useInput3((input, key) => {
|
|
3500
|
-
if (
|
|
3468
|
+
if (key.escape) {
|
|
3501
3469
|
if (state.isRunning) {
|
|
3470
|
+
agentRef.current.interrupt();
|
|
3471
|
+
currentCallbacksRef.current?._stopBatcher?.();
|
|
3502
3472
|
dispatch({ type: "SET_RUNNING", running: false });
|
|
3503
3473
|
dispatch({ type: "FINISH_STREAMING" });
|
|
3504
|
-
} else {
|
|
3505
|
-
process.exit(0);
|
|
3506
3474
|
}
|
|
3507
3475
|
return;
|
|
3508
3476
|
}
|
|
3477
|
+
if (input === "c" && key.ctrl) {
|
|
3478
|
+
if (state.isRunning) {
|
|
3479
|
+
agentRef.current.interrupt();
|
|
3480
|
+
currentCallbacksRef.current?._stopBatcher?.();
|
|
3481
|
+
dispatch({ type: "SET_RUNNING", running: false });
|
|
3482
|
+
dispatch({ type: "FINISH_STREAMING" });
|
|
3483
|
+
return;
|
|
3484
|
+
}
|
|
3485
|
+
}
|
|
3509
3486
|
if (input === "d" && key.ctrl) {
|
|
3510
3487
|
process.exit(0);
|
|
3511
3488
|
}
|
|
3512
3489
|
});
|
|
3513
|
-
return /* @__PURE__ */ jsxs8(
|
|
3514
|
-
/* @__PURE__ */
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
]
|
|
3526
|
-
}
|
|
3527
|
-
),
|
|
3528
|
-
confirmPending && /* @__PURE__ */ jsx8(
|
|
3529
|
-
ConfirmPrompt,
|
|
3530
|
-
{
|
|
3531
|
-
confirm: confirmPending,
|
|
3532
|
-
onRespond: handleConfirmResponse
|
|
3533
|
-
}
|
|
3534
|
-
),
|
|
3535
|
-
state.todoPlan && /* @__PURE__ */ jsx8(TodoPanel, { plan: state.todoPlan }),
|
|
3536
|
-
/* @__PURE__ */ jsx8(
|
|
3490
|
+
return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", paddingX: 0, width: "100%", children: [
|
|
3491
|
+
/* @__PURE__ */ jsxs8(Box9, { paddingX: 1, flexDirection: "column", children: [
|
|
3492
|
+
/* @__PURE__ */ jsx9(Header, { modelName: state.modelName }),
|
|
3493
|
+
/* @__PURE__ */ jsx9(ChatArea, { messages: state.messages })
|
|
3494
|
+
] }),
|
|
3495
|
+
state.error && /* @__PURE__ */ jsxs8(Box9, { borderStyle: "round", borderColor: "#fb4934", paddingX: 1, marginBottom: 1, children: [
|
|
3496
|
+
/* @__PURE__ */ jsx9(Text8, { color: "#fb4934", bold: true, children: "ERROR: " }),
|
|
3497
|
+
/* @__PURE__ */ jsx9(Text8, { color: "#fb4934", children: state.error })
|
|
3498
|
+
] }),
|
|
3499
|
+
confirmPending && /* @__PURE__ */ jsx9(ConfirmPrompt, { confirm: confirmPending, onRespond: handleConfirmResponse }),
|
|
3500
|
+
state.todoPlan && /* @__PURE__ */ jsx9(TodoPanel, { plan: state.todoPlan }),
|
|
3501
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(
|
|
3537
3502
|
InputArea,
|
|
3538
3503
|
{
|
|
3539
3504
|
onSubmit: handleSubmit,
|
|
3540
|
-
isRunning: state.isRunning || !!confirmPending
|
|
3505
|
+
isRunning: state.isRunning || !!confirmPending,
|
|
3506
|
+
onExitRequest: () => process.exit(0)
|
|
3541
3507
|
}
|
|
3542
|
-
),
|
|
3543
|
-
/* @__PURE__ */
|
|
3508
|
+
) }),
|
|
3509
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 0, children: /* @__PURE__ */ jsx9(
|
|
3544
3510
|
StatusBar,
|
|
3545
3511
|
{
|
|
3546
|
-
agentMode: state.agentMode,
|
|
3547
|
-
collaboration: state.collaboration,
|
|
3548
|
-
coderWorking: state.coderWorking,
|
|
3549
3512
|
isRunning: state.isRunning,
|
|
3550
3513
|
modelName: state.modelName,
|
|
3551
3514
|
todoPlan: state.todoPlan,
|
|
3552
3515
|
subAgentProgress: state.subAgentProgress
|
|
3553
3516
|
}
|
|
3554
|
-
)
|
|
3517
|
+
) })
|
|
3555
3518
|
] }, resetKey);
|
|
3556
3519
|
}
|
|
3557
3520
|
function handleSlashCommand2(input, ctx) {
|
|
3558
|
-
const { config,
|
|
3521
|
+
const { config, agent, registry, dispatch, setResetKey, client, todoStore, subAgentTracker, agentRegistry, skillRegistry } = ctx;
|
|
3559
3522
|
const parts = input.trim().split(/\s+/);
|
|
3560
3523
|
const command = parts[0];
|
|
3561
3524
|
switch (command) {
|
|
3562
3525
|
case "/help":
|
|
3563
3526
|
dispatch({ type: "ADD_USER_MESSAGE", text: input });
|
|
3564
3527
|
dispatch({ type: "START_ASSISTANT" });
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
text: `\u53EF\u7528\u547D\u4EE4:
|
|
3528
|
+
{
|
|
3529
|
+
let helpText = `\u53EF\u7528\u547D\u4EE4:
|
|
3568
3530
|
/help \u663E\u793A\u6B64\u5E2E\u52A9\u4FE1\u606F
|
|
3569
|
-
/
|
|
3570
|
-
/
|
|
3571
|
-
/dual \u5207\u6362\u5230\u53CC Agent \u6A21\u5F0F
|
|
3531
|
+
/skills \u5217\u51FA\u6240\u6709\u53EF\u7528\u6280\u80FD
|
|
3532
|
+
/agents \u5217\u51FA\u6240\u6709\u53EF\u7528\u5B50 Agent
|
|
3572
3533
|
/parallel \u5207\u6362\u5E76\u884C\u5B50 Agent \u529F\u80FD on/off
|
|
3573
3534
|
/todo \u5207\u6362 todo \u8BA1\u5212\u529F\u80FD on/off
|
|
3574
3535
|
/clear \u6E05\u7A7A\u5BF9\u8BDD\u5386\u53F2
|
|
3575
3536
|
/info \u663E\u793A\u5F53\u524D\u914D\u7F6E
|
|
3576
3537
|
Ctrl+C \u53D6\u6D88\u5F53\u524D\u8BF7\u6C42 / \u9000\u51FA
|
|
3577
|
-
Ctrl+D \u9000\u51FA
|
|
3578
|
-
|
|
3538
|
+
Ctrl+D \u9000\u51FA`;
|
|
3539
|
+
const skills = skillRegistry.list();
|
|
3540
|
+
if (skills.length > 0) {
|
|
3541
|
+
helpText += `
|
|
3542
|
+
|
|
3543
|
+
\u53EF\u7528\u6280\u80FD:
|
|
3544
|
+
${skills.map((s) => ` /${s.name} ${s.description}`).join("\n")}`;
|
|
3545
|
+
}
|
|
3546
|
+
dispatch({ type: "APPEND_CONTENT", text: helpText });
|
|
3547
|
+
}
|
|
3579
3548
|
dispatch({ type: "FINISH_STREAMING" });
|
|
3580
3549
|
break;
|
|
3581
|
-
case "/
|
|
3582
|
-
const
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
dispatch({
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
dispatch({ type: "FINISH_STREAMING" });
|
|
3592
|
-
} else if (["delegated", "autonomous", "controlled"].includes(mode)) {
|
|
3593
|
-
config.collaboration = mode;
|
|
3594
|
-
orchestrator?.setMode(mode);
|
|
3595
|
-
dispatch({ type: "SET_MODE", agentMode: config.agent_mode, collaboration: mode });
|
|
3550
|
+
case "/skills": {
|
|
3551
|
+
const skills = skillRegistry.list();
|
|
3552
|
+
dispatch({ type: "ADD_USER_MESSAGE", text: input });
|
|
3553
|
+
dispatch({ type: "START_ASSISTANT" });
|
|
3554
|
+
if (skills.length === 0) {
|
|
3555
|
+
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" });
|
|
3556
|
+
} else {
|
|
3557
|
+
const lines = skills.map((s) => ` /${s.name}: ${s.description}`);
|
|
3558
|
+
dispatch({ type: "APPEND_CONTENT", text: `\u53EF\u7528\u6280\u80FD (${skills.length}):
|
|
3559
|
+
${lines.join("\n")}` });
|
|
3596
3560
|
}
|
|
3561
|
+
dispatch({ type: "FINISH_STREAMING" });
|
|
3597
3562
|
break;
|
|
3598
3563
|
}
|
|
3599
|
-
case "/
|
|
3600
|
-
|
|
3601
|
-
dispatch({ type: "
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3564
|
+
case "/agents": {
|
|
3565
|
+
const agents = agentRegistry.list();
|
|
3566
|
+
dispatch({ type: "ADD_USER_MESSAGE", text: input });
|
|
3567
|
+
dispatch({ type: "START_ASSISTANT" });
|
|
3568
|
+
if (agents.length === 0) {
|
|
3569
|
+
dispatch({ type: "APPEND_CONTENT", text: "\u6682\u65E0\u53EF\u7528\u5B50 Agent\u3002" });
|
|
3570
|
+
} else {
|
|
3571
|
+
const lines = agents.map((a) => ` ${a.name}: ${a.description} [tools: ${a.tools.join(", ")}]`);
|
|
3572
|
+
dispatch({ type: "APPEND_CONTENT", text: `\u53EF\u7528\u5B50 Agent (${agents.length}):
|
|
3573
|
+
${lines.join("\n")}` });
|
|
3574
|
+
}
|
|
3575
|
+
dispatch({ type: "FINISH_STREAMING" });
|
|
3606
3576
|
break;
|
|
3577
|
+
}
|
|
3607
3578
|
case "/clear":
|
|
3579
|
+
agent.getConversation().clear();
|
|
3608
3580
|
dispatch({ type: "CLEAR_MESSAGES" });
|
|
3609
3581
|
setResetKey((prev) => prev + 1);
|
|
3610
3582
|
break;
|
|
@@ -3615,7 +3587,7 @@ function handleSlashCommand2(input, ctx) {
|
|
|
3615
3587
|
if (next === "off") {
|
|
3616
3588
|
registry.unregister("spawn-agents");
|
|
3617
3589
|
} else if (!registry.has("spawn-agents")) {
|
|
3618
|
-
registry.register(createSpawnAgentsTool(client, registry, config, subAgentTracker
|
|
3590
|
+
registry.register(createSpawnAgentsTool(client, registry, config, subAgentTracker));
|
|
3619
3591
|
}
|
|
3620
3592
|
dispatch({ type: "ADD_USER_MESSAGE", text: input });
|
|
3621
3593
|
dispatch({ type: "START_ASSISTANT" });
|
|
@@ -3644,9 +3616,9 @@ function handleSlashCommand2(input, ctx) {
|
|
|
3644
3616
|
dispatch({
|
|
3645
3617
|
type: "APPEND_CONTENT",
|
|
3646
3618
|
text: `\u6A21\u578B: ${config.model}
|
|
3647
|
-
|
|
3648
|
-
\
|
|
3649
|
-
\
|
|
3619
|
+
\u57FA\u7840 URL: ${config.base_url}
|
|
3620
|
+
\u5B50 Agent: ${agentRegistry.listNames().join(", ") || "\u65E0"}
|
|
3621
|
+
\u6280\u80FD: ${skillRegistry.listNames().map((n) => "/" + n).join(", ") || "\u65E0"}`
|
|
3650
3622
|
});
|
|
3651
3623
|
dispatch({ type: "FINISH_STREAMING" });
|
|
3652
3624
|
break;
|
|
@@ -3664,6 +3636,7 @@ Agent \u6A21\u5F0F: ${config.agent_mode}
|
|
|
3664
3636
|
var init_App = __esm({
|
|
3665
3637
|
"src/cli/tui/App.tsx"() {
|
|
3666
3638
|
"use strict";
|
|
3639
|
+
init_client();
|
|
3667
3640
|
init_permission();
|
|
3668
3641
|
init_state();
|
|
3669
3642
|
init_bridge();
|
|
@@ -3674,6 +3647,7 @@ var init_App = __esm({
|
|
|
3674
3647
|
init_StatusBar();
|
|
3675
3648
|
init_ConfirmPrompt();
|
|
3676
3649
|
init_TodoPanel();
|
|
3650
|
+
init_Header();
|
|
3677
3651
|
}
|
|
3678
3652
|
});
|
|
3679
3653
|
|
|
@@ -3683,10 +3657,14 @@ __export(tui_exports, {
|
|
|
3683
3657
|
startTui: () => startTui
|
|
3684
3658
|
});
|
|
3685
3659
|
import { render } from "ink";
|
|
3686
|
-
import { jsx as
|
|
3660
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
3687
3661
|
async function startTui(options) {
|
|
3688
3662
|
const { config } = options;
|
|
3689
|
-
const
|
|
3663
|
+
const agentRegistry = new SubAgentConfigRegistry();
|
|
3664
|
+
for (const agentConfig of loadAllAgentConfigs()) {
|
|
3665
|
+
agentRegistry.register(agentConfig);
|
|
3666
|
+
}
|
|
3667
|
+
const { systemPrompt } = await buildPrompt(config, agentRegistry.list());
|
|
3690
3668
|
const registry = new ToolRegistry();
|
|
3691
3669
|
registerCoreTools(registry);
|
|
3692
3670
|
registry.setPermissions(config.permissions);
|
|
@@ -3698,32 +3676,37 @@ async function startTui(options) {
|
|
|
3698
3676
|
maxTokens: config.max_tokens
|
|
3699
3677
|
});
|
|
3700
3678
|
const todoStore = new TodoStore();
|
|
3701
|
-
const memoStore = new MemoStore();
|
|
3702
3679
|
const subAgentTracker = new SubAgentTracker();
|
|
3680
|
+
const skillRegistry = new SkillRegistry();
|
|
3681
|
+
for (const skill of loadAllSkills()) {
|
|
3682
|
+
skillRegistry.register(skill);
|
|
3683
|
+
}
|
|
3703
3684
|
if (config.features.parallel_agents === "on") {
|
|
3704
|
-
registry.register(createSpawnAgentsTool(client, registry, config, subAgentTracker
|
|
3685
|
+
registry.register(createSpawnAgentsTool(client, registry, config, subAgentTracker));
|
|
3705
3686
|
}
|
|
3706
3687
|
if (config.features.todo === "on") {
|
|
3707
3688
|
registry.register(createTodoTool(todoStore));
|
|
3708
3689
|
}
|
|
3709
|
-
registry.register(
|
|
3690
|
+
registry.register(createDispatchTool(client, registry, config, agentRegistry));
|
|
3710
3691
|
const agent = new Agent(client, registry, config, systemPrompt);
|
|
3711
|
-
const orchestrator = new Orchestrator(registry, config, systemPrompt, memoStore);
|
|
3712
3692
|
const { waitUntilExit } = render(
|
|
3713
|
-
/* @__PURE__ */
|
|
3693
|
+
/* @__PURE__ */ jsx10(
|
|
3714
3694
|
App,
|
|
3715
3695
|
{
|
|
3716
3696
|
config,
|
|
3717
3697
|
client,
|
|
3718
3698
|
agent,
|
|
3719
|
-
orchestrator,
|
|
3720
3699
|
registry,
|
|
3721
3700
|
todoStore,
|
|
3722
|
-
|
|
3723
|
-
|
|
3701
|
+
subAgentTracker,
|
|
3702
|
+
agentRegistry,
|
|
3703
|
+
skillRegistry
|
|
3724
3704
|
}
|
|
3725
3705
|
),
|
|
3726
|
-
{
|
|
3706
|
+
{
|
|
3707
|
+
patchConsole: true,
|
|
3708
|
+
exitOnCtrlC: false
|
|
3709
|
+
}
|
|
3727
3710
|
);
|
|
3728
3711
|
await waitUntilExit();
|
|
3729
3712
|
}
|
|
@@ -3734,41 +3717,129 @@ var init_tui = __esm({
|
|
|
3734
3717
|
init_registry();
|
|
3735
3718
|
init_register();
|
|
3736
3719
|
init_agent();
|
|
3737
|
-
init_orchestrator();
|
|
3738
3720
|
init_builder();
|
|
3739
3721
|
init_todo_store();
|
|
3740
|
-
init_memo_store();
|
|
3741
3722
|
init_sub_agent_tracker();
|
|
3723
|
+
init_registry2();
|
|
3724
|
+
init_loader();
|
|
3725
|
+
init_registry3();
|
|
3726
|
+
init_loader2();
|
|
3742
3727
|
init_spawn_agents();
|
|
3743
3728
|
init_todo();
|
|
3744
|
-
|
|
3729
|
+
init_dispatch();
|
|
3745
3730
|
init_App();
|
|
3746
3731
|
}
|
|
3747
3732
|
});
|
|
3748
3733
|
|
|
3749
3734
|
// src/cli/index.ts
|
|
3750
|
-
|
|
3735
|
+
import { Command } from "commander";
|
|
3736
|
+
|
|
3737
|
+
// src/config/loader.ts
|
|
3738
|
+
import * as fs from "fs";
|
|
3739
|
+
import * as path from "path";
|
|
3740
|
+
import * as os from "os";
|
|
3741
|
+
import { parse as parseYaml } from "yaml";
|
|
3742
|
+
|
|
3743
|
+
// src/config/defaults.ts
|
|
3744
|
+
var DEFAULT_CONFIG = {
|
|
3745
|
+
model: "deepseek-chat",
|
|
3746
|
+
api_key: "",
|
|
3747
|
+
base_url: "https://api.deepseek.com/v1",
|
|
3748
|
+
temperature: 0.7,
|
|
3749
|
+
max_tokens: 8192,
|
|
3750
|
+
features: {
|
|
3751
|
+
git: "auto",
|
|
3752
|
+
mcp: "off",
|
|
3753
|
+
planning_layer: "on",
|
|
3754
|
+
parallel_agents: "on",
|
|
3755
|
+
todo: "on"
|
|
3756
|
+
},
|
|
3757
|
+
permissions: {
|
|
3758
|
+
auto_approve: ["read-file", "glob", "grep", "spawn-agents", "todo", "dispatch"],
|
|
3759
|
+
require_approval: ["write-file", "edit-file", "bash", "git"]
|
|
3760
|
+
},
|
|
3761
|
+
mcp_servers: [],
|
|
3762
|
+
prompts: [],
|
|
3763
|
+
max_tool_output: 3e4
|
|
3764
|
+
};
|
|
3765
|
+
|
|
3766
|
+
// src/config/loader.ts
|
|
3767
|
+
function deepMerge(target, source) {
|
|
3768
|
+
const result = { ...target };
|
|
3769
|
+
for (const key of Object.keys(source)) {
|
|
3770
|
+
const sourceVal = source[key];
|
|
3771
|
+
const targetVal = target[key];
|
|
3772
|
+
if (sourceVal !== void 0 && sourceVal !== null && typeof sourceVal === "object" && !Array.isArray(sourceVal) && typeof targetVal === "object" && !Array.isArray(targetVal) && targetVal !== null) {
|
|
3773
|
+
result[key] = deepMerge(targetVal, sourceVal);
|
|
3774
|
+
} else if (sourceVal !== void 0) {
|
|
3775
|
+
result[key] = sourceVal;
|
|
3776
|
+
}
|
|
3777
|
+
}
|
|
3778
|
+
return result;
|
|
3779
|
+
}
|
|
3780
|
+
function loadYamlFile(filePath) {
|
|
3781
|
+
try {
|
|
3782
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
3783
|
+
return parseYaml(content) || {};
|
|
3784
|
+
} catch {
|
|
3785
|
+
return {};
|
|
3786
|
+
}
|
|
3787
|
+
}
|
|
3788
|
+
function loadEnvConfig() {
|
|
3789
|
+
const config = {};
|
|
3790
|
+
if (process.env["ZENCODE_API_KEY"]) {
|
|
3791
|
+
config.api_key = process.env["ZENCODE_API_KEY"];
|
|
3792
|
+
}
|
|
3793
|
+
if (process.env["ZENCODE_MODEL"]) {
|
|
3794
|
+
config.model = process.env["ZENCODE_MODEL"];
|
|
3795
|
+
}
|
|
3796
|
+
if (process.env["ZENCODE_BASE_URL"]) {
|
|
3797
|
+
config.base_url = process.env["ZENCODE_BASE_URL"];
|
|
3798
|
+
}
|
|
3799
|
+
return config;
|
|
3800
|
+
}
|
|
3801
|
+
function loadCliConfig(opts) {
|
|
3802
|
+
const config = {};
|
|
3803
|
+
if (opts.model) config.model = opts.model;
|
|
3804
|
+
if (opts.apiKey) config.api_key = opts.apiKey;
|
|
3805
|
+
if (opts.baseUrl) config.base_url = opts.baseUrl;
|
|
3806
|
+
return config;
|
|
3807
|
+
}
|
|
3808
|
+
function loadConfig(cliOpts = {}) {
|
|
3809
|
+
const globalConfigPath = path.join(os.homedir(), ".zencode", "config.yaml");
|
|
3810
|
+
const projectDirConfigPath = path.resolve(".zencode", "config.yaml");
|
|
3811
|
+
const projectFileConfigPath = path.resolve(".zencode.yaml");
|
|
3812
|
+
let config = { ...DEFAULT_CONFIG };
|
|
3813
|
+
config = deepMerge(config, loadYamlFile(globalConfigPath));
|
|
3814
|
+
config = deepMerge(config, loadYamlFile(projectDirConfigPath));
|
|
3815
|
+
config = deepMerge(config, loadYamlFile(projectFileConfigPath));
|
|
3816
|
+
config = deepMerge(config, loadEnvConfig());
|
|
3817
|
+
config = deepMerge(config, loadCliConfig(cliOpts));
|
|
3818
|
+
return config;
|
|
3819
|
+
}
|
|
3820
|
+
|
|
3821
|
+
// src/cli/index.ts
|
|
3751
3822
|
init_client();
|
|
3752
3823
|
init_registry();
|
|
3753
3824
|
init_register();
|
|
3754
3825
|
init_agent();
|
|
3755
|
-
init_orchestrator();
|
|
3756
3826
|
init_builder();
|
|
3757
|
-
import { Command } from "commander";
|
|
3758
3827
|
|
|
3759
3828
|
// src/cli/repl.ts
|
|
3760
3829
|
init_client();
|
|
3761
3830
|
init_registry();
|
|
3762
3831
|
init_agent();
|
|
3763
|
-
init_orchestrator();
|
|
3764
3832
|
init_builder();
|
|
3765
3833
|
init_register();
|
|
3766
3834
|
init_permission();
|
|
3767
3835
|
init_todo_store();
|
|
3768
|
-
|
|
3836
|
+
init_registry2();
|
|
3837
|
+
init_loader();
|
|
3838
|
+
init_registry3();
|
|
3839
|
+
init_loader2();
|
|
3769
3840
|
init_spawn_agents();
|
|
3770
3841
|
init_todo();
|
|
3771
|
-
|
|
3842
|
+
init_dispatch();
|
|
3772
3843
|
init_bridge();
|
|
3773
3844
|
import * as readline from "readline";
|
|
3774
3845
|
|
|
@@ -3800,12 +3871,8 @@ function printToolCall(toolName, params) {
|
|
|
3800
3871
|
const action = String(params["action"]);
|
|
3801
3872
|
const id = params["id"] ? ` [${params["id"]}]` : "";
|
|
3802
3873
|
detail = ` ${chalk2.dim(`${action}${id}`)}`;
|
|
3803
|
-
} else if (toolName === "memo" && params["action"]) {
|
|
3804
|
-
const action = String(params["action"]);
|
|
3805
|
-
const key = params["key"] ? ` [${params["key"]}]` : "";
|
|
3806
|
-
detail = ` ${chalk2.dim(`${action}${key}`)}`;
|
|
3807
3874
|
}
|
|
3808
|
-
const icon = toolName === "spawn-agents" ? "\u26A1" : toolName === "todo" ? "\u{1F4CB}" :
|
|
3875
|
+
const icon = toolName === "spawn-agents" ? "\u26A1" : toolName === "todo" ? "\u{1F4CB}" : "\u2699";
|
|
3809
3876
|
console.log(chalk2.yellow(` ${icon} ${toolName}`) + detail);
|
|
3810
3877
|
}
|
|
3811
3878
|
function printToolResult(toolName, result, truncated) {
|
|
@@ -3828,10 +3895,9 @@ function printWarning(message) {
|
|
|
3828
3895
|
function printSuccess(message) {
|
|
3829
3896
|
console.log(chalk2.green(`\u2713 ${message}`));
|
|
3830
3897
|
}
|
|
3831
|
-
function printWelcome(modelName
|
|
3898
|
+
function printWelcome(modelName) {
|
|
3832
3899
|
console.log(chalk2.bold.cyan("\n ZenCode") + chalk2.dim(" - \u6781\u7B80 AI \u7F16\u7A0B\u52A9\u624B\n"));
|
|
3833
3900
|
console.log(chalk2.dim(` \u6A21\u578B: ${modelName}`));
|
|
3834
|
-
console.log(chalk2.dim(` \u6A21\u5F0F: ${mode}`));
|
|
3835
3901
|
console.log(chalk2.dim(` \u8F93\u5165 /help \u67E5\u770B\u547D\u4EE4\uFF0CCtrl+C \u9000\u51FA
|
|
3836
3902
|
`));
|
|
3837
3903
|
}
|
|
@@ -3845,9 +3911,8 @@ function handleSlashCommand(input, context) {
|
|
|
3845
3911
|
console.log(`
|
|
3846
3912
|
\u53EF\u7528\u547D\u4EE4:
|
|
3847
3913
|
/help \u663E\u793A\u6B64\u5E2E\u52A9\u4FE1\u606F
|
|
3848
|
-
/
|
|
3849
|
-
/
|
|
3850
|
-
/dual \u5207\u6362\u5230\u53CC Agent \u6A21\u5F0F
|
|
3914
|
+
/skills \u5217\u51FA\u6240\u6709\u53EF\u7528\u6280\u80FD\uFF08\u7528\u6237\u81EA\u5B9A\u4E49\u659C\u6760\u547D\u4EE4\uFF09
|
|
3915
|
+
/agents \u5217\u51FA\u6240\u6709\u53EF\u7528\u5B50 Agent
|
|
3851
3916
|
/parallel \u5207\u6362\u5E76\u884C\u5B50 Agent \u529F\u80FD on/off
|
|
3852
3917
|
/todo \u5207\u6362 todo \u8BA1\u5212\u529F\u80FD on/off
|
|
3853
3918
|
/clear \u6E05\u7A7A\u5BF9\u8BDD\u5386\u53F2
|
|
@@ -3855,33 +3920,33 @@ function handleSlashCommand(input, context) {
|
|
|
3855
3920
|
/exit \u9000\u51FA
|
|
3856
3921
|
`);
|
|
3857
3922
|
return true;
|
|
3858
|
-
case "/
|
|
3859
|
-
const
|
|
3860
|
-
if (
|
|
3861
|
-
printInfo(
|
|
3862
|
-
printInfo("\u53EF\u9009: delegated, autonomous, controlled");
|
|
3863
|
-
return true;
|
|
3864
|
-
}
|
|
3865
|
-
if (["delegated", "autonomous", "controlled"].includes(mode)) {
|
|
3866
|
-
context.config.collaboration = mode;
|
|
3867
|
-
context.setMode?.(mode);
|
|
3868
|
-
printSuccess(`\u5DF2\u5207\u6362\u5230 ${mode} \u6A21\u5F0F`);
|
|
3923
|
+
case "/skills": {
|
|
3924
|
+
const skills = context.skillRegistry.list();
|
|
3925
|
+
if (skills.length === 0) {
|
|
3926
|
+
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");
|
|
3869
3927
|
} else {
|
|
3870
|
-
|
|
3928
|
+
printInfo(`\u53EF\u7528\u6280\u80FD (${skills.length}):`);
|
|
3929
|
+
for (const s of skills) {
|
|
3930
|
+
printInfo(` /${s.name}: ${s.description}`);
|
|
3931
|
+
}
|
|
3871
3932
|
}
|
|
3872
3933
|
return true;
|
|
3873
3934
|
}
|
|
3874
|
-
case "/
|
|
3875
|
-
context.
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3935
|
+
case "/agents": {
|
|
3936
|
+
const agents = context.agentRegistry.list();
|
|
3937
|
+
if (agents.length === 0) {
|
|
3938
|
+
printInfo("\u6682\u65E0\u53EF\u7528\u5B50 Agent");
|
|
3939
|
+
} else {
|
|
3940
|
+
printInfo(`\u53EF\u7528\u5B50 Agent (${agents.length}):`);
|
|
3941
|
+
for (const a of agents) {
|
|
3942
|
+
printInfo(` ${a.name}: ${a.description}`);
|
|
3943
|
+
}
|
|
3944
|
+
}
|
|
3881
3945
|
return true;
|
|
3946
|
+
}
|
|
3882
3947
|
case "/clear":
|
|
3883
|
-
printSuccess("\u5BF9\u8BDD\u5386\u53F2\u5DF2\u6E05\u7A7A
|
|
3884
|
-
return
|
|
3948
|
+
printSuccess("\u5BF9\u8BDD\u5386\u53F2\u5DF2\u6E05\u7A7A");
|
|
3949
|
+
return "clear";
|
|
3885
3950
|
case "/parallel": {
|
|
3886
3951
|
const current = context.config.features.parallel_agents;
|
|
3887
3952
|
const next = current === "on" ? "off" : "on";
|
|
@@ -3890,7 +3955,7 @@ function handleSlashCommand(input, context) {
|
|
|
3890
3955
|
context.registry.unregister("spawn-agents");
|
|
3891
3956
|
} else if (!context.registry.has("spawn-agents")) {
|
|
3892
3957
|
context.registry.register(
|
|
3893
|
-
createSpawnAgentsTool(context.client, context.registry, context.config
|
|
3958
|
+
createSpawnAgentsTool(context.client, context.registry, context.config)
|
|
3894
3959
|
);
|
|
3895
3960
|
}
|
|
3896
3961
|
printSuccess(`\u5E76\u884C\u5B50 Agent \u529F\u80FD\u5DF2${next === "on" ? "\u5F00\u542F" : "\u5173\u95ED"}`);
|
|
@@ -3910,9 +3975,9 @@ function handleSlashCommand(input, context) {
|
|
|
3910
3975
|
}
|
|
3911
3976
|
case "/info":
|
|
3912
3977
|
printInfo(`\u6A21\u578B: ${context.config.model}`);
|
|
3913
|
-
printInfo(`Agent \u6A21\u5F0F: ${context.config.agent_mode}`);
|
|
3914
|
-
printInfo(`\u534F\u4F5C\u6A21\u5F0F: ${context.config.collaboration}`);
|
|
3915
3978
|
printInfo(`\u57FA\u7840 URL: ${context.config.base_url}`);
|
|
3979
|
+
printInfo(`\u5B50 Agent: ${context.agentRegistry.listNames().join(", ") || "\u65E0"}`);
|
|
3980
|
+
printInfo(`\u6280\u80FD: ${context.skillRegistry.listNames().join(", ") || "\u65E0"}`);
|
|
3916
3981
|
return true;
|
|
3917
3982
|
case "/exit":
|
|
3918
3983
|
process.exit(0);
|
|
@@ -3923,7 +3988,11 @@ function handleSlashCommand(input, context) {
|
|
|
3923
3988
|
}
|
|
3924
3989
|
async function startRepl(options) {
|
|
3925
3990
|
const { config } = options;
|
|
3926
|
-
const
|
|
3991
|
+
const agentRegistry = new SubAgentConfigRegistry();
|
|
3992
|
+
for (const agentConfig of loadAllAgentConfigs()) {
|
|
3993
|
+
agentRegistry.register(agentConfig);
|
|
3994
|
+
}
|
|
3995
|
+
const { systemPrompt } = await buildPrompt(config, agentRegistry.list());
|
|
3927
3996
|
const registry = new ToolRegistry();
|
|
3928
3997
|
registerCoreTools(registry);
|
|
3929
3998
|
registry.setPermissions(config.permissions);
|
|
@@ -3935,23 +4004,19 @@ async function startRepl(options) {
|
|
|
3935
4004
|
maxTokens: config.max_tokens
|
|
3936
4005
|
});
|
|
3937
4006
|
const todoStore = new TodoStore();
|
|
3938
|
-
const memoStore = new MemoStore();
|
|
3939
4007
|
if (config.features.parallel_agents === "on") {
|
|
3940
|
-
registry.register(createSpawnAgentsTool(client, registry, config
|
|
4008
|
+
registry.register(createSpawnAgentsTool(client, registry, config));
|
|
3941
4009
|
}
|
|
3942
4010
|
if (config.features.todo === "on") {
|
|
3943
4011
|
registry.register(createTodoTool(todoStore));
|
|
3944
4012
|
}
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
if (config.agent_mode === "single") {
|
|
3949
|
-
singleAgent = new Agent(client, registry, config, systemPrompt);
|
|
3950
|
-
} else {
|
|
3951
|
-
orchestrator = new Orchestrator(registry, config, systemPrompt, memoStore);
|
|
4013
|
+
const skillRegistry = new SkillRegistry();
|
|
4014
|
+
for (const skill of loadAllSkills()) {
|
|
4015
|
+
skillRegistry.register(skill);
|
|
3952
4016
|
}
|
|
3953
|
-
|
|
3954
|
-
|
|
4017
|
+
registry.register(createDispatchTool(client, registry, config, agentRegistry));
|
|
4018
|
+
let agent = new Agent(client, registry, config, systemPrompt);
|
|
4019
|
+
printWelcome(config.model);
|
|
3955
4020
|
const rl = readline.createInterface({
|
|
3956
4021
|
input: process.stdin,
|
|
3957
4022
|
output: process.stdout,
|
|
@@ -3959,13 +4024,13 @@ async function startRepl(options) {
|
|
|
3959
4024
|
historySize: 100
|
|
3960
4025
|
});
|
|
3961
4026
|
setConfirmHandler(async (promptText) => {
|
|
3962
|
-
return new Promise((
|
|
4027
|
+
return new Promise((resolve10) => {
|
|
3963
4028
|
process.stdout.write(promptText);
|
|
3964
4029
|
const onData = (data) => {
|
|
3965
4030
|
process.stdin.removeListener("data", onData);
|
|
3966
4031
|
process.stdin.pause();
|
|
3967
4032
|
const answer = data.toString().trim().toLowerCase();
|
|
3968
|
-
|
|
4033
|
+
resolve10(answer === "y");
|
|
3969
4034
|
};
|
|
3970
4035
|
process.stdin.resume();
|
|
3971
4036
|
process.stdin.once("data", onData);
|
|
@@ -3979,29 +4044,75 @@ async function startRepl(options) {
|
|
|
3979
4044
|
return;
|
|
3980
4045
|
}
|
|
3981
4046
|
if (input.startsWith("/")) {
|
|
4047
|
+
const slashParts = input.slice(1).split(/\s+/);
|
|
4048
|
+
const skillName = slashParts[0] ?? "";
|
|
4049
|
+
const skillArgs = slashParts.slice(1).join(" ");
|
|
4050
|
+
const skill = skillRegistry.get(skillName);
|
|
4051
|
+
if (skill) {
|
|
4052
|
+
const expandedPrompt = skillRegistry.expandPrompt(skill, skillArgs);
|
|
4053
|
+
rl.pause();
|
|
4054
|
+
let isStreaming2 = false;
|
|
4055
|
+
const thinkFilter2 = createThinkFilter();
|
|
4056
|
+
const callbacks2 = {
|
|
4057
|
+
onContent: (text) => {
|
|
4058
|
+
const filtered = thinkFilter2(text);
|
|
4059
|
+
if (!filtered) return;
|
|
4060
|
+
if (!isStreaming2) {
|
|
4061
|
+
isStreaming2 = true;
|
|
4062
|
+
process.stdout.write("\n");
|
|
4063
|
+
}
|
|
4064
|
+
printStream(filtered);
|
|
4065
|
+
},
|
|
4066
|
+
onToolExecuting: (name, params) => {
|
|
4067
|
+
if (isStreaming2) {
|
|
4068
|
+
process.stdout.write("\n");
|
|
4069
|
+
isStreaming2 = false;
|
|
4070
|
+
}
|
|
4071
|
+
printToolCall(name, params);
|
|
4072
|
+
},
|
|
4073
|
+
onToolResult: (name, result, truncated) => {
|
|
4074
|
+
printToolResult(name, result, truncated);
|
|
4075
|
+
},
|
|
4076
|
+
onError: (err) => {
|
|
4077
|
+
if (isStreaming2) {
|
|
4078
|
+
process.stdout.write("\n");
|
|
4079
|
+
isStreaming2 = false;
|
|
4080
|
+
}
|
|
4081
|
+
printError(err.message);
|
|
4082
|
+
}
|
|
4083
|
+
};
|
|
4084
|
+
try {
|
|
4085
|
+
await agent.run(expandedPrompt, callbacks2);
|
|
4086
|
+
} catch (err) {
|
|
4087
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4088
|
+
printError(msg);
|
|
4089
|
+
}
|
|
4090
|
+
if (isStreaming2) {
|
|
4091
|
+
process.stdout.write("\n");
|
|
4092
|
+
}
|
|
4093
|
+
console.log();
|
|
4094
|
+
rl.resume();
|
|
4095
|
+
rl.prompt();
|
|
4096
|
+
return;
|
|
4097
|
+
}
|
|
3982
4098
|
const handled = handleSlashCommand(input, {
|
|
3983
4099
|
config,
|
|
3984
|
-
orchestrator,
|
|
3985
4100
|
registry,
|
|
3986
4101
|
client,
|
|
3987
4102
|
todoStore,
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
orchestrator?.setMode(mode);
|
|
3991
|
-
}
|
|
4103
|
+
agentRegistry,
|
|
4104
|
+
skillRegistry
|
|
3992
4105
|
});
|
|
4106
|
+
if (handled === "clear") {
|
|
4107
|
+
agent = new Agent(client, registry, config, systemPrompt);
|
|
4108
|
+
rl.prompt();
|
|
4109
|
+
return;
|
|
4110
|
+
}
|
|
3993
4111
|
if (handled) {
|
|
3994
4112
|
rl.prompt();
|
|
3995
4113
|
return;
|
|
3996
4114
|
}
|
|
3997
4115
|
}
|
|
3998
|
-
if (config.agent_mode === "single" && !singleAgent) {
|
|
3999
|
-
singleAgent = new Agent(client, registry, config, systemPrompt);
|
|
4000
|
-
orchestrator = void 0;
|
|
4001
|
-
} else if (config.agent_mode === "dual" && !orchestrator) {
|
|
4002
|
-
orchestrator = new Orchestrator(registry, config, systemPrompt, memoStore);
|
|
4003
|
-
singleAgent = void 0;
|
|
4004
|
-
}
|
|
4005
4116
|
rl.pause();
|
|
4006
4117
|
let isStreaming = false;
|
|
4007
4118
|
const thinkFilter = createThinkFilter();
|
|
@@ -4025,16 +4136,6 @@ async function startRepl(options) {
|
|
|
4025
4136
|
onToolResult: (name, result, truncated) => {
|
|
4026
4137
|
printToolResult(name, result, truncated);
|
|
4027
4138
|
},
|
|
4028
|
-
onCoderStart: () => {
|
|
4029
|
-
if (isStreaming) {
|
|
4030
|
-
process.stdout.write("\n");
|
|
4031
|
-
isStreaming = false;
|
|
4032
|
-
}
|
|
4033
|
-
printInfo("\u7F16\u7801 Agent \u5F00\u59CB\u5DE5\u4F5C...");
|
|
4034
|
-
},
|
|
4035
|
-
onCoderEnd: () => {
|
|
4036
|
-
printInfo("\u7F16\u7801 Agent \u5B8C\u6210");
|
|
4037
|
-
},
|
|
4038
4139
|
onError: (err) => {
|
|
4039
4140
|
if (isStreaming) {
|
|
4040
4141
|
process.stdout.write("\n");
|
|
@@ -4044,11 +4145,7 @@ async function startRepl(options) {
|
|
|
4044
4145
|
}
|
|
4045
4146
|
};
|
|
4046
4147
|
try {
|
|
4047
|
-
|
|
4048
|
-
await singleAgent.run(input, callbacks);
|
|
4049
|
-
} else if (orchestrator) {
|
|
4050
|
-
await orchestrator.run(input, callbacks);
|
|
4051
|
-
}
|
|
4148
|
+
await agent.run(input, callbacks);
|
|
4052
4149
|
} catch (err) {
|
|
4053
4150
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4054
4151
|
printError(msg);
|
|
@@ -4068,13 +4165,18 @@ async function startRepl(options) {
|
|
|
4068
4165
|
|
|
4069
4166
|
// src/cli/index.ts
|
|
4070
4167
|
init_todo_store();
|
|
4071
|
-
|
|
4168
|
+
init_registry2();
|
|
4169
|
+
init_loader();
|
|
4072
4170
|
init_spawn_agents();
|
|
4073
4171
|
init_todo();
|
|
4074
|
-
|
|
4172
|
+
init_dispatch();
|
|
4075
4173
|
init_bridge();
|
|
4076
4174
|
async function runOnce(prompt, config) {
|
|
4077
|
-
const
|
|
4175
|
+
const agentRegistry = new SubAgentConfigRegistry();
|
|
4176
|
+
for (const agentConfig of loadAllAgentConfigs()) {
|
|
4177
|
+
agentRegistry.register(agentConfig);
|
|
4178
|
+
}
|
|
4179
|
+
const { systemPrompt } = await buildPrompt(config, agentRegistry.list());
|
|
4078
4180
|
const registry = new ToolRegistry();
|
|
4079
4181
|
registerCoreTools(registry);
|
|
4080
4182
|
registry.setPermissions(config.permissions);
|
|
@@ -4086,14 +4188,13 @@ async function runOnce(prompt, config) {
|
|
|
4086
4188
|
maxTokens: config.max_tokens
|
|
4087
4189
|
});
|
|
4088
4190
|
const todoStore = new TodoStore();
|
|
4089
|
-
const memoStore = new MemoStore();
|
|
4090
4191
|
if (config.features.parallel_agents === "on") {
|
|
4091
|
-
registry.register(createSpawnAgentsTool(client, registry, config
|
|
4192
|
+
registry.register(createSpawnAgentsTool(client, registry, config));
|
|
4092
4193
|
}
|
|
4093
4194
|
if (config.features.todo === "on") {
|
|
4094
4195
|
registry.register(createTodoTool(todoStore));
|
|
4095
4196
|
}
|
|
4096
|
-
registry.register(
|
|
4197
|
+
registry.register(createDispatchTool(client, registry, config, agentRegistry));
|
|
4097
4198
|
let isStreaming = false;
|
|
4098
4199
|
const thinkFilter = createThinkFilter();
|
|
4099
4200
|
const callbacks = {
|
|
@@ -4115,28 +4216,13 @@ async function runOnce(prompt, config) {
|
|
|
4115
4216
|
onToolResult: (name, result, truncated) => {
|
|
4116
4217
|
printToolResult(name, result, truncated);
|
|
4117
4218
|
},
|
|
4118
|
-
onCoderStart: () => {
|
|
4119
|
-
if (isStreaming) {
|
|
4120
|
-
process.stdout.write("\n");
|
|
4121
|
-
isStreaming = false;
|
|
4122
|
-
}
|
|
4123
|
-
printInfo("\u7F16\u7801 Agent \u5F00\u59CB\u5DE5\u4F5C...");
|
|
4124
|
-
},
|
|
4125
|
-
onCoderEnd: () => {
|
|
4126
|
-
printInfo("\u7F16\u7801 Agent \u5B8C\u6210");
|
|
4127
|
-
},
|
|
4128
4219
|
onError: (err) => {
|
|
4129
4220
|
printError(err.message);
|
|
4130
4221
|
}
|
|
4131
4222
|
};
|
|
4132
4223
|
try {
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
await agent.run(prompt, callbacks);
|
|
4136
|
-
} else {
|
|
4137
|
-
const orchestrator = new Orchestrator(registry, config, systemPrompt, memoStore);
|
|
4138
|
-
await orchestrator.run(prompt, callbacks);
|
|
4139
|
-
}
|
|
4224
|
+
const agent = new Agent(client, registry, config, systemPrompt);
|
|
4225
|
+
await agent.run(prompt, callbacks);
|
|
4140
4226
|
} catch (err) {
|
|
4141
4227
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4142
4228
|
printError(msg);
|
|
@@ -4148,7 +4234,7 @@ async function runOnce(prompt, config) {
|
|
|
4148
4234
|
}
|
|
4149
4235
|
function createCli() {
|
|
4150
4236
|
const program2 = new Command();
|
|
4151
|
-
program2.name("zencode").description("\u6781\u7B80 CLI AI \u7F16\u7A0B\u5DE5\u5177").version("0.1
|
|
4237
|
+
program2.name("zencode").description("\u6781\u7B80 CLI AI \u7F16\u7A0B\u5DE5\u5177").version("0.2.1").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) => {
|
|
4152
4238
|
const config = loadConfig(opts);
|
|
4153
4239
|
if (!config.api_key) {
|
|
4154
4240
|
printError("\u672A\u8BBE\u7F6E API \u5BC6\u94A5\u3002\u8BF7\u901A\u8FC7\u4EE5\u4E0B\u65B9\u5F0F\u4E4B\u4E00\u8BBE\u7F6E\uFF1A");
|