Skip to content

Comments

feat: implement tool call approval mechanism with dynamic code strategy#5398

Draft
Soulter wants to merge 1 commit intomasterfrom
feat/tool-call-approval-1
Draft

feat: implement tool call approval mechanism with dynamic code strategy#5398
Soulter wants to merge 1 commit intomasterfrom
feat/tool-call-approval-1

Conversation

@Soulter
Copy link
Member

@Soulter Soulter commented Feb 24, 2026

Modifications / 改动点

  • This is NOT a breaking change. / 这不是一个破坏性变更。

Screenshots or Test Results / 运行截图或测试结果


Checklist / 检查清单

  • 😊 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。/ If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
  • 👀 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”。/ My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
  • 🤓 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到了 requirements.txtpyproject.toml 文件相应位置。/ I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in requirements.txt and pyproject.toml.
  • 😮 我的更改没有引入恶意代码。/ My changes do not introduce malicious code.

Summary by Sourcery

在智能体流水线中添加可配置的工具调用审批机制,使用动态代码确认策略。

New Features:

  • 引入带有动态代码策略的工具调用审批配置,包括超时时间和代码格式选项。
  • 将工具调用审批检查集成到工具执行循环中,使某些工具调用在运行前需要用户确认。

Enhancements:

  • 在智能体构建和运行上下文中传播工具调用审批设置,以支持运行时配置。
  • 扩展聊天提供方的配置模式和 UI 元数据,以向用户开放工具调用审批选项。

Tests:

  • 添加异步测试,覆盖以下情况:审批关闭、动态代码审批成功与被拒绝,以及不支持策略的处理。
Original summary in English

Summary by Sourcery

Add configurable tool call approval to the agent pipeline using a dynamic code confirmation strategy.

New Features:

  • Introduce a tool call approval configuration with a dynamic code strategy, including timeout and code format options.
  • Integrate tool call approval checks into the tool execution loop so certain tool calls require user confirmation before running.

Enhancements:

  • Propagate tool call approval settings through agent build and run context to support runtime configuration.
  • Extend chat provider configuration schema and UI metadata to expose tool call approval options to users.

Tests:

  • Add async tests covering disabled approval, successful and rejected dynamic code approvals, and unsupported strategy handling.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @Soulter, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a robust mechanism for approving tool calls made by the agent. This feature provides an additional layer of security and user control by requiring explicit confirmation before potentially sensitive or impactful agent actions are executed. The primary implementation uses a dynamic code strategy, prompting the user to enter a generated code to proceed with a tool call, and includes comprehensive configuration options to tailor its behavior.

Highlights

  • Tool Call Approval Mechanism: Implemented a new feature that requires user approval before an agent can execute a tool call, enhancing security and control.
  • Dynamic Code Strategy: Introduced a 'dynamic_code' approval strategy where a unique code is generated and sent to the user, who must then input it to approve the tool call.
  • Configurable Approval Settings: Added extensive configuration options for the tool call approval, including enabling/disabling the feature, selecting approval strategies, setting timeouts, and customizing dynamic code parameters (length, case sensitivity).
  • Integration into Agent Workflow: The approval logic is seamlessly integrated into the agent's run context and tool execution loop, ensuring that tool calls are intercepted for approval when enabled.
Changelog
  • astrbot/core/agent/run_context.py
    • Added 'tool_call_approval' field to 'ContextWrapper' to store runtime configuration for tool call approval.
  • astrbot/core/agent/runners/tool_loop_agent_runner.py
    • Imported necessary classes for tool call approval ('ToolCallApprovalContext', 'request_tool_call_approval').
    • Integrated tool call approval logic into the '_handle_function_tools' method, checking for approval before executing tools and handling various approval outcomes (approved, rejected, timeout, unsupported strategy, error).
  • astrbot/core/agent/tool_call_approval.py
    • Added a new file defining the core logic for tool call approval.
    • Introduced 'ToolCallApprovalContext' and 'ToolCallApprovalResult' data classes.
    • Defined 'BaseToolCallApprovalStrategy' abstract base class for extensible approval methods.
    • Implemented 'DynamicCodeApprovalStrategy' which generates a random code and waits for user input for approval.
    • Included utility functions for registering strategies and requesting approval based on configuration.
  • astrbot/core/astr_main_agent.py
    • Added 'tool_call_approval' field to 'MainAgentBuildConfig' to hold configuration for the approval mechanism.
    • Passed the 'tool_call_approval' configuration from 'MainAgentBuildConfig' to the 'AgentContextWrapper' during agent initialization.
  • astrbot/core/config/default.py
    • Added default configuration for 'tool_call_approval', including 'enable', 'strategy', 'timeout', and 'dynamic_code' settings.
    • Updated the 'ChatProviderTemplate' schema to include validation and descriptions for the new 'tool_call_approval' configuration fields.
  • astrbot/core/pipeline/process_stage/method/agent_sub_stages/internal.py
    • Added 'tool_call_approval' attribute to 'InternalAgentSubStage' to store the approval configuration.
    • Passed the 'tool_call_approval' configuration to 'MainAgentBuildConfig' during the agent's build process.
  • tests/test_tool_call_approval.py
    • Added a new test file to cover the functionality of the tool call approval mechanism.
    • Included tests for disabled approval, successful dynamic code approval, rejected dynamic code approval, and unsupported approval strategies.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request introduces a tool call approval mechanism with a dynamic code strategy, which aims to enhance security by requiring user confirmation before executing certain tool calls. However, a critical security flaw has been identified in the implementation of the message handler within group chat scenarios. This flaw allows any user in a group to either approve a tool call (Broken Access Control) or accidentally/intentionally cancel it by sending any message (Denial of Service). It is strongly recommended to restrict the approval process to the original initiator of the request to mitigate these vulnerabilities.

Comment on lines +197 to +219
async def _handler(
controller: SessionController, incoming: AstrMessageEvent
) -> None:
raw_input = (incoming.message_str or "").strip()
if _is_code_match(
expected=expected_code,
actual=raw_input,
case_sensitive=case_sensitive,
):
if not controller.future.done():
controller.future.set_result(
ToolCallApprovalResult(approved=True, reason="approved"),
)
else:
if not controller.future.done():
controller.future.set_result(
ToolCallApprovalResult(
approved=False,
reason="rejected",
detail=raw_input,
)
)
controller.stop()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The tool call approval handler is vulnerable to Denial of Service (DoS) and Broken Access Control in group chat environments.

  1. Denial of Service: The handler automatically rejects the tool call if ANY message is received in the session that does not match the expected code. In a group chat, any message from any user will trigger this rejection, causing the tool call to be cancelled. This allows any member of the group to prevent the bot from executing tools.
  2. Broken Access Control: The mechanism does not verify the identity of the user providing the approval code. Since the code is sent to the group, any member can see it and send it back to approve the tool call, bypassing the intent of 'user approval'.

Remediation: Modify the _handler to verify that the sender of the message is the same user who initiated the tool call request. Messages from other users should be ignored.

    async def _handler(
        controller: SessionController, incoming: AstrMessageEvent
    ) -> None:
        if incoming.get_sender_id() != event.get_sender_id():
            return
        raw_input = (incoming.message_str or "").strip()
        if _is_code_match(
            expected=expected_code,
            actual=raw_input,
            case_sensitive=case_sensitive,
        ):
            if not controller.future.done():
                controller.future.set_result(
                    ToolCallApprovalResult(approved=True, reason="approved"),
                )
        else:
            if not controller.future.done():
                controller.future.set_result(
                    ToolCallApprovalResult(
                        approved=False,
                        reason="rejected",
                        detail=raw_input,
                    )
                )
        controller.stop()

timeout=timeout_seconds,
case_sensitive=case_sensitive,
)
except Exception as exc: # noqa: BLE001
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The except Exception as exc: # noqa: BLE001 is too broad. It's generally better to catch more specific exceptions to avoid masking unexpected errors. Consider catching more specific exceptions that _wait_for_code_input might raise, or at least asyncio.TimeoutError if that's the primary concern for register_wait.

if parsed < minimum:
return minimum
return parsed
except Exception: # noqa: BLE001
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to the previous comment, catching a broad Exception can hide bugs. It's better to catch ValueError or TypeError if int() conversion is the only expected failure, or specify (ValueError, TypeError) if both are possible. If other exceptions are possible, they should be handled explicitly.

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.

1 participant