316 lines
12 KiB
Markdown
316 lines
12 KiB
Markdown
# Skill-Driven Architecture — Implementation Plan
|
|
|
|
## Summary
|
|
|
|
Overhaul Didactyl so that **skills are the universal unit of behavior** and **Nostr is the source of truth for all agent state**. The agent boots from a single nsec; everything else — context template, LLM config, relay list, personality — lives on Nostr as skills or encrypted events.
|
|
|
|
---
|
|
|
|
## Architecture Decisions (Finalized)
|
|
|
|
| Decision | Detail |
|
|
|----------|--------|
|
|
| Genesis config | `genesis.jsonc` is consumed on first run only; can be deleted afterward |
|
|
| Runtime identity seed | nsec (via CLI flag or systemd credential) is the only required input after genesis |
|
|
| Context template | Becomes the `didactyl_default` skill — first entry in kind 10123 adoption list |
|
|
| Context modes | Removed. `inject`/`full`/`override` replaced by adoption-list-order composition |
|
|
| Execution params | `llm`, `temperature`, `max_tokens`, `seed`, `tools` move from skill content to trigger tags |
|
|
| DM trigger type | New trigger type `dm` — makes DM handling consistent with all other triggers |
|
|
| LLM config storage | Kind 30078 `d=llm_config`, NIP-44 encrypted to self, stored on Nostr |
|
|
| Admin config storage | Admin pubkey stored as tag on soul event or dedicated config event on Nostr |
|
|
| Bootstrap relays | Listed in `genesis.jsonc`; agent adopts admin relay list on first run |
|
|
| Skill portability | Skills are general Nostr events; implementation-specific variables resolve to empty |
|
|
|
|
---
|
|
|
|
## Phase 1 — Specification Updates
|
|
|
|
Update the authoritative documentation to reflect all decisions before writing code.
|
|
|
|
### 1.1 Update docs/SKILLS.md
|
|
|
|
- Remove `llm`, `temperature`, `max_tokens`, `seed`, `tools` from the Content Fields table
|
|
- Add execution-parameter tags to the Trigger Tags table: `llm`, `temperature`, `max_tokens`, `seed`, `tools`
|
|
- Add `dm` trigger type with filter format `{"from":"admin"}`, `{"from":"wot"}`, `{"from":"any"}`
|
|
- Update all examples to show execution params as tags, not content fields
|
|
- Update the private skill decrypted payload example accordingly
|
|
- Remove the LLM Specification section header (move fallback-chain docs into trigger tag reference)
|
|
- Update the Execution Flow mermaid diagram
|
|
|
|
### 1.2 Update docs/CONTEXT.md
|
|
|
|
- Remove the Context Modes section entirely (inject/full/override)
|
|
- Remove the context_mode branching from the Context Assembly Flow mermaid diagram
|
|
- Replace with adoption-list-order composition model
|
|
- Update Context Parts table to reference skills instead of soul event
|
|
- Update Token Budget section to remove context_mode references
|
|
|
|
### 1.3 Write docs/GENESIS.md (new)
|
|
|
|
- Document the genesis.jsonc format and purpose
|
|
- Document the first-run flow: genesis consumed, events published, LLM config encrypted
|
|
- Document the subsequent-run flow: nsec only, everything fetched from Nostr
|
|
- Document bootstrap relay strategy
|
|
|
|
---
|
|
|
|
## Phase 2 — Genesis Startup Flow
|
|
|
|
### 2.1 Define genesis.jsonc schema
|
|
|
|
```jsonc
|
|
{
|
|
"key": { "nsec": "nsec1..." },
|
|
"admin": { "pubkey": "npub1..." },
|
|
"dm_protocol": "nip04",
|
|
"llm": {
|
|
"provider": "...",
|
|
"api_key": "...",
|
|
"model": "...",
|
|
"base_url": "...",
|
|
"max_tokens": 512,
|
|
"temperature": 0.7
|
|
},
|
|
"api": {
|
|
"enabled": true,
|
|
"port": 8484,
|
|
"bind_address": "127.0.0.1"
|
|
},
|
|
"bootstrap_relays": [
|
|
"wss://relay.damus.io",
|
|
"wss://nos.lol"
|
|
],
|
|
"startup_events": [
|
|
// Kind 0 profile, kind 10002 relay list, kind 10050 DM relays,
|
|
// kind 3 contact list, kind 31123/31124 skills,
|
|
// kind 10123 adoption list
|
|
],
|
|
"default_skill": {
|
|
// The didactyl_default skill content — soul + context template
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2.2 Implement first-run detection
|
|
|
|
- On startup, check if the agent has published a kind 10002 event (relay list) on bootstrap relays
|
|
- If no kind 10002 found: this is a first run — consume genesis.jsonc
|
|
- If kind 10002 found: this is a subsequent run — fetch everything from Nostr
|
|
|
|
### 2.3 Implement genesis event publishing
|
|
|
|
- Connect to bootstrap relays
|
|
- Fetch admin kind 10002 (relay list) and adopt those relays
|
|
- Publish all startup_events from genesis.jsonc
|
|
- Publish default_skill as kind 31123 with `["d", "didactyl_default"]`
|
|
- Publish kind 10123 adoption list with didactyl_default as first entry
|
|
- Encrypt and publish LLM config as kind 30078 `d=llm_config`
|
|
- Encrypt and publish admin pubkey + dm_protocol as kind 30078 `d=agent_config`
|
|
|
|
### 2.4 Implement nsec-only startup
|
|
|
|
- Accept nsec via `--nsec` CLI flag or `DIDACTYL_NSEC` environment variable
|
|
- Accept optional `--api-port` and `--api-bind` for local API config
|
|
- Derive pubkey from nsec
|
|
- Connect to hardcoded bootstrap relays (compiled-in fallback list)
|
|
- Fetch own kind 10002 → connect to own relays
|
|
- Fetch kind 30078 `d=llm_config` → decrypt → initialize LLM client
|
|
- Fetch kind 30078 `d=agent_config` → decrypt → get admin pubkey, dm_protocol
|
|
- Fetch kind 10123 → get adoption list
|
|
- Fetch adopted skills → build context template
|
|
- Proceed to normal operation
|
|
|
|
---
|
|
|
|
## Phase 3 — Adoption-List-Order Context Assembly
|
|
|
|
### 3.1 Refactor context building in agent.c
|
|
|
|
Current flow (agent_build_admin_messages_json):
|
|
- If prompt template exists: use prompt_template_build_messages
|
|
- Else: hardcoded context assembly (append_admin_identity_context, append_startup_events_context, append_adopted_skills_context, etc.)
|
|
|
|
New flow:
|
|
- Fetch kind 10123 adoption list
|
|
- For each adopted skill in order:
|
|
- Fetch skill content
|
|
- If skill has a template: resolve template variables via tools_execute
|
|
- Append resolved content as system message(s)
|
|
- Append DM history (if dm trigger)
|
|
- Append user message / triggering event
|
|
|
|
### 3.2 Unify template variable resolution
|
|
|
|
Current state: two template formats exist:
|
|
- YAML section format with `tool:` directives (prompt_template.c)
|
|
- `{{variable}}` inline syntax (documented in SKILLS.md but not fully implemented)
|
|
|
|
Target: single `{{variable}}` format that resolves through tools_execute.
|
|
|
|
- Implement `{{variable}}` resolution in a new or updated template engine
|
|
- Each `{{variable_name}}` calls `tools_execute(ctx, variable_name, "{}")`
|
|
- Unknown variables resolve to empty string
|
|
- The YAML section format becomes legacy (still supported for backward compat during transition)
|
|
|
|
### 3.3 Remove hardcoded context assembly
|
|
|
|
- Remove append_admin_identity_context, append_startup_events_context, append_adopted_skills_context from agent.c
|
|
- These become unnecessary because the didactyl_default skill template handles all of it via {{variable}} resolution
|
|
- Keep the functions available as tools so templates can call them
|
|
|
|
---
|
|
|
|
## Phase 4 — Execution Params on Trigger Tags
|
|
|
|
### 4.1 Update trigger_manager to read execution params from tags
|
|
|
|
Current trigger registration reads: `trigger`, `filter`, `action`, `enabled`
|
|
|
|
Add reading of: `llm`, `temperature`, `max_tokens`, `seed`, `tools`
|
|
|
|
Store these on the trigger_entry_t struct.
|
|
|
|
### 4.2 Apply execution params at invocation time
|
|
|
|
When a trigger fires:
|
|
- If `llm` tag present: temporarily override the LLM model for this execution
|
|
- If `temperature` tag present: temporarily override temperature
|
|
- If `max_tokens` tag present: temporarily override max_tokens
|
|
- If `tools` tag present: filter available tools for this execution
|
|
- After execution: restore defaults
|
|
|
|
Use the existing model_set/model_get pattern (llm_set_config/llm_get_config) for temporary overrides.
|
|
|
|
### 4.3 Remove execution params from skill content parsing
|
|
|
|
- Stop reading `llm`, `temperature`, `max_tokens`, `seed`, `tools` from skill content JSON
|
|
- These fields in content are ignored (backward compat: warn if present)
|
|
|
|
---
|
|
|
|
## Phase 5 — DM Trigger Type
|
|
|
|
### 5.1 Add dm trigger type to trigger_manager
|
|
|
|
- New trigger type: `TRIGGER_TYPE_DM`
|
|
- Filter format: `{"from":"admin"}`, `{"from":"wot"}`, `{"from":"any"}`
|
|
- Registration: when loading skills with `["trigger", "dm"]`, register as DM trigger
|
|
|
|
### 5.2 Refactor agent_on_message to use trigger dispatch
|
|
|
|
Current flow:
|
|
- agent_on_message receives DM
|
|
- Checks sender tier
|
|
- Builds context directly
|
|
- Calls LLM
|
|
|
|
New flow:
|
|
- agent_on_message receives DM
|
|
- Checks sender tier
|
|
- Finds matching DM trigger(s) from registered triggers
|
|
- For each matching trigger: execute via the standard trigger execution path
|
|
- If no DM trigger matches: fall back to default behavior (or reject)
|
|
|
|
### 5.3 didactyl_default gets a dm trigger
|
|
|
|
The default skill in genesis.jsonc includes:
|
|
|
|
```json
|
|
["trigger", "dm"],
|
|
["filter", "{\"from\":\"admin\"}"],
|
|
["llm", "default"],
|
|
["tools", "true"],
|
|
["enabled", "true"]
|
|
```
|
|
|
|
This makes the normal admin DM conversation a triggered skill execution, consistent with everything else.
|
|
|
|
---
|
|
|
|
## Phase 6 — Encrypted Config Storage
|
|
|
|
### 6.1 Implement config_store tool
|
|
|
|
New tool: `config_store` — encrypts and publishes agent config to Nostr
|
|
|
|
- Kind 30078 with configurable d-tag
|
|
- NIP-44 encrypted to self (same pattern as memory tool)
|
|
- Used for: `d=llm_config`, `d=agent_config`
|
|
|
|
### 6.2 Implement config_recall tool
|
|
|
|
New tool: `config_recall` — fetches and decrypts agent config from Nostr
|
|
|
|
- Query kind 30078 by d-tag and own pubkey
|
|
- NIP-44 decrypt
|
|
- Return JSON content
|
|
|
|
### 6.3 Use config tools during startup
|
|
|
|
- During genesis: call config_store for llm_config and agent_config
|
|
- During nsec-only startup: call config_recall to recover LLM and agent config
|
|
- These tools are also available to the LLM for runtime config changes
|
|
|
|
---
|
|
|
|
## Phase 7 — Documentation Cleanup
|
|
|
|
### 7.1 Update README.md
|
|
|
|
- Update startup instructions to reflect genesis.jsonc
|
|
- Update architecture overview
|
|
- Remove references to config.jsonc as the primary config
|
|
|
|
### 7.2 Deprecate config.jsonc
|
|
|
|
- Keep config.jsonc.example as reference but mark as legacy
|
|
- Document migration path from config.jsonc to genesis.jsonc
|
|
|
|
### 7.3 Update context_template.md
|
|
|
|
- Mark as legacy/deprecated
|
|
- Point to didactyl_default skill as the replacement
|
|
|
|
---
|
|
|
|
## File Change Summary
|
|
|
|
| File | Change Type | Description |
|
|
|------|------------|-------------|
|
|
| `docs/SKILLS.md` | Modify | Execution params to trigger tags, add dm trigger type |
|
|
| `docs/CONTEXT.md` | Modify | Remove context_mode, adoption-list composition |
|
|
| `docs/GENESIS.md` | New | Genesis config documentation |
|
|
| `genesis.jsonc` | Modify | Add bootstrap_relays, flesh out default_skill and startup_events |
|
|
| `src/agent.c` | Major refactor | Adoption-list context assembly, DM trigger dispatch |
|
|
| `src/config.c` | Modify | Support genesis.jsonc format, nsec-only mode |
|
|
| `src/config.h` | Modify | New config structs for minimal runtime config |
|
|
| `src/main.c` | Modify | First-run detection, nsec CLI flag, genesis flow |
|
|
| `src/trigger_manager.c` | Modify | DM trigger type, execution params from tags |
|
|
| `src/trigger_manager.h` | Modify | New trigger type enum, execution param fields |
|
|
| `src/prompt_template.c` | Modify | {{variable}} resolution engine |
|
|
| `src/tools/tool_config.c` | New | config_store and config_recall tools |
|
|
| `src/tools/tools_dispatch.c` | Modify | Register new config tools |
|
|
| `src/tools/tools_schema.c` | Modify | Schema for new config tools |
|
|
| `README.md` | Modify | Updated startup docs |
|
|
| `context_template.md` | Deprecate | Replaced by didactyl_default skill |
|
|
| `config.jsonc.example` | Deprecate | Replaced by genesis.jsonc |
|
|
|
|
---
|
|
|
|
## Dependency Order
|
|
|
|
```mermaid
|
|
graph TD
|
|
P1[Phase 1: Spec Updates] --> P2[Phase 2: Genesis Startup]
|
|
P1 --> P3[Phase 3: Adoption-List Context]
|
|
P1 --> P4[Phase 4: Execution Params on Tags]
|
|
P1 --> P5[Phase 5: DM Trigger Type]
|
|
P2 --> P6[Phase 6: Encrypted Config Storage]
|
|
P3 --> P5
|
|
P4 --> P5
|
|
P5 --> P7[Phase 7: Documentation Cleanup]
|
|
P6 --> P7
|
|
```
|
|
|
|
Phases 2, 3, 4 can proceed in parallel after Phase 1. Phase 5 depends on 3 and 4. Phase 6 depends on 2. Phase 7 is last.
|