159 lines
6.1 KiB
Markdown
159 lines
6.1 KiB
Markdown
# Plan: Unified Prompt Context for HTTP API and Nostr Paths
|
|
|
|
## Problem
|
|
|
|
The agent produces completely different LLM context depending on whether a message arrives via **Nostr DM** or the **HTTP API CLI chat app**.
|
|
|
|
### Nostr Path (working correctly)
|
|
- `agent_on_message()` → `agent_build_admin_messages_json()` → tool loop
|
|
- Builds **18 sections, ~8224 bytes** of context including:
|
|
- System prompt / personality (from soul template)
|
|
- Agent identity (pubkey)
|
|
- Sender verification (admin tier)
|
|
- Admin context (kind 0 profile, relay list, recent posts)
|
|
- Startup events memory
|
|
- Adopted skills
|
|
- DM history (decrypted from Nostr relays)
|
|
- Current user message
|
|
|
|
### HTTP API Path (broken)
|
|
- CLI sends `{messages: [{role: "user", content: "Hello"}]}` to `/api/prompt/run`
|
|
- `run_prompt_with_tools()` passes these raw messages directly to the LLM
|
|
- Result: **1 section, ~35 bytes** — just the bare user message, zero agent context
|
|
|
|
## Solution: New `POST /api/prompt/agent` Endpoint
|
|
|
|
Add a new endpoint that mirrors the Nostr path's context assembly, so the CLI gets the same full agent context.
|
|
|
|
### Architecture
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
A[Nostr DM arrives] --> B[agent_on_message]
|
|
B --> C[agent_build_admin_messages_json]
|
|
C --> D[Append user message]
|
|
D --> E[Tool loop with llm_chat_with_tools_messages]
|
|
E --> F[Send DM reply]
|
|
|
|
G[CLI sends POST /api/prompt/agent] --> H[handle_prompt_agent]
|
|
H --> C
|
|
C --> I[Append user message]
|
|
I --> J[Tool loop - same as run_prompt_with_tools but with context]
|
|
J --> K[Return JSON response]
|
|
|
|
style C fill:#4a9,stroke:#333,color:#fff
|
|
```
|
|
|
|
Both paths share `agent_build_admin_messages_json()` as the single source of truth for context assembly.
|
|
|
|
### Request Format
|
|
|
|
```json
|
|
{
|
|
"message": "What is the capital of France?",
|
|
"model": "claude-haiku-4.5",
|
|
"max_turns": 4
|
|
}
|
|
```
|
|
|
|
| Field | Type | Required | Description |
|
|
|---|---|---|---|
|
|
| `message` | string | yes | The user message to send to the agent |
|
|
| `model` | string | no | Override the configured LLM model for this request |
|
|
| `max_turns` | int | no | Max tool-use turns, default 4, max 16 |
|
|
|
|
### Response Format
|
|
|
|
Same as existing `/api/prompt/run`:
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"final_response": "The capital of France is Paris.",
|
|
"turns": [...],
|
|
"model_used": "claude-haiku-4.5",
|
|
"total_input_tokens_estimate": 1973,
|
|
"total_output_tokens_estimate": 12
|
|
}
|
|
```
|
|
|
|
## Implementation Steps
|
|
|
|
### 1. Add `handle_prompt_agent()` in `src/http_api.c`
|
|
|
|
New function that:
|
|
1. Parses the JSON body to extract `message`, optional `model`, optional `max_turns`
|
|
2. Applies model override if present via `maybe_model_override_begin()`
|
|
3. Calls `agent_build_admin_messages_json(message, DIDACTYL_SENDER_ADMIN, &base_messages_json)` — same call the Nostr path uses
|
|
4. Parses the result into a cJSON array
|
|
5. Appends `{role: "user", content: message}` to the array — same as `agent_on_message()` does at line 1916
|
|
6. Builds tool schema via `tools_build_openai_schema_json()`
|
|
7. Runs the same tool loop as `run_prompt_with_tools()` but using the context-enriched messages
|
|
8. Logs context via `agent_append_context_log("http_api_agent", "llm_chat_with_tools_messages", messages_json)`
|
|
9. Returns the same response format as `/api/prompt/run`
|
|
|
|
Key reference points in existing code:
|
|
- Context building: `agent_build_admin_messages_json()` at `src/agent.c:1737`
|
|
- User message append: `append_simple_message()` pattern at `src/agent.c:1916`
|
|
- Tool loop: reuse the loop logic from `run_prompt_with_tools()` at `src/http_api.c:278-345`
|
|
- Context logging: `agent_append_context_log()` at `src/agent.c:1932`
|
|
|
|
### 2. Register the Route in `http_handler()`
|
|
|
|
Add before the existing `/api/prompt/run` route at `src/http_api.c:648`:
|
|
|
|
```c
|
|
if (method_is(hm, "POST") && mg_match(hm->uri, mg_str("/api/prompt/agent"), NULL)) {
|
|
handle_prompt_agent(c, hm);
|
|
return;
|
|
}
|
|
```
|
|
|
|
### 3. Update `chat-didactyl-cli.js`
|
|
|
|
Change the CLI to call the new endpoint:
|
|
|
|
- Change `callDidactyl()` to POST to `/api/prompt/agent` instead of `/api/prompt/run`
|
|
- Send `{message: "user text", max_turns: N}` instead of `{messages: [...], max_turns: N}`
|
|
- The CLI no longer needs to maintain a `transcript` array for context — the server handles DM history from Nostr relays
|
|
- Keep the transcript for local display purposes only
|
|
|
|
### 4. Context Logging Parity
|
|
|
|
Use a distinct but parallel phase label:
|
|
- Nostr path: `llm_chat_with_tools_messages` (existing)
|
|
- HTTP API agent path: `llm_chat_with_tools_messages_agent_api` (new)
|
|
- HTTP API raw path: `llm_chat_with_tools_messages_http_api` (existing, unchanged)
|
|
|
|
This lets you distinguish the source in `context.log.md` while confirming the context structure is identical.
|
|
|
|
### 5. Update `docs/API.md`
|
|
|
|
Add documentation for the new `POST /api/prompt/agent` endpoint following the existing documentation style.
|
|
|
|
## Files to Modify
|
|
|
|
| File | Change |
|
|
|---|---|
|
|
| `src/http_api.c` | Add `handle_prompt_agent()` function and route registration |
|
|
| `chat-didactyl-cli.js` | Switch to `/api/prompt/agent`, simplify payload |
|
|
| `docs/API.md` | Document new endpoint |
|
|
|
|
## What Stays the Same
|
|
|
|
- `/api/prompt/run` — unchanged, still accepts raw message arrays for custom/advanced use
|
|
- `/api/prompt/run-simple` — unchanged
|
|
- `/api/context/current` and `/api/context/parts` — unchanged
|
|
- `agent_build_admin_messages_json()` — unchanged, already does exactly what we need
|
|
- Nostr message handling — unchanged
|
|
|
|
## Remaining Consideration: Conversation History
|
|
|
|
The Nostr path gets DM history by querying encrypted kind-4 events from relays. The new `/api/prompt/agent` endpoint will include this same history since it calls `agent_build_admin_messages_json()`. This means:
|
|
|
|
- Messages sent via the CLI will NOT appear in the Nostr DM history (they are not published as Nostr events)
|
|
- Messages sent via Nostr WILL appear in the context when using the CLI
|
|
- This is acceptable — the CLI is a development/admin tool that piggybacks on the agent's full context
|
|
|
|
If in the future you want CLI messages to also appear in history, that would require either publishing them as Nostr DMs or maintaining a separate local history store — but that is out of scope for this change.
|