# 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 |
| `skill_edit` | Edit an existing self skill by d tag and republish it as kind `31123`/`31124` |
| `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": ["<event_id_hex>"],
    "reason": "Mistake"
  }
}
```

**nostr_react**
```json
{
  "name": "nostr_react",
  "arguments": {
    "event_id": "<event_id_hex>",
    "event_pubkey": "<pubkey_hex>",
    "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": "<pubkey_hex>",
    "message": "Secret message"
  }
}
```

**nostr_dm_send_nip17**
```json
{
  "name": "nostr_dm_send_nip17",
  "arguments": {
    "recipient_pubkey": "<pubkey_hex>",
    "message": "Private message via NIP-17",
    "subject": "Confidential"
  }
}
```

### Nostr Identity & Utility

**nostr_profile_get**
```json
{
  "name": "nostr_profile_get",
  "arguments": {
    "pubkey": "<pubkey_hex>"
  }
}
```

**nostr_nip05_lookup**
```json
{
  "name": "nostr_nip05_lookup",
  "arguments": {
    "identifier": "user@domain.com"
  }
}
```

**nostr_encode**
```json
{
  "name": "nostr_encode",
  "arguments": {
    "type": "npub",
    "hex": "<pubkey_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": "<pubkey_hex>",
    "plaintext": "Sensitive data"
  }
}
```

**nostr_decrypt**
```json
{
  "name": "nostr_decrypt",
  "arguments": {
    "sender_pubkey": "<pubkey_hex>",
    "ciphertext": "<nip44_ciphertext>"
  }
}
```

**nostr_list_manage**
```json
{
  "name": "nostr_list_manage",
  "arguments": {
    "list_kind": 10000,
    "action": "add",
    "items": [["p", "<pubkey_hex>"]]
  }
}
```

**nostr_block_list**
```json
{
  "name": "nostr_block_list",
  "arguments": {
    "type": "p"
  }
}
```

**nostr_block_edit**
```json
{
  "name": "nostr_block_edit",
  "arguments": {
    "action": "add",
    "items": [["p", "<pubkey_hex>"]],
    "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": "I can tell you the weather.",
    "description": "Fetches weather data",
    "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": "<author_pubkey_hex>",
    "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)
