# Triggered Skills — Implementation Plan

## Overview

Extend the existing skill system so that skills can carry Nostr subscription triggers. When matching events arrive, didactyl executes the skill automatically — either via template interpolation (fast, no LLM) or LLM-mediated reasoning (full agent loop).

See [docs/TOOLS_AND_SKILLS.md](../docs/TOOLS_AND_SKILLS.md) for the full architecture.

---

## Implementation Steps

### 1. Trigger Manager Module

Create `src/trigger_manager.c` and `src/trigger_manager.h` — the core component that manages dynamic Nostr subscriptions tied to skills.

**Data structures:**

```c
#define TRIGGER_MAX_ACTIVE 16
#define TRIGGER_COOLDOWN_SECONDS 60

typedef struct {
    char skill_d_tag[65];
    char skill_content[4096];   // the action template or LLM prompt
    int action_type;            // 0 = llm, 1 = template
    char filter_json[2048];     // the Nostr subscription filter
    int enabled;
    time_t last_fired;
    nostr_pool_subscription_t* subscription;
} active_trigger_t;

typedef struct {
    active_trigger_t triggers[TRIGGER_MAX_ACTIVE];
    int count;
    didactyl_config_t* cfg;
    pthread_mutex_t mutex;
} trigger_manager_t;
```

**API:**

```c
int trigger_manager_init(trigger_manager_t* mgr, didactyl_config_t* cfg);
int trigger_manager_load_from_skills(trigger_manager_t* mgr);
int trigger_manager_add(trigger_manager_t* mgr, const char* skill_d_tag, 
                        const char* content, const char* filter_json, 
                        int action_type, int enabled);
int trigger_manager_remove(trigger_manager_t* mgr, const char* skill_d_tag);
int trigger_manager_update(trigger_manager_t* mgr, const char* skill_d_tag,
                           const char* content, const char* filter_json,
                           int action_type, int enabled);
int trigger_manager_active_count(trigger_manager_t* mgr);
char* trigger_manager_status_json(trigger_manager_t* mgr);
void trigger_manager_cleanup(trigger_manager_t* mgr);
```

### 2. Template Engine

Create a simple template interpolation engine in `src/trigger_manager.c` (or a separate `src/template.c` if it grows).

**Functionality:**
- Parse placeholders like `{content}`, `{pubkey}`, `{author_display_name}` from a template string
- Extract values from a triggering Nostr event (cJSON object)
- Produce an interpolated output string
- Parse action prefixes: `DM admin:`, `DM <pubkey>:`, `POST:`, `LOG:`

### 3. Trigger Event Callback

When a subscribed event arrives:

```c
static void on_trigger_event(cJSON* event, const char* relay_url, void* user_data) {
    active_trigger_t* trigger = (active_trigger_t*)user_data;
    
    // Check cooldown
    if (time(NULL) - trigger->last_fired < TRIGGER_COOLDOWN_SECONDS) return;
    trigger->last_fired = time(NULL);
    
    if (trigger->action_type == 1) {
        // Template: interpolate and execute
        char* output = template_interpolate(trigger->skill_content, event);
        template_execute_action(output);  // parse prefix, DM/POST/LOG
        free(output);
    } else {
        // LLM: build context and run agent loop
        trigger_run_llm_action(trigger, event, relay_url);
    }
}
```

### 4. Skill Loading on Startup

In `main.c`, after `agent_init()` and skill adoption list is available:

1. Query own kind 10123 adoption list
2. For each adopted skill address, query the skill event
3. Check for `trigger` tag — if present, extract `filter`, `action`, `enabled` tags
4. Register with trigger manager
5. Trigger manager creates Nostr subscriptions

### 5. Extend skill_create for Live Trigger Registration

When `skill_create` is called with trigger tags:
1. Publish the skill event as normal
2. If trigger tags are present, also register with the trigger manager immediately
3. No restart required — the subscription goes live right away

When `skill_remove` is called for a triggered skill:
1. Remove from adoption list as normal
2. Also unregister from trigger manager, tearing down the subscription

### 6. Extend Agent for Trigger-Initiated Conversations

The agent currently only handles DM-initiated conversations via `agent_on_message()`. Add a new entry point:

```c
void agent_on_trigger(const char* skill_d_tag,
                      const char* skill_content,
                      cJSON* triggering_event,
                      const char* relay_url);
```

This builds an LLM conversation with:
- System context (soul)
- A system message explaining this is a triggered skill execution
- The skill content as instructions
- The triggering event as user context
- Full tool access (same as admin tier)

The LLM response actions (DMs, posts, etc.) are executed via tools as normal.

### 7. New Tool: trigger_list

Add a tool so the LLM can inspect active triggers:

```json
{
  "name": "trigger_list",
  "description": "List all active triggered skills with their filters and status",
  "parameters": { "type": "object", "properties": {} }
}
```

### 8. Config Extension

Add trigger-related limits to config:

```json
{
  "triggers": {
    "enabled": true,
    "max_active": 16,
    "cooldown_seconds": 60,
    "llm_rate_limit_per_minute": 10,
    "template_rate_limit_per_minute": 60
  }
}
```

### 9. Integration into Main Loop

The trigger manager subscriptions are serviced by the same `nostr_handler_poll()` call in the main loop — no changes needed to the poll loop itself, since all subscriptions share the relay pool.

---

## File Changes Summary

| File | Change |
|---|---|
| `src/trigger_manager.c` | **NEW** — trigger manager, template engine, event callbacks |
| `src/trigger_manager.h` | **NEW** — trigger manager API |
| `src/main.c` | Add trigger_manager_init, trigger_manager_load_from_skills after agent_init |
| `src/agent.c` | Add agent_on_trigger entry point for LLM-mediated trigger actions |
| `src/agent.h` | Declare agent_on_trigger |
| `src/tools.c` | Extend skill_create/skill_remove to register/unregister triggers; add trigger_list tool |
| `src/config.h` | Add triggers_config_t struct |
| `src/config.c` | Parse triggers config section |
| `docs/TOOLS_AND_SKILLS.md` | Already written — full architecture reference |

---

## Implementation Order

1. `trigger_manager.h` / `trigger_manager.c` — core module with data structures and API stubs
2. Template engine — interpolation and action prefix parsing
3. Trigger event callback — on_trigger_event with cooldown
4. Startup loading — query adoption list, find triggered skills, create subscriptions
5. agent_on_trigger — LLM-mediated trigger execution path
6. Live registration — extend skill_create/skill_remove for immediate trigger management
7. trigger_list tool — LLM visibility into active triggers
8. Config parsing — triggers section with limits
9. Rate limiting — enforce LLM and template rate limits
10. Testing — manual trigger creation and verification

---

## Dependencies

- Existing `nostr_handler_query_json()` for loading skills
- Existing `nostr_relay_pool_subscribe()` for creating trigger subscriptions
- Existing `agent_on_message()` pattern for the LLM-mediated path
- Existing `skill_create` / `skill_remove` tools for lifecycle hooks
