@tiens.nguyen/gonext-local-worker 1.0.225 → 1.0.226

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.
Files changed (2) hide show
  1. package/gonext-repl.mjs +51 -6
  2. package/package.json +1 -1
package/gonext-repl.mjs CHANGED
@@ -699,6 +699,21 @@ async function runAgentTurn(history) {
699
699
  let thinkWord = pickWord(); // playful word shown UNDER the in-progress line (flavor)
700
700
  let almostDone = false; // set from the worker's "…almost completed thinking…" heartbeat
701
701
  let runningCmd = null; // the command currently executing (from a "Running → …" event)
702
+ // The model's CURRENT Thought (task #64): while it streams its reasoning inside the
703
+ // hidden ~~~ fence, show a one-clause summary on the blinking line so a 300s CPU-bound
704
+ // step reads as "◑ Adding the nav links to app.component.html… (52s)" instead of a
705
+ // random word. `fenceThought` accumulates the raw in-fence text for the current step.
706
+ let liveThought = "";
707
+ let fenceThought = "";
708
+ // First non-empty Thought clause: drop the "Thought:" prefix, stop at the <code> block
709
+ // (we never show code), take the first line, and cap length so it never wraps.
710
+ const extractThought = (buf) => {
711
+ let t = String(buf || "");
712
+ const ci = t.search(/<code[\s>]/i);
713
+ if (ci >= 0) t = t.slice(0, ci);
714
+ t = t.replace(/^\s*Thought:\s*/i, "").split(/\n/)[0].trim();
715
+ return t;
716
+ };
702
717
  const thinkSecs = () => Math.max(1, Math.round((Date.now() - thinkingSince) / 1000));
703
718
 
704
719
  // The status is TWO in-place lines — the in-progress action, then the playful word
@@ -724,9 +739,12 @@ async function runAgentTurn(history) {
724
739
  const glyph = SPINNER[secs % SPINNER.length]; // rotates → reads as "in progress"
725
740
  // Line 1 = WHAT is happening now: the running command, else the phase (Thinking /
726
741
  // Almost done). Line 2 = the playful word (flavor). Truncate so neither wraps.
742
+ // Priority: a live command > the model's current Thought clause (task #64, so a
743
+ // long CPU-bound step reads as what it's actually reasoning about, not "Thinking")
744
+ // > the "almost done" heartbeat > the bare fallback.
727
745
  const primary = runningCmd
728
746
  ? `Running ${runningCmd}`
729
- : almostDone ? "Almost done" : "Thinking";
747
+ : liveThought || (almostDone ? "Almost done" : "Thinking");
730
748
  const max = Math.max(24, (process.stdout.columns || 80) - 14);
731
749
  const fit = (s) => (s.length > max ? s.slice(0, max - 1) + "…" : s);
732
750
  clearStatus();
@@ -744,6 +762,10 @@ async function runAgentTurn(history) {
744
762
  clearStatus();
745
763
  thinking = false;
746
764
  runningCmd = null;
765
+ // A concrete action landed → the Thought that led to it is spent; clear it so the
766
+ // next step's blinking line starts from "Thinking", not last step's stale clause.
767
+ liveThought = "";
768
+ fenceThought = "";
747
769
  lastContentAt = Date.now();
748
770
  };
749
771
 
@@ -782,11 +804,26 @@ async function runAgentTurn(history) {
782
804
  almostDone = /almost/i.test(line);
783
805
  continue;
784
806
  }
785
- if (isFenceLine(line)) { inStreamFence = !inStreamFence; continue; }
786
- // Inside a ~~~ fence = raw model Thought/code stream suppressed entirely. Do NOT
787
- // bump lastContentAt: while the (now hidden) tokens flow, the blinking bullet keeps
788
- // ticking so the user sees the step is still being worked on.
789
- if (inStreamFence) continue;
807
+ // A fence opening starts a fresh step's raw stream → reset the harvested Thought
808
+ // so the blinking line doesn't carry last step's clause into this one.
809
+ if (isFenceLine(line)) {
810
+ inStreamFence = !inStreamFence;
811
+ if (inStreamFence) fenceThought = "";
812
+ continue;
813
+ }
814
+ // Inside a ~~~ fence = raw model Thought/code stream → suppressed from scrollback. Do
815
+ // NOT bump lastContentAt: while the (now hidden) tokens flow, the blinking bullet keeps
816
+ // ticking so the user sees the step is still being worked on. But HARVEST the first
817
+ // Thought clause for the blinking line (task #64) — up to the <code> block, which is
818
+ // where the prose ends and the tool call begins (we never surface code here).
819
+ if (inStreamFence) {
820
+ if (!/<code[\s>]/i.test(fenceThought)) {
821
+ fenceThought += line + "\n";
822
+ const th = extractThought(fenceThought);
823
+ if (th) liveThought = th;
824
+ }
825
+ continue;
826
+ }
790
827
  if (isToolStepSummary(line)) { lastContentAt = Date.now(); continue; } // swallow
791
828
  if (isRoutingLine(line)) {
792
829
  // Swallow the router's play-by-play entirely — the blinking-bullet ticker (which
@@ -914,6 +951,14 @@ async function runAgentTurn(history) {
914
951
  answerShownLive = true;
915
952
  lastWasBlank = false;
916
953
  }
954
+ // While in the hidden fence, the model's FIRST Thought line often sits in `carry` with
955
+ // no newline yet (a long reasoning sentence streams token-by-token) — recompute the
956
+ // live clause from the partial too, so the blinking line updates mid-sentence instead
957
+ // of only once the line finally breaks (task #64).
958
+ if (inStreamFence && !/<code[\s>]/i.test(fenceThought)) {
959
+ const th = extractThought(fenceThought + carry);
960
+ if (th) liveThought = th;
961
+ }
917
962
  // A non-empty remainder is a partial CONTENT line (heartbeats always arrive whole with
918
963
  // a newline), so tokens are actively flowing — keep the ticker asleep.
919
964
  if (carry.trim() !== "") lastContentAt = Date.now();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiens.nguyen/gonext-local-worker",
3
- "version": "1.0.225",
3
+ "version": "1.0.226",
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",