@noobdemon/noob-cli 1.12.6 → 1.12.8
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/CHANGELOG.md +15 -1
- package/package.json +2 -2
- package/src/agent.js +9 -7
- package/src/models.js +4 -75
- package/src/repl.js +13 -42
- package/src/tokens.js +6 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
Tất cả thay đổi đáng kể của `@noobdemon/noob-cli` được ghi vào file này.
|
|
4
4
|
|
|
5
|
-
## [1.12.
|
|
5
|
+
## [1.12.8] - 2026-06-13
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Claude Opus 4.8** (`gateway-claude-opus-4-8`, provider `anthropic`, tier `flagship`) — set làm `DEFAULT_MODEL` mới.
|
|
9
|
+
|
|
10
|
+
### Removed
|
|
11
|
+
- **Rút catalog xuống 3 flagship** (`src/models.js`): chỉ còn `gateway-claude-opus-4-8`, `gateway-gpt-5-5`, `gateway-deepseek-v4-pro`. Gỡ toàn bộ 32 model cũ: o3/o3-mini/o4-mini/DeepSeek R1/Qwen QwQ (reasoning), GPT-5 Mini/Nano + Gemini Flash + DeepSeek V4 Flash + GPT-4.1 Mini/Nano (fast), Gemini 2.5/3/3.1 Pro, Grok 3/4, Qwen 3 Max, Kimi K2, Llama 3.3 70B, Claude Sonnet 4/4.6, Opus 4.1/4.5/4.6/4.7, GPT-5/5.1/5.3/5.4/4o/5 Online. `PROVIDERS` map còn 3 entry (`openai`/`anthropic`/`deepseek`).
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- **Worker `claude-code-proxy`** (`worker/src/worker.js`): `mapModel` map "opus/claude/sonnet/haiku" → `opus-4-8`, "gpt/o3/o4" → `gpt-5-5`, "deepseek" → `deepseek-v4-pro`; fallback default → `opus-4-8`. `modelsList()` chỉ còn 3 id mới. Đã deploy (version `689b94d1`).
|
|
15
|
+
|
|
16
|
+
## [1.12.7] - 2026-06-12
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- **Gỡ auto-compact, chuyển sang MANUAL only** (`src/repl.js` + `src/tokens.js` + `src/agent.js`): trước đây CLI tự gọi `maybeSummarize({force:true})` khi context đạt 75% — gián đoạn workflow giữa chừng và summary có thể mất chi tiết user cần. Giờ user toàn quyền quyết định khi nào tóm tắt bằng `/compact`. Chỉ còn 2 mốc CẢNH BÁO (không auto-action): **60% (120k tokens)** nhắc nhẹ một lần, **80% (160k tokens)** cảnh báo mạnh gợi ý gõ `/compact` trước khi provider reject ở ~200k. Đồng thời sửa bug `CONTEXT_WINDOW=2_000_000` → `200_000` (khớp Claude Opus 4.7 + GPT-4o); ngưỡng cũ 2M khiến 75% = 1.5M token không bao giờ chạm → user báo `/compact không hoạt động`. `SUMMARIZE_THRESHOLD_CHARS` 6M → 600k, `MAX_PROMPT_CHARS` 1.2M → 800k, `keepTail` 16/24 → 12/16 cho khớp window thực.
|
|
6
20
|
|
|
7
21
|
### Added
|
|
8
22
|
- **Tool `write_todos`** (`src/repl/agent-dispatch.js` + `src/tools.js` + `src/agent.js`): tool ẢO để model declare structured todo list thay vì viết markdown `- [ ]`. Shape `{todos: [{text, done}]}` — REPLACE toàn bộ list mỗi lần gọi (no patch). Dispatcher intercept TRƯỚC `execTool`: set `state.todos` + `tui.setTodos` trực tiếp, set flag `state._todosFromTool=true` để `repl.js` skip parse markdown sau turn (tránh overwrite structured state). In compact box lần đầu, diff (chỉ dòng đổi) các lần sau. SYSTEM prompt rule TODO-BASED EXECUTION đã update: model PHẢI dùng `write_todos`, không viết markdown. Lý do: parser markdown cũ (`parseTodosFromHistory`) fragile khi model format sai (sai indent, dùng `*` thay `-`, thiếu space). Structured tool call → CLI render luôn đúng, progress bar trên status line cập nhật ngay. Stub trong `TOOLS.write_todos` làm fail-safe nếu lỡ qua `runTool` trực tiếp. Smoke `scripts/smoke-write-todos.mjs` 27/27 pass + regression `smoke-dispatch.mjs` 23/23 pass.
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noobdemon/noob-cli",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.8",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
|
-
"description": "Trợ lý lập trình agentic trong terminal (kiểu Claude Code), tiếng Việt, dùng sức mạnh Noob Demon —
|
|
7
|
+
"description": "Trợ lý lập trình agentic trong terminal (kiểu Claude Code), tiếng Việt, dùng sức mạnh Noob Demon — 3 mô hình flagship (Claude Opus 4.8, GPT-5.5, DeepSeek V4 Pro).",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"bin": {
|
|
10
10
|
"noob": "bin/noob.js"
|
package/src/agent.js
CHANGED
|
@@ -146,10 +146,12 @@ const MAX_STEPS = 10000;
|
|
|
146
146
|
// loop detection cũ bằng cách xen kẽ 2-3 tool call khác nhau.
|
|
147
147
|
const LOOP_DETECT_WINDOW = 6;
|
|
148
148
|
const LOOP_DETECT_THRESHOLD = 2;
|
|
149
|
-
const MAX_PROMPT_CHARS =
|
|
149
|
+
const MAX_PROMPT_CHARS = 800000; // ~200k tokens (ngang CONTEXT_WINDOW) — compact() là safety net cuối, repl.js auto-compact ở 75% (150k token) chạy trước.
|
|
150
150
|
// Khi history vượt ngưỡng này, gọi model phụ tóm tắt các lượt cũ thay vì cắt cụt
|
|
151
151
|
// → giữ được "trí nhớ dài hạn" trong phiên mà không nổ context.
|
|
152
|
-
|
|
152
|
+
// 600k chars ≈ 150k tokens = trùng ngưỡng auto-compact 75% của repl.js. Khi
|
|
153
|
+
// /compact thủ công hoặc auto-compact gọi với force=true thì ngưỡng này bị bypass.
|
|
154
|
+
const SUMMARIZE_THRESHOLD_CHARS = 600000;
|
|
153
155
|
|
|
154
156
|
// HARD GOAL block (do /goal <text> set): chèn ngay sau memoryBlock, attention
|
|
155
157
|
// cao. Mục đích — chống 3 failure mode bài "dynamic workflows" của Anthropic
|
|
@@ -262,11 +264,11 @@ export async function maybeSummarize(history, { model, signal, force = false } =
|
|
|
262
264
|
const totalChars = history.reduce((s, m) => s + (m.content?.length || 0), 0);
|
|
263
265
|
if (!force && totalChars < SUMMARIZE_THRESHOLD_CHARS) return false;
|
|
264
266
|
// Giữ tail nguyên vẹn; tóm tắt phần trước.
|
|
265
|
-
// Với CONTEXT_WINDOW =
|
|
266
|
-
// gần nhất (
|
|
267
|
-
// force (gọi từ /compact hoặc auto-compact 75%): giữ
|
|
268
|
-
// non-force: giữ
|
|
269
|
-
const keepTail = force ?
|
|
267
|
+
// Với CONTEXT_WINDOW = 200k tokens, tail cần đủ để giữ vài lượt tool result
|
|
268
|
+
// gần nhất (chuỗi edit_file + run_command đang dở).
|
|
269
|
+
// force (gọi từ /compact hoặc auto-compact 75%): giữ 12 tail.
|
|
270
|
+
// non-force: giữ 16 tail (rộng tay hơn vì phiên dài mới trigger).
|
|
271
|
+
const keepTail = force ? 12 : 16;
|
|
270
272
|
if (history.length <= keepTail + 2) return false;
|
|
271
273
|
// Nếu lượt đầu đã là summary (role=system, name=summary) → tóm tắt thêm.
|
|
272
274
|
const head = history.slice(0, history.length - keepTail);
|
package/src/models.js
CHANGED
|
@@ -1,98 +1,27 @@
|
|
|
1
1
|
// Model catalog supported by the Noob Demon gateway.
|
|
2
2
|
export const MODELS = [
|
|
3
|
-
{ id: 'gateway-gpt-5', name: 'GPT-5', provider: 'openai', tier: 'flagship' },
|
|
4
|
-
{ id: 'gateway-gpt-5-1', name: 'GPT-5.1', provider: 'openai', tier: 'flagship' },
|
|
5
|
-
{ id: 'gateway-gpt-5-3', name: 'GPT-5.3', provider: 'openai', tier: 'flagship' },
|
|
6
|
-
{ id: 'gateway-gpt-5-4', name: 'GPT-5.4', provider: 'openai', tier: 'flagship' },
|
|
7
|
-
{ id: 'gateway-gpt-5-5', name: 'GPT-5.5', provider: 'openai', tier: 'flagship' },
|
|
8
|
-
{ id: 'gateway-gpt-o3', name: 'o3', provider: 'openai', tier: 'reasoning' },
|
|
9
|
-
{ id: 'gateway-gpt-o3-mini', name: 'o3 Mini', provider: 'openai', tier: 'reasoning' },
|
|
10
|
-
{ id: 'gateway-gpt-o4-mini', name: 'o4-mini', provider: 'openai', tier: 'reasoning' },
|
|
11
|
-
{ id: 'gateway-gpt-4o', name: 'GPT-4o', provider: 'openai', tier: 'standard' },
|
|
12
|
-
{ id: 'gateway-gpt-4-1-mini', name: 'GPT-4.1 Mini', provider: 'openai', tier: 'fast' },
|
|
13
|
-
{ id: 'gateway-gpt-4-1-nano', name: 'GPT-4.1 Nano', provider: 'openai', tier: 'fast' },
|
|
14
|
-
{ id: 'gateway-gpt-5-mini', name: 'GPT-5 Mini', provider: 'openai', tier: 'fast' },
|
|
15
|
-
{ id: 'gateway-gpt-5-nano', name: 'GPT-5 Nano', provider: 'openai', tier: 'fast' },
|
|
16
|
-
{ id: 'gateway-gpt-5-online', name: 'GPT-5 Online', provider: 'openai', tier: 'standard' },
|
|
17
|
-
{
|
|
18
|
-
id: 'gateway-claude-opus-4-7',
|
|
19
|
-
name: 'Claude Opus 4.7',
|
|
20
|
-
provider: 'anthropic',
|
|
21
|
-
tier: 'flagship',
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
id: 'gateway-claude-opus-4-6',
|
|
25
|
-
name: 'Claude Opus 4.6',
|
|
26
|
-
provider: 'anthropic',
|
|
27
|
-
tier: 'flagship',
|
|
28
|
-
},
|
|
29
3
|
{
|
|
30
|
-
id: 'gateway-claude-opus-4-
|
|
31
|
-
name: 'Claude Opus 4.
|
|
4
|
+
id: 'gateway-claude-opus-4-8',
|
|
5
|
+
name: 'Claude Opus 4.8',
|
|
32
6
|
provider: 'anthropic',
|
|
33
7
|
tier: 'flagship',
|
|
34
8
|
},
|
|
35
|
-
{
|
|
36
|
-
id: 'gateway-claude-opus-4-1',
|
|
37
|
-
name: 'Claude Opus 4.1',
|
|
38
|
-
provider: 'anthropic',
|
|
39
|
-
tier: 'standard',
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
id: 'gateway-claude-sonnet-4',
|
|
43
|
-
name: 'Claude Sonnet 4',
|
|
44
|
-
provider: 'anthropic',
|
|
45
|
-
tier: 'standard',
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
id: 'gateway-claude-sonnet-4-6',
|
|
49
|
-
name: 'Claude Sonnet 4.6',
|
|
50
|
-
provider: 'anthropic',
|
|
51
|
-
tier: 'standard',
|
|
52
|
-
},
|
|
53
|
-
{ id: 'gateway-google-2.5-pro', name: 'Gemini 2.5 Pro', provider: 'google', tier: 'flagship' },
|
|
54
|
-
{ id: 'gateway-gemini-3-pro', name: 'Gemini 3 Pro', provider: 'google', tier: 'flagship' },
|
|
55
|
-
{ id: 'gateway-gemini-3-1-pro', name: 'Gemini 3.1 Pro', provider: 'google', tier: 'flagship' },
|
|
56
|
-
{ id: 'gateway-gemini-2.5-flash', name: 'Gemini 2.5 Flash', provider: 'google', tier: 'fast' },
|
|
9
|
+
{ id: 'gateway-gpt-5-5', name: 'GPT-5.5', provider: 'openai', tier: 'flagship' },
|
|
57
10
|
{
|
|
58
11
|
id: 'gateway-deepseek-v4-pro',
|
|
59
12
|
name: 'DeepSeek V4 Pro',
|
|
60
13
|
provider: 'deepseek',
|
|
61
14
|
tier: 'flagship',
|
|
62
15
|
},
|
|
63
|
-
{
|
|
64
|
-
id: 'gateway-deepseek-v4-flash',
|
|
65
|
-
name: 'DeepSeek V4 Flash',
|
|
66
|
-
provider: 'deepseek',
|
|
67
|
-
tier: 'fast',
|
|
68
|
-
},
|
|
69
|
-
{ id: 'gateway-deepseek-r1', name: 'DeepSeek R1', provider: 'deepseek', tier: 'reasoning' },
|
|
70
|
-
{ id: 'gateway-deepseek-v3', name: 'DeepSeek V3', provider: 'deepseek', tier: 'standard' },
|
|
71
|
-
{ id: 'gateway-grok-4', name: 'Grok 4', provider: 'xai', tier: 'flagship' },
|
|
72
|
-
{ id: 'gateway-grok-3', name: 'Grok 3', provider: 'xai', tier: 'standard' },
|
|
73
|
-
{ id: 'gateway-qwen-3-max', name: 'Qwen 3 Max', provider: 'alibaba', tier: 'standard' },
|
|
74
|
-
{ id: 'gateway-qwen-qwq-32b', name: 'Qwen QwQ 32B', provider: 'alibaba', tier: 'reasoning' },
|
|
75
|
-
{ id: 'gateway-deepinfra-kimi-k2', name: 'Kimi K2', provider: 'moonshot', tier: 'standard' },
|
|
76
|
-
{
|
|
77
|
-
id: 'gateway-llama-3-3-70b-versatile',
|
|
78
|
-
name: 'Llama 3.3 70B',
|
|
79
|
-
provider: 'meta',
|
|
80
|
-
tier: 'standard',
|
|
81
|
-
},
|
|
82
16
|
];
|
|
83
17
|
|
|
84
18
|
export const PROVIDERS = {
|
|
85
19
|
openai: { name: 'OpenAI', color: '#10a37f' },
|
|
86
20
|
anthropic: { name: 'Anthropic', color: '#d97706' },
|
|
87
|
-
google: { name: 'Google', color: '#3b82f6' },
|
|
88
21
|
deepseek: { name: 'DeepSeek', color: '#06b6d4' },
|
|
89
|
-
xai: { name: 'xAI', color: '#ef4444' },
|
|
90
|
-
alibaba: { name: 'Alibaba', color: '#8b5cf6' },
|
|
91
|
-
moonshot: { name: 'Moonshot', color: '#ec4899' },
|
|
92
|
-
meta: { name: 'Meta', color: '#6366f1' },
|
|
93
22
|
};
|
|
94
23
|
|
|
95
|
-
export const DEFAULT_MODEL = 'gateway-claude-opus-4-
|
|
24
|
+
export const DEFAULT_MODEL = 'gateway-claude-opus-4-8';
|
|
96
25
|
|
|
97
26
|
export function findModel(id) {
|
|
98
27
|
if (!id || typeof id !== 'string') return undefined;
|
package/src/repl.js
CHANGED
|
@@ -1481,53 +1481,24 @@ NGUYÊN TẮC:
|
|
|
1481
1481
|
// Reset turn-scoped auto-approve — chỉ áp dụng trong runAgent vừa rồi.
|
|
1482
1482
|
// (autoApprove + autoApproveFile vẫn giữ nguyên cho phiên.)
|
|
1483
1483
|
state.autoApproveTurn.clear();
|
|
1484
|
-
//
|
|
1485
|
-
//
|
|
1486
|
-
//
|
|
1487
|
-
//
|
|
1488
|
-
//
|
|
1489
|
-
//
|
|
1490
|
-
//
|
|
1484
|
+
// [2026-06-12] GỠ AUTO-COMPACT — user kiểm soát compact thủ công bằng /compact.
|
|
1485
|
+
// Lý do: auto-compact gián đoạn workflow giữa chừng, summary có thể mất chi
|
|
1486
|
+
// tiết user cần. Giữ 2 mốc CẢNH BÁO (60% / 80%) để user biết khi nào nên
|
|
1487
|
+
// chạy /compact, nhưng KHÔNG tự động chạy nữa.
|
|
1488
|
+
// Với CONTEXT_WINDOW = 200k tokens:
|
|
1489
|
+
// 60% (120k) → nhắc nhẹ một lần
|
|
1490
|
+
// 80% (160k) → cảnh báo mạnh — nên /compact ngay trước khi provider reject
|
|
1491
1491
|
try {
|
|
1492
1492
|
const totalTokens = countMessages(state.history);
|
|
1493
1493
|
const k = Math.round(totalTokens / 1000);
|
|
1494
1494
|
const pct = Math.round((totalTokens / CONTEXT_WINDOW) * 100);
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
console.log(c.accent(` ⚡ ${t.autoCompactTrigger(k)} (${pct}% context)`));
|
|
1499
|
-
tui.setBusy(true, t.compactRunning);
|
|
1500
|
-
try {
|
|
1501
|
-
const ok = await maybeSummarize(state.history, { model: state.model, force: true });
|
|
1502
|
-
tui.setBusy(false);
|
|
1503
|
-
if (ok) {
|
|
1504
|
-
const afterTokens = countMessages(state.history);
|
|
1505
|
-
const aK = Math.round(afterTokens / 1000);
|
|
1506
|
-
const saved =
|
|
1507
|
-
totalTokens > 0 ? Math.round(((totalTokens - afterTokens) / totalTokens) * 100) : 0;
|
|
1508
|
-
console.log(
|
|
1509
|
-
c.ok(
|
|
1510
|
-
` ${t.autoCompactDone(k, aK, saved)} (${Math.round((afterTokens / CONTEXT_WINDOW) * 100)}% context)`
|
|
1511
|
-
)
|
|
1512
|
-
);
|
|
1513
|
-
state._longSessionWarned = false;
|
|
1514
|
-
persist();
|
|
1515
|
-
} else {
|
|
1516
|
-
console.log(c.err(' ' + t.autoCompactFail));
|
|
1517
|
-
}
|
|
1518
|
-
} catch (e) {
|
|
1519
|
-
tui.setBusy(false);
|
|
1520
|
-
console.log(c.err(' ' + t.autoCompactFail));
|
|
1521
|
-
} finally {
|
|
1522
|
-
state._autoCompacting = false;
|
|
1523
|
-
}
|
|
1524
|
-
} else if (totalTokens >= CONTEXT_WINDOW * 0.6) {
|
|
1525
|
-
// Mốc 2 (≥60% — 1.2M tokens): cảnh báo mạnh.
|
|
1526
|
-
console.log(c.err(` ⚠ ${t.veryLongSession(k)} (${pct}% context)`));
|
|
1495
|
+
if (totalTokens >= CONTEXT_WINDOW * 0.8) {
|
|
1496
|
+
// Mốc 2 (≥80% — 160k tokens): cảnh báo mạnh, gợi ý /compact ngay.
|
|
1497
|
+
console.log(c.err(` ⚠ ${t.veryLongSession(k)} (${pct}% context) — gõ /compact để tóm tắt, tránh provider reject ở ~200k.`));
|
|
1527
1498
|
state._longSessionWarned = true;
|
|
1528
|
-
} else if (totalTokens >= CONTEXT_WINDOW * 0.
|
|
1529
|
-
// Mốc 1 (≥
|
|
1530
|
-
console.log(c.dim(` ⓘ ${t.longSession(k)} (${pct}% context)
|
|
1499
|
+
} else if (totalTokens >= CONTEXT_WINDOW * 0.6 && !state._longSessionWarned) {
|
|
1500
|
+
// Mốc 1 (≥60% — 120k tokens): nhắc nhẹ một lần.
|
|
1501
|
+
console.log(c.dim(` ⓘ ${t.longSession(k)} (${pct}% context) — cân nhắc /compact nếu phiên còn dài.`));
|
|
1531
1502
|
state._longSessionWarned = true;
|
|
1532
1503
|
}
|
|
1533
1504
|
} catch {}
|
package/src/tokens.js
CHANGED
|
@@ -57,8 +57,12 @@ export function countMessages(messages = []) {
|
|
|
57
57
|
// window đủ rộng (256 chars) để qua mọi ranh giới token thực tế của cl100k/o200k
|
|
58
58
|
// (token dài nhất ~ vài chục byte).
|
|
59
59
|
const TAIL_WINDOW = 256;
|
|
60
|
-
// Context window tối đa của model
|
|
61
|
-
|
|
60
|
+
// Context window tối đa của model. Đặt 200k tokens — match Claude 3.5/Opus 4,
|
|
61
|
+
// GPT-4o, và an toàn cho mọi model phổ biến qua gateway (Gemini 1M, DeepSeek
|
|
62
|
+
// 128k, Grok 128k...). Đặt cao hơn 200k là vô nghĩa: provider sẽ reject prompt
|
|
63
|
+
// TRƯỚC khi auto-compact của repl.js có cơ hội trigger → user thấy 'compact
|
|
64
|
+
// không hoạt động' dù logic compact vẫn đúng.
|
|
65
|
+
export const CONTEXT_WINDOW = 200_000;
|
|
62
66
|
|
|
63
67
|
export class TokenMeter {
|
|
64
68
|
constructor() {
|