Skip to content

fix(core): enforce persona tool boundaries#8786

Open
he-yufeng wants to merge 1 commit into
AstrBotDevs:masterfrom
he-yufeng:fix/persona-late-tool-policy
Open

fix(core): enforce persona tool boundaries#8786
he-yufeng wants to merge 1 commit into
AstrBotDevs:masterfrom
he-yufeng:fix/persona-late-tool-policy

Conversation

@he-yufeng

@he-yufeng he-yufeng commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Summary

  • keep subagent tools=null aligned with the normal tool manager path so non-builtin tools stay permission-guarded
  • enforce an explicit persona tool allowlist after late tool injections such as web search, cron tools, and proactive messaging
  • add regressions for both the subagent default-tool path and persona tools=[] late-injection path

Fixes #8780.
Fixes #8781.

To verify

  • python -m py_compile astrbot/core/astr_agent_tool_exec.py astrbot/core/astr_main_agent.py tests/unit/test_astr_agent_tool_exec.py tests/unit/test_astr_main_agent.py
  • python -m pytest tests/unit/test_astr_agent_tool_exec.py::test_build_handoff_toolset_keeps_permission_guards_for_default_tools tests/unit/test_astr_main_agent.py::TestEnsurePersonaAndSkills::test_persona_empty_tools_filters_late_builtin_tools -q
  • python -m pytest tests/unit/test_astr_agent_tool_exec.py -q
  • python -m pytest tests/unit/test_astr_main_agent.py -q -k "persona or web_search or proactive_cron or plugin_tool_fix"
  • python -m ruff check astrbot/core/astr_agent_tool_exec.py astrbot/core/astr_main_agent.py tests/unit/test_astr_agent_tool_exec.py tests/unit/test_astr_main_agent.py
  • python -m ruff format --check astrbot/core/astr_agent_tool_exec.py astrbot/core/astr_main_agent.py tests/unit/test_astr_agent_tool_exec.py tests/unit/test_astr_main_agent.py
  • git diff --check

Note

A full local tests/unit/test_astr_main_agent.py run still hits unrelated Windows path-separator expectations in the existing video attachment tests. The targeted persona, tool injection, handoff, ruff, py_compile, and whitespace checks above pass.

Summary by Sourcery

Enforce persona tool restrictions and preserve permission guards when building toolsets for main agents and subagents.

Bug Fixes:

  • Ensure personas with an explicit empty tools list block late-injected builtin tools such as web search and cron utilities.
  • Prevent subagent handoff toolsets built with default tools from bypassing permission guards on non-builtin tools.

Tests:

  • Add regression tests covering handoff toolset construction with default tools to ensure permission guards are preserved.
  • Add regression tests verifying that personas with an empty tool list correctly filter out late-injected builtin tools.

@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. area:core The bug / feature is about astrbot's core, backend feature:persona The bug / feature is about astrbot AI persona system (system prompt) labels Jun 14, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request refactors tool filtering and handoff toolset building to ensure permission guards are preserved and persona-specific tool restrictions are correctly applied. Specifically, _build_handoff_toolset now retrieves tools from the full toolset to maintain permission guards, and build_main_agent filters out tools not allowed by the selected persona. Additionally, corresponding unit tests have been added. Feedback on the changes includes a recommendation to optimize the tool filtering logic in build_main_agent to O(N) complexity and a suggestion to add a null check in the new unit test's finally block to prevent AttributeError from masking assertion failures.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

result.provider_request.func_tool.empty()
)
finally:
if result.reset_coro:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

If build_main_agent returns None (e.g., due to a configuration or provider resolution failure), result will be None. In the finally block, attempting to access result.reset_coro will raise an AttributeError, which masks the actual test assertion failure (assert result is not None). Adding a null check for result prevents this masking.

Suggested change
if result.reset_coro:
if result and result.reset_coro:

Comment on lines +1550 to +1553
if persona_allowed_tools is not None and req.func_tool:
for tool in list(req.func_tool):
if tool.name not in persona_allowed_tools:
req.func_tool.remove_tool(tool.name)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Instead of iterating over the tools and calling remove_tool (which performs a list comprehension internally on every call, leading to $O(N^2)$ complexity), you can filter the tools list in-place in a single $O(N)$ pass.

Suggested change
if persona_allowed_tools is not None and req.func_tool:
for tool in list(req.func_tool):
if tool.name not in persona_allowed_tools:
req.func_tool.remove_tool(tool.name)
if persona_allowed_tools is not None and req.func_tool:
req.func_tool.tools = [
tool for tool in req.func_tool.tools
if tool.name in persona_allowed_tools
]

@he-yufeng

Copy link
Copy Markdown
Contributor Author

The failing unit job is the FastAPI 0.137 route-introspection issue, not this patch.

The log has the same two failures fixed separately in #8783:

  • tests/test_fastapi_v1_dashboard.py::test_v1_openapi_alias_websocket_routes_are_mounted
  • tests/test_fastapi_v1_dashboard.py::test_dashboard_config_aliases_are_registered_on_fastapi

Both fail because FastAPI now leaves an _IncludedRouter entry in asgi_app.router.routes, so the old test code either misses websocket aliases or reads .path from an object that does not expose it.

#8783 is scoped to that test compatibility issue and its full CI is green. I am keeping this branch focused on the persona/tool-boundary fix instead of mixing the unrelated test stabilization into it.

@zouyonghe

Copy link
Copy Markdown
Member

Reviewed the diff for #8780/#8781. The approach looks focused: default subagent toolsets now keep permission guards, and persona explicit allowlists are re-applied after late tool injection. The added regressions cover both reported bypass paths. I did not find a blocking issue in the patch.

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

Labels

area:core The bug / feature is about astrbot's core, backend feature:persona The bug / feature is about astrbot AI persona system (system prompt) size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

2 participants