Files
didactyl/docs/SKILLS.md

13 KiB

Didactyl — Skills

See also: CONTEXT.md · TOOLS.md

Overview

A skill is a set of instructions for the LLM stored as a Nostr event. Skills teach the agent how to accomplish tasks — the LLM reads the instructions, reasons about them, and uses tools to take action.

Think of it like a woodshop: a skill is knowing how to carve — the technique, the judgment, the decision-making. A tool is the chisel. 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.

Skills are portable, shareable, and discoverable — they live on Nostr relays as standard events. Every skill execution involves the LLM.


Skill Events

Kind Purpose Replaceable?
31123 Public skill definition Yes, by d-tag
31124 Private skill definition Yes, by d-tag
10123 Skill adoption list Yes, single per pubkey

Skill Content

The skill event's content field is a JSON object that defines the complete execution specification:

{
  "kind": 31123,
  "content": {
    "description": "Check spelling and grammar",
    "context_mode": "full",
    "llm": "openai/gpt-4o-mini, cheap",
    "tools": false,
    "max_tokens": 2000,
    "temperature": 0.1,
    "seed": 42,
    "template": "system:\nYou are a spelling and grammar checker.\n\nRules:\n- Fix spelling errors\n- Fix grammar errors\n- Preserve original formatting\n- Ignore: API, JSON, HTTP, nostr, pubkey, npub, nsec, NIP\n- Canadian English preferred\n- Return ONLY the corrected text, no explanations\n\nuser:\n{{message}}"
  },
  "tags": [
    ["d", "spellcheck"],
    ["scope", "public"],
    ["description", "Spelling and grammar checker"]
  ]
}

Content Fields

Field Type Default Description
description string Human-readable description
context_mode string inject inject, full, or override
llm string default LLM fallback chain
tools bool/array true false = no tools, true = all tools, array = specific tool names
template string Context template (required for full mode)
soul bool true Whether to include the agent's soul in context
max_tokens int Override max tokens for this skill
temperature float Override temperature for this skill
seed int Optional deterministic seed (effective only on providers/engines that support seeded sampling)

Determinism Controls

For reproducible outputs, set temperature low (typically 0) and also set seed when the selected model backend supports it. If a provider ignores seeds, temperature still applies but deterministic replay is not guaranteed.


Private Skill Encoding (31124)

Private skills use NIP-44 encryption on the event content field.

Rules for kind 31124:

  • Keep d tag exposed so the event remains addressable/replaceable.
  • Move all other skill metadata (including non-d tags) into the plaintext payload before encryption.
  • Encrypt the full payload with NIP-44 and store the ciphertext in event content.
  • On receive: resolve by d, decrypt content, then read both content variables and tag values from decrypted payload.

Private Skill Event (on relay)

{
  "kind": 31124,
  "content": "<nip44-ciphertext>",
  "tags": [
    ["d", "mention-monitor"]
  ]
}

Decrypted Private Payload (application-level JSON)

{
  "content": {
    "description": "Monitor mentions and DM summaries",
    "context_mode": "inject",
    "llm": "openai/gpt-4o-mini, fast",
    "tools": ["nostr_query", "nostr_dm"],
    "temperature": 0,
    "seed": 42,
    "template": "When {{triggering_event}} includes Bitcoin or Lightning, summarize and DM admin."
  },
  "private_tags": [
    ["scope", "private"],
    ["trigger", "nostr-subscription"],
    ["filter", "{\"#p\":[\"<admin_pubkey>\"],\"kinds\":[1]}"],
    ["enabled", "true"]
  ]
}

This mirrors the DM-style encrypted payload pattern (historically associated with NIP-04), but private skills should use NIP-44 ciphertext encoding.

Context Modes

Skills control how the LLM context window is assembled using the context_mode field:

Mode Description Use Case
inject Skill instructions appended to the agent's default context Behavioral rules, knowledge additions
full Skill provides its own complete context template Spellcheck, translation, focused analysis
override Skill replaces the default system prompt, keeps standard context structure Different personality, same capabilities

Default is inject. See the agent's soul/personality documentation for details on how context assembly works.

Template Variables Are Tool Calls

Template variables are tool calls. When the template engine encounters a variable like {{admin_profile}}, it runs the corresponding tool and inserts the result into the context that gets sent to the LLM.

This is the unified resolution model: the soul prompt template uses tool: section directives, and skill templates use {{tool_name}} inline syntax, but both resolve through tools_execute(). There is no separate variable-resolver layer — every variable is backed by a tool.

Variable Tool Called Description
{{soul}} (built-in) The agent's identity/system context
{{admin_profile}} nostr_admin_profile Admin kind 0 profile
{{admin_notes}} nostr_admin_notes Admin recent notes
{{admin_relays}} nostr_admin_relays Admin relay list
{{adopted_skills}} adopted_skills Other adopted skill instructions
{{dm_history}} (expand directive) Recent DM conversation
{{message}} (built-in) Current user message
{{triggering_event}} trigger_event For triggered skills, the event JSON

This means new data sources are added by adding tools, not by extending the template engine. If you can call it as a tool, you can use it as a template variable.


LLM Specification with Fallback Chains

Each skill specifies which LLM to use with CSS font-family-style fallbacks. The runtime tries each model in order; if one is unavailable (API error, rate limit, not configured), it falls back to the next.

Format

model-spec := model-ref ["," model-ref]*
model-ref  := provider "/" model-name
            | category-alias

Examples

Skill LLM Spec
Spelling checker openai/gpt-4o-mini, cheap
Complex analysis anthropic/claude-sonnet-4-20250514, openai/gpt-4o, smart
Translation openai/gpt-4o-mini, fast
Default behavior default

Category Aliases

Aliases map to models in the agent's config:

{
  "llm": {
    "provider": "openai",
    "model": "gpt-4o",
    "aliases": {
      "fast": "openai/gpt-4o-mini",
      "cheap": "openai/gpt-4o-mini",
      "smart": "anthropic/claude-sonnet-4-20250514"
    }
  }
}

If all specified models fail, the agent's default model is used as a last resort.

sequenceDiagram
    participant Skill as Skill Spec
    participant Resolver as LLM Resolver
    participant API1 as Model 1
    participant API2 as Model 2
    participant Default as Default Model

    Skill->>Resolver: "anthropic/claude-sonnet, openai/gpt-4o-mini, cheap"
    Resolver->>API1: try anthropic/claude-sonnet
    alt Available
        API1-->>Resolver: ✅
    else Unavailable
        API1-->>Resolver: ❌
        Resolver->>API2: try openai/gpt-4o-mini
        alt Available
            API2-->>Resolver: ✅
        else Unavailable
            Resolver->>Default: resolve "cheap" alias
        end
    end

Triggered Skills

A triggered skill has a trigger source attached. Didactyl supports nostr-subscription, webhook, cron, and chain trigger types.

Trigger Tags

Tag Required Description
trigger Yes Trigger type: nostr-subscription, webhook, cron, or chain
filter Yes Type-specific filter — see Trigger Types below
enabled No Whether active (default: true)

Note: The action tag is deprecated. All triggered skills are LLM-mediated. If you need a fast hardcoded action without LLM reasoning, use a tool or an external program — not a skill. Skills are instructions for the LLM; the LLM is always in the loop.

Triggered Skill Execution

When a trigger fires, the skill content is sent to the LLM as instructions along with the triggering event JSON. The LLM reads both, reasons about what to do, and uses tools to take action.

This is the same execution model as DM-invoked skills — the only difference is the input source. A DM-invoked skill receives a user message; a triggered skill receives a triggering event.

{
  "kind": 31123,
  "content": "You monitor Nostr mentions. When the triggering event mentions Bitcoin or Lightning, summarize it and DM the admin. Otherwise, ignore silently.",
  "tags": [
    ["d", "mention-monitor"],
    ["trigger", "nostr-subscription"],
    ["filter", "{\"#p\":[\"<admin_pubkey>\"],\"kinds\":[1]}"],
    ["enabled", "true"]
  ]
}

A cron-triggered skill:

{
  "kind": 31123,
  "content": "DM the admin a short heartbeat status message with the current time.",
  "tags": [
    ["d", "heartbeat"],
    ["trigger", "cron"],
    ["filter", "*/5 * * * *"],
    ["enabled", "true"]
  ]
}

Trigger Types

nostr-subscription

filter is a JSON-encoded Nostr subscription filter.

["trigger", "nostr-subscription"],
["filter", "{\"#p\":[\"<admin_pubkey>\"],\"kinds\":[1]}"]

webhook

filter is required for schema compatibility and can be a simple placeholder like {}; webhook firing happens via HTTP.

["trigger", "webhook"],
["filter", "{}"]

cron

filter is a standard 5-field cron expression: minute hour day-of-month month day-of-week.

["trigger", "cron"],
["filter", "0 * * * *"]

chain

filter is the source skill d tag to chain from.

["trigger", "chain"],
["filter", "source-skill-d-tag"]

Trigger Lifecycle

flowchart TD
    subgraph Creation
        ADMIN_CMD[Admin: 'Warn me when @jack posts'] --> LLM_REASON[LLM resolves pubkey + builds skill]
        LLM_REASON --> SKILL_CREATE[skill_create with trigger tags]
        SKILL_CREATE --> PUBLISHED[Skill published to Nostr]
    end

    subgraph Activation
        STARTUP[Didactyl starts up] --> LOAD_SKILLS[Load adopted skills from kind 10123]
        LOAD_SKILLS --> FIND_TRIGGERS[Find skills with trigger tags]
        FIND_TRIGGERS --> REGISTER[Register trigger by type]
        REGISTER --> NOSTR_SUB[nostr-subscription: create Nostr subscriptions]
        REGISTER --> CRON_REG[cron: keep expression for poll loop]
        REGISTER --> WEBHOOK_REG[webhook: route via /api/trigger/:d_tag]
        REGISTER --> CHAIN_REG[chain: wait for source skill completion]
    end

    subgraph Execution
        EVENT_IN[Matching Nostr event] --> LOOKUP[Find associated skill]
        WEBHOOK_IN[POST /api/trigger/:d_tag] --> LOOKUP
        CRON_TICK[cron poll match] --> LOOKUP
        LOOKUP --> RESOLVE[Resolve LLM + assemble context + run with tools]
        RESOLVE --> CHAIN_CHECK[Check chain triggers]
        CHAIN_CHECK --> CHAIN_FIRE[Fire matching chain triggers]
    end

    PUBLISHED --> LOAD_SKILLS

Execution Flow

sequenceDiagram
    participant Input as Message/Trigger
    participant Dispatch as Dispatcher
    participant Skill as Skill Resolver
    participant LLM_Res as LLM Resolver
    participant Ctx as Context Assembler
    participant LLM as LLM API

    Input->>Dispatch: message or trigger event
    Dispatch->>Skill: which skill handles this?

    alt No specific skill
        Skill-->>Dispatch: use default soul context
        Dispatch->>LLM_Res: resolve "default" LLM
    else Skill with context_mode=inject
        Skill-->>Dispatch: soul + skill instructions
        Dispatch->>LLM_Res: resolve skill.llm or "default"
    else Skill with context_mode=full
        Skill-->>Dispatch: skill template only
        Dispatch->>LLM_Res: resolve skill.llm
    end

    LLM_Res->>LLM_Res: try models in fallback order
    LLM_Res-->>Dispatch: resolved model

    Dispatch->>Ctx: assemble context per mode
    Ctx->>LLM: request with resolved model + context
    LLM-->>Input: response

Limits and Safety

Limit Default Description
Max concurrent triggers 16 Prevents resource exhaustion
Trigger cooldown 60s per skill Prevents rapid-fire execution
LLM action rate limit 10/min Prevents runaway LLM costs

Storage on Nostr

Data Storage
Skills Kind 31123/31124 events
Adopted skills Kind 10123 event
Trigger definitions Tags on skill events

Future Extensions

Extension Description
Skill composition Pipeline multiple skills
Agent-to-agent sharing Discover and adopt skills across agents
Trigger marketplace Popular triggers rise via adoption count

  • Tool architecture and complete tool catalog: TOOLS.md
  • Project overview and runtime behavior: README.md