@tiens.nguyen/gu-cli 1.0.704 → 1.0.705
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/gonext_agent_chat.py +17 -6
- package/gu-repl.mjs +33 -2
- package/package.json +2 -1
- package/verbose-lines.mjs +33 -0
package/gonext_agent_chat.py
CHANGED
|
@@ -1329,6 +1329,11 @@ def _render_numbered_tool_list(tools) -> str:
|
|
|
1329
1329
|
return ("\n".join(lines) + "\n") if lines else ""
|
|
1330
1330
|
|
|
1331
1331
|
|
|
1332
|
+
# A step line only `gu --verbose` shows (#45): retry notices and context trimming are how the
|
|
1333
|
+
# agent keeps itself going, and the user can do nothing with them.
|
|
1334
|
+
_VERBOSE_PREFIX = "__VERBOSE__::"
|
|
1335
|
+
|
|
1336
|
+
|
|
1332
1337
|
def _summarise_step(step_log):
|
|
1333
1338
|
"""Return a short human-readable description of an agent step."""
|
|
1334
1339
|
tool_calls = getattr(step_log, "tool_calls", None) or []
|
|
@@ -1337,6 +1342,9 @@ def _summarise_step(step_log):
|
|
|
1337
1342
|
step_num = getattr(step_log, "step_number", None)
|
|
1338
1343
|
|
|
1339
1344
|
parts = []
|
|
1345
|
+
# A retry the agent recovers from by itself is machinery, not progress: the terminal shows
|
|
1346
|
+
# it only under `gu --verbose` (#45). The model still gets smolagents' exact error text.
|
|
1347
|
+
verbose_only = False
|
|
1340
1348
|
for tc in tool_calls:
|
|
1341
1349
|
name = getattr(tc, "name", "")
|
|
1342
1350
|
args = getattr(tc, "arguments", None)
|
|
@@ -1404,15 +1412,18 @@ def _summarise_step(step_log):
|
|
|
1404
1412
|
# step loop), so the model keeps the precise wording it needs to self-correct.
|
|
1405
1413
|
_msg = "the model wrote no code this step — asking again"
|
|
1406
1414
|
parts.append(f"→ {_msg}" if parts else _msg)
|
|
1415
|
+
verbose_only = True
|
|
1407
1416
|
elif "code parsing failed" in err.lower() or "syntaxerror" in err.lower():
|
|
1408
1417
|
# The sibling shape: there WAS a block, and its body would not compile.
|
|
1409
1418
|
_msg = "the model's code wouldn't run — asking again"
|
|
1410
1419
|
parts.append(f"→ {_msg}" if parts else _msg)
|
|
1420
|
+
verbose_only = True
|
|
1411
1421
|
else:
|
|
1412
1422
|
parts.append(f"→ Error: {_clip(err, 160)}")
|
|
1413
1423
|
|
|
1414
1424
|
# No numeric "Step N:" prefix — show only the semantic action.
|
|
1415
|
-
|
|
1425
|
+
text = " | ".join(parts) if parts else "thinking…"
|
|
1426
|
+
return f"{_VERBOSE_PREFIX}{text}" if verbose_only else text
|
|
1416
1427
|
|
|
1417
1428
|
|
|
1418
1429
|
# Keywords that strongly indicate the user wants to make an HTTP/network request,
|
|
@@ -8689,7 +8700,7 @@ def run_agent_chat(cfg):
|
|
|
8689
8700
|
# once, and that answer is remembered. The line exists so the figure is not mistaken
|
|
8690
8701
|
# for something the endpoint reported — see the "measured vs remembered" note above.
|
|
8691
8702
|
_emit({"type": "step", "text": (
|
|
8692
|
-
f"context window {_ctx_window // 1000}k for {coding_model_id} (published default — "
|
|
8703
|
+
f"{_VERBOSE_PREFIX}context window {_ctx_window // 1000}k for {coding_model_id} (published default — "
|
|
8693
8704
|
f"this endpoint does not report one). Corrects itself only if it is too HIGH; "
|
|
8694
8705
|
f"/context-window to set it yourself")})
|
|
8695
8706
|
else:
|
|
@@ -8705,7 +8716,7 @@ def run_agent_chat(cfg):
|
|
|
8705
8716
|
# command immediately and cannot act on an env var without leaving, editing a file and
|
|
8706
8717
|
# restarting — which is how this stayed unset long enough to matter.
|
|
8707
8718
|
_emit({"type": "step", "text": (
|
|
8708
|
-
f"context window unknown — compacting at ~{_compact_at // 1000}k tokens; "
|
|
8719
|
+
f"{_VERBOSE_PREFIX}context window unknown — compacting at ~{_compact_at // 1000}k tokens; "
|
|
8709
8720
|
"run /context-window <tokens> to use more")})
|
|
8710
8721
|
|
|
8711
8722
|
# Task #90 Phase 3 — completion gate (code-enforced, complements the prompt). Fires at
|
|
@@ -8958,7 +8969,7 @@ def run_agent_chat(cfg):
|
|
|
8958
8969
|
if _cs and not _compact_state["told"]:
|
|
8959
8970
|
_compact_state["told"] = True
|
|
8960
8971
|
_emit({"type": "step", "text": (
|
|
8961
|
-
f"Context reached ~{_in_tok // 1000}k tokens — trimming older "
|
|
8972
|
+
f"{_VERBOSE_PREFIX}Context reached ~{_in_tok // 1000}k tokens — trimming older "
|
|
8962
8973
|
"steps to stay fast (files are re-readable)")})
|
|
8963
8974
|
if _cs:
|
|
8964
8975
|
_log(f"context compact: saved ~{_cs} chars "
|
|
@@ -9477,9 +9488,9 @@ def run_agent_chat(cfg):
|
|
|
9477
9488
|
# to say. On empty-content there is genuinely nothing to show — the model spent
|
|
9478
9489
|
# the turn in its reasoning channel and wrote nothing — so a short line stays.
|
|
9479
9490
|
_emit({"type": "step", "text": (
|
|
9480
|
-
"The model thought but didn't act — asking it again…"
|
|
9491
|
+
f"{_VERBOSE_PREFIX}The model thought but didn't act — asking it again…"
|
|
9481
9492
|
if why == "empty-content"
|
|
9482
|
-
else "Asking the model to take the action it described…")})
|
|
9493
|
+
else f"{_VERBOSE_PREFIX}Asking the model to take the action it described…")})
|
|
9483
9494
|
retry_kwargs = dict(completion_kwargs)
|
|
9484
9495
|
retry_kwargs["messages"] = list(completion_kwargs.get("messages") or []) + [
|
|
9485
9496
|
{"role": "user", "content": self._CODE_NOW_DIRECTIVE}
|
package/gu-repl.mjs
CHANGED
|
@@ -137,6 +137,7 @@ import { REQUIRED_AGENT_MODEL, REQUIRED_AGENT_PREFIX, REQUIRED_EMBED_MODEL } fro
|
|
|
137
137
|
// plain SGR code: terminals disagree about colour, so accent.mjs picks 24-bit, 256 or the
|
|
138
138
|
// sixteen-colour magenta from what this one actually claims to support.
|
|
139
139
|
import { accent } from "./accent.mjs";
|
|
140
|
+
import { isNoCodeRetry, splitVerboseLine, stepHasCode } from "./verbose-lines.mjs";
|
|
140
141
|
import {
|
|
141
142
|
createTranscript,
|
|
142
143
|
displayRows,
|
|
@@ -8466,6 +8467,16 @@ async function runAgentTurn(history, { attachJobId = "" } = {}) {
|
|
|
8466
8467
|
// 400 chars — enough for a one-line label, not for the paragraphs we now print). Bounded so a
|
|
8467
8468
|
// runaway generation cannot grow without limit; the prose we keep is far shorter anyway.
|
|
8468
8469
|
let stepThought = "";
|
|
8470
|
+
// #45: the prose of a step whose output had NO code, held back until the next line says what
|
|
8471
|
+
// became of it. A no-code retry notice discards it (the model restates the plan on the retry);
|
|
8472
|
+
// anything else prints it. Always null under --verbose, which shows everything as it comes.
|
|
8473
|
+
let heldThought = null;
|
|
8474
|
+
const releaseHeldThought = () => {
|
|
8475
|
+
if (heldThought === null) return;
|
|
8476
|
+
const raw = heldThought;
|
|
8477
|
+
heldThought = null;
|
|
8478
|
+
if (printThoughtBlock(raw, onContent)) lastWasBlank = false;
|
|
8479
|
+
};
|
|
8469
8480
|
let hoverRow = -1; // last mouse viewport row (from motion events) → highlight when it's on the thought line
|
|
8470
8481
|
let thoughtRow = -1; // absolute viewport row of the thought line (line 1), learned via \x1b[6n
|
|
8471
8482
|
let lastDsrAt = 0; // throttle the cursor-position probe
|
|
@@ -8894,7 +8905,7 @@ async function runAgentTurn(history, { attachJobId = "" } = {}) {
|
|
|
8894
8905
|
carry += stripThinkTags(chunk);
|
|
8895
8906
|
let nl;
|
|
8896
8907
|
while ((nl = carry.indexOf("\n")) >= 0) {
|
|
8897
|
-
|
|
8908
|
+
let line = carry.slice(0, nl);
|
|
8898
8909
|
carry = carry.slice(nl + 1);
|
|
8899
8910
|
const alreadyFlushed = carryFlushedLen;
|
|
8900
8911
|
carryFlushedLen = 0; // reset — `carry` now starts a fresh pending line
|
|
@@ -8907,6 +8918,21 @@ async function runAgentTurn(history, { attachJobId = "" } = {}) {
|
|
|
8907
8918
|
almostDone = /almost/i.test(line);
|
|
8908
8919
|
continue;
|
|
8909
8920
|
}
|
|
8921
|
+
// #45: retry notices and context-window bookkeeping are the agent's own machinery. Shown
|
|
8922
|
+
// under --verbose only; otherwise swallowed, together with the no-code false start the
|
|
8923
|
+
// retry notice is about.
|
|
8924
|
+
{
|
|
8925
|
+
const v = splitVerboseLine(line);
|
|
8926
|
+
if (v.verboseOnly) {
|
|
8927
|
+
if (!REPL_VERBOSE) {
|
|
8928
|
+
if (isNoCodeRetry(v.text)) heldThought = null;
|
|
8929
|
+
lastContentAt = Date.now();
|
|
8930
|
+
continue;
|
|
8931
|
+
}
|
|
8932
|
+
line = v.text;
|
|
8933
|
+
}
|
|
8934
|
+
}
|
|
8935
|
+
if (heldThought !== null && line.trim() !== "") releaseHeldThought();
|
|
8910
8936
|
// Task #136: a RAG index file was written. Swallow the line — it belongs in the
|
|
8911
8937
|
// BOTTOM BAR, not in the action list — and remember the newest one. Kept out of the
|
|
8912
8938
|
// bullet stream deliberately: indexing writes a shard per batch, so printing them
|
|
@@ -8939,7 +8965,11 @@ async function runAgentTurn(history, { attachJobId = "" } = {}) {
|
|
|
8939
8965
|
// The step's model output is complete. Print its reasoning HERE, before the tools
|
|
8940
8966
|
// report what they did, so the transcript reads in the order it happened: the model
|
|
8941
8967
|
// says what it intends, then the actions it took appear under it.
|
|
8942
|
-
if (
|
|
8968
|
+
if (!REPL_VERBOSE && stepThought.trim() && !stepHasCode(stepThought)) {
|
|
8969
|
+
heldThought = stepThought; // #45: decided by the next line — see releaseHeldThought
|
|
8970
|
+
} else if (printThoughtBlock(stepThought, onContent)) {
|
|
8971
|
+
lastWasBlank = false;
|
|
8972
|
+
}
|
|
8943
8973
|
stepThought = "";
|
|
8944
8974
|
}
|
|
8945
8975
|
continue;
|
|
@@ -9328,6 +9358,7 @@ async function runAgentTurn(history, { attachJobId = "" } = {}) {
|
|
|
9328
9358
|
lastWasBlank = false;
|
|
9329
9359
|
}
|
|
9330
9360
|
}
|
|
9361
|
+
releaseHeldThought(); // #45: nothing followed it, so it was not a false start
|
|
9331
9362
|
clearStatus();
|
|
9332
9363
|
// Task #104: expose this turn's code-model INPUT + the CUMULATIVE output-token
|
|
9333
9364
|
// total (all turns for this user + coding server) so the caller can print a
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiens.nguyen/gu-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.705",
|
|
4
4
|
"description": "Gu CLI — the gu terminal plus the local worker that runs agent / OCR / PDF / embedding jobs on your Mac (Ollama / MLX / OpenAI-compatible).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -60,6 +60,7 @@
|
|
|
60
60
|
"job-pools.mjs",
|
|
61
61
|
"rag-job-config.mjs",
|
|
62
62
|
"completed-flush.mjs",
|
|
63
|
+
"verbose-lines.mjs",
|
|
63
64
|
"agent-model-label.mjs",
|
|
64
65
|
"coding-choice-tags.mjs",
|
|
65
66
|
"coding-provider-setup.mjs",
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Step lines only `gu --verbose` shows (#45).
|
|
3
|
+
*
|
|
4
|
+
* The agent keeps itself going with retries ("the model wrote no code this step — asking
|
|
5
|
+
* again") and context bookkeeping ("context window 128k … published default", "Context reached
|
|
6
|
+
* ~40k tokens — trimming older steps"). The user can do nothing with either, so the agent tags
|
|
7
|
+
* them with this prefix and the terminal drops them unless it runs with --verbose.
|
|
8
|
+
*/
|
|
9
|
+
export const VERBOSE_PREFIX = "__VERBOSE__::";
|
|
10
|
+
|
|
11
|
+
/** Untag a line: `{ verboseOnly, text }`. Lines without the prefix come back unchanged. */
|
|
12
|
+
export function splitVerboseLine(line) {
|
|
13
|
+
const s = String(line ?? "");
|
|
14
|
+
return s.startsWith(VERBOSE_PREFIX)
|
|
15
|
+
? { verboseOnly: true, text: s.slice(VERBOSE_PREFIX.length) }
|
|
16
|
+
: { verboseOnly: false, text: s };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The notice that the model's LAST reply carried no code at all. That reply's prose is then a
|
|
21
|
+
* false start: the model is asked again and restates it, so printing both shows the same plan
|
|
22
|
+
* twice around a notice the user does not need.
|
|
23
|
+
*/
|
|
24
|
+
export function isNoCodeRetry(text) {
|
|
25
|
+
return /wrote no code this step|thought but didn't act|take the action it described/i.test(
|
|
26
|
+
String(text ?? "")
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Did a step's raw model output contain a code block? */
|
|
31
|
+
export function stepHasCode(raw) {
|
|
32
|
+
return /<code[\s>]/i.test(String(raw ?? ""));
|
|
33
|
+
}
|