Files
didactyl/plans/dm_history_context_cleanup.md

8.7 KiB

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_dmidentity_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_dmidentity_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:

#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:

"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

[
  {"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

[
  {"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.