Problem
The ReAct loop uses an internal final_answer tool to signal that the agent has finished reasoning. When a TOOL_PRE_INVOKE allowlist plugin is active, it blocks final_answer because the developer didn't (and shouldn't need to) include framework-internal tools in their allowlist. The loop fails with a PluginViolationError that is not actionable from the developer's perspective.
Reproduction
ALLOWED_TOOLS = frozenset({"get_weather", "search"})
@hook(HookType.TOOL_PRE_INVOKE, mode=PluginMode.CONCURRENT, priority=5)
async def enforce_tool_allowlist(payload, _):
if payload.model_tool_call.name not in ALLOWED_TOOLS:
return block(f"Tool '{payload.model_tool_call.name}' is not permitted")
with start_session(plugins=[enforce_tool_allowlist]) as m:
out, _ = await react(goal="...", context=m.ctx, backend=m.backend, tools=tools)
# PluginViolationError: Tool 'final_answer' is not permitted
Expected behavior
Framework-internal tools like final_answer should not be subject to user-defined tool hooks by default. Allowlist plugins should only need to list the tools the developer explicitly provides.
Related
Part of #920. The second issue in that report (empty context in COMPONENT_PRE_EXECUTE) is tracked separately.
Problem
The ReAct loop uses an internal
final_answertool to signal that the agent has finished reasoning. When aTOOL_PRE_INVOKEallowlist plugin is active, it blocksfinal_answerbecause the developer didn't (and shouldn't need to) include framework-internal tools in their allowlist. The loop fails with aPluginViolationErrorthat is not actionable from the developer's perspective.Reproduction
Expected behavior
Framework-internal tools like
final_answershould not be subject to user-defined tool hooks by default. Allowlist plugins should only need to list the tools the developer explicitly provides.Related
Part of #920. The second issue in that report (empty context in
COMPONENT_PRE_EXECUTE) is tracked separately.