fix: external CLI tabs use --resume for subsequent turns

ober

96d644eae214ef6dc37b6afd73b5060d2cf012b1

diff --git a/src/jcode/tool/external-llm.ss b/src/jcode/tool/external-llm.ss
index d6268d6..0f2d83f 100644
--- a/src/jcode/tool/external-llm.ss
+++ b/src/jcode/tool/external-llm.ss
@@ -252,30 +252,44 @@
   (and (string? s) (not (string=? s "")) s))
 
 ;; (provider-spec-session name session-id prompt)
-;;   Returns (label argv auth-paths parse-fn) for a sessioned turn.
+;;   Returns (label argv auth-paths parse-fn effective-sid) for a sessioned
+;;   turn. effective-sid is the UUID we chose for this invocation (used as
+;;   a fallback when the CLI's JSON output doesn't echo session_id).
+;;
+;;   claude/gemini: --session-id creates a session; reusing the same UUID
+;;   that way errors with "Session ID X is already in use." Subsequent
+;;   turns must use --resume <uuid>.
 (def (provider-spec-session name session-id prompt)
   (case name
     ((claude)
-     (list "claude"
-           (list "claude" "-p" prompt
-                 "--session-id" (or (nonempty-string session-id)
-                                    (make-external-session-id))
-                 "--output-format" "json"
-                 "--dangerously-skip-permissions")
-           (list (path-join (home) ".claude")
-                 (path-join (home) ".claude.json")
-                 (path-join (home) "Library/Application Support/claude"))
-           parse-claude-json))
+     (let ((resume? (nonempty-string session-id)))
+       (let ((eff-sid (or resume? (make-external-session-id))))
+         (list "claude"
+               (append (list "claude" "-p" prompt)
+                       (if resume?
+                         (list "--resume" eff-sid)
+                         (list "--session-id" eff-sid))
+                       (list "--output-format" "json"
+                             "--dangerously-skip-permissions"))
+               (list (path-join (home) ".claude")
+                     (path-join (home) ".claude.json")
+                     (path-join (home) "Library/Application Support/claude"))
+               parse-claude-json
+               eff-sid))))
     ((gemini)
-     (list "gemini"
-           (list "gemini" "-p" prompt
-                 "--session-id" (or (nonempty-string session-id)
-                                    (make-external-session-id))
-                 "-o" "json"
-                 "--yolo" "--skip-trust")
-           (list (path-join (home) ".gemini")
-                 (path-join (home) ".config/gemini"))
-           parse-gemini-json))
+     (let ((resume? (nonempty-string session-id)))
+       (let ((eff-sid (or resume? (make-external-session-id))))
+         (list "gemini"
+               (append (list "gemini" "-p" prompt)
+                       (if resume?
+                         (list "--resume" eff-sid)
+                         (list "--session-id" eff-sid))
+                       (list "-o" "json"
+                             "--yolo" "--skip-trust"))
+               (list (path-join (home) ".gemini")
+                     (path-join (home) ".config/gemini"))
+               parse-gemini-json
+               eff-sid))))
     ((codex)
      (list "codex"
            (if (nonempty-string session-id)
@@ -287,7 +301,8 @@
                    "--skip-git-repo-check" "--json" prompt))
            (list (path-join (home) ".codex")
                  (path-join (home) ".config/codex"))
-           parse-codex-jsonl))
+           parse-codex-jsonl
+           session-id))
     ((opencode)
      (list "opencode"
            (if (nonempty-string session-id)
@@ -298,7 +313,8 @@
            (list (path-join (home) ".config/opencode")
                  (path-join (home) ".local/share/opencode")
                  (path-join (home) ".cache/opencode"))
-           parse-opencode-jsonl))
+           parse-opencode-jsonl
+           session-id))
     (else #f)))
 
 (def (ask-external-llm-session provider prompt session-id)
@@ -324,6 +340,8 @@
          (argv     (cadr spec))
          (auth     (caddr spec))
          (parse-fn (cadddr spec))
+         (eff-sid  (and (> (length spec) 4) (list-ref spec 4)))
+         (sid-fallback (or eff-sid session-id))
          (chosen   (string->symbol label))
          (cwd      (current-directory))
          (tmp-out  (path-join "/tmp" (format "jcode-tab-~a.log" label)))
@@ -352,11 +370,11 @@
       (let ((text (read-text-or-empty tmp-out)))
         (cond
           ((eqv? status 0)
-           (parse-fn text session-id))
+           (parse-fn text sid-fallback))
           (else
            (make-ext-result
              (format "ERROR: ~a exited with status ~a\n\n~a" label status text)
-             #t session-id 0 0 0.0)))))))
+             #t sid-fallback 0 0 0.0)))))))
 
 ;; ---------- per-provider parsers ----------
 
diff --git a/src/jcode/ui/tui.ss b/src/jcode/ui/tui.ss
index faae67d..f9b255c 100644
--- a/src/jcode/ui/tui.ss
+++ b/src/jcode/ui/tui.ss
@@ -1565,12 +1565,6 @@
 
 (def *external-providers* '(claude codex gemini opencode))
 
-(def (provider-needs-uuid? provider)
-  ;; Claude and Gemini accept a UUID we generate. Codex and Opencode
-  ;; assign their own session id; we learn it from the first turn's
-  ;; JSON output and pass it forward.
-  (memq provider '(claude gemini)))
-
 (def (current-tab-provider state)
   ;; Returns provider symbol of the active tab, or #f for main.
   (let ((tabs (app-state-tabs state))
@@ -1680,9 +1674,7 @@
       (else
        (let* ((new-tab (make-tab
                          provider
-                         (if (provider-needs-uuid? provider)
-                           (make-external-session-id)
-                           #f)
+                         #f                          ;; session-id (assigned on first successful turn)
                          '()                         ;; empty transcript
                          (make-fresh-input)
                          #f 0 "" 0 0 0 0 0.0