models: register "grok" provider metadata (add-grok phase 3)

ober

0132f3f66a06816f75c777c3aff42dbb534599d5

diff --git a/src/jcode/core/models.ss b/src/jcode/core/models.ss
index a3e109b..7114952 100644
--- a/src/jcode/core/models.ss
+++ b/src/jcode/core/models.ss
@@ -19,15 +19,18 @@
 (import :std/text/json
         :std/os/path
         :std/misc/string
-        :jcode/core/log)
+        :jcode/core/log
+        :jcode/core/grok-auth)
 
 (def logger (make-logger "models"))
 
 ;; ---- Provider metadata ----
 
 (def (all-providers)
+  ;; "grok" is the Grok CLI session-token provider (~/.grok/auth.json), kept
+  ;; distinct from "xai" (api.x.ai API-key) and "groq" (Groq Inc., separate).
   '("anthropic" "openai" "openrouter" "deepseek" "google" "ollama" "mlx"
-    "xai" "groq" "mistral" "together" "cerebras" "perplexity"))
+    "xai" "grok" "groq" "mistral" "together" "cerebras" "perplexity"))
 
 (def (provider-display-name p)
   (case (string->symbol p)
@@ -39,6 +42,7 @@
     ((ollama)      "Ollama (local)")
     ((mlx)         "MLX (local)")
     ((xai)         "xAI (Grok)")
+    ((grok)        "Grok CLI")
     ((groq)        "Groq")
     ((mistral)     "Mistral")
     ((together)    "Together AI")
@@ -56,6 +60,7 @@
     ((ollama)      "llama3.2")
     ((mlx)         "/Users/user/llm/mlx-models/Qwen3.6-35B-A3B-TurboQuant-6bit")
     ((xai)         "grok-3-mini")
+    ((grok)        "grok-build")
     ((groq)        "llama-3.3-70b-versatile")
     ((mistral)     "mistral-large-latest")
     ((together)    "meta-llama/Llama-3.3-70B-Instruct-Turbo")
@@ -84,6 +89,7 @@
     ((ollama)      ollama-models)
     ((mlx)         mlx-models)
     ((xai)         xai-models)
+    ((grok)        (grok-models-or-fallback))
     ((groq)        groq-models)
     ((mistral)     mistral-models)
     ((together)    together-models)
@@ -226,6 +232,17 @@
     ("grok-3-mini"     . "Grok 3 Mini")
     ("grok-2"          . "Grok 2")))
 
+;; Grok CLI provider: prefer ~/.grok/models_cache.json when present so the
+;; list mirrors what the user installed via `grok login` / `grok models`,
+;; else show the canonical "grok-build" entry. Filesystem hit per call is
+;; tiny (small JSON, parsed lazily through std/text/json).
+(def grok-models-fallback
+  '(("grok-build" . "Grok Build")))
+
+(def (grok-models-or-fallback)
+  (let ((cached (grok-models-cache-models)))
+    (if (pair? cached) cached grok-models-fallback)))
+
 (def groq-models
   '(("llama-3.3-70b-versatile"  . "Llama 3.3 70B")
     ("llama-3.1-8b-instant"     . "Llama 3.1 8B Instant")
@@ -441,6 +458,10 @@
       ;; 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)
+      ;; grok-build: Grok CLI proxy advertises a 512k context window in
+      ;; ~/.grok/models_cache.json (info.context_window). Hardcode here so
+      ;; the TUI ctx% bar has a value even before models_cache.json is read.
+      ((string-contains mid "grok-build")               512000)
       ((string-contains mid "qwen3")                    32768)
       ((string-contains mid "llama-3")                  131072)
       ((string-contains mid "deepseek")                 131072)
diff --git a/test/run.ss b/test/run.ss
index eb696a5..db1101d 100644
--- a/test/run.ss
+++ b/test/run.ss
@@ -1668,6 +1668,30 @@
 (check! "detect: nothing keyed → #f"
         (detect-provider-from-list *detect-provider-order* (lambda (p) #f)) #f)
 
+;; ── models: grok metadata ─────────────────────────────────────────
+;; provider-default-models for "grok" returns whatever
+;; ~/.grok/models_cache.json holds, falling back to grok-models-fallback.
+;; Tests assert "grok-build" is present in both paths without depending on
+;; the user's actual ~/.grok contents.
+
+(section "=== models: grok metadata ===")
+
+(check-pred! "all-providers contains grok"        (all-providers) (lambda (l) (member "grok" l)))
+(check-pred! "all-providers still contains groq"  (all-providers) (lambda (l) (member "groq" l)))
+(check-pred! "all-providers still contains xai"   (all-providers) (lambda (l) (member "xai" l)))
+
+(check! "display name grok = Grok CLI"       (provider-display-name "grok") "Grok CLI")
+(check! "display name xai unchanged"         (provider-display-name "xai")  "xAI (Grok)")
+(check! "default model grok = grok-build"    (provider-default-model "grok") "grok-build")
+(check! "default model xai unchanged"        (provider-default-model "xai") "grok-3-mini")
+
+(check-pred! "default models grok includes grok-build"
+  (provider-default-models "grok")
+  (lambda (ms) (assoc "grok-build" ms)))
+
+(check! "ctx window grok-build = 512000" (model-context-window "grok-build") 512000)
+(check! "ctx window grok-3 unaffected (no clause)" (model-context-window "grok-3") #f)
+
 ;; ── Results ───────────────────────────────────────────────────────
 
 (printf "~n~a passed, ~a failed~n" pass-count fail-count)