# DM History Context Cleanup, Skill Rename & Startup Events Unification ## Problems ### 1. DM History as JSON in System Prompt The `{{nostr_dm_history}}` template variable resolves to a raw JSON array string pasted into the system prompt. The LLM receives conversation history as JSON-within-text, wasting tokens and forcing it to parse structured data embedded in prose. ### 2. Misleading Skill Name The skill `default_admin_dm` is really an identity and rules skill. Rename to `identity_and_rules`. ### 3. `default_skill` Config Section Is Redundant The `default_skill` section in genesis.jsonc is a special-case config path that converts a skill into a startup event and auto-adopts it. This is unnecessary — skills should just be startup events like everything else. The program should treat all startup events equally and halt if any fail to publish. ## Solution Three coordinated changes: ### Part A: DM History Text Format Add a `format` parameter to the `nostr_dm_history` tool. Create a separate `dm_history` skill. ### Part B: Skill Rename Rename `default_admin_dm` → `identity_and_rules` everywhere. ### Part C: Eliminate `default_skill` Config Section Move skills into `startup_events`. Auto-adopt kind 31123/31124 startup events. Make startup event publishing mandatory (halt on failure). --- ## Implementation Steps ### Part A: DM History Text Format #### A1. Add `format` parameter to `execute_nostr_dm_history()` **File:** `src/tools/tool_agent.c` — line 265 Add a `format` parameter: `"json"` (default, current behavior) or `"text"`. When `format` is `"text"`, after the filtering loop (lines 320-360), iterate the filtered array and build plain text: ``` User: Good morning Assistant: Good morning! ☀️ How can I help you today? User: What is the capital of England? Assistant: London! 🇬🇧 ``` Existing `include_current=false` default already deduplicates the current live message (line 339). #### A2. Add `format` to the tool schema **File:** `src/tools/tools_schema.c` — around line 1493 Add `format` property with description. Update tool description to mention the parameter. --- ### Part B: Skill Rename #### B1. Update `src/default_events.h` - `DIDACTYL_DEFAULT_SKILL_D_TAG`: `"default_admin_dm"` → `"identity_and_rules"` - `DIDACTYL_DEFAULT_SKILL_TAGS_JSON`: update d-tag and description - `DIDACTYL_DEFAULT_SKILL_TEMPLATE`: keep as-is (already clean, no `{{nostr_dm_history}}`) #### B2. Update tool description examples in `src/tools/tools_schema.c` - Line 854: `skill_get` example - Line 883: `skill_view` example - Line 911: `skill_set` example #### B3. Update `docs/GENESIS.md` - Line 33 and 124: `default_admin_dm` → `identity_and_rules` --- ### Part C: Eliminate `default_skill` Config Section #### C1. Auto-adopt kind 31123/31124 startup events **File:** `src/config.c` Replace `config_ensure_default_skill_startup_events()` with a new function `config_ensure_startup_skill_adoption()` that: 1. Scans all startup events for kind 31123 and 31124 2. For each skill found, extracts its `d_tag` from tags 3. Builds the skill address (`kind:pubkey:d_tag`) 4. Ensures a kind 10123 adoption list startup event exists with all skill addresses 5. If no 10123 event exists, creates one This replaces the single-skill logic with a generic "adopt all startup skills" approach. #### C2. Remove `default_skill` config parsing **File:** `src/config.c` - Remove `parse_default_skill_config()` function (lines 733-816) - Remove the call at line 1369 - Keep backward compatibility: if `default_skill` section exists in genesis.jsonc, convert it to a startup event during parsing (migration path) — OR just remove support and let users update their genesis.jsonc #### C3. Remove `default_skill_config_t` from config struct **File:** `src/config.h` - Remove `default_skill_config_t` typedef (lines 67-72) - Remove `default_skill` field from `didactyl_config_t` (line 134) #### C4. Update `src/main.c` - Line 1060: Replace `config_ensure_default_skill_startup_events()` call with `config_ensure_startup_skill_adoption()` #### C5. Make startup event publishing mandatory **File:** `src/nostr_handler.c` In the startup publish flow (around line 3150), after all relays have been tried: - Check that each startup event was published to at least one relay - If any event failed on all relays, log an error and return failure - The caller in `main.c` should halt on this failure #### C6. Update setup wizard **File:** `src/setup_wizard.c` - `configure_default_skill_for_agent()` (line 498): Instead of populating `cfg->default_skill`, append the identity skill as a startup event directly - Add the `dm_history` skill as a second startup event - The auto-adoption logic (C1) handles the adoption list #### C7. Update `default_events.h` Add the `dm_history` skill template: ```c #define DIDACTYL_DEFAULT_DM_HISTORY_SKILL_D_TAG "dm_history" static const char* DIDACTYL_DEFAULT_DM_HISTORY_SKILL_TEMPLATE = "## Recent Conversation\n\n" "{{nostr_dm_history({\"format\":\"text\",\"limit\":12})}}"; ``` --- ### Part D: Genesis Config Updates #### D1. Update `genesis.jsonc.example` Remove `default_skill` section. Add skills as startup events: ```jsonc "startup_events": [ { "kind": 0, "content_fields": { "name": "Didactyl", "about": "I am a Didactyl agent living on Nostr" }, "tags": [] }, { "kind": 3, "content": "", "tags": [["p", "ADMIN_HEX_PUBKEY"]] }, { "kind": 10002, "content": "", "tags": [ ["r", "wss://relay.damus.io"], ["r", "wss://relay.primal.net"] ] }, { "kind": 31124, "content": "# Didactyl Agent\n\nYou are {{my_kind0_profile}}\n\nYour npub: {{my_npub}}\n\n## Rules\n\n- Communicate through encrypted Nostr direct messages\n- Keep responses concise and clear\n- Be helpful and technically accurate\n- If unsure, state uncertainty directly\n- Use tools when a request requires taking action\n- After a tool call, base your answer on the actual tool result\n- Never claim a tool was run if no tool was executed\n- Maintain your task list as short-term working memory\n- Never reveal your private key (nsec)\n- You may share your public key (npub) with anyone", "tags": [ ["d", "identity_and_rules"], ["app", "didactyl"], ["scope", "private"], ["description", "Agent identity and behavioral rules"], ["trigger", "dm"], ["filter", "{\"from\":\"admin\"}"] ] }, { "kind": 31124, "content": "## Recent Conversation\n\n{{nostr_dm_history({\"format\":\"text\",\"limit\":12})}}", "tags": [ ["d", "dm_history"], ["app", "didactyl"], ["scope", "private"], ["description", "DM conversation history for context continuity"], ["trigger", "dm"], ["filter", "{\"from\":\"admin\"}"] ] } ] ``` #### D2. Update live `genesis.jsonc` Same changes as D1 but for the actual deployment config. --- ## Result ### Before ```json [ {"role": "system", "content": "You are {...}\n\nYour npub: npub1...\n\n## Rules\n...\n[{\"role\":\"user\",\"content\":\"Good morning\",...}]"}, {"role": "user", "content": "And Fiji?"} ] ``` ### After ```json [ {"role": "system", "content": "# Didactyl Agent\n\nYou are {...}\n\nYour npub: npub1...\n\n## Rules\n...\n\n---\n\n## Recent Conversation\n\nUser: Good morning\nAssistant: Good morning! ☀️ How can I help you today?\nUser: What is the capital of England?\nAssistant: London! 🇬🇧\nUser: How about France?\nAssistant: Paris! 🇫🇷"}, {"role": "user", "content": "And Fiji?"} ] ``` --- ## Files Changed | File | Change | |------|--------| | `src/tools/tool_agent.c` | Add `format` parameter to `execute_nostr_dm_history()` | | `src/tools/tools_schema.c` | Add `format` to schema; update example d-tags | | `src/default_events.h` | Rename d-tag; add `dm_history` skill template | | `src/config.h` | Remove `default_skill_config_t` and field | | `src/config.c` | Remove `parse_default_skill_config()`; replace `config_ensure_default_skill_startup_events()` with `config_ensure_startup_skill_adoption()` | | `src/main.c` | Update call to new adoption function | | `src/nostr_handler.c` | Make startup event publishing mandatory (halt on total failure) | | `src/setup_wizard.c` | Put skills in startup_events; add dm_history | | `genesis.jsonc.example` | Remove `default_skill`; add skills as startup events | | `genesis.jsonc` | Same as example | | `docs/GENESIS.md` | Update references | ## Migration Note For backward compatibility, consider keeping `parse_default_skill_config()` as a migration shim that converts `default_skill` into a startup event with a deprecation warning. This way existing genesis.jsonc files with `default_skill` still work but users are prompted to update.