feat(plugins): tool override flag for replacing built-in tools (closes #11049) (#26759)

Plugins can now replace a built-in tool by passing override=True to
ctx.register_tool(). Without it, the registry rejects any registration
that would shadow an existing tool from a different toolset (unchanged
default behavior).

Unlocks the use case from #11049: drop-in replacement of browser/web
backends without forking core. Composes with the existing pre_tool_call
hook for runtime interception of any implementation.

The override is audit-logged at INFO so it surfaces in agent.log.
This commit is contained in:
Teknium 2026-05-15 22:12:57 -07:00 committed by GitHub
parent 9c304a7f56
commit 016c772e7f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 180 additions and 5 deletions

View file

@ -325,8 +325,15 @@ class PluginContext:
is_async: bool = False,
description: str = "",
emoji: str = "",
override: bool = False,
) -> None:
"""Register a tool in the global registry **and** track it as plugin-provided."""
"""Register a tool in the global registry **and** track it as plugin-provided.
Pass ``override=True`` to replace an existing built-in tool with the
same name (e.g. swap the default ``browser_navigate`` for a custom
CDP-backed implementation). Without it, attempting to register a name
already claimed by a different toolset is rejected.
"""
from tools.registry import registry
registry.register(
@ -339,9 +346,13 @@ class PluginContext:
is_async=is_async,
description=description,
emoji=emoji,
override=override,
)
self._manager._plugin_tool_names.add(name)
logger.debug("Plugin %s registered tool: %s", self.manifest.name, name)
logger.debug(
"Plugin %s registered tool: %s%s",
self.manifest.name, name, " (override)" if override else "",
)
# -- message injection --------------------------------------------------