TUI: stop clipping newest lines; read streaming logprobs for confidence

ober

1d52d374941b4777f41cdb4371cbfdd9418d03a7

diff --git a/src/jcode/core/models.ss b/src/jcode/core/models.ss
index d60e83e..a3e109b 100644
--- a/src/jcode/core/models.ss
+++ b/src/jcode/core/models.ss
@@ -54,7 +54,7 @@
     ((deepseek)    "deepseek-chat")
     ((google)      "gemini-2.5-flash")
     ((ollama)      "llama3.2")
-    ((mlx)         "/Users/user/mine/jerboa-lora/jerboa-mlx-6bit-v3")
+    ((mlx)         "/Users/user/llm/mlx-models/Qwen3.6-35B-A3B-TurboQuant-6bit")
     ((xai)         "grok-3-mini")
     ((groq)        "llama-3.3-70b-versatile")
     ((mistral)     "mistral-large-latest")
@@ -219,7 +219,7 @@
     ("gemma2"          . "Gemma 2")))
 
 (def mlx-models
-  '(("/Users/user/mine/jerboa-lora/jerboa-mlx-4bit-v2" . "Jerboa Qwen3 30B (MLX 4-bit)")))
+  '(("/Users/user/llm/mlx-models/Qwen3.6-35B-A3B-TurboQuant-6bit" . "Qwen3.6 35B-A3B TurboQuant (MLX 6-bit)")))
 
 (def xai-models
   '(("grok-3"          . "Grok 3")
@@ -437,6 +437,10 @@
       ;; ctx limit) dominates. Keep this conservative so compaction kicks
       ;; in before prefill blows the 32 GB wired budget.
       ((string-contains mid "jerboa-mlx")               16384)
+      ;; majentik TurboQuant local MLX builds (Qwen3.6-35B-A3B 6-bit): ~28 GB
+      ;; weights leave only ~4 GB under the 32 GB wired cap, so keep the window
+      ;; conservative — compaction must fire before prefill OOMs the GPU.
+      ((string-contains mid "TurboQuant")               16384)
       ((string-contains mid "qwen3")                    32768)
       ((string-contains mid "llama-3")                  131072)
       ((string-contains mid "deepseek")                 131072)
diff --git a/src/jcode/provider/provider.ss b/src/jcode/provider/provider.ss
index e397d37..4da8c92 100644
--- a/src/jcode/provider/provider.ss
+++ b/src/jcode/provider/provider.ss
@@ -606,10 +606,14 @@
                            (if (> p 0) (* p (log p)) 0))
                          norms)))))))
 
-(def (accumulate-logprobs! delta logprob-box entropy-box)
-  ;; delta is a hash-table; if it has logprobs.content[], walk each token
-  ;; entry, push logprob and (top-k entropy if available).
-  (let ((lp (and (hash-table? delta) (hash-get delta "logprobs"))))
+(def (accumulate-logprobs! choice logprob-box entropy-box)
+  ;; OpenAI-standard streaming carries logprobs at choice.logprobs (a sibling
+  ;; of delta); some servers nest it under choice.delta.logprobs. Accept the
+  ;; whole choice and check both. If logprobs.content[] is present, walk each
+  ;; token entry, push logprob and (top-k entropy if available).
+  (let* ((delta (and (hash-table? choice) (hash-get choice "delta")))
+         (lp    (or (and (hash-table? choice) (hash-get choice "logprobs"))
+                    (and (hash-table? delta)  (hash-get delta "logprobs")))))
     (when (and lp (hash-table? lp))
       (let ((entries (hash-get lp "content")))
         (when (and entries (list? entries))
@@ -1196,10 +1200,12 @@
                      (when choice
                        (let ((fr (hash-get choice "finish_reason")))
                          (when (and fr (string? fr) (not (string=? fr "")))
-                           (set-box! finish-reason-box fr))))
+                           (set-box! finish-reason-box fr)))
+                       ;; Accumulate per-token logprobs/entropy when requested.
+                       ;; logprobs sit at the choice level (sibling of delta),
+                       ;; so this runs on `choice`, not gated on `delta`.
+                       (accumulate-logprobs! choice logprob-box entropy-box))
                      (when delta
-                       ;; Accumulate per-token logprobs/entropy when requested
-                       (accumulate-logprobs! delta logprob-box entropy-box)
                        ;; Text content token
                        (let ((content (hash-get delta "content")))
                          (when (and content (string? content) (> (string-length content) 0))
diff --git a/src/jcode/ui/tui-message.ss b/src/jcode/ui/tui-message.ss
index 03475d7..cca739d 100644
--- a/src/jcode/ui/tui-message.ss
+++ b/src/jcode/ui/tui-message.ss
@@ -254,9 +254,14 @@
     ((error)     'msg-border-error)
     (else        'msg-border-tool)))
 
-(def (render-msg-block! msg x y width max-lines)
-  "Render msg-block at position (x, y) within width, up to max-lines rows."
-  (let* ((lines (msg-block-lines msg))
+(def (render-msg-block! msg x y width max-lines from-line)
+  "Render msg-block at position (x, y) within width, up to max-lines rows.
+   Skips the first FROM-LINE wrapped lines: used to bottom-anchor a block
+   taller than the visible area so its newest (tail) lines show, not its head."
+  (let* ((all (msg-block-lines msg))
+         (lines (if (and (> from-line 0) (<= from-line (length all)))
+                  (list-tail all from-line)
+                  all))
          (bg-face (face-ref 'default))
          (bf (role->border-face (msg-block-role msg)))
          (bfg (face-fg-attr bf))
diff --git a/src/jcode/ui/tui.ss b/src/jcode/ui/tui.ss
index 852a107..08cc758 100644
--- a/src/jcode/ui/tui.ss
+++ b/src/jcode/ui/tui.ss
@@ -1579,12 +1579,16 @@
               ;; Partial skip: render bottom portion of this block
               (let* ((visible-h (- mh skip))
                      (start-row (max y (- row visible-h -1))))
-                (render-msg-block! msg x start-row w visible-h)
+                ;; Head of the block; the bottom `skip` lines are below the fold.
+                (render-msg-block! msg x start-row w visible-h 0)
                 (loop (cdr msgs) (- start-row 1) 0)))
             ;; Full render
             (let ((start-row (max y (- row mh -1)))
                   (avail (min mh (- row y -1))))
-              (render-msg-block! msg x start-row w avail)
+              ;; Bottom-anchored: when the block overflows above the top of the
+              ;; message area, drop its first (mh - avail) lines so the newest
+              ;; lines stay visible instead of being clipped off the bottom.
+              (render-msg-block! msg x start-row w avail (- mh avail))
               (loop (cdr msgs) (- start-row 1) 0))))))))
 
 (def (draw-spinner! state)