# Didactyl — Tools See also: [SKILLS.md](SKILLS.md) · [CONTEXT.md](CONTEXT.md) ## Overview Didactyl is a **Nostr-first sovereign AI agent** that reasons with an LLM and takes actions through **tools**. This document describes the tools architecture: what tools are, how they are exposed to the model, how execution loops work, what tool categories exist, and how access is gated. --- ## What Tools Are Tools are in the agent's hands — the chisels in the woodshop. They are hardcoded C functions that the LLM can invoke during a conversation to take actions in the world. A **skill** teaches the agent *how* to carve — the technique, the judgment, the decision-making. A **tool** is the chisel — the physical capability. The skill never directly uses the chisel without the craftsperson (the LLM) in the loop. If you want a hardcoded program that runs without reasoning, that's a tool or an external program — not a skill. ## How Tools Work 1. A trigger fires (DM, cron, subscription, webhook, or chain) — see [SKILLS.md](SKILLS.md) 2. The agent builds the context window from triggered skills and their `{{...}}` references 3. The agent builds an LLM request with the context, triggering event, and a JSON schema of available tools 4. The LLM decides whether to call a tool or respond directly 5. If a tool is called, didactyl executes it and feeds the result back to the LLM 6. The loop repeats until the LLM produces a final text response 7. For DM triggers, the response is sent back as a DM ``` Trigger fires (DM, cron, subscription, webhook, chain) │ ├─ Build context from triggered skills ├─ Build tool schemas (filtered by skill's tools tag) │ ├─ Send to LLM: context + tool schemas │ │ ┌─────────────────────────────────────┐ │ │ LLM reasons about the request │ │ │ │ │ │ Option A: call a tool │ │ │ → agent executes tool │ │ │ → feeds result back to LLM │ │ │ → loop continues │ │ │ │ │ │ Option B: produce text response │ │ │ → loop ends │ │ └─────────────────────────────────────┘ │ └─ Deliver response (DM reply, chain forward, etc.) ``` --- ## Tool Categories ### Nostr Event & Messaging Tools | Tool | Description | |---|---| | `nostr_post` | Publish a Nostr event to connected relays | | `nostr_delete` | Request deletion of one or more previously published events (NIP-09 kind 5) | | `nostr_react` | React to a Nostr event with like/dislike/emoji (NIP-25 kind 7) | | `nostr_query` | Query events from relays using a Nostr filter | | `nostr_my_events` | Query recent events authored by this agent and return kind, event_id, timestamp, d_tag, and cache presence | | `nostr_dm_send` | Send a NIP-04 encrypted DM | | `nostr_dm_send_nip17` | Send a private DM using NIP-17 gift wrap protocol | ### Nostr Identity & Utility Tools | Tool | Description | |---|---| | `nostr_profile_get` | Look up a Nostr profile (kind 0 metadata) by pubkey | | `nostr_nip05_lookup` | Look up or verify a NIP-05 identifier (`user@domain`) | | `nostr_encode` | Encode a Nostr entity into `nostr:` URI (`npub`, `note`, `nprofile`, `nevent`, `naddr`) | | `nostr_decode` | Decode a Nostr bech32/`nostr:` URI into components | | `nostr_relay_status` | Get connection status and statistics for all relays | | `nostr_relay_info` | Fetch NIP-11 relay information document | | `nostr_subscription_status` | List currently managed runtime Nostr subscriptions and filters | | `nostr_subscription_set` | Update one managed runtime subscription by name (toggle enabled and/or replace filter) | | `nostr_encrypt` | Encrypt plaintext using NIP-44 for a recipient | | `nostr_decrypt` | Decrypt NIP-44 ciphertext from a sender | | `nostr_list_manage` | Add/remove tag tuples in replaceable list events (NIP-51 style) | | `nostr_block_list` | View blocked pubkeys, event IDs, or hashtags from the local encrypted kind-10000 block list cache | | `nostr_block_edit` | Add or remove blocked tuples in kind-10000 block list; entries are private (encrypted) by default | ### Context & Identity Tools These tools provide the agent with information about itself and its administrator, often used to build system prompts or context blocks. | Tool | Description | |---|---| | `nostr_pubkey` | Return this agent's pubkey in hex format | | `nostr_npub` | Return this agent's pubkey encoded as npub bech32 | | `my_pubkey` | Alias for `nostr_pubkey` | | `my_npub` | Alias for `nostr_npub` | | `agent_identity` | Build agent identity context block with pubkey and npub | | `admin_identity` | Build admin identity context block from cached runtime metadata | | `nostr_admin_profile` | Build admin profile context block from cached kind 0 metadata | | `nostr_admin_contacts` | Build admin contacts context block from cached kind 3 contact list | | `nostr_admin_relays` | Build admin relay context block from cached kind 10002 data | | `nostr_admin_notes` | Build admin notes context block from cached kind 1 notes | | `nostr_agent_profile` | Build agent profile context block from cached kind 0 metadata | | `nostr_agent_contacts` | Build agent contacts context block from cached kind 3 contact list | | `nostr_agent_relays` | Build agent relay context block from cached kind 10002 data | | `nostr_agent_notes` | Build agent notes context block from cached kind 1 notes | | `my_kind0_profile` | Alias for `nostr_agent_profile` | | `my_contacts` | Alias for `nostr_agent_contacts` | | `my_relays` | Alias for `nostr_agent_relays` | | `my_notes` | Alias for `nostr_agent_notes` | ### Skills & Trigger Tools These tools manage skill and trigger lifecycle; skill semantics and trigger execution details are documented in [SKILLS.md](SKILLS.md). | Tool | Description | |---|---| | `skill_create` | Create or update a skill definition as kind `31123`/`31124` and optionally auto-adopt it (content should be markdown instructions; runtime handles role packaging) | | `skill_edit` | Edit an existing self skill by d tag and republish it as kind `31123`/`31124` (content should remain markdown-first) | | `skill_list` | List available skills discovered online (agent + admin), with adoption status and optional filters | | `skill_adopt` | Adopt a skill by adding its address to kind `10123` adoption list | | `skill_remove` | Remove a skill address from kind `10123` adoption list | | `skill_search` | Search public skills by query/author and optionally rank by adoption popularity | | `trigger_list` | List active triggered skills and their runtime status | ### Task & Memory Tools These tools manage the agent's short-term and long-term memory, persisted on Nostr. | Tool | Description | |---|---| | `task_list` | Build current task list context block from agent task memory on Nostr | | `task_manage` | Manage agent short-term task memory stored on Nostr kind `30078` (d=tasks): list/add/update/remove/clear/replace | | `memory_save` | Prepend a new entry to encrypted agent memory (kind `30078`, d=memory) and truncate oldest content if needed | | `memory_recall` | Recall encrypted agent memory (kind `30078`, d=memory) | ### LLM / Model Management Tools | Tool | Description | |---|---| | `model_get` | Get current active LLM runtime configuration (excluding API key) | | `model_set` | Update active LLM configuration and persist it to Nostr kind `30078` (d=llm_config) | | `model_list` | List available model IDs using provider OpenAI-compatible `/models` endpoint | ### Configuration Persistence Tools | Tool | Description | |---|---| | `config_store` | Encrypt and publish agent config as kind `30078` for a given d_tag | | `config_recall` | Fetch and decrypt agent config kind `30078` by d_tag | ### System & Runtime Tools | Tool | Description | |---|---| | `agent_version` | Return current Didactyl version and metadata from build macros | | `local_http_fetch` | Fetch HTTP(S) resources with optional method, headers, timeout, and body | | `local_shell_exec` | Execute a shell command and return stdout/stderr | | `local_file_read` | Read a local file as text from the configured working directory | | `local_file_write` | Write text content to a local file in the configured working directory | | `tool_list` | List available tools with name, description, and JSON parameter schema | ### Cashu Wallet Tools (NIP-60) | Tool | Description | |---|---| | `cashu_wallet_balance` | Return wallet balances aggregated by mint and unit from loaded token proofs | | `cashu_wallet_info` | Fetch mint info for a specific `mint_url` (or configured default mint) | | `cashu_wallet_mint_quote` | Request a mint quote for an amount and unit | | `cashu_wallet_mint_check` | Check whether a mint quote is paid/issued | | `cashu_wallet_mint_claim` | Claim newly minted proofs for a paid quote and persist token event | | `cashu_wallet_melt_quote` | Request a melt quote for a Lightning invoice/payment request | | `cashu_wallet_melt_pay` | Pay a melt quote using selected proofs and persist history/token rollover | | `cashu_wallet_check_proofs` | Ask mint for current proof states and summarize unspent/pending/spent proofs | | `cashu_wallet_receive_token` | Receive an ecash token string (cashuA/cashuB), swap to fresh proofs, and store it in wallet state | | `cashu_wallet_send_token` | Create an outbound ecash token from wallet proofs and return a `cashuA`/`cashuB` token string | | `cashu_wallet_mints_set` | Set wallet mints (NIP-60), public mints (NIP-61), or both | ### Blossom Tools | Tool | Description | |---|---| | `blossom_upload` | Upload a local file to a Blossom server and return blob metadata | | `blossom_download` | Download a Blossom blob to a local file path | | `blossom_head` | Fetch Blossom blob metadata by SHA-256 | | `blossom_delete` | Delete a Blossom blob by SHA-256 using signed auth | | `blossom_list` | List Blossom blobs for a pubkey (defaults to agent pubkey) | ### Content Publishing Conveniences | Tool | Description | |---|---| | `nostr_post_readme` | Publish `README.md` as kind `30023` with deterministic d-tag `readme.md` | | `nostr_file_md_to_longform_post` | Read a markdown file and publish it as kind `30023` longform post (defaults d-tag to lowercase filename) | --- ## Tool Execution Examples These examples show the JSON structure for tool calls. ### Nostr Event & Messaging **nostr_post** ```json { "name": "nostr_post", "arguments": { "kind": 1, "content": "Hello from Didactyl!", "tags": [["t", "didactyl"]] } } ``` **nostr_delete** ```json { "name": "nostr_delete", "arguments": { "event_ids": [""], "reason": "Mistake" } } ``` **nostr_react** ```json { "name": "nostr_react", "arguments": { "event_id": "", "event_pubkey": "", "reaction": "🤙" } } ``` **nostr_query** ```json { "name": "nostr_query", "arguments": { "filter": { "kinds": [1], "limit": 5 }, "timeout_ms": 5000 } } ``` **nostr_my_events** ```json { "name": "nostr_my_events", "arguments": { "kind": 1, "limit": 10 } } ``` **nostr_dm_send** ```json { "name": "nostr_dm_send", "arguments": { "recipient_pubkey": "", "message": "Secret message" } } ``` **nostr_dm_send_nip17** ```json { "name": "nostr_dm_send_nip17", "arguments": { "recipient_pubkey": "", "message": "Private message via NIP-17", "subject": "Confidential" } } ``` ### Nostr Identity & Utility **nostr_profile_get** ```json { "name": "nostr_profile_get", "arguments": { "pubkey": "" } } ``` **nostr_nip05_lookup** ```json { "name": "nostr_nip05_lookup", "arguments": { "identifier": "user@domain.com" } } ``` **nostr_encode** ```json { "name": "nostr_encode", "arguments": { "type": "npub", "hex": "" } } ``` **nostr_decode** ```json { "name": "nostr_decode", "arguments": { "uri": "nostr:npub1..." } } ``` **nostr_relay_status** ```json { "name": "nostr_relay_status", "arguments": {} } ``` **nostr_relay_info** ```json { "name": "nostr_relay_info", "arguments": { "relay_url": "wss://relay.damus.io" } } ``` **nostr_subscription_status** ```json { "name": "nostr_subscription_status", "arguments": {} } ``` **nostr_subscription_set** ```json { "name": "nostr_subscription_set", "arguments": { "name": "admin_context_profile", "enabled": true } } ``` **nostr_encrypt** ```json { "name": "nostr_encrypt", "arguments": { "recipient_pubkey": "", "plaintext": "Sensitive data" } } ``` **nostr_decrypt** ```json { "name": "nostr_decrypt", "arguments": { "sender_pubkey": "", "ciphertext": "" } } ``` **nostr_list_manage** ```json { "name": "nostr_list_manage", "arguments": { "list_kind": 10000, "action": "add", "items": [["p", ""]] } } ``` **nostr_block_list** ```json { "name": "nostr_block_list", "arguments": { "type": "p" } } ``` **nostr_block_edit** ```json { "name": "nostr_block_edit", "arguments": { "action": "add", "items": [["p", ""]], "public": false } } ``` ### Context & Identity **nostr_pubkey** / **my_pubkey** ```json { "name": "nostr_pubkey", "arguments": {} } ``` **agent_identity** ```json { "name": "agent_identity", "arguments": {} } ``` **admin_identity** ```json { "name": "admin_identity", "arguments": {} } ``` **nostr_admin_profile** ```json { "name": "nostr_admin_profile", "arguments": {} } ``` **nostr_agent_notes** / **my_notes** ```json { "name": "my_notes", "arguments": {} } ``` ### Skills & Triggers **skill_create** ```json { "name": "skill_create", "arguments": { "d": "weather-skill", "content": "## Weather Assistant\n\nYou provide concise weather summaries.\n\nInput:\n{{message}}", "description": "Fetches weather data", "trigger": "dm", "filter": "{\"from\":\"admin\"}", "auto_adopt": true } } ``` **skill_edit** ```json { "name": "skill_edit", "arguments": { "d": "weather-skill", "description": "Updated weather skill description" } } ``` **skill_list** ```json { "name": "skill_list", "arguments": { "adopted": true } } ``` **skill_adopt** ```json { "name": "skill_adopt", "arguments": { "pubkey": "", "d": "cool-skill" } } ``` **skill_search** ```json { "name": "skill_search", "arguments": { "query": "bitcoin", "popular": true } } ``` **trigger_list** ```json { "name": "trigger_list", "arguments": {} } ``` ### Tasks & Memory **task_list** ```json { "name": "task_list", "arguments": {} } ``` **task_manage** ```json { "name": "task_manage", "arguments": { "action": "add", "text": "Finish documentation", "status": "active" } } ``` **memory_save** ```json { "name": "memory_save", "arguments": { "content": "User likes coffee." } } ``` **memory_recall** ```json { "name": "memory_recall", "arguments": {} } ``` ### LLM & Configuration **model_get** ```json { "name": "model_get", "arguments": {} } ``` **model_set** ```json { "name": "model_set", "arguments": { "model": "gpt-4o", "temperature": 0.7 } } ``` **config_store** ```json { "name": "config_store", "arguments": { "d_tag": "custom_setting", "content": "{\"value\": 42}" } } ``` **config_recall** ```json { "name": "config_recall", "arguments": { "d_tag": "custom_setting" } } ``` ### System & Runtime **agent_version** ```json { "name": "agent_version", "arguments": {} } ``` **local_http_fetch** ```json { "name": "local_http_fetch", "arguments": { "url": "https://api.github.com/zen", "method": "GET" } } ``` **local_shell_exec** ```json { "name": "local_shell_exec", "arguments": { "command": "ls -la" } } ``` **local_file_read** ```json { "name": "local_file_read", "arguments": { "path": "README.md" } } ``` **local_file_write** ```json { "name": "local_file_write", "arguments": { "path": "test.txt", "content": "Hello world", "append": false } } ``` **tool_list** ```json { "name": "tool_list", "arguments": {} } ``` ### Cashu Wallet **cashu_wallet_balance** ```json { "name": "cashu_wallet_balance", "arguments": {} } ``` **cashu_wallet_mint_quote** ```json { "name": "cashu_wallet_mint_quote", "arguments": { "amount": 100, "unit": "sat" } } ``` **cashu_wallet_receive_token** ```json { "name": "cashu_wallet_receive_token", "arguments": { "token": "cashuA..." } } ``` **cashu_wallet_send_token** ```json { "name": "cashu_wallet_send_token", "arguments": { "amount": 21 } } ``` **cashu_wallet_mints_set** ```json { "name": "cashu_wallet_mints_set", "arguments": { "target": "both", "wallet_mints": ["https://mint.host.com"], "public_mints": ["https://mint.host.com"] } } ``` --- ## Security Model Tool access is gated at two levels: ### Sender Tier (DM triggers) | Tier | Identity | Tools | Response | |------|----------|-------|----------| | **ADMIN** | Configured admin pubkey | All tools | Full LLM with context | | **WOT** | In admin's kind 3 contact list | None | Chat-only LLM | | **STRANGER** | Anyone else | None | Configurable static response | ### Skill Requirements (all triggers) Skills declare which tools they need via `requires_tool` tags (see [SKILLS.md — Requirements Tags](SKILLS.md#requirements-tags)). During execution, only the required and optional tools declared by the skill are exposed to the LLM. If a skill has no `requires_tool` tags, all available tools are exposed. --- ## Tool Portability Tool names serve as the **capability vocabulary** for cross-app skill portability. When a skill declares `["requires_tool", "http_fetch"]`, any app that provides an `http_fetch` capability can run that skill — regardless of how it implements the fetch internally. ### Runtime Availability Not all tools are available in every runtime. Tools that require specific host capabilities: | Tool | Requires | |------|----------| | `local_shell_exec` | Host OS shell access | | `local_file_read` | Host filesystem | | `local_file_write` | Host filesystem | | `blossom_upload` | Filesystem + HTTP | | `blossom_download` | Filesystem + HTTP | Tools available in any runtime (including browser): | Tool | Capability | |------|-----------| | `nostr_*` | Nostr relay WebSocket connections | | `local_http_fetch` | HTTP client | | `memory_save` / `memory_recall` | Nostr event storage | | `cashu_wallet_*` | HTTP client + Nostr storage | Skills should declare `requires_tool` tags so apps can determine compatibility. See [SKILLS.md — Requirements Tags](SKILLS.md#requirements-tags). --- ## Related Documentation - Skill definitions, adoption, triggers, and autonomous activation: [SKILLS.md](SKILLS.md) - Context assembly model: [CONTEXT.md](CONTEXT.md)