@noobdemon/noob-cli 1.9.2 → 1.9.4

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noobdemon/noob-cli",
3
- "version": "1.9.2",
3
+ "version": "1.9.4",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
package/src/agent.js CHANGED
@@ -16,7 +16,7 @@ To call a tool, emit EXACTLY ONE fenced code block tagged \`tool\` containing a
16
16
  {"name": "<tool>", "input": { ... }}
17
17
  \`\`\`
18
18
 
19
- Then STOP and wait — the runtime executes the tool and replies with a TOOL RESULT. Use one tool per step. When the task is complete (or you are only answering a question), reply normally in Markdown with NO tool block.
19
+ Then STOP and wait — the runtime executes the tool and replies with a TOOL RESULT. Use one tool per step. When the task is complete (or you are only answering a question), reply normally in Markdown with NO tool block. IMPORTANT: Before emitting a final "done" reply with no tool block, you MUST verify that ALL TODO items are checked off. If any remain unchecked, emit another tool call instead.
20
20
 
21
21
  Available tools (each is self-contained; pick the SMALLEST tool that answers the question):
22
22
  - read_file {"path": str, "offset"?: int, "limit"?: int} — read a file. Default reads whole file. For files you suspect are LARGE (>500 lines), first check size via list_dir/glob, then read with offset+limit (e.g. 200 lines at a time) instead of slurping. The "N " line-number prefix in output is DISPLAY ONLY — never copy it into edit_file.
@@ -33,6 +33,7 @@ Available tools (each is self-contained; pick the SMALLEST tool that answers the
33
33
  Context is finite. Don't slurp the whole repo up front. Discover information progressively: list_dir/glob to map → grep to locate → read_file (with offset+limit for big files) to inspect only what matters. Each tool result spends your attention budget — make every call earn it. When a tool returns a huge blob, extract the few facts you need, then move on; don't re-read it later (the result stays in history).
34
34
 
35
35
  # Rules
36
+ - TODO-BASED EXECUTION: For any multi-step task (3+ actions), CREATE a todo list FIRST as your very first tool call using write_file to a temp block in your response (format: "- [ ] item"). Then WORK THROUGH EVERY ITEM, checking them off ("- [x]") as you complete each. BEFORE summarizing or claiming "done", mentally verify: "Have I checked off ALL items? Is there anything left unchecked?" If ANY item remains unchecked, CONTINUE — do not stop. If the user's request implies multiple deliverables, treat each as a TODO item. NEVER stop mid-plan. NEVER assume something is done without a tool result proving it.
36
37
  - GROUND TRUTH = real TOOL RESULTs in this conversation, not your memory or what you intended to do. A file changed only if a write_file/edit_file result confirms it (see the FILES CHANGED list). A test passed / build succeeded / command worked only if a run_command result above shows it. Never narrate outcomes you didn't observe; if you haven't checked, say so and check now (read_file / list_dir / run the command). Before any "done/summary" reply, reconcile every file and result you're about to claim against the actual tool results above — if it isn't there, you didn't do it yet.
37
38
  - Investigate before editing: read the relevant files first; never invent file contents.
38
39
  - Make the smallest change that fully solves the task. Match the surrounding code style.
@@ -522,7 +523,7 @@ async function streamWithRetry({ model, message, system, signal, tokenMeter, onD
522
523
  lastErr = err;
523
524
  if (attempt >= MAX_RETRIES) break;
524
525
  const backoff = Math.min(30000, 1000 * Math.pow(2, attempt));
525
- onStatus?.(`mạng lỗi (${err.message}) — thử lại sau ${(backoff/1000)|0}s [${attempt+1}/${MAX_RETRIES}]`);
526
+ onStatus?.(`Lỗi kết nối — thử lại sau ${(backoff/1000)|0}s [${attempt+1}/${MAX_RETRIES}]…`);
526
527
  await sleep(backoff, signal);
527
528
  }
528
529
  }
package/src/api.js CHANGED
@@ -116,7 +116,7 @@ function hasUnclosedToolBlock(text) {
116
116
  *
117
117
  * @returns {Promise<{text:string, reasoning:string}>}
118
118
  */
119
- export async function stream({ mode = "chat", message, model, system, conversation, effort, signal, onDelta, onReasoning, onStatus, idleMs = 25000, maxContinues = Infinity }) {
119
+ export async function stream({ mode = "chat", message, model, system, conversation, effort, signal, onDelta, onReasoning, onStatus, idleMs = 45000, maxContinues = Infinity }) {
120
120
  const endpoint = mode === "search" ? "/api/search" : mode === "merge" ? "/api/merge" : "/api/chat";
121
121
 
122
122
  let fullText = "";
@@ -209,9 +209,9 @@ async function streamOnce({ endpoint, mode, message, model, system, conversation
209
209
  let warnTimer = null;
210
210
  let probeTimer = null;
211
211
  let probeInFlight = false;
212
- const WARN_MS = Math.min(8000, idleMs / 3);
213
- const PROBE_MS = Math.min(12000, (idleMs * 2) / 3);
214
- const WIRE_MS = idleMs * 2; // socket dead-cứng (mất cả heartbeat) — ngưỡng rộng
212
+ const WARN_MS = Math.min(15000, idleMs / 3);
213
+ const PROBE_MS = Math.min(25000, (idleMs * 2) / 3);
214
+ const WIRE_MS = idleMs * 3; // socket dead-cứng (mất cả heartbeat) — ngưỡng rất rộng
215
215
  const clearContentTimers = () => {
216
216
  clearTimeout(warnTimer); warnTimer = null;
217
217
  clearTimeout(probeTimer); probeTimer = null;
@@ -220,7 +220,7 @@ async function streamOnce({ endpoint, mode, message, model, system, conversation
220
220
  const armContent = () => {
221
221
  clearContentTimers();
222
222
  warnTimer = setTimeout(() => {
223
- if (onStatus) onStatus("Đang chờ proxy phản hồi…");
223
+ if (onStatus) onStatus("Model đang suy nghĩ… (đợi thêm nếu task phức tạp)");
224
224
  }, WARN_MS);
225
225
  probeTimer = setTimeout(async () => {
226
226
  if (probeInFlight || ctrl.signal.aborted) return;
@@ -234,7 +234,7 @@ async function streamOnce({ endpoint, mode, message, model, system, conversation
234
234
  } catch {
235
235
  if (!ctrl.signal.aborted) {
236
236
  timedOut = true;
237
- if (onStatus) onStatus("Proxy không phản hồi — đang gọi lại model…");
237
+ if (onStatus) onStatus("Model không phản hồi — đang kết nối lại…");
238
238
  ctrl.abort();
239
239
  }
240
240
  } finally {
@@ -328,7 +328,7 @@ async function streamOnce({ endpoint, mode, message, model, system, conversation
328
328
  return { text, reasoning, truncated };
329
329
  } catch (err) {
330
330
  if (signal?.aborted) throw err; // người dùng bấm Ctrl+C → huỷ thật, không nối tiếp
331
- if (timedOut) throw new ApiError("Kết nối tới máy chủ quá thời gian chờ (treo).", { code: "timeout" });
331
+ if (timedOut) throw new ApiError("Kết nối tới máy chủ quá thời gian chờ (treo). Đang thử lại…", { code: "timeout" });
332
332
  // Rớt mạng giữa chừng (không phải huỷ, không phải treo): với chat, nếu đã có
333
333
  // chữ thì trả phần đã nhận + cờ truncated để lớp trên nối tiếp.
334
334
  if (mode === "chat" && text) return { text, reasoning, truncated: true };