v0.2.24 - Improve skills editor trigger filters with per-trigger helper widgets and presets
This commit is contained in:
@@ -54,11 +54,11 @@ Skills compose by adoption-list order (`10123`) and trigger tags carry runtime e
|
|||||||
|
|
||||||
Didactyl will support local inference, which is very privacy preserving. Remote inference does however have it's advantages, and in those cases Didactyl supports using Bitcoin Lightning and eCash inference providers.
|
Didactyl will support local inference, which is very privacy preserving. Remote inference does however have it's advantages, and in those cases Didactyl supports using Bitcoin Lightning and eCash inference providers.
|
||||||
|
|
||||||
## Current Status — v0.2.23
|
## Current Status — v0.2.24
|
||||||
|
|
||||||
**Active build — this project is barely working. Experiment at your own risk.**
|
**Active build — this project is barely working. Experiment at your own risk.**
|
||||||
|
|
||||||
> Last release update: v0.2.23 — Improve skill authoring guidance and context viewing workflow: enrich skill_create/skill_edit schema descriptions for markdown content, role markers, template variables, and trigger/filter pairing; add external Anvil context.logs viewer assets (index.html + watch_context.sh) for Live Server markdown-to-HTML monitoring without Didactyl runtime changes
|
> Last release update: v0.2.24 — Improve skills editor trigger filters with per-trigger helper widgets and presets
|
||||||
|
|
||||||
- Connects to configured relays with auto-reconnect and relay state transition logging
|
- Connects to configured relays with auto-reconnect and relay state transition logging
|
||||||
- Publishes configured startup events per relay as each relay becomes connected
|
- Publishes configured startup events per relay as each relay becomes connected
|
||||||
|
|||||||
@@ -92,8 +92,8 @@ Trigger event occurs (DM, cron, subscription, webhook, chain)
|
|||||||
│ ├─ Bump all skill headings down one level (# -> ##)
|
│ ├─ Bump all skill headings down one level (# -> ##)
|
||||||
│ └─ Concatenate skills with `---` separators
|
│ └─ Concatenate skills with `---` separators
|
||||||
│
|
│
|
||||||
├─ Split Roles
|
├─ Build API roles
|
||||||
│ └─ Parse `system:` and `user:` markers to build API messages array
|
│ └─ Runtime constructs OpenAI-compatible `messages` (system + user + assistant/tool loop)
|
||||||
│
|
│
|
||||||
├─ Attach tool schemas (filtered by skill requires_tool tags)
|
├─ Attach tool schemas (filtered by skill requires_tool tags)
|
||||||
│
|
│
|
||||||
@@ -105,10 +105,9 @@ Trigger event occurs (DM, cron, subscription, webhook, chain)
|
|||||||
|
|
||||||
### Visualization
|
### Visualization
|
||||||
|
|
||||||
The assembled context is a single, coherent markdown document before being split into API messages:
|
The assembled context starts as a single markdown document (skills + resolved variables), then the runtime maps it into API role messages.
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
system:
|
|
||||||
# Didactyl Agent
|
# Didactyl Agent
|
||||||
|
|
||||||
- **npub**: `npub1...`
|
- **npub**: `npub1...`
|
||||||
@@ -123,11 +122,10 @@ You speak concisely and directly.
|
|||||||
|
|
||||||
Respond helpfully to the admin.
|
Respond helpfully to the admin.
|
||||||
Use tools as needed.
|
Use tools as needed.
|
||||||
|
|
||||||
user:
|
|
||||||
Who mentioned me today?
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
For DM triggers, the runtime appends the incoming DM text as the `user` message. For non-DM triggers, it appends the trigger payload as the `user` message.
|
||||||
|
|
||||||
### Why Order Matters
|
### Why Order Matters
|
||||||
|
|
||||||
- Earlier skills in the adoption list appear first in the context window.
|
- Earlier skills in the adoption list appear first in the context window.
|
||||||
@@ -248,17 +246,13 @@ Output files are written to:
|
|||||||
|
|
||||||
### Snapshot Formats
|
### Snapshot Formats
|
||||||
|
|
||||||
Initial snapshots (`*_init.md`) are the assembled markdown context document, including role markers and heading bump effects:
|
Initial snapshots (`*_init.md`) are the assembled markdown context document (with heading-bump effects). They reflect authored skill markdown before runtime message-role packaging:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
# Didactyl Agent
|
# Didactyl Agent
|
||||||
|
|
||||||
system:
|
|
||||||
## Anvil Agent
|
## Anvil Agent
|
||||||
...
|
...
|
||||||
|
|
||||||
user:
|
|
||||||
Who was WSB historically?
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Turn snapshots (`*_tNNN.md`) are human-readable markdown renderings of the OpenAI-compatible `messages` array used for that loop turn:
|
Turn snapshots (`*_tNNN.md`) are human-readable markdown renderings of the OpenAI-compatible `messages` array used for that loop turn:
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ The `content` field of a skill event IS the template — markdown instructions t
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"kind": 31123,
|
"kind": 31123,
|
||||||
"content": "system:\n## Spelling and Grammar Checker\n\nYou are a spelling and grammar checker.\n\n### Rules\n\n- Fix spelling errors\n- Fix grammar errors\n- Preserve original formatting\n- Return **ONLY** the corrected text, no explanations\n\nuser:\n{{message}}",
|
"content": "## Spelling and Grammar Checker\n\nYou are a spelling and grammar checker.\n\n### Rules\n\n- Fix spelling errors\n- Fix grammar errors\n- Preserve original formatting\n- Return **ONLY** the corrected text, no explanations\n\nInput text:\n{{message}}",
|
||||||
"tags": [
|
"tags": [
|
||||||
["d", "spellcheck"],
|
["d", "spellcheck"],
|
||||||
["description", "Check spelling and grammar"],
|
["description", "Check spelling and grammar"],
|
||||||
@@ -80,8 +80,8 @@ The `content` field of a skill event IS the template — markdown instructions t
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- **`content`** — the template in markdown. May include `{{...}}` template variables and `system:` / `user:` role markers. This is what goes into the context window.
|
- **`content`** — the template in markdown. May include `{{...}}` template variables. This is what goes into the context window.
|
||||||
- **Role Markers** — `system:`, `user:`, and `assistant:` are parsed only when they appear at the start of a line. Content without a marker defaults to `system:`.
|
- **Role ownership** — skill authors should write plain markdown instructions, not `system:` / `user:` / `assistant:` markers. The runtime owns role construction and appends trigger payloads (for DM triggers, raw message text) as the `user` message at execution time.
|
||||||
- **Heading Levels** — The runtime owns the `#` (h1) heading for the document title. All headings in skill content are automatically bumped down one level (`#` becomes `##`, `##` becomes `###`) during context assembly. Skill authors should use `##` for their top-level sections.
|
- **Heading Levels** — The runtime owns the `#` (h1) heading for the document title. All headings in skill content are automatically bumped down one level (`#` becomes `##`, `##` becomes `###`) during context assembly. Skill authors should use `##` for their top-level sections.
|
||||||
- **`["description", "..."]`** — human-readable description for discovery and UI display.
|
- **`["description", "..."]`** — human-readable description for discovery and UI display.
|
||||||
- Each `["tag", "value"]` is a separate tag on the Nostr event.
|
- Each `["tag", "value"]` is a separate tag on the Nostr event.
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ These tools manage skill and trigger lifecycle; skill semantics and trigger exec
|
|||||||
|
|
||||||
| Tool | Description |
|
| Tool | Description |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `skill_create` | Create or update a skill definition as kind `31123`/`31124` and optionally auto-adopt it (content should be markdown with optional `system:`/`user:`/`assistant:` role markers) |
|
| `skill_create` | Create or update a skill definition as kind `31123`/`31124` and optionally auto-adopt it (content should be markdown instructions; runtime handles role packaging) |
|
||||||
| `skill_edit` | Edit an existing self skill by d tag and republish it as kind `31123`/`31124` (content should remain markdown-first) |
|
| `skill_edit` | Edit an existing self skill by d tag and republish it as kind `31123`/`31124` (content should remain markdown-first) |
|
||||||
| `skill_list` | List available skills discovered online (agent + admin), with adoption status and optional filters |
|
| `skill_list` | List available skills discovered online (agent + admin), with adoption status and optional filters |
|
||||||
| `skill_adopt` | Adopt a skill by adding its address to kind `10123` adoption list |
|
| `skill_adopt` | Adopt a skill by adding its address to kind `10123` adoption list |
|
||||||
@@ -469,7 +469,7 @@ These examples show the JSON structure for tool calls.
|
|||||||
"name": "skill_create",
|
"name": "skill_create",
|
||||||
"arguments": {
|
"arguments": {
|
||||||
"d": "weather-skill",
|
"d": "weather-skill",
|
||||||
"content": "system:\n## Weather Assistant\n\nYou provide concise weather summaries.\n\nuser:\n{{message}}",
|
"content": "## Weather Assistant\n\nYou provide concise weather summaries.\n\nInput:\n{{message}}",
|
||||||
"description": "Fetches weather data",
|
"description": "Fetches weather data",
|
||||||
"trigger": "dm",
|
"trigger": "dm",
|
||||||
"filter": "{\"from\":\"admin\"}",
|
"filter": "{\"from\":\"admin\"}",
|
||||||
|
|||||||
94
plans/remove_role_markers_from_skills.md
Normal file
94
plans/remove_role_markers_from_skills.md
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
# Remove Role Markers from Skill Content
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Skill content should always be **system-role instructions** — pure markdown. The `system:`, `user:`, and `assistant:` role markers should not appear in skill content authored by users or the LLM agent.
|
||||||
|
|
||||||
|
The runtime already handles `user:` injection internally (appending the DM message or trigger event as the user message). Skill authors writing `user:` in their content is either redundant or creates conflicts (double `user:` markers).
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
### Principle
|
||||||
|
|
||||||
|
- **Skill content = system instructions.** Always. No role markers.
|
||||||
|
- **The runtime owns role assignment.** It injects `user:` before the actual input (DM, trigger event).
|
||||||
|
- **`context_roles_split()` stays** — the runtime still uses it internally to split the assembled markdown (which includes the runtime-injected `user:` marker) into API messages.
|
||||||
|
|
||||||
|
### Files to Change
|
||||||
|
|
||||||
|
#### Documentation
|
||||||
|
|
||||||
|
| File | Change |
|
||||||
|
|------|--------|
|
||||||
|
| `docs/SKILLS.md` | Remove role markers from skill content spec and examples. Clarify that skill content is always system instructions. |
|
||||||
|
| `docs/CONTEXT.md` | Clarify that `user:` is injected by the runtime, not by skill authors. Update examples. |
|
||||||
|
| `docs/TOOLS.md` | Remove role marker references from `skill_create` and `skill_edit` descriptions. |
|
||||||
|
|
||||||
|
#### Source Code
|
||||||
|
|
||||||
|
| File | Change |
|
||||||
|
|------|--------|
|
||||||
|
| `src/tools/tools_schema.c` | Remove role marker references from `skill_create` and `skill_edit` tool descriptions and parameter descriptions. |
|
||||||
|
| `src/context_roles.c` | **No change** — still needed for runtime-level `user:` splitting. |
|
||||||
|
| `src/context_roles.h` | **No change**. |
|
||||||
|
| `src/agent.c` | **No change** — already injects `user:` correctly. |
|
||||||
|
|
||||||
|
#### Web Pages
|
||||||
|
|
||||||
|
| File | Change |
|
||||||
|
|------|--------|
|
||||||
|
| `~/lt/client-ndk/www/skills-edit.html` | Remove `system:`/`user:`/`assistant:` syntax highlighting from the editor overlay. Update placeholder text. |
|
||||||
|
|
||||||
|
#### Plans (historical docs, lower priority)
|
||||||
|
|
||||||
|
| File | Change |
|
||||||
|
|------|--------|
|
||||||
|
| `plans/skills_edit_page.md` | Remove role marker references from content description and highlighting spec. |
|
||||||
|
| `plans/markdown_context_window.md` | Update role marker section to clarify runtime-only usage. |
|
||||||
|
| `plans/example_context.md` | Update examples to show skill content without role markers. |
|
||||||
|
| `plans/example_context_v2.md` | Update examples to show skill content without role markers. |
|
||||||
|
| `plans/skills_demo_page.md` | Update template examples to remove `system:`/`user:` from skill templates. |
|
||||||
|
|
||||||
|
### What Does NOT Change
|
||||||
|
|
||||||
|
- **`context_roles_split()`** — The runtime still assembles a full markdown string with `user:` injected at the boundary, then splits it. This is an internal runtime mechanism, not a skill authoring concern.
|
||||||
|
- **skills-tv.html** — This is a separate client-side execution model that builds its own messages array. It can keep its `system:`/`user:` template format since it is not going through the Didactyl agent. However, it could be simplified later.
|
||||||
|
- **DM history** — Already uses proper role assignment in the messages array, not role markers.
|
||||||
|
|
||||||
|
## Detailed Changes
|
||||||
|
|
||||||
|
### docs/SKILLS.md
|
||||||
|
|
||||||
|
**Before:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"content": "system:\n## Spelling and Grammar Checker\n\nYou are a spelling and grammar checker.\n\n### Rules\n...\n\nuser:\n{{message}}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**After:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"content": "## Spelling and Grammar Checker\n\nYou are a spelling and grammar checker.\n\n### Rules\n..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Remove the bullet points about role markers. Add a note:
|
||||||
|
|
||||||
|
> Skill content is always system-role instructions. The runtime handles user message injection (DM content, trigger events) automatically. Do not include `system:`, `user:`, or `assistant:` markers in skill content.
|
||||||
|
|
||||||
|
### src/tools/tools_schema.c
|
||||||
|
|
||||||
|
**skill_create description** — Remove "use role markers (system:/user:/assistant:) at line start when needed; unmarked content defaults to system."
|
||||||
|
|
||||||
|
Replace with: "Write content as markdown system instructions. The runtime handles user message injection. Prefer ## for top-level sections."
|
||||||
|
|
||||||
|
**skill_create content param** — Remove "May include role markers at line start (system:/user:/assistant:)"
|
||||||
|
|
||||||
|
Replace with: "Markdown system instructions for the skill. May include markdown headings/lists/code fences and template variables."
|
||||||
|
|
||||||
|
Same pattern for **skill_edit**.
|
||||||
|
|
||||||
|
### skills-edit.html
|
||||||
|
|
||||||
|
Remove the `.hl-role` CSS class and the regex that highlights `system:`, `user:`, `assistant:` in the editor overlay. Update the textarea placeholder from `system:\nYou are...\n\nuser:\n{{message}}` to just `## My Skill\n\nYou are a helpful assistant.\n\nRespond to {{message}} with...`.
|
||||||
307
plans/skills_edit_page.md
Normal file
307
plans/skills_edit_page.md
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
# Skills Editor Page — `skills-edit.html`
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
A standalone web page in `~/lt/client-ndk/www/` for editing Didactyl agent skills. Built on top of [`template.html`](../../client-ndk/www/template.html) — all existing template functionality (auth, sidenav, hamburger, relay status, footer, theme toggle, logout) remains intact.
|
||||||
|
|
||||||
|
The logged-in user IS the agent. The page uses Nostr subscriptions via the NDK worker to fetch skills and the adoption list, and publishes events via `publishEvent()` to save/adopt/remove skills.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Data Model
|
||||||
|
|
||||||
|
### Nostr Event Kinds
|
||||||
|
|
||||||
|
| Kind | Purpose | Replaceable? |
|
||||||
|
|------|---------|-------------|
|
||||||
|
| `31123` | Public skill definition | Yes, by d-tag |
|
||||||
|
| `31124` | Private skill definition (NIP-44 encrypted content) | Yes, by d-tag |
|
||||||
|
| `10123` | Skill adoption list | Yes, single per pubkey |
|
||||||
|
|
||||||
|
### Skill Event Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
content: markdown template with {{variables}} (plain authored instructions; runtime handles role packaging)
|
||||||
|
tags:
|
||||||
|
[d, slug]
|
||||||
|
[description, human-readable description]
|
||||||
|
[trigger, dm|cron|nostr-subscription|webhook|chain] (optional)
|
||||||
|
[filter, type-specific filter JSON] (optional)
|
||||||
|
[llm, fallback chain string] (optional)
|
||||||
|
[temperature, number] (optional)
|
||||||
|
[max_tokens, number] (optional)
|
||||||
|
[seed, number] (optional)
|
||||||
|
[requires_tool, tool_name] (0..N)
|
||||||
|
[requires_skill, skill_d_tag] (0..N)
|
||||||
|
[optional_tool, tool_name] (0..N)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adoption List (kind 10123)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"kind": 10123,
|
||||||
|
"tags": [
|
||||||
|
["a", "31123:<pubkey>:skill-slug"],
|
||||||
|
["a", "31124:<pubkey>:private-skill-slug"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Page Layout
|
||||||
|
|
||||||
|
Three-column layout filling the body area between header and footer:
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ HEADER [hamburger] Skills Editor [avatar]│
|
||||||
|
├──────────────┬──────────────────────────────────┬───────────────────┤
|
||||||
|
│ │ │ │
|
||||||
|
│ SKILLS │ SKILL EDITOR │ TOOLS │
|
||||||
|
│ LIST │ │ │
|
||||||
|
│ │ [slug] [description] │ nostr_post │
|
||||||
|
│ ┌────────┐ │ [trigger] [filter] │ nostr_query │
|
||||||
|
│ │ search │ │ [llm] [temp] [tokens] │ nostr_dm_send │
|
||||||
|
│ └────────┘ │ │ local_shell_exec │
|
||||||
|
│ │ ┌──────────────────────────────┐ │ memory_save │
|
||||||
|
│ My|Pub|Adpt │ │ │ │ memory_recall │
|
||||||
|
│ │ │ Large textarea with │ │ skill_create │
|
||||||
|
│ skill-1 ★ │ │ syntax highlighting for │ │ ... │
|
||||||
|
│ skill-2 │ │ {{variables}} and tool names │ │ │
|
||||||
|
│ skill-3 ★ │ │ │ │ Double-click to │
|
||||||
|
│ skill-4 │ │ │ │ add as │
|
||||||
|
│ ... │ │ │ │ requires_tool │
|
||||||
|
│ │ └──────────────────────────────┘ │ │
|
||||||
|
│ ────────── │ │ │
|
||||||
|
│ ADOPTED │ [Save] [Save Private] [New] │ │
|
||||||
|
│ identity ↕ │ [Adopt] [Remove from adopted] │ │
|
||||||
|
│ chat ↕ │ │ │
|
||||||
|
│ monitor ↕ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
├──────────────┴──────────────────────────────────┴───────────────────┤
|
||||||
|
│ FOOTER [relay status] [status] [info] │
|
||||||
|
└─────────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Left Column — Skills List (~20% width)
|
||||||
|
|
||||||
|
- **Search input** at top to filter skills by slug/description
|
||||||
|
- **Filter tabs**: My | Public | Adopted
|
||||||
|
- **My**: skills authored by the current pubkey (kind 31123 + 31124)
|
||||||
|
- **Public**: public skills from anyone (kind 31123 only, no author filter) — for discovery and copying
|
||||||
|
- **Adopted**: skills currently in the kind 10123 adoption list
|
||||||
|
- **Scrollable skill list**: each item shows slug, description snippet, author name/short-pubkey, ★ if adopted
|
||||||
|
- Clicking a skill loads it into the editor
|
||||||
|
- **Copy/Fork button**: when viewing a public skill from another author, a "Copy to My Skills" button creates a new skill under the current pubkey with the same content (new d-tag to avoid collision)
|
||||||
|
- **Adopted Skills section** below the list:
|
||||||
|
- Shows current adoption list entries in order
|
||||||
|
- Drag-to-reorder support (or up/down buttons)
|
||||||
|
- Visual indicator of adoption order
|
||||||
|
|
||||||
|
### Center Column — Skill Editor (~60% width)
|
||||||
|
|
||||||
|
- **Metadata row**: slug (d-tag), description inputs
|
||||||
|
- **Trigger row**: trigger type dropdown, filter input
|
||||||
|
- **Execution params row**: llm input, temperature, max_tokens, seed
|
||||||
|
- **Requirements display**: shows current requires_tool, requires_skill, optional_tool tags as removable chips/badges
|
||||||
|
- **Large textarea** for skill content (markdown template)
|
||||||
|
- Syntax highlighting overlay for `{{variable_names}}` in accent color
|
||||||
|
- Tool name references highlighted when they match known tools
|
||||||
|
- **Action buttons**:
|
||||||
|
- Save (publish as kind 31123)
|
||||||
|
- Save Private (publish as kind 31124 with NIP-44 encryption)
|
||||||
|
- New Skill (clear editor)
|
||||||
|
- Adopt (add to kind 10123 list)
|
||||||
|
- Remove from Adopted (remove from kind 10123 list)
|
||||||
|
- Delete Skill (kind 5 deletion event)
|
||||||
|
|
||||||
|
### Right Column — Tools List (~20% width)
|
||||||
|
|
||||||
|
- **Hardcoded list** of all Didactyl tools from TOOLS.md
|
||||||
|
- Each tool shows name and brief description
|
||||||
|
- **Double-click** a tool to add it as a `requires_tool` tag on the current skill
|
||||||
|
- Visual indicator if tool is already required by current skill
|
||||||
|
- Search/filter input at top
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Syntax Highlighting Approach
|
||||||
|
|
||||||
|
Use a transparent overlay `<div>` positioned exactly over the `<textarea>`. The textarea has transparent text color; the overlay div renders the same text with highlighted spans. Both scroll together.
|
||||||
|
|
||||||
|
Highlighted elements:
|
||||||
|
- `{{variable_name}}` — accent color (e.g., `var(--accent-color)`)
|
||||||
|
- Tool names (when matching known tools) — distinct highlight color
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Nostr Subscriptions
|
||||||
|
|
||||||
|
### On page load (after auth):
|
||||||
|
|
||||||
|
1. **My skills subscription**: `{ kinds: [31123, 31124], authors: [currentPubkey], limit: 200 }` — fetch agent's own skills (public + private)
|
||||||
|
2. **Public skills subscription** (when Public filter active): `{ kinds: [31123], limit: 200 }` — fetch public skills from anyone for discovery/copying
|
||||||
|
3. **Adoption list subscription**: `{ kinds: [10123], authors: [currentPubkey], limit: 1 }` — fetch current adoption list
|
||||||
|
|
||||||
|
The Public subscription is activated on demand when the user switches to the Public tab, to avoid fetching large amounts of data unnecessarily. My skills and adoption list are always loaded.
|
||||||
|
|
||||||
|
### Event handling:
|
||||||
|
|
||||||
|
- `ndkEvent` listener processes incoming skill events and adoption list events
|
||||||
|
- Skills are parsed and stored in a local array, keyed by `pubkey:d-tag`
|
||||||
|
- Adoption list tags are parsed to determine which skills are adopted and their order
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Skill Publishing
|
||||||
|
|
||||||
|
### Save Public Skill (kind 31123)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const event = {
|
||||||
|
kind: 31123,
|
||||||
|
content: editorContent, // raw markdown template
|
||||||
|
tags: [
|
||||||
|
[d, slug],
|
||||||
|
[description, descriptionText],
|
||||||
|
// trigger tags if set
|
||||||
|
// execution param tags
|
||||||
|
// requires_tool tags
|
||||||
|
// requires_skill tags
|
||||||
|
// optional_tool tags
|
||||||
|
],
|
||||||
|
created_at: Math.floor(Date.now() / 1000)
|
||||||
|
};
|
||||||
|
await publishEvent(event);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Save Private Skill (kind 31124)
|
||||||
|
|
||||||
|
Same structure but content is NIP-44 encrypted. The `d` tag stays in plaintext; other metadata moves into the encrypted payload per SKILLS.md spec.
|
||||||
|
|
||||||
|
### Update Adoption List (kind 10123)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const event = {
|
||||||
|
kind: 10123,
|
||||||
|
content: '',
|
||||||
|
tags: adoptedSkillAddresses.map(addr => [a, addr]),
|
||||||
|
created_at: Math.floor(Date.now() / 1000)
|
||||||
|
};
|
||||||
|
await publishEvent(event);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Hardcoded Tools List
|
||||||
|
|
||||||
|
Extracted from TOOLS.md — all tool names with descriptions:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const DIDACTYL_TOOLS = [
|
||||||
|
{ name: 'nostr_post', description: 'Publish a Nostr event to connected relays' },
|
||||||
|
{ name: 'nostr_delete', description: 'Request deletion of events - NIP-09 kind 5' },
|
||||||
|
{ name: 'nostr_react', description: 'React to a Nostr event - NIP-25 kind 7' },
|
||||||
|
{ name: 'nostr_query', description: 'Query events from relays using a Nostr filter' },
|
||||||
|
{ name: 'nostr_my_events', description: 'Query recent events authored by this agent' },
|
||||||
|
{ name: 'nostr_dm_send', description: 'Send a NIP-04 encrypted DM' },
|
||||||
|
{ name: 'nostr_dm_send_nip17', description: 'Send a private DM using NIP-17 gift wrap' },
|
||||||
|
{ name: 'nostr_profile_get', description: 'Look up a Nostr profile by pubkey' },
|
||||||
|
{ name: 'nostr_nip05_lookup', description: 'Look up or verify a NIP-05 identifier' },
|
||||||
|
{ name: 'nostr_encode', description: 'Encode a Nostr entity into nostr: URI' },
|
||||||
|
{ name: 'nostr_decode', description: 'Decode a Nostr bech32/nostr: URI' },
|
||||||
|
{ name: 'nostr_relay_status', description: 'Get relay connection status and statistics' },
|
||||||
|
{ name: 'nostr_relay_info', description: 'Fetch NIP-11 relay information document' },
|
||||||
|
{ name: 'nostr_subscription_status', description: 'List managed runtime Nostr subscriptions' },
|
||||||
|
{ name: 'nostr_subscription_set', description: 'Update a managed runtime subscription' },
|
||||||
|
{ name: 'nostr_encrypt', description: 'Encrypt plaintext using NIP-44' },
|
||||||
|
{ name: 'nostr_decrypt', description: 'Decrypt NIP-44 ciphertext' },
|
||||||
|
{ name: 'nostr_list_manage', description: 'Add/remove tags in replaceable list events' },
|
||||||
|
{ name: 'nostr_block_list', description: 'View blocked pubkeys/events/hashtags' },
|
||||||
|
{ name: 'nostr_block_edit', description: 'Add or remove blocked tuples in kind-10000' },
|
||||||
|
{ name: 'nostr_pubkey', description: 'Return agent pubkey in hex' },
|
||||||
|
{ name: 'nostr_npub', description: 'Return agent pubkey as npub bech32' },
|
||||||
|
{ name: 'agent_identity', description: 'Build agent identity context block' },
|
||||||
|
{ name: 'admin_identity', description: 'Build admin identity context block' },
|
||||||
|
{ name: 'nostr_admin_profile', description: 'Build admin profile context block' },
|
||||||
|
{ name: 'nostr_admin_contacts', description: 'Build admin contacts context block' },
|
||||||
|
{ name: 'nostr_admin_relays', description: 'Build admin relay context block' },
|
||||||
|
{ name: 'nostr_admin_notes', description: 'Build admin notes context block' },
|
||||||
|
{ name: 'nostr_agent_profile', description: 'Build agent profile context block' },
|
||||||
|
{ name: 'nostr_agent_contacts', description: 'Build agent contacts context block' },
|
||||||
|
{ name: 'nostr_agent_relays', description: 'Build agent relay context block' },
|
||||||
|
{ name: 'nostr_agent_notes', description: 'Build agent notes context block' },
|
||||||
|
{ name: 'skill_create', description: 'Create or update a skill definition' },
|
||||||
|
{ name: 'skill_edit', description: 'Edit an existing self skill by d tag' },
|
||||||
|
{ name: 'skill_list', description: 'List available skills with adoption status' },
|
||||||
|
{ name: 'skill_adopt', description: 'Adopt a skill into kind 10123 list' },
|
||||||
|
{ name: 'skill_remove', description: 'Remove a skill from kind 10123 list' },
|
||||||
|
{ name: 'skill_search', description: 'Search public skills by query/author' },
|
||||||
|
{ name: 'trigger_list', description: 'List active triggered skills and status' },
|
||||||
|
{ name: 'task_list', description: 'Build current task list context block' },
|
||||||
|
{ name: 'task_manage', description: 'Manage agent task memory on Nostr' },
|
||||||
|
{ name: 'memory_save', description: 'Prepend entry to encrypted agent memory' },
|
||||||
|
{ name: 'memory_recall', description: 'Recall encrypted agent memory' },
|
||||||
|
{ name: 'model_get', description: 'Get current LLM runtime configuration' },
|
||||||
|
{ name: 'model_set', description: 'Update active LLM configuration' },
|
||||||
|
{ name: 'model_list', description: 'List available model IDs' },
|
||||||
|
{ name: 'config_store', description: 'Encrypt and publish agent config' },
|
||||||
|
{ name: 'config_recall', description: 'Fetch and decrypt agent config' },
|
||||||
|
{ name: 'agent_version', description: 'Return current Didactyl version' },
|
||||||
|
{ name: 'local_http_fetch', description: 'Fetch HTTP/S resources' },
|
||||||
|
{ name: 'local_shell_exec', description: 'Execute a shell command' },
|
||||||
|
{ name: 'local_file_read', description: 'Read a local file as text' },
|
||||||
|
{ name: 'local_file_write', description: 'Write text content to a local file' },
|
||||||
|
{ name: 'tool_list', description: 'List available tools with schemas' },
|
||||||
|
{ name: 'cashu_wallet_balance', description: 'Return wallet balances by mint' },
|
||||||
|
{ name: 'cashu_wallet_info', description: 'Fetch mint info' },
|
||||||
|
{ name: 'cashu_wallet_mint_quote', description: 'Request a mint quote' },
|
||||||
|
{ name: 'cashu_wallet_mint_check', description: 'Check mint quote status' },
|
||||||
|
{ name: 'cashu_wallet_mint_claim', description: 'Claim minted proofs' },
|
||||||
|
{ name: 'cashu_wallet_melt_quote', description: 'Request a melt quote' },
|
||||||
|
{ name: 'cashu_wallet_melt_pay', description: 'Pay a melt quote' },
|
||||||
|
{ name: 'cashu_wallet_check_proofs', description: 'Check proof states' },
|
||||||
|
{ name: 'cashu_wallet_receive_token', description: 'Receive an ecash token' },
|
||||||
|
{ name: 'cashu_wallet_send_token', description: 'Create outbound ecash token' },
|
||||||
|
{ name: 'cashu_wallet_mints_set', description: 'Set wallet/public mints' },
|
||||||
|
{ name: 'blossom_upload', description: 'Upload a file to Blossom server' },
|
||||||
|
{ name: 'blossom_download', description: 'Download a Blossom blob' },
|
||||||
|
{ name: 'blossom_head', description: 'Fetch Blossom blob metadata' },
|
||||||
|
{ name: 'blossom_delete', description: 'Delete a Blossom blob' },
|
||||||
|
{ name: 'blossom_list', description: 'List Blossom blobs for a pubkey' },
|
||||||
|
{ name: 'nostr_post_readme', description: 'Publish README.md as kind 30023' },
|
||||||
|
{ name: 'nostr_file_md_to_longform_post', description: 'Publish markdown file as kind 30023' },
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Sequence
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[Copy template.html to skills-edit.html] --> B[Add page-specific CSS for 3-column layout]
|
||||||
|
B --> C[Build HTML structure: left/center/right columns]
|
||||||
|
C --> D[Implement Nostr subscriptions for skills + adoption list]
|
||||||
|
D --> E[Build skills list rendering with search and filter]
|
||||||
|
E --> F[Build skill editor form with metadata fields]
|
||||||
|
F --> G[Build large textarea with syntax highlighting overlay]
|
||||||
|
G --> H[Build tools list column with double-click to add]
|
||||||
|
H --> I[Implement skill save/publish for kind 31123]
|
||||||
|
I --> J[Implement private skill save for kind 31124]
|
||||||
|
J --> K[Implement adoption list management - adopt/remove/reorder]
|
||||||
|
K --> L[Implement skill deletion via kind 5]
|
||||||
|
L --> M[Wire up all event listeners and UI interactions]
|
||||||
|
M --> N[Test and refine]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Files Modified
|
||||||
|
|
||||||
|
| File | Change |
|
||||||
|
|------|--------|
|
||||||
|
| `~/lt/client-ndk/www/skills-edit.html` | **NEW** — the entire skill editor page |
|
||||||
|
| `~/lt/client-ndk/www/index.html` | Add navigation entry for skills-edit page (optional) |
|
||||||
@@ -12,8 +12,8 @@
|
|||||||
// Using DIDACTYL_ prefix to avoid conflicts with nostr_core_lib VERSION macros
|
// Using DIDACTYL_ prefix to avoid conflicts with nostr_core_lib VERSION macros
|
||||||
#define DIDACTYL_VERSION_MAJOR 0
|
#define DIDACTYL_VERSION_MAJOR 0
|
||||||
#define DIDACTYL_VERSION_MINOR 2
|
#define DIDACTYL_VERSION_MINOR 2
|
||||||
#define DIDACTYL_VERSION_PATCH 23
|
#define DIDACTYL_VERSION_PATCH 24
|
||||||
#define DIDACTYL_VERSION "v0.2.23"
|
#define DIDACTYL_VERSION "v0.2.24"
|
||||||
|
|
||||||
// Agent metadata
|
// Agent metadata
|
||||||
#define DIDACTYL_NAME "Didactyl"
|
#define DIDACTYL_NAME "Didactyl"
|
||||||
|
|||||||
@@ -754,7 +754,7 @@ char* tools_build_openai_schema_json_legacy(const tools_context_t* ctx) {
|
|||||||
|
|
||||||
cJSON_AddStringToObject(t22, "type", "function");
|
cJSON_AddStringToObject(t22, "type", "function");
|
||||||
cJSON_AddStringToObject(t22_fn, "name", "skill_create");
|
cJSON_AddStringToObject(t22_fn, "name", "skill_create");
|
||||||
cJSON_AddStringToObject(t22_fn, "description", "Create or update a skill definition as kind 31123/31124 and optionally auto-adopt it. Write content as markdown context instructions: use role markers (system:/user:/assistant:) at line start when needed; unmarked content defaults to system. The runtime owns document H1 and will bump skill headings down one level, so prefer ## for top-level sections in skill content. For auto-run behavior, set trigger + filter together.");
|
cJSON_AddStringToObject(t22_fn, "description", "Create or update a skill definition as kind 31123/31124 and optionally auto-adopt it. Write content as markdown context instructions (no role markers). The runtime owns role packaging and document H1, and will bump skill headings down one level, so prefer ## for top-level sections in skill content. For auto-run behavior, set trigger + filter together.");
|
||||||
cJSON_AddStringToObject(t22_params, "type", "object");
|
cJSON_AddStringToObject(t22_params, "type", "object");
|
||||||
cJSON_AddItemToObject(t22_params, "properties", t22_props);
|
cJSON_AddItemToObject(t22_params, "properties", t22_props);
|
||||||
cJSON_AddItemToObject(t22_params, "required", t22_required);
|
cJSON_AddItemToObject(t22_params, "required", t22_required);
|
||||||
@@ -766,7 +766,7 @@ char* tools_build_openai_schema_json_legacy(const tools_context_t* ctx) {
|
|||||||
cJSON_AddStringToObject(p_skill_create_content, "type", "string");
|
cJSON_AddStringToObject(p_skill_create_content, "type", "string");
|
||||||
cJSON_AddStringToObject(p_skill_create_content,
|
cJSON_AddStringToObject(p_skill_create_content,
|
||||||
"description",
|
"description",
|
||||||
"Markdown template for the skill body. May include role markers at line start (system:/user:/assistant:), markdown headings/lists/code fences, and template variables like {{message}}, {{dm_history}}, {{agent_identity}}, {{admin_profile}}, or {{skill_d_tag}} references. Unmarked content defaults to system. Runtime prepends document title and bumps headings one level.");
|
"Markdown template for the skill body. Use plain markdown headings/lists/code fences and template variables like {{message}}, {{dm_history}}, {{agent_identity}}, {{admin_profile}}, or {{skill_d_tag}} references. Do not author system/user/assistant role markers in skill content; runtime appends trigger payloads as user input and handles role packaging. Runtime prepends document title and bumps headings one level.");
|
||||||
cJSON_AddItemToObject(t22_props, "content", p_skill_create_content);
|
cJSON_AddItemToObject(t22_props, "content", p_skill_create_content);
|
||||||
cJSON* p_skill_create_scope = cJSON_CreateObject();
|
cJSON* p_skill_create_scope = cJSON_CreateObject();
|
||||||
cJSON_AddStringToObject(p_skill_create_scope, "type", "string");
|
cJSON_AddStringToObject(p_skill_create_scope, "type", "string");
|
||||||
@@ -1002,7 +1002,7 @@ char* tools_build_openai_schema_json_legacy(const tools_context_t* ctx) {
|
|||||||
|
|
||||||
cJSON_AddStringToObject(t25b, "type", "function");
|
cJSON_AddStringToObject(t25b, "type", "function");
|
||||||
cJSON_AddStringToObject(t25b_fn, "name", "skill_edit");
|
cJSON_AddStringToObject(t25b_fn, "name", "skill_edit");
|
||||||
cJSON_AddStringToObject(t25b_fn, "description", "Edit an existing self skill by d tag and republish it as kind 31123/31124. Keep skill content markdown-first with optional role markers (system:/user:/assistant:) at line start; unmarked content defaults to system. Runtime owns H1 and bumps skill headings down one level. For triggered skills, update trigger and filter together.");
|
cJSON_AddStringToObject(t25b_fn, "description", "Edit an existing self skill by d tag and republish it as kind 31123/31124. Keep skill content markdown-first (no role markers). Runtime owns role packaging, owns H1, and bumps skill headings down one level. For triggered skills, update trigger and filter together.");
|
||||||
cJSON_AddStringToObject(t25b_params, "type", "object");
|
cJSON_AddStringToObject(t25b_params, "type", "object");
|
||||||
cJSON_AddItemToObject(t25b_params, "properties", t25b_props);
|
cJSON_AddItemToObject(t25b_params, "properties", t25b_props);
|
||||||
cJSON_AddItemToObject(t25b_params, "required", t25b_required);
|
cJSON_AddItemToObject(t25b_params, "required", t25b_required);
|
||||||
@@ -1015,7 +1015,7 @@ char* tools_build_openai_schema_json_legacy(const tools_context_t* ctx) {
|
|||||||
cJSON_AddStringToObject(p_skill_edit_content, "type", "string");
|
cJSON_AddStringToObject(p_skill_edit_content, "type", "string");
|
||||||
cJSON_AddStringToObject(p_skill_edit_content,
|
cJSON_AddStringToObject(p_skill_edit_content,
|
||||||
"description",
|
"description",
|
||||||
"Replacement markdown template for this skill. Supports role markers (system:/user:/assistant:) at line start, markdown structure, and template variables such as {{message}}, {{dm_history}}, {{agent_identity}}, {{admin_profile}}, and {{skill_d_tag}} references. If omitted, existing content is retained.");
|
"Replacement markdown template for this skill. Use markdown structure and template variables such as {{message}}, {{dm_history}}, {{agent_identity}}, {{admin_profile}}, and {{skill_d_tag}} references. Do not include authored system/user/assistant role markers; runtime handles role packaging and user payload injection. If omitted, existing content is retained.");
|
||||||
cJSON_AddItemToObject(t25b_props, "content", p_skill_edit_content);
|
cJSON_AddItemToObject(t25b_props, "content", p_skill_edit_content);
|
||||||
|
|
||||||
cJSON* p_skill_edit_scope = cJSON_CreateObject();
|
cJSON* p_skill_edit_scope = cJSON_CreateObject();
|
||||||
|
|||||||
Reference in New Issue
Block a user