# Plan: Unify Template Variables with Tools

## Problem

The soul template system (`---template---` in kind 31120) uses `{{variable}}` placeholders that are resolved by a hardcoded C function (`agent_template_resolve_var()`). This is a parallel data-fetching system alongside the existing tool registry. Every new context data source requires:

1. Adding a C function to produce the data
2. Adding an `if (strcmp(...))` branch in the resolver
3. Documenting the new variable name
4. Hoping the template author uses the exact right name

This caused the bug that started this investigation — 5 out of 9 template variables were misspelled/mismatched, producing empty context sections and a provider rejection.

## Goal

**Eliminate template variables entirely.** Template sections fetch their data by calling tools. The tool registry is the single mechanism for getting data into context.

## Design

### New Template Syntax

Replace `content: |` with `tool:` and optional `args:` directives:

```yaml
---template---

- section: admin_identity
  role: system
  tool: admin_identity
  skip_if_empty: true

- section: admin_profile
  role: system
  tool: nostr_admin_profile
  skip_if_empty: true

- section: admin_contacts
  role: system
  tool: nostr_admin_contacts
  skip_if_empty: true

- section: admin_relays
  role: system
  tool: nostr_admin_relays
  skip_if_empty: true

- section: admin_notes
  role: system
  tool: nostr_admin_notes
  skip_if_empty: true

- section: tools
  role: system
  tool: tool_list
  skip_if_empty: true

- section: tasks
  role: system
  tool: task_list
  skip_if_empty: true

- section: dm_history
  role: expand
  limit: 12

- section: conversation
  role: user
  tool: message_current
  skip_if_empty: true
```

A section can still use the old `content:` with `{{variables}}` for static text or mixed content. But the primary mechanism for dynamic data is `tool:`.

### How It Works

```mermaid
flowchart TD
    DM[Incoming DM] --> BUILD[Build context from template]
    BUILD --> SOUL[Emit personality as system msg]
    SOUL --> LOOP[For each template section]
    LOOP --> CHECK{Has tool: directive?}
    CHECK -->|Yes| EXEC["tools_execute(tool_name, args)"]
    EXEC --> FORMAT[Extract content from JSON result]
    FORMAT --> EMIT[Emit as chat message]
    CHECK -->|No| RESOLVE["Resolve {{vars}} from content template<br/>(legacy path, eventually removed)"]
    RESOLVE --> EMIT
    EMIT --> LOOP
    LOOP --> DONE[Complete messages array]
```

### New Context Tools

Add lightweight "context tools" that read from cached in-memory data. These are fast (no network), deterministic, and use the same `tools_execute()` interface as everything else.

| Tool | Returns | Source |
|---|---|---|
| `admin_identity` | Admin pubkey + verification text | config + sender tier |
| `nostr_admin_profile` | Admin kind 0 profile JSON | cached `g_admin_kind0_json` |
| `nostr_admin_contacts` | Admin kind 3 contacts JSON array | cached `g_admin_wot_contacts` |
| `nostr_admin_relays` | Admin kind 10002 relay list JSON | cached `g_admin_kind10002_json` |
| `nostr_admin_notes` | Admin recent kind 1 notes | cached `g_admin_kind1_notes` |
| `task_list` | Current task list from tasks.json | file read (already exists as `task_manage` with `action: list`) |
| `message_current` | Current user message text | passed via tool context |
| `agent_identity` | Agent pubkey, npub | config |

Existing tools that already work for context:
- `tool_list` — returns tool schemas (already exists)
- `task_manage` with `{"action":"list"}` — returns tasks (already exists)
- `local_file_read` — can read any file (already exists)

### Tool Result → Message Content

Tool results are JSON objects like `{"success":true,"content":"..."}`. The template system extracts the `content` field (or a configurable field) as the message text. If the tool returns an error or empty content, and `skip_if_empty: true` is set, the section is omitted.

For tools that return structured data (like `tool_list` returning a schema array), the template can specify a `result_field` or `format` to control extraction:

```yaml
- section: tools
  role: system
  tool: tool_list
  result_field: tools
  skip_if_empty: true
```

### Implementation Phases

#### Phase 1: Add context tools + tool: directive support

1. Add new `context_*` tools to `tools.c` that wrap the existing cached data getters
2. Extend `prompt_template_section_t` with `tool_name` and `tool_args` fields
3. Extend `prompt_template_parse()` to recognize `tool:` and `args:` directives
4. Extend `prompt_template_build_messages()` to call `tools_execute()` when a section has `tool_name` set
5. Update the soul template in `config.jsonc` to use `tool:` directives
6. Keep `content:` + `{{variable}}` working as a fallback for backward compatibility

#### Phase 2: Migrate all sections to tool-based

1. Convert all template sections from `content: {{variable}}` to `tool:` directives
2. Verify all context data flows through tools
3. Update `context_template.md` to reflect new syntax

#### Phase 3: Remove legacy variable resolver

1. Remove `agent_template_resolve_var()` and all its helper functions that are now redundant
2. Remove `prompt_var_resolver_fn` callback from `prompt_template_build_messages()`
3. Clean up dead code in `agent.c`
4. Update all documentation

### Template Section Struct Changes

```c
typedef struct {
    char name[PROMPT_TEMPLATE_MAX_NAME_LEN];
    char role[PROMPT_TEMPLATE_MAX_ROLE_LEN];
    char* content_template;       // legacy: {{variable}} content
    char* tool_name;              // NEW: tool to call for content
    char* tool_args;              // NEW: JSON args for tool call
    char* result_field;           // NEW: which JSON field to extract (default: "content")
    int limit;
    int skip_if_empty;
    char* provider_name;
    char* provider_content_template;
} prompt_template_section_t;
```

### Soul Template Example (After Migration)

```
# Didactyl Agent

You are Didactyl, a sovereign AI agent living on Nostr.
...

---template---

- section: admin_identity
  role: system
  tool: admin_identity
  skip_if_empty: true

- section: admin_profile
  role: system
  tool: nostr_admin_profile
  skip_if_empty: true

- section: admin_contacts
  role: system
  tool: nostr_admin_contacts
  skip_if_empty: true

- section: admin_relays
  role: system
  tool: nostr_admin_relays
  skip_if_empty: true

- section: admin_notes
  role: system
  tool: nostr_admin_notes
  skip_if_empty: true

- section: tools
  role: system
  tool: tool_list
  result_field: tools
  skip_if_empty: true

- section: tasks
  role: system
  tool: task_manage
  args: {"action":"list"}
  result_field: tasks
  skip_if_empty: true

- section: dm_history
  role: expand
  limit: 12

- section: conversation
  role: user
  tool: message_current
  skip_if_empty: true
```

### What Gets Deleted Eventually

- `agent_template_resolve_var()` and all its `if (strcmp(...))` branches
- `build_tool_schemas_json_string()`
- `build_tasks_content_string()` (replaced by `task_manage` tool)
- `build_admin_recent_posts_text()` (replaced by `nostr_admin_notes` tool)
- `build_admin_profile_plain_text()` (replaced by `nostr_admin_profile` tool)
- `build_admin_relay_list_plain_text()` (replaced by `nostr_admin_relays` tool)
- `build_sender_verification_text()` (folded into `admin_identity` tool)
- `build_startup_events_json_string()` (replaced by tool if needed)
- `build_adopted_skills_payload_string()` (replaced by tool if needed)
- `nostr_handler_get_admin_kind3_context()` (just added, will be replaced by tool)
- The `prompt_var_resolver_fn` callback type

### Benefits

1. **Single data-fetching mechanism** — tools are the only way to get data
2. **No more variable name mismatches** — tool names are validated at registration
3. **User-configurable context** — admin can add any tool output to context by editing the soul template
4. **Testable** — `--test-tool nostr_admin_profile` shows exactly what goes into context
5. **Self-documenting** — `tool_list` shows all available context tools with descriptions
6. **Extensible** — new tools automatically become available as context sources
7. **Provider overrides still work** — `provider:` directive can still override formatting per-provider

### Risks

1. **Performance** — Tool calls add function dispatch overhead vs direct variable resolution. Mitigated: context tools read cached data, no network calls.
2. **Backward compatibility** — Old soul templates with `{{variables}}` would break. Mitigated: Phase 1 keeps both paths working.
3. **Tool context threading** — `tools_execute()` needs access to the tools context, which `prompt_template_build_messages()` doesn't currently have. Solution: pass `tools_context_t*` to the build function.
