@noobdemon/noob-cli 1.6.0 → 1.7.0
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/package.json +1 -1
- package/src/repl.js +56 -6
- package/src/subagent.js +9 -4
package/package.json
CHANGED
package/src/repl.js
CHANGED
|
@@ -30,6 +30,8 @@ const SLASH = [
|
|
|
30
30
|
{ name: "/init", desc: "quét dự án & tạo noob.md" },
|
|
31
31
|
{ name: "/karpathy", desc: "rà soát code (Karpathy)" },
|
|
32
32
|
{ name: "/ultra", desc: "tự hành: tự nghĩ & làm nhiệm vụ" },
|
|
33
|
+
{ name: "/agent", desc: "bật/tắt agent mode (spawn sub-agent)" },
|
|
34
|
+
{ name: "/tokens", desc: "xem số token đã dùng phiên này" },
|
|
33
35
|
{ name: "/learn", desc: "chưng cất bài học vào noob.md" },
|
|
34
36
|
{ name: "/memory", desc: "xem bộ nhớ noob.md" },
|
|
35
37
|
{ name: "/login", desc: "đăng nhập bằng API key" },
|
|
@@ -129,7 +131,9 @@ export async function startRepl(opts = {}) {
|
|
|
129
131
|
autoApprove: new Set(),
|
|
130
132
|
yolo: !!opts.yolo || config.yoloDefault, // cờ --yolo HOẶC mặc định đã lưu (/auto-yolo)
|
|
131
133
|
ultra: false, // chế độ tự hành (self-quest) đang chạy?
|
|
134
|
+
agentMode: false, // /agent on → cho phép spawn_agent / spawn_agents
|
|
132
135
|
};
|
|
136
|
+
const tokenMeter = new TokenMeter();
|
|
133
137
|
|
|
134
138
|
// Prompt = dòng trạng thái sống. Luôn phản ánh yolo + version theo thời gian
|
|
135
139
|
// thực (vẽ lại mỗi lượt và ngay khi Shift+Tab), nên không cần gõ /status.
|
|
@@ -634,10 +638,49 @@ NGUYÊN TẮC:
|
|
|
634
638
|
startSpin(t.thinking);
|
|
635
639
|
let printer = null;
|
|
636
640
|
|
|
641
|
+
const dispatchTool = async (name, input, depth = 0) => {
|
|
642
|
+
// spawn_agent / spawn_agents chỉ được phép khi agentMode bật; depth giới hạn
|
|
643
|
+
// bởi MAX_SUBAGENT_DEPTH để tránh đệ quy nổ.
|
|
644
|
+
if (name === "spawn_agent" || name === "spawn_agents") {
|
|
645
|
+
if (!state.agentMode)
|
|
646
|
+
return { allow: true, result: "ERROR: agent mode đang TẮT — gõ /agent on để bật trước khi spawn." };
|
|
647
|
+
if (depth >= MAX_SUBAGENT_DEPTH)
|
|
648
|
+
return { allow: true, result: `ERROR: đã đạt depth tối đa (${MAX_SUBAGENT_DEPTH}) — không spawn thêm.` };
|
|
649
|
+
const tasks = name === "spawn_agent" ? [input] : (Array.isArray(input?.agents) ? input.agents : []);
|
|
650
|
+
if (!tasks.length) return { allow: true, result: "ERROR: thiếu task cho sub-agent." };
|
|
651
|
+
stopSpin();
|
|
652
|
+
console.log(chalk.hex("#8b5cf6")(` ⊕ spawn ${tasks.length} sub-agent (depth ${depth + 1}/${MAX_SUBAGENT_DEPTH})`));
|
|
653
|
+
startSpin(t.thinking);
|
|
654
|
+
try {
|
|
655
|
+
const results = await Promise.all(tasks.map((task, i) =>
|
|
656
|
+
runSubAgent({
|
|
657
|
+
task: task?.task || task?.prompt || "",
|
|
658
|
+
context: task?.context || "",
|
|
659
|
+
depth: depth + 1,
|
|
660
|
+
model: state.model.id,
|
|
661
|
+
signal: abort.signal,
|
|
662
|
+
tokenMeter,
|
|
663
|
+
dispatchTool: (n, inp) => dispatchTool(n, inp, depth + 1),
|
|
664
|
+
onLog: (msg) => { stopSpin(); console.log(chalk.hex("#8b5cf6")(" " + msg)); startSpin(t.thinking); },
|
|
665
|
+
}).then((r) => `── sub-agent #${i + 1} ──\n${r}`).catch((e) => `── sub-agent #${i + 1} (LỖI) ──\n${e?.message || String(e)}`)
|
|
666
|
+
));
|
|
667
|
+
return { allow: true, result: results.join("\n\n") };
|
|
668
|
+
} catch (err) {
|
|
669
|
+
return { allow: true, result: "ERROR sub-agent: " + (err?.message || String(err)) };
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
stopSpin();
|
|
673
|
+
const res = await execTool(name, input);
|
|
674
|
+
startSpin(t.thinking);
|
|
675
|
+
return res;
|
|
676
|
+
};
|
|
677
|
+
|
|
637
678
|
const answer = await runAgent({
|
|
638
679
|
history: state.history,
|
|
639
680
|
model: state.model.id,
|
|
640
681
|
signal: abort.signal,
|
|
682
|
+
tokenMeter,
|
|
683
|
+
extraToolsDoc: state.agentMode ? spawnAgentToolsDoc(0) : "",
|
|
641
684
|
onStatus: () => tick(t.thinking),
|
|
642
685
|
onSteer: () => {
|
|
643
686
|
if (!pending.length) return [];
|
|
@@ -659,12 +702,7 @@ NGUYÊN TẮC:
|
|
|
659
702
|
printer?.flush();
|
|
660
703
|
}
|
|
661
704
|
},
|
|
662
|
-
onTool:
|
|
663
|
-
stopSpin();
|
|
664
|
-
const res = await execTool(name, input);
|
|
665
|
-
startSpin(t.thinking);
|
|
666
|
-
return res;
|
|
667
|
-
},
|
|
705
|
+
onTool: (name, input) => dispatchTool(name, input, 0),
|
|
668
706
|
});
|
|
669
707
|
|
|
670
708
|
stopSpin();
|
|
@@ -773,6 +811,18 @@ NGUYÊN TẮC:
|
|
|
773
811
|
state.yolo = !state.yolo;
|
|
774
812
|
console.log((state.yolo ? c.err : c.ok)(" " + (state.yolo ? t.yoloOn : t.yoloOff)));
|
|
775
813
|
break;
|
|
814
|
+
case "agent": {
|
|
815
|
+
const v = arg.toLowerCase();
|
|
816
|
+
if (v === "on" || v === "bật" || v === "bat") state.agentMode = true;
|
|
817
|
+
else if (v === "off" || v === "tắt" || v === "tat") state.agentMode = false;
|
|
818
|
+
else state.agentMode = !state.agentMode;
|
|
819
|
+
console.log((state.agentMode ? c.accent : c.dim)(" agent mode: " + (state.agentMode ? "BẬT (spawn_agent / spawn_agents khả dụng, depth tối đa " + MAX_SUBAGENT_DEPTH + ")" : "tắt")));
|
|
820
|
+
break;
|
|
821
|
+
}
|
|
822
|
+
case "tokens": {
|
|
823
|
+
console.log(c.dim(` tokens — input: ${tokenMeter.input.toLocaleString("vi-VN")} · output: ${tokenMeter.output.toLocaleString("vi-VN")} · tổng: ${tokenMeter.total.toLocaleString("vi-VN")} · ${tokenMeter.format()}`));
|
|
824
|
+
break;
|
|
825
|
+
}
|
|
776
826
|
case "auto-yolo":
|
|
777
827
|
case "autoyolo":
|
|
778
828
|
await toggleAutoYolo();
|
package/src/subagent.js
CHANGED
|
@@ -31,14 +31,17 @@ Ví dụ phân cấp: cha giao "build full app" → đẻ 1 sub-agent "build bac
|
|
|
31
31
|
|
|
32
32
|
// Chạy một sub-agent. dispatchTool: hàm để thực thi tool con (chia sẻ với cha).
|
|
33
33
|
// model: dùng chung model của cha. onLog: callback để log tiến độ ra UI cha.
|
|
34
|
-
export async function runSubAgent({ task, context, model, signal, dispatchTool, depth = 1, onLog }) {
|
|
34
|
+
export async function runSubAgent({ task, context, model, signal, dispatchTool, depth = 1, onLog, tokenMeter }) {
|
|
35
35
|
const sys = `Bạn là SUB-AGENT (depth=${depth}) được agent cha ủy thác MỘT nhiệm vụ cụ thể. Làm xong → trả lời NGẮN GỌN bằng Markdown tóm tắt KẾT QUẢ (file đã đụng, phát hiện, lỗi nếu có). Không tán gẫu. Không hỏi lại cha — tự quyết với thông tin được cấp.
|
|
36
36
|
|
|
37
37
|
# NHIỆM VỤ
|
|
38
38
|
${task}
|
|
39
39
|
${context ? `\n# NGỮ CẢNH TỪ CHA\n${context}` : ""}`;
|
|
40
40
|
const history = [{ role: "user", content: sys }];
|
|
41
|
-
|
|
41
|
+
// Dùng chung meter của cha nếu được truyền vào → token sub-agent cộng dồn
|
|
42
|
+
// vào tổng phiên. Nếu không có thì tự tạo cục bộ (giữ tương thích cũ).
|
|
43
|
+
const meter = tokenMeter || new TokenMeter();
|
|
44
|
+
const before = { input: meter.input, output: meter.output };
|
|
42
45
|
onLog?.(`↳ sub-agent (depth=${depth}) bắt đầu: ${task.slice(0, 80)}${task.length > 80 ? "…" : ""}`);
|
|
43
46
|
const result = await runAgent({
|
|
44
47
|
history,
|
|
@@ -51,6 +54,8 @@ ${context ? `\n# NGỮ CẢNH TỪ CHA\n${context}` : ""}`;
|
|
|
51
54
|
onDelta: () => {},
|
|
52
55
|
onSteer: () => [],
|
|
53
56
|
});
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
const used = { input: meter.input - before.input, output: meter.output - before.output };
|
|
58
|
+
onLog?.(`↳ sub-agent (depth=${depth}) xong (↑${used.input} ↓${used.output})`);
|
|
59
|
+
// Trả về string sạch để cha (model) đọc dễ. Token đã cộng vào meter rồi.
|
|
60
|
+
return result;
|
|
56
61
|
}
|