v0.0.72 - Refined wizard framing and menu hotkey display; diagnosed hardcoded Didactyl fallback causing name mismatch

This commit is contained in:
Your Name
2026-03-16 14:37:04 -04:00
parent 5857058a21
commit 0cc0667a80
43 changed files with 25348 additions and 18627 deletions

View File

@@ -4,32 +4,26 @@ See also: [SKILLS.md](SKILLS.md) · [TOOLS.md](TOOLS.md)
## What Is Context?
Every time Didactyl talks to an LLM, it sends a **context** — the complete package of information the model needs to reason and respond. Understanding context is fundamental to understanding how Didactyl works.
Every time Didactyl talks to an LLM, it sends a **context** — the complete package of information the model needs to reason and respond.
Context is not just "the prompt." It is everything the LLM receives in a single request:
Context is not just a prompt string; it is the full request payload:
1. **Messages**the conversation: system prompts, user messages, assistant responses, tool results
2. **Tool schemas** the JSON descriptions of every tool the LLM can call
3. **Model parameters** — temperature, max tokens, etc.
The LLM sees all of this together and produces its response based on the totality of what it was given.
1. **Messages**system/user/assistant/tool history
2. **Tool schemas** — JSON descriptions of callable tools
3. **Model parameters** model, temperature, max tokens, seed, etc.
---
## The OpenAI Chat Completion Format
## OpenAI-Compatible Chat Format
Didactyl uses the OpenAI-compatible chat completion API format, which is the industry standard. Every LLM request looks like this:
Didactyl uses OpenAI-compatible chat completions.
```json
{
"model": "claude-opus-4.6",
"messages": [
{"role": "system", "content": "You are Didactyl, a sovereign AI agent..."},
{"role": "system", "content": "Admin profile: ..."},
{"role": "user", "content": "Post a note about Bitcoin"},
{"role": "assistant", "content": null, "tool_calls": [...]},
{"role": "tool", "content": "{\"success\":true,...}", "tool_call_id": "..."},
{"role": "assistant", "content": "Done! I posted your note."}
{"role": "system", "content": "..."},
{"role": "user", "content": "..."}
],
"tools": [
{
@@ -37,7 +31,7 @@ Didactyl uses the OpenAI-compatible chat completion API format, which is the ind
"function": {
"name": "nostr_post",
"description": "Publish a Nostr event",
"parameters": { ... }
"parameters": {"type":"object"}
}
}
],
@@ -46,145 +40,122 @@ Didactyl uses the OpenAI-compatible chat completion API format, which is the ind
}
```
### Messages
Messages are an ordered array. Each message has a `role`:
### Message Roles
| Role | Purpose |
|------|---------|
| `system` | Instructions, identity, context data — the LLM reads these but the user didn't write them |
| `user` | What the human (or trigger event) said |
| `assistant` | What the LLM previously said or did (including tool calls) |
| `tool` | Results from tool executions, fed back to the LLM |
The LLM processes all messages in order and generates the next response.
### Tool Schemas
The `tools` array describes every tool the LLM can call. Each tool has a name, description, and JSON Schema for its parameters. The LLM uses these descriptions to decide when and how to call tools.
Tools are not part of the message history — they are a separate capability declaration sent alongside the messages.
### Model Parameters
Temperature, max tokens, and other settings control how the LLM generates its response. These can be set globally or overridden per-skill.
| `system` | Instructions and injected context |
| `user` | Input message or trigger payload |
| `assistant` | Model responses / tool call envelopes |
| `tool` | Tool execution results fed back to model |
---
## How Didactyl Builds Context
## Context Assembly Model
Didactyl assembles context dynamically for each request. The context varies depending on what triggered the request (DM, trigger event) and which skill is active.
Didactyl uses **skill composition by adoption order**.
### Context Assembly Flow
There are no context modes.
### Assembly Steps
1. Load adopted skills from kind `10123`.
2. Resolve adopted skills in list order.
3. Expand each skill template variables via tools.
4. Append resolved skill output to messages in that same order.
5. Append live input (DM text or triggering event payload).
6. Attach tool schemas.
7. Apply execution parameters from trigger tags (if invoked via trigger).
```mermaid
flowchart TD
INPUT[Input: DM message or trigger event] --> MODE{Skill context_mode?}
MODE -->|inject| INJECT[Start with agent soul/personality]
INJECT --> ADD_ADMIN[Add admin identity + profile]
ADD_ADMIN --> ADD_SKILLS[Add adopted skill instructions]
ADD_SKILLS --> ADD_HISTORY[Add conversation history]
ADD_HISTORY --> ADD_SKILL_INST[Append this skill instructions]
ADD_SKILL_INST --> ADD_USER[Add user message or trigger event]
MODE -->|full| FULL[Start with skill template only]
FULL --> RESOLVE[Resolve template variables via tool calls]
RESOLVE --> ADD_USER2[Add user message or trigger event]
MODE -->|override| OVERRIDE[Replace soul with skill prompt]
OVERRIDE --> ADD_ADMIN2[Add admin identity + profile]
ADD_ADMIN2 --> ADD_SKILLS2[Add adopted skill instructions]
ADD_SKILLS2 --> ADD_HISTORY2[Add conversation history]
ADD_HISTORY2 --> ADD_USER3[Add user message or trigger event]
ADD_USER --> TOOLS[Attach tool schemas]
ADD_USER2 --> TOOLS
ADD_USER3 --> TOOLS
TOOLS --> LLM[Send to LLM]
INPUT[Input: DM or trigger event] --> ADOPT[Load adopted skills from kind 10123]
ADOPT --> ORDER[Resolve skills in listed order]
ORDER --> EXPAND[Expand template variables via tools]
EXPAND --> MESSAGES[Append resolved skill messages]
MESSAGES --> LIVE[Append live input message/event]
LIVE --> TOOLS[Attach tool schemas]
TOOLS --> PARAMS[Apply runtime params from trigger tags]
PARAMS --> LLM[Send to LLM]
```
### Context Parts
### Why Order Matters
These are the building blocks that get assembled into the messages array:
- Earlier adopted skills usually establish broad behavior.
- Later adopted skills can refine or narrow behavior.
- If instructions conflict, prompt-order effects apply.
---
## Context Parts
| Part | Source | Description |
|------|--------|-------------|
| **Soul/Personality** | Kind 31120 event | The agent's identity, rules, and behavior instructions |
| **Admin Identity** | Config / kind 3 | Who the admin is, their pubkey |
| **Admin Profile** | Kind 0 event | Admin's display name, about, picture |
| **Admin Relay List** | Kind 10002 event | Admin's preferred relays |
| **Admin Notes** | Kind 1 events | Admin's recent public posts — gives the agent awareness of what the admin is doing |
| **Adopted Skills** | Kind 31123/31124 events | Instructions from other adopted skills |
| **Conversation History** | Kind 4/14 events | Recent DM exchanges between admin and agent |
| **Skill Instructions** | Active skill content | The specific skill being executed |
| **User Message** | DM or trigger event | The input that triggered this execution |
| **Tool Schemas** | Tool registry | JSON descriptions of available tools (sent separately from messages) |
### Template Variables Are Tool Calls
The soul's template system uses `tool:` directives to populate context sections. Each section can specify a tool to call, and the tool's output becomes that section's content:
```yaml
- section: admin_profile
role: system
tool: nostr_admin_profile
skip_if_empty: true
```
This calls `nostr_admin_profile`, gets the result, and inserts it as a system message. Template variables like `{{admin_profile}}` in skill templates work the same way — they resolve to tool calls. There is one unified resolution mechanism: `tools_execute()`.
This means new context sources are added by adding tools, not by modifying the template engine.
| Skill templates | Adopted skill events | Core instructions assembled in order |
| Resolved variables | Tool outputs | Runtime data inserted into templates |
| Conversation history | DM history/events | Recent dialogue context |
| Live input | DM or trigger event | Current request payload |
| Tool schemas | Tool registry | Capability declaration for tool calling |
| Runtime params | Trigger tags | LLM/tool limits for this execution |
---
## Context Modes
## Template Variables Are Tool Calls
Skills control how much of the default context the LLM sees using the `context_mode` field:
Template variables resolve through tool execution.
| Mode | What the LLM sees | Use case |
|------|-------------------|----------|
| `inject` | Everything: soul + admin context + history + skill instructions | Default. Agent stays itself, gains new knowledge. |
| `full` | Only what the skill template provides | Focused tasks: spellcheck, translation. Minimal context = cheaper, faster. |
| `override` | Skill replaces soul, but admin context + history still included | Different personality, same capabilities. |
Example:
Most skills use `inject` (the default). `full` is for specialized tasks where you want to minimize token cost. `override` is rare.
- `{{admin_profile}}` resolves by running `nostr_admin_profile`
- `{{admin_notes}}` resolves by running `nostr_admin_notes`
Unknown variables should resolve to empty values for portability.
---
## Context for Triggered Skills
## Trigger Runtime Parameters
When a trigger fires (Nostr subscription match, webhook POST, cron timer, chain completion), the context is assembled the same way as for a DM — the only difference is the user message:
Execution controls are attached to trigger tags, not skill content:
| Input Source | User Message Content |
|---|---|
| Admin DM | The DM text |
| Nostr subscription trigger | The matching Nostr event as JSON |
| Webhook trigger | The HTTP POST payload as JSON |
| Cron trigger | A synthetic event with timestamp and cron expression |
| Chain trigger | A synthetic event with the source skill's d_tag and output |
- `llm`
- `max_tokens`
- `temperature`
- `seed`
- `tools`
The LLM receives the skill instructions as system context and the triggering event as the user message, then reasons about what to do and which tools to use.
Resolution order for a triggered run:
1. Start with agent defaults
2. Apply trigger tag overrides
3. Execute
4. Restore defaults
---
## Triggered vs Adopted Use
- **Adopted skill (`10123`)**: contributes context/instructions
- **Triggered skill**: contributes context and may supply execution overrides via tags
This separation keeps composition simple while allowing per-trigger runtime control.
---
## Token Budget
Context has a cost — every token sent to the LLM costs money and takes time. Didactyl manages this through:
Context cost is controlled by:
- **Context mode selection** — `full` mode skills send minimal context
- **Skip-if-empty sections** — template sections with no content are omitted
- **History limits** — conversation history is capped (default: 12 turns)
- **Skill content limits** — adopted skill instructions are truncated to prevent context overflow
- **Per-skill model selection** — cheap models for simple tasks, expensive models for complex ones
- Adoption-list ordering and skill count
- Conversation-history limits
- Skill/template truncation limits
- Per-trigger model/runtime parameter choices
The `/api/context/current` and `/api/context/parts` API endpoints let you inspect exactly what context would be sent right now, including character counts and token estimates.
Use runtime context inspection endpoints to see the exact payload before LLM calls.
---
## Related Documentation
- Skills — how to define LLM execution units: [SKILLS.md](SKILLS.md)
- Tools — what the LLM can do: [TOOLS.md](TOOLS.md)
- API — inspect context at runtime: [API.md](API.md)
- Skills spec: [SKILLS.md](SKILLS.md)
- Tool catalog: [TOOLS.md](TOOLS.md)
- API details: [API.md](API.md)