Skip to content

feat(tools): let a tool supply the hint and payload of its confirmation request - #7798

Open
inamdarmihir wants to merge 1 commit into
google:mainfrom
inamdarmihir:feat/7737-tool-confirmation-hook
Open

inamdarmihir wants to merge 1 commit into
google:mainfrom
inamdarmihir:feat/7737-tool-confirmation-hook

Conversation

@inamdarmihir

Copy link
Copy Markdown
Contributor

Link to Issue or Description of Change

1. Link to an existing issue (if applicable):

Problem:
Since 2.10.0 the framework gate (apply_confirmation_gate) holds any call whose check_require_confirmation is true before run_async runs, always with a fixed generic hint and no payload. A tool can no longer tell the user what they are approving: a FunctionTool subclass that used to call request_confirmation(hint=..., payload=...) from run_async now never gets there, and the client silently receives the generic text and payload=None. The only alternative (calling request_confirmation inside the tool) moves enforcement back into every tool and is unavailable for tools you don't write.

Solution:
Add an optional hook on BaseTool:

async def get_confirmation_request(self, args, tool_context) -> ConfirmationRequest

ConfirmationRequest (new, in tool_confirmation.py) carries hint and a JSON-serializable payload. The gate, and the run_async fallback paths of FunctionTool and McpTool, request confirmation with what the hook returns. The default reproduces today's exact hint with payload=None, so existing tools are unchanged. The hook is only called when the call is gated and not yet answered (never for approved, rejected or ungated calls). Enforcement is unchanged: the call still runs only once approved, whatever the hook returns. If the hook raises, the call is neither run nor recorded as awaiting confirmation and the error is handled like any tool error (fail closed, never skipping the gate). The workflow ToolNode picks it up through the shared gate.

Left out on purpose to keep the API small: a FunctionTool(...) constructor shortcut, feeding the hook from a require_confirmation callable, an McpToolset constructor argument, and a rejection-text hook. A subclass override covers FunctionTool, McpTool and any BaseTool.

Testing Plan

Unit Tests:

  • I have added or updated unit tests for my change.
  • All unit tests pass locally.

Tests cover: default hint with payload=None; custom hint, nested payload, payload with empty hint; the hook receives the call args and the same tool_context; hook not called for ungated, approved or rejected calls; hook raising records nothing, does not run the tool, and reaches on_tool_error_callback; approval and rejection resume (single agent and resumable SequentialAgent) end to end through Runner; FunctionTool.run_async called directly; McpTool through the gate and run_async; ToolNode hint/payload through to approval. With the source change stashed, the touched test modules fail at import.

pytest tests/unittests/tools tests/unittests/flows/llm_flows/tools \
  tests/unittests/runners/test_run_tool_confirmation.py tests/unittests/workflow/test_tool_node.py
3777 passed, 1 skipped

Manual End-to-End (E2E) Tests:

Throwaway script: a gated tool overriding the hook emits an adk_request_confirmation call carrying its hint and payload; a plain gated FunctionTool still emits the generic hint.

Checklist

  • I have read the CONTRIBUTING.md document.
  • I have performed a self-review of my own code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.
  • I have manually tested my changes end-to-end.
  • Any dependent changes have been merged and published in downstream modules.

Additional context

The tool-confirmation docs (docs/guides/tools/tool_confirmation, base_tool) are updated. The hook name follows the sketch in the issue; happy to rename.

…on request

The framework confirmation gate always asked with a fixed generic hint and
no payload, so a tool gated through require_confirmation or
check_require_confirmation could not tell the user what they were approving.

Add BaseTool.get_confirmation_request(args, tool_context), returning a new
ConfirmationRequest(hint, payload). The gate, and the run_async fallbacks of
FunctionTool and McpTool, now request confirmation with what it returns. The
default reproduces the previous hint with no payload, so nothing changes for
existing tools. Enforcement of the answer is unchanged, and a hook that raises
fails the call instead of letting it through.

Fixes google#7737
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Let tools supply the hint and payload for the framework tool-confirmation gate (require_confirmation)

2 participants