# Didactyl Admin Web Frontend — Project Brief

## What Is Didactyl?

Didactyl is a sovereign AI agent that lives on Nostr. It connects to Nostr relays, listens for encrypted DMs from its administrator, reasons with an LLM, and takes actions — posting events, querying relays, running shell commands, managing skills. Everything the agent knows and does is stored as Nostr events.

The agent is a C binary that runs on a server. It has no web interface of its own — all interaction happens through Nostr DMs.

## What We Are Building

A **local web admin dashboard** that connects to the running didactyl agent via a localhost HTTP API. The dashboard is a prompt crafting and agent inspection tool for the administrator.

This is **not** a chat interface. The administrator already chats with the agent through Nostr DMs. This dashboard is for:

1. **Inspecting** what the agent sees — its full LLM context, broken into labeled parts with token counts
2. **Crafting** custom prompts — editing system prompts, user messages, and context pieces
3. **Running** prompts against the LLM — with or without the agent tool set
4. **Comparing** prompt variants side-by-side — A/B testing different prompt wordings or models

---

## The API

The didactyl agent exposes a localhost-only HTTP API on port `8484` by default. Full API documentation is in `docs/API.md`. All endpoints return JSON with CORS headers.

### Base URL

```
http://127.0.0.1:8484
```

### Currently Implemented Endpoints

| Method | Path | Purpose |
|---|---|---|
| GET | `/api/status` | Agent runtime status — name, version, pubkey, relay count, trigger count |
| GET | `/api/context/current` | Full LLM context messages array with total char/token counts |
| GET | `/api/context/parts` | Context broken into labeled parts with individual sizes |
| POST | `/api/prompt/run-simple` | Simple prompt: system + user message, no tools, returns text |
| POST | `/api/prompt/run` | Full prompt: messages array with tools enabled, returns conversation trace |
| POST | `/api/prompt/compare` | A/B test: two prompt variants run sequentially, responses side-by-side |
| GET | `/api/model` | Current LLM model config (provider, model, base_url, max_tokens, temperature) |
| PUT | `/api/model` | Change model at runtime — persists to config.json |
| GET | `/api/models` | List available models from the configured provider |

### Planned Future Endpoints

These are not yet implemented but are on the roadmap:

| Method | Path | Purpose |
|---|---|---|
| GET | `/api/config` | Runtime config with redacted secrets |
| GET | `/api/events/soul` | Agent soul/system prompt event |
| PUT | `/api/events/soul` | Update soul content |
| GET | `/api/events/skills` | List skills |
| GET/PUT | `/api/events/skills/:slug` | Read/update individual skills |
| GET | `/api/events/profile` | Agent Nostr profile |
| GET | `/api/tools` | List all tool schemas |
| POST | `/api/tools/:name/execute` | Execute a tool directly |
| GET | `/api/triggers` | Active trigger subscriptions |
| GET | `/api/relays` | Relay connection status |

---

## Core User Workflows

### 1. Context Inspector

The primary read-only workflow. The admin wants to understand what the agent sees when it processes a message.

```mermaid
flowchart TD
    LOAD[Load /api/context/parts] --> DISPLAY[Display parts list]
    DISPLAY --> DETAIL[Click part to expand content]
    DETAIL --> TOKENS[Show char count and token estimate per part]
    TOKENS --> TOTAL[Show total context size]
```

**What to show:**
- A list/table of context parts with name, role, character count, estimated tokens
- Total context size as a summary bar or header
- Expandable content for each part
- The parts are: `system_prompt`, `admin_identity`, `admin_profile`, `admin_relay_list`, `startup_events`, `adopted_skills`, `dm_history` (one entry per turn, up to limit), `admin_notes`
- Part names come from the `---template---` section of the soul event (kind 31120); they may differ if the soul is customised

### 2. Simple Prompt Crafting

Quick iteration on prompt wording without tools.

```mermaid
flowchart TD
    WRITE[Write system prompt + user message] --> RUN[POST /api/prompt/run-simple]
    RUN --> RESULT[Display response text]
    RESULT --> EDIT[Edit and re-run]
    EDIT --> RUN
```

**What to show:**
- Two text areas: system prompt, user message
- Optional model override dropdown/input
- Run button
- Response display with model used and token estimates

### 3. Full Prompt with Tools

Craft a complete messages array and run it with the agent tool set.

```mermaid
flowchart TD
    CONTEXT[Load context from /api/context/parts] --> EDIT[Edit/rearrange context parts]
    EDIT --> ADD[Add user message]
    ADD --> RUN[POST /api/prompt/run]
    RUN --> TRACE[Display conversation trace]
    TRACE --> TOOLS[Show tool calls and results per turn]
    TOOLS --> FINAL[Show final response]
```

**What to show:**
- Pre-populate from context parts or start from scratch
- Messages editor — add/remove/reorder messages with role and content
- Max turns slider/input
- Optional model override
- Run button
- Turn-by-turn trace showing tool calls with name, arguments, and results
- Final response text
- Token estimates

### 4. A/B Prompt Comparison

Compare two prompt variants side-by-side.

```mermaid
flowchart TD
    CRAFT_A[Craft variant A messages] --> CRAFT_B[Craft variant B messages]
    CRAFT_B --> COMPARE[POST /api/prompt/compare]
    COMPARE --> SIDE[Display responses side-by-side]
    SIDE --> DIFF[Compare final responses and token usage]
```

**What to show:**
- Two prompt editors side-by-side, each with messages array + model override + max turns
- Compare button
- Side-by-side response display
- Highlight differences in final response text
- Token usage comparison

### 5. Status Dashboard

Simple overview of agent health.

**What to show:**
- Agent name, version, pubkey
- Connected relays count vs configured
- Active triggers count
- API connection status indicator

---

## Key Design Decisions

### Localhost Only

The API binds to `127.0.0.1` — the frontend must run on the same machine as the agent, or use SSH tunneling. There is no authentication. This is intentional — it is a local dev/admin tool.

### No WebSocket

The API is plain HTTP request/response. There is no WebSocket or streaming. Prompt execution calls may take several seconds for LLM responses — the frontend should show a loading state.

### Token Estimation

All token counts from the API use a `chars / 4` heuristic. This is approximate. The frontend can display these as-is or add its own tokenizer if more precision is needed.

### Model Override

The `model` field in prompt requests temporarily overrides the agent configured model for that single request, then restores the original. This enables cross-model comparison without changing agent config.

### Tool Execution Is Real

When using `/api/prompt/run` or `/api/prompt/compare`, tool calls are **actually executed**. If the LLM decides to post a Nostr event, it will really post it. The frontend should make this clear to the user — perhaps with a warning or confirmation before running prompts with tools enabled.

---

## Response Shapes Quick Reference

### Status

```json
{
  "success": true,
  "name": "Didactyl",
  "version": "v0.0.26",
  "pubkey": "52a3e8...",
  "relay_count": 4,
  "connected_relays": 4,
  "active_triggers": 0
}
```

### Context Parts

```json
{
  "success": true,
  "total_chars": 13131,
  "total_estimated_tokens": 3283,
  "parts": [
    {
      "name": "system_prompt",
      "role": "system",
      "chars": 1200,
      "estimated_tokens": 300,
      "content": "# Didactyl Agent..."
    }
  ],
  "messages": [...]
}
```

### Simple Prompt Response

```json
{
  "success": true,
  "response": "ok",
  "model_used": "claude-haiku-4.5",
  "input_tokens_estimate": 10,
  "output_tokens_estimate": 1
}
```

### Full Prompt Response

```json
{
  "success": true,
  "final_response": "Done! I posted a tweet.",
  "turns": [
    {
      "turn": 1,
      "tool_calls": [
        {"name": "nostr_post", "arguments": "...", "result": "..."}
      ]
    }
  ],
  "model_used": "claude-haiku-4.5",
  "total_input_tokens_estimate": 3200,
  "total_output_tokens_estimate": 180
}
```

### Compare Response

```json
{
  "success": true,
  "variant_a": { "...same shape as full prompt response..." },
  "variant_b": { "...same shape as full prompt response..." }
}
```

### Error Response

```json
{
  "success": false,
  "error": "description of what went wrong"
}
```

---

## Technology Suggestions

No technology is mandated for the frontend. Some reasonable choices:

- **Vanilla HTML/JS** — simplest, no build step, just open in browser
- **React/Preact** — if you want component structure
- **Svelte** — lightweight, good for small dashboards
- **Vue** — also fine

The frontend is a separate project from didactyl. It just needs to make HTTP requests to `localhost:8484`.

---

## File References

| File | Description |
|---|---|
| `docs/API.md` | Full API endpoint reference with request/response examples |
| `plans/admin_api.md` | Original architecture plan for the HTTP API |
| `src/http_api.c` | C implementation of all endpoints |
| `src/http_api.h` | Public API header |
| `config.json.example` | Example config showing the `api` section |

---

## Getting Started

1. Ensure didactyl is running with `api.enabled: true` in config.json
2. Verify the API is up: `curl http://127.0.0.1:8484/api/status`
3. Build the frontend to talk to `http://127.0.0.1:8484`
4. Start with the status endpoint and context inspector, then add prompt crafting
