Tools¶
Qanot AI provides built-in tools that the agent can call during conversations. Tools are the mechanism by which the agent interacts with the file system, web, memory, scheduling, RAG, image generation, multi-agent delegation, and diagnostics.
How Tools Work¶
The agent loop works like this:
- The LLM sees tool definitions in its prompt
- It responds with
tool_useblocks specifying which tool to call and with what parameters - Qanot executes the tool and returns the result
- The LLM processes the result and either calls more tools or responds to the user
Each tool execution has a 120-second timeout (configurable per-tool). Results exceeding 50,000 characters are truncated.
Built-in Tools¶
read_file¶
Read a file from the workspace or an absolute path.
| Parameter | Type | Required | Description |
|---|---|---|---|
path |
string | Yes | File path (relative to workspace or absolute) |
Returns the file content as text. Files exceeding 50,000 characters are truncated with a note showing total size.
write_file¶
Write content to a file, creating parent directories as needed. Paths are validated by fs_safe.validate_write_path() to block writes to system directories and symlink attacks.
| Parameter | Type | Required | Description |
|---|---|---|---|
path |
string | Yes | File path (relative to workspace or absolute) |
content |
string | Yes | File content |
Returns {"success": true, "path": "...", "bytes": 123}.
list_files¶
List files and directories in a given path.
| Parameter | Type | Required | Description |
|---|---|---|---|
path |
string | No | Directory path (default: workspace root) |
Returns a JSON array of entries with name, type ("file" or "dir"), and size.
run_command¶
Execute a shell command in the workspace directory.
| Parameter | Type | Required | Description |
|---|---|---|---|
command |
string | Yes | Shell command (pipes, redirects, && supported) |
timeout |
integer | No | Timeout in seconds (default: 120, max: 120) |
cwd |
string | No | Working directory (default: workspace) |
approved |
boolean | No | User approval confirmation (for cautious mode) |
Security: Uses a 3-tier security model configured via exec_security:
open(default) -- Only a blocklist of dangerous patterns is enforced. Commands likerm -rf /,mkfs,dd, fork bombs, and attack tools are always blocked regardless of mode.cautious-- Dangerous patterns blocked, plus risky commands (pip install, curl, sudo, git push, docker, database clients, etc.) require user approval via inline buttons. If the user denies, the command is rejected.strict-- Only commands matchingexec_allowlist(prefix match) are permitted. Everything else is blocked.
Commands time out after 120 seconds. Output is capped at 50,000 characters.
memory_search¶
Search across the agent's memory files.
| Parameter | Type | Required | Description |
|---|---|---|---|
query |
string | Yes | Search query |
When RAG is enabled, uses semantic vector search. Falls back to case-insensitive substring matching across MEMORY.md, daily notes (last 30), and SESSION-STATE.md. Results are limited to 50.
session_status¶
Get current session statistics including context usage and cost.
| Parameter | Type | Required | Description |
|---|---|---|---|
| (none) | -- | -- | No parameters |
Returns:
{
"context_percent": 23.5,
"total_input_tokens": 45000,
"total_output_tokens": 12000,
"total_tokens": 57000,
"max_tokens": 200000,
"buffer_active": false,
"turn_count": 8,
"last_prompt_tokens": 45000,
"user_cost": {"input_tokens": 45000, "output_tokens": 12000, "cost_usd": 0.12},
"total_cost": 1.45
}
cost_status¶
Get token usage and cost statistics per user.
| Parameter | Type | Required | Description |
|---|---|---|---|
user_id |
string | No | User ID to query (default: current user) |
Returns per-user token counts, cost breakdown, and totals. When user_id is omitted, returns stats for the current user. Cost tracking must be enabled.
send_file¶
Send a file from the workspace to the user via Telegram.
| Parameter | Type | Required | Description |
|---|---|---|---|
path |
string | Yes | File path (relative to workspace or absolute) |
Returns {"success": true, "path": "...", "size": 12345}. Files are queued for delivery by the Telegram adapter. Maximum file size is 50 MB (Telegram limit).
Web Tools¶
web_search¶
Search the internet using the Brave Search API.
| Parameter | Type | Required | Description |
|---|---|---|---|
query |
string | Yes | Search query (max 2000 characters) |
count |
integer | No | Number of results (1-10, default: 5) |
Returns JSON with query, result count, and an array of results each containing title, url, description, and optionally age. Results are cached for 15 minutes (up to 50 entries). Requires brave_api_key in config.
web_fetch¶
Fetch and extract readable content from a web page URL.
| Parameter | Type | Required | Description |
|---|---|---|---|
url |
string | Yes | URL to fetch (http:// or https://) |
max_chars |
integer | No | Max output characters (default: 50,000) |
Returns JSON with url, final_url, title, content (extracted text), content_type, length, and a source disclaimer. HTML pages are converted to simplified markdown (headings, links, paragraphs). JSON responses are pretty-printed.
SSRF Protection: URLs are validated against: - Blocked hostnames (localhost, metadata.google.internal) - Blocked ports (SSH, SMTP, database ports, Docker daemon, etc.) - Private/reserved IP networks (127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, link-local, IPv6 loopback) - DNS resolution is checked to catch hostname-to-private-IP redirects - Redirect targets are re-validated (max 3 redirects) - Response body is limited to 2 MB - 30-second timeout
Image Tools¶
Available when gemini_api_key is configured. Powered by Gemini (Nano Banana) image generation.
generate_image¶
Generate a new image from a text description.
| Parameter | Type | Required | Description |
|---|---|---|---|
prompt |
string | Yes | Detailed text description of the image to generate |
model |
string | No | Image model (default: gemini-3-pro-image-preview) |
Supported models:
- gemini-3-pro-image-preview -- Nano Banana Pro (highest quality)
- gemini-3.1-flash-image-preview -- Nano Banana 2 (fast)
- gemini-2.5-flash-image -- Nano Banana (speed optimized)
Returns {"status": "ok", "image_path": "...", "model": "...", "description": "...", "size_bytes": 123456}. The image is saved to workspace/generated/ and automatically sent to the user via Telegram.
edit_image¶
Edit the user's last sent photo based on a text instruction.
| Parameter | Type | Required | Description |
|---|---|---|---|
prompt |
string | Yes | Text instruction describing the edit (e.g., "make it black and white") |
model |
string | No | Image model (same options as generate_image) |
The tool searches backwards through the conversation to find the last user-sent image. If no image is found, returns an error asking the user to send a photo first. The edited image is saved and sent to the user.
Cron Tools¶
These tools let the agent create and manage scheduled jobs. See Scheduler for details on how cron jobs execute.
cron_create¶
Create a new scheduled job or one-shot reminder.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Unique job name (max 200 chars) |
schedule |
string | No* | Cron expression (e.g., 0 */4 * * *) |
at |
string | No* | ISO 8601 timestamp for one-shot reminder (e.g., 2026-03-12T17:00:00+05:00) |
prompt |
string | Yes | Reminder text or task prompt (max 10,000 chars) |
mode |
string | No | isolated (full agent with tools) or systemEvent (text delivery only). Default: systemEvent |
delete_after_run |
boolean | No | Auto-delete after execution (default: true for at reminders) |
timezone |
string | No | IANA timezone (e.g., Asia/Tashkent) |
*Either schedule or at is required.
{
"name": "daily-summary",
"schedule": "0 20 * * *",
"prompt": "Write a summary of today's conversations and save to MEMORY.md",
"mode": "isolated"
}
The scheduler reloads automatically after creation.
cron_list¶
List all scheduled jobs.
| Parameter | Type | Required | Description |
|---|---|---|---|
| (none) | -- | -- | No parameters |
Returns the full jobs.json content.
cron_update¶
Update an existing job.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Job name to update |
schedule |
string | No | New cron expression |
mode |
string | No | New execution mode (systemEvent or isolated) |
prompt |
string | No | New prompt |
enabled |
boolean | No | Enable/disable |
cron_delete¶
Delete a scheduled job.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Job name to delete |
RAG Tools¶
Available when rag_enabled: true and a compatible embedding provider exists. See RAG for the full documentation.
rag_index¶
Index a file into the RAG system. Re-indexing the same file replaces existing chunks.
| Parameter | Type | Required | Description |
|---|---|---|---|
path |
string | Yes | File path (.txt, .md, .csv, .pdf) -- must be within workspace |
name |
string | No | Display name / source identifier (default: filename) |
Returns {"indexed": true, "source": "Employee Handbook", "chunks": 42}. PDF support requires PyMuPDF (pip install PyMuPDF). Paths are validated to prevent directory traversal outside workspace.
rag_search¶
Search indexed documents with hybrid semantic + keyword search.
| Parameter | Type | Required | Description |
|---|---|---|---|
query |
string | Yes | Search query (max 10,000 characters) |
top_k |
integer | No | Number of results (1-100, default: 5) |
Returns an array of results with text, source, and score (0-1, higher is better).
rag_list¶
List all indexed document sources with chunk counts.
| Parameter | Type | Required | Description |
|---|---|---|---|
| (none) | -- | -- | No parameters |
rag_forget¶
Remove a document source from the RAG index.
| Parameter | Type | Required | Description |
|---|---|---|---|
source |
string | Yes | Source name to remove |
Returns {"deleted": true, "source": "...", "chunks_removed": 42}.
Delegation Tools¶
Multi-agent collaboration tools. The main agent can delegate tasks to specialized agents, have multi-turn conversations with them, and share results via a project board.
delegate_to_agent¶
One-shot task delegation -- hand off a task to another agent and wait for the result.
| Parameter | Type | Required | Description |
|---|---|---|---|
task |
string | Yes | Task description (detailed) |
agent_id |
string | Yes | Target agent identifier |
context |
string | No | Optional context relevant to the task (max 4,000 chars) |
{
"task": "Write SEO-optimized meta descriptions for these 5 product pages",
"agent_id": "seo-expert",
"context": "Target market is Uzbekistan, write in Uzbek language"
}
Returns the agent's result (max 8,000 chars). Has a 120-second timeout. Maximum delegation depth is 2 (agents can delegate to other agents, but not infinitely). Loop detection prevents circular delegations. Results are posted to the shared project board.
converse_with_agent¶
Multi-turn ping-pong conversation with another agent (up to 5 turns).
| Parameter | Type | Required | Description |
|---|---|---|---|
message |
string | Yes | Message to send to the agent |
agent_id |
string | Yes | Target agent identifier |
max_turns |
integer | No | Maximum conversation turns (1-5, default: 3) |
{
"message": "Let's design the database schema for a blog platform",
"agent_id": "architect",
"max_turns": 5
}
Returns the full conversation transcript. Useful for collaborative problem-solving and negotiations between agents.
view_project_board¶
View the shared project board -- see results from all agents' completed work.
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id |
string | No | Filter by agent ID |
Returns an array of board entries with agent_id, task, result, and timestamp. Max 20 entries per user. Board data is evicted after 6 hours of inactivity.
clear_project_board¶
Clear the shared project board.
| Parameter | Type | Required | Description |
|---|---|---|---|
| (none) | -- | -- | No parameters |
list_agents¶
List all available agents with their model, role, and capabilities.
| Parameter | Type | Required | Description |
|---|---|---|---|
| (none) | -- | -- | No parameters |
Returns an array of agent definitions including id, name, model, prompt (truncated), and tools_allow/tools_deny.
agent_session_history¶
Read another agent's conversation transcript.
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id |
string | Yes | Agent whose history to read |
Returns the last 20 messages from the agent's session, including role, content, timestamp, and whether tools were used.
agent_sessions_list¶
List all active agent sessions with metadata.
| Parameter | Type | Required | Description |
|---|---|---|---|
| (none) | -- | -- | No parameters |
Returns session info for each active agent: message count, last activity timestamp.
view_agent_activity¶
View the agent activity log -- real-time monitoring of all agent interactions.
| Parameter | Type | Required | Description |
|---|---|---|---|
limit |
integer | No | Max entries to return (default: 20, max: 50) |
agent_id |
string | No | Filter by agent ID |
Returns a feed of delegation events, results, errors, and timing data.
set_monitor_group¶
Configure a Telegram group for real-time agent monitoring.
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id |
integer | Yes | Telegram group ID (negative number, e.g., -1001234567890) |
When set, agent-to-agent interactions are forwarded to this group so you can watch them in real time. Each agent bot must be added to the group.
Sub-Agent Tools¶
spawn_sub_agent¶
Spawn an isolated background sub-agent for complex, long-running tasks. The sub-agent works independently and delivers results to the user via Telegram when finished.
| Parameter | Type | Required | Description |
|---|---|---|---|
task |
string | Yes | Task description -- detailed and self-contained (max 10,000 chars) |
{"task": "Research Python vs Rust performance benchmarks -- use web_search to find recent comparisons and summarize findings with sources"}
Returns {"status": "spawned", "task_id": "abc12345", "message": "..."}. The sub-agent has a 5-minute timeout. Maximum 3 concurrent sub-agents per user. Available tools: web_search, web_fetch, read_file, memory_search.
list_sub_agents¶
List active sub-agents for the current user.
| Parameter | Type | Required | Description |
|---|---|---|---|
| (none) | -- | -- | No parameters |
Returns {"active": 2, "agents": [{"task_id": "abc12345", "status": "running"}, ...]}.
Agent Manager Tools¶
Dynamic agent lifecycle management -- create, update, and delete agents at runtime without restarting the bot.
create_agent¶
Create a new agent with its own personality, model, and optionally its own Telegram bot.
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | Yes | Agent ID (lowercase alphanumeric + hyphens, max 32 chars) |
name |
string | Yes | Agent display name |
prompt |
string | No | Agent personality and instructions (auto-generated if omitted) |
model |
string | No | LLM model (default: main agent's model) |
provider |
string | No | LLM provider (default: main agent's provider) |
bot_token |
string | No | Telegram bot token -- if provided, launches as a standalone Telegram bot |
tools_allow |
array[string] | No | Allowlist of tools (empty = all tools) |
tools_deny |
array[string] | No | Denylist of tools |
timeout |
integer | No | Timeout in seconds (default: 120) |
{
"id": "seo-expert",
"name": "SEO Mutaxassis",
"prompt": "You are an SEO expert specializing in the Uzbekistan market...",
"model": "claude-haiku-4-5"
}
Agents are persisted to config.json and hot-launched without restart. If bot_token is provided, the agent runs as an independent Telegram bot. Without a token, it is an internal agent accessible via delegate_to_agent. A SOUL.md file is created in workspace/agents/<id>/.
update_agent¶
Update an existing agent's configuration. Only provide fields you want to change.
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | Yes | Agent ID to update |
name |
string | No | New display name |
prompt |
string | No | New personality/instructions (also updates SOUL.md) |
model |
string | No | New LLM model |
provider |
string | No | New LLM provider |
bot_token |
string | No | New Telegram bot token (empty string removes token and stops bot) |
tools_allow |
array[string] | No | New tool allowlist |
tools_deny |
array[string] | No | New tool denylist |
timeout |
integer | No | New timeout |
Returns {"status": "updated", "agent_id": "...", "changes": ["model"]}.
delete_agent¶
Delete an agent. If it has a running Telegram bot, the bot is stopped.
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | Yes | Agent ID to delete |
Returns {"status": "deleted", "agent_id": "...", "bot_stopped": true}.
restart_self¶
Restart the entire bot process. Useful after configuration changes, new agent creation, or error recovery. The bot sends SIGTERM to itself and relies on the service manager (systemd/launchd) to respawn it.
| Parameter | Type | Required | Description |
|---|---|---|---|
reason |
string | No | Reason for restart |
Returns immediately. The bot restarts after a 2-second delay.
Doctor Tool¶
doctor¶
Run comprehensive system health diagnostics. Checks 7 subsystems and reports status (ok/warning/error) for each.
| Parameter | Type | Required | Description |
|---|---|---|---|
| (none) | -- | -- | No parameters |
Checks performed: - config -- bot_token, API keys, workspace_dir writability, sessions_dir, required workspace files (SOUL.md, TOOLS.md, IDENTITY.md) - memory -- MEMORY.md readability and size, SESSION-STATE.md size (warns if >100KB), daily notes count (30 days), memory/ directory size - context -- Current context usage %, token counts, buffer status, compaction mode - provider -- Single vs multi-provider mode, model names - rag -- RAG database existence and size, FTS5 availability, embedding cache entries - sessions -- Sessions directory size, recent session file count (7 days), latest session timestamp - disk -- Workspace size, available disk space (warns if <100MB)
Returns:
{
"status": "healthy",
"checks": {
"config": {"status": "ok", "details": "..."},
"memory": {"status": "ok", "details": "..."},
"context": {"status": "ok", "details": "..."},
"provider": {"status": "ok", "details": "..."},
"rag": {"status": "ok", "details": "..."},
"sessions": {"status": "ok", "details": "..."},
"disk": {"status": "ok", "details": "..."}
},
"warnings": [],
"timestamp": "2026-03-16T12:00:00+00:00"
}
Creating Custom Tools¶
Custom tools are added through the plugin system. For quick one-off tools, you can also register directly on the ToolRegistry:
from qanot.agent import ToolRegistry
registry = ToolRegistry()
async def my_tool(params: dict) -> str:
name = params.get("name", "world")
return f"Hello, {name}!"
registry.register(
name="greet",
description="Greet someone by name.",
parameters={
"type": "object",
"required": ["name"],
"properties": {
"name": {"type": "string", "description": "Name to greet"},
},
},
handler=my_tool,
)
Tool handlers must be async functions that accept a dict parameter and return a str. JSON is the conventional return format for structured data. Raise exceptions for errors -- they are caught and returned as {"error": "..."}.
Tool Safety¶
- 3-tier command security:
run_commandusesexec_security(open/cautious/strict) to control which commands are allowed. Dangerous patterns likerm -rf /, fork bombs, and attack tools are always blocked. - File write validation:
write_filevalidates paths withfs_safe.validate_write_path()-- system directories are blocked, symlinks are checked. - SSRF protection:
web_fetchvalidates URLs against private networks, blocked ports, and internal hostnames. DNS resolution is checked, and redirect targets are re-validated. - Timeout: Command execution times out after 120 seconds. Web fetch times out after 30 seconds.
- Result truncation: Oversized results are truncated to 50,000 characters to prevent context bloat.
- Loop detection: The agent loop detects repeated identical tool calls (3 consecutive or alternating patterns) and breaks the loop with a message to the user.
- Deterministic error hints: Tool errors containing patterns like "not found" or "permission denied" get a hint telling the LLM not to retry with the same parameters.
- Delegation limits: Max depth of 2, loop detection, 120-second timeout, result size caps.
- Sub-agent limits: Max 3 concurrent per user, 5-minute timeout.
- File size limits:
send_fileenforces Telegram's 50 MB upload limit.