TUI: capture Chez parameters across spawn in tool execution

ober

e46e348eab50b6f8bc0eaaaff8bd0459279d2d16

diff --git a/src/jcode/core/agent.ss b/src/jcode/core/agent.ss
index cf46246..5aeff0e 100644
--- a/src/jcode/core/agent.ss
+++ b/src/jcode/core/agent.ss
@@ -285,11 +285,24 @@ Prefer using the edit tool over write for modifying existing files."
   (log-info logger "executing-tools" `((count . ,(length tool-calls))))
   (if (<= (length tool-calls) 1)
     (map execute-single-tool tool-calls)
-    ;; Parallel: spawn a green thread per tool call, join all
-    (let ((threads (map (lambda (tc)
-                          (spawn (lambda () (execute-single-tool tc))))
-                        tool-calls)))
-      (map thread-join! threads))))
+    ;; Parallel: spawn a green thread per tool call, join all.
+    ;; Capture parameters before spawn — Chez parameters are thread-local
+    ;; and don't inherit. Without this, current-error-port defaults to
+    ;; real stderr (TTY in raw mode under TUI); log-debug writes back up
+    ;; the TTY buffer and deadlock the log mutex. Same class of bug as
+    ;; commit 0a3a04d (TUI worker thread).
+    (let ((err-port (current-error-port))
+          (log-lvl  (current-log-level))
+          (tool-cb  (current-tool-cb)))
+      (let ((threads (map (lambda (tc)
+                            (spawn (lambda ()
+                                     (parameterize
+                                       ((current-error-port err-port)
+                                        (current-log-level log-lvl)
+                                        (current-tool-cb tool-cb))
+                                       (execute-single-tool tc)))))
+                          tool-calls)))
+        (map thread-join! threads)))))
 
 (def (execute-single-tool tc)
   (let* ((name (tool-call-name tc))
diff --git a/src/jcode/tool/batch.ss b/src/jcode/tool/batch.ss
index afa6a46..0624681 100644
--- a/src/jcode/tool/batch.ss
+++ b/src/jcode/tool/batch.ss
@@ -25,7 +25,13 @@
       ((not (list? calls))
        "Error: 'calls' must be an array of tool call objects")
       (else
-       (let ((results
+       ;; Capture parameters before spawn — Chez parameters are thread-local
+       ;; and don't inherit. Without this, current-error-port defaults to real
+       ;; stderr (TTY in raw mode under TUI), and any log call writes back up
+       ;; the TTY buffer, deadlocking the log mutex. See agent.ss execute-tool-calls.
+       (let* ((err-port (current-error-port))
+              (log-lvl  (current-log-level))
+              (results
                (let ((threads
                        (map (lambda (call)
                               (let ((tool-name (and (hash-table? call) (hash-get call "tool")))
@@ -33,8 +39,16 @@
                                                     (or (hash-get call "args")
                                                         (make-hash-table)))))
                                 (if tool-name
-                                  (spawn (lambda () (cons tool-name (tool-execute tool-name tool-args))))
-                                  (spawn (lambda () (cons "?" "Error: missing 'tool' field"))))))
+                                  (spawn (lambda ()
+                                           (parameterize
+                                             ((current-error-port err-port)
+                                              (current-log-level log-lvl))
+                                             (cons tool-name (tool-execute tool-name tool-args)))))
+                                  (spawn (lambda ()
+                                           (parameterize
+                                             ((current-error-port err-port)
+                                              (current-log-level log-lvl))
+                                             (cons "?" "Error: missing 'tool' field")))))))
                             calls)))
                  (map thread-join! threads))))
          (string-join