feat(gptel): implement safe Eval tool for AI agent

This commit is contained in:
kj
2026-08-05 23:46:18 -03:00
parent b5b4fb34af
commit 9a64a40a3b

View File

@@ -442,6 +442,62 @@ Tries `patch -p1' first and falls back to `-p0'."
(let ((tool (gptel-get-tool name)))
(when tool
(setf (gptel-tool-confirm tool) nil))))
;; Sobrescribir la tool "Eval" para que sea segura: con `inhibit-interaction',
;; las llamadas interactivas (read-*, y-or-n-p, call-interactively, ...)
;; lanzan error en vez de quedarse esperando input y colgar al agente.
(gptel-make-tool
:name "Eval"
:function
(lambda (expression)
(let ((standard-output (generate-new-buffer " *gptel-agent-eval-elisp*"))
(result nil) (output nil))
(unwind-protect
(condition-case err
(progn
(let ((inhibit-interaction t))
(setq result (eval (read expression) t)))
(when (> (buffer-size standard-output) 0)
(setq output (with-current-buffer standard-output (buffer-string))))
(concat
(format "Result:\n%S" result)
(and output (format "\n\nSTDOUT:\n%s" output))))
((error user-error)
(concat
(if (eq (car err) 'inhibited-interaction)
"Error: la expresión intentó pedir entrada al usuario de forma interactiva, lo cual está bloqueado en la tool Eval."
(format "Error: eval failed with error %S: %S" (car err) (cdr err)))
(and output (format "\n\nSTDOUT:\n%s" output)))))
(kill-buffer standard-output))))
:description "Evaluate Elisp EXPRESSION and return result and any printed output.
EXPRESSION can be anything to evaluate. It can be a function call, a
variable, a quasi-quoted expression. The only requirement is that only
the first sexp will be read and evaluated, so if you need to evaluate
multiple expressions, make one call per expression. Do not combine
expressions using progn etc. Just go expression by expression and try
to make standalone single expressions.
Interactive operations are blocked: if the expression tries to prompt
for input (read-*, y-or-n-p, call-interactively, etc.) it will error
instead of waiting.
The return value is formatted to a string using %S, so a string will be
returned as an escaped embedded string and literal forms will be
compatible with `read' where possible. Some forms have no printed
representation that can be read and will be represented with
#<hash-notation> instead.
Output from `print', `prin1', and `princ' is captured and returned as STDOUT.
Use `print' for diagnostic output, not `message' (which goes to *Messages*
and is not captured)."
:args '((:name "expression"
:type string
:description "A single elisp sexp to evaluate."))
:category "gptel-agent"
:confirm t
:include t)
(gptel-agent-update)
;; Herramientas del agente (equivalente a las tools de opencode)