@noobdemon/noob-cli 1.7.6 → 1.7.7
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/agent.js +22 -8
- package/src/tui.js +11 -5
package/package.json
CHANGED
package/src/agent.js
CHANGED
|
@@ -123,14 +123,18 @@ function runtimeContext() {
|
|
|
123
123
|
// Lược ngữ cảnh để không vượt context khi phiên dài. KHÔNG đụng vào history thật
|
|
124
124
|
// (vẫn lưu/đầy đủ để resume) — chỉ thu gọn BẢN SAO dùng cho prompt.
|
|
125
125
|
// Nếu history đã có summary (do summarizeHistory ghi vào _summary), dùng làm head.
|
|
126
|
-
|
|
126
|
+
// Exported cho test (scripts/test-compact.js) — không dùng ngoài file này.
|
|
127
|
+
export function compact(history, budget) {
|
|
127
128
|
const len = (m) => (m.content || "").length + 24;
|
|
128
129
|
let total = history.reduce((s, m) => s + len(m), 0);
|
|
129
130
|
if (total <= budget) return history;
|
|
130
131
|
const out = history.map((m) => ({ ...m }));
|
|
131
132
|
// (1) Rút gọn các TOOL RESULT cũ trước (giữ 5 cái gần nhất) — đây là phần phình
|
|
132
133
|
// nhất (đọc file lớn) và model đã xử lý xong rồi.
|
|
133
|
-
|
|
134
|
+
// SKIP session_summary — đó là bộ nhớ dài hạn, không phải tool result cồng kềnh.
|
|
135
|
+
const toolIdx = out
|
|
136
|
+
.map((m, i) => (m.role === "tool" && m.name !== "session_summary" ? i : -1))
|
|
137
|
+
.filter((i) => i >= 0);
|
|
134
138
|
for (const i of toolIdx.slice(0, Math.max(0, toolIdx.length - 5))) {
|
|
135
139
|
if (total <= budget) break;
|
|
136
140
|
const before = len(out[i]);
|
|
@@ -138,11 +142,17 @@ function compact(history, budget) {
|
|
|
138
142
|
total -= before - len(out[i]);
|
|
139
143
|
}
|
|
140
144
|
if (total <= budget) return out;
|
|
141
|
-
// (2) Vẫn dài → bỏ các lượt cũ nhất, giữ
|
|
142
|
-
// 12 message gần
|
|
145
|
+
// (2) Vẫn dài → bỏ các lượt cũ nhất, giữ MỌI session_summary (bộ nhớ dài hạn
|
|
146
|
+
// do maybeSummarize() chèn) + USER đầu tiên (nhiệm vụ gốc) + 12 message gần
|
|
147
|
+
// nhất. KHÔNG BỎ session_summary — đó là bộ nhớ phiên, mất nó là mất hết.
|
|
148
|
+
const summaryIdx = out
|
|
149
|
+
.map((m, i) => (m.role === "tool" && m.name === "session_summary" ? i : -1))
|
|
150
|
+
.filter((i) => i >= 0);
|
|
143
151
|
const firstUser = out.findIndex((m) => m.role === "user");
|
|
144
|
-
const
|
|
145
|
-
const
|
|
152
|
+
const headIndices = new Set([...summaryIdx, ...(firstUser >= 0 ? [firstUser] : [])]);
|
|
153
|
+
const head = [...headIndices].sort((a, b) => a - b).map((i) => out[i]);
|
|
154
|
+
const maxHeadIdx = head.length ? Math.max(...headIndices) : -1;
|
|
155
|
+
const tailStart = Math.max(maxHeadIdx + 1, out.length - 12);
|
|
146
156
|
const elided = { role: "tool", name: "context", content: "[… các lượt trước đã được lược bớt …]" };
|
|
147
157
|
return [...head, elided, ...out.slice(tailStart)];
|
|
148
158
|
}
|
|
@@ -214,7 +224,8 @@ ${transcript}`;
|
|
|
214
224
|
// tool có thật (không phải từ lời model tự kể). Chống lỗi model "tưởng đã tạo
|
|
215
225
|
// file" (chỉ kể trong văn xuôi, quên gọi write_file) rồi khăng khăng "file bị
|
|
216
226
|
// revert". Dựng từ history ĐẦY ĐỦ (kể cả khi đã nén) để luôn đúng thực tế.
|
|
217
|
-
|
|
227
|
+
// Exported cho test (scripts/test-files-ledger.js).
|
|
228
|
+
export function filesLedger(history) {
|
|
218
229
|
const touched = new Map(); // path -> "đã ghi" | "đã sửa"
|
|
219
230
|
for (const m of history) {
|
|
220
231
|
if (m.role !== "tool" || typeof m.content !== "string" || m.content.startsWith("ERROR")) continue;
|
|
@@ -265,7 +276,10 @@ function buildPrompt(history, extraToolsDoc) {
|
|
|
265
276
|
parts.push("");
|
|
266
277
|
}
|
|
267
278
|
parts.push("=".repeat(60));
|
|
268
|
-
|
|
279
|
+
// Recency bias: câu chốt cuối prompt nằm ở vị trí attention mạnh nhất. Nhắc
|
|
280
|
+
// model đối chiếu FILES CHANGED trước khi claim đã sửa file — chống ảo giác
|
|
281
|
+
// "đã tạo file" khi chưa gọi write_file/edit_file.
|
|
282
|
+
parts.push("Continue. Emit a tool block to act, or reply in Markdown if done. Before claiming any file was created/edited, verify it appears in the FILES CHANGED list above — if not, emit the tool call now.");
|
|
269
283
|
return parts.join("\n");
|
|
270
284
|
}
|
|
271
285
|
|
package/src/tui.js
CHANGED
|
@@ -163,14 +163,20 @@ export function createTui({ onLine, onInterrupt, onEOF, onShiftTab, completer }
|
|
|
163
163
|
function topRow() {
|
|
164
164
|
if (liveOut) {
|
|
165
165
|
// Khi đang stream prose mà busy, ghép meta (elapsed+token) vào cuối liveOut
|
|
166
|
-
//
|
|
166
|
+
// CHỈ KHI liveOut đủ ngắn để cả dòng (liveOut + meta) chắc chắn fit trong
|
|
167
|
+
// 1 dòng terminal. Nếu liveOut quá dài → ưu tiên prose, bỏ meta lượt này
|
|
168
|
+
// (tránh terminal wrap làm dòng tạm kẹt lại trong prose vĩnh viễn — xem
|
|
169
|
+
// Note "token meter chèn vào prose" trong noob.md).
|
|
167
170
|
if (busy && busyMeta) {
|
|
168
171
|
const meta = c.dim(" · " + busyMeta);
|
|
169
|
-
const
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
+
const metaLen = visLen(meta);
|
|
173
|
+
const liveLen = visLen(liveOut);
|
|
174
|
+
if (liveLen + metaLen <= cols()) {
|
|
175
|
+
return liveOut + meta;
|
|
176
|
+
}
|
|
177
|
+
// liveOut đã dài: hiện prose nguyên trạng, bỏ meta để tránh wrap.
|
|
172
178
|
}
|
|
173
|
-
return liveOut.slice(0, cols());
|
|
179
|
+
return liveOut.length > cols() ? liveOut.slice(0, cols()) : liveOut;
|
|
174
180
|
}
|
|
175
181
|
const spin = FRAMES[frame % FRAMES.length];
|
|
176
182
|
// Khi busy, LUÔN hiện elapsed+tokens (busyMeta) cạnh statusText/busyLabel để
|