feat(tools): centralize tool emoji metadata in registry + skin integration

- Add 'emoji' field to ToolEntry and 'get_emoji()' to ToolRegistry
- Add emoji= to all 50+ registry.register() calls across tool files
- Add get_tool_emoji() helper in agent/display.py with 3-tier resolution:
  skin override → registry default → hardcoded fallback
- Replace hardcoded emoji maps in run_agent.py, delegate_tool.py, and
  gateway/run.py with centralized get_tool_emoji() calls
- Add 'tool_emojis' field to SkinConfig so skins can override per-tool
  emojis (e.g. ares skin could use swords instead of wrenches)
- Add 11 tests (5 registry emoji, 6 display/skin integration)
- Update AGENTS.md skin docs table

Based on the approach from PR #1061 by ForgingAlex (emoji centralization
in registry). This salvage fixes several issues from the original:
- Does NOT split the cronjob tool (which would crash on missing schemas)
- Does NOT change image_generate toolset/requires_env/is_async
- Does NOT delete existing tests
- Completes the centralization (gateway/run.py was missed)
- Hooks into the skin system for full customizability
This commit is contained in:
teknium1 2026-03-15 20:21:21 -07:00
parent a56937735e
commit 210d5ade1e
30 changed files with 268 additions and 81 deletions

View file

@ -26,11 +26,11 @@ class ToolEntry:
__slots__ = (
"name", "toolset", "schema", "handler", "check_fn",
"requires_env", "is_async", "description",
"requires_env", "is_async", "description", "emoji",
)
def __init__(self, name, toolset, schema, handler, check_fn,
requires_env, is_async, description):
requires_env, is_async, description, emoji):
self.name = name
self.toolset = toolset
self.schema = schema
@ -39,6 +39,7 @@ class ToolEntry:
self.requires_env = requires_env
self.is_async = is_async
self.description = description
self.emoji = emoji
class ToolRegistry:
@ -62,6 +63,7 @@ class ToolRegistry:
requires_env: list = None,
is_async: bool = False,
description: str = "",
emoji: str = "",
):
"""Register a tool. Called at module-import time by each tool file."""
self._tools[name] = ToolEntry(
@ -73,6 +75,7 @@ class ToolRegistry:
requires_env=requires_env or [],
is_async=is_async,
description=description or schema.get("description", ""),
emoji=emoji,
)
if check_fn and toolset not in self._toolset_checks:
self._toolset_checks[toolset] = check_fn
@ -141,6 +144,11 @@ class ToolRegistry:
entry = self._tools.get(name)
return entry.toolset if entry else None
def get_emoji(self, name: str, default: str = "") -> str:
"""Return the emoji for a tool, or *default* if unset."""
entry = self._tools.get(name)
return (entry.emoji if entry and entry.emoji else default)
def get_tool_to_toolset_map(self) -> Dict[str, str]:
"""Return ``{tool_name: toolset_name}`` for every registered tool."""
return {name: e.toolset for name, e in self._tools.items()}