# 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/:d_tag` | 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