From 333cb9a37094ff50db6dfa61aeac1ffa35171856 Mon Sep 17 00:00:00 2001 From: Gilles Ceyssat Date: Tue, 12 May 2026 07:53:56 +0400 Subject: [PATCH] feat(cli): wire confirm_callback for `jarvis agents ask` (with --yes/--no-yes) `jarvis agents ask` is a non-interactive CLI path, so the AgentExecutor's ToolExecutor never had a confirm_callback wired. Tools whose ToolSpec sets requires_confirmation=True (shell_exec, git_*, ...) returned "requires confirmation but no confirmation callback is available" and the agent relayed that back as natural language, never executing. This adds a --yes/--no-yes flag (default --yes): - --yes: auto-approve (lambda _prompt: True). Suited for CLI runs where the operator already authorized the engagement scope. - --no-yes: prompt on TTY via click.confirm. The callback is set on the AgentExecutor itself; executor.py:_invoke_agent reads it and forwards it to the constructed agent through agent_kwargs (both `interactive=True` and `confirm_callback=...`). --- src/openjarvis/cli/agent_cmd.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/openjarvis/cli/agent_cmd.py b/src/openjarvis/cli/agent_cmd.py index db24144c..31724ef5 100644 --- a/src/openjarvis/cli/agent_cmd.py +++ b/src/openjarvis/cli/agent_cmd.py @@ -702,16 +702,34 @@ def errors(): @agent.command("ask") @click.argument("agent_id") @click.argument("message") -def ask(agent_id, message): +@click.option( + "--yes/--no-yes", + "auto_approve", + default=True, + help="Auto-approve tool execution that would otherwise need confirmation. " + "Default: on (suits non-interactive CLI use). Pass --no-yes to require a " + "TTY prompt for tools whose ToolSpec sets requires_confirmation=True.", +) +def ask(agent_id, message, auto_approve): """Ask an agent a question (immediate response).""" manager = _get_manager() agent_id = _resolve_agent_id(manager, agent_id) manager.send_message(agent_id, message, mode="immediate") click.echo("Asking agent...") - _, executor, _ = _get_scheduler_and_executor() + _, executor, system = _get_scheduler_and_executor() if executor is None: click.echo("Executor not available", err=True) raise SystemExit(1) + # Wire a confirmation callback so the agent's own ToolExecutor can actually + # run tools whose ToolSpec sets requires_confirmation=True (e.g. shell_exec, + # git_*). `executor` is the AgentExecutor; the callback is read in + # _invoke_agent and propagated to the constructed agent via agent_kwargs. + if auto_approve: + executor._confirm_callback = lambda _prompt: True + else: + executor._confirm_callback = ( + lambda prompt: click.confirm(f"\n{prompt}", default=False) + ) executor.execute_tick(agent_id) msgs = manager.list_messages(agent_id) responses = [m for m in msgs if m["direction"] == "agent_to_user"]