fix: external CLI tabs use --resume for subsequent turns
ober
96d644eae214ef6dc37b6afd73b5060d2cf012b1
--- 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 ---------- --- 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