Files
sovereign_browser/plans/chat-page-discrete-events.md

7.8 KiB
Raw Blame History

Chat Page — Discrete Event Model

Goal

Redesign the sovereign://agents/chat page to work with sovereign_browser's discrete event Nostr interaction model (no live subscriptions). The page needs explicit Save/Refresh buttons for conversations and skills, matching how the rest of the browser works.

What's wrong now

The current chat page tries to mimic ai.html's live-subscription model:

  • It auto-saves conversations after each agent response (via a debounced scheduleSave())
  • It fetches the conversation list on page load but has no way to refresh
  • It fetches skills on page load but has no way to refresh
  • New Chat saves an empty conversation immediately (good), but there's no explicit save button for renaming or manual saves

The user wants explicit control: Save to publish, Refresh to fetch.

Design

Conversation list (left pane, top section)

┌─────────────────────────────┐
│ [+ New Chat]  [↻ Refresh]   │
├─────────────────────────────┤
│ > Capital of France    [×]  │  ← selected, click to load
│   EU population        [×]  │
│   Hello world          [×]  │
└─────────────────────────────┘
  • + New Chat — Creates a new local session (does NOT save to Nostr yet). The conversation is "unsaved" until the user sends a message or clicks Save.
  • ↻ Refresh — Fetches the conversation list from Nostr (one-shot relay query via relay_fetch.c) and updates the list. Shows a "Refreshing..." state while fetching.
  • Click a conversation — Loads it from the local SQLite cache (fast, no relay fetch). If the conversation isn't in the cache, fetch it from Nostr.
  • [×] delete button — Deletes the conversation (publishes kind 5 tombstone, removes from local cache).

Chat thread (right pane)

┌─────────────────────────────────────────────┐
│  Capital of France          [✏ Rename] [💾] │  ← conversation header
├─────────────────────────────────────────────┤
│  You: What is the capital of France?        │
│  Assistant: The capital of France is Paris. │
│                                              │
│  [⋯]                                        │  ← dot-menu on each bubble
├─────────────────────────────────────────────┤
│  [Type a message...              ] [Send]   │
└─────────────────────────────────────────────┘
  • ✏ Rename — Inline edit the conversation title. Saves to Nostr on Enter or blur.
  • 💾 Save — Explicitly saves the conversation to Nostr (publishes kind 30078 with the current messages + title). Shows "Saved!" feedback.
  • Auto-save is removed — The user must click Save to persist. The only exception: the first message exchange auto-saves once (to create the Nostr event), so the conversation appears in the list on other devices. After that, explicit Save only.

Skills list (left pane, bottom section)

┌─────────────────────────────┐
│ Skills              [↻ Refresh] │
├─────────────────────────────┤
│ [✓] Web scraper    [requires: │
│      browser,fs]   [×]        │
│ [ ] Code reviewer  [requires: │
│      shell]        [×]        │
│ [+ Create Skill]              │
└─────────────────────────────┘
  • ↻ Refresh — Fetches skills from Nostr (one-shot relay query) and updates the list.
  • Checkbox — Toggle skill selection (saved locally, not to Nostr).
  • [×] delete — Deletes the skill (kind 5 tombstone) — only for user-authored skills.
  • + Create Skill — Opens the create form. "Publish" button saves to Nostr.

New endpoints needed

  • sovereign://agents/conversations/refresh (GET) — Triggers a one-shot relay fetch for kind 30078 events with t:client-ai-chat-v1 by the user's pubkey. Stores results in SQLite. Returns {"status":"refreshed","count":N} when done. This is a blocking call (waits for relay response or timeout).

  • sovereign://agents/skills/refresh (GET) — Triggers a one-shot relay fetch for kind 31123 events. Stores in SQLite. Returns {"status":"refreshed","count":N}.

  • sovereign://agents/conversations/rename (GET with ?id=...&title=...) — Renames a conversation. Updates the local SQLite cache and re-publishes the kind 30078 event with the new title. Returns {"status":"renamed"}.

Changes to existing endpoints

  • sovereign://agents/conversations/new — Should NOT save to Nostr immediately. Just create a local session. The conversation is saved to Nostr on the first explicit Save (or after the first message exchange).

  • sovereign://agents/conversations/save — Keep as-is (explicit save). Remove the auto-save call from applyStatus() in chat.js.

Changes to www/agents/chat.js

  1. Remove scheduleSave() / doSave() auto-save — Delete the debounced auto-save logic. The user clicks Save explicitly.

  2. Add Refresh button handlerrefreshConvList() calls sovereign://agents/conversations/refresh, then re-fetches the list.

  3. Add Rename button handlerrenameConv(id) prompts for a new title, calls sovereign://agents/conversations/rename?id=...&title=....

  4. Add Save button handlersaveConv() calls sovereign://agents/conversations/save?title=... explicitly.

  5. Add Skills Refresh button handlerrefreshSkills() calls sovereign://agents/skills/refresh, then re-fetches the skills list.

  6. First-message auto-save — After the first agent response in a new conversation, auto-save once (so the conversation exists on Nostr). After that, no auto-save.

Changes to src/nostr_bridge.c

  1. Add handle_agents_conversations_refresh() — Calls a new agent_conversations_refresh() function that does a one-shot relay fetch.

  2. Add handle_agents_skills_refresh() — Calls a new agent_skills_refresh() function that does a one-shot relay fetch.

  3. Add handle_agents_conversations_rename() — Calls agent_conversations_rename(id, title).

  4. Update handle_agents_conversations_new() — Remove the immediate agent_conversations_save() call. Just create the local session.

Changes to src/agent_conversations.c / src/agent_skills.c

  1. Add agent_conversations_refresh() — One-shot relay fetch for kind 30078 t:client-ai-chat-v1 events by the user's pubkey. Store in SQLite. This reuses the relay fetch logic from relay_fetch.c but with a specific filter.

  2. Add agent_conversations_rename(id, title) — Fetch the existing event, decrypt, update the title, re-encrypt, re-publish.

  3. Add agent_skills_refresh() — One-shot relay fetch for kind 31123 events. Store in SQLite.

Changes to www/agents/chat.html

Add the Refresh, Rename, and Save buttons to the HTML structure.

Phasing

Phase 1: Add Refresh buttons (conversations + skills) and the refresh endpoints. Remove auto-save. Add explicit Save button.

Phase 2: Add Rename button and endpoint.

Phase 3: First-message auto-save (so new conversations appear on Nostr after the first exchange, but subsequent saves are explicit).