480 lines
21 KiB
Python
480 lines
21 KiB
Python
"""
|
|
Tool registration functions that create ToolMetadata and register all available tools.
|
|
"""
|
|
import os
|
|
from typing import Dict, Any, Callable
|
|
from .registry import ToolRegistry, ToolMetadata
|
|
from .base_tools import (
|
|
file_tools, system_tools, browser_tools, ai_tools,
|
|
memory_tools, skill_tools, task_tools
|
|
)
|
|
from .config_tools import config_tools
|
|
|
|
def create_tool_registry() -> ToolRegistry:
|
|
"""Create and populate a tool registry with all available tools."""
|
|
registry = ToolRegistry()
|
|
|
|
# Register file tools
|
|
_register_file_tools(registry, file_tools)
|
|
|
|
# Register system tools
|
|
_register_system_tools(registry, system_tools)
|
|
|
|
# Register browser tools
|
|
_register_browser_tools(registry, browser_tools)
|
|
|
|
# Register AI tools
|
|
_register_ai_tools(registry, ai_tools)
|
|
|
|
# Register memory tools
|
|
_register_memory_tools(registry, memory_tools)
|
|
|
|
# Register skill tools
|
|
_register_skill_tools(registry, skill_tools)
|
|
|
|
# Register task tools
|
|
_register_task_tools(registry, task_tools)
|
|
|
|
# Register config tools
|
|
_register_config_tools(registry, config_tools)
|
|
|
|
return registry
|
|
|
|
def _register_file_tools(registry: ToolRegistry, tools: Dict[str, Callable]):
|
|
"""Register file operation tools."""
|
|
# read_file
|
|
registry.register_tool(
|
|
'read_file',
|
|
tools['read_file'],
|
|
ToolMetadata(
|
|
name='read_file',
|
|
description='Read a text file with line numbers and pagination',
|
|
parameters={
|
|
'path': {'type': 'string', 'required': True, 'description': 'File path to read'},
|
|
'offset': {'type': 'integer', 'required': False, 'default': 1, 'description': 'Line number to start from (1-indexed)'},
|
|
'limit': {'type': 'integer', 'required': False, 'default': 500, 'description': 'Maximum number of lines to read'}
|
|
},
|
|
permissions=['file_read'],
|
|
examples=[
|
|
{'path': 'config.txt'},
|
|
{'path': 'logs/app.log', 'offset': 100, 'limit': 50}
|
|
],
|
|
security_notes='Cannot read files outside user work directory or system protected directories'
|
|
)
|
|
)
|
|
|
|
# write_file
|
|
registry.register_tool(
|
|
'write_file',
|
|
tools['write_file'],
|
|
ToolMetadata(
|
|
name='write_file',
|
|
description='Write content to a file, completely replacing existing content',
|
|
parameters={
|
|
'path': {'type': 'string', 'required': True, 'description': 'Path to the file to write'},
|
|
'content': {'type': 'string', 'required': True, 'description': 'Complete content to write to the file'}
|
|
},
|
|
permissions=['file_write'],
|
|
examples=[
|
|
{'path': 'output.txt', 'content': 'Hello World!'}
|
|
],
|
|
security_notes='Cannot write to system protected directories or outside user work directory'
|
|
)
|
|
)
|
|
|
|
# search_files
|
|
registry.register_tool(
|
|
'search_files',
|
|
tools['search_files'],
|
|
ToolMetadata(
|
|
name='search_files',
|
|
description='Search file contents or find files by name using ripgrep',
|
|
parameters={
|
|
'pattern': {'type': 'string', 'required': True, 'description': 'Regex pattern for content search, or glob pattern for file search'},
|
|
'target': {'type': 'string', 'required': False, 'default': 'content', 'description': "'content' searches inside files, 'files' searches for files by name"},
|
|
'path': {'type': 'string', 'required': False, 'default': '.', 'description': 'Directory or file to search in'},
|
|
'file_glob': {'type': 'string', 'required': False, 'description': 'Filter files by pattern in grep mode (e.g., "*.py")'},
|
|
'limit': {'type': 'integer', 'required': False, 'default': 50, 'description': 'Maximum number of results to return'}
|
|
},
|
|
permissions=['file_read'],
|
|
examples=[
|
|
{'pattern': 'TODO', 'target': 'content', 'path': './src'},
|
|
{'pattern': '*.py', 'target': 'files', 'path': './src'}
|
|
],
|
|
security_notes='Search is limited to user accessible directories'
|
|
)
|
|
)
|
|
|
|
# patch
|
|
registry.register_tool(
|
|
'patch',
|
|
tools['patch'],
|
|
ToolMetadata(
|
|
name='patch',
|
|
description='Targeted find-and-replace edits in files with fuzzy matching',
|
|
parameters={
|
|
'mode': {'type': 'string', 'required': False, 'default': 'replace', 'description': "Edit mode: 'replace' for targeted find-and-replace, 'patch' for V4A multi-file patches"},
|
|
'path': {'type': 'string', 'required': False, 'description': 'File path to edit (required for replace mode)'},
|
|
'old_string': {'type': 'string', 'required': False, 'description': 'Text to find in the file (required for replace mode)'},
|
|
'new_string': {'type': 'string', 'required': False, 'description': 'Replacement text (required for replace mode)'},
|
|
'replace_all': {'type': 'boolean', 'required': False, 'default': False, 'description': 'Replace all occurrences instead of requiring a unique match'}
|
|
},
|
|
permissions=['file_write'],
|
|
examples=[
|
|
{'mode': 'replace', 'path': 'config.txt', 'old_string': 'old_value', 'new_string': 'new_value'}
|
|
],
|
|
security_notes='Edits are limited to user accessible files and require proper permissions'
|
|
)
|
|
)
|
|
|
|
def _register_system_tools(registry: ToolRegistry, tools: Dict[str, Callable]):
|
|
"""Register system operation tools."""
|
|
# terminal
|
|
registry.register_tool(
|
|
'terminal',
|
|
tools['terminal'],
|
|
ToolMetadata(
|
|
name='terminal',
|
|
description='Execute shell commands on a Linux environment',
|
|
parameters={
|
|
'command': {'type': 'string', 'required': True, 'description': 'The command to execute on the VM'},
|
|
'background': {'type': 'boolean', 'required': False, 'default': False, 'description': 'Run the command in the background'},
|
|
'timeout': {'type': 'integer', 'required': False, 'default': 180, 'description': 'Max seconds to wait'},
|
|
'workdir': {'type': 'string', 'required': False, 'description': 'Working directory for this command'},
|
|
'pty': {'type': 'boolean', 'required': False, 'default': False, 'description': 'Run in pseudo-terminal (PTY) mode'},
|
|
'notify_on_complete': {'type': 'boolean', 'required': False, 'default': False, 'description': 'Auto-notify when background process completes'}
|
|
},
|
|
permissions=['system_execute'],
|
|
examples=[
|
|
{'command': 'ls -la'},
|
|
{'command': 'python script.py', 'background': True, 'notify_on_complete': True}
|
|
],
|
|
security_notes='Commands are executed with user privileges. Dangerous commands may be restricted.'
|
|
)
|
|
)
|
|
|
|
# process
|
|
registry.register_tool(
|
|
'process',
|
|
tools['process'],
|
|
ToolMetadata(
|
|
name='process',
|
|
description='Manage background processes started with terminal(background=true)',
|
|
parameters={
|
|
'action': {'type': 'string', 'required': True, 'description': "Action: 'list', 'poll', 'log', 'wait', 'kill', 'write', 'submit', 'close'"},
|
|
'session_id': {'type': 'string', 'required': False, 'description': 'Process session ID (required for all actions except list)'},
|
|
'data': {'type': 'string', 'required': False, 'description': 'Text to send to process stdin (for write and submit actions)'},
|
|
'timeout': {'type': 'integer', 'required': False, 'description': 'Max seconds to block for wait action'},
|
|
'offset': {'type': 'integer', 'required': False, 'description': 'Line offset for log action'},
|
|
'limit': {'type': 'integer', 'required': False, 'description': 'Max lines to return for log action'}
|
|
},
|
|
permissions=['system_manage'],
|
|
examples=[
|
|
{'action': 'list'},
|
|
{'action': 'poll', 'session_id': 'abc123'}
|
|
],
|
|
security_notes='Can only manage processes started by the current user session'
|
|
)
|
|
)
|
|
|
|
# execute_code
|
|
registry.register_tool(
|
|
'execute_code',
|
|
tools['execute_code'],
|
|
ToolMetadata(
|
|
name='execute_code',
|
|
description='Run a Python script that can call Hermes tools programmatically',
|
|
parameters={
|
|
'code': {'type': 'string', 'required': True, 'description': 'Python code to execute'}
|
|
},
|
|
permissions=['code_execute'],
|
|
examples=[
|
|
{'code': 'print("Hello from Python!")'}
|
|
],
|
|
security_notes='Code execution is sandboxed but still requires caution. Limited to 5-minute timeout.'
|
|
)
|
|
)
|
|
|
|
def _register_browser_tools(registry: ToolRegistry, tools: Dict[str, Callable]):
|
|
"""Register browser automation tools."""
|
|
browser_permissions = ['browser_access']
|
|
|
|
# Common browser tool metadata
|
|
browser_examples = [{'url': 'https://example.com'}]
|
|
browser_security_notes = 'Browser access is limited to HTTP/HTTPS URLs. Local file access may be restricted.'
|
|
|
|
browser_tools_list = [
|
|
('browser_navigate', 'Navigate to a URL in the browser'),
|
|
('browser_snapshot', 'Get a text-based snapshot of the current page'),
|
|
('browser_click', 'Click on an element identified by its ref ID'),
|
|
('browser_type', 'Type text into an input field identified by its ref ID'),
|
|
('browser_press', 'Press a keyboard key'),
|
|
('browser_scroll', 'Scroll the page in a direction'),
|
|
('browser_console', 'Get browser console output and JavaScript errors'),
|
|
('browser_get_images', 'Get a list of all images on the current page'),
|
|
('browser_vision', 'Take a screenshot and analyze it with vision AI'),
|
|
('browser_back', 'Navigate back to the previous page')
|
|
]
|
|
|
|
for tool_name, description in browser_tools_list:
|
|
registry.register_tool(
|
|
tool_name,
|
|
tools[tool_name],
|
|
ToolMetadata(
|
|
name=tool_name,
|
|
description=description,
|
|
parameters={}, # Parameters vary by tool, simplified for now
|
|
permissions=browser_permissions,
|
|
examples=browser_examples,
|
|
security_notes=browser_security_notes
|
|
)
|
|
)
|
|
|
|
def _register_ai_tools(registry: ToolRegistry, tools: Dict[str, Callable]):
|
|
"""Register AI-powered tools."""
|
|
# vision_analyze
|
|
registry.register_tool(
|
|
'vision_analyze',
|
|
tools['vision_analyze'],
|
|
ToolMetadata(
|
|
name='vision_analyze',
|
|
description='Analyze images using AI vision with comprehensive description and Q&A',
|
|
parameters={
|
|
'image_url': {'type': 'string', 'required': True, 'description': 'Image URL or local file path to analyze'},
|
|
'question': {'type': 'string', 'required': True, 'description': 'Specific question about the image content'}
|
|
},
|
|
permissions=['ai_vision'],
|
|
examples=[
|
|
{'image_url': 'https://example.com/image.jpg', 'question': 'What objects are in this image?'}
|
|
],
|
|
security_notes='Image analysis may have privacy implications. Local files must be in accessible directories.'
|
|
)
|
|
)
|
|
|
|
# text_to_speech
|
|
registry.register_tool(
|
|
'text_to_speech',
|
|
tools['text_to_speech'],
|
|
ToolMetadata(
|
|
name='text_to_speech',
|
|
description='Convert text to speech audio with user-configured voice',
|
|
parameters={
|
|
'text': {'type': 'string', 'required': True, 'description': 'Text to convert to speech (under 4000 characters)'},
|
|
'output_path': {'type': 'string', 'required': False, 'description': 'Optional custom file path to save the audio'}
|
|
},
|
|
permissions=['ai_tts'],
|
|
examples=[
|
|
{'text': 'Hello, this is a test message.'}
|
|
],
|
|
security_notes='Audio files are saved to user-accessible directories only'
|
|
)
|
|
)
|
|
|
|
def _register_memory_tools(registry: ToolRegistry, tools: Dict[str, Callable]):
|
|
"""Register memory management tools."""
|
|
# memory
|
|
registry.register_tool(
|
|
'memory',
|
|
tools['memory'],
|
|
ToolMetadata(
|
|
name='memory',
|
|
description='Save durable information to persistent memory across sessions',
|
|
parameters={
|
|
'action': {'type': 'string', 'required': True, 'description': "Action: 'add', 'replace', or 'remove'"},
|
|
'target': {'type': 'string', 'required': True, 'description': "Target: 'memory' for personal notes, 'user' for user profile"},
|
|
'content': {'type': 'string', 'required': False, 'description': 'Content to add/replace (required for add/replace)'},
|
|
'old_text': {'type': 'string', 'required': False, 'description': 'Text to identify entry for replace/remove'}
|
|
},
|
|
permissions=['memory_manage'],
|
|
examples=[
|
|
{'action': 'add', 'target': 'memory', 'content': 'User prefers dark mode'}
|
|
],
|
|
security_notes='Memory is isolated per user. Sensitive information should be handled carefully.'
|
|
)
|
|
)
|
|
|
|
# session_search
|
|
registry.register_tool(
|
|
'session_search',
|
|
tools['session_search'],
|
|
ToolMetadata(
|
|
name='session_search',
|
|
description='Search long-term memory of past conversations or browse recent sessions',
|
|
parameters={
|
|
'query': {'type': 'string', 'required': False, 'description': 'Search query keywords or phrases'},
|
|
'limit': {'type': 'integer', 'required': False, 'default': 3, 'description': 'Max sessions to summarize'}
|
|
},
|
|
permissions=['memory_read'],
|
|
examples=[
|
|
{'query': 'database setup'},
|
|
{} # Browse recent sessions
|
|
],
|
|
security_notes='Only searches sessions belonging to the current user'
|
|
)
|
|
)
|
|
|
|
def _register_skill_tools(registry: ToolRegistry, tools: Dict[str, Callable]):
|
|
"""Register skill management tools."""
|
|
# skill_view
|
|
registry.register_tool(
|
|
'skill_view',
|
|
tools['skill_view'],
|
|
ToolMetadata(
|
|
name='skill_view',
|
|
description='Load a skill\'s full content or access its linked files',
|
|
parameters={
|
|
'name': {'type': 'string', 'required': True, 'description': 'The skill name'},
|
|
'file_path': {'type': 'string', 'required': False, 'description': 'Path to a linked file within the skill'}
|
|
},
|
|
permissions=['skill_read'],
|
|
examples=[
|
|
{'name': 'module-development-spec'},
|
|
{'name': 'bricks-framework', 'file_path': 'templates/base.ui'}
|
|
],
|
|
security_notes='Skills are loaded from the configured skills_path directory'
|
|
)
|
|
)
|
|
|
|
# skills_list
|
|
registry.register_tool(
|
|
'skills_list',
|
|
tools['skills_list'],
|
|
ToolMetadata(
|
|
name='skills_list',
|
|
description='List available skills with name and description',
|
|
parameters={
|
|
'category': {'type': 'string', 'required': False, 'description': 'Optional category filter'}
|
|
},
|
|
permissions=['skill_read'],
|
|
examples=[
|
|
{},
|
|
{'category': 'software-development'}
|
|
],
|
|
security_notes='Lists only skills accessible to the current user'
|
|
)
|
|
)
|
|
|
|
# skill_manage
|
|
registry.register_tool(
|
|
'skill_manage',
|
|
tools['skill_manage'],
|
|
ToolMetadata(
|
|
name='skill_manage',
|
|
description='Manage skills (create, update, delete) with procedural memory',
|
|
parameters={
|
|
'action': {'type': 'string', 'required': True, 'description': "Action: 'create', 'patch', 'edit', 'delete', 'write_file', 'remove_file'"},
|
|
'name': {'type': 'string', 'required': True, 'description': 'Skill name'},
|
|
'content': {'type': 'string', 'required': False, 'description': 'Full SKILL.md content (for create/edit)'}
|
|
},
|
|
permissions=['skill_manage'],
|
|
examples=[
|
|
{'action': 'list'},
|
|
{'action': 'create', 'name': 'my-skill', 'content': '# My Skill\n...'}
|
|
],
|
|
security_notes='Skill management affects the shared skills repository. Use with caution.'
|
|
)
|
|
)
|
|
|
|
def _register_task_tools(registry: ToolRegistry, tools: Dict[str, Callable]):
|
|
"""Register task management tools."""
|
|
# todo
|
|
registry.register_tool(
|
|
'todo',
|
|
tools['todo'],
|
|
ToolMetadata(
|
|
name='todo',
|
|
description='Manage task list for the current session with priority ordering',
|
|
parameters={
|
|
'todos': {'type': 'array', 'required': False, 'description': 'Task items to write (omit to read current list)'},
|
|
'merge': {'type': 'boolean', 'required': False, 'default': False, 'description': 'Update existing items by id, add new ones'}
|
|
},
|
|
permissions=['task_manage'],
|
|
examples=[
|
|
{},
|
|
{'todos': [{'id': 'task1', 'content': 'Do something', 'status': 'pending'}]}
|
|
],
|
|
security_notes='Task lists are session-specific and not persisted across sessions'
|
|
)
|
|
)
|
|
|
|
# delegate_task
|
|
registry.register_tool(
|
|
'delegate_task',
|
|
tools['delegate_task'],
|
|
ToolMetadata(
|
|
name='delegate_task',
|
|
description='Spawn subagents to work on tasks in isolated contexts',
|
|
parameters={
|
|
'goal': {'type': 'string', 'required': False, 'description': 'Single task goal'},
|
|
'tasks': {'type': 'array', 'required': False, 'description': 'Batch tasks to run in parallel (limit 3)'},
|
|
'context': {'type': 'string', 'required': False, 'description': 'Background information for subagent'}
|
|
},
|
|
permissions=['task_delegate'],
|
|
examples=[
|
|
{'goal': 'Debug this error', 'context': 'Error message: ...'},
|
|
{'tasks': [{'goal': 'Task A'}, {'goal': 'Task B'}]}
|
|
],
|
|
security_notes='Subagents have no memory of parent conversation and cannot call clarify'
|
|
)
|
|
)
|
|
|
|
# clarify
|
|
registry.register_tool(
|
|
'clarify',
|
|
tools['clarify'],
|
|
ToolMetadata(
|
|
name='clarify',
|
|
description='Ask user for clarification, feedback, or decision before proceeding',
|
|
parameters={
|
|
'question': {'type': 'string', 'required': True, 'description': 'Question to present to the user'},
|
|
'choices': {'type': 'array', 'required': False, 'description': 'Up to 4 answer choices for multiple choice'}
|
|
},
|
|
permissions=['user_interact'],
|
|
examples=[
|
|
{'question': 'Which approach should I take?', 'choices': ['Option A', 'Option B']}
|
|
],
|
|
security_notes='Used for interactive decision making with the user'
|
|
)
|
|
)
|
|
|
|
# cronjob
|
|
registry.register_tool(
|
|
'cronjob',
|
|
tools['cronjob'],
|
|
ToolMetadata(
|
|
name='cronjob',
|
|
description='Manage scheduled cron jobs with compressed tool interface',
|
|
parameters={
|
|
'action': {'type': 'string', 'required': True, 'description': "Action: 'create', 'list', 'update', 'pause', 'resume', 'remove', 'run'"},
|
|
'prompt': {'type': 'string', 'required': False, 'description': 'Self-contained prompt for create action'},
|
|
'schedule': {'type': 'string', 'required': False, 'description': 'Schedule string like \'30m\', \'every 2h\', or cron format'}
|
|
},
|
|
permissions=['schedule_manage'],
|
|
examples=[
|
|
{'action': 'list'},
|
|
{'action': 'create', 'prompt': 'Check system status', 'schedule': 'every 1h'}
|
|
],
|
|
security_notes='Cron jobs run autonomously with no user present. Prompts must be self-contained.'
|
|
)
|
|
)
|
|
|
|
def _register_config_tools(registry: ToolRegistry, tools: Dict[str, Callable]):
|
|
"""Register configuration tools."""
|
|
# get_app_config
|
|
registry.register_tool(
|
|
'get_app_config',
|
|
tools['get_app_config'],
|
|
ToolMetadata(
|
|
name='get_app_config',
|
|
description='Read application configuration file and extract skills_path',
|
|
parameters={
|
|
'config_path': {'type': 'string', 'required': False, 'description': 'Optional custom config file path'}
|
|
},
|
|
permissions=['config_read'],
|
|
examples=[
|
|
{},
|
|
{'config_path': './custom/config.json'}
|
|
],
|
|
security_notes='Reads configuration files from accessible directories only'
|
|
)
|
|
) |