@tiens.nguyen/gonext-local-worker 1.0.313 → 1.0.314
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-repl.mjs +12 -1
- package/gonext_agent_chat.py +28 -8
- package/package.json +1 -1
package/gonext-repl.mjs
CHANGED
|
@@ -948,6 +948,11 @@ let cancelRequested = false; // a cancel POST has been sent for the current turn
|
|
|
948
948
|
let approvalActive = false;
|
|
949
949
|
let approvalSel = 0; // 0 = Yes (default highlighted), 1 = No
|
|
950
950
|
let approvalResolve = null;
|
|
951
|
+
// The active turn's live-status wiper (its local clearStatus), or null between turns. A
|
|
952
|
+
// picker calls this before drawing so the "Almost done…/thought/token" block is gone
|
|
953
|
+
// first — otherwise the 120ms ticker keeps redrawing that block on top of the picker and
|
|
954
|
+
// its relative-cursor math lands on the wrong rows (duplicated/interleaved options).
|
|
955
|
+
let clearActiveStatus = null;
|
|
951
956
|
function drawApprovalOptions(redraw) {
|
|
952
957
|
// Two option lines, redrawn in place. On redraw the cursor sits one row below "No";
|
|
953
958
|
// step up 2 rows, clear+rewrite both, landing back where we started.
|
|
@@ -986,6 +991,10 @@ const MAXSTEP_PREFIX = "__MAXSTEP__::";
|
|
|
986
991
|
const TIMEOUT_PREFIX = "__TIMEOUT__::";
|
|
987
992
|
const COMPACT_PREFIX = "__COMPACT__::";
|
|
988
993
|
async function approvalPrompt(command) {
|
|
994
|
+
// Wipe the live "thinking" status block BEFORE any picker draws, and stop the ticker from
|
|
995
|
+
// redrawing it (the tick early-returns while a picker is active) — otherwise the block
|
|
996
|
+
// reappears under the options and corrupts the layout.
|
|
997
|
+
if (clearActiveStatus) clearActiveStatus();
|
|
989
998
|
// Context-compaction prompt (task #87 rec#3): the running context got large. Ask whether
|
|
990
999
|
// to aggressively compact older steps. No answer in 30s = No (keep full detail — the safe
|
|
991
1000
|
// default; the always-on auto-trim still bounds things).
|
|
@@ -1463,10 +1472,11 @@ async function runAgentTurn(history) {
|
|
|
1463
1472
|
process.stdout.write(seq);
|
|
1464
1473
|
statusShown = false;
|
|
1465
1474
|
};
|
|
1475
|
+
clearActiveStatus = clearStatus; // let pickers wipe this turn's status block before they draw
|
|
1466
1476
|
|
|
1467
1477
|
const tick = () => {
|
|
1468
1478
|
if (!following || followAborted || !jobRunning) return;
|
|
1469
|
-
if (approvalActive) return; // a
|
|
1479
|
+
if (approvalActive || listPickerActive) return; // a picker owns the screen — don't draw over it
|
|
1470
1480
|
if (Date.now() - lastContentAt < QUIET_MS) return; // tokens still flowing
|
|
1471
1481
|
// A plain-reply answer streams onto a line with NO trailing newline until it's fully
|
|
1472
1482
|
// done — overwriting the ticker there corrupts the visible answer mid-word. Stay
|
|
@@ -2009,6 +2019,7 @@ async function runAgentTurn(history) {
|
|
|
2009
2019
|
} finally {
|
|
2010
2020
|
clearInterval(ticker);
|
|
2011
2021
|
clearStatus();
|
|
2022
|
+
clearActiveStatus = null; // no turn owns the status wiper between turns
|
|
2012
2023
|
// Task #96: stop mouse-reporting the moment the turn ends (also on cancel/error — this
|
|
2013
2024
|
// finally always runs), and detach the click parser, so the user's shell selection is
|
|
2014
2025
|
// never captured between turns.
|
package/gonext_agent_chat.py
CHANGED
|
@@ -5071,20 +5071,40 @@ def run_agent_chat(cfg):
|
|
|
5071
5071
|
"""Recover a <code>…</code> tool call that a reasoning model placed in the
|
|
5072
5072
|
REASONING channel instead of the visible message (task #111). No-op when the
|
|
5073
5073
|
visible content already has a usable code block, or when there is no reasoning
|
|
5074
|
-
text (e.g. Ollama with reasoning_effort='none') — so the Ollama path is unchanged.
|
|
5074
|
+
text (e.g. Ollama with reasoning_effort='none') — so the Ollama path is unchanged.
|
|
5075
|
+
|
|
5076
|
+
Kimi K3 @ Moonshot splits its output two ways when it fails: (a) the full
|
|
5077
|
+
<code>…</code> block lands in reasoning and only prose leaks to content; or
|
|
5078
|
+
(b) the OPENER + code land in reasoning while the CLOSING </code> leaks into
|
|
5079
|
+
content (seen live: content = 'Thought: …contains.</code>'). Handle both."""
|
|
5075
5080
|
_pair = r"<code[^>]*>[\s\S]*?</code>"
|
|
5076
5081
|
if re.search(_pair, content or "", re.I):
|
|
5077
|
-
return content # already
|
|
5078
|
-
|
|
5082
|
+
return content # content already has a usable block — never override it
|
|
5083
|
+
reasoning = reasoning or ""
|
|
5084
|
+
if not reasoning.strip():
|
|
5079
5085
|
return content # nothing to recover from (Ollama / non-reasoning model)
|
|
5080
|
-
|
|
5081
|
-
|
|
5086
|
+
code = None
|
|
5087
|
+
# (a) Prefer a COMPLETE block already inside reasoning (the model drafts the
|
|
5088
|
+
# whole tool call there) — take the LAST one, ignoring any stray </code> in content.
|
|
5082
5089
|
last = None
|
|
5083
|
-
for last in re.finditer(r"<code[^>]*>([\s\S]*?)</code>",
|
|
5090
|
+
for last in re.finditer(r"<code[^>]*>([\s\S]*?)</code>", reasoning, re.I):
|
|
5084
5091
|
pass
|
|
5085
|
-
if
|
|
5092
|
+
if last:
|
|
5093
|
+
code = last.group(1)
|
|
5094
|
+
else:
|
|
5095
|
+
# (b) Opener + code in reasoning, closer leaked to content → take everything
|
|
5096
|
+
# after the LAST opener in reasoning (its </code> is the one now in content).
|
|
5097
|
+
m = None
|
|
5098
|
+
for m in re.finditer(r"<code[^>]*>([\s\S]*)$", reasoning, re.I):
|
|
5099
|
+
pass
|
|
5100
|
+
if m:
|
|
5101
|
+
code = m.group(1)
|
|
5102
|
+
if code is None:
|
|
5086
5103
|
return content
|
|
5087
|
-
|
|
5104
|
+
# A leaked 'Thought:' line (or a stray </code>) can ride along after the code —
|
|
5105
|
+
# cut it so only the executable snippet remains.
|
|
5106
|
+
code = re.split(r"\n\s*Thought\s*:\s", code, maxsplit=1)[0]
|
|
5107
|
+
code = re.sub(r"</?code[^>]*>", "", code, flags=re.I).strip()
|
|
5088
5108
|
if not code:
|
|
5089
5109
|
return content
|
|
5090
5110
|
# Keep the visible Thought prose (strip any stray/unbalanced code tags), then
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiens.nguyen/gonext-local-worker",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.314",
|
|
4
4
|
"description": "Polls GoNext cloud API for async local LLM jobs and runs them against Ollama/OpenAI-compatible servers on this Mac",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|