Files
didactyl/plans/dm_history_context_cleanup.md
T

247 lines
8.7 KiB
Markdown

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