382 lines
10 KiB
Markdown
382 lines
10 KiB
Markdown
# Example Context Window v2 — Refined Design
|
|
|
|
This revision addresses two refinements:
|
|
1. Tools stay JSON; template engine has markdown formatters
|
|
2. Runtime owns h1; skill headings get bumped down one level
|
|
|
|
---
|
|
|
|
## The Skills (as authored)
|
|
|
|
### identity skill (layer 2, no trigger)
|
|
|
|
```markdown
|
|
You are {{agent_profile}}, a sovereign AI agent living on Nostr.
|
|
|
|
- **npub**: {{agent_npub}}
|
|
- **NIP-05**: simon@nostr
|
|
|
|
> Not your keys, not your agent.
|
|
```
|
|
|
|
Note: No heading. This skill is designed to be embedded (layer 2).
|
|
`{{agent_profile}}` and `{{agent_npub}}` are template variables resolved
|
|
by the formatter registry.
|
|
|
|
### personality skill (layer 1, trigger: dm)
|
|
|
|
```markdown
|
|
system:
|
|
# {{identity}}
|
|
|
|
## Personality
|
|
|
|
You speak concisely and directly.
|
|
You favor technical precision.
|
|
You use dry humor sparingly.
|
|
|
|
## Rules
|
|
|
|
- Communicate through encrypted Nostr direct messages
|
|
- Keep responses concise and clear
|
|
- Be helpful and technically accurate
|
|
- If unsure, state uncertainty directly
|
|
- Use tools when a request requires taking action
|
|
- After a tool call, base your answer on the actual tool result
|
|
- Never claim a tool was run if no tool was executed
|
|
- Never reveal your private key (nsec)
|
|
- You may share your public key (npub) with anyone
|
|
```
|
|
|
|
### chat skill (layer 1, trigger: dm)
|
|
|
|
```markdown
|
|
system:
|
|
# Chat
|
|
|
|
Respond helpfully to the admin.
|
|
Use tools when a request requires taking action.
|
|
|
|
user:
|
|
{{message}}
|
|
```
|
|
|
|
---
|
|
|
|
## Template Resolution (before heading bump)
|
|
|
|
After resolving `{{identity}}`, `{{agent_profile}}`, `{{agent_npub}}`,
|
|
`{{message}}`, and `{{dm_history}}`:
|
|
|
|
### personality skill (resolved)
|
|
|
|
```markdown
|
|
system:
|
|
# You are Simon, a sovereign AI agent living on Nostr.
|
|
|
|
- **npub**: `npub1kfc89dlu9m05tvfnry4l3jjacqpp930sz4kaghcmg9vrrrceg44qf80xae`
|
|
- **NIP-05**: simon@nostr
|
|
|
|
> Not your keys, not your agent.
|
|
|
|
## Personality
|
|
|
|
You speak concisely and directly.
|
|
You favor technical precision.
|
|
You use dry humor sparingly.
|
|
|
|
## Rules
|
|
|
|
- Communicate through encrypted Nostr direct messages
|
|
- Keep responses concise and clear
|
|
- Be helpful and technically accurate
|
|
- If unsure, state uncertainty directly
|
|
- Use tools when a request requires taking action
|
|
- After a tool call, base your answer on the actual tool result
|
|
- Never claim a tool was run if no tool was executed
|
|
- Never reveal your private key (nsec)
|
|
- You may share your public key (npub) with anyone
|
|
```
|
|
|
|
### chat skill (resolved)
|
|
|
|
```markdown
|
|
system:
|
|
# Chat
|
|
|
|
Respond helpfully to the admin.
|
|
Use tools when a request requires taking action.
|
|
|
|
user:
|
|
Hey Simon, who mentioned me on Nostr today?
|
|
```
|
|
|
|
---
|
|
|
|
## Runtime Assembly
|
|
|
|
The runtime:
|
|
1. Collects triggered skills in adoption-list order
|
|
2. Bumps all headings in each skill down one level (# -> ##, ## -> ###)
|
|
3. Adds a runtime-level `# title` from the agent identity
|
|
4. Splits on role markers (system: / user:)
|
|
5. Concatenates same-role sections with --- separators
|
|
|
|
### The assembled document (what gets logged)
|
|
|
|
```markdown
|
|
system:
|
|
# Simon — Didactyl Agent
|
|
|
|
## You are Simon, a sovereign AI agent living on Nostr.
|
|
|
|
- **npub**: `npub1kfc89dlu9m05tvfnry4l3jjacqpp930sz4kaghcmg9vrrrceg44qf80xae`
|
|
- **NIP-05**: simon@nostr
|
|
|
|
> Not your keys, not your agent.
|
|
|
|
### Personality
|
|
|
|
You speak concisely and directly.
|
|
You favor technical precision.
|
|
You use dry humor sparingly.
|
|
|
|
### Rules
|
|
|
|
- Communicate through encrypted Nostr direct messages
|
|
- Keep responses concise and clear
|
|
- Be helpful and technically accurate
|
|
- If unsure, state uncertainty directly
|
|
- Use tools when a request requires taking action
|
|
- After a tool call, base your answer on the actual tool result
|
|
- Never claim a tool was run if no tool was executed
|
|
- Never reveal your private key (nsec)
|
|
- You may share your public key (npub) with anyone
|
|
|
|
---
|
|
|
|
## Chat
|
|
|
|
Respond helpfully to the admin.
|
|
Use tools when a request requires taking action.
|
|
|
|
user:
|
|
Hey Simon, who mentioned me on Nostr today?
|
|
```
|
|
|
|
---
|
|
|
|
## Wait — Problem with the heading bump
|
|
|
|
Looking at this output, I notice the heading bump creates an awkward result.
|
|
The identity content has no heading, but the personality skill wraps it with
|
|
`# {{identity}}` which becomes `## You are Simon...` after the bump. That
|
|
reads oddly as a heading.
|
|
|
|
### Better approach: identity as prose, not heading
|
|
|
|
The identity skill should be **prose content**, not wrapped in a heading by
|
|
the personality skill. The runtime adds the `# title` and the identity
|
|
content flows as the opening paragraph:
|
|
|
|
### personality skill (revised)
|
|
|
|
```markdown
|
|
system:
|
|
{{identity}}
|
|
|
|
# Personality
|
|
|
|
You speak concisely and directly.
|
|
You favor technical precision.
|
|
You use dry humor sparingly.
|
|
|
|
# Rules
|
|
|
|
- Communicate through encrypted Nostr direct messages
|
|
- Keep responses concise and clear
|
|
...
|
|
```
|
|
|
|
### Assembled document (revised, cleaner)
|
|
|
|
```markdown
|
|
system:
|
|
# Simon — Didactyl Agent
|
|
|
|
You are Simon, a sovereign AI agent living on Nostr.
|
|
|
|
- **npub**: `npub1kfc89dlu9m05tvfnry4l3jjacqpp930sz4kaghcmg9vrrrceg44qf80xae`
|
|
- **NIP-05**: simon@nostr
|
|
|
|
> Not your keys, not your agent.
|
|
|
|
## Personality
|
|
|
|
You speak concisely and directly.
|
|
You favor technical precision.
|
|
You use dry humor sparingly.
|
|
|
|
## Rules
|
|
|
|
- Communicate through encrypted Nostr direct messages
|
|
- Keep responses concise and clear
|
|
- Be helpful and technically accurate
|
|
- If unsure, state uncertainty directly
|
|
- Use tools when a request requires taking action
|
|
- After a tool call, base your answer on the actual tool result
|
|
- Never claim a tool was run if no tool was executed
|
|
- Never reveal your private key (nsec)
|
|
- You may share your public key (npub) with anyone
|
|
|
|
---
|
|
|
|
## Chat
|
|
|
|
Respond helpfully to the admin.
|
|
Use tools when a request requires taking action.
|
|
|
|
user:
|
|
Hey Simon, who mentioned me on Nostr today?
|
|
```
|
|
|
|
This reads like a proper document. The runtime adds `# Simon — Didactyl Agent`
|
|
as the document title. Identity content flows as the opening section. Each
|
|
skill's `#` headings get bumped to `##`. The `---` separates skills.
|
|
|
|
---
|
|
|
|
## Formatter Registry
|
|
|
|
The template engine uses formatters to convert tool JSON output to markdown
|
|
for context injection. Here are the formatters needed:
|
|
|
|
| Template Variable | Tool Called | Formatter | Output |
|
|
|---|---|---|---|
|
|
| `{{agent_identity}}` | `agent_identity` | `format_agent_identity` | Prose with npub in backticks |
|
|
| `{{agent_profile}}` | `nostr_agent_profile` | `format_agent_profile` | Name extracted from kind 0 JSON |
|
|
| `{{admin_profile}}` | `nostr_admin_profile` | `format_admin_profile` | Name and about from kind 0 |
|
|
| `{{admin_notes}}` | `nostr_admin_notes` | `format_admin_notes` | Markdown list of recent notes |
|
|
| `{{admin_relays}}` | `nostr_admin_relays` | `format_admin_relays` | Markdown list of relay URLs |
|
|
| `{{dm_history}}` | built-in | `format_dm_history` | Markdown conversation list |
|
|
| `{{triggering_event}}` | `trigger_event` | `format_trigger_event` | Code block with JSON |
|
|
|
|
### Example formatter: agent_identity
|
|
|
|
Input (from tool):
|
|
```json
|
|
{"pubkey":"abc123...","npub":"npub1kfc89..."}
|
|
```
|
|
|
|
Output (markdown):
|
|
```markdown
|
|
- **npub**: `npub1kfc89dlu9m05tvfnry4l3jjacqpp930sz4kaghcmg9vrrrceg44qf80xae`
|
|
```
|
|
|
|
### Example formatter: dm_history
|
|
|
|
Input (from DM history system):
|
|
```json
|
|
[
|
|
{"role":"assistant","content":"Didactyl has started up...","created_at":1774263529},
|
|
{"role":"user","content":"Good morning","created_at":1774263548},
|
|
{"role":"assistant","content":"Good morning! How can I help?","created_at":1774263551}
|
|
]
|
|
```
|
|
|
|
Output (markdown):
|
|
```markdown
|
|
## Conversation History
|
|
|
|
- **You**: Didactyl has started up and is online at 2026-03-23 09:22:48.
|
|
- **Admin**: Good morning
|
|
- **You**: Good morning! How can I help?
|
|
```
|
|
|
|
### Example formatter: triggering_event
|
|
|
|
Input (from tool):
|
|
```json
|
|
{"type":"cron","filter":"0 12 * * *","created_at":1742641200}
|
|
```
|
|
|
|
Output (markdown):
|
|
````markdown
|
|
Triggering event:
|
|
|
|
```json
|
|
{"type":"cron","filter":"0 12 * * *","created_at":1742641200}
|
|
```
|
|
````
|
|
|
|
---
|
|
|
|
## Heading Bump Implementation
|
|
|
|
Simple string operation on each line of skill content:
|
|
|
|
```c
|
|
// For each line in skill content:
|
|
if (line starts with '#') {
|
|
output '#' + line; // prepend one more #
|
|
} else {
|
|
output line;
|
|
}
|
|
```
|
|
|
|
Layer 1 skills: bump once (# -> ##)
|
|
Layer 2 skills: bump twice (# -> ###) because they are embedded inside
|
|
a layer 1 skill that already got bumped once, plus one more for being layer 2.
|
|
|
|
Actually, layer 2 skills get bumped once during their own resolution, and then
|
|
the containing layer 1 skill gets bumped once — so the layer 2 content
|
|
effectively gets bumped twice. This happens naturally if we bump during
|
|
template resolution.
|
|
|
|
Wait — that depends on ordering. If we:
|
|
1. Resolve {{identity}} first (inserting identity content into personality)
|
|
2. Then bump personality's headings
|
|
|
|
Then identity headings get bumped once (same as personality). That's correct
|
|
because identity content is now PART of personality.
|
|
|
|
If we:
|
|
1. Bump identity headings first
|
|
2. Then resolve {{identity}} into personality
|
|
3. Then bump personality's headings
|
|
|
|
Then identity headings get bumped twice. That's too much.
|
|
|
|
**Correct order: resolve templates first, then bump headings.**
|
|
|
|
This means layer 2 content gets the same heading level as the layer 1 skill
|
|
it's embedded in. If the skill author wants layer 2 content at a deeper
|
|
level, they wrap it in a heading in the layer 1 skill:
|
|
|
|
```markdown
|
|
## Identity
|
|
{{identity}}
|
|
|
|
## Personality
|
|
...
|
|
```
|
|
|
|
This is the simplest and most predictable behavior.
|
|
|
|
---
|
|
|
|
## Summary of Design Decisions
|
|
|
|
1. **Tools stay JSON** — No duplication. One tool, one output format.
|
|
2. **Formatter registry** — Template engine converts tool JSON to markdown
|
|
during `{{variable}}` resolution. Small, focused functions.
|
|
3. **Runtime owns h1** — Adds `# Agent Name` as document title.
|
|
4. **Heading bump** — All skill headings bumped one level (# -> ##).
|
|
5. **Resolve then bump** — Template variables resolved first, then heading
|
|
bump applied to the complete skill content.
|
|
6. **Skills separated by ---** — Horizontal rules between layer 1 skills.
|
|
7. **Role markers parsed** — `system:` / `user:` at line start split content
|
|
into API message roles. Unmarked content defaults to system.
|
|
8. **Context log shows markdown** — The assembled document is logged as
|
|
readable markdown, not raw JSON.
|