zencode-cli 0.2.1 → 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 +117 -287
- package/dist/bin/zencode.js +1393 -1559
- package/dist/bin/zencode.js.map +1 -1
- package/package.json +56 -56
- package/DESIGN.md +0 -613
package/dist/bin/zencode.js
CHANGED
|
@@ -9,129 +9,16 @@ var __export = (target, all) => {
|
|
|
9
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
// src/config/defaults.ts
|
|
13
|
-
var DEFAULT_CONFIG;
|
|
14
|
-
var init_defaults = __esm({
|
|
15
|
-
"src/config/defaults.ts"() {
|
|
16
|
-
"use strict";
|
|
17
|
-
DEFAULT_CONFIG = {
|
|
18
|
-
model: "deepseek-chat",
|
|
19
|
-
api_key: "",
|
|
20
|
-
base_url: "https://api.deepseek.com/v1",
|
|
21
|
-
temperature: 0.7,
|
|
22
|
-
max_tokens: 8192,
|
|
23
|
-
agent_mode: "dual",
|
|
24
|
-
collaboration: "delegated",
|
|
25
|
-
dual_agent: {},
|
|
26
|
-
features: {
|
|
27
|
-
git: "auto",
|
|
28
|
-
mcp: "off",
|
|
29
|
-
planning_layer: "on",
|
|
30
|
-
parallel_agents: "on",
|
|
31
|
-
todo: "on"
|
|
32
|
-
},
|
|
33
|
-
permissions: {
|
|
34
|
-
auto_approve: ["read-file", "glob", "grep", "spawn-agents", "todo", "memo"],
|
|
35
|
-
require_approval: ["write-file", "edit-file", "bash", "git"]
|
|
36
|
-
},
|
|
37
|
-
mcp_servers: [],
|
|
38
|
-
prompts: [],
|
|
39
|
-
max_tool_output: 3e4
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
// src/config/loader.ts
|
|
45
|
-
import * as fs from "fs";
|
|
46
|
-
import * as path from "path";
|
|
47
|
-
import * as os from "os";
|
|
48
|
-
import { parse as parseYaml } from "yaml";
|
|
49
|
-
function deepMerge(target, source) {
|
|
50
|
-
const result = { ...target };
|
|
51
|
-
for (const key of Object.keys(source)) {
|
|
52
|
-
const sourceVal = source[key];
|
|
53
|
-
const targetVal = target[key];
|
|
54
|
-
if (sourceVal !== void 0 && sourceVal !== null && typeof sourceVal === "object" && !Array.isArray(sourceVal) && typeof targetVal === "object" && !Array.isArray(targetVal) && targetVal !== null) {
|
|
55
|
-
result[key] = deepMerge(targetVal, sourceVal);
|
|
56
|
-
} else if (sourceVal !== void 0) {
|
|
57
|
-
result[key] = sourceVal;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return result;
|
|
61
|
-
}
|
|
62
|
-
function loadYamlFile(filePath) {
|
|
63
|
-
try {
|
|
64
|
-
const content = fs.readFileSync(filePath, "utf-8");
|
|
65
|
-
return parseYaml(content) || {};
|
|
66
|
-
} catch {
|
|
67
|
-
return {};
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
function loadEnvConfig() {
|
|
71
|
-
const config = {};
|
|
72
|
-
if (process.env["ZENCODE_API_KEY"]) {
|
|
73
|
-
config.api_key = process.env["ZENCODE_API_KEY"];
|
|
74
|
-
}
|
|
75
|
-
if (process.env["ZENCODE_MODEL"]) {
|
|
76
|
-
config.model = process.env["ZENCODE_MODEL"];
|
|
77
|
-
}
|
|
78
|
-
if (process.env["ZENCODE_BASE_URL"]) {
|
|
79
|
-
config.base_url = process.env["ZENCODE_BASE_URL"];
|
|
80
|
-
}
|
|
81
|
-
if (process.env["ZENCODE_MODE"]) {
|
|
82
|
-
const mode = process.env["ZENCODE_MODE"];
|
|
83
|
-
if (mode === "dual" || mode === "single") {
|
|
84
|
-
config.agent_mode = mode;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return config;
|
|
88
|
-
}
|
|
89
|
-
function loadCliConfig(opts) {
|
|
90
|
-
const config = {};
|
|
91
|
-
if (opts.model) config.model = opts.model;
|
|
92
|
-
if (opts.apiKey) config.api_key = opts.apiKey;
|
|
93
|
-
if (opts.baseUrl) config.base_url = opts.baseUrl;
|
|
94
|
-
if (opts.single) config.agent_mode = "single";
|
|
95
|
-
if (opts.dual) config.agent_mode = "dual";
|
|
96
|
-
if (opts.mode) {
|
|
97
|
-
const m = opts.mode;
|
|
98
|
-
if (m === "delegated" || m === "autonomous" || m === "controlled") {
|
|
99
|
-
config.collaboration = m;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
return config;
|
|
103
|
-
}
|
|
104
|
-
function loadConfig(cliOpts = {}) {
|
|
105
|
-
const globalConfigPath = path.join(os.homedir(), ".zencode", "config.yaml");
|
|
106
|
-
const projectDirConfigPath = path.resolve(".zencode", "config.yaml");
|
|
107
|
-
const projectFileConfigPath = path.resolve(".zencode.yaml");
|
|
108
|
-
let config = { ...DEFAULT_CONFIG };
|
|
109
|
-
config = deepMerge(config, loadYamlFile(globalConfigPath));
|
|
110
|
-
config = deepMerge(config, loadYamlFile(projectDirConfigPath));
|
|
111
|
-
config = deepMerge(config, loadYamlFile(projectFileConfigPath));
|
|
112
|
-
config = deepMerge(config, loadEnvConfig());
|
|
113
|
-
config = deepMerge(config, loadCliConfig(cliOpts));
|
|
114
|
-
return config;
|
|
115
|
-
}
|
|
116
|
-
function resolveModelConfig(config, role) {
|
|
117
|
-
const roleConfig = config.dual_agent[role] || {};
|
|
118
|
-
return {
|
|
119
|
-
model: roleConfig.model || config.model,
|
|
120
|
-
api_key: roleConfig.api_key || config.api_key,
|
|
121
|
-
base_url: roleConfig.base_url || config.base_url,
|
|
122
|
-
temperature: roleConfig.temperature ?? config.temperature,
|
|
123
|
-
max_tokens: roleConfig.max_tokens ?? config.max_tokens
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
var init_loader = __esm({
|
|
127
|
-
"src/config/loader.ts"() {
|
|
128
|
-
"use strict";
|
|
129
|
-
init_defaults();
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
|
|
133
12
|
// src/llm/client.ts
|
|
134
13
|
import OpenAI from "openai";
|
|
14
|
+
function isAbortError(error) {
|
|
15
|
+
if (!error) return false;
|
|
16
|
+
const err = error;
|
|
17
|
+
const name = err.name || err.cause?.name || "";
|
|
18
|
+
const code = err.code || err.cause?.code || "";
|
|
19
|
+
const message = err.message || "";
|
|
20
|
+
return name === "AbortError" || name === "APIUserAbortError" || code === "ABORT_ERR" || /abort|aborted|cancel|cancelled|canceled|interrupted/i.test(message);
|
|
21
|
+
}
|
|
135
22
|
function createLLMClient(options) {
|
|
136
23
|
return new LLMClient(options);
|
|
137
24
|
}
|
|
@@ -144,6 +31,7 @@ var init_client = __esm({
|
|
|
144
31
|
model;
|
|
145
32
|
temperature;
|
|
146
33
|
maxTokens;
|
|
34
|
+
activeAbortController = null;
|
|
147
35
|
constructor(options) {
|
|
148
36
|
this.client = new OpenAI({
|
|
149
37
|
apiKey: options.apiKey,
|
|
@@ -168,15 +56,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
|
});
|
|
@@ -542,14 +473,24 @@ var init_edit_file = __esm({
|
|
|
542
473
|
|
|
543
474
|
// src/tools/bash.ts
|
|
544
475
|
import { exec } from "child_process";
|
|
545
|
-
|
|
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;
|
|
546
486
|
var init_bash = __esm({
|
|
547
487
|
"src/tools/bash.ts"() {
|
|
548
488
|
"use strict";
|
|
549
489
|
DEFAULT_TIMEOUT = 12e4;
|
|
490
|
+
IS_WIN = process.platform === "win32";
|
|
550
491
|
bashTool = {
|
|
551
492
|
name: "bash",
|
|
552
|
-
description: "\u6267\u884C shell \u547D\u4EE4\u3002\u7528\u4E8E\u8FD0\u884C\u6784\u5EFA\u3001\u6D4B\u8BD5\u3001git \u64CD\u4F5C\u7B49\u7CFB\u7EDF\u547D\u4EE4\u3002\u4E0D\u8981\u7528 bash \u505A\u6587\u4EF6\u8BFB\u5199\uFF08\u7528 read-file/edit-file/write-file\uFF09\u6216\u641C\u7D22\uFF08\u7528 glob/grep\uFF09\u3002",
|
|
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",
|
|
553
494
|
parameters: {
|
|
554
495
|
type: "object",
|
|
555
496
|
properties: {
|
|
@@ -568,7 +509,7 @@ var init_bash = __esm({
|
|
|
568
509
|
async execute(params) {
|
|
569
510
|
const command = params["command"];
|
|
570
511
|
const timeout = params["timeout"] || DEFAULT_TIMEOUT;
|
|
571
|
-
return new Promise((
|
|
512
|
+
return new Promise((resolve10) => {
|
|
572
513
|
exec(
|
|
573
514
|
command,
|
|
574
515
|
{
|
|
@@ -576,9 +517,12 @@ var init_bash = __esm({
|
|
|
576
517
|
timeout,
|
|
577
518
|
maxBuffer: 1024 * 1024 * 10,
|
|
578
519
|
// 10MB
|
|
579
|
-
shell: process.platform === "win32" ? "cmd.exe" : "/bin/bash"
|
|
520
|
+
shell: process.platform === "win32" ? "cmd.exe" : "/bin/bash",
|
|
521
|
+
encoding: "buffer"
|
|
580
522
|
},
|
|
581
|
-
(error,
|
|
523
|
+
(error, stdoutBuf, stderrBuf) => {
|
|
524
|
+
const stdout = stdoutBuf ? decodeBuffer(stdoutBuf) : "";
|
|
525
|
+
const stderr = stderrBuf ? decodeBuffer(stderrBuf) : "";
|
|
582
526
|
let output = "";
|
|
583
527
|
if (stdout) output += stdout;
|
|
584
528
|
if (stderr) output += (output ? "\n" : "") + `[stderr]
|
|
@@ -589,7 +533,7 @@ ${stderr}`;
|
|
|
589
533
|
} else if (error && !stdout && !stderr) {
|
|
590
534
|
output = `\u547D\u4EE4\u6267\u884C\u5931\u8D25\uFF1A${error.message}`;
|
|
591
535
|
}
|
|
592
|
-
|
|
536
|
+
resolve10({ content: output || "\uFF08\u65E0\u8F93\u51FA\uFF09" });
|
|
593
537
|
}
|
|
594
538
|
);
|
|
595
539
|
});
|
|
@@ -900,6 +844,18 @@ var init_conversation = __esm({
|
|
|
900
844
|
content
|
|
901
845
|
});
|
|
902
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
|
+
}
|
|
903
859
|
/**
|
|
904
860
|
* 获取完整的消息列表(包含系统提示词)
|
|
905
861
|
*/
|
|
@@ -933,67 +889,6 @@ var init_conversation = __esm({
|
|
|
933
889
|
}
|
|
934
890
|
});
|
|
935
891
|
|
|
936
|
-
// src/core/auto-memo.ts
|
|
937
|
-
function extractExports(code) {
|
|
938
|
-
const names = [];
|
|
939
|
-
for (const m of code.matchAll(/export\s+(?:default\s+)?(?:function|class|const|let|var)\s+(\w+)/g)) {
|
|
940
|
-
names.push(m[1]);
|
|
941
|
-
}
|
|
942
|
-
for (const m of code.matchAll(/^(?:async\s+)?function\s+(\w+)/gm)) {
|
|
943
|
-
if (!names.includes(m[1])) names.push(m[1]);
|
|
944
|
-
}
|
|
945
|
-
for (const m of code.matchAll(/(?:^|\n)\s{0,4}(?:async\s+)?function\s+(\w+)/g)) {
|
|
946
|
-
if (!names.includes(m[1])) names.push(m[1]);
|
|
947
|
-
}
|
|
948
|
-
return [...new Set(names)].slice(0, 8).join(", ");
|
|
949
|
-
}
|
|
950
|
-
function autoMemoForTool(memoStore, toolName, params, result) {
|
|
951
|
-
if (!memoStore) return;
|
|
952
|
-
if (toolName === "write-file") {
|
|
953
|
-
const path8 = params["path"];
|
|
954
|
-
const content = params["content"] || "";
|
|
955
|
-
const lines = content.split("\n").length;
|
|
956
|
-
const exports = extractExports(content);
|
|
957
|
-
memoStore.write(
|
|
958
|
-
`file:${path8}`,
|
|
959
|
-
content,
|
|
960
|
-
"auto",
|
|
961
|
-
`\u65B0\u5EFA ${lines}\u884C${exports ? " | \u5BFC\u51FA: " + exports : ""}`
|
|
962
|
-
);
|
|
963
|
-
}
|
|
964
|
-
if (toolName === "edit-file") {
|
|
965
|
-
const path8 = params["path"];
|
|
966
|
-
const newStr = params["new_string"] || "";
|
|
967
|
-
const oldStr = params["old_string"] || "";
|
|
968
|
-
const changeLines = newStr.split("\n").length;
|
|
969
|
-
memoStore.write(
|
|
970
|
-
`file:${path8}`,
|
|
971
|
-
`--- \u65E7 ---
|
|
972
|
-
${oldStr}
|
|
973
|
-
--- \u65B0 ---
|
|
974
|
-
${newStr}`,
|
|
975
|
-
"auto",
|
|
976
|
-
`\u7F16\u8F91 ${changeLines}\u884C\u53D8\u66F4`
|
|
977
|
-
);
|
|
978
|
-
}
|
|
979
|
-
if (toolName === "read-file") {
|
|
980
|
-
const path8 = params["path"];
|
|
981
|
-
const lines = result.split("\n").length;
|
|
982
|
-
const exports = extractExports(result);
|
|
983
|
-
memoStore.write(
|
|
984
|
-
`file:${path8}`,
|
|
985
|
-
result,
|
|
986
|
-
"auto",
|
|
987
|
-
`\u5DF2\u8BFB ${lines}\u884C${exports ? " | \u5BFC\u51FA: " + exports : ""}`
|
|
988
|
-
);
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
var init_auto_memo = __esm({
|
|
992
|
-
"src/core/auto-memo.ts"() {
|
|
993
|
-
"use strict";
|
|
994
|
-
}
|
|
995
|
-
});
|
|
996
|
-
|
|
997
892
|
// src/core/read-tracker.ts
|
|
998
893
|
import * as fs6 from "fs";
|
|
999
894
|
import * as path6 from "path";
|
|
@@ -1042,7 +937,6 @@ var init_agent = __esm({
|
|
|
1042
937
|
"use strict";
|
|
1043
938
|
init_permission();
|
|
1044
939
|
init_conversation();
|
|
1045
|
-
init_auto_memo();
|
|
1046
940
|
init_read_tracker();
|
|
1047
941
|
Agent = class {
|
|
1048
942
|
conversation;
|
|
@@ -1050,24 +944,26 @@ var init_agent = __esm({
|
|
|
1050
944
|
registry;
|
|
1051
945
|
config;
|
|
1052
946
|
fixedTools;
|
|
1053
|
-
memoStore;
|
|
1054
947
|
readTracker = new ReadTracker();
|
|
1055
|
-
|
|
948
|
+
interrupted = false;
|
|
949
|
+
constructor(client, registry, config, systemPrompt, tools) {
|
|
1056
950
|
this.client = client;
|
|
1057
951
|
this.registry = registry;
|
|
1058
952
|
this.config = config;
|
|
1059
953
|
this.conversation = new Conversation();
|
|
1060
954
|
this.conversation.setSystemPrompt(systemPrompt);
|
|
1061
955
|
this.fixedTools = tools;
|
|
1062
|
-
this.memoStore = memoStore;
|
|
1063
956
|
}
|
|
1064
957
|
/**
|
|
1065
958
|
* 执行一轮完整的 agent 循环
|
|
1066
959
|
*/
|
|
1067
960
|
async run(userMessage, callbacks = {}) {
|
|
961
|
+
this.interrupted = false;
|
|
962
|
+
this.conversation.clearReasoningContent();
|
|
1068
963
|
this.conversation.addUserMessage(userMessage);
|
|
1069
964
|
let lastContent = "";
|
|
1070
965
|
while (true) {
|
|
966
|
+
if (this.interrupted) break;
|
|
1071
967
|
const tools = this.fixedTools || this.registry.toToolDefinitions();
|
|
1072
968
|
const assistantMsg = await this.client.chatStream(
|
|
1073
969
|
this.conversation.getMessages(),
|
|
@@ -1080,6 +976,7 @@ var init_agent = __esm({
|
|
|
1080
976
|
break;
|
|
1081
977
|
}
|
|
1082
978
|
for (const toolCall of assistantMsg.tool_calls) {
|
|
979
|
+
if (this.interrupted) break;
|
|
1083
980
|
const toolName = toolCall.function.name;
|
|
1084
981
|
let params;
|
|
1085
982
|
try {
|
|
@@ -1129,7 +1026,6 @@ var init_agent = __esm({
|
|
|
1129
1026
|
}
|
|
1130
1027
|
const result = await this.registry.execute(toolName, params, this.config.max_tool_output);
|
|
1131
1028
|
callbacks.onToolResult?.(toolName, result.content, result.truncated ?? false);
|
|
1132
|
-
autoMemoForTool(this.memoStore, toolName, params, result.content);
|
|
1133
1029
|
if (toolName === "read-file") {
|
|
1134
1030
|
this.readTracker.markRead(params["path"]);
|
|
1135
1031
|
} else if (toolName === "write-file") {
|
|
@@ -1147,6 +1043,10 @@ var init_agent = __esm({
|
|
|
1147
1043
|
}
|
|
1148
1044
|
return lastContent;
|
|
1149
1045
|
}
|
|
1046
|
+
interrupt() {
|
|
1047
|
+
this.interrupted = true;
|
|
1048
|
+
this.client.abortActiveStream();
|
|
1049
|
+
}
|
|
1150
1050
|
/**
|
|
1151
1051
|
* 获取对话历史
|
|
1152
1052
|
*/
|
|
@@ -1157,498 +1057,52 @@ var init_agent = __esm({
|
|
|
1157
1057
|
}
|
|
1158
1058
|
});
|
|
1159
1059
|
|
|
1160
|
-
// src/core/
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
"
|
|
1164
|
-
|
|
1165
|
-
init_conversation();
|
|
1166
|
-
init_permission();
|
|
1167
|
-
init_auto_memo();
|
|
1168
|
-
init_read_tracker();
|
|
1169
|
-
Coder = class {
|
|
1170
|
-
client;
|
|
1171
|
-
registry;
|
|
1172
|
-
config;
|
|
1173
|
-
systemPrompt;
|
|
1174
|
-
tools;
|
|
1175
|
-
memoStore;
|
|
1176
|
-
constructor(client, registry, config, systemPrompt, tools, memoStore) {
|
|
1177
|
-
this.client = client;
|
|
1178
|
-
this.registry = registry;
|
|
1179
|
-
this.config = config;
|
|
1180
|
-
this.systemPrompt = systemPrompt;
|
|
1181
|
-
this.tools = tools;
|
|
1182
|
-
this.memoStore = memoStore;
|
|
1183
|
-
}
|
|
1184
|
-
/**
|
|
1185
|
-
* 执行编码任务(短生命周期,每次新建会话)
|
|
1186
|
-
*
|
|
1187
|
-
* @param taskMessage 调度者发来的任务描述(作为 user message)
|
|
1188
|
-
* @param callbacks 回调
|
|
1189
|
-
* @returns 编码者的最终响应
|
|
1190
|
-
*/
|
|
1191
|
-
async execute(taskMessage, callbacks = {}) {
|
|
1192
|
-
const conversation = new Conversation();
|
|
1193
|
-
conversation.setSystemPrompt(this.systemPrompt);
|
|
1194
|
-
conversation.addUserMessage(taskMessage);
|
|
1195
|
-
const readTracker = new ReadTracker();
|
|
1196
|
-
let lastContent = "";
|
|
1197
|
-
while (true) {
|
|
1198
|
-
const assistantMsg = await this.client.chatStream(
|
|
1199
|
-
conversation.getMessages(),
|
|
1200
|
-
this.tools.length > 0 ? this.tools : void 0,
|
|
1201
|
-
callbacks
|
|
1202
|
-
);
|
|
1203
|
-
conversation.addAssistantMessage(assistantMsg);
|
|
1204
|
-
if (!assistantMsg.tool_calls || assistantMsg.tool_calls.length === 0) {
|
|
1205
|
-
lastContent = assistantMsg.content || "";
|
|
1206
|
-
break;
|
|
1207
|
-
}
|
|
1208
|
-
for (const toolCall of assistantMsg.tool_calls) {
|
|
1209
|
-
const toolName = toolCall.function.name;
|
|
1210
|
-
let params;
|
|
1211
|
-
try {
|
|
1212
|
-
params = JSON.parse(toolCall.function.arguments);
|
|
1213
|
-
} catch {
|
|
1214
|
-
conversation.addToolResult(toolCall.id, "\u53C2\u6570\u89E3\u6790\u5931\u8D25\uFF1A\u65E0\u6548\u7684 JSON");
|
|
1215
|
-
continue;
|
|
1216
|
-
}
|
|
1217
|
-
try {
|
|
1218
|
-
if (toolName === "edit-file") {
|
|
1219
|
-
const editPath = params["path"];
|
|
1220
|
-
if (!readTracker.hasRead(editPath)) {
|
|
1221
|
-
conversation.addToolResult(
|
|
1222
|
-
toolCall.id,
|
|
1223
|
-
`\u26A0 \u7981\u6B62\u7F16\u8F91\u672A\u8BFB\u53D6\u7684\u6587\u4EF6\u3002\u8BF7\u5148 read-file "${editPath}" \u4E86\u89E3\u5F53\u524D\u5185\u5BB9\uFF0C\u518D edit-file\u3002`
|
|
1224
|
-
);
|
|
1225
|
-
continue;
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
|
-
if (toolName === "write-file") {
|
|
1229
|
-
const warn = readTracker.checkWriteOverwrite(
|
|
1230
|
-
params["path"],
|
|
1231
|
-
params["overwrite"]
|
|
1232
|
-
);
|
|
1233
|
-
if (warn) {
|
|
1234
|
-
conversation.addToolResult(toolCall.id, warn);
|
|
1235
|
-
continue;
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1238
|
-
const permLevel = this.registry.getPermissionLevel(toolName);
|
|
1239
|
-
if (permLevel === "deny") {
|
|
1240
|
-
callbacks.onDenied?.(toolName);
|
|
1241
|
-
conversation.addToolResult(toolCall.id, `\u5DE5\u5177 "${toolName}" \u5DF2\u88AB\u7981\u6B62\u6267\u884C`);
|
|
1242
|
-
continue;
|
|
1243
|
-
}
|
|
1244
|
-
if (permLevel === "auto") {
|
|
1245
|
-
callbacks.onToolExecuting?.(toolName, params);
|
|
1246
|
-
}
|
|
1247
|
-
if (permLevel === "confirm") {
|
|
1248
|
-
const confirmResult = await confirmExecution(toolName, params);
|
|
1249
|
-
if (!confirmResult.approved) {
|
|
1250
|
-
callbacks.onDenied?.(toolName, confirmResult.feedback);
|
|
1251
|
-
const denyMsg = confirmResult.feedback ? `\u7528\u6237\u62D2\u7EDD\u4E86\u6B64\u64CD\u4F5C\uFF0C\u7528\u6237\u53CD\u9988: ${confirmResult.feedback}` : "\u7528\u6237\u62D2\u7EDD\u4E86\u6B64\u64CD\u4F5C";
|
|
1252
|
-
conversation.addToolResult(toolCall.id, denyMsg);
|
|
1253
|
-
continue;
|
|
1254
|
-
}
|
|
1255
|
-
}
|
|
1256
|
-
const result = await this.registry.execute(toolName, params, this.config.max_tool_output);
|
|
1257
|
-
callbacks.onToolResult?.(toolName, result.content, result.truncated ?? false);
|
|
1258
|
-
autoMemoForTool(this.memoStore, toolName, params, result.content);
|
|
1259
|
-
if (toolName === "read-file") {
|
|
1260
|
-
readTracker.markRead(params["path"]);
|
|
1261
|
-
} else if (toolName === "write-file") {
|
|
1262
|
-
readTracker.markWritten(params["path"]);
|
|
1263
|
-
}
|
|
1264
|
-
conversation.addToolResult(toolCall.id, result.content);
|
|
1265
|
-
} catch (err) {
|
|
1266
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1267
|
-
conversation.addToolResult(toolCall.id, `\u5DE5\u5177\u6267\u884C\u5F02\u5E38\uFF1A${msg}`);
|
|
1268
|
-
}
|
|
1269
|
-
}
|
|
1270
|
-
if (assistantMsg.content) {
|
|
1271
|
-
lastContent = assistantMsg.content;
|
|
1272
|
-
}
|
|
1273
|
-
}
|
|
1274
|
-
return lastContent;
|
|
1275
|
-
}
|
|
1276
|
-
};
|
|
1277
|
-
}
|
|
1278
|
-
});
|
|
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
|
|
1279
1065
|
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
return AUTONOMOUS_MODE;
|
|
1287
|
-
case "controlled":
|
|
1288
|
-
return CONTROLLED_MODE;
|
|
1289
|
-
default:
|
|
1290
|
-
return DELEGATED_MODE;
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1293
|
-
var CODER_IDENTITY, DELEGATED_MODE, AUTONOMOUS_MODE, CONTROLLED_MODE;
|
|
1294
|
-
var init_modes = __esm({
|
|
1295
|
-
"src/core/dual-agent/modes.ts"() {
|
|
1296
|
-
"use strict";
|
|
1297
|
-
CODER_IDENTITY = `\u4F60\u662F\u7F16\u7801\u5B50 Agent\u3002
|
|
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
|
|
1298
1072
|
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
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
|
|
1309
1083
|
|
|
1310
|
-
\
|
|
1311
|
-
\
|
|
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
|
|
1312
1093
|
|
|
1313
|
-
|
|
1314
|
-
- old_string \u5FC5\u987B\u4ECE read-file \u8FD4\u56DE\u7684\u5185\u5BB9\u4E2D**\u7CBE\u786E\u590D\u5236**\uFF0C\u4E0D\u80FD\u51ED\u8BB0\u5FC6\u624B\u5199
|
|
1315
|
-
- old_string \u5305\u542B\u8DB3\u591F\u4E0A\u4E0B\u6587\u884C\uFF083-5 \u884C\uFF09\u786E\u4FDD\u552F\u4E00\u5339\u914D
|
|
1316
|
-
- \u7F29\u8FDB\u3001\u7A7A\u683C\u3001\u6362\u884C\u7B26\u5FC5\u987B\u5B8C\u5168\u4E00\u81F4
|
|
1317
|
-
- \u5982\u679C\u4E0D\u786E\u5B9A\u6587\u4EF6\u5185\u5BB9\uFF0C\u5148 read-file \u518D\u64CD\u4F5C
|
|
1094
|
+
# \u4EA4\u4E92\u98CE\u683C
|
|
1318
1095
|
|
|
1319
|
-
|
|
1320
|
-
- \
|
|
1321
|
-
- \
|
|
1322
|
-
- \
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
coderToolNames: ["read-file", "write-file", "edit-file", "bash", "glob", "grep", "memo"],
|
|
1330
|
-
coderSystemPrompt: `${CODER_IDENTITY}`
|
|
1331
|
-
};
|
|
1332
|
-
AUTONOMOUS_MODE = {
|
|
1333
|
-
name: "autonomous",
|
|
1334
|
-
description: "A\u89C4\u5212\uFF0CB\u81EA\u4E3B\u6267\u884C\uFF08\u9002\u5408\u80FD\u529B\u5F3A\u7684\u6A21\u578B\uFF09",
|
|
1335
|
-
coderHasTools: true,
|
|
1336
|
-
coderToolNames: ["read-file", "write-file", "edit-file", "bash", "glob", "grep", "memo"],
|
|
1337
|
-
coderSystemPrompt: `${CODER_IDENTITY}`
|
|
1338
|
-
};
|
|
1339
|
-
CONTROLLED_MODE = {
|
|
1340
|
-
name: "controlled",
|
|
1341
|
-
description: "A\u5168\u6743\u7BA1\u7406\uFF0CB\u53EA\u8FD4\u56DE\u4EE3\u7801",
|
|
1342
|
-
coderHasTools: false,
|
|
1343
|
-
coderSystemPrompt: "\u4F60\u662F\u88AB\u8C03\u5EA6\u8005\u6D3E\u51FA\u7684\u5B50 Agent\u3002\u6839\u636E\u63D0\u4F9B\u7684\u4EE3\u7801\u548C\u9700\u6C42\uFF0C\u53EA\u8FD4\u56DE\u4FEE\u6539\u540E\u7684\u4EE3\u7801\u3002\u4E0D\u8981\u81EA\u884C\u64CD\u4F5C\u6587\u4EF6\uFF0C\u4E0D\u8981\u505A\u989D\u5916\u6539\u52A8\u3002"
|
|
1344
|
-
};
|
|
1345
|
-
}
|
|
1346
|
-
});
|
|
1347
|
-
|
|
1348
|
-
// src/core/dual-agent/orchestrator.ts
|
|
1349
|
-
var SEND_TO_CODER_TOOL, Orchestrator;
|
|
1350
|
-
var init_orchestrator = __esm({
|
|
1351
|
-
"src/core/dual-agent/orchestrator.ts"() {
|
|
1352
|
-
"use strict";
|
|
1353
|
-
init_client();
|
|
1354
|
-
init_loader();
|
|
1355
|
-
init_conversation();
|
|
1356
|
-
init_permission();
|
|
1357
|
-
init_coder();
|
|
1358
|
-
init_modes();
|
|
1359
|
-
init_auto_memo();
|
|
1360
|
-
init_read_tracker();
|
|
1361
|
-
SEND_TO_CODER_TOOL = {
|
|
1362
|
-
type: "function",
|
|
1363
|
-
function: {
|
|
1364
|
-
name: "send-to-coder",
|
|
1365
|
-
description: "\u5C06\u7F16\u7801\u4EFB\u52A1\u53D1\u9001\u7ED9\u7F16\u7801 Agent\u3002\u4F20\u5165\u4EFB\u52A1\u63CF\u8FF0\u548C\u76F8\u5173\u4EE3\u7801\u4E0A\u4E0B\u6587\uFF0C\u7F16\u7801 Agent \u5C06\u8FD4\u56DE\u4EE3\u7801\u6216\u89E3\u51B3\u65B9\u6848\u3002",
|
|
1366
|
-
parameters: {
|
|
1367
|
-
type: "object",
|
|
1368
|
-
properties: {
|
|
1369
|
-
task: {
|
|
1370
|
-
type: "string",
|
|
1371
|
-
description: "\u8981\u53D1\u9001\u7ED9\u7F16\u7801 Agent \u7684\u4EFB\u52A1\u63CF\u8FF0\uFF0C\u5305\u542B\u76F8\u5173\u4EE3\u7801\u4E0A\u4E0B\u6587"
|
|
1372
|
-
}
|
|
1373
|
-
},
|
|
1374
|
-
required: ["task"]
|
|
1375
|
-
}
|
|
1376
|
-
}
|
|
1377
|
-
};
|
|
1378
|
-
Orchestrator = class {
|
|
1379
|
-
conversation;
|
|
1380
|
-
orchestratorClient;
|
|
1381
|
-
coderClient;
|
|
1382
|
-
registry;
|
|
1383
|
-
config;
|
|
1384
|
-
mode;
|
|
1385
|
-
baseSystemPrompt;
|
|
1386
|
-
memoStore;
|
|
1387
|
-
readTracker = new ReadTracker();
|
|
1388
|
-
constructor(registry, config, systemPrompt, memoStore) {
|
|
1389
|
-
this.registry = registry;
|
|
1390
|
-
this.config = config;
|
|
1391
|
-
this.mode = config.collaboration;
|
|
1392
|
-
this.baseSystemPrompt = systemPrompt;
|
|
1393
|
-
this.memoStore = memoStore;
|
|
1394
|
-
const orchConfig = resolveModelConfig(config, "orchestrator");
|
|
1395
|
-
this.orchestratorClient = createLLMClient({
|
|
1396
|
-
apiKey: orchConfig.api_key,
|
|
1397
|
-
baseURL: orchConfig.base_url,
|
|
1398
|
-
model: orchConfig.model,
|
|
1399
|
-
temperature: orchConfig.temperature,
|
|
1400
|
-
maxTokens: orchConfig.max_tokens
|
|
1401
|
-
});
|
|
1402
|
-
const coderConfig = resolveModelConfig(config, "coder");
|
|
1403
|
-
this.coderClient = createLLMClient({
|
|
1404
|
-
apiKey: coderConfig.api_key,
|
|
1405
|
-
baseURL: coderConfig.base_url,
|
|
1406
|
-
model: coderConfig.model,
|
|
1407
|
-
temperature: coderConfig.temperature,
|
|
1408
|
-
maxTokens: coderConfig.max_tokens
|
|
1409
|
-
});
|
|
1410
|
-
this.conversation = new Conversation();
|
|
1411
|
-
this.conversation.setSystemPrompt(this.buildSystemPrompt());
|
|
1412
|
-
}
|
|
1413
|
-
/**
|
|
1414
|
-
* 动态获取调度者的工具列表:注册表工具 + send-to-coder
|
|
1415
|
-
*/
|
|
1416
|
-
getTools() {
|
|
1417
|
-
return [
|
|
1418
|
-
...this.registry.toToolDefinitions(),
|
|
1419
|
-
SEND_TO_CODER_TOOL
|
|
1420
|
-
];
|
|
1421
|
-
}
|
|
1422
|
-
/**
|
|
1423
|
-
* 构建调度者的系统提示词(包含当前协作模式)
|
|
1424
|
-
*/
|
|
1425
|
-
buildSystemPrompt() {
|
|
1426
|
-
const modeInfo = getMode(this.mode);
|
|
1427
|
-
return `${this.baseSystemPrompt}
|
|
1428
|
-
|
|
1429
|
-
# \u4F60\u662F\u8C03\u5EA6\u8005 Agent
|
|
1430
|
-
|
|
1431
|
-
\u534F\u4F5C\u6A21\u5F0F\uFF1A${modeInfo.name} - ${modeInfo.description}
|
|
1432
|
-
|
|
1433
|
-
\u4F60\u662F\u4FA6\u5BDF\u5175 + \u6307\u6325\u5B98\u3002\u4F60\u81EA\u5DF1\u4E0D\u5199\u4EE3\u7801\uFF0C\u4F60\u7684\u804C\u8D23\u662F\uFF1A\u6536\u96C6\u60C5\u62A5 \u2192 \u59D4\u6D3E\u7F16\u7801 Agent\u3002
|
|
1434
|
-
|
|
1435
|
-
## \u6838\u5FC3\u6D41\u7A0B
|
|
1436
|
-
|
|
1437
|
-
1. **\u8BC4\u4F30**\uFF1A\u4EFB\u52A1\u662F\u5426\u9700\u8981\u4E86\u89E3\u73B0\u6709\u4EE3\u7801\uFF1F
|
|
1438
|
-
- \u9700\u8981\u4FEE\u6539\u73B0\u6709\u6587\u4EF6\u3001\u9700\u8981\u7406\u89E3\u4F9D\u8D56\u5173\u7CFB \u2192 \u5148\u6536\u96C6
|
|
1439
|
-
- \u9700\u6C42\u81EA\u5305\u542B\u3001\u76EE\u6807\u8DEF\u5F84\u660E\u786E \u2192 \u8DF3\u5230\u7B2C 3 \u6B65
|
|
1440
|
-
|
|
1441
|
-
2. **\u6536\u96C6\u4E0A\u4E0B\u6587**\uFF08\u9AD8\u6548\uFF0C\u4E0D\u91CD\u590D\uFF09
|
|
1442
|
-
- \u7528 glob/grep \u5B9A\u4F4D + read-file \u6216 spawn-agents \u5E76\u884C\u8BFB\u53D6
|
|
1443
|
-
- \u6587\u4EF6\u64CD\u4F5C\uFF08read-file \u7B49\uFF09\u4F1A\u81EA\u52A8\u8BB0\u5F55\u5230 memo\uFF0C\u4E0D\u9700\u8981\u624B\u52A8 memo write \u6587\u4EF6\u5185\u5BB9
|
|
1444
|
-
- \u4ECD\u53EF memo write \u8BB0\u5F55\u5206\u6790\u7ED3\u8BBA\u3001\u67B6\u6784\u51B3\u7B56\u7B49\u975E\u6587\u4EF6\u4FE1\u606F
|
|
1445
|
-
|
|
1446
|
-
3. **\u59D4\u6D3E\u7F16\u7801**\uFF1Asend-to-coder
|
|
1447
|
-
- task \u4E2D\u5199\u6E05\uFF1A\u505A\u4EC0\u4E48 + \u76EE\u6807\u8DEF\u5F84
|
|
1448
|
-
- \u7F16\u7801 Agent \u4F1A\u81EA\u52A8\u770B\u5230 memo \u4E2D\u7684\u6587\u4EF6\u7D22\u5F15\uFF08\u5305\u62EC\u5DF2\u521B\u5EFA\u7684\u6587\u4EF6\u548C\u5BFC\u51FA\u7684\u51FD\u6570\u540D\uFF09
|
|
1449
|
-
- \u6BCF\u6B21\u53EA\u53D1\u4E00\u4E2A\u5177\u4F53\u6B65\u9AA4
|
|
1450
|
-
|
|
1451
|
-
4. **\u8FED\u4EE3/\u9A8C\u8BC1**\uFF1A\u9700\u8981\u65F6\u7EE7\u7EED\u59D4\u6D3E\u6216\u7528 bash \u8FD0\u884C\u9A8C\u8BC1
|
|
1452
|
-
|
|
1453
|
-
## \u91CD\u8981
|
|
1454
|
-
|
|
1455
|
-
- memo \u7D22\u5F15\u4F1A\u81EA\u52A8\u6CE8\u5165\u7ED9\u7F16\u7801 Agent\uFF0C\u5305\u542B\u6240\u6709\u5DF2\u64CD\u4F5C\u6587\u4EF6\u7684\u6458\u8981\u548C\u5BFC\u51FA\u51FD\u6570\u540D
|
|
1456
|
-
- \u4E0D\u8981\u91CD\u590D\u63A2\u7D22\uFF08glob \u67E5\u8FC7\u5C31\u4E0D\u8981\u518D bash ls \u540C\u4E00\u76EE\u5F55\uFF09
|
|
1457
|
-
- bash \u7528\u4E8E\u6267\u884C\u547D\u4EE4\uFF08\u6784\u5EFA\u3001\u6D4B\u8BD5\uFF09\uFF0C\u4E0D\u9700\u8981\u59D4\u6D3E
|
|
1458
|
-
- \u5B8C\u6210\u6240\u6709\u6B65\u9AA4\u540E\u7B80\u8981\u544A\u77E5\u7528\u6237\u7ED3\u679C`;
|
|
1459
|
-
}
|
|
1460
|
-
/**
|
|
1461
|
-
* 切换协作模式
|
|
1462
|
-
*/
|
|
1463
|
-
setMode(mode) {
|
|
1464
|
-
this.mode = mode;
|
|
1465
|
-
this.conversation.setSystemPrompt(this.buildSystemPrompt());
|
|
1466
|
-
}
|
|
1467
|
-
/**
|
|
1468
|
-
* 执行用户请求
|
|
1469
|
-
*/
|
|
1470
|
-
async run(userMessage, callbacks = {}) {
|
|
1471
|
-
callbacks.onModeInfo?.(this.mode);
|
|
1472
|
-
this.conversation.addUserMessage(userMessage);
|
|
1473
|
-
let lastContent = "";
|
|
1474
|
-
while (true) {
|
|
1475
|
-
const tools = this.getTools();
|
|
1476
|
-
const assistantMsg = await this.orchestratorClient.chatStream(
|
|
1477
|
-
this.conversation.getMessages(),
|
|
1478
|
-
tools,
|
|
1479
|
-
callbacks
|
|
1480
|
-
);
|
|
1481
|
-
this.conversation.addAssistantMessage(assistantMsg);
|
|
1482
|
-
if (!assistantMsg.tool_calls || assistantMsg.tool_calls.length === 0) {
|
|
1483
|
-
lastContent = assistantMsg.content || "";
|
|
1484
|
-
break;
|
|
1485
|
-
}
|
|
1486
|
-
for (const toolCall of assistantMsg.tool_calls) {
|
|
1487
|
-
const toolName = toolCall.function.name;
|
|
1488
|
-
let params;
|
|
1489
|
-
try {
|
|
1490
|
-
params = JSON.parse(toolCall.function.arguments);
|
|
1491
|
-
} catch {
|
|
1492
|
-
this.conversation.addToolResult(toolCall.id, "\u53C2\u6570\u89E3\u6790\u5931\u8D25\uFF1A\u65E0\u6548\u7684 JSON");
|
|
1493
|
-
continue;
|
|
1494
|
-
}
|
|
1495
|
-
try {
|
|
1496
|
-
if (toolName === "send-to-coder") {
|
|
1497
|
-
const task = params["task"];
|
|
1498
|
-
const coderResponse = await this.invokeCoder(task, callbacks);
|
|
1499
|
-
this.conversation.addToolResult(toolCall.id, coderResponse);
|
|
1500
|
-
continue;
|
|
1501
|
-
}
|
|
1502
|
-
if (toolName === "edit-file") {
|
|
1503
|
-
const editPath = params["path"];
|
|
1504
|
-
if (!this.readTracker.hasRead(editPath)) {
|
|
1505
|
-
this.conversation.addToolResult(
|
|
1506
|
-
toolCall.id,
|
|
1507
|
-
`\u26A0 \u7981\u6B62\u7F16\u8F91\u672A\u8BFB\u53D6\u7684\u6587\u4EF6\u3002\u8BF7\u5148 read-file "${editPath}" \u4E86\u89E3\u5F53\u524D\u5185\u5BB9\uFF0C\u518D edit-file\u3002`
|
|
1508
|
-
);
|
|
1509
|
-
continue;
|
|
1510
|
-
}
|
|
1511
|
-
}
|
|
1512
|
-
if (toolName === "write-file") {
|
|
1513
|
-
const warn = this.readTracker.checkWriteOverwrite(
|
|
1514
|
-
params["path"],
|
|
1515
|
-
params["overwrite"]
|
|
1516
|
-
);
|
|
1517
|
-
if (warn) {
|
|
1518
|
-
this.conversation.addToolResult(toolCall.id, warn);
|
|
1519
|
-
continue;
|
|
1520
|
-
}
|
|
1521
|
-
}
|
|
1522
|
-
const permLevel = this.registry.getPermissionLevel(toolName);
|
|
1523
|
-
if (permLevel === "deny") {
|
|
1524
|
-
callbacks.onDenied?.(toolName);
|
|
1525
|
-
this.conversation.addToolResult(toolCall.id, `\u5DE5\u5177 "${toolName}" \u5DF2\u88AB\u7981\u6B62\u6267\u884C`);
|
|
1526
|
-
continue;
|
|
1527
|
-
}
|
|
1528
|
-
if (permLevel === "auto") {
|
|
1529
|
-
callbacks.onToolExecuting?.(toolName, params);
|
|
1530
|
-
}
|
|
1531
|
-
if (permLevel === "confirm") {
|
|
1532
|
-
const confirmResult = await confirmExecution(toolName, params);
|
|
1533
|
-
if (!confirmResult.approved) {
|
|
1534
|
-
callbacks.onDenied?.(toolName, confirmResult.feedback);
|
|
1535
|
-
const denyMsg = confirmResult.feedback ? `\u7528\u6237\u62D2\u7EDD\u4E86\u6B64\u64CD\u4F5C\uFF0C\u7528\u6237\u53CD\u9988: ${confirmResult.feedback}` : "\u7528\u6237\u62D2\u7EDD\u4E86\u6B64\u64CD\u4F5C";
|
|
1536
|
-
this.conversation.addToolResult(toolCall.id, denyMsg);
|
|
1537
|
-
continue;
|
|
1538
|
-
}
|
|
1539
|
-
}
|
|
1540
|
-
const result = await this.registry.execute(toolName, params, this.config.max_tool_output);
|
|
1541
|
-
callbacks.onToolResult?.(toolName, result.content, result.truncated ?? false);
|
|
1542
|
-
autoMemoForTool(this.memoStore, toolName, params, result.content);
|
|
1543
|
-
if (toolName === "read-file") {
|
|
1544
|
-
this.readTracker.markRead(params["path"]);
|
|
1545
|
-
} else if (toolName === "write-file") {
|
|
1546
|
-
this.readTracker.markWritten(params["path"]);
|
|
1547
|
-
}
|
|
1548
|
-
this.conversation.addToolResult(toolCall.id, result.content);
|
|
1549
|
-
} catch (err) {
|
|
1550
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1551
|
-
this.conversation.addToolResult(toolCall.id, `\u5DE5\u5177\u6267\u884C\u5F02\u5E38\uFF1A${msg}`);
|
|
1552
|
-
}
|
|
1553
|
-
}
|
|
1554
|
-
if (assistantMsg.content) {
|
|
1555
|
-
lastContent = assistantMsg.content;
|
|
1556
|
-
}
|
|
1557
|
-
}
|
|
1558
|
-
return lastContent;
|
|
1559
|
-
}
|
|
1560
|
-
/**
|
|
1561
|
-
* 调用编码者 Agent
|
|
1562
|
-
*/
|
|
1563
|
-
async invokeCoder(task, callbacks) {
|
|
1564
|
-
callbacks.onCoderStart?.();
|
|
1565
|
-
const modeInfo = getMode(this.mode);
|
|
1566
|
-
let coderTools = [];
|
|
1567
|
-
if (modeInfo.coderHasTools && modeInfo.coderToolNames) {
|
|
1568
|
-
coderTools = this.registry.toToolDefinitions(modeInfo.coderToolNames);
|
|
1569
|
-
}
|
|
1570
|
-
let taskWithMemo = task;
|
|
1571
|
-
if (this.memoStore) {
|
|
1572
|
-
const index = this.memoStore.buildIndex();
|
|
1573
|
-
if (index) {
|
|
1574
|
-
taskWithMemo += `
|
|
1575
|
-
|
|
1576
|
-
[\u5171\u4EAB\u5907\u5FD8\u5F55]
|
|
1577
|
-
${index}`;
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
|
-
taskWithMemo += "\n\n[\u91CD\u8981\uFF1A\u7ACB\u5373\u5F00\u59CB\u7F16\u7801\uFF0C\u4E0D\u8981\u63A2\u7D22\uFF0C\u4E0D\u8981\u8F93\u51FA\u5206\u6790]";
|
|
1581
|
-
const coder = new Coder(
|
|
1582
|
-
this.coderClient,
|
|
1583
|
-
this.registry,
|
|
1584
|
-
this.config,
|
|
1585
|
-
modeInfo.coderSystemPrompt,
|
|
1586
|
-
coderTools,
|
|
1587
|
-
this.memoStore
|
|
1588
|
-
);
|
|
1589
|
-
const response = await coder.execute(taskWithMemo, {
|
|
1590
|
-
onContent: callbacks.onContent,
|
|
1591
|
-
onToolCallStreaming: callbacks.onToolCallStreaming,
|
|
1592
|
-
onToolExecuting: callbacks.onToolExecuting,
|
|
1593
|
-
onToolResult: callbacks.onToolResult,
|
|
1594
|
-
onDenied: callbacks.onDenied
|
|
1595
|
-
});
|
|
1596
|
-
callbacks.onCoderEnd?.(response);
|
|
1597
|
-
return response;
|
|
1598
|
-
}
|
|
1599
|
-
/**
|
|
1600
|
-
* 获取对话历史
|
|
1601
|
-
*/
|
|
1602
|
-
getConversation() {
|
|
1603
|
-
return this.conversation;
|
|
1604
|
-
}
|
|
1605
|
-
};
|
|
1606
|
-
}
|
|
1607
|
-
});
|
|
1608
|
-
|
|
1609
|
-
// src/core/prompt/layers/core.ts
|
|
1610
|
-
import * as os2 from "os";
|
|
1611
|
-
function buildCorePrompt() {
|
|
1612
|
-
return `\u4F60\u662F ZenCode\uFF0C\u4E00\u4E2A CLI \u73AF\u5883\u4E0B\u7684 AI \u7F16\u7A0B\u52A9\u624B\u3002\u4F60\u5E2E\u52A9\u7528\u6237\u5B8C\u6210\u8F6F\u4EF6\u5DE5\u7A0B\u4EFB\u52A1\uFF1A\u4FEEbug\u3001\u52A0\u529F\u80FD\u3001\u91CD\u6784\u4EE3\u7801\u3001\u89E3\u91CA\u4EE3\u7801\u7B49\u3002
|
|
1613
|
-
|
|
1614
|
-
\u5DE5\u4F5C\u76EE\u5F55\uFF1A${process.cwd()}
|
|
1615
|
-
\u7CFB\u7EDF\uFF1A${os2.platform()} ${os2.arch()}
|
|
1616
|
-
|
|
1617
|
-
# \u5DE5\u5177\u4F7F\u7528\u539F\u5219
|
|
1618
|
-
|
|
1619
|
-
\u4F60\u6709\u4EE5\u4E0B\u5DE5\u5177\u53EF\u7528\uFF0C\u8BF7\u6839\u636E\u4EFB\u52A1\u9009\u62E9\u6700\u5408\u9002\u7684\u5DE5\u5177\uFF1A
|
|
1620
|
-
|
|
1621
|
-
- **read-file**\uFF1A\u8BFB\u53D6\u6587\u4EF6\u5185\u5BB9\u3002\u4FEE\u6539\u4EE3\u7801\u524D\u5FC5\u987B\u5148\u8BFB\u53D6\u76EE\u6807\u6587\u4EF6\u3002\u652F\u6301 offset/limit \u8BFB\u53D6\u5927\u6587\u4EF6\u7684\u7279\u5B9A\u90E8\u5206\u3002
|
|
1622
|
-
- **edit-file**\uFF1A\u901A\u8FC7\u5B57\u7B26\u4E32\u66FF\u6362\u7F16\u8F91\u6587\u4EF6\u3002\u4F18\u5148\u4F7F\u7528 edit-file \u800C\u975E write-file \u4FEE\u6539\u5DF2\u6709\u6587\u4EF6\u2014\u2014\u5B83\u66F4\u7CBE\u786E\u3001\u66F4\u5B89\u5168\u3002
|
|
1623
|
-
- \u26A0\uFE0F \u7CFB\u7EDF\u5F3A\u5236\uFF1A\u672A\u7528 read-file \u8BFB\u53D6\u8FC7\u7684\u6587\u4EF6\u65E0\u6CD5 edit-file\uFF0C\u4F1A\u88AB\u62E6\u622A
|
|
1624
|
-
- old_string \u5FC5\u987B\u4E0E\u6587\u4EF6\u4E2D\u7684\u5185\u5BB9**\u5B8C\u5168\u4E00\u81F4**\uFF08\u5305\u62EC\u7F29\u8FDB\u3001\u7A7A\u683C\u3001\u6362\u884C\u7B26\uFF09
|
|
1625
|
-
- old_string \u4E0D\u552F\u4E00\u65F6\uFF0C\u5305\u542B\u66F4\u591A\u4E0A\u4E0B\u6587\u884C\uFF08\u5EFA\u8BAE 3-5 \u884C\uFF09\u4F7F\u5176\u552F\u4E00
|
|
1626
|
-
- \u4E0D\u8981\u51ED\u8BB0\u5FC6\u731C\u6D4B\u6587\u4EF6\u5185\u5BB9\uFF0C\u5FC5\u987B\u57FA\u4E8E read-file \u7684\u5B9E\u9645\u8FD4\u56DE\u503C
|
|
1627
|
-
- **write-file**\uFF1A\u521B\u5EFA\u65B0\u6587\u4EF6\u6216\u5B8C\u6574\u91CD\u5199\u6587\u4EF6\u3002\u4EC5\u5728\u521B\u5EFA\u65B0\u6587\u4EF6\u6216\u9700\u8981\u5927\u5E45\u91CD\u5199\u65F6\u4F7F\u7528\u3002
|
|
1628
|
-
- **glob**\uFF1A\u6309\u6A21\u5F0F\u641C\u7D22\u6587\u4EF6\u8DEF\u5F84\u3002\u7528\u4E8E\u67E5\u627E\u6587\u4EF6\u4F4D\u7F6E\uFF08\u5982 \`**/*.ts\`\u3001\`src/**/config.*\`\uFF09\u3002
|
|
1629
|
-
- **grep**\uFF1A\u5728\u6587\u4EF6\u5185\u5BB9\u4E2D\u641C\u7D22\u6B63\u5219\u8868\u8FBE\u5F0F\u3002\u7528\u4E8E\u67E5\u627E\u51FD\u6570\u5B9A\u4E49\u3001\u5F15\u7528\u3001\u7279\u5B9A\u4EE3\u7801\u6A21\u5F0F\u3002
|
|
1630
|
-
- **bash**\uFF1A\u6267\u884C shell \u547D\u4EE4\u3002\u7528\u4E8E\u8FD0\u884C\u6784\u5EFA\u3001\u6D4B\u8BD5\u3001git \u64CD\u4F5C\u7B49\u3002\u4E0D\u8981\u7528 bash \u505A\u80FD\u7528\u4E0A\u8FF0\u5DE5\u5177\u5B8C\u6210\u7684\u4E8B\uFF08\u5982\u4E0D\u8981\u7528 cat \u8BFB\u6587\u4EF6\u3001\u4E0D\u8981\u7528 sed \u7F16\u8F91\u6587\u4EF6\u3001\u4E0D\u8981\u7528 find \u641C\u7D22\u6587\u4EF6\uFF09\u3002
|
|
1631
|
-
|
|
1632
|
-
\u5173\u952E\u89C4\u5219\uFF1A
|
|
1633
|
-
- **\u5148\u8BFB\u540E\u6539**\uFF1A\u4FEE\u6539\u6587\u4EF6\u524D\u5FC5\u987B read-file \u8BFB\u53D6\u8BE5\u6587\u4EF6\uFF08\u7CFB\u7EDF\u4F1A\u62E6\u622A\u672A\u8BFB\u53D6\u5C31 edit \u7684\u64CD\u4F5C\uFF09
|
|
1634
|
-
- edit-file \u7684 old_string \u5FC5\u987B\u4ECE read-file \u8FD4\u56DE\u7684\u5185\u5BB9\u4E2D\u7CBE\u786E\u590D\u5236\uFF0C\u4E0D\u8981\u624B\u52A8\u8F93\u5165\u6216\u51ED\u8BB0\u5FC6
|
|
1635
|
-
- \u4F18\u5148 edit-file \u7F16\u8F91\u5DF2\u6709\u6587\u4EF6\uFF0C\u800C\u975E write-file \u91CD\u5199
|
|
1636
|
-
- \u4E0D\u8981\u521B\u5EFA\u4E0D\u5FC5\u8981\u7684\u65B0\u6587\u4EF6\uFF0C\u4F18\u5148\u5728\u73B0\u6709\u6587\u4EF6\u4E2D\u4FEE\u6539
|
|
1637
|
-
- \u53EA\u505A\u5FC5\u8981\u7684\u6700\u5C0F\u6539\u52A8\uFF0C\u4E0D\u505A\u989D\u5916"\u6539\u8FDB"
|
|
1638
|
-
- \u4E0D\u8981\u6DFB\u52A0\u7528\u6237\u672A\u8981\u6C42\u7684\u6CE8\u91CA\u3001\u6587\u6863\u3001\u7C7B\u578B\u6CE8\u89E3
|
|
1639
|
-
- \u4E0D\u8981\u5F15\u5165\u5B89\u5168\u6F0F\u6D1E\uFF08\u6CE8\u5165\u3001XSS\u3001SQL \u6CE8\u5165\u7B49 OWASP Top 10\uFF09
|
|
1640
|
-
- \u5F15\u7528\u4EE3\u7801\u65F6\u4F7F\u7528 \`\u6587\u4EF6\u8DEF\u5F84:\u884C\u53F7\` \u683C\u5F0F\uFF08\u5982 \`src/app.ts:42\`\uFF09\uFF0C\u65B9\u4FBF\u7528\u6237\u8DF3\u8F6C
|
|
1641
|
-
|
|
1642
|
-
# \u4EA4\u4E92\u98CE\u683C
|
|
1643
|
-
|
|
1644
|
-
- \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
|
|
1645
|
-
- \u4E0D\u786E\u5B9A\u65F6\u5148\u8C03\u67E5\u9A8C\u8BC1\uFF0C\u800C\u975E\u76F4\u89C9\u6027\u5730\u786E\u8BA4\u7528\u6237\u7684\u5047\u8BBE
|
|
1646
|
-
- \u4E0D\u8981\u7ED9\u51FA\u65F6\u95F4\u9884\u4F30\uFF08"\u5927\u6982\u9700\u8981\u51E0\u5206\u949F"\u4E4B\u7C7B\uFF09
|
|
1647
|
-
- \u56DE\u590D\u7B80\u6D01\uFF0C\u76F4\u63A5\u7ED9\u7ED3\u679C`;
|
|
1648
|
-
}
|
|
1649
|
-
var init_core = __esm({
|
|
1650
|
-
"src/core/prompt/layers/core.ts"() {
|
|
1651
|
-
"use strict";
|
|
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"() {
|
|
1104
|
+
"use strict";
|
|
1105
|
+
IS_WIN2 = os2.platform() === "win32";
|
|
1652
1106
|
}
|
|
1653
1107
|
});
|
|
1654
1108
|
|
|
@@ -1658,8 +1112,8 @@ function buildPlanningPrompt() {
|
|
|
1658
1112
|
|
|
1659
1113
|
\u5904\u7406\u7F16\u7A0B\u4EFB\u52A1\u65F6\uFF1A
|
|
1660
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
|
|
1661
|
-
2. \
|
|
1662
|
-
3. \
|
|
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
|
|
1663
1117
|
|
|
1664
1118
|
\u591A\u6B65\u4EFB\u52A1\u7BA1\u7406\uFF1A
|
|
1665
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
|
|
@@ -1667,11 +1121,8 @@ function buildPlanningPrompt() {
|
|
|
1667
1121
|
- \u6BCF\u6B65\u5B8C\u6210\u540E\u68C0\u67E5\u8BA1\u5212\uFF0C\u51B3\u5B9A\u4E0B\u4E00\u6B65
|
|
1668
1122
|
|
|
1669
1123
|
\u4EE3\u7801\u8D28\u91CF\uFF1A
|
|
1670
|
-
- \u4E0D\u8981\u5199\u4E0D\u5FC5\u8981\u7684\u6D4B\u8BD5\uFF1B\u5982\u679C\u8981\u5199\u6D4B\u8BD5\uFF0C\u5148\u786E\u8BA4\u9879\u76EE\u7684\u6D4B\u8BD5\u6846\u67B6\u548C\u4F9D\u8D56\uFF0C\u786E\u4FDD\u80FD\u5B9E\u9645\u8FD0\u884C
|
|
1671
|
-
- \u4E0D\u8981\u8FC7\u5EA6\u5DE5\u7A0B\u5316\uFF1A\u4E0D\u8981\u4E3A\u4E00\u6B21\u6027\u64CD\u4F5C\u521B\u5EFA\u62BD\u8C61\u3001\u4E0D\u8981\u4E3A\u5047\u8BBE\u7684\u672A\u6765\u9700\u6C42\u8BBE\u8BA1
|
|
1672
|
-
- \u4E0D\u8981\u4E3A\u4E0D\u53EF\u80FD\u53D1\u751F\u7684\u573A\u666F\u6DFB\u52A0\u9519\u8BEF\u5904\u7406\uFF1B\u53EA\u5728\u7CFB\u7EDF\u8FB9\u754C\uFF08\u7528\u6237\u8F93\u5165\u3001\u5916\u90E8 API\uFF09\u505A\u6821\u9A8C
|
|
1673
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
|
|
1674
|
-
- \
|
|
1125
|
+
- \u4E0D\u8981\u7559\u4E0BTODO\u7136\u540E\u653E\u7740\u4E0D\u7BA1`;
|
|
1675
1126
|
}
|
|
1676
1127
|
var init_planning = __esm({
|
|
1677
1128
|
"src/core/prompt/layers/planning.ts"() {
|
|
@@ -1695,7 +1146,7 @@ function buildParallelPrompt() {
|
|
|
1695
1146
|
\u6B63\u786E\uFF1Aspawn-agents \u540C\u65F6\u8BFB auth controller\u3001auth service\u3001auth middleware\u3001auth types
|
|
1696
1147
|
\u9519\u8BEF\uFF1A\u5148 read-file controller\uFF0C\u518D read-file service\uFF0C\u518D read-file middleware...
|
|
1697
1148
|
|
|
1698
|
-
\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`;
|
|
1699
1150
|
}
|
|
1700
1151
|
var init_parallel = __esm({
|
|
1701
1152
|
"src/core/prompt/layers/parallel.ts"() {
|
|
@@ -1728,6 +1179,24 @@ var init_git = __esm({
|
|
|
1728
1179
|
}
|
|
1729
1180
|
});
|
|
1730
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
|
+
|
|
1731
1200
|
// src/core/prompt/layers/project.ts
|
|
1732
1201
|
import * as fs7 from "fs/promises";
|
|
1733
1202
|
import * as path7 from "path";
|
|
@@ -1769,7 +1238,7 @@ function isGitRepo() {
|
|
|
1769
1238
|
return false;
|
|
1770
1239
|
}
|
|
1771
1240
|
}
|
|
1772
|
-
async function buildPrompt(config) {
|
|
1241
|
+
async function buildPrompt(config, agents) {
|
|
1773
1242
|
const layers = [];
|
|
1774
1243
|
layers.push(buildCorePrompt());
|
|
1775
1244
|
if (config.features.planning_layer === "on") {
|
|
@@ -1782,6 +1251,12 @@ async function buildPrompt(config) {
|
|
|
1782
1251
|
if (config.features.parallel_agents === "on") {
|
|
1783
1252
|
layers.push(buildParallelPrompt());
|
|
1784
1253
|
}
|
|
1254
|
+
if (agents && agents.length > 0) {
|
|
1255
|
+
const agentsPrompt = buildAgentsPrompt(agents);
|
|
1256
|
+
if (agentsPrompt) {
|
|
1257
|
+
layers.push(agentsPrompt);
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1785
1260
|
const projectPrompt = await buildProjectPrompt();
|
|
1786
1261
|
if (projectPrompt) {
|
|
1787
1262
|
layers.push(projectPrompt);
|
|
@@ -1800,6 +1275,7 @@ var init_builder = __esm({
|
|
|
1800
1275
|
init_planning();
|
|
1801
1276
|
init_parallel();
|
|
1802
1277
|
init_git();
|
|
1278
|
+
init_agents();
|
|
1803
1279
|
init_project();
|
|
1804
1280
|
}
|
|
1805
1281
|
});
|
|
@@ -1854,74 +1330,266 @@ var init_todo_store = __esm({
|
|
|
1854
1330
|
}
|
|
1855
1331
|
});
|
|
1856
1332
|
|
|
1857
|
-
// src/core/
|
|
1858
|
-
var
|
|
1859
|
-
var
|
|
1860
|
-
"src/core/
|
|
1333
|
+
// src/core/sub-agents/registry.ts
|
|
1334
|
+
var SubAgentConfigRegistry;
|
|
1335
|
+
var init_registry2 = __esm({
|
|
1336
|
+
"src/core/sub-agents/registry.ts"() {
|
|
1861
1337
|
"use strict";
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
write(key, content, author = "agent", summary) {
|
|
1867
|
-
const trimmed = content.slice(0, MAX_CONTENT_LENGTH);
|
|
1868
|
-
const entry = {
|
|
1869
|
-
key,
|
|
1870
|
-
summary: summary || content.slice(0, 80).replace(/\n/g, " "),
|
|
1871
|
-
content: trimmed,
|
|
1872
|
-
author,
|
|
1873
|
-
updatedAt: Date.now()
|
|
1874
|
-
};
|
|
1875
|
-
if (!this.entries.has(key) && this.entries.size >= MAX_ENTRIES) {
|
|
1876
|
-
let oldest = null;
|
|
1877
|
-
let oldestTime = Infinity;
|
|
1878
|
-
for (const [k, v] of this.entries) {
|
|
1879
|
-
if (v.updatedAt < oldestTime) {
|
|
1880
|
-
oldestTime = v.updatedAt;
|
|
1881
|
-
oldest = k;
|
|
1882
|
-
}
|
|
1883
|
-
}
|
|
1884
|
-
if (oldest) this.entries.delete(oldest);
|
|
1885
|
-
}
|
|
1886
|
-
this.entries.set(key, entry);
|
|
1887
|
-
return entry;
|
|
1338
|
+
SubAgentConfigRegistry = class {
|
|
1339
|
+
configs = /* @__PURE__ */ new Map();
|
|
1340
|
+
register(config) {
|
|
1341
|
+
this.configs.set(config.name, config);
|
|
1888
1342
|
}
|
|
1889
|
-
|
|
1890
|
-
return this.
|
|
1343
|
+
get(name) {
|
|
1344
|
+
return this.configs.get(name);
|
|
1345
|
+
}
|
|
1346
|
+
has(name) {
|
|
1347
|
+
return this.configs.has(name);
|
|
1891
1348
|
}
|
|
1892
1349
|
list() {
|
|
1893
|
-
return [...this.
|
|
1894
|
-
key: e.key,
|
|
1895
|
-
author: e.author,
|
|
1896
|
-
summary: e.summary
|
|
1897
|
-
}));
|
|
1350
|
+
return [...this.configs.values()];
|
|
1898
1351
|
}
|
|
1899
|
-
|
|
1900
|
-
return this.
|
|
1352
|
+
listNames() {
|
|
1353
|
+
return [...this.configs.keys()];
|
|
1901
1354
|
}
|
|
1902
|
-
|
|
1903
|
-
|
|
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()];
|
|
1904
1516
|
}
|
|
1905
1517
|
/**
|
|
1906
|
-
*
|
|
1907
|
-
* 只输出 key + summary,清爽紧凑
|
|
1908
|
-
* Coder 需要详情时用 memo read <key> 获取完整内容
|
|
1518
|
+
* 展开 skill 的 prompt 模板,替换 $ARGS 为用户参数
|
|
1909
1519
|
*/
|
|
1910
|
-
|
|
1911
|
-
if (
|
|
1912
|
-
|
|
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;
|
|
1913
1530
|
}
|
|
1914
1531
|
};
|
|
1915
1532
|
}
|
|
1916
1533
|
});
|
|
1917
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
|
+
|
|
1918
1587
|
// src/core/sub-agent.ts
|
|
1919
1588
|
var DEFAULT_TIMEOUT_MS, SubAgent;
|
|
1920
1589
|
var init_sub_agent = __esm({
|
|
1921
1590
|
"src/core/sub-agent.ts"() {
|
|
1922
1591
|
"use strict";
|
|
1923
1592
|
init_conversation();
|
|
1924
|
-
init_auto_memo();
|
|
1925
1593
|
init_read_tracker();
|
|
1926
1594
|
DEFAULT_TIMEOUT_MS = 12e4;
|
|
1927
1595
|
SubAgent = class {
|
|
@@ -1932,8 +1600,7 @@ var init_sub_agent = __esm({
|
|
|
1932
1600
|
allowedTools;
|
|
1933
1601
|
maxTurns;
|
|
1934
1602
|
timeoutMs;
|
|
1935
|
-
|
|
1936
|
-
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) {
|
|
1937
1604
|
this.client = client;
|
|
1938
1605
|
this.registry = registry;
|
|
1939
1606
|
this.config = config;
|
|
@@ -1941,7 +1608,6 @@ var init_sub_agent = __esm({
|
|
|
1941
1608
|
this.allowedTools = allowedTools.filter((t) => t !== "spawn-agents" && t !== "todo");
|
|
1942
1609
|
this.maxTurns = Math.min(maxTurns, 15);
|
|
1943
1610
|
this.timeoutMs = timeoutMs;
|
|
1944
|
-
this.memoStore = memoStore;
|
|
1945
1611
|
}
|
|
1946
1612
|
async run() {
|
|
1947
1613
|
return Promise.race([
|
|
@@ -1960,17 +1626,8 @@ var init_sub_agent = __esm({
|
|
|
1960
1626
|
async execute() {
|
|
1961
1627
|
const conversation = new Conversation();
|
|
1962
1628
|
const readTracker = new ReadTracker();
|
|
1963
|
-
|
|
1629
|
+
const systemPrompt = `\u4F60\u662F ZenCode \u5B50 Agent\u3002\u4F60\u7684\u4EFB\u52A1\uFF1A${this.task}
|
|
1964
1630
|
\u5B8C\u6210\u540E\u76F4\u63A5\u8FD4\u56DE\u7ED3\u679C\uFF0C\u4E0D\u8981\u591A\u4F59\u89E3\u91CA\u3002`;
|
|
1965
|
-
if (this.memoStore) {
|
|
1966
|
-
const index = this.memoStore.buildIndex();
|
|
1967
|
-
if (index) {
|
|
1968
|
-
systemPrompt += `
|
|
1969
|
-
|
|
1970
|
-
[\u5171\u4EAB\u5907\u5FD8\u5F55 - \u53EF\u7528 memo read \u8BFB\u53D6\u8BE6\u60C5]
|
|
1971
|
-
${index}`;
|
|
1972
|
-
}
|
|
1973
|
-
}
|
|
1974
1631
|
conversation.setSystemPrompt(systemPrompt);
|
|
1975
1632
|
conversation.addUserMessage(this.task);
|
|
1976
1633
|
const tools = this.registry.toToolDefinitions(this.allowedTools);
|
|
@@ -2027,7 +1684,6 @@ ${index}`;
|
|
|
2027
1684
|
params,
|
|
2028
1685
|
this.config.max_tool_output
|
|
2029
1686
|
);
|
|
2030
|
-
autoMemoForTool(this.memoStore, toolName, params, result.content);
|
|
2031
1687
|
if (toolName === "read-file") {
|
|
2032
1688
|
readTracker.markRead(params["path"]);
|
|
2033
1689
|
} else if (toolName === "write-file") {
|
|
@@ -2050,7 +1706,7 @@ ${index}`;
|
|
|
2050
1706
|
});
|
|
2051
1707
|
|
|
2052
1708
|
// src/tools/spawn-agents.ts
|
|
2053
|
-
function createSpawnAgentsTool(client, registry, config, tracker
|
|
1709
|
+
function createSpawnAgentsTool(client, registry, config, tracker) {
|
|
2054
1710
|
return {
|
|
2055
1711
|
name: "spawn-agents",
|
|
2056
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",
|
|
@@ -2104,7 +1760,7 @@ function createSpawnAgentsTool(client, registry, config, tracker, memoStore) {
|
|
|
2104
1760
|
if (tools.length === 0) {
|
|
2105
1761
|
tools = DEFAULT_TOOLS.filter((t) => autoTools.includes(t));
|
|
2106
1762
|
}
|
|
2107
|
-
return new SubAgent(client, registry, config, task.description, tools, maxTurns
|
|
1763
|
+
return new SubAgent(client, registry, config, task.description, tools, maxTurns);
|
|
2108
1764
|
});
|
|
2109
1765
|
const wrappedRuns = agents.map(
|
|
2110
1766
|
(agent) => agent.run().then(
|
|
@@ -2145,7 +1801,7 @@ var init_spawn_agents = __esm({
|
|
|
2145
1801
|
"src/tools/spawn-agents.ts"() {
|
|
2146
1802
|
"use strict";
|
|
2147
1803
|
init_sub_agent();
|
|
2148
|
-
DEFAULT_TOOLS = ["read-file", "glob", "grep"
|
|
1804
|
+
DEFAULT_TOOLS = ["read-file", "glob", "grep"];
|
|
2149
1805
|
MAX_CONCURRENT = 10;
|
|
2150
1806
|
MAX_TURNS_LIMIT = 15;
|
|
2151
1807
|
}
|
|
@@ -2219,141 +1875,302 @@ ${lines.join("\n")}`
|
|
|
2219
1875
|
if (!item) {
|
|
2220
1876
|
return { content: `\u9519\u8BEF\uFF1A\u672A\u627E\u5230\u6761\u76EE "${id}"` };
|
|
2221
1877
|
}
|
|
2222
|
-
const icon = item.status === "completed" ? "\u25CF" : item.status === "in-progress" ? "\u25D0" : "\u25CB";
|
|
2223
|
-
return {
|
|
2224
|
-
content: `\u5DF2\u66F4\u65B0\uFF1A${icon} [${item.id}] ${item.title} \u2192 ${item.status}`
|
|
2225
|
-
};
|
|
2226
|
-
}
|
|
2227
|
-
case "list": {
|
|
2228
|
-
const plan = store.list();
|
|
2229
|
-
if (!plan) {
|
|
2230
|
-
return { content: "\u5F53\u524D\u6CA1\u6709\u8BA1\u5212" };
|
|
1878
|
+
const icon = item.status === "completed" ? "\u25CF" : item.status === "in-progress" ? "\u25D0" : "\u25CB";
|
|
1879
|
+
return {
|
|
1880
|
+
content: `\u5DF2\u66F4\u65B0\uFF1A${icon} [${item.id}] ${item.title} \u2192 ${item.status}`
|
|
1881
|
+
};
|
|
1882
|
+
}
|
|
1883
|
+
case "list": {
|
|
1884
|
+
const plan = store.list();
|
|
1885
|
+
if (!plan) {
|
|
1886
|
+
return { content: "\u5F53\u524D\u6CA1\u6709\u8BA1\u5212" };
|
|
1887
|
+
}
|
|
1888
|
+
const completed = plan.items.filter(
|
|
1889
|
+
(i) => i.status === "completed"
|
|
1890
|
+
).length;
|
|
1891
|
+
const lines = plan.items.map((item) => {
|
|
1892
|
+
const icon = item.status === "completed" ? "\u25CF" : item.status === "in-progress" ? "\u25D0" : "\u25CB";
|
|
1893
|
+
return `${icon} [${item.id}] ${item.title}`;
|
|
1894
|
+
});
|
|
1895
|
+
return {
|
|
1896
|
+
content: `\u8BA1\u5212\u8FDB\u5EA6 ${completed}/${plan.items.length}\uFF1A
|
|
1897
|
+
${lines.join("\n")}`
|
|
1898
|
+
};
|
|
1899
|
+
}
|
|
1900
|
+
case "clear": {
|
|
1901
|
+
store.clear();
|
|
1902
|
+
return { content: "\u8BA1\u5212\u5DF2\u6E05\u7A7A" };
|
|
1903
|
+
}
|
|
1904
|
+
default:
|
|
1905
|
+
return {
|
|
1906
|
+
content: `\u9519\u8BEF\uFF1A\u672A\u77E5\u64CD\u4F5C "${action}"\u3002\u652F\u6301: create, update, list, clear`
|
|
1907
|
+
};
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
};
|
|
1911
|
+
}
|
|
1912
|
+
var init_todo = __esm({
|
|
1913
|
+
"src/tools/todo.ts"() {
|
|
1914
|
+
"use strict";
|
|
1915
|
+
}
|
|
1916
|
+
});
|
|
1917
|
+
|
|
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;
|
|
2231
2052
|
}
|
|
2232
|
-
const completed = plan.items.filter(
|
|
2233
|
-
(i) => i.status === "completed"
|
|
2234
|
-
).length;
|
|
2235
|
-
const lines = plan.items.map((item) => {
|
|
2236
|
-
const icon = item.status === "completed" ? "\u25CF" : item.status === "in-progress" ? "\u25D0" : "\u25CB";
|
|
2237
|
-
return `${icon} [${item.id}] ${item.title}`;
|
|
2238
|
-
});
|
|
2239
|
-
return {
|
|
2240
|
-
content: `\u8BA1\u5212\u8FDB\u5EA6 ${completed}/${plan.items.length}\uFF1A
|
|
2241
|
-
${lines.join("\n")}`
|
|
2242
|
-
};
|
|
2243
|
-
}
|
|
2244
|
-
case "clear": {
|
|
2245
|
-
store.clear();
|
|
2246
|
-
return { content: "\u8BA1\u5212\u5DF2\u6E05\u7A7A" };
|
|
2247
2053
|
}
|
|
2248
|
-
|
|
2249
|
-
return {
|
|
2250
|
-
content: `\u9519\u8BEF\uFF1A\u672A\u77E5\u64CD\u4F5C "${action}"\u3002\u652F\u6301: create, update, list, clear`
|
|
2251
|
-
};
|
|
2054
|
+
return lastContent;
|
|
2252
2055
|
}
|
|
2253
|
-
}
|
|
2254
|
-
};
|
|
2255
|
-
}
|
|
2256
|
-
var init_todo = __esm({
|
|
2257
|
-
"src/tools/todo.ts"() {
|
|
2258
|
-
"use strict";
|
|
2056
|
+
};
|
|
2259
2057
|
}
|
|
2260
2058
|
});
|
|
2261
2059
|
|
|
2262
|
-
// src/tools/
|
|
2263
|
-
function
|
|
2060
|
+
// src/tools/dispatch.ts
|
|
2061
|
+
function createDispatchTool(defaultClient, toolRegistry, config, agentRegistry, callbacks) {
|
|
2264
2062
|
return {
|
|
2265
|
-
name: "
|
|
2266
|
-
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",
|
|
2267
2065
|
parameters: {
|
|
2268
2066
|
type: "object",
|
|
2269
2067
|
properties: {
|
|
2270
|
-
|
|
2068
|
+
agent: {
|
|
2271
2069
|
type: "string",
|
|
2272
|
-
description:
|
|
2273
|
-
enum:
|
|
2274
|
-
},
|
|
2275
|
-
key: {
|
|
2276
|
-
type: "string",
|
|
2277
|
-
description: "write/read/delete \u65F6\u5FC5\u586B\uFF1A\u5907\u5FD8\u5F55 key"
|
|
2070
|
+
description: `\u5B50 Agent \u540D\u79F0\u3002\u53EF\u9009: ${agentRegistry.listNames().join(", ")}`,
|
|
2071
|
+
enum: agentRegistry.listNames()
|
|
2278
2072
|
},
|
|
2279
|
-
|
|
2073
|
+
task: {
|
|
2280
2074
|
type: "string",
|
|
2281
|
-
description: "
|
|
2075
|
+
description: "\u8981\u6267\u884C\u7684\u5177\u4F53\u4EFB\u52A1\u63CF\u8FF0"
|
|
2282
2076
|
},
|
|
2283
|
-
|
|
2077
|
+
context: {
|
|
2284
2078
|
type: "string",
|
|
2285
|
-
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"
|
|
2286
2080
|
}
|
|
2287
2081
|
},
|
|
2288
|
-
required: ["
|
|
2082
|
+
required: ["agent", "task"]
|
|
2289
2083
|
},
|
|
2290
2084
|
permissionLevel: "auto",
|
|
2291
2085
|
async execute(params) {
|
|
2292
|
-
const
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
}
|
|
2316
|
-
case "list": {
|
|
2317
|
-
const items = store.list();
|
|
2318
|
-
if (items.length === 0) {
|
|
2319
|
-
return { content: "\u5907\u5FD8\u5F55\u4E3A\u7A7A" };
|
|
2320
|
-
}
|
|
2321
|
-
const lines = items.map(
|
|
2322
|
-
(item) => `[${item.key}] (${item.author}) ${item.summary}`
|
|
2323
|
-
);
|
|
2324
|
-
return { content: `\u5171 ${items.length} \u6761\u5907\u5FD8\u5F55\uFF1A
|
|
2325
|
-
${lines.join("\n")}` };
|
|
2326
|
-
}
|
|
2327
|
-
case "delete": {
|
|
2328
|
-
const key = params["key"];
|
|
2329
|
-
if (!key) {
|
|
2330
|
-
return { content: "\u9519\u8BEF\uFF1Adelete \u9700\u8981\u63D0\u4F9B key" };
|
|
2331
|
-
}
|
|
2332
|
-
const ok = store.delete(key);
|
|
2333
|
-
return { content: ok ? `\u5DF2\u5220\u9664 memo [${key}]` : `memo [${key}] \u4E0D\u5B58\u5728` };
|
|
2334
|
-
}
|
|
2335
|
-
case "clear": {
|
|
2336
|
-
store.clear();
|
|
2337
|
-
return { content: "\u5907\u5FD8\u5F55\u5DF2\u6E05\u7A7A" };
|
|
2338
|
-
}
|
|
2339
|
-
default:
|
|
2340
|
-
return { content: `\u9519\u8BEF\uFF1A\u672A\u77E5\u64CD\u4F5C "${action}"\u3002\u652F\u6301: write, read, list, delete, clear` };
|
|
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}` };
|
|
2341
2110
|
}
|
|
2342
2111
|
}
|
|
2343
2112
|
};
|
|
2344
2113
|
}
|
|
2345
|
-
var
|
|
2346
|
-
"src/tools/
|
|
2114
|
+
var init_dispatch = __esm({
|
|
2115
|
+
"src/tools/dispatch.ts"() {
|
|
2347
2116
|
"use strict";
|
|
2117
|
+
init_runner();
|
|
2118
|
+
init_client();
|
|
2348
2119
|
}
|
|
2349
2120
|
});
|
|
2350
2121
|
|
|
2351
2122
|
// src/cli/tui/bridge.ts
|
|
2123
|
+
function gray(text) {
|
|
2124
|
+
return `${ANSI_GRAY}${text}${ANSI_RESET}`;
|
|
2125
|
+
}
|
|
2352
2126
|
function createThinkFilter() {
|
|
2353
2127
|
let inThink = false;
|
|
2354
2128
|
let tagBuffer = "";
|
|
2355
|
-
let
|
|
2129
|
+
let thinkLineBuffer = "";
|
|
2130
|
+
let thinkHasVisibleContent = false;
|
|
2131
|
+
let thinkLastEmittedBlank = false;
|
|
2356
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
|
+
}
|
|
2357
2174
|
return function filter(text) {
|
|
2358
2175
|
let result = "";
|
|
2359
2176
|
for (let i = 0; i < text.length; i++) {
|
|
@@ -2362,24 +2179,25 @@ function createThinkFilter() {
|
|
|
2362
2179
|
tagBuffer += ch;
|
|
2363
2180
|
if (tagBuffer === "<think>") {
|
|
2364
2181
|
inThink = true;
|
|
2365
|
-
|
|
2182
|
+
thinkLineBuffer = "";
|
|
2183
|
+
thinkHasVisibleContent = false;
|
|
2184
|
+
thinkLastEmittedBlank = false;
|
|
2366
2185
|
tagBuffer = "";
|
|
2367
|
-
result +=
|
|
2186
|
+
result += `${gray("Thinking")}
|
|
2368
2187
|
`;
|
|
2369
2188
|
} else if (tagBuffer === "</think>") {
|
|
2370
2189
|
inThink = false;
|
|
2371
2190
|
postThink = true;
|
|
2372
2191
|
tagBuffer = "";
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
`;
|
|
2377
|
-
} else if (!"<think>".startsWith(tagBuffer) && !"</think>".startsWith(tagBuffer)) {
|
|
2378
|
-
if (inThink && lineStart) {
|
|
2379
|
-
result += "\u2502 ";
|
|
2380
|
-
lineStart = false;
|
|
2192
|
+
if (thinkLineBuffer.length > 0) {
|
|
2193
|
+
result += flushThinkLine(thinkLineBuffer);
|
|
2194
|
+
thinkLineBuffer = "";
|
|
2381
2195
|
}
|
|
2382
|
-
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);
|
|
2383
2201
|
tagBuffer = "";
|
|
2384
2202
|
}
|
|
2385
2203
|
continue;
|
|
@@ -2388,34 +2206,20 @@ function createThinkFilter() {
|
|
|
2388
2206
|
tagBuffer = "<";
|
|
2389
2207
|
continue;
|
|
2390
2208
|
}
|
|
2391
|
-
if (inThink)
|
|
2392
|
-
|
|
2393
|
-
result += "\u2502 ";
|
|
2394
|
-
lineStart = false;
|
|
2395
|
-
}
|
|
2396
|
-
result += ch;
|
|
2397
|
-
if (ch === "\n") {
|
|
2398
|
-
lineStart = true;
|
|
2399
|
-
}
|
|
2400
|
-
} else {
|
|
2401
|
-
if (postThink) {
|
|
2402
|
-
if (ch === "\n" || ch === "\r" || ch === " " || ch === " ") continue;
|
|
2403
|
-
postThink = false;
|
|
2404
|
-
}
|
|
2405
|
-
result += ch;
|
|
2406
|
-
}
|
|
2209
|
+
if (inThink) result = appendThinkText(result, ch);
|
|
2210
|
+
else result = appendOutsideText(result, ch);
|
|
2407
2211
|
}
|
|
2408
2212
|
return result;
|
|
2409
2213
|
};
|
|
2410
2214
|
}
|
|
2411
|
-
function createTokenBatcher(dispatch) {
|
|
2215
|
+
function createTokenBatcher(dispatch, type) {
|
|
2412
2216
|
let buffer = "";
|
|
2413
2217
|
let timer = null;
|
|
2414
2218
|
function flush() {
|
|
2415
2219
|
if (buffer.length > 0) {
|
|
2416
2220
|
const text = buffer;
|
|
2417
2221
|
buffer = "";
|
|
2418
|
-
dispatch({ type
|
|
2222
|
+
dispatch({ type, text });
|
|
2419
2223
|
}
|
|
2420
2224
|
}
|
|
2421
2225
|
function start() {
|
|
@@ -2460,8 +2264,10 @@ function registerConfirmToolId(toolName, id) {
|
|
|
2460
2264
|
activeToolIds.set(toolName, id);
|
|
2461
2265
|
}
|
|
2462
2266
|
function createBridgeCallbacks(dispatch) {
|
|
2463
|
-
const
|
|
2464
|
-
const
|
|
2267
|
+
const contentBatcher = createTokenBatcher(dispatch, "APPEND_CONTENT");
|
|
2268
|
+
const thoughtBatcher = createTokenBatcher(dispatch, "APPEND_THOUGHT");
|
|
2269
|
+
let inThink = false;
|
|
2270
|
+
let tagBuffer = "";
|
|
2465
2271
|
activeToolIds = /* @__PURE__ */ new Map();
|
|
2466
2272
|
streamingToolIds = /* @__PURE__ */ new Map();
|
|
2467
2273
|
lastStreamingArgs = /* @__PURE__ */ new Map();
|
|
@@ -2480,12 +2286,41 @@ function createBridgeCallbacks(dispatch) {
|
|
|
2480
2286
|
}
|
|
2481
2287
|
return {
|
|
2482
2288
|
onContent: (text) => {
|
|
2483
|
-
|
|
2484
|
-
|
|
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
|
+
}
|
|
2485
2319
|
},
|
|
2486
2320
|
onToolCallStreaming: (index, name, accumulatedArgs) => {
|
|
2487
2321
|
if (!streamingToolIds.has(index)) {
|
|
2488
|
-
|
|
2322
|
+
contentBatcher.flush();
|
|
2323
|
+
thoughtBatcher.flush();
|
|
2489
2324
|
const id2 = `tool-${++toolCallCounter}`;
|
|
2490
2325
|
streamingToolIds.set(index, id2);
|
|
2491
2326
|
activeToolIds.set(name, id2);
|
|
@@ -2505,8 +2340,10 @@ function createBridgeCallbacks(dispatch) {
|
|
|
2505
2340
|
},
|
|
2506
2341
|
onToolExecuting: (name, params) => {
|
|
2507
2342
|
flushStreamingUpdate();
|
|
2508
|
-
|
|
2509
|
-
|
|
2343
|
+
contentBatcher.flush();
|
|
2344
|
+
thoughtBatcher.flush();
|
|
2345
|
+
contentBatcher.pause();
|
|
2346
|
+
thoughtBatcher.pause();
|
|
2510
2347
|
const existingId = activeToolIds.get(name);
|
|
2511
2348
|
const id = existingId || `tool-${++toolCallCounter}`;
|
|
2512
2349
|
activeToolIds.set(name, id);
|
|
@@ -2520,41 +2357,36 @@ function createBridgeCallbacks(dispatch) {
|
|
|
2520
2357
|
if (isWriteTool) {
|
|
2521
2358
|
const code = extractCodeFromArgs(name, lastStreamingArgs.get(id) || "");
|
|
2522
2359
|
const codeLines = code ? code.split("\n").length : 0;
|
|
2523
|
-
summary = codeLines > 0 ? `${codeLines}
|
|
2360
|
+
summary = codeLines > 0 ? `${codeLines} lines` : truncated ? "truncated" : `${lines.length} lines`;
|
|
2524
2361
|
} else {
|
|
2525
|
-
summary = truncated ?
|
|
2362
|
+
summary = truncated ? `truncated` : `${lines.length} lines`;
|
|
2526
2363
|
}
|
|
2527
2364
|
const preview = lines.slice(0, 5).join("\n").slice(0, 200);
|
|
2528
2365
|
const resultContent = lines.length > 5 || preview.length >= 200 ? preview + "..." : preview;
|
|
2529
2366
|
dispatch({ type: "TOOL_RESULT", id, resultSummary: summary, resultContent });
|
|
2530
2367
|
},
|
|
2531
|
-
onCoderStart: () => {
|
|
2532
|
-
batcher.flush();
|
|
2533
|
-
dispatch({ type: "CODER_START" });
|
|
2534
|
-
},
|
|
2535
|
-
onCoderEnd: () => {
|
|
2536
|
-
dispatch({ type: "CODER_END" });
|
|
2537
|
-
},
|
|
2538
2368
|
onDenied: (toolName, feedback) => {
|
|
2539
2369
|
const id = activeToolIds.get(toolName) || `tool-${++toolCallCounter}`;
|
|
2540
2370
|
dispatch({ type: "TOOL_DENIED", id, feedback });
|
|
2541
2371
|
},
|
|
2542
2372
|
onError: (err) => {
|
|
2543
|
-
|
|
2373
|
+
contentBatcher.stop();
|
|
2374
|
+
thoughtBatcher.stop();
|
|
2544
2375
|
dispatch({ type: "SET_ERROR", error: err.message });
|
|
2545
2376
|
},
|
|
2546
|
-
// Called internally when streaming is complete
|
|
2547
2377
|
_stopBatcher: () => {
|
|
2548
|
-
|
|
2378
|
+
contentBatcher.stop();
|
|
2379
|
+
thoughtBatcher.stop();
|
|
2549
2380
|
}
|
|
2550
2381
|
};
|
|
2551
2382
|
}
|
|
2552
|
-
var BATCH_INTERVAL_MS,
|
|
2383
|
+
var BATCH_INTERVAL_MS, ANSI_GRAY, ANSI_RESET, toolCallCounter, activeToolIds, streamingToolIds, lastStreamingArgs;
|
|
2553
2384
|
var init_bridge = __esm({
|
|
2554
2385
|
"src/cli/tui/bridge.ts"() {
|
|
2555
2386
|
"use strict";
|
|
2556
2387
|
BATCH_INTERVAL_MS = 64;
|
|
2557
|
-
|
|
2388
|
+
ANSI_GRAY = "\x1B[90m";
|
|
2389
|
+
ANSI_RESET = "\x1B[0m";
|
|
2558
2390
|
toolCallCounter = 0;
|
|
2559
2391
|
activeToolIds = /* @__PURE__ */ new Map();
|
|
2560
2392
|
streamingToolIds = /* @__PURE__ */ new Map();
|
|
@@ -2613,15 +2445,12 @@ var init_sub_agent_tracker = __esm({
|
|
|
2613
2445
|
});
|
|
2614
2446
|
|
|
2615
2447
|
// src/cli/tui/state.ts
|
|
2616
|
-
function createInitialState(modelName
|
|
2448
|
+
function createInitialState(modelName) {
|
|
2617
2449
|
return {
|
|
2618
2450
|
messages: [],
|
|
2619
2451
|
isRunning: false,
|
|
2620
2452
|
error: void 0,
|
|
2621
|
-
coderWorking: false,
|
|
2622
2453
|
modelName,
|
|
2623
|
-
agentMode,
|
|
2624
|
-
collaboration,
|
|
2625
2454
|
todoPlan: null,
|
|
2626
2455
|
subAgentProgress: null
|
|
2627
2456
|
};
|
|
@@ -2691,6 +2520,21 @@ function tuiReducer(state, action) {
|
|
|
2691
2520
|
})
|
|
2692
2521
|
};
|
|
2693
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
|
+
}
|
|
2694
2538
|
case "TOOL_EXECUTING": {
|
|
2695
2539
|
return {
|
|
2696
2540
|
...state,
|
|
@@ -2836,14 +2680,8 @@ function tuiReducer(state, action) {
|
|
|
2836
2680
|
return { ...state, error: action.error, isRunning: false };
|
|
2837
2681
|
case "CLEAR_ERROR":
|
|
2838
2682
|
return { ...state, error: void 0 };
|
|
2839
|
-
case "CODER_START":
|
|
2840
|
-
return { ...state, coderWorking: true };
|
|
2841
|
-
case "CODER_END":
|
|
2842
|
-
return { ...state, coderWorking: false };
|
|
2843
2683
|
case "CLEAR_MESSAGES":
|
|
2844
2684
|
return { ...state, messages: [] };
|
|
2845
|
-
case "SET_MODE":
|
|
2846
|
-
return { ...state, agentMode: action.agentMode, collaboration: action.collaboration };
|
|
2847
2685
|
case "SET_TODO_PLAN":
|
|
2848
2686
|
return { ...state, todoPlan: action.plan };
|
|
2849
2687
|
case "SET_SUB_AGENT_PROGRESS":
|
|
@@ -2866,136 +2704,78 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
2866
2704
|
function getToolParamSummary(name, params) {
|
|
2867
2705
|
switch (name) {
|
|
2868
2706
|
case "bash":
|
|
2869
|
-
return String(params["command"] || "").slice(0,
|
|
2707
|
+
return String(params["command"] || "").slice(0, 100);
|
|
2870
2708
|
case "read-file":
|
|
2871
2709
|
case "write-file":
|
|
2872
2710
|
case "edit-file":
|
|
2873
2711
|
return String(params["path"] || "");
|
|
2874
2712
|
case "glob":
|
|
2875
|
-
return String(params["pattern"] || "");
|
|
2876
2713
|
case "grep":
|
|
2877
2714
|
return String(params["pattern"] || "");
|
|
2878
|
-
case "send-to-coder":
|
|
2879
|
-
return String(params["task"] || "").slice(0, 40);
|
|
2880
|
-
case "spawn-agents": {
|
|
2881
|
-
const tasks = params["tasks"];
|
|
2882
|
-
if (!tasks) return "";
|
|
2883
|
-
if (tasks.length <= 2) {
|
|
2884
|
-
return tasks.map((t) => t.description.slice(0, 30)).join(", ");
|
|
2885
|
-
}
|
|
2886
|
-
return `${tasks.length} \u4E2A\u5E76\u884C\u4EFB\u52A1`;
|
|
2887
|
-
}
|
|
2888
|
-
case "todo": {
|
|
2889
|
-
const action = String(params["action"] || "");
|
|
2890
|
-
const id = params["id"] ? ` [${params["id"]}]` : "";
|
|
2891
|
-
return `${action}${id}`;
|
|
2892
|
-
}
|
|
2893
|
-
case "memo": {
|
|
2894
|
-
const action = String(params["action"] || "");
|
|
2895
|
-
const key = params["key"] ? ` [${params["key"]}]` : "";
|
|
2896
|
-
return `${action}${key}`;
|
|
2897
|
-
}
|
|
2898
|
-
default: {
|
|
2899
|
-
const keys = Object.keys(params);
|
|
2900
|
-
if (keys.length > 0 && keys[0]) {
|
|
2901
|
-
return String(params[keys[0]] || "").slice(0, 40);
|
|
2902
|
-
}
|
|
2903
|
-
return "";
|
|
2904
|
-
}
|
|
2905
|
-
}
|
|
2906
|
-
}
|
|
2907
|
-
function getToolIcon(name) {
|
|
2908
|
-
switch (name) {
|
|
2909
|
-
case "bash":
|
|
2910
|
-
return "$";
|
|
2911
|
-
case "write-file":
|
|
2912
|
-
return "+";
|
|
2913
|
-
case "edit-file":
|
|
2914
|
-
return "\xB1";
|
|
2915
|
-
case "read-file":
|
|
2916
|
-
return "\u{1F4C4}";
|
|
2917
|
-
case "glob":
|
|
2918
|
-
return "\u{1F50D}";
|
|
2919
|
-
case "grep":
|
|
2920
|
-
return "\u{1F50D}";
|
|
2921
|
-
case "spawn-agents":
|
|
2922
|
-
return "\u26A1";
|
|
2923
|
-
case "todo":
|
|
2924
|
-
return "\u{1F4CB}";
|
|
2925
|
-
case "memo":
|
|
2926
|
-
return "\u{1F4DD}";
|
|
2927
2715
|
default:
|
|
2928
|
-
|
|
2716
|
+
const keys = Object.keys(params);
|
|
2717
|
+
return keys.length > 0 ? String(params[keys[0]] || "").slice(0, 60) : "";
|
|
2929
2718
|
}
|
|
2930
2719
|
}
|
|
2931
|
-
function
|
|
2932
|
-
|
|
2933
|
-
|
|
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, " "));
|
|
2934
2726
|
}
|
|
2935
|
-
if (
|
|
2936
|
-
|
|
2727
|
+
if (lines.length > maxLines) {
|
|
2728
|
+
result.push(`... (and ${lines.length - maxLines} more lines)`);
|
|
2937
2729
|
}
|
|
2938
|
-
return
|
|
2939
|
-
}
|
|
2940
|
-
function truncateCode(code, maxLines) {
|
|
2941
|
-
const lines = code.split("\n");
|
|
2942
|
-
if (lines.length <= maxLines) return code;
|
|
2943
|
-
return lines.slice(0, maxLines).join("\n") + `
|
|
2944
|
-
... (\u5171 ${lines.length} \u884C)`;
|
|
2730
|
+
return result;
|
|
2945
2731
|
}
|
|
2946
2732
|
function ToolCallLine({ toolCall }) {
|
|
2947
|
-
const { name, params, status,
|
|
2733
|
+
const { name, params, status, resultContent, denyFeedback } = toolCall;
|
|
2948
2734
|
const summary = getToolParamSummary(name, params);
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
let
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
statusText = "[y/N]";
|
|
2969
|
-
break;
|
|
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";
|
|
2970
2754
|
}
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
marginTop: 0,
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
/* @__PURE__ */ jsx(Text, { color: "red", children: "\u53CD\u9988:" }),
|
|
2996
|
-
/* @__PURE__ */ jsx(Text, { children: denyFeedback })
|
|
2997
|
-
] })
|
|
2998
|
-
] });
|
|
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
|
+
) });
|
|
2999
2779
|
}
|
|
3000
2780
|
var init_ToolCallLine = __esm({
|
|
3001
2781
|
"src/cli/tui/components/ToolCallLine.tsx"() {
|
|
@@ -3014,209 +2794,94 @@ var init_MessageBubble = __esm({
|
|
|
3014
2794
|
init_ToolCallLine();
|
|
3015
2795
|
MessageBubble = React.memo(function MessageBubble2({ message }) {
|
|
3016
2796
|
const { role, blocks, isStreaming } = message;
|
|
3017
|
-
const
|
|
3018
|
-
const
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
})
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
import React2, { useRef } from "react";
|
|
3047
|
-
import { Static, Box as Box3, Text as Text3 } from "ink";
|
|
3048
|
-
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
3049
|
-
function splitMessages(messages) {
|
|
3050
|
-
let staticEnd = 0;
|
|
3051
|
-
for (let i = 0; i < messages.length; i++) {
|
|
3052
|
-
if (messages[i].isStreaming || messages[i].confirmPending) break;
|
|
3053
|
-
staticEnd = i + 1;
|
|
3054
|
-
}
|
|
3055
|
-
const dynamicMsgs = messages.slice(staticEnd);
|
|
3056
|
-
const streamingMsg = dynamicMsgs.find((m) => m.isStreaming) || null;
|
|
3057
|
-
const otherDynamic = dynamicMsgs.filter((m) => m !== streamingMsg);
|
|
3058
|
-
return {
|
|
3059
|
-
staticMsgs: messages.slice(0, staticEnd),
|
|
3060
|
-
streamingMsg,
|
|
3061
|
-
otherDynamic
|
|
3062
|
-
};
|
|
3063
|
-
}
|
|
3064
|
-
function extractFromBlocks(msg, staticItems, dynamicNodes, isStreaming) {
|
|
3065
|
-
let lineIdx = 0;
|
|
3066
|
-
for (let bi = 0; bi < msg.blocks.length; bi++) {
|
|
3067
|
-
const block = msg.blocks[bi];
|
|
3068
|
-
const isLastBlock = bi === msg.blocks.length - 1;
|
|
3069
|
-
if (block.type === "text") {
|
|
3070
|
-
const lines = block.text.split("\n");
|
|
3071
|
-
if (isStreaming && isLastBlock) {
|
|
3072
|
-
const partial = lines.pop() || "";
|
|
3073
|
-
for (const line of lines) {
|
|
3074
|
-
staticItems.push({
|
|
3075
|
-
id: `${msg.id}-L${lineIdx}`,
|
|
3076
|
-
type: "line",
|
|
3077
|
-
text: line,
|
|
3078
|
-
isFirstLine: lineIdx === 0
|
|
3079
|
-
});
|
|
3080
|
-
lineIdx++;
|
|
3081
|
-
}
|
|
3082
|
-
if (partial || lineIdx === 0) {
|
|
3083
|
-
dynamicNodes.push(
|
|
3084
|
-
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
3085
|
-
/* @__PURE__ */ jsx3(Text3, { color: "cyan", bold: true, children: lineIdx === 0 ? "\u25C6 " : " " }),
|
|
3086
|
-
/* @__PURE__ */ jsx3(Box3, { flexGrow: 1, children: /* @__PURE__ */ jsx3(Text3, { children: partial }) })
|
|
3087
|
-
] }, "partial")
|
|
3088
|
-
);
|
|
3089
|
-
}
|
|
3090
|
-
} else {
|
|
3091
|
-
for (const line of lines) {
|
|
3092
|
-
staticItems.push({
|
|
3093
|
-
id: `${msg.id}-L${lineIdx}`,
|
|
3094
|
-
type: "line",
|
|
3095
|
-
text: line,
|
|
3096
|
-
isFirstLine: lineIdx === 0
|
|
3097
|
-
});
|
|
3098
|
-
lineIdx++;
|
|
3099
|
-
}
|
|
3100
|
-
}
|
|
3101
|
-
} else if (block.type === "tool") {
|
|
3102
|
-
const tc = block.toolCall;
|
|
3103
|
-
const isWriteTool = tc.name === "write-file" || tc.name === "edit-file";
|
|
3104
|
-
staticItems.push({
|
|
3105
|
-
id: `${msg.id}-TH${tc.id}`,
|
|
3106
|
-
type: "tool-header",
|
|
3107
|
-
toolCall: tc
|
|
3108
|
-
});
|
|
3109
|
-
if (isWriteTool && tc.status === "running" && isStreaming) {
|
|
3110
|
-
const lineCount = parseInt(tc.streamingContent || "0", 10);
|
|
3111
|
-
dynamicNodes.push(
|
|
3112
|
-
/* @__PURE__ */ jsx3(Box3, { paddingX: 1, marginLeft: 4, children: /* @__PURE__ */ jsxs3(Text3, { color: "cyan", children: [
|
|
3113
|
-
"\u270E \u751F\u6210\u4E2D...",
|
|
3114
|
-
lineCount > 0 ? ` ${lineCount} \u884C` : ""
|
|
3115
|
-
] }) }, `tool-progress-${tc.id}`)
|
|
3116
|
-
);
|
|
3117
|
-
}
|
|
3118
|
-
if (tc.status === "done" || tc.status === "denied") {
|
|
3119
|
-
staticItems.push({
|
|
3120
|
-
id: `${msg.id}-TD${tc.id}`,
|
|
3121
|
-
type: "tool-done",
|
|
3122
|
-
toolCall: tc
|
|
3123
|
-
});
|
|
3124
|
-
}
|
|
3125
|
-
}
|
|
3126
|
-
}
|
|
3127
|
-
}
|
|
3128
|
-
function renderStaticItem(item) {
|
|
3129
|
-
if (item.type === "message") {
|
|
3130
|
-
return /* @__PURE__ */ jsx3(Box3, { paddingX: 1, children: /* @__PURE__ */ jsx3(MessageBubble, { message: item.message }) }, item.id);
|
|
3131
|
-
}
|
|
3132
|
-
if (item.type === "line") {
|
|
3133
|
-
return /* @__PURE__ */ jsxs3(Box3, { paddingX: 1, children: [
|
|
3134
|
-
/* @__PURE__ */ jsx3(Text3, { color: "cyan", bold: true, children: item.isFirstLine ? "\u25C6 " : " " }),
|
|
3135
|
-
/* @__PURE__ */ jsx3(Text3, { children: item.text })
|
|
3136
|
-
] }, item.id);
|
|
3137
|
-
}
|
|
3138
|
-
if (item.type === "tool-header") {
|
|
3139
|
-
const tc = item.toolCall;
|
|
3140
|
-
const icon = getToolIcon(tc.name);
|
|
3141
|
-
const summary = getToolParamSummary(tc.name, tc.params);
|
|
3142
|
-
return /* @__PURE__ */ jsx3(Box3, { paddingX: 1, marginLeft: 2, children: /* @__PURE__ */ jsxs3(Box3, { gap: 1, children: [
|
|
3143
|
-
/* @__PURE__ */ jsx3(Text3, { color: "yellow", children: "\u23F3" }),
|
|
3144
|
-
/* @__PURE__ */ jsxs3(Text3, { color: "yellow", bold: true, children: [
|
|
3145
|
-
icon,
|
|
3146
|
-
" ",
|
|
3147
|
-
tc.name
|
|
3148
|
-
] }),
|
|
3149
|
-
summary ? /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: summary }) : null
|
|
3150
|
-
] }) }, item.id);
|
|
3151
|
-
}
|
|
3152
|
-
if (item.type === "tool-done") {
|
|
3153
|
-
return /* @__PURE__ */ jsx3(Box3, { paddingX: 1, marginLeft: 0, children: /* @__PURE__ */ jsx3(ToolCallLine, { toolCall: item.toolCall }) }, item.id);
|
|
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
|
+
});
|
|
3154
2826
|
}
|
|
3155
|
-
|
|
3156
|
-
|
|
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";
|
|
3157
2833
|
var ChatArea;
|
|
3158
2834
|
var init_ChatArea = __esm({
|
|
3159
2835
|
"src/cli/tui/components/ChatArea.tsx"() {
|
|
3160
2836
|
"use strict";
|
|
3161
2837
|
init_MessageBubble();
|
|
3162
|
-
init_ToolCallLine();
|
|
3163
2838
|
ChatArea = React2.memo(function ChatArea2({ messages }) {
|
|
3164
|
-
|
|
3165
|
-
const streamedIds = useRef(/* @__PURE__ */ new Set());
|
|
3166
|
-
if (streamingMsg) {
|
|
3167
|
-
streamedIds.current.add(streamingMsg.id);
|
|
3168
|
-
}
|
|
3169
|
-
const currentItems = [];
|
|
3170
|
-
for (const msg of staticMsgs) {
|
|
3171
|
-
if (streamedIds.current.has(msg.id)) {
|
|
3172
|
-
const ignore = [];
|
|
3173
|
-
extractFromBlocks(msg, currentItems, ignore, false);
|
|
3174
|
-
} else {
|
|
3175
|
-
currentItems.push({ id: msg.id, type: "message", message: msg });
|
|
3176
|
-
}
|
|
3177
|
-
}
|
|
3178
|
-
const dynamicNodes = [];
|
|
3179
|
-
if (streamingMsg) {
|
|
3180
|
-
extractFromBlocks(streamingMsg, currentItems, dynamicNodes, true);
|
|
3181
|
-
}
|
|
3182
|
-
const seenIds = useRef(/* @__PURE__ */ new Set());
|
|
3183
|
-
const accumulated = useRef([]);
|
|
3184
|
-
let hasNew = false;
|
|
3185
|
-
for (const item of currentItems) {
|
|
3186
|
-
if (!seenIds.current.has(item.id)) {
|
|
3187
|
-
seenIds.current.add(item.id);
|
|
3188
|
-
accumulated.current.push(item);
|
|
3189
|
-
hasNew = true;
|
|
3190
|
-
}
|
|
3191
|
-
}
|
|
3192
|
-
if (hasNew) {
|
|
3193
|
-
accumulated.current = [...accumulated.current];
|
|
3194
|
-
}
|
|
3195
|
-
const staticItems = accumulated.current;
|
|
3196
|
-
const showPlaceholder = streamingMsg && streamingMsg.blocks.length === 0;
|
|
3197
|
-
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
3198
|
-
staticItems.length > 0 && /* @__PURE__ */ jsx3(Static, { items: staticItems, children: renderStaticItem }),
|
|
3199
|
-
(dynamicNodes.length > 0 || showPlaceholder) && /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", paddingX: 1, children: [
|
|
3200
|
-
showPlaceholder && /* @__PURE__ */ jsxs3(Box3, { children: [
|
|
3201
|
-
/* @__PURE__ */ jsx3(Text3, { color: "cyan", bold: true, children: "\u25C6 " }),
|
|
3202
|
-
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "..." })
|
|
3203
|
-
] }),
|
|
3204
|
-
dynamicNodes
|
|
3205
|
-
] }),
|
|
3206
|
-
otherDynamic.map((msg) => /* @__PURE__ */ jsx3(Box3, { paddingX: 1, children: /* @__PURE__ */ jsx3(MessageBubble, { message: msg }) }, msg.id))
|
|
3207
|
-
] });
|
|
2839
|
+
return /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", width: "100%", children: messages.map((msg) => /* @__PURE__ */ jsx3(MessageBubble, { message: msg }, msg.id)) });
|
|
3208
2840
|
});
|
|
3209
2841
|
}
|
|
3210
2842
|
});
|
|
3211
2843
|
|
|
3212
2844
|
// src/cli/tui/components/InputArea.tsx
|
|
3213
|
-
import { useState } from "react";
|
|
3214
|
-
import { Box as Box4, Text as
|
|
3215
|
-
import { Fragment, jsx as jsx4, jsxs as
|
|
3216
|
-
function CleanTextInput({
|
|
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
|
+
}) {
|
|
3217
2854
|
const [value, setValue] = useState("");
|
|
3218
2855
|
const [cursor, setCursor] = useState(0);
|
|
2856
|
+
const lastCtrlCAtRef = useRef(0);
|
|
3219
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
|
+
}
|
|
3220
2885
|
if (key.return) {
|
|
3221
2886
|
const trimmed = value.trim();
|
|
3222
2887
|
if (trimmed) {
|
|
@@ -3269,30 +2934,33 @@ function CleanTextInput({ onSubmit, placeholder }) {
|
|
|
3269
2934
|
setCursor((prev) => prev + input.length);
|
|
3270
2935
|
});
|
|
3271
2936
|
if (value.length === 0) {
|
|
3272
|
-
return /* @__PURE__ */
|
|
3273
|
-
/* @__PURE__ */ jsx4(
|
|
3274
|
-
/* @__PURE__ */ jsx4(
|
|
2937
|
+
return /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
2938
|
+
/* @__PURE__ */ jsx4(Text3, { inverse: true, children: " " }),
|
|
2939
|
+
/* @__PURE__ */ jsx4(Text3, { dimColor: true, children: placeholder || "" })
|
|
3275
2940
|
] });
|
|
3276
2941
|
}
|
|
3277
2942
|
const before = value.slice(0, cursor);
|
|
3278
2943
|
const at = cursor < value.length ? value[cursor] : " ";
|
|
3279
2944
|
const after = cursor < value.length ? value.slice(cursor + 1) : "";
|
|
3280
|
-
return /* @__PURE__ */
|
|
3281
|
-
/* @__PURE__ */ jsx4(
|
|
3282
|
-
/* @__PURE__ */ jsx4(
|
|
3283
|
-
/* @__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 })
|
|
3284
2949
|
] });
|
|
3285
2950
|
}
|
|
3286
|
-
function InputArea({ onSubmit, isRunning }) {
|
|
3287
|
-
return /* @__PURE__ */
|
|
3288
|
-
/* @__PURE__ */ jsx4(
|
|
3289
|
-
|
|
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(
|
|
3290
2956
|
CleanTextInput,
|
|
3291
2957
|
{
|
|
3292
2958
|
onSubmit,
|
|
3293
|
-
placeholder: "
|
|
2959
|
+
placeholder: "Type a message or /command...",
|
|
2960
|
+
onExitRequest,
|
|
2961
|
+
onScroll
|
|
3294
2962
|
}
|
|
3295
|
-
)
|
|
2963
|
+
) })
|
|
3296
2964
|
] });
|
|
3297
2965
|
}
|
|
3298
2966
|
var init_InputArea = __esm({
|
|
@@ -3302,43 +2970,36 @@ var init_InputArea = __esm({
|
|
|
3302
2970
|
});
|
|
3303
2971
|
|
|
3304
2972
|
// src/cli/tui/components/StatusBar.tsx
|
|
3305
|
-
import { Box as Box5, Text as
|
|
3306
|
-
import { Fragment as Fragment2, jsx as jsx5, jsxs as
|
|
3307
|
-
function StatusBar({
|
|
3308
|
-
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 }) {
|
|
3309
2976
|
const todoProgress = todoPlan ? `${todoPlan.items.filter((i) => i.status === "completed").length}/${todoPlan.items.length}` : null;
|
|
3310
|
-
return /* @__PURE__ */
|
|
3311
|
-
/* @__PURE__ */ jsx5(
|
|
3312
|
-
/* @__PURE__ */
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
"
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
todoProgress && /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
3334
|
-
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "\u2502" }),
|
|
3335
|
-
/* @__PURE__ */ jsxs5(Text5, { color: "cyan", children: [
|
|
3336
|
-
"plan ",
|
|
3337
|
-
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
|
+
] })
|
|
3338
3000
|
] })
|
|
3339
3001
|
] }),
|
|
3340
|
-
/* @__PURE__ */ jsx5(
|
|
3341
|
-
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "/help" })
|
|
3002
|
+
/* @__PURE__ */ jsx5(Box5, { backgroundColor: "#3c3836", paddingX: 1, children: /* @__PURE__ */ jsx5(Text4, { color: "#ebdbb2", children: "/help" }) })
|
|
3342
3003
|
] });
|
|
3343
3004
|
}
|
|
3344
3005
|
var init_StatusBar = __esm({
|
|
@@ -3349,8 +3010,8 @@ var init_StatusBar = __esm({
|
|
|
3349
3010
|
|
|
3350
3011
|
// src/cli/tui/components/ConfirmPrompt.tsx
|
|
3351
3012
|
import { useState as useState2 } from "react";
|
|
3352
|
-
import { Box as Box6, Text as
|
|
3353
|
-
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";
|
|
3354
3015
|
function getToolDetails(toolName, params) {
|
|
3355
3016
|
const lines = [];
|
|
3356
3017
|
let label = toolName;
|
|
@@ -3436,18 +3097,18 @@ function FeedbackInput({ onSubmit }) {
|
|
|
3436
3097
|
setCursor((prev) => prev + input.length);
|
|
3437
3098
|
});
|
|
3438
3099
|
if (value.length === 0) {
|
|
3439
|
-
return /* @__PURE__ */
|
|
3440
|
-
/* @__PURE__ */ jsx6(
|
|
3441
|
-
/* @__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..." })
|
|
3442
3103
|
] });
|
|
3443
3104
|
}
|
|
3444
3105
|
const before = value.slice(0, cursor);
|
|
3445
3106
|
const at = cursor < value.length ? value[cursor] : " ";
|
|
3446
3107
|
const after = cursor < value.length ? value.slice(cursor + 1) : "";
|
|
3447
|
-
return /* @__PURE__ */
|
|
3448
|
-
/* @__PURE__ */ jsx6(
|
|
3449
|
-
/* @__PURE__ */ jsx6(
|
|
3450
|
-
/* @__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 })
|
|
3451
3112
|
] });
|
|
3452
3113
|
}
|
|
3453
3114
|
function ConfirmPrompt({ confirm, onRespond }) {
|
|
@@ -3475,66 +3136,69 @@ function ConfirmPrompt({ confirm, onRespond }) {
|
|
|
3475
3136
|
}, { isActive: !feedbackMode });
|
|
3476
3137
|
const { lines, label } = getToolDetails(confirm.toolName, confirm.params);
|
|
3477
3138
|
if (feedbackMode) {
|
|
3478
|
-
return /* @__PURE__ */
|
|
3479
|
-
/* @__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(
|
|
3480
3142
|
Box6,
|
|
3481
3143
|
{
|
|
3482
3144
|
flexDirection: "column",
|
|
3483
3145
|
borderStyle: "round",
|
|
3484
|
-
borderColor: "
|
|
3146
|
+
borderColor: "#fe8019",
|
|
3485
3147
|
paddingX: 1,
|
|
3486
3148
|
children: [
|
|
3487
|
-
/* @__PURE__ */ jsx6(
|
|
3488
|
-
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
|
+
] })
|
|
3489
3167
|
]
|
|
3490
3168
|
}
|
|
3491
3169
|
),
|
|
3492
|
-
/* @__PURE__ */
|
|
3493
|
-
/* @__PURE__ */ jsx6(Text6, { color: "cyan", children: "\u53CD\u9988: " }),
|
|
3494
|
-
/* @__PURE__ */ jsx6(
|
|
3495
|
-
FeedbackInput,
|
|
3496
|
-
{
|
|
3497
|
-
onSubmit: (text) => {
|
|
3498
|
-
const trimmed = text.trim();
|
|
3499
|
-
if (trimmed) {
|
|
3500
|
-
onRespond({ feedback: trimmed });
|
|
3501
|
-
} else {
|
|
3502
|
-
setFeedbackMode(false);
|
|
3503
|
-
}
|
|
3504
|
-
}
|
|
3505
|
-
}
|
|
3506
|
-
)
|
|
3507
|
-
] }),
|
|
3508
|
-
/* @__PURE__ */ jsx6(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Enter \u53D1\u9001 Esc \u8FD4\u56DE\u9009\u62E9" }) })
|
|
3170
|
+
/* @__PURE__ */ jsx6(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx6(Text5, { dimColor: true, children: "Enter to send \xB7 Esc to cancel" }) })
|
|
3509
3171
|
] });
|
|
3510
3172
|
}
|
|
3511
|
-
return /* @__PURE__ */
|
|
3512
|
-
/* @__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(
|
|
3513
3176
|
Box6,
|
|
3514
3177
|
{
|
|
3515
3178
|
flexDirection: "column",
|
|
3516
3179
|
borderStyle: "round",
|
|
3517
|
-
borderColor: "
|
|
3180
|
+
borderColor: "#fe8019",
|
|
3518
3181
|
paddingX: 1,
|
|
3519
3182
|
children: [
|
|
3520
|
-
/* @__PURE__ */ jsx6(
|
|
3521
|
-
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
|
+
}) })
|
|
3522
3198
|
]
|
|
3523
3199
|
}
|
|
3524
3200
|
),
|
|
3525
|
-
/* @__PURE__ */ jsx6(Box6, {
|
|
3526
|
-
const isSelected = i === selected;
|
|
3527
|
-
return /* @__PURE__ */ jsx6(Box6, { children: isSelected ? /* @__PURE__ */ jsxs6(Text6, { bold: true, color: "cyan", inverse: true, children: [
|
|
3528
|
-
" ",
|
|
3529
|
-
opt.label,
|
|
3530
|
-
" "
|
|
3531
|
-
] }) : /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
3532
|
-
" ",
|
|
3533
|
-
opt.label,
|
|
3534
|
-
" "
|
|
3535
|
-
] }) }, opt.key);
|
|
3536
|
-
}) }),
|
|
3537
|
-
/* @__PURE__ */ jsx6(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "\u2190 \u2192 \u9009\u62E9 Enter \u786E\u8BA4 y \u5141\u8BB8 n \u62D2\u7EDD a \u59CB\u7EC8 Tab \u53CD\u9988" }) })
|
|
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" }) })
|
|
3538
3202
|
] });
|
|
3539
3203
|
}
|
|
3540
3204
|
var OPTIONS;
|
|
@@ -3550,55 +3214,57 @@ var init_ConfirmPrompt = __esm({
|
|
|
3550
3214
|
});
|
|
3551
3215
|
|
|
3552
3216
|
// src/cli/tui/components/TodoPanel.tsx
|
|
3553
|
-
import { Box as Box7, Text as
|
|
3554
|
-
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";
|
|
3555
3219
|
function getStatusIcon(status) {
|
|
3556
3220
|
switch (status) {
|
|
3557
3221
|
case "completed":
|
|
3558
|
-
return "\
|
|
3222
|
+
return "\u2705";
|
|
3559
3223
|
case "in-progress":
|
|
3560
|
-
return "\
|
|
3224
|
+
return "\u23F3";
|
|
3561
3225
|
default:
|
|
3562
|
-
return "\
|
|
3226
|
+
return "\u26AA";
|
|
3563
3227
|
}
|
|
3564
3228
|
}
|
|
3565
3229
|
function getStatusColor(status) {
|
|
3566
3230
|
switch (status) {
|
|
3567
3231
|
case "completed":
|
|
3568
|
-
return "
|
|
3232
|
+
return "#b8bb26";
|
|
3569
3233
|
case "in-progress":
|
|
3570
|
-
return "
|
|
3234
|
+
return "#fabd2f";
|
|
3571
3235
|
default:
|
|
3572
|
-
return "
|
|
3236
|
+
return "#928374";
|
|
3573
3237
|
}
|
|
3574
3238
|
}
|
|
3575
3239
|
function TodoPanel({ plan }) {
|
|
3576
3240
|
const completed = plan.items.filter((i) => i.status === "completed").length;
|
|
3577
3241
|
const total = plan.items.length;
|
|
3578
|
-
return /* @__PURE__ */
|
|
3242
|
+
return /* @__PURE__ */ jsxs6(
|
|
3579
3243
|
Box7,
|
|
3580
3244
|
{
|
|
3581
3245
|
flexDirection: "column",
|
|
3582
3246
|
borderStyle: "round",
|
|
3583
|
-
borderColor: "
|
|
3247
|
+
borderColor: "#83a598",
|
|
3584
3248
|
paddingX: 1,
|
|
3585
|
-
|
|
3249
|
+
marginY: 1,
|
|
3586
3250
|
children: [
|
|
3587
|
-
/* @__PURE__ */
|
|
3588
|
-
/* @__PURE__ */ jsx7(
|
|
3589
|
-
/* @__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: [
|
|
3590
3254
|
completed,
|
|
3591
3255
|
"/",
|
|
3592
|
-
total
|
|
3256
|
+
total,
|
|
3257
|
+
" tasks"
|
|
3593
3258
|
] })
|
|
3594
3259
|
] }),
|
|
3595
|
-
plan.items.map((item) => /* @__PURE__ */
|
|
3596
|
-
/* @__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) }),
|
|
3597
3262
|
/* @__PURE__ */ jsx7(
|
|
3598
|
-
|
|
3263
|
+
Text6,
|
|
3599
3264
|
{
|
|
3600
|
-
color: item.status === "completed" ? "
|
|
3265
|
+
color: item.status === "completed" ? "#b8bb26" : item.status === "in-progress" ? "#fabd2f" : "#ebdbb2",
|
|
3601
3266
|
dimColor: item.status === "pending",
|
|
3267
|
+
strikethrough: item.status === "completed",
|
|
3602
3268
|
children: item.title
|
|
3603
3269
|
}
|
|
3604
3270
|
)
|
|
@@ -3613,26 +3279,62 @@ var init_TodoPanel = __esm({
|
|
|
3613
3279
|
}
|
|
3614
3280
|
});
|
|
3615
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
|
+
|
|
3616
3312
|
// src/cli/tui/App.tsx
|
|
3617
3313
|
import { useReducer, useCallback, useEffect, useRef as useRef2, useState as useState3 } from "react";
|
|
3618
|
-
import { Box as
|
|
3619
|
-
import { jsx as
|
|
3620
|
-
function App({ config, client, agent,
|
|
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]);
|
|
3621
3328
|
const [state, dispatch] = useReducer(
|
|
3622
3329
|
tuiReducer,
|
|
3623
|
-
createInitialState(
|
|
3624
|
-
config.model,
|
|
3625
|
-
config.agent_mode,
|
|
3626
|
-
config.collaboration
|
|
3627
|
-
)
|
|
3330
|
+
createInitialState(config.model)
|
|
3628
3331
|
);
|
|
3629
3332
|
const [resetKey, setResetKey] = useState3(0);
|
|
3333
|
+
const currentCallbacksRef = useRef2(null);
|
|
3630
3334
|
const agentRef = useRef2(agent);
|
|
3631
|
-
const orchestratorRef = useRef2(orchestrator);
|
|
3632
3335
|
const todoStoreRef = useRef2(todoStore);
|
|
3633
3336
|
const subAgentTrackerRef = useRef2(subAgentTracker);
|
|
3634
3337
|
agentRef.current = agent;
|
|
3635
|
-
orchestratorRef.current = orchestrator;
|
|
3636
3338
|
todoStoreRef.current = todoStore;
|
|
3637
3339
|
subAgentTrackerRef.current = subAgentTracker;
|
|
3638
3340
|
useEffect(() => {
|
|
@@ -3672,7 +3374,7 @@ function App({ config, client, agent, orchestrator, registry, todoStore, memoSto
|
|
|
3672
3374
|
);
|
|
3673
3375
|
useEffect(() => {
|
|
3674
3376
|
setStructuredConfirmHandler((toolName, params) => {
|
|
3675
|
-
return new Promise((
|
|
3377
|
+
return new Promise((resolve10) => {
|
|
3676
3378
|
const id = `confirm-${Date.now()}`;
|
|
3677
3379
|
registerConfirmToolId(toolName, id);
|
|
3678
3380
|
dispatch({
|
|
@@ -3683,13 +3385,13 @@ function App({ config, client, agent, orchestrator, registry, todoStore, memoSto
|
|
|
3683
3385
|
resolve: (result) => {
|
|
3684
3386
|
if (result === "always") {
|
|
3685
3387
|
registry.addAutoApprove(toolName);
|
|
3686
|
-
|
|
3388
|
+
resolve10({ approved: true });
|
|
3687
3389
|
} else if (result === "allow") {
|
|
3688
|
-
|
|
3390
|
+
resolve10({ approved: true });
|
|
3689
3391
|
} else if (result === "deny") {
|
|
3690
|
-
|
|
3392
|
+
resolve10({ approved: false });
|
|
3691
3393
|
} else {
|
|
3692
|
-
|
|
3394
|
+
resolve10({ approved: false, feedback: result.feedback });
|
|
3693
3395
|
}
|
|
3694
3396
|
}
|
|
3695
3397
|
});
|
|
@@ -3699,39 +3401,53 @@ function App({ config, client, agent, orchestrator, registry, todoStore, memoSto
|
|
|
3699
3401
|
setStructuredConfirmHandler(null);
|
|
3700
3402
|
};
|
|
3701
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
|
+
}, []);
|
|
3702
3424
|
const handleSubmit = useCallback(async (text) => {
|
|
3703
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
|
+
}
|
|
3704
3435
|
handleSlashCommand2(text, {
|
|
3705
3436
|
config,
|
|
3706
|
-
|
|
3437
|
+
agent: agentRef.current,
|
|
3707
3438
|
registry,
|
|
3708
3439
|
dispatch,
|
|
3709
3440
|
setResetKey,
|
|
3710
3441
|
client,
|
|
3711
3442
|
todoStore,
|
|
3712
|
-
|
|
3713
|
-
|
|
3443
|
+
subAgentTracker,
|
|
3444
|
+
agentRegistry,
|
|
3445
|
+
skillRegistry
|
|
3714
3446
|
});
|
|
3715
3447
|
return;
|
|
3716
3448
|
}
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
dispatch({ type: "SET_RUNNING", running: true });
|
|
3720
|
-
const callbacks = createBridgeCallbacks(dispatch);
|
|
3721
|
-
try {
|
|
3722
|
-
if (config.agent_mode === "single") {
|
|
3723
|
-
await agentRef.current.run(text, callbacks);
|
|
3724
|
-
} else {
|
|
3725
|
-
await orchestratorRef.current.run(text, callbacks);
|
|
3726
|
-
}
|
|
3727
|
-
} catch (err) {
|
|
3728
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
3729
|
-
dispatch({ type: "SET_ERROR", error: msg });
|
|
3730
|
-
}
|
|
3731
|
-
callbacks._stopBatcher?.();
|
|
3732
|
-
dispatch({ type: "FINISH_STREAMING" });
|
|
3733
|
-
dispatch({ type: "SET_RUNNING", running: false });
|
|
3734
|
-
}, [config]);
|
|
3449
|
+
await runAgent(text);
|
|
3450
|
+
}, [config, runAgent]);
|
|
3735
3451
|
const handleConfirmResponse = useCallback((result) => {
|
|
3736
3452
|
if (confirmPending) {
|
|
3737
3453
|
confirmPending.resolve(result);
|
|
@@ -3749,114 +3465,118 @@ function App({ config, client, agent, orchestrator, registry, todoStore, memoSto
|
|
|
3749
3465
|
}
|
|
3750
3466
|
}, [confirmPending, state.messages]);
|
|
3751
3467
|
useInput3((input, key) => {
|
|
3752
|
-
if (
|
|
3468
|
+
if (key.escape) {
|
|
3753
3469
|
if (state.isRunning) {
|
|
3470
|
+
agentRef.current.interrupt();
|
|
3471
|
+
currentCallbacksRef.current?._stopBatcher?.();
|
|
3754
3472
|
dispatch({ type: "SET_RUNNING", running: false });
|
|
3755
3473
|
dispatch({ type: "FINISH_STREAMING" });
|
|
3756
|
-
} else {
|
|
3757
|
-
process.exit(0);
|
|
3758
3474
|
}
|
|
3759
3475
|
return;
|
|
3760
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
|
+
}
|
|
3761
3486
|
if (input === "d" && key.ctrl) {
|
|
3762
3487
|
process.exit(0);
|
|
3763
3488
|
}
|
|
3764
3489
|
});
|
|
3765
|
-
return /* @__PURE__ */ jsxs8(
|
|
3766
|
-
/* @__PURE__ */
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
]
|
|
3778
|
-
}
|
|
3779
|
-
),
|
|
3780
|
-
confirmPending && /* @__PURE__ */ jsx8(
|
|
3781
|
-
ConfirmPrompt,
|
|
3782
|
-
{
|
|
3783
|
-
confirm: confirmPending,
|
|
3784
|
-
onRespond: handleConfirmResponse
|
|
3785
|
-
}
|
|
3786
|
-
),
|
|
3787
|
-
state.todoPlan && /* @__PURE__ */ jsx8(TodoPanel, { plan: state.todoPlan }),
|
|
3788
|
-
/* @__PURE__ */ jsx8(
|
|
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(
|
|
3789
3502
|
InputArea,
|
|
3790
3503
|
{
|
|
3791
3504
|
onSubmit: handleSubmit,
|
|
3792
|
-
isRunning: state.isRunning || !!confirmPending
|
|
3505
|
+
isRunning: state.isRunning || !!confirmPending,
|
|
3506
|
+
onExitRequest: () => process.exit(0)
|
|
3793
3507
|
}
|
|
3794
|
-
),
|
|
3795
|
-
/* @__PURE__ */
|
|
3508
|
+
) }),
|
|
3509
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 0, children: /* @__PURE__ */ jsx9(
|
|
3796
3510
|
StatusBar,
|
|
3797
3511
|
{
|
|
3798
|
-
agentMode: state.agentMode,
|
|
3799
|
-
collaboration: state.collaboration,
|
|
3800
|
-
coderWorking: state.coderWorking,
|
|
3801
3512
|
isRunning: state.isRunning,
|
|
3802
3513
|
modelName: state.modelName,
|
|
3803
3514
|
todoPlan: state.todoPlan,
|
|
3804
3515
|
subAgentProgress: state.subAgentProgress
|
|
3805
3516
|
}
|
|
3806
|
-
)
|
|
3517
|
+
) })
|
|
3807
3518
|
] }, resetKey);
|
|
3808
3519
|
}
|
|
3809
3520
|
function handleSlashCommand2(input, ctx) {
|
|
3810
|
-
const { config,
|
|
3521
|
+
const { config, agent, registry, dispatch, setResetKey, client, todoStore, subAgentTracker, agentRegistry, skillRegistry } = ctx;
|
|
3811
3522
|
const parts = input.trim().split(/\s+/);
|
|
3812
3523
|
const command = parts[0];
|
|
3813
3524
|
switch (command) {
|
|
3814
3525
|
case "/help":
|
|
3815
3526
|
dispatch({ type: "ADD_USER_MESSAGE", text: input });
|
|
3816
3527
|
dispatch({ type: "START_ASSISTANT" });
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
text: `\u53EF\u7528\u547D\u4EE4:
|
|
3528
|
+
{
|
|
3529
|
+
let helpText = `\u53EF\u7528\u547D\u4EE4:
|
|
3820
3530
|
/help \u663E\u793A\u6B64\u5E2E\u52A9\u4FE1\u606F
|
|
3821
|
-
/
|
|
3822
|
-
/
|
|
3823
|
-
/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
|
|
3824
3533
|
/parallel \u5207\u6362\u5E76\u884C\u5B50 Agent \u529F\u80FD on/off
|
|
3825
3534
|
/todo \u5207\u6362 todo \u8BA1\u5212\u529F\u80FD on/off
|
|
3826
3535
|
/clear \u6E05\u7A7A\u5BF9\u8BDD\u5386\u53F2
|
|
3827
3536
|
/info \u663E\u793A\u5F53\u524D\u914D\u7F6E
|
|
3828
3537
|
Ctrl+C \u53D6\u6D88\u5F53\u524D\u8BF7\u6C42 / \u9000\u51FA
|
|
3829
|
-
Ctrl+D \u9000\u51FA
|
|
3830
|
-
|
|
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
|
+
}
|
|
3831
3548
|
dispatch({ type: "FINISH_STREAMING" });
|
|
3832
3549
|
break;
|
|
3833
|
-
case "/
|
|
3834
|
-
const
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
dispatch({
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
dispatch({ type: "FINISH_STREAMING" });
|
|
3844
|
-
} else if (["delegated", "autonomous", "controlled"].includes(mode)) {
|
|
3845
|
-
config.collaboration = mode;
|
|
3846
|
-
orchestrator?.setMode(mode);
|
|
3847
|
-
dispatch({ type: "SET_MODE", agentMode: config.agent_mode, collaboration: mode });
|
|
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")}` });
|
|
3848
3560
|
}
|
|
3561
|
+
dispatch({ type: "FINISH_STREAMING" });
|
|
3849
3562
|
break;
|
|
3850
3563
|
}
|
|
3851
|
-
case "/
|
|
3852
|
-
|
|
3853
|
-
dispatch({ type: "
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
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" });
|
|
3858
3576
|
break;
|
|
3577
|
+
}
|
|
3859
3578
|
case "/clear":
|
|
3579
|
+
agent.getConversation().clear();
|
|
3860
3580
|
dispatch({ type: "CLEAR_MESSAGES" });
|
|
3861
3581
|
setResetKey((prev) => prev + 1);
|
|
3862
3582
|
break;
|
|
@@ -3867,7 +3587,7 @@ function handleSlashCommand2(input, ctx) {
|
|
|
3867
3587
|
if (next === "off") {
|
|
3868
3588
|
registry.unregister("spawn-agents");
|
|
3869
3589
|
} else if (!registry.has("spawn-agents")) {
|
|
3870
|
-
registry.register(createSpawnAgentsTool(client, registry, config, subAgentTracker
|
|
3590
|
+
registry.register(createSpawnAgentsTool(client, registry, config, subAgentTracker));
|
|
3871
3591
|
}
|
|
3872
3592
|
dispatch({ type: "ADD_USER_MESSAGE", text: input });
|
|
3873
3593
|
dispatch({ type: "START_ASSISTANT" });
|
|
@@ -3896,9 +3616,9 @@ function handleSlashCommand2(input, ctx) {
|
|
|
3896
3616
|
dispatch({
|
|
3897
3617
|
type: "APPEND_CONTENT",
|
|
3898
3618
|
text: `\u6A21\u578B: ${config.model}
|
|
3899
|
-
|
|
3900
|
-
\
|
|
3901
|
-
\
|
|
3619
|
+
\u57FA\u7840 URL: ${config.base_url}
|
|
3620
|
+
\u5B50 Agent: ${agentRegistry.listNames().join(", ") || "\u65E0"}
|
|
3621
|
+
\u6280\u80FD: ${skillRegistry.listNames().map((n) => "/" + n).join(", ") || "\u65E0"}`
|
|
3902
3622
|
});
|
|
3903
3623
|
dispatch({ type: "FINISH_STREAMING" });
|
|
3904
3624
|
break;
|
|
@@ -3916,6 +3636,7 @@ Agent \u6A21\u5F0F: ${config.agent_mode}
|
|
|
3916
3636
|
var init_App = __esm({
|
|
3917
3637
|
"src/cli/tui/App.tsx"() {
|
|
3918
3638
|
"use strict";
|
|
3639
|
+
init_client();
|
|
3919
3640
|
init_permission();
|
|
3920
3641
|
init_state();
|
|
3921
3642
|
init_bridge();
|
|
@@ -3926,6 +3647,7 @@ var init_App = __esm({
|
|
|
3926
3647
|
init_StatusBar();
|
|
3927
3648
|
init_ConfirmPrompt();
|
|
3928
3649
|
init_TodoPanel();
|
|
3650
|
+
init_Header();
|
|
3929
3651
|
}
|
|
3930
3652
|
});
|
|
3931
3653
|
|
|
@@ -3935,10 +3657,14 @@ __export(tui_exports, {
|
|
|
3935
3657
|
startTui: () => startTui
|
|
3936
3658
|
});
|
|
3937
3659
|
import { render } from "ink";
|
|
3938
|
-
import { jsx as
|
|
3660
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
3939
3661
|
async function startTui(options) {
|
|
3940
3662
|
const { config } = options;
|
|
3941
|
-
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());
|
|
3942
3668
|
const registry = new ToolRegistry();
|
|
3943
3669
|
registerCoreTools(registry);
|
|
3944
3670
|
registry.setPermissions(config.permissions);
|
|
@@ -3950,32 +3676,37 @@ async function startTui(options) {
|
|
|
3950
3676
|
maxTokens: config.max_tokens
|
|
3951
3677
|
});
|
|
3952
3678
|
const todoStore = new TodoStore();
|
|
3953
|
-
const memoStore = new MemoStore();
|
|
3954
3679
|
const subAgentTracker = new SubAgentTracker();
|
|
3680
|
+
const skillRegistry = new SkillRegistry();
|
|
3681
|
+
for (const skill of loadAllSkills()) {
|
|
3682
|
+
skillRegistry.register(skill);
|
|
3683
|
+
}
|
|
3955
3684
|
if (config.features.parallel_agents === "on") {
|
|
3956
|
-
registry.register(createSpawnAgentsTool(client, registry, config, subAgentTracker
|
|
3685
|
+
registry.register(createSpawnAgentsTool(client, registry, config, subAgentTracker));
|
|
3957
3686
|
}
|
|
3958
3687
|
if (config.features.todo === "on") {
|
|
3959
3688
|
registry.register(createTodoTool(todoStore));
|
|
3960
3689
|
}
|
|
3961
|
-
registry.register(
|
|
3962
|
-
const agent = new Agent(client, registry, config, systemPrompt
|
|
3963
|
-
const orchestrator = new Orchestrator(registry, config, systemPrompt, memoStore);
|
|
3690
|
+
registry.register(createDispatchTool(client, registry, config, agentRegistry));
|
|
3691
|
+
const agent = new Agent(client, registry, config, systemPrompt);
|
|
3964
3692
|
const { waitUntilExit } = render(
|
|
3965
|
-
/* @__PURE__ */
|
|
3693
|
+
/* @__PURE__ */ jsx10(
|
|
3966
3694
|
App,
|
|
3967
3695
|
{
|
|
3968
3696
|
config,
|
|
3969
3697
|
client,
|
|
3970
3698
|
agent,
|
|
3971
|
-
orchestrator,
|
|
3972
3699
|
registry,
|
|
3973
3700
|
todoStore,
|
|
3974
|
-
|
|
3975
|
-
|
|
3701
|
+
subAgentTracker,
|
|
3702
|
+
agentRegistry,
|
|
3703
|
+
skillRegistry
|
|
3976
3704
|
}
|
|
3977
3705
|
),
|
|
3978
|
-
{
|
|
3706
|
+
{
|
|
3707
|
+
patchConsole: true,
|
|
3708
|
+
exitOnCtrlC: false
|
|
3709
|
+
}
|
|
3979
3710
|
);
|
|
3980
3711
|
await waitUntilExit();
|
|
3981
3712
|
}
|
|
@@ -3986,41 +3717,129 @@ var init_tui = __esm({
|
|
|
3986
3717
|
init_registry();
|
|
3987
3718
|
init_register();
|
|
3988
3719
|
init_agent();
|
|
3989
|
-
init_orchestrator();
|
|
3990
3720
|
init_builder();
|
|
3991
3721
|
init_todo_store();
|
|
3992
|
-
init_memo_store();
|
|
3993
3722
|
init_sub_agent_tracker();
|
|
3723
|
+
init_registry2();
|
|
3724
|
+
init_loader();
|
|
3725
|
+
init_registry3();
|
|
3726
|
+
init_loader2();
|
|
3994
3727
|
init_spawn_agents();
|
|
3995
3728
|
init_todo();
|
|
3996
|
-
|
|
3729
|
+
init_dispatch();
|
|
3997
3730
|
init_App();
|
|
3998
3731
|
}
|
|
3999
3732
|
});
|
|
4000
3733
|
|
|
4001
3734
|
// src/cli/index.ts
|
|
4002
|
-
|
|
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
|
|
4003
3822
|
init_client();
|
|
4004
3823
|
init_registry();
|
|
4005
3824
|
init_register();
|
|
4006
3825
|
init_agent();
|
|
4007
|
-
init_orchestrator();
|
|
4008
3826
|
init_builder();
|
|
4009
|
-
import { Command } from "commander";
|
|
4010
3827
|
|
|
4011
3828
|
// src/cli/repl.ts
|
|
4012
3829
|
init_client();
|
|
4013
3830
|
init_registry();
|
|
4014
3831
|
init_agent();
|
|
4015
|
-
init_orchestrator();
|
|
4016
3832
|
init_builder();
|
|
4017
3833
|
init_register();
|
|
4018
3834
|
init_permission();
|
|
4019
3835
|
init_todo_store();
|
|
4020
|
-
|
|
3836
|
+
init_registry2();
|
|
3837
|
+
init_loader();
|
|
3838
|
+
init_registry3();
|
|
3839
|
+
init_loader2();
|
|
4021
3840
|
init_spawn_agents();
|
|
4022
3841
|
init_todo();
|
|
4023
|
-
|
|
3842
|
+
init_dispatch();
|
|
4024
3843
|
init_bridge();
|
|
4025
3844
|
import * as readline from "readline";
|
|
4026
3845
|
|
|
@@ -4052,12 +3871,8 @@ function printToolCall(toolName, params) {
|
|
|
4052
3871
|
const action = String(params["action"]);
|
|
4053
3872
|
const id = params["id"] ? ` [${params["id"]}]` : "";
|
|
4054
3873
|
detail = ` ${chalk2.dim(`${action}${id}`)}`;
|
|
4055
|
-
} else if (toolName === "memo" && params["action"]) {
|
|
4056
|
-
const action = String(params["action"]);
|
|
4057
|
-
const key = params["key"] ? ` [${params["key"]}]` : "";
|
|
4058
|
-
detail = ` ${chalk2.dim(`${action}${key}`)}`;
|
|
4059
3874
|
}
|
|
4060
|
-
const icon = toolName === "spawn-agents" ? "\u26A1" : toolName === "todo" ? "\u{1F4CB}" :
|
|
3875
|
+
const icon = toolName === "spawn-agents" ? "\u26A1" : toolName === "todo" ? "\u{1F4CB}" : "\u2699";
|
|
4061
3876
|
console.log(chalk2.yellow(` ${icon} ${toolName}`) + detail);
|
|
4062
3877
|
}
|
|
4063
3878
|
function printToolResult(toolName, result, truncated) {
|
|
@@ -4080,10 +3895,9 @@ function printWarning(message) {
|
|
|
4080
3895
|
function printSuccess(message) {
|
|
4081
3896
|
console.log(chalk2.green(`\u2713 ${message}`));
|
|
4082
3897
|
}
|
|
4083
|
-
function printWelcome(modelName
|
|
3898
|
+
function printWelcome(modelName) {
|
|
4084
3899
|
console.log(chalk2.bold.cyan("\n ZenCode") + chalk2.dim(" - \u6781\u7B80 AI \u7F16\u7A0B\u52A9\u624B\n"));
|
|
4085
3900
|
console.log(chalk2.dim(` \u6A21\u578B: ${modelName}`));
|
|
4086
|
-
console.log(chalk2.dim(` \u6A21\u5F0F: ${mode}`));
|
|
4087
3901
|
console.log(chalk2.dim(` \u8F93\u5165 /help \u67E5\u770B\u547D\u4EE4\uFF0CCtrl+C \u9000\u51FA
|
|
4088
3902
|
`));
|
|
4089
3903
|
}
|
|
@@ -4097,9 +3911,8 @@ function handleSlashCommand(input, context) {
|
|
|
4097
3911
|
console.log(`
|
|
4098
3912
|
\u53EF\u7528\u547D\u4EE4:
|
|
4099
3913
|
/help \u663E\u793A\u6B64\u5E2E\u52A9\u4FE1\u606F
|
|
4100
|
-
/
|
|
4101
|
-
/
|
|
4102
|
-
/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
|
|
4103
3916
|
/parallel \u5207\u6362\u5E76\u884C\u5B50 Agent \u529F\u80FD on/off
|
|
4104
3917
|
/todo \u5207\u6362 todo \u8BA1\u5212\u529F\u80FD on/off
|
|
4105
3918
|
/clear \u6E05\u7A7A\u5BF9\u8BDD\u5386\u53F2
|
|
@@ -4107,33 +3920,33 @@ function handleSlashCommand(input, context) {
|
|
|
4107
3920
|
/exit \u9000\u51FA
|
|
4108
3921
|
`);
|
|
4109
3922
|
return true;
|
|
4110
|
-
case "/
|
|
4111
|
-
const
|
|
4112
|
-
if (
|
|
4113
|
-
printInfo(
|
|
4114
|
-
printInfo("\u53EF\u9009: delegated, autonomous, controlled");
|
|
4115
|
-
return true;
|
|
4116
|
-
}
|
|
4117
|
-
if (["delegated", "autonomous", "controlled"].includes(mode)) {
|
|
4118
|
-
context.config.collaboration = mode;
|
|
4119
|
-
context.setMode?.(mode);
|
|
4120
|
-
printSuccess(`\u5DF2\u5207\u6362\u5230 ${mode} \u6A21\u5F0F`);
|
|
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");
|
|
4121
3927
|
} else {
|
|
4122
|
-
|
|
3928
|
+
printInfo(`\u53EF\u7528\u6280\u80FD (${skills.length}):`);
|
|
3929
|
+
for (const s of skills) {
|
|
3930
|
+
printInfo(` /${s.name}: ${s.description}`);
|
|
3931
|
+
}
|
|
4123
3932
|
}
|
|
4124
3933
|
return true;
|
|
4125
3934
|
}
|
|
4126
|
-
case "/
|
|
4127
|
-
context.
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
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
|
+
}
|
|
4133
3945
|
return true;
|
|
3946
|
+
}
|
|
4134
3947
|
case "/clear":
|
|
4135
|
-
printSuccess("\u5BF9\u8BDD\u5386\u53F2\u5DF2\u6E05\u7A7A
|
|
4136
|
-
return
|
|
3948
|
+
printSuccess("\u5BF9\u8BDD\u5386\u53F2\u5DF2\u6E05\u7A7A");
|
|
3949
|
+
return "clear";
|
|
4137
3950
|
case "/parallel": {
|
|
4138
3951
|
const current = context.config.features.parallel_agents;
|
|
4139
3952
|
const next = current === "on" ? "off" : "on";
|
|
@@ -4142,7 +3955,7 @@ function handleSlashCommand(input, context) {
|
|
|
4142
3955
|
context.registry.unregister("spawn-agents");
|
|
4143
3956
|
} else if (!context.registry.has("spawn-agents")) {
|
|
4144
3957
|
context.registry.register(
|
|
4145
|
-
createSpawnAgentsTool(context.client, context.registry, context.config
|
|
3958
|
+
createSpawnAgentsTool(context.client, context.registry, context.config)
|
|
4146
3959
|
);
|
|
4147
3960
|
}
|
|
4148
3961
|
printSuccess(`\u5E76\u884C\u5B50 Agent \u529F\u80FD\u5DF2${next === "on" ? "\u5F00\u542F" : "\u5173\u95ED"}`);
|
|
@@ -4162,9 +3975,9 @@ function handleSlashCommand(input, context) {
|
|
|
4162
3975
|
}
|
|
4163
3976
|
case "/info":
|
|
4164
3977
|
printInfo(`\u6A21\u578B: ${context.config.model}`);
|
|
4165
|
-
printInfo(`Agent \u6A21\u5F0F: ${context.config.agent_mode}`);
|
|
4166
|
-
printInfo(`\u534F\u4F5C\u6A21\u5F0F: ${context.config.collaboration}`);
|
|
4167
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"}`);
|
|
4168
3981
|
return true;
|
|
4169
3982
|
case "/exit":
|
|
4170
3983
|
process.exit(0);
|
|
@@ -4175,7 +3988,11 @@ function handleSlashCommand(input, context) {
|
|
|
4175
3988
|
}
|
|
4176
3989
|
async function startRepl(options) {
|
|
4177
3990
|
const { config } = options;
|
|
4178
|
-
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());
|
|
4179
3996
|
const registry = new ToolRegistry();
|
|
4180
3997
|
registerCoreTools(registry);
|
|
4181
3998
|
registry.setPermissions(config.permissions);
|
|
@@ -4187,23 +4004,19 @@ async function startRepl(options) {
|
|
|
4187
4004
|
maxTokens: config.max_tokens
|
|
4188
4005
|
});
|
|
4189
4006
|
const todoStore = new TodoStore();
|
|
4190
|
-
const memoStore = new MemoStore();
|
|
4191
4007
|
if (config.features.parallel_agents === "on") {
|
|
4192
|
-
registry.register(createSpawnAgentsTool(client, registry, config
|
|
4008
|
+
registry.register(createSpawnAgentsTool(client, registry, config));
|
|
4193
4009
|
}
|
|
4194
4010
|
if (config.features.todo === "on") {
|
|
4195
4011
|
registry.register(createTodoTool(todoStore));
|
|
4196
4012
|
}
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
if (config.agent_mode === "single") {
|
|
4201
|
-
singleAgent = new Agent(client, registry, config, systemPrompt);
|
|
4202
|
-
} else {
|
|
4203
|
-
orchestrator = new Orchestrator(registry, config, systemPrompt, memoStore);
|
|
4013
|
+
const skillRegistry = new SkillRegistry();
|
|
4014
|
+
for (const skill of loadAllSkills()) {
|
|
4015
|
+
skillRegistry.register(skill);
|
|
4204
4016
|
}
|
|
4205
|
-
|
|
4206
|
-
|
|
4017
|
+
registry.register(createDispatchTool(client, registry, config, agentRegistry));
|
|
4018
|
+
let agent = new Agent(client, registry, config, systemPrompt);
|
|
4019
|
+
printWelcome(config.model);
|
|
4207
4020
|
const rl = readline.createInterface({
|
|
4208
4021
|
input: process.stdin,
|
|
4209
4022
|
output: process.stdout,
|
|
@@ -4211,13 +4024,13 @@ async function startRepl(options) {
|
|
|
4211
4024
|
historySize: 100
|
|
4212
4025
|
});
|
|
4213
4026
|
setConfirmHandler(async (promptText) => {
|
|
4214
|
-
return new Promise((
|
|
4027
|
+
return new Promise((resolve10) => {
|
|
4215
4028
|
process.stdout.write(promptText);
|
|
4216
4029
|
const onData = (data) => {
|
|
4217
4030
|
process.stdin.removeListener("data", onData);
|
|
4218
4031
|
process.stdin.pause();
|
|
4219
4032
|
const answer = data.toString().trim().toLowerCase();
|
|
4220
|
-
|
|
4033
|
+
resolve10(answer === "y");
|
|
4221
4034
|
};
|
|
4222
4035
|
process.stdin.resume();
|
|
4223
4036
|
process.stdin.once("data", onData);
|
|
@@ -4231,29 +4044,75 @@ async function startRepl(options) {
|
|
|
4231
4044
|
return;
|
|
4232
4045
|
}
|
|
4233
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
|
+
}
|
|
4234
4098
|
const handled = handleSlashCommand(input, {
|
|
4235
4099
|
config,
|
|
4236
|
-
orchestrator,
|
|
4237
4100
|
registry,
|
|
4238
4101
|
client,
|
|
4239
4102
|
todoStore,
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
orchestrator?.setMode(mode);
|
|
4243
|
-
}
|
|
4103
|
+
agentRegistry,
|
|
4104
|
+
skillRegistry
|
|
4244
4105
|
});
|
|
4106
|
+
if (handled === "clear") {
|
|
4107
|
+
agent = new Agent(client, registry, config, systemPrompt);
|
|
4108
|
+
rl.prompt();
|
|
4109
|
+
return;
|
|
4110
|
+
}
|
|
4245
4111
|
if (handled) {
|
|
4246
4112
|
rl.prompt();
|
|
4247
4113
|
return;
|
|
4248
4114
|
}
|
|
4249
4115
|
}
|
|
4250
|
-
if (config.agent_mode === "single" && !singleAgent) {
|
|
4251
|
-
singleAgent = new Agent(client, registry, config, systemPrompt);
|
|
4252
|
-
orchestrator = void 0;
|
|
4253
|
-
} else if (config.agent_mode === "dual" && !orchestrator) {
|
|
4254
|
-
orchestrator = new Orchestrator(registry, config, systemPrompt, memoStore);
|
|
4255
|
-
singleAgent = void 0;
|
|
4256
|
-
}
|
|
4257
4116
|
rl.pause();
|
|
4258
4117
|
let isStreaming = false;
|
|
4259
4118
|
const thinkFilter = createThinkFilter();
|
|
@@ -4277,16 +4136,6 @@ async function startRepl(options) {
|
|
|
4277
4136
|
onToolResult: (name, result, truncated) => {
|
|
4278
4137
|
printToolResult(name, result, truncated);
|
|
4279
4138
|
},
|
|
4280
|
-
onCoderStart: () => {
|
|
4281
|
-
if (isStreaming) {
|
|
4282
|
-
process.stdout.write("\n");
|
|
4283
|
-
isStreaming = false;
|
|
4284
|
-
}
|
|
4285
|
-
printInfo("\u7F16\u7801 Agent \u5F00\u59CB\u5DE5\u4F5C...");
|
|
4286
|
-
},
|
|
4287
|
-
onCoderEnd: () => {
|
|
4288
|
-
printInfo("\u7F16\u7801 Agent \u5B8C\u6210");
|
|
4289
|
-
},
|
|
4290
4139
|
onError: (err) => {
|
|
4291
4140
|
if (isStreaming) {
|
|
4292
4141
|
process.stdout.write("\n");
|
|
@@ -4296,11 +4145,7 @@ async function startRepl(options) {
|
|
|
4296
4145
|
}
|
|
4297
4146
|
};
|
|
4298
4147
|
try {
|
|
4299
|
-
|
|
4300
|
-
await singleAgent.run(input, callbacks);
|
|
4301
|
-
} else if (orchestrator) {
|
|
4302
|
-
await orchestrator.run(input, callbacks);
|
|
4303
|
-
}
|
|
4148
|
+
await agent.run(input, callbacks);
|
|
4304
4149
|
} catch (err) {
|
|
4305
4150
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4306
4151
|
printError(msg);
|
|
@@ -4320,13 +4165,18 @@ async function startRepl(options) {
|
|
|
4320
4165
|
|
|
4321
4166
|
// src/cli/index.ts
|
|
4322
4167
|
init_todo_store();
|
|
4323
|
-
|
|
4168
|
+
init_registry2();
|
|
4169
|
+
init_loader();
|
|
4324
4170
|
init_spawn_agents();
|
|
4325
4171
|
init_todo();
|
|
4326
|
-
|
|
4172
|
+
init_dispatch();
|
|
4327
4173
|
init_bridge();
|
|
4328
4174
|
async function runOnce(prompt, config) {
|
|
4329
|
-
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());
|
|
4330
4180
|
const registry = new ToolRegistry();
|
|
4331
4181
|
registerCoreTools(registry);
|
|
4332
4182
|
registry.setPermissions(config.permissions);
|
|
@@ -4338,14 +4188,13 @@ async function runOnce(prompt, config) {
|
|
|
4338
4188
|
maxTokens: config.max_tokens
|
|
4339
4189
|
});
|
|
4340
4190
|
const todoStore = new TodoStore();
|
|
4341
|
-
const memoStore = new MemoStore();
|
|
4342
4191
|
if (config.features.parallel_agents === "on") {
|
|
4343
|
-
registry.register(createSpawnAgentsTool(client, registry, config
|
|
4192
|
+
registry.register(createSpawnAgentsTool(client, registry, config));
|
|
4344
4193
|
}
|
|
4345
4194
|
if (config.features.todo === "on") {
|
|
4346
4195
|
registry.register(createTodoTool(todoStore));
|
|
4347
4196
|
}
|
|
4348
|
-
registry.register(
|
|
4197
|
+
registry.register(createDispatchTool(client, registry, config, agentRegistry));
|
|
4349
4198
|
let isStreaming = false;
|
|
4350
4199
|
const thinkFilter = createThinkFilter();
|
|
4351
4200
|
const callbacks = {
|
|
@@ -4367,28 +4216,13 @@ async function runOnce(prompt, config) {
|
|
|
4367
4216
|
onToolResult: (name, result, truncated) => {
|
|
4368
4217
|
printToolResult(name, result, truncated);
|
|
4369
4218
|
},
|
|
4370
|
-
onCoderStart: () => {
|
|
4371
|
-
if (isStreaming) {
|
|
4372
|
-
process.stdout.write("\n");
|
|
4373
|
-
isStreaming = false;
|
|
4374
|
-
}
|
|
4375
|
-
printInfo("\u7F16\u7801 Agent \u5F00\u59CB\u5DE5\u4F5C...");
|
|
4376
|
-
},
|
|
4377
|
-
onCoderEnd: () => {
|
|
4378
|
-
printInfo("\u7F16\u7801 Agent \u5B8C\u6210");
|
|
4379
|
-
},
|
|
4380
4219
|
onError: (err) => {
|
|
4381
4220
|
printError(err.message);
|
|
4382
4221
|
}
|
|
4383
4222
|
};
|
|
4384
4223
|
try {
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
await agent.run(prompt, callbacks);
|
|
4388
|
-
} else {
|
|
4389
|
-
const orchestrator = new Orchestrator(registry, config, systemPrompt, memoStore);
|
|
4390
|
-
await orchestrator.run(prompt, callbacks);
|
|
4391
|
-
}
|
|
4224
|
+
const agent = new Agent(client, registry, config, systemPrompt);
|
|
4225
|
+
await agent.run(prompt, callbacks);
|
|
4392
4226
|
} catch (err) {
|
|
4393
4227
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4394
4228
|
printError(msg);
|
|
@@ -4400,7 +4234,7 @@ async function runOnce(prompt, config) {
|
|
|
4400
4234
|
}
|
|
4401
4235
|
function createCli() {
|
|
4402
4236
|
const program2 = new Command();
|
|
4403
|
-
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("--
|
|
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) => {
|
|
4404
4238
|
const config = loadConfig(opts);
|
|
4405
4239
|
if (!config.api_key) {
|
|
4406
4240
|
printError("\u672A\u8BBE\u7F6E API \u5BC6\u94A5\u3002\u8BF7\u901A\u8FC7\u4EE5\u4E0B\u65B9\u5F0F\u4E4B\u4E00\u8BBE\u7F6E\uFF1A");
|