tools: runtime tool ACL — /tool off/on commands (session-only)

ober

a3bfa9a561dffa052ef10d7d7dbc97465c08a8dc

diff --git a/src/jcode/tool/registry.ss b/src/jcode/tool/registry.ss
index dd9a139..5e80dc6 100644
--- a/src/jcode/tool/registry.ss
+++ b/src/jcode/tool/registry.ss
@@ -19,7 +19,10 @@
         set-tool-internal!
         tool-exists?
         tool-primary-arg
-        tool-signature)
+        tool-signature
+        user-disable-tool!
+        user-enable-tool!
+        user-disabled-tools-list)
 
 (import :std/text/json
         :std/misc/string
@@ -38,6 +41,7 @@
 ;;          AND rejected at execute time as a safety net.
 (def current-mode (make-parameter 'build))
 (def current-disabled-tools (make-parameter '()))
+(def *user-disabled-tools* (make-parameter '()))
 (def current-tool-allowlist (make-parameter #f))
 (def current-compact-tool-schemas (make-parameter #f))
 
@@ -55,9 +59,19 @@
       (not (write-tool-name? name))))
 
 (def (tool-disabled? name)
-  (let ((allow (current-tool-allowlist)))
+  (let ((allow (current-tool-allowlist))
+        (user-disabled (*user-disabled-tools*)))
     (or (and (member name (current-disabled-tools)) #t)
+        (and (member name user-disabled) #t)
         (and allow (not (member name allow))))))
+(def (user-disable-tool! name)
+  (let ((lst (*user-disabled-tools*)))
+    (unless (member name lst)
+      (*user-disabled-tools* (cons name lst)))))
+(def (user-enable-tool! name)
+  (*user-disabled-tools* (remove (lambda (x) (equal? x name)) (*user-disabled-tools*))))
+(def (user-disabled-tools-list)
+  (*user-disabled-tools*))
 
 (def (mode-blocked-message name)
   (format "Tool '~a' is blocked in PLAN mode (read-only). Switch to BUILD mode with /build to run write tools."
diff --git a/src/jcode/ui/tui.ss b/src/jcode/ui/tui.ss
index 7e1a506..02fce9e 100644
--- a/src/jcode/ui/tui.ss
+++ b/src/jcode/ui/tui.ss
@@ -802,6 +802,8 @@
                "  /doit [on|off|status|max N| max unlimited]"
                "              Local-only auto-continue mode for long tasks"
                "  /tools      List available tools"
+                "  /tool off <name>  Disable a tool for this session"
+                "  /tool on <name>   Re-enable a disabled tool"
                "  /agents     List named sub-agent roles (task tool)"
                "  /clear      Start new session"
                "  /compact    Summarize older turns to free up context"
@@ -854,6 +856,16 @@
       ((equal? cmd "tools")
        (add-message! state
          (msg-block-system (string-append "Tools: " (string-join (list-tools) ", ")))))
+((string-prefix? "tool off " cmd)
+       (let* ((name (string-trim (substring cmd 9 (string-length cmd)))))
+         (user-disable-tool! name)
+         (add-message! state
+           (msg-block-system (string-append "Tool '" name "' disabled for this session.")))))
+      ((string-prefix? "tool on " cmd)
+       (let* ((name (string-trim (substring cmd 8 (string-length cmd)))))
+         (user-enable-tool! name)
+         (add-message! state
+           (msg-block-system (string-append "Tool '" name "' re-enabled.")))))
       ((equal? cmd "plan")
        (set-mode! state 'plan))
       ((equal? cmd "build")