🎁 Get the FREE AI Skills Starter GuideSubscribe →
BytesAgainBytesAgain
🦀 ClawHub

OpenPot Awareness

by @almnotai

Teaches this agent how to serve content to the OpenPot iOS client — cards, apps, page captures, calendar, voice, chat persistence, and onboarding

Versionv6.0.0
Downloads336
TERMINAL
clawhub install openpot-awareness

📖 About This Skill


name: openpot-awareness description: Teaches this agent how to serve content to the OpenPot iOS client — cards, apps, page captures, calendar, voice, chat persistence, and onboarding emoji: 🫕 version: 6.0.0 homepage: https://openpot.app

OpenPot Awareness Skill

You are connected to OpenPot — a native iOS app that serves as a command center for AI agents. OpenPot has configurable tabs: Chat (always on), Pulse (notification cards), Calendar, Apps, Terminal, and Agents (always on). Users choose which tabs to display in Settings — not every user will have all tabs visible.

Three Output Surfaces

| Surface | When to use | How | |---------|-------------|-----| | Chat | Default. Conversations, answers, follow-ups. | Normal chat response | | Pulse cards | Proactive output: reports, alerts, briefs. | POST /api/cards | | Web apps | Persistent tools the user returns to. | Build HTML, serve via /api/apps |

Default to chat. Use cards for output the user did not ask for in the current conversation. Use apps only when the user requests a persistent tool.

Decision Framework — Chat vs. Card vs. App

| Situation | Surface | Why | |-----------|---------|-----| | User asked a question | Chat | They're in a conversation. Answer there. | | Cron job produced output | Card | User didn't ask. Push it to Pulse. | | Alert threshold crossed | Card | Proactive. User needs to know. | | User asked for a persistent tool | App | They want something that lives in their toolkit. | | User asked about a previous card | Chat | They're referencing it in conversation. | | Scheduled digest or rollup | Card | Proactive summary, not a conversation. |

When in doubt, use chat. Cards and apps are for specific use cases.

Triggers

Activate this skill when:

  • User says "OpenPot sync" — run the sync process (see Sync section)
  • User sends a page capture (message contains
  • ---PAGE CAPTURE CONTEXT---) — see Page Captures section
  • User asks about setting up OpenPot — see Onboarding section
  • User asks about calendar, voice, chat persistence, or
  • building an app — see the relevant section below
  • User asks "what OpenPot features do I support?" — check status
  • User asks to "set up chat persistence" or "save my chats"
  • see Chat Persistence section


    Pulse Cards

    Pulse cards are proactive notifications you push to the user's OpenPot app. They appear in the Pulse tab as a card stream.

    When to Create a Card

  • Scheduled output (cron jobs): morning briefs, DCA signals, health checks
  • Threshold alerts: a metric crossed a boundary the user cares about
  • Proactive observations: something changed that the user should know
  • Digests: summaries of activity over a time period
  • Do NOT create a card for content the user asked for in the current conversation. That belongs in chat.

    Card API

    Endpoint: POST /api/cards

    Required fields:

    | Field | Type | Description | |-------|------|-------------| | title | String | Card headline. Under 60 characters. | | body | String | 1-2 line summary visible on the compact card. | | category | String | Determines Pulse channel routing. Use canonical list below. | | agent_id | String | Your agent ID. |

    Optional fields:

    | Field | Type | Description | |-------|------|-------------| | priority | String | "normal" (default) or "high". Reserve high for genuinely urgent items. | | origin | String | Why this card was created: "cron", "alert", "agent_initiated", "announce". | | expanded_body | String | Full markdown report. When present, the card is tappable and opens a detail view. | | actions | [String] | Action buttons in the detail view. Vocabulary: "discuss", "dismiss", "acknowledge", "snooze". |

    Body Text Rule

    The body field is ALWAYS a complete thought. Never a sentence fragment that leaves the user wondering what the rest says. If the body is cut off mid-sentence, the card feels broken.

  • Short notifications (1-2 lines): write the complete message
  • Medium content (4-10 lines): write the full text — OpenPot unfolds it inline
  • Reports with tables/sections: keep body as 1-2 line summary, put detail in expanded_body
  • Minimal Card Example

    {
      "title": "System Health Check",
      "body": "All services operational. CPU 23%, memory 41%.",
      "category": "system",
      "agent_id": "your-agent-id",
      "priority": "normal",
      "origin": "cron"
    }
    

    Report Card Example (with expanded detail)

    {
      "title": "Weekly DCA Signals",
      "body": "5 double-down signals, 4 baseline holds.",
      "category": "finance",
      "agent_id": "your-agent-id",
      "priority": "normal",
      "origin": "cron",
      "expanded_body": "## Weekly DCA Signal Report\n\nGenerated: Monday 4:30 PM ET\n\n| Ticker | Price | Signal | Conviction |\n|--------|-------|--------|------------|\n| CRCL | $2.14 | Double-down | High |\n| AFRM | $41.30 | Double-down | Medium |\n\n4 baseline holds. Market weakness = accumulation window.",
      "actions": ["discuss", "dismiss"]
    }
    

    Canonical Category List

    Use these exact strings. Inconsistent casing or synonyms create duplicate channels.

    | Category | Use for | |----------|---------| | briefing | Morning briefs, evening summaries, weekly rollups | | system | Health checks, service status, uptime reports | | finance | DCA signals, portfolio updates, market observations | | calendar | Schedule digests, conflict alerts, deadline warnings | | projects | Task updates, milestone tracking, blockers | | education | Learning content, research summaries | | health | Health tracking, medication reminders | | entertainment | Media recommendations, leisure suggestions |

    You may create new categories when the user's needs expand. When you do, include a note in your first card: "I've created a new Pulse channel for [topic]. You can rename or reorganize it."

    Expanded Cards (Tap-to-Open Detail)

    When a card represents a report, include an expanded_body field with the full markdown content.

  • Keep body as a 1-2 line summary (the compact card preview).
  • Put the full analysis in expanded_body using markdown.
  • Include "actions": ["discuss", "dismiss"] for report cards.
  • Cards without expanded_body are glanceable only.
  • Keep total expanded_body under 4,000 characters.
  • Cards that SHOULD have expanded_body: DCA signal reports, morning briefs, system health diagnostics, any multi-item analysis or report.

    Cards that should NOT: Simple reminders, single-fact notifications, calendar alerts.

    Action Buttons

    | Action | Button Label | Behavior | |--------|-------------|----------| | "discuss" | "Discuss with [Agent]" | Opens chat with the card content as context | | "dismiss" | "Dismiss" | Closes the detail view and dismisses the card | | "acknowledge" | "Got it" | Marks the card as read and closes | | "snooze" | "Snooze" | Dismisses temporarily, resurfaces later |


    Web Apps

    Web apps are persistent HTML tools that live in the user's Apps tab. The Apps tab displays apps as an iOS-style grid with emoji icons. Unlike cards (ephemeral notifications), apps are tools the user returns to repeatedly.

    When to Build an App

    Only when the user explicitly requests a persistent tool. Examples: "Build me a medication tracker," "I need a DCA calculator." Never build an app speculatively. Always confirm before building.

    App Types

    | Type | Description | Backend needed? | |------|-------------|-----------------| | Type 1: Static tool | Calculator, converter, reference — pure HTML/CSS/JS | No | | Type 2: Smart app | Tracker, dashboard — needs persistent data via backend API | Yes | | Type 3: Connected app | Integrates with external APIs via the agent's backend | Yes |

    File Rules

  • One self-contained HTML file per app. All CSS and JS inline.
  • Filename: lowercase, hyphenated, descriptive. Example: medication-tracker.html
  • Title: set in </code> tag. This appears in the Apps tab.</li> <li style="color:#94a3b8;margin:3px 0">Served by your HTTP server via <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">GET /api/apps</code> (listing) and direct URL (content).</li></p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">App Metadata</h3></p><p style="margin:8px 0">Your <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">GET /api/apps</code> endpoint must return metadata for each app:</p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">[ { "filename": "medication-tracker.html", "title": "Medication Tracker", "description": "Track daily medications and schedules", "category": "health", "emoji": "💊" } ] </code></pre></p><p style="margin:8px 0">The <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">emoji</code> field is displayed as the app's icon in the grid. Choose an emoji that represents the app's purpose. If <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">emoji</code> is omitted, OpenPot uses the first letter of the title as a fallback.</p><p style="margin:8px 0"><strong style="color:#e5e7eb">Required fields:</strong> <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">filename</code>, <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">title</code>, <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">category</code> <strong style="color:#e5e7eb">Recommended fields:</strong> <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">description</code>, <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">emoji</code></p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">App Categories</h3></p><p style="margin:8px 0">| Category | Icon color | |----------|-----------| | tools | Blue | | health | Red | | finance | Green | | projects | Teal | | monitoring | Orange | | entertainment | Purple | | reference | Gray |</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Design Guidelines</h3></p><p style="margin:8px 0"><li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Dark theme default.</strong> Background: <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">#1A1D28</code> or similar dark. Text: white/light gray.</li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Mobile-first.</strong> Renders in a WKWebView on iPhone and iPad. Design for touch.</li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">No scrollbars.</strong> Use CSS <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">overflow: auto</code> with <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">-webkit-overflow-scrolling: touch</code>.</li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Card aesthetic.</strong> Rounded corners (12-16pt), subtle borders.</li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Gold accent.</strong> Use warm gold (<code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">#C9A84C</code> or similar) for primary actions.</li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Readable typography.</strong> Minimum 16px body text.</li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">No external images.</strong> Use inline SVG or CSS-drawn elements.</li></p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Before You Build — Confirmation Step</h3></p><p style="margin:8px 0">Before creating any app, confirm with the user: 1. What the app does (one sentence) 2. Which type (1, 2, or 3) 3. The filename you will use 4. The category (tools, health, finance, projects, monitoring, entertainment) 5. An emoji for the app icon</p><p style="margin:8px 0"><hr style="border:none;border-top:1px solid #1e1e3f;margin:12px 0"></p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Page Captures</h2></p><p style="margin:8px 0">The user can capture web pages from OpenPot's in-app browser. Captures arrive as chat messages with the user's note followed by a structured context block.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">What You Receive</h3></p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">[User's note here]</p><p style="margin:8px 0">---PAGE CAPTURE CONTEXT--- URL: https://example.com/product Title: Product Name Description: Product description text... Site: Example</p><p style="margin:8px 0">Readable Text: [Up to 4,000 characters of extracted page content]</p><p style="margin:8px 0">Tables: | Header | Header | |--------|--------| | Value | Value |</p><p style="margin:8px 0">Screenshot: ~/.openclaw/workspace/attachments/{uuid}.jpg ---END PAGE CAPTURE CONTEXT--- </code></pre></p><p style="margin:8px 0">Fields: URL, Title, Description, Site (metadata), Readable Text (main content up to 4,000 chars), Tables (if present), Screenshot (file path to captured viewport image, omitted in Data Only mode).</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Processing a Capture</h3></p><p style="margin:8px 0">1. <strong style="color:#e5e7eb">Use the text data first.</strong> URL, title, readable text, and tables cover most questions without needing the screenshot.</p><p style="margin:8px 0">2. <strong style="color:#e5e7eb">For visual analysis,</strong> use your <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">read</code> tool on the Screenshot path. Only do this when the question requires seeing the page.</p><p style="margin:8px 0">3. <strong style="color:#e5e7eb">Write a visual description</strong> for your own records after analyzing. Example: "matte black single-handle kitchen faucet, pull-down sprayer, modern industrial style." This description is your permanent memory of the page. The image file is temporary.</p><p style="margin:8px 0">4. <strong style="color:#e5e7eb">Respond to the user's note.</strong> Keep responses concise — the user is in a chat strip inside the browser. If your response needs depth, suggest moving to Chat.</p><p style="margin:8px 0">5. <strong style="color:#e5e7eb">If no note was provided,</strong> confirm briefly: "Noted — [one-line description]. I can pull this up anytime."</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Saving to Pulse</h3></p><p style="margin:8px 0">When the user signals a page has ongoing value — "save this," "remember this," "bookmark this" — push a Pulse card:</p><p style="margin:8px 0"><li style="color:#94a3b8;margin:3px 0">title: Your one-line summary</li> <li style="color:#94a3b8;margin:3px 0">body: Your visual description + the user's note, combined naturally</li> <li style="color:#94a3b8;margin:3px 0">expanded_body: Full details — URL, price/ratings if product, key data</li> <li style="color:#94a3b8;margin:3px 0">category: Most appropriate channel</li> <li style="color:#94a3b8;margin:3px 0">actions: ["discuss", "dismiss"]</li></p><p style="margin:8px 0">Include the URL as a tappable link. Not every capture becomes a card — only when the user signals intent to revisit.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Re-encounters</h3></p><p style="margin:8px 0">When the user returns to a previously captured page, do not re-analyze from scratch. You have your original visual notes, the user's note, the extracted data, and any conversation that followed. Pick up where you left off.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Link Cards</h3></p><p style="margin:8px 0">When recommending a web page to the user, use this format:</p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">:::link url: https://example.com/page title: Page Title site: Site Name note: Why this matters — your annotation. ::: </code></pre></p><p style="margin:8px 0">OpenPot renders these as tappable cards that open in the in-app browser.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Card Recategorization</h3></p><p style="margin:8px 0">When the user moves a card to a different Pulse channel, you receive a notification. This is a learning signal. If the user moves multiple cards of the same type, ask: "Want me to route [type] to [channel] automatically?" Never change routing without asking.</p><p style="margin:8px 0"><hr style="border:none;border-top:1px solid #1e1e3f;margin:12px 0"></p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Calendar</h2></p><p style="margin:8px 0">OpenPot has a Calendar tab with native Year, Month, and Agenda views. The Calendar tab aggregates events from multiple sources into one unified display.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Calendar Sources</h3></p><p style="margin:8px 0">| Source | How it works | |--------|-------------| | Backend API | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">GET /api/calendar/events</code> serves events from any provider (Google Calendar, Apple Calendar, CalDAV, or any ClawHub calendar skill) | | Agent calendar | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">:::calendar</code> blocks in chat create local events stored on-device with gold accent | | User-created | User adds events directly in the Calendar tab via the "+" button or long-press on a date |</p><p style="margin:8px 0">OpenPot does not care where backend events come from. It reads one endpoint. Any calendar skill from ClawHub that feeds into your <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/api/calendar/events</code> endpoint works automatically — Google Calendar, Apple Calendar, CalDAV, Outlook, etc. The unified endpoint normalizes all sources into the same schema.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Authorization Rule</h3></p><p style="margin:8px 0">**Never add, modify, or delete calendar events without explicit user permission.** You may read the calendar, summarize it, and present it. Creating events requires the user to say yes first. Always ask: "Would you like me to add this to your calendar?"</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Event Format</h3></p><p style="margin:8px 0">The <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/api/calendar/events</code> endpoint returns events in this schema:</p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">{ "id": "string — stable unique ID, not a UUID", "title": "string — under 60 chars", "start_date": "ISO 8601 or date-only string", "end_date": "ISO 8601 or date-only (optional)", "is_all_day": true, "notes": "string or null — plain text only, no HTML", "location": "string or null", "calendar_name": "string — human-readable calendar name", "calendar_color": "string — hex color", "source": "string — the provider (google_calendar, apple_calendar, caldav, agent, manual, etc.)", "status": "confirmed | tentative | cancelled" } </code></pre></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Date Format Rules</h4></p><p style="margin:8px 0">| Event Type | start_date Format | Example | |------------|-------------------|---------| | All-day | Date-only string | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">"2026-04-14"</code> | | Timed | ISO 8601 with timezone offset | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">"2026-04-14T09:00:00-04:00"</code> | | Query param | ISO 8601 with Z | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">"2026-04-01T04:00:00Z"</code> |</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Calendar Context Updates</h3></p><p style="margin:8px 0">OpenPot automatically sends calendar context messages when the user interacts with the Calendar tab. These arrive as chat messages wrapped in <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">[calendar_context]</code> tags.</p><p style="margin:8px 0"><strong style="color:#e5e7eb">When you receive a <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">[calendar_context]</code> message:</strong></p><p style="margin:8px 0"><li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Absorb the schedule information silently.</strong> Do NOT respond to the</li> message — no acknowledgment, no confirmation, no "Got it." <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Use the context to inform future conversations.</strong> If the user asks</li> "what do I have today," "am I free this afternoon," or "what's coming up this week," reference the most recent calendar context you received. <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Calendar context replaces any previously received context.</strong> Always</li> use the most recent one. Do not accumulate old context. <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Never quote the raw context back to the user.</strong> Synthesize it</li> naturally into your responses.</p><p style="margin:8px 0">Example incoming context:</p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">[calendar_context] Today (April 14, 2026): <li style="color:#94a3b8;margin:3px 0">Cardiac APP Review at 9:00 AM (Room 4B)</li> <li style="color:#94a3b8;margin:3px 0">Team standup at 11:00 AM</li> Tomorrow (April 15, 2026): <li style="color:#94a3b8;margin:3px 0">Oil Change — Miata at 9:00 AM</li> Upcoming 7 days: 8 events [/calendar_context] </code></pre></p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Ask About Event</h3></p><p style="margin:8px 0">The user may send a message prefixed with "Tell me about this event:" followed by event details (title, date/time, location, calendar name). This is triggered from the Calendar tab's long-press menu. When you receive this:</p><p style="margin:8px 0"><li style="color:#94a3b8;margin:3px 0">Provide relevant context about the event from your memory</li> <li style="color:#94a3b8;margin:3px 0">Suggest preparation steps if appropriate</li> <li style="color:#94a3b8;margin:3px 0">Reference related information you know about</li> <li style="color:#94a3b8;margin:3px 0">If you have no additional context, say so honestly and offer to</li> help with logistics (directions, reminders, related tasks)</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Calendar Pulse Cards</h3></p><p style="margin:8px 0">When creating a calendar-category Pulse card: <li style="color:#94a3b8;margin:3px 0">Use <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">"category": "calendar"</code></li> <li style="color:#94a3b8;margin:3px 0">Do NOT include <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">expanded_body</code> — calendar cards open the calendar view</li> <li style="color:#94a3b8;margin:3px 0">Include <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">"actions": ["view_calendar", "dismiss"]</code></li></p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Common Pitfalls</h3></p><p style="margin:8px 0">1. <strong style="color:#e5e7eb">String IDs, not UUIDs.</strong> UUID objects cause decode failures on iOS. 2. <strong style="color:#e5e7eb">Timezone offsets required on timed events.</strong> Omitting the offset causes events to render at wrong times. 3. <strong style="color:#e5e7eb">All-day events use date-only strings.</strong> Do not add <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">T00:00:00</code>. 4. <strong style="color:#e5e7eb">Calendar color must be a hex string.</strong> Format: <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">"#f83a22"</code>. 5. <strong style="color:#e5e7eb">Notes field: plain text only.</strong> HTML renders as raw tags on iOS. Strip all HTML before serving notes to OpenPot. 6. <strong style="color:#e5e7eb">Query all accessible calendars.</strong> Do not limit to a single calendar — users have shared, subscribed, and multiple provider calendars. 7. <strong style="color:#e5e7eb">The endpoint must handle both date formats as query params.</strong> 8. <strong style="color:#e5e7eb">Restart the backend server after configuration changes.</strong> 9. <strong style="color:#e5e7eb">Do not return HTML in any text field.</strong> OpenPot renders it as raw tags, not formatted content.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Calendar Setup Steps</h3></p><p style="margin:8px 0">1. Ensure your backend server has a <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/api/calendar/events</code> endpoint 2. Configure credentials for your calendar provider (Google OAuth, Apple EventKit permissions, CalDAV credentials, etc.) 3. Store tokens and credentials securely (not in SOUL.md or version control) 4. Test: <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">curl -H "Authorization: Bearer <token>" http://localhost:8000/api/calendar/events?start=2026-04-01&end=2026-04-30</code> 5. Update <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">openpot-status.json</code> with calendar feature as installed 6. Restart the backend server</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Creating Calendar Events</h3></p><p style="margin:8px 0">The user may have calendars connected from external providers (Google Calendar, Apple Calendar, CalDAV), and each provider may have multiple sub-calendars (personal, work, family, hobbies, sports, etc.). If no external calendars are connected yet, you can help set that up — see the Calendar Setup Steps above.</p><p style="margin:8px 0">Regardless of what external calendars are connected, you always have your agent calendar. It is yours, stored locally on the OpenPot device, and requires no backend or provider setup.</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Which Calendar? Always Clarify.</h4></p><p style="margin:8px 0">When the user asks you to add an event, you need to know which calendar it belongs on. If the user doesn't specify, ask:</p><p style="margin:8px 0">**"Which calendar should I put this on — your [list their known calendars], or my agent calendar?"**</p><p style="margin:8px 0">Learn the user's calendars early. When you first access their calendar data, note which calendars exist and what they're used for. Over time you'll know which calendar is for what without asking.</p><p style="margin:8px 0"><strong style="color:#e5e7eb">Routing rules:</strong> <li style="color:#94a3b8;margin:3px 0">User says "my calendar" or names a specific calendar → use the</li> appropriate provider calendar API <li style="color:#94a3b8;margin:3px 0">User says "agent calendar," "your calendar," or "track this for me"</li> → use the <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">:::calendar</code> block (agent calendar) <li style="color:#94a3b8;margin:3px 0">User says "remind me" or "don't forget" about a future date → use</li> the <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">:::calendar</code> block with a <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">remind</code> interval <li style="color:#94a3b8;margin:3px 0">User asks you to add to a specific provider calendar → use that</li> provider's API (Google Calendar API, etc.) <li style="color:#94a3b8;margin:3px 0">When in doubt → ask. Never guess.</li></p><p style="margin:8px 0"><strong style="color:#e5e7eb">You have full access to provider calendars.</strong> You can read, create, edit, and delete events on the user's connected calendars (Google, Apple, CalDAV, etc.) using their respective APIs. The agent calendar is an additional calendar, not a replacement.</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Agent Calendar — The <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">:::calendar</code> Block</h4></p><p style="margin:8px 0">To put an event on YOUR agent calendar, include a <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">:::calendar</code> block in your chat response. OpenPot parses it, stores it locally on the device, and displays it on the Calendar tab with a gold accent color.</p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">:::calendar title: Trailer Registration Expires date: 2027-07-15 notes: Renew at DMV or online remind: 2w ::: </code></pre></p><p style="margin:8px 0">This works on any tier — no backend server needed.</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Required Fields</h4></p><p style="margin:8px 0">| Field | Format | Description | |-------|--------|-------------| | title | String | Event title, under 60 characters | | date | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">YYYY-MM-DD</code> | The date of the event |</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Optional Fields</h4></p><p style="margin:8px 0">| Field | Format | Description | |-------|--------|-------------| | end_date | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">YYYY-MM-DD</code> | End date for multi-day events | | time | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">HH:MM</code> | Start time (24h format). Omit for all-day events. | | end_time | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">HH:MM</code> | End time (24h). Only valid with <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">time</code>. | | notes | String | Additional context, plain text | | category | String | Maps to a Pulse channel for future alerts | | remind | String | When to push a reminder card: <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">1d</code>, <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">1w</code>, <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">2w</code>, <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">1m</code>, <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">3m</code> |</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">How It Works</h4></p><p style="margin:8px 0"><li style="color:#94a3b8;margin:3px 0">The <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">:::calendar</code> block is parsed by OpenPot and replaced with a</li> compact calendar card in the chat bubble (not shown as raw text) <li style="color:#94a3b8;margin:3px 0">The event is stored locally on the device in the Calendar tab</li> <li style="color:#94a3b8;margin:3px 0">Events appear with a gold accent color to distinguish them from</li> provider calendar sources <li style="color:#94a3b8;margin:3px 0">If <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">remind</code> is set, OpenPot automatically pushes a Pulse card to</li> the user when the reminder window is reached <li style="color:#94a3b8;margin:3px 0">No backend, no HTTP server, no OAuth — works on any tier</li></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Rules</h4></p><p style="margin:8px 0"><li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Always confirm which calendar before creating an event.</strong></li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Never silently create events on any calendar.</strong></li> <li style="color:#94a3b8;margin:3px 0">Keep titles under 60 characters</li> <li style="color:#94a3b8;margin:3px 0">Use date-only for all-day events, add <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">time</code> for timed events</li> <li style="color:#94a3b8;margin:3px 0">Use <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">remind</code> for events the user should be warned about in advance</li> <li style="color:#94a3b8;margin:3px 0">Learn the user's calendar layout early</li></p><p style="margin:8px 0"><hr style="border:none;border-top:1px solid #1e1e3f;margin:12px 0"></p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Chat Persistence</h2></p><p style="margin:8px 0">Chat messages can be stored in a PostgreSQL database on your backend server, giving the user persistent chat history that survives app reinstalls and works across devices. This is a Tier 2 feature — requires an HTTP server on port 8000.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">When to Set Up</h3></p><p style="margin:8px 0">Set up chat persistence when: <li style="color:#94a3b8;margin:3px 0">The user asks: "save my chats," "set up chat persistence," "I want</li> my messages backed up" <li style="color:#94a3b8;margin:3px 0">During OpenPot sync, if you detect an HTTP server on port 8000 but</li> no <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/api/chat/sessions</code> endpoint responding <li style="color:#94a3b8;margin:3px 0">The user reinstalls the app and loses their chat history</li></p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">What You Need</h3></p><p style="margin:8px 0"><li style="color:#94a3b8;margin:3px 0">PostgreSQL database (the same one used for memory)</li> <li style="color:#94a3b8;margin:3px 0">FastAPI server on port 8000 (the same one serving other endpoints)</li> <li style="color:#94a3b8;margin:3px 0">Bearer token authentication (reuse existing token)</li></p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Database Schema</h3></p><p style="margin:8px 0">Create the following in your PostgreSQL database:</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Enums</h4> <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">CREATE TYPE sender_type AS ENUM ('user', 'assistant', 'system'); CREATE TYPE message_type AS ENUM ('text', 'image', 'file', 'voice', 'system'); </code></pre></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Main Table</h4> <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">CREATE TABLE chat_messages ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), session_id VARCHAR(255) NOT NULL, channel VARCHAR(255), content TEXT NOT NULL, sender_type sender_type NOT NULL, sender_name VARCHAR(255), message_type message_type NOT NULL DEFAULT 'text', run_id VARCHAR(255), parent_id UUID REFERENCES chat_messages(id), metadata JSONB DEFAULT '{}', attachments JSONB DEFAULT '[]', protected BOOLEAN DEFAULT FALSE, hidden BOOLEAN DEFAULT FALSE, search_vector tsvector, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); </code></pre></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Indexes</h4> <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">CREATE INDEX idx_chat_messages_session ON chat_messages(session_id, created_at DESC) WHERE hidden = FALSE; CREATE INDEX idx_chat_messages_search ON chat_messages USING GIN(search_vector); CREATE INDEX idx_chat_messages_run ON chat_messages(run_id) WHERE run_id IS NOT NULL; </code></pre></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Search Vector Trigger</h4> <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">CREATE OR REPLACE FUNCTION update_search_vector() RETURNS TRIGGER AS $$ BEGIN NEW.search_vector := setweight(to_tsvector('english', COALESCE(NEW.content, '')), 'A') || setweight(to_tsvector('english', COALESCE(NEW.sender_name, '')), 'B') || setweight(to_tsvector('english', COALESCE(NEW.channel, '')), 'C'); RETURN NEW; END; $$ LANGUAGE plpgsql;</p><p style="margin:8px 0">CREATE TRIGGER chat_messages_search_update BEFORE INSERT OR UPDATE OF content, sender_name, channel ON chat_messages FOR EACH ROW EXECUTE FUNCTION update_search_vector(); </code></pre></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Updated_at Trigger</h4> <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">CREATE OR REPLACE FUNCTION update_updated_at() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql;</p><p style="margin:8px 0">CREATE TRIGGER chat_messages_updated_at BEFORE UPDATE ON chat_messages FOR EACH ROW EXECUTE FUNCTION update_updated_at(); </code></pre></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Session View</h4> <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">CREATE OR REPLACE VIEW chat_sessions AS SELECT session_id, channel, MIN(created_at) AS first_message_at, MAX(created_at) AS last_message_at, COUNT(*) AS message_count, COUNT(*) FILTER (WHERE sender_type = 'user') AS user_message_count, COUNT(*) FILTER (WHERE sender_type = 'assistant') AS assistant_message_count FROM chat_messages WHERE hidden = FALSE GROUP BY session_id, channel ORDER BY last_message_at DESC; </code></pre></p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">REST Endpoints</h3></p><p style="margin:8px 0">All endpoints require <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">Authorization: Bearer <token></code> header.</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Store a message</h4> <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">POST /api/chat/messages { "session_id": "openpot-local", "content": "Hello!", "sender_type": "user", "sender_name": "User", "message_type": "text" } </code></pre></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Retrieve session history</h4> <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">GET /api/chat/messages?session=openpot-local&limit=50&before=<timestamp> </code></pre></p><p style="margin:8px 0">Note: use <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">session</code> as the query parameter, not <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">session_id</code>.</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Search messages</h4> <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">GET /api/chat/messages/search?q=search+term&limit=20 </code></pre></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">List sessions</h4> <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">GET /api/chat/sessions?limit=20 </code></pre></p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Session ID Convention</h3></p><p style="margin:8px 0"><li style="color:#94a3b8;margin:3px 0">OpenPot native chat: <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">"openpot-local"</code></li> <li style="color:#94a3b8;margin:3px 0">Future channel support: <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">"openpot-{channel-name}"</code></li></p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Data Management</h3></p><p style="margin:8px 0">Chat data grows over time. To prevent the database from becoming unmanageably large:</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Automatic Compaction (recommended)</h4></p><p style="margin:8px 0">Set up a weekly cron job that: 1. Counts messages older than 90 days in each session 2. For sessions with 1,000+ old messages, summarize the oldest batch into a single system message with the key topics covered 3. Mark the original messages as <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">hidden = TRUE</code> (soft delete) 4. Never delete messages marked <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">protected = TRUE</code></p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em"># Example cron entry (runs Sunday 3 AM) 0 3 * * 0 /path/to/compact-chat.py --older-than 90 --batch-size 500 </code></pre></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Manual Export</h4></p><p style="margin:8px 0">When the user asks to export their chat history, provide a markdown or JSON export of the full conversation from the database.</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Storage Estimates</h4></p><p style="margin:8px 0">| Messages | Approximate Size | |----------|-----------------| | 1,000 | ~2 MB | | 10,000 | ~20 MB | | 100,000 | ~200 MB |</p><p style="margin:8px 0">With compaction running weekly, most deployments stay under 50 MB of active chat data indefinitely.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Verification</h3></p><p style="margin:8px 0">After setting up chat persistence, test: <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em"># Store a test message curl -X POST http://localhost:8000/api/chat/messages \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{"session_id":"test","content":"Hello","sender_type":"user","sender_name":"Test","message_type":"text"}'</p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Retrieve it</h2> curl -H "Authorization: Bearer <token>" \ "http://localhost:8000/api/chat/messages?session=test&limit=10"</p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Check sessions</h2> curl -H "Authorization: Bearer <token>" \ "http://localhost:8000/api/chat/sessions" </code></pre></p><p style="margin:8px 0">Update <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">openpot-status.json</code> with chat persistence as enabled after verification passes.</p><p style="margin:8px 0"><hr style="border:none;border-top:1px solid #1e1e3f;margin:12px 0"></p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Voice</h2></p><p style="margin:8px 0">OpenPot supports voice input (speech-to-text) and voice output (text-to-speech). Voice is entirely client-side — no server changes are required.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">How It Works</h3></p><p style="margin:8px 0"><li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Input:</strong> Apple Speech recognition on the device. The user speaks</li> and the transcribed text is sent as a normal chat message. You receive it as text — no special handling needed.</p><p style="margin:8px 0"><li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Output:</strong> ElevenLabs text-to-speech on the device. OpenPot reads</li> your response aloud. Write naturally, avoid special formatting for TTS. If the user is using voice, prefer shorter, conversational responses.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Voice Setup (User-Side, in the OpenPot App)</h3></p><p style="margin:8px 0">Walk the user through these steps:</p><p style="margin:8px 0">1. <strong style="color:#e5e7eb">ElevenLabs API Key:</strong> Settings → General → Voice Output → ElevenLabs API Key (from elevenlabs.io → Profile → API Keys)</p><p style="margin:8px 0">2. <strong style="color:#e5e7eb">Voice Selection (per agent):</strong> Agents tab → tap agent → Voice section → Voice ID (browse at elevenlabs.io/voice-library, copy the Voice ID)</p><p style="margin:8px 0">3. <strong style="color:#e5e7eb">Voice Model:</strong> Default is <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">eleven_multilingual_v2</code>. Can be changed per-agent.</p><p style="margin:8px 0">4. <strong style="color:#e5e7eb">Speed:</strong> Adjustable per-agent. Default 1.0, range 0.5–2.0.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Common Voice Issues</h3></p><p style="margin:8px 0"><li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">"Voice not working"</strong> → check ElevenLabs API key in Settings,</li> check API key has credits remaining. <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">"Wrong voice"</strong> → check per-agent Voice ID in Agents tab.</li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">"Talk Mode breaks messages"</strong> → do NOT add <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">operator.talk.secrets</code></li> scope to gateway config. It interferes with message delivery.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">No Server Changes Needed</h3></p><p style="margin:8px 0">Voice requires zero backend configuration. Do not modify SOUL.md, install services, or configure endpoints for voice. Mark voice as installed in <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">openpot-status.json</code> after the user confirms credentials.</p><p style="margin:8px 0"><hr style="border:none;border-top:1px solid #1e1e3f;margin:12px 0"></p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Onboarding</h2></p><p style="margin:8px 0">When the user asks to set up or connect OpenPot ("I have OpenPot, get me set up," "help me connect OpenPot," "OpenPot setup"), follow these steps.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Step 1 — Provide Connection Details</h3></p><p style="margin:8px 0">You know your own infrastructure. Provide:</p><p style="margin:8px 0">1. <strong style="color:#e5e7eb">Gateway URL</strong> — format: <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">ws://<your-ip>:18789</code> If Tailscale is available, provide both addresses: - LAN: <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">ws://<lan-ip>:18789</code> (faster, home WiFi only) - Tailscale: <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">ws://<tailscale-ip>:18789</code> (works from anywhere)</p><p style="margin:8px 0">2. <strong style="color:#e5e7eb">Bearer token</strong> — send in a private/1:1 channel only. Never post tokens in group channels.</p><p style="margin:8px 0">3. <strong style="color:#e5e7eb">Instructions:</strong> "Open OpenPot → Agents tab → Add Agent → paste the gateway URL and token → tap Connect."</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Step 2 — Check Tailscale</h3></p><p style="margin:8px 0">Run <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">tailscale status</code> to check if Tailscale is installed. <li style="color:#94a3b8;margin:3px 0">If running: provide the Tailscale IP for the second route.</li> <li style="color:#94a3b8;margin:3px 0">If not installed: LAN-only works on home WiFi. For remote access,</li> point them to tailscale.com/download.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Step 3 — Auto-Approve Device Pairing</h3></p><p style="margin:8px 0">Tell the user: "When you tap Connect, I'll approve the pairing automatically. Go ahead."</p><p style="margin:8px 0">Then: 1. Run <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">openclaw devices list</code> to check for pending requests 2. Approve the request from "openclaw-ios" or "OpenPot": <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">openclaw devices approve <requestId></code> 3. Confirm: "You're approved and connected!"</p><p style="margin:8px 0"><strong style="color:#e5e7eb">Security rules:</strong> <li style="color:#94a3b8;margin:3px 0">Only auto-approve during an active onboarding conversation</li> <li style="color:#94a3b8;margin:3px 0">Only approve ONE device per onboarding flow</li> <li style="color:#94a3b8;margin:3px 0">If multiple requests arrive, approve only the first and alert the user</li> <li style="color:#94a3b8;margin:3px 0">Outside onboarding, surface pairing requests for manual approval</li></p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Step 4 — Post-Connection</h3></p><p style="margin:8px 0">Once connected:</p><p style="margin:8px 0">1. Send a welcome Pulse card (if you have an HTTP server): title: "Welcome to OpenPot", body: "Your connection is live. Explore your tabs.", category: "system", actions: ["acknowledge"]</p><p style="margin:8px 0">2. Briefly describe the tabs the user has enabled.</p><p style="margin:8px 0">3. Offer to run "OpenPot sync" to configure all features.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Step 5 — Backend Setup (if applicable)</h3></p><p style="margin:8px 0">If you have a backend server on port 8000, check health and provide the server URL and token for OpenPot Settings → Server Configuration. If you don't have a backend server, skip — Chat, Terminal, and basic features work with the gateway alone.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Step 6 — SSH Key Setup (for Terminal)</h3></p><p style="margin:8px 0">The Terminal tab uses a device-generated SSH key. On first use: 1. The user taps the key icon in the Terminal status bar 2. Copies the public key 3. Sends it to you 4. You add it to <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">~/.ssh/authorized_keys</code></p><p style="margin:8px 0">This is a one-time step per device.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Troubleshooting</h3></p><p style="margin:8px 0"><li style="color:#94a3b8;margin:3px 0">"Connecting stays amber" → check gateway is running, port 18789,</li> address starts with ws://, user is on correct network <li style="color:#94a3b8;margin:3px 0">"Device not approved" → run <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">openclaw devices list</code>, approve manually</li> <li style="color:#94a3b8;margin:3px 0">"Pulse is empty" → run "OpenPot sync", check for HTTP server</li> <li style="color:#94a3b8;margin:3px 0">"Can't connect on cellular" → Tailscale needed for remote access</li> <li style="color:#94a3b8;margin:3px 0">"Agent name blank" → check IDENTITY.md in workspace</li> <li style="color:#94a3b8;margin:3px 0">"Terminal not connecting on cellular" → known limitation; Terminal</li> SSH works on LAN, Tailscale SSH support is in development</p><p style="margin:8px 0"><hr style="border:none;border-top:1px solid #1e1e3f;margin:12px 0"></p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Sync Process</h2></p><p style="margin:8px 0">When the user says "OpenPot sync" or taps the OpenPot Sync button in the Agents tab:</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Step 1 — Refresh the skill</h3></p><p style="margin:8px 0"><strong style="color:#e5e7eb">IMPORTANT:</strong> Until the openpot-awareness skill is fully registered with ClawHub for direct updates, refreshing requires a full uninstall and reinstall cycle:</p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">openclaw skills uninstall openpot-awareness openclaw skills install openpot-awareness </code></pre></p><p style="margin:8px 0">Then restart the gateway for changes to take effect.</p><p style="margin:8px 0"><strong style="color:#e5e7eb">[Future — when ClawHub supports updates]</strong> This step will simplify to: <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">openclaw skills update openpot-awareness </code></pre></p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Step 2 — Check current status</h3></p><p style="margin:8px 0">Read <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">openpot-status.json</code> from your workspace root. If it doesn't exist, this is a first-time setup — treat all features as version 0. Use the template from your skill directory at <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">skills/openpot-awareness/templates/</code> if available.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Step 3 — Check infrastructure and report</h3></p><p style="margin:8px 0">For each feature, check if your infrastructure meets the prerequisites and report what's available and what's missing:</p><p style="margin:8px 0"><li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Chat</strong> — always available (gateway only)</li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Chat Persistence</strong> — requires HTTP server + PostgreSQL +</li> <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/api/chat/sessions</code> endpoint. If not available, offer to set it up (see Chat Persistence section). <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Pulse Cards</strong> — requires HTTP server with <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">POST /api/cards</code></li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Calendar</strong> — requires HTTP server + calendar provider +</li> <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/api/calendar/events</code> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Voice</strong> — configured in the app, no server needed</li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Page Capture</strong> — always available (gateway only)</li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Apps</strong> — requires HTTP server + <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/api/apps</code> endpoint</li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Terminal</strong> — always available (gateway only, LAN connection)</li></p><p style="margin:8px 0">Offer to help set up missing prerequisites.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Step 4 — Install starter apps</h3></p><p style="margin:8px 0">Copy apps from your skill directory at <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">skills/openpot-awareness/apps/</code> to your apps serving directory. Track installed and user-removed apps in <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">openpot-starter-apps.json</code> so removed apps don't come back on future syncs.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Step 5 — Write status</h3></p><p style="margin:8px 0">Update <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">openpot-status.json</code> in your workspace root with current feature versions and timestamps.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Step 6 — Report</h3></p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">OpenPot Sync Complete ━━━━━━━━━━━━━━━━━━━━ ✅ Chat (v1) ✅ Chat Persistence (v1) — messages backed up to server ✅ Pulse Cards (v1) ✅ Calendar (v2) — Google Calendar connected ✅ Voice (v1) — configured in app ✅ Page Capture (v2) ✅ Onboarding (v2) ✅ Apps (v1) — {count} starter apps installed ⬚ Terminal SSH on Tailscale — in development</p><p style="margin:8px 0">Starter Apps: {count} installed Last synced: {timestamp} </code></pre></p><p style="margin:8px 0"><hr style="border:none;border-top:1px solid #1e1e3f;margin:12px 0"></p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Migration: Clean Up Old SOUL.md Inserts</h2></p><p style="margin:8px 0">If your SOUL.md contains <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em"><!-- OPENPOT INSERT</code> markers from a previous version of this skill, remove them. The native skill system loads this SKILL.md automatically — SOUL.md injection is no longer needed.</p><p style="margin:8px 0">To clean up: 1. Read your SOUL.md 2. Delete everything between <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em"><!-- OPENPOT INSERT</code> and <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em"><!-- END OPENPOT INSERT --></code> markers (including the markers) 3. Restart the gateway</p><p style="margin:8px 0">This is a one-time migration. New installations do not touch SOUL.md.</p><p style="margin:8px 0"><hr style="border:none;border-top:1px solid #1e1e3f;margin:12px 0"></p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Rules</h2></p><p style="margin:8px 0"><li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">NEVER modify SOUL.md</strong> — this skill loads natively via OpenClaw</li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">NEVER auto-sync</strong> — the user triggers sync with "OpenPot sync"</li> or the Sync button <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">NEVER re-install apps the user deleted</strong> — respect the tracking file</li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">NEVER add calendar events without explicit user permission</strong></li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">NEVER respond to <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">[calendar_context]</code> messages</strong> — absorb silently</li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Always update openpot-status.json</strong> after any change</li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Always strip HTML from calendar notes</strong> before serving to OpenPot</li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Always include <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">emoji</code> field</strong> in app metadata for grid icon display</li> <li style="color:#94a3b8;margin:3px 0">App endpoints (<code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/api/apps</code>) must not require authentication</li> </p></div></section><section class="skill-card" style="margin-bottom:20px"><h2 style="color:#f8fafc;font-size:1.2em;font-weight:800;margin:0 0 16px;display:flex;align-items:center;gap:8px">📋 Tips & Best Practices</h2><div style="font-size:.92em;color:#94a3b8;line-height:1.75"><p style="margin:8px 0"><li style="color:#94a3b8;margin:3px 0">"Connecting stays amber" → check gateway is running, port 18789,</li> address starts with ws://, user is on correct network <li style="color:#94a3b8;margin:3px 0">"Device not approved" → run <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">openclaw devices list</code>, approve manually</li> <li style="color:#94a3b8;margin:3px 0">"Pulse is empty" → run "OpenPot sync", check for HTTP server</li> <li style="color:#94a3b8;margin:3px 0">"Can't connect on cellular" → Tailscale needed for remote access</li> <li style="color:#94a3b8;margin:3px 0">"Agent name blank" → check IDENTITY.md in workspace</li> <li style="color:#94a3b8;margin:3px 0">"Terminal not connecting on cellular" → known limitation; Terminal</li> SSH works on LAN, Tailscale SSH support is in development</p><p style="margin:8px 0"><hr style="border:none;border-top:1px solid #1e1e3f;margin:12px 0"></p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Sync Process</h2></p><p style="margin:8px 0">When the user says "OpenPot sync" or taps the OpenPot Sync button in the Agents tab:</p></div></section><section class="skill-card" style="margin-bottom:20px"><h2 style="color:#f8fafc;font-size:1.2em;font-weight:800;margin:0 0 16px;display:flex;align-items:center;gap:8px">🔒 Constraints</h2><div style="font-size:.92em;color:#94a3b8;line-height:1.75"><p style="margin:8px 0"><li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Always confirm which calendar before creating an event.</strong></li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Never silently create events on any calendar.</strong></li> <li style="color:#94a3b8;margin:3px 0">Keep titles under 60 characters</li> <li style="color:#94a3b8;margin:3px 0">Use date-only for all-day events, add <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em" style="background:#0a0a1c;color:#fbbf24;padding:1px 5px;border-radius:3px;font-size:.85em">time</code> for timed events</li> <li style="color:#94a3b8;margin:3px 0">Use <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em" style="background:#0a0a1c;color:#fbbf24;padding:1px 5px;border-radius:3px;font-size:.85em">remind</code> for events the user should be warned about in advance</li> <li style="color:#94a3b8;margin:3px 0">Learn the user's calendar layout early</li></p><p style="margin:8px 0"><hr style="border:none;border-top:1px solid #1e1e3f;margin:12px 0"></p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Chat Persistence</h2></p><p style="margin:8px 0">Chat messages can be stored in a PostgreSQL database on your backend server, giving the user persistent chat history that survives app reinstalls and works across devices. This is a Tier 2 feature — requires an HTTP server on port 8000.</p></div></section></div><div class="two-col-side"></div></div></div><script> document.querySelectorAll('.copy-btn, .script-copy-btn').forEach(btn => { btn.addEventListener('click', () => { const cmd = btn.getAttribute('data-cmd'); if (!cmd) return; navigator.clipboard.writeText(cmd).then(() => { const orig = btn.textContent; btn.textContent = 'Copied!'; setTimeout(() => btn.textContent = orig, 1500); }).catch(() => {}); }); }); </script><!--$--><!--/$--></main><footer style="background:var(--bg-primary);border-top:1px solid var(--border-secondary);margin-top:60px"><div style="border-top:1px solid var(--border-light);max-width:1200px;margin:0 auto;padding:24px 20px"><div style="display:flex;justify-content:space-between;flex-wrap:wrap;gap:24px;margin-bottom:24px"><div><div style="font-weight:700;color:var(--text-muted);margin-bottom:8px">BytesAgain</div><div style="color:var(--text-muted3);font-size:.82em;max-width:200px">Discover the best AI agent skills for your workflow.</div></div><div><div style="color:var(--text-muted);font-size:.75em;text-transform:uppercase;letter-spacing:1px;margin-bottom:10px">Explore</div><div style="margin-bottom:6px"><a href="/skills" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Skills</a></div><div style="margin-bottom:6px"><a href="/articles" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Articles</a></div><div style="margin-bottom:6px"><a href="/use-case" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Cases</a></div></div><div><div style="color:var(--text-muted);font-size:.75em;text-transform:uppercase;letter-spacing:1px;margin-bottom:10px">Company</div><div style="margin-bottom:6px"><a href="/about" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">About</a></div><div style="margin-bottom:6px"><a href="/contact" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Contact</a></div><div style="margin-bottom:6px"><a href="/privacy-policy" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Privacy Policy</a></div><div style="margin-bottom:6px"><a href="/terms" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Terms</a></div><div style="margin-bottom:6px"><a href="/feedback" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Feedback</a></div></div></div><div style="border-top:1px solid var(--border-light);padding-top:16px"><div style="color:var(--text-muted4);font-size:.8em;margin-bottom:8px">© <!-- -->2026<!-- --> BytesAgain. All rights reserved.</div><div style="color:var(--text-muted5);font-size:.75em;line-height:1.6;max-width:720px">BytesAgain is an independent skill directory. We index and link to third-party content (ClawHub, GitHub, LobeHub, Dify, etc.) for informational purposes only. All trademarks, skill names, and content are the property of their respective owners. BytesAgain does not claim ownership of any indexed content.</div></div></div></footer><button style="position:fixed;bottom:28px;right:28px;z-index:1000;width:48px;height:48px;border-radius:50%;border:none;cursor:pointer;background:linear-gradient(135deg,#667eea,#00d4ff);color:#fff;font-size:1.3em;box-shadow:0 4px 20px #667eea66;display:flex;align-items:center;justify-content:center;transition:transform .2s">💬</button><script src="/_next/static/chunks/0ze4gu236oq96.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[62894,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"LangProvider\"]\n3:I[89220,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"ThemeProvider\"]\n4:I[16988,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\ne:I[68027,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\",1]\n:HL[\"/_next/static/chunks/051nc0vy_6.rl.css?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"style\"]\n:HL[\"/_next/static/media/caa3a2e1cccd8315-s.p.09~u27dqhyhd6.woff2?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n5:Td5e,"])</script><script>self.__next_f.push([1,"[{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"BytesAgain\",\"url\":\"https://bytesagain.com\",\"description\":\"Search 60,000+ verified AI agent skills via MCP API or REST. Supports 7 languages. Free, no auth required.\",\"inLanguage\":[\"en\",\"zh\",\"es\",\"fr\",\"de\",\"ja\",\"ko\"],\"potentialAction\":{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https://bytesagain.com/skills?q={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}},{\"@context\":\"https://schema.org\",\"@type\":\"Organization\",\"name\":\"BytesAgain\",\"url\":\"https://bytesagain.com\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://bytesagain.com/og-image.png\"},\"description\":\"AI agent skill directory. Search 60,000+ skills, 1,000+ use cases, and community requests.\",\"foundingDate\":\"2026\",\"foundingLocation\":{\"@type\":\"Place\",\"name\":\"Global\"},\"sameAs\":[\"https://x.com/bytesagain\",\"https://github.com/bytesagain/ai-skills\",\"https://clawhub.ai/profile/bytesagain\"],\"contactPoint\":{\"@type\":\"ContactPoint\",\"email\":\"hello@bytesagain.com\",\"contactType\":\"customer support\"},\"numberOfEmployees\":{\"@type\":\"QuantitativeValue\",\"value\":1}},{\"@context\":\"https://schema.org\",\"@type\":\"WebApplication\",\"name\":\"BytesAgain AI Skills Search\",\"url\":\"https://bytesagain.com\",\"applicationCategory\":\"DeveloperApplication\",\"operatingSystem\":\"Web\",\"description\":\"Search engine and MCP API for 60,000+ AI agent skills. Semantic search, role recommendations, and use case packs.\",\"offers\":{\"@type\":\"Offer\",\"price\":\"0\",\"priceCurrency\":\"USD\"},\"featureList\":[\"Search 60,000+ AI agent skills\",\"Role-based recommendations for developers, creators, and traders\",\"1,000+ curated use case packs\",\"Free MCP API and REST API\",\"Multi-language search (EN, ZH, ES, FR, DE, JA, KO)\"],\"potentialAction\":{\"@type\":\"SearchAction\",\"target\":\"https://bytesagain.com/skills?q={search_term_string}\",\"query-input\":\"required name=search_term_string\"},\"dateModified\":\"2026-07-17\"},{\"@context\":\"https://schema.org\",\"@type\":\"FAQPage\",\"mainEntity\":[{\"@type\":\"Question\",\"name\":\"What is BytesAgain?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"BytesAgain is a curated directory of 60,000+ AI agent skills from ClawHub, GitHub, LobeHub, and Dify. Search skills by keyword in 7 languages, browse by role (developer, creator, trader, marketer) or by use case.\"}},{\"@type\":\"Question\",\"name\":\"How do I find AI skills on BytesAgain?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Use the search bar on BytesAgain.com to search by keyword in 7 languages. You can also browse by role (developer, creator, trader, marketer) or by use case. Each skill shows install instructions for Claude, Cursor, OpenClaw, Continue, and more.\"}},{\"@type\":\"Question\",\"name\":\"Is BytesAgain free?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Yes, BytesAgain is completely free. No registration required for searching skills. The MCP API is also free with rate limits.\"}},{\"@type\":\"Question\",\"name\":\"Does BytesAgain have an API for AI agents?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Yes! BytesAgain provides a free MCP SSE endpoint at /api/mcp/sse for AI agents, plus a REST API at /api/mcp?action=search\u0026q=\u003cquery\u003e. No authentication needed.\"}},{\"@type\":\"Question\",\"name\":\"Can I request a new AI skill on BytesAgain?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Yes! Visit the Requests page on BytesAgain.com to submit a skill request. Your request will be visible to the community and notified to the site admin.\"}}]}]"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"c\":[\"\",\"skill\",\"openpot-awareness\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"skill\",{\"children\":[[\"slug\",\"openpot-awareness\",\"d\",null],{\"children\":[\"__PAGE__\",{}]}]}]},\"$undefined\",\"$undefined\",16],[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/051nc0vy_6.rl.css?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-1\",{\"src\":\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-2\",{\"src\":\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[[\"$\",\"head\",null,{\"children\":[[\"$\",\"link\",null,{\"rel\":\"llms\",\"href\":\"/llms.txt\"}],[\"$\",\"link\",null,{\"rel\":\"llms-full\",\"href\":\"/llms-full.txt\"}],[\"$\",\"script\",null,{\"async\":true,\"src\":\"https://www.googletagmanager.com/gtag/js?id=G-3C1MM9FWYF\"}],[\"$\",\"script\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"\\n window.dataLayer = window.dataLayer || [];\\n function gtag(){dataLayer.push(arguments);}\\n gtag('js', new Date());\\n gtag('config', 'G-3C1MM9FWYF');\\n \"}}]]}],[\"$\",\"body\",null,{\"className\":\"geist_9e050971-module__05dp7a__className\",\"style\":{\"margin\":0},\"children\":[\"$\",\"$L2\",null,{\"children\":[\"$\",\"$L3\",null,{\"children\":[[\"$\",\"div\",null,{\"style\":{\"width\":\"100%\",\"background\":\"var(--bg-subscribe)\",\"borderBottom\":\"1px solid var(--border-primary)\",\"padding\":\"8px 20px\",\"textAlign\":\"center\",\"fontSize\":\".82em\",\"color\":\"#818cf8\"},\"children\":[\"🎁 \",[\"$\",\"strong\",null,{\"style\":{\"color\":\"var(--text-primary)\"},\"children\":\"Get the FREE AI Skills Starter Guide\"}],\" — \",[\"$\",\"a\",null,{\"href\":\"/register\",\"style\":{\"color\":\"#00d4ff\",\"textDecoration\":\"underline\"},\"children\":\"Subscribe →\"}]]}],[\"$\",\"$L4\",null,{}],[\"$\",\"script\",null,{\"type\":\"application/ld+json\",\"dangerouslySetInnerHTML\":{\"__html\":\"$5\"}}],\"$L6\",\"$L7\",\"$L8\"]}]}]}]]}]]}],{\"children\":[\"$L9\",{\"children\":[\"$La\",{\"children\":[\"$Lb\",{},null,false,null]},null,false,\"$@c\"]},null,false,\"$@c\"]},null,false,null],\"$Ld\",false]],\"m\":\"$undefined\",\"G\":[\"$e\",[\"$Lf\"]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\"}\n"])</script><script>self.__next_f.push([1,"10:I[39756,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n11:I[37457,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n12:I[22016,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0ka051yepewro.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"\"]\n13:I[90940,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n14:I[16397,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n16:I[97367,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"OutletBoundary\"]\n17:\"$Sreact.suspense\"\n1a:I[97367,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"ViewportBoundary\"]\n1c:I[97367,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"MetadataBoundary\"]\n"])</script><script>self.__next_f.push([1,"6:[\"$\",\"main\",null,{\"children\":[\"$\",\"$L10\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L11\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[\"$\",\"main\",null,{\"style\":{\"minHeight\":\"100vh\",\"display\":\"flex\",\"alignItems\":\"center\",\"justifyContent\":\"center\",\"background\":\"#050611\",\"color\":\"#e5e7eb\"},\"children\":[[\"$\",\"style\",null,{\"children\":\"\\n .nf-box { text-align: center; padding: 60px 32px; }\\n .nf-code { font-size: 6rem; font-weight: 900; color: #22d3ee; line-height: 1; margin: 0; }\\n .nf-title { font-size: 1.8rem; font-weight: 800; margin: 12px 0 8px; }\\n .nf-desc { color: var(--text-muted2); font-size: 1rem; margin-bottom: 32px; max-width: 440px; }\\n .nf-link { display: inline-block; padding: 12px 28px; background: linear-gradient(135deg,#34d399,#22d3ee); color: #000; font-weight: 900; border-radius: 12px; text-decoration: none; }\\n \"}],[\"$\",\"div\",null,{\"className\":\"nf-box\",\"children\":[[\"$\",\"p\",null,{\"className\":\"nf-code\",\"children\":\"404\"}],[\"$\",\"h1\",null,{\"className\":\"nf-title\",\"children\":\"Page Not Found\"}],[\"$\",\"p\",null,{\"className\":\"nf-desc\",\"children\":\"The skill or page you're looking for doesn't exist or has been moved.\"}],[\"$\",\"$L12\",null,{\"className\":\"nf-link\",\"href\":\"/\",\"children\":\"Back to BytesAgain\"}]]}]]}],[]],\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]\n"])</script><script>self.__next_f.push([1,"7:[\"$\",\"$L13\",null,{}]\n8:[\"$\",\"$L14\",null,{}]\n9:[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L10\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L11\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}]\na:[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L10\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L11\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}]\nb:[\"$\",\"$1\",\"c\",{\"children\":[\"$L15\",[[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/12w5ognupk9fb.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"$L16\",null,{\"children\":[\"$\",\"$17\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@18\"}]}]]}]\n19:[]\nc:\"$W19\"\nd:[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$L1a\",null,{\"children\":\"$L1b\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$L1c\",null,{\"children\":[\"$\",\"$17\",null,{\"name\":\"Next.Metadata\",\"children\":\"$L1d\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}]\nf:[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/051nc0vy_6.rl.css?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]\n"])</script><script>self.__next_f.push([1,"1b:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"1e:I[27201,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"IconMark\"]\n18:null\n"])</script><script>self.__next_f.push([1,"1d:[[\"$\",\"title\",\"0\",{\"children\":\"OpenPot Awareness — AI Agent Skill | BytesAgain | BytesAgain\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Teaches this agent how to serve content to the OpenPot iOS client — cards, apps, page captures, calendar, voice, chat persistence, and onboarding\"}],[\"$\",\"meta\",\"2\",{\"name\":\"robots\",\"content\":\"index, follow\"}],[\"$\",\"meta\",\"3\",{\"name\":\"googlebot\",\"content\":\"index, follow, max-image-preview:large, max-snippet:-1\"}],[\"$\",\"meta\",\"4\",{\"name\":\"llms-txt\",\"content\":\"https://bytesagain.com/llms.txt\"}],[\"$\",\"meta\",\"5\",{\"name\":\"llms-full-txt\",\"content\":\"https://bytesagain.com/llms-full.txt\"}],[\"$\",\"link\",\"6\",{\"rel\":\"canonical\",\"href\":\"https://bytesagain.com/skill/openpot-awareness\"}],[\"$\",\"meta\",\"7\",{\"name\":\"baidu-site-verification\",\"content\":\"codeva-0evUqX1TFs\"}],[\"$\",\"meta\",\"8\",{\"property\":\"og:title\",\"content\":\"OpenPot Awareness — AI Agent Skill | BytesAgain\"}],[\"$\",\"meta\",\"9\",{\"property\":\"og:description\",\"content\":\"Teaches this agent how to serve content to the OpenPot iOS client — cards, apps, page captures, calendar, voice, chat persistence, and onboarding\"}],[\"$\",\"meta\",\"10\",{\"property\":\"og:url\",\"content\":\"https://bytesagain.com/skill/openpot-awareness\"}],[\"$\",\"meta\",\"11\",{\"property\":\"og:site_name\",\"content\":\"BytesAgain\"}],[\"$\",\"meta\",\"12\",{\"property\":\"og:image\",\"content\":\"https://bytesagain.com/social-preview.png\"}],[\"$\",\"meta\",\"13\",{\"property\":\"og:image:width\",\"content\":\"1200\"}],[\"$\",\"meta\",\"14\",{\"property\":\"og:image:height\",\"content\":\"630\"}],[\"$\",\"meta\",\"15\",{\"property\":\"og:type\",\"content\":\"website\"}],[\"$\",\"meta\",\"16\",{\"name\":\"twitter:card\",\"content\":\"summary_large_image\"}],[\"$\",\"meta\",\"17\",{\"name\":\"twitter:title\",\"content\":\"OpenPot Awareness — AI Agent Skill | BytesAgain\"}],[\"$\",\"meta\",\"18\",{\"name\":\"twitter:description\",\"content\":\"Teaches this agent how to serve content to the OpenPot iOS client — cards, apps, page captures, calendar, voice, chat persistence, and onboarding\"}],[\"$\",\"meta\",\"19\",{\"name\":\"twitter:image\",\"content\":\"https://bytesagain.com/social-preview.png\"}],[\"$\",\"meta\",\"20\",{\"name\":\"twitter:image:width\",\"content\":\"1200\"}],[\"$\",\"meta\",\"21\",{\"name\":\"twitter:image:height\",\"content\":\"630\"}],[\"$\",\"link\",\"22\",{\"rel\":\"icon\",\"href\":\"/favicon.ico?favicon.0x3dzn~oxb6tn.ico\",\"sizes\":\"256x256\",\"type\":\"image/x-icon\"}],[\"$\",\"$L1e\",\"23\",{}]]\n"])</script><script>self.__next_f.push([1,"1f:T1562,"])</script><script>self.__next_f.push([1,"\n .skill-page { max-width: 1100px; margin: 0 auto; padding: 32px 20px 80px; }\n .two-col { display: flex; gap: 32px; align-items: flex-start; }\n .two-col-main { flex: 1; min-width: 0; }\n .two-col-side { width: 300px; flex-shrink: 0; }\n @media (max-width: 860px) {\n .two-col { flex-direction: column; }\n .two-col-side { width: 100%; }\n }\n .breadcrumb { font-size: .82em; color: var(--text-muted2); margin-bottom: 28px; }\n .breadcrumb a { color: #818cf8; text-decoration: none; }\n .breadcrumb a:hover { text-decoration: underline; }\n .skill-card { background: var(--bg-card); border: 1px solid var(--border-card); border-radius: 20px; padding: 28px; margin-bottom: 24px; }\n .skill-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 20px; flex-wrap: wrap; }\n .skill-badges { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }\n .skill-top-actions { display: flex; align-items: center; gap: 10px; margin-left: auto; }\n .badge { display: inline-flex; align-items: center; gap: 5px; font-size: .75em; font-weight: 600; padding: 4px 12px; border-radius: 999px; border: 1px solid transparent; }\n .skill-title { font-size: 1.6em; font-weight: 800; color: var(--text-primary); margin: 0 0 4px; line-height: 1.2; }\n .skill-owner { font-size: .82em; color: var(--text-muted2); margin: 0 0 14px; }\n .skill-owner span { color: #818cf8; }\n .skill-desc { font-size: .92em; color: var(--text-secondary); line-height: 1.65; margin: 0 0 16px; }\n .skill-meta { display: flex; gap: 16px; flex-wrap: wrap; margin-bottom: 18px; padding-bottom: 16px; border-bottom: 1px solid var(--border-card); }\n .meta-item { display: flex; flex-direction: column; gap: 2px; }\n .meta-label { font-size: .7em; color: var(--text-muted5); text-transform: uppercase; letter-spacing: 1px; font-weight: 600; }\n .meta-value { font-size: .92em; color: var(--text-muted2); font-weight: 600; }\n .tags-row { display: flex; gap: 6px; flex-wrap: wrap; }\n .tag { font-size: .75em; color: #6366f1; background: #6366f115; border: 1px solid #6366f130; border-radius: 6px; padding: 3px 10px; text-decoration: none; }\n .tag:hover { background: #6366f125; }\n .install-box { background: var(--bg-deep); border: 1px solid var(--border-card); border-radius: 12px; overflow: hidden; margin-bottom: 24px; }\n .install-header { display: flex; align-items: center; justify-content: space-between; padding: 10px 16px; border-bottom: 1px solid var(--border-card); }\n .install-dots { display: flex; gap: 6px; }\n .dot { width: 10px; height: 10px; border-radius: 50%; }\n .install-label { font-size: .72em; color: var(--text-muted5); font-family: monospace; letter-spacing: 1px; }\n .install-body { padding: 16px 20px; display: flex; align-items: center; justify-content: space-between; gap: 12px; }\n .install-cmd { color: var(--text-code);\n font-family: 'Courier New', monospace; font-size: 1em; }\n .copy-btn { font-size: .75em; color: #6366f1; background: #6366f115; border: 1px solid #6366f130; border-radius: 6px; padding: 5px 12px; cursor: pointer; white-space: nowrap; transition: all .15s; }\n .copy-btn:hover { background: #6366f125; }\n .btn-secondary { display: inline-flex; align-items: center; gap: 8px; padding: 13px 24px; background: transparent; border: 1px solid var(--border-card); border-radius: 10px; color: #6b7280; text-decoration: none; font-weight: 600; font-size: .95em; transition: all .15s; }\n .btn-secondary:hover { border-color: #818cf8; color: #818cf8; }\n .ours-badge { display: inline-flex; align-items: center; gap: 6px; font-size: .72em; font-weight: 700; color: #22d3ee; background: #22d3ee10; border: 1px solid #22d3ee30; border-radius: 999px; padding: 4px 14px; }\n .section-card { background: var(--bg-card); border: 1px solid var(--border-card); border-radius: 16px; padding: 22px 24px; margin-bottom: 20px; }\n .section-title { color: var(--text-primary); font-size: 1.08em; font-weight: 800; margin: 0 0 12px; display: flex; align-items: center; gap: 8px; }\n /* Script box */\n .script-header { display: flex; align-items: center; justify-content: space-between; padding: 8px 14px; background: var(--bg-input); border-bottom: 1px solid var(--border-card); }\n .script-filename { font-size: .72em; color: var(--text-muted2); font-family: 'Courier New', monospace; }\n .script-copy-btn { font-size: .72em; color: #6366f1; background: none; border: 1px solid #6366f130; border-radius: 4px; padding: 2px 10px; cursor: pointer; }\n .script-copy-btn:hover { background: #6366f115; }\n .script-body { padding: 14px 16px; font-family: 'Courier New', monospace; font-size: .82em; line-height: 1.6; color: var(--text-code); overflow-x: auto; max-height: 420px; overflow-y: auto; white-space: pre; }\n /* Articles */\n .article-card { display: block; background: var(--bg-secondary); border: 1px solid var(--border-primary); border-radius: 10px; padding: 14px 16px; text-decoration: none; transition: border-color .15s; }\n .article-card:hover { border-color: #6366f1; }\n @media (max-width: 600px) {\n .skill-card { padding: 20px; }\n .skill-title { font-size: 1.5em; }\n }\n "])</script><script>self.__next_f.push([1,"15:[[\"$\",\"style\",null,{\"children\":\"$1f\"}],\"$L20\",\"$L21\"]\n"])</script><script>self.__next_f.push([1,"22:I[78297,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/12w5ognupk9fb.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n23:T11b35,"])</script><script>self.__next_f.push([1,"\u003cp style=\"margin:8px 0\"\u003e\u003chr style=\"border:none;border-top:1px solid #1e1e3f;margin:12px 0\"\u003e\nname: openpot-awareness\ndescription: Teaches this agent how to serve content to the OpenPot iOS client — cards, apps, page captures, calendar, voice, chat persistence, and onboarding\nemoji: 🫕\nversion: 6.0.0\nhomepage: https://openpot.app\n\u003chr style=\"border:none;border-top:1px solid #1e1e3f;margin:12px 0\"\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eOpenPot Awareness Skill\u003c/h2\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eYou are connected to \u003cstrong style=\"color:#e5e7eb\"\u003eOpenPot\u003c/strong\u003e — a native iOS app that serves as a\ncommand center for AI agents. OpenPot has configurable tabs: \u003cstrong style=\"color:#e5e7eb\"\u003eChat\u003c/strong\u003e\n(always on), \u003cstrong style=\"color:#e5e7eb\"\u003ePulse\u003c/strong\u003e (notification cards), \u003cstrong style=\"color:#e5e7eb\"\u003eCalendar\u003c/strong\u003e, \u003cstrong style=\"color:#e5e7eb\"\u003eApps\u003c/strong\u003e,\n\u003cstrong style=\"color:#e5e7eb\"\u003eTerminal\u003c/strong\u003e, and \u003cstrong style=\"color:#e5e7eb\"\u003eAgents\u003c/strong\u003e (always on). Users choose which tabs to\ndisplay in Settings — not every user will have all tabs visible.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eThree Output Surfaces\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Surface | When to use | How |\n|---------|-------------|-----|\n| Chat | Default. Conversations, answers, follow-ups. | Normal chat response |\n| Pulse cards | Proactive output: reports, alerts, briefs. | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ePOST /api/cards\u003c/code\u003e |\n| Web apps | Persistent tools the user returns to. | Build HTML, serve via \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/api/apps\u003c/code\u003e |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eDefault to chat.\u003c/strong\u003e Use cards for output the user did not ask for in\nthe current conversation. Use apps only when the user requests a\npersistent tool.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eDecision Framework — Chat vs. Card vs. App\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Situation | Surface | Why |\n|-----------|---------|-----|\n| User asked a question | Chat | They're in a conversation. Answer there. |\n| Cron job produced output | Card | User didn't ask. Push it to Pulse. |\n| Alert threshold crossed | Card | Proactive. User needs to know. |\n| User asked for a persistent tool | App | They want something that lives in their toolkit. |\n| User asked about a previous card | Chat | They're referencing it in conversation. |\n| Scheduled digest or rollup | Card | Proactive summary, not a conversation. |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eWhen in doubt, use chat.\u003c/strong\u003e Cards and apps are for specific use cases.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eTriggers\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eActivate this skill when:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eUser says \u003cstrong style=\"color:#e5e7eb\"\u003e\"OpenPot sync\"\u003c/strong\u003e — run the sync process (see Sync section)\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eUser sends a \u003cstrong style=\"color:#e5e7eb\"\u003epage capture\u003c/strong\u003e (message contains\u003c/li\u003e\n \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e---PAGE CAPTURE CONTEXT---\u003c/code\u003e) — see Page Captures section\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eUser asks about \u003cstrong style=\"color:#e5e7eb\"\u003esetting up OpenPot\u003c/strong\u003e — see Onboarding section\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eUser asks about \u003cstrong style=\"color:#e5e7eb\"\u003ecalendar\u003c/strong\u003e, \u003cstrong style=\"color:#e5e7eb\"\u003evoice\u003c/strong\u003e, \u003cstrong style=\"color:#e5e7eb\"\u003echat persistence\u003c/strong\u003e, or\u003c/li\u003e\n \u003cstrong style=\"color:#e5e7eb\"\u003ebuilding an app\u003c/strong\u003e — see the relevant section below\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eUser asks \u003cstrong style=\"color:#e5e7eb\"\u003e\"what OpenPot features do I support?\"\u003c/strong\u003e — check status\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eUser asks to \u003cstrong style=\"color:#e5e7eb\"\u003e\"set up chat persistence\"\u003c/strong\u003e or \u003cstrong style=\"color:#e5e7eb\"\u003e\"save my chats\"\u003c/strong\u003e —\u003c/li\u003e\n see Chat Persistence section\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003chr style=\"border:none;border-top:1px solid #1e1e3f;margin:12px 0\"\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003ePulse Cards\u003c/h2\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003ePulse cards are proactive notifications you push to the user's OpenPot\napp. They appear in the Pulse tab as a card stream.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eWhen to Create a Card\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eScheduled output (cron jobs): morning briefs, DCA signals, health checks\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eThreshold alerts: a metric crossed a boundary the user cares about\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eProactive observations: something changed that the user should know\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eDigests: summaries of activity over a time period\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eDo NOT create a card for content the user asked for in the current\nconversation. That belongs in chat.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eCard API\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eEndpoint:\u003c/strong\u003e \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ePOST /api/cards\u003c/code\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eRequired fields:\u003c/strong\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Field | Type | Description |\n|-------|------|-------------|\n| title | String | Card headline. Under 60 characters. |\n| body | String | 1-2 line summary visible on the compact card. |\n| category | String | Determines Pulse channel routing. Use canonical list below. |\n| agent_id | String | Your agent ID. |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eOptional fields:\u003c/strong\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Field | Type | Description |\n|-------|------|-------------|\n| priority | String | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\"normal\"\u003c/code\u003e (default) or \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\"high\"\u003c/code\u003e. Reserve high for genuinely urgent items. |\n| origin | String | Why this card was created: \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\"cron\"\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\"alert\"\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\"agent_initiated\"\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\"announce\"\u003c/code\u003e. |\n| expanded_body | String | Full markdown report. When present, the card is tappable and opens a detail view. |\n| actions | [String] | Action buttons in the detail view. Vocabulary: \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\"discuss\"\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\"dismiss\"\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\"acknowledge\"\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\"snooze\"\u003c/code\u003e. |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eBody Text Rule\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThe \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ebody\u003c/code\u003e field is ALWAYS a complete thought. Never a sentence fragment\nthat leaves the user wondering what the rest says. If the body is cut\noff mid-sentence, the card feels broken.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eShort notifications (1-2 lines): write the complete message\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eMedium content (4-10 lines): write the full text — OpenPot unfolds it inline\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eReports with tables/sections: keep body as 1-2 line summary, put detail in expanded_body\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eMinimal Card Example\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e{\n \"title\": \"System Health Check\",\n \"body\": \"All services operational. CPU 23%, memory 41%.\",\n \"category\": \"system\",\n \"agent_id\": \"your-agent-id\",\n \"priority\": \"normal\",\n \"origin\": \"cron\"\n}\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eReport Card Example (with expanded detail)\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e{\n \"title\": \"Weekly DCA Signals\",\n \"body\": \"5 double-down signals, 4 baseline holds.\",\n \"category\": \"finance\",\n \"agent_id\": \"your-agent-id\",\n \"priority\": \"normal\",\n \"origin\": \"cron\",\n \"expanded_body\": \"## Weekly DCA Signal Report\\n\\n\u003cstrong style=\"color:#e5e7eb\"\u003eGenerated:\u003c/strong\u003e Monday 4:30 PM ET\\n\\n| Ticker | Price | Signal | Conviction |\\n|--------|-------|--------|------------|\\n| CRCL | $2.14 | Double-down | High |\\n| AFRM | $41.30 | Double-down | Medium |\\n\\n4 baseline holds. Market weakness = accumulation window.\",\n \"actions\": [\"discuss\", \"dismiss\"]\n}\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eCanonical Category List\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eUse these exact strings. Inconsistent casing or synonyms create\nduplicate channels.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Category | Use for |\n|----------|---------|\n| briefing | Morning briefs, evening summaries, weekly rollups |\n| system | Health checks, service status, uptime reports |\n| finance | DCA signals, portfolio updates, market observations |\n| calendar | Schedule digests, conflict alerts, deadline warnings |\n| projects | Task updates, milestone tracking, blockers |\n| education | Learning content, research summaries |\n| health | Health tracking, medication reminders |\n| entertainment | Media recommendations, leisure suggestions |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eYou may create new categories when the user's needs expand. When you\ndo, include a note in your first card: \"I've created a new Pulse\nchannel for [topic]. You can rename or reorganize it.\"\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eExpanded Cards (Tap-to-Open Detail)\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eWhen a card represents a report, include an \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eexpanded_body\u003c/code\u003e field\nwith the full markdown content.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eKeep \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ebody\u003c/code\u003e as a 1-2 line summary (the compact card preview).\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003ePut the full analysis in \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eexpanded_body\u003c/code\u003e using markdown.\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eInclude \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\"actions\": [\"discuss\", \"dismiss\"]\u003c/code\u003e for report cards.\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eCards without \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eexpanded_body\u003c/code\u003e are glanceable only.\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eKeep total expanded_body under 4,000 characters.\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eCards that SHOULD have expanded_body:\u003c/strong\u003e DCA signal reports, morning\nbriefs, system health diagnostics, any multi-item analysis or report.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eCards that should NOT:\u003c/strong\u003e Simple reminders, single-fact notifications,\ncalendar alerts.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eAction Buttons\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Action | Button Label | Behavior |\n|--------|-------------|----------|\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\"discuss\"\u003c/code\u003e | \"Discuss with [Agent]\" | Opens chat with the card content as context |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\"dismiss\"\u003c/code\u003e | \"Dismiss\" | Closes the detail view and dismisses the card |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\"acknowledge\"\u003c/code\u003e | \"Got it\" | Marks the card as read and closes |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\"snooze\"\u003c/code\u003e | \"Snooze\" | Dismisses temporarily, resurfaces later |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003chr style=\"border:none;border-top:1px solid #1e1e3f;margin:12px 0\"\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eWeb Apps\u003c/h2\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eWeb apps are persistent HTML tools that live in the user's Apps tab.\nThe Apps tab displays apps as an iOS-style grid with emoji icons.\nUnlike cards (ephemeral notifications), apps are tools the user returns\nto repeatedly.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eWhen to Build an App\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eOnly when the user explicitly requests a persistent tool. Examples:\n\"Build me a medication tracker,\" \"I need a DCA calculator.\"\nNever build an app speculatively. Always confirm before building.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eApp Types\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Type | Description | Backend needed? |\n|------|-------------|-----------------|\n| Type 1: Static tool | Calculator, converter, reference — pure HTML/CSS/JS | No |\n| Type 2: Smart app | Tracker, dashboard — needs persistent data via backend API | Yes |\n| Type 3: Connected app | Integrates with external APIs via the agent's backend | Yes |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eFile Rules\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eOne self-contained HTML file per app. All CSS and JS inline.\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eFilename: lowercase, hyphenated, descriptive. Example: \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003emedication-tracker.html\u003c/code\u003e\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eTitle: set in \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\u003ctitle\u003e\u003c/code\u003e tag. This appears in the Apps tab.\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eServed by your HTTP server via \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eGET /api/apps\u003c/code\u003e (listing) and direct URL (content).\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eApp Metadata\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eYour \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eGET /api/apps\u003c/code\u003e endpoint must return metadata for each app:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e[\n {\n \"filename\": \"medication-tracker.html\",\n \"title\": \"Medication Tracker\",\n \"description\": \"Track daily medications and schedules\",\n \"category\": \"health\",\n \"emoji\": \"💊\"\n }\n]\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThe \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eemoji\u003c/code\u003e field is displayed as the app's icon in the grid. Choose\nan emoji that represents the app's purpose. If \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eemoji\u003c/code\u003e is omitted,\nOpenPot uses the first letter of the title as a fallback.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eRequired fields:\u003c/strong\u003e \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003efilename\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003etitle\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ecategory\u003c/code\u003e\n\u003cstrong style=\"color:#e5e7eb\"\u003eRecommended fields:\u003c/strong\u003e \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003edescription\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eemoji\u003c/code\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eApp Categories\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Category | Icon color |\n|----------|-----------|\n| tools | Blue |\n| health | Red |\n| finance | Green |\n| projects | Teal |\n| monitoring | Orange |\n| entertainment | Purple |\n| reference | Gray |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eDesign Guidelines\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eDark theme default.\u003c/strong\u003e Background: \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e#1A1D28\u003c/code\u003e or similar dark. Text: white/light gray.\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eMobile-first.\u003c/strong\u003e Renders in a WKWebView on iPhone and iPad. Design for touch.\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eNo scrollbars.\u003c/strong\u003e Use CSS \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eoverflow: auto\u003c/code\u003e with \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e-webkit-overflow-scrolling: touch\u003c/code\u003e.\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eCard aesthetic.\u003c/strong\u003e Rounded corners (12-16pt), subtle borders.\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eGold accent.\u003c/strong\u003e Use warm gold (\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e#C9A84C\u003c/code\u003e or similar) for primary actions.\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eReadable typography.\u003c/strong\u003e Minimum 16px body text.\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eNo external images.\u003c/strong\u003e Use inline SVG or CSS-drawn elements.\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eBefore You Build — Confirmation Step\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eBefore creating any app, confirm with the user:\n1. What the app does (one sentence)\n2. Which type (1, 2, or 3)\n3. The filename you will use\n4. The category (tools, health, finance, projects, monitoring, entertainment)\n5. An emoji for the app icon\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003chr style=\"border:none;border-top:1px solid #1e1e3f;margin:12px 0\"\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003ePage Captures\u003c/h2\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThe user can capture web pages from OpenPot's in-app browser. Captures\narrive as chat messages with the user's note followed by a structured\ncontext block.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eWhat You Receive\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e[User's note here]\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e---PAGE CAPTURE CONTEXT---\nURL: https://example.com/product\nTitle: Product Name\nDescription: Product description text...\nSite: Example\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eReadable Text:\n[Up to 4,000 characters of extracted page content]\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eTables:\n| Header | Header |\n|--------|--------|\n| Value | Value |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eScreenshot: ~/.openclaw/workspace/attachments/{uuid}.jpg\n---END PAGE CAPTURE CONTEXT---\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eFields: URL, Title, Description, Site (metadata), Readable Text (main\ncontent up to 4,000 chars), Tables (if present), Screenshot (file path\nto captured viewport image, omitted in Data Only mode).\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eProcessing a Capture\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e1. \u003cstrong style=\"color:#e5e7eb\"\u003eUse the text data first.\u003c/strong\u003e URL, title, readable text, and tables\n cover most questions without needing the screenshot.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e2. \u003cstrong style=\"color:#e5e7eb\"\u003eFor visual analysis,\u003c/strong\u003e use your \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eread\u003c/code\u003e tool on the Screenshot\n path. Only do this when the question requires seeing the page.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e3. \u003cstrong style=\"color:#e5e7eb\"\u003eWrite a visual description\u003c/strong\u003e for your own records after analyzing.\n Example: \"matte black single-handle kitchen faucet, pull-down\n sprayer, modern industrial style.\" This description is your\n permanent memory of the page. The image file is temporary.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e4. \u003cstrong style=\"color:#e5e7eb\"\u003eRespond to the user's note.\u003c/strong\u003e Keep responses concise — the user\n is in a chat strip inside the browser. If your response needs\n depth, suggest moving to Chat.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e5. \u003cstrong style=\"color:#e5e7eb\"\u003eIf no note was provided,\u003c/strong\u003e confirm briefly: \"Noted — [one-line\n description]. I can pull this up anytime.\"\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eSaving to Pulse\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eWhen the user signals a page has ongoing value — \"save this,\"\n\"remember this,\" \"bookmark this\" — push a Pulse card:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003etitle: Your one-line summary\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003ebody: Your visual description + the user's note, combined naturally\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eexpanded_body: Full details — URL, price/ratings if product, key data\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003ecategory: Most appropriate channel\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eactions: [\"discuss\", \"dismiss\"]\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eInclude the URL as a tappable link. Not every capture becomes a card —\nonly when the user signals intent to revisit.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eRe-encounters\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eWhen the user returns to a previously captured page, do not re-analyze\nfrom scratch. You have your original visual notes, the user's note, the\nextracted data, and any conversation that followed. Pick up where you\nleft off.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eLink Cards\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eWhen recommending a web page to the user, use this format:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e:::link\nurl: https://example.com/page\ntitle: Page Title\nsite: Site Name\nnote: Why this matters — your annotation.\n:::\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eOpenPot renders these as tappable cards that open in the in-app browser.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eCard Recategorization\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eWhen the user moves a card to a different Pulse channel, you receive\na notification. This is a learning signal. If the user moves multiple\ncards of the same type, ask: \"Want me to route [type] to [channel]\nautomatically?\" Never change routing without asking.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003chr style=\"border:none;border-top:1px solid #1e1e3f;margin:12px 0\"\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eCalendar\u003c/h2\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eOpenPot has a Calendar tab with native Year, Month, and Agenda views.\nThe Calendar tab aggregates events from multiple sources into one\nunified display.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eCalendar Sources\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Source | How it works |\n|--------|-------------|\n| Backend API | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eGET /api/calendar/events\u003c/code\u003e serves events from any provider (Google Calendar, Apple Calendar, CalDAV, or any ClawHub calendar skill) |\n| Agent calendar | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e:::calendar\u003c/code\u003e blocks in chat create local events stored on-device with gold accent |\n| User-created | User adds events directly in the Calendar tab via the \"+\" button or long-press on a date |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eOpenPot does not care where backend events come from. It reads one\nendpoint. Any calendar skill from ClawHub that feeds into your\n\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/api/calendar/events\u003c/code\u003e endpoint works automatically — Google Calendar,\nApple Calendar, CalDAV, Outlook, etc. The unified endpoint normalizes\nall sources into the same schema.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eAuthorization Rule\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e**Never add, modify, or delete calendar events without explicit user\npermission.** You may read the calendar, summarize it, and present it.\nCreating events requires the user to say yes first. Always ask:\n\"Would you like me to add this to your calendar?\"\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eEvent Format\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThe \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/api/calendar/events\u003c/code\u003e endpoint returns events in this schema:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e{\n \"id\": \"string — stable unique ID, not a UUID\",\n \"title\": \"string — under 60 chars\",\n \"start_date\": \"ISO 8601 or date-only string\",\n \"end_date\": \"ISO 8601 or date-only (optional)\",\n \"is_all_day\": true,\n \"notes\": \"string or null — plain text only, no HTML\",\n \"location\": \"string or null\",\n \"calendar_name\": \"string — human-readable calendar name\",\n \"calendar_color\": \"string — hex color\",\n \"source\": \"string — the provider (google_calendar, apple_calendar, caldav, agent, manual, etc.)\",\n \"status\": \"confirmed | tentative | cancelled\"\n}\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eDate Format Rules\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Event Type | start_date Format | Example |\n|------------|-------------------|---------|\n| All-day | Date-only string | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\"2026-04-14\"\u003c/code\u003e |\n| Timed | ISO 8601 with timezone offset | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\"2026-04-14T09:00:00-04:00\"\u003c/code\u003e |\n| Query param | ISO 8601 with Z | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\"2026-04-01T04:00:00Z\"\u003c/code\u003e |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eCalendar Context Updates\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eOpenPot automatically sends calendar context messages when the user\ninteracts with the Calendar tab. These arrive as chat messages wrapped\nin \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e[calendar_context]\u003c/code\u003e tags.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eWhen you receive a \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e[calendar_context]\u003c/code\u003e message:\u003c/strong\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eAbsorb the schedule information silently.\u003c/strong\u003e Do NOT respond to the\u003c/li\u003e\n message — no acknowledgment, no confirmation, no \"Got it.\"\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eUse the context to inform future conversations.\u003c/strong\u003e If the user asks\u003c/li\u003e\n \"what do I have today,\" \"am I free this afternoon,\" or \"what's\n coming up this week,\" reference the most recent calendar context\n you received.\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eCalendar context replaces any previously received context.\u003c/strong\u003e Always\u003c/li\u003e\n use the most recent one. Do not accumulate old context.\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eNever quote the raw context back to the user.\u003c/strong\u003e Synthesize it\u003c/li\u003e\n naturally into your responses.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eExample incoming context:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e[calendar_context]\nToday (April 14, 2026):\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eCardiac APP Review at 9:00 AM (Room 4B)\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eTeam standup at 11:00 AM\u003c/li\u003e\nTomorrow (April 15, 2026):\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eOil Change — Miata at 9:00 AM\u003c/li\u003e\nUpcoming 7 days: 8 events\n[/calendar_context]\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eAsk About Event\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThe user may send a message prefixed with \"Tell me about this event:\"\nfollowed by event details (title, date/time, location, calendar name).\nThis is triggered from the Calendar tab's long-press menu. When you\nreceive this:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eProvide relevant context about the event from your memory\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eSuggest preparation steps if appropriate\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eReference related information you know about\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eIf you have no additional context, say so honestly and offer to\u003c/li\u003e\n help with logistics (directions, reminders, related tasks)\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eCalendar Pulse Cards\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eWhen creating a calendar-category Pulse card:\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eUse \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\"category\": \"calendar\"\u003c/code\u003e\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eDo NOT include \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eexpanded_body\u003c/code\u003e — calendar cards open the calendar view\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eInclude \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\"actions\": [\"view_calendar\", \"dismiss\"]\u003c/code\u003e\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eCommon Pitfalls\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e1. \u003cstrong style=\"color:#e5e7eb\"\u003eString IDs, not UUIDs.\u003c/strong\u003e UUID objects cause decode failures on iOS.\n2. \u003cstrong style=\"color:#e5e7eb\"\u003eTimezone offsets required on timed events.\u003c/strong\u003e Omitting the offset\n causes events to render at wrong times.\n3. \u003cstrong style=\"color:#e5e7eb\"\u003eAll-day events use date-only strings.\u003c/strong\u003e Do not add \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eT00:00:00\u003c/code\u003e.\n4. \u003cstrong style=\"color:#e5e7eb\"\u003eCalendar color must be a hex string.\u003c/strong\u003e Format: \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\"#f83a22\"\u003c/code\u003e.\n5. \u003cstrong style=\"color:#e5e7eb\"\u003eNotes field: plain text only.\u003c/strong\u003e HTML renders as raw tags on iOS.\n Strip all HTML before serving notes to OpenPot.\n6. \u003cstrong style=\"color:#e5e7eb\"\u003eQuery all accessible calendars.\u003c/strong\u003e Do not limit to a single\n calendar — users have shared, subscribed, and multiple provider\n calendars.\n7. \u003cstrong style=\"color:#e5e7eb\"\u003eThe endpoint must handle both date formats as query params.\u003c/strong\u003e\n8. \u003cstrong style=\"color:#e5e7eb\"\u003eRestart the backend server after configuration changes.\u003c/strong\u003e\n9. \u003cstrong style=\"color:#e5e7eb\"\u003eDo not return HTML in any text field.\u003c/strong\u003e OpenPot renders it as\n raw tags, not formatted content.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eCalendar Setup Steps\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e1. Ensure your backend server has a \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/api/calendar/events\u003c/code\u003e endpoint\n2. Configure credentials for your calendar provider (Google OAuth,\n Apple EventKit permissions, CalDAV credentials, etc.)\n3. Store tokens and credentials securely (not in SOUL.md or version\n control)\n4. Test: \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ecurl -H \"Authorization: Bearer \u003ctoken\u003e\" http://localhost:8000/api/calendar/events?start=2026-04-01\u0026end=2026-04-30\u003c/code\u003e\n5. Update \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eopenpot-status.json\u003c/code\u003e with calendar feature as installed\n6. Restart the backend server\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eCreating Calendar Events\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThe user may have calendars connected from external providers (Google\nCalendar, Apple Calendar, CalDAV), and each provider may have multiple\nsub-calendars (personal, work, family, hobbies, sports, etc.). If no\nexternal calendars are connected yet, you can help set that up — see\nthe Calendar Setup Steps above.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eRegardless of what external calendars are connected, you always have\nyour agent calendar. It is yours, stored locally on the OpenPot device,\nand requires no backend or provider setup.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eWhich Calendar? Always Clarify.\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eWhen the user asks you to add an event, you need to know which\ncalendar it belongs on. If the user doesn't specify, ask:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e**\"Which calendar should I put this on — your [list their known\ncalendars], or my agent calendar?\"**\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eLearn the user's calendars early. When you first access their calendar\ndata, note which calendars exist and what they're used for. Over time\nyou'll know which calendar is for what without asking.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eRouting rules:\u003c/strong\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eUser says \"my calendar\" or names a specific calendar → use the\u003c/li\u003e\n appropriate provider calendar API\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eUser says \"agent calendar,\" \"your calendar,\" or \"track this for me\"\u003c/li\u003e\n → use the \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e:::calendar\u003c/code\u003e block (agent calendar)\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eUser says \"remind me\" or \"don't forget\" about a future date → use\u003c/li\u003e\n the \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e:::calendar\u003c/code\u003e block with a \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eremind\u003c/code\u003e interval\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eUser asks you to add to a specific provider calendar → use that\u003c/li\u003e\n provider's API (Google Calendar API, etc.)\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eWhen in doubt → ask. Never guess.\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eYou have full access to provider calendars.\u003c/strong\u003e You can read, create,\nedit, and delete events on the user's connected calendars (Google,\nApple, CalDAV, etc.) using their respective APIs. The agent calendar\nis an additional calendar, not a replacement.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eAgent Calendar — The \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e:::calendar\u003c/code\u003e Block\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eTo put an event on YOUR agent calendar, include a \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e:::calendar\u003c/code\u003e block\nin your chat response. OpenPot parses it, stores it locally on the\ndevice, and displays it on the Calendar tab with a gold accent color.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e:::calendar\ntitle: Trailer Registration Expires\ndate: 2027-07-15\nnotes: Renew at DMV or online\nremind: 2w\n:::\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThis works on any tier — no backend server needed.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eRequired Fields\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Field | Format | Description |\n|-------|--------|-------------|\n| title | String | Event title, under 60 characters |\n| date | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eYYYY-MM-DD\u003c/code\u003e | The date of the event |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eOptional Fields\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Field | Format | Description |\n|-------|--------|-------------|\n| end_date | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eYYYY-MM-DD\u003c/code\u003e | End date for multi-day events |\n| time | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eHH:MM\u003c/code\u003e | Start time (24h format). Omit for all-day events. |\n| end_time | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eHH:MM\u003c/code\u003e | End time (24h). Only valid with \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003etime\u003c/code\u003e. |\n| notes | String | Additional context, plain text |\n| category | String | Maps to a Pulse channel for future alerts |\n| remind | String | When to push a reminder card: \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e1d\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e1w\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e2w\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e1m\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e3m\u003c/code\u003e |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eHow It Works\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eThe \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e:::calendar\u003c/code\u003e block is parsed by OpenPot and replaced with a\u003c/li\u003e\n compact calendar card in the chat bubble (not shown as raw text)\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eThe event is stored locally on the device in the Calendar tab\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eEvents appear with a gold accent color to distinguish them from\u003c/li\u003e\n provider calendar sources\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eIf \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eremind\u003c/code\u003e is set, OpenPot automatically pushes a Pulse card to\u003c/li\u003e\n the user when the reminder window is reached\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eNo backend, no HTTP server, no OAuth — works on any tier\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eRules\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eAlways confirm which calendar before creating an event.\u003c/strong\u003e\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eNever silently create events on any calendar.\u003c/strong\u003e\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eKeep titles under 60 characters\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eUse date-only for all-day events, add \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003etime\u003c/code\u003e for timed events\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eUse \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eremind\u003c/code\u003e for events the user should be warned about in advance\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eLearn the user's calendar layout early\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003chr style=\"border:none;border-top:1px solid #1e1e3f;margin:12px 0\"\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eChat Persistence\u003c/h2\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eChat messages can be stored in a PostgreSQL database on your backend\nserver, giving the user persistent chat history that survives app\nreinstalls and works across devices. This is a Tier 2 feature —\nrequires an HTTP server on port 8000.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eWhen to Set Up\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eSet up chat persistence when:\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eThe user asks: \"save my chats,\" \"set up chat persistence,\" \"I want\u003c/li\u003e\n my messages backed up\"\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eDuring OpenPot sync, if you detect an HTTP server on port 8000 but\u003c/li\u003e\n no \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/api/chat/sessions\u003c/code\u003e endpoint responding\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eThe user reinstalls the app and loses their chat history\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eWhat You Need\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003ePostgreSQL database (the same one used for memory)\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eFastAPI server on port 8000 (the same one serving other endpoints)\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eBearer token authentication (reuse existing token)\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eDatabase Schema\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eCreate the following in your PostgreSQL database:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eEnums\u003c/h4\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eCREATE TYPE sender_type AS ENUM ('user', 'assistant', 'system');\nCREATE TYPE message_type AS ENUM ('text', 'image', 'file', 'voice', 'system');\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eMain Table\u003c/h4\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eCREATE TABLE chat_messages (\n id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n session_id VARCHAR(255) NOT NULL,\n channel VARCHAR(255),\n content TEXT NOT NULL,\n sender_type sender_type NOT NULL,\n sender_name VARCHAR(255),\n message_type message_type NOT NULL DEFAULT 'text',\n run_id VARCHAR(255),\n parent_id UUID REFERENCES chat_messages(id),\n metadata JSONB DEFAULT '{}',\n attachments JSONB DEFAULT '[]',\n protected BOOLEAN DEFAULT FALSE,\n hidden BOOLEAN DEFAULT FALSE,\n search_vector tsvector,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n);\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eIndexes\u003c/h4\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eCREATE INDEX idx_chat_messages_session\n ON chat_messages(session_id, created_at DESC)\n WHERE hidden = FALSE;\nCREATE INDEX idx_chat_messages_search\n ON chat_messages USING GIN(search_vector);\nCREATE INDEX idx_chat_messages_run\n ON chat_messages(run_id) WHERE run_id IS NOT NULL;\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eSearch Vector Trigger\u003c/h4\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eCREATE OR REPLACE FUNCTION update_search_vector()\nRETURNS TRIGGER AS $$\nBEGIN\n NEW.search_vector :=\n setweight(to_tsvector('english', COALESCE(NEW.content, '')), 'A') ||\n setweight(to_tsvector('english', COALESCE(NEW.sender_name, '')), 'B') ||\n setweight(to_tsvector('english', COALESCE(NEW.channel, '')), 'C');\n RETURN NEW;\nEND;\n$$ LANGUAGE plpgsql;\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eCREATE TRIGGER chat_messages_search_update\n BEFORE INSERT OR UPDATE OF content, sender_name, channel\n ON chat_messages\n FOR EACH ROW\n EXECUTE FUNCTION update_search_vector();\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eUpdated_at Trigger\u003c/h4\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eCREATE OR REPLACE FUNCTION update_updated_at()\nRETURNS TRIGGER AS $$\nBEGIN\n NEW.updated_at = NOW();\n RETURN NEW;\nEND;\n$$ LANGUAGE plpgsql;\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eCREATE TRIGGER chat_messages_updated_at\n BEFORE UPDATE ON chat_messages\n FOR EACH ROW\n EXECUTE FUNCTION update_updated_at();\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eSession View\u003c/h4\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eCREATE OR REPLACE VIEW chat_sessions AS\nSELECT\n session_id,\n channel,\n MIN(created_at) AS first_message_at,\n MAX(created_at) AS last_message_at,\n COUNT(*) AS message_count,\n COUNT(*) FILTER (WHERE sender_type = 'user') AS user_message_count,\n COUNT(*) FILTER (WHERE sender_type = 'assistant') AS assistant_message_count\nFROM chat_messages\nWHERE hidden = FALSE\nGROUP BY session_id, channel\nORDER BY last_message_at DESC;\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eREST Endpoints\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eAll endpoints require \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eAuthorization: Bearer \u003ctoken\u003e\u003c/code\u003e header.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eStore a message\u003c/h4\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003ePOST /api/chat/messages\n{\n \"session_id\": \"openpot-local\",\n \"content\": \"Hello!\",\n \"sender_type\": \"user\",\n \"sender_name\": \"User\",\n \"message_type\": \"text\"\n}\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eRetrieve session history\u003c/h4\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eGET /api/chat/messages?session=openpot-local\u0026limit=50\u0026before=\u003ctimestamp\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eNote: use \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003esession\u003c/code\u003e as the query parameter, not \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003esession_id\u003c/code\u003e.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eSearch messages\u003c/h4\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eGET /api/chat/messages/search?q=search+term\u0026limit=20\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eList sessions\u003c/h4\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eGET /api/chat/sessions?limit=20\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eSession ID Convention\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eOpenPot native chat: \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\"openpot-local\"\u003c/code\u003e\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eFuture channel support: \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\"openpot-{channel-name}\"\u003c/code\u003e\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eData Management\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eChat data grows over time. To prevent the database from becoming\nunmanageably large:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eAutomatic Compaction (recommended)\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eSet up a weekly cron job that:\n1. Counts messages older than 90 days in each session\n2. For sessions with 1,000+ old messages, summarize the oldest batch\n into a single system message with the key topics covered\n3. Mark the original messages as \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ehidden = TRUE\u003c/code\u003e (soft delete)\n4. Never delete messages marked \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eprotected = TRUE\u003c/code\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e# Example cron entry (runs Sunday 3 AM)\n0 3 * * 0 /path/to/compact-chat.py --older-than 90 --batch-size 500\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eManual Export\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eWhen the user asks to export their chat history, provide a markdown\nor JSON export of the full conversation from the database.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eStorage Estimates\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Messages | Approximate Size |\n|----------|-----------------|\n| 1,000 | ~2 MB |\n| 10,000 | ~20 MB |\n| 100,000 | ~200 MB |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eWith compaction running weekly, most deployments stay under 50 MB\nof active chat data indefinitely.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eVerification\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eAfter setting up chat persistence, test:\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e# Store a test message\ncurl -X POST http://localhost:8000/api/chat/messages \\\n -H \"Authorization: Bearer \u003ctoken\u003e\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"session_id\":\"test\",\"content\":\"Hello\",\"sender_type\":\"user\",\"sender_name\":\"Test\",\"message_type\":\"text\"}'\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eRetrieve it\u003c/h2\u003e\ncurl -H \"Authorization: Bearer \u003ctoken\u003e\" \\\n \"http://localhost:8000/api/chat/messages?session=test\u0026limit=10\"\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eCheck sessions\u003c/h2\u003e\ncurl -H \"Authorization: Bearer \u003ctoken\u003e\" \\\n \"http://localhost:8000/api/chat/sessions\"\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eUpdate \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eopenpot-status.json\u003c/code\u003e with chat persistence as enabled after\nverification passes.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003chr style=\"border:none;border-top:1px solid #1e1e3f;margin:12px 0\"\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eVoice\u003c/h2\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eOpenPot supports voice input (speech-to-text) and voice output\n(text-to-speech). Voice is entirely client-side — no server changes\nare required.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eHow It Works\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eInput:\u003c/strong\u003e Apple Speech recognition on the device. The user speaks\u003c/li\u003e\n and the transcribed text is sent as a normal chat message. You\n receive it as text — no special handling needed.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eOutput:\u003c/strong\u003e ElevenLabs text-to-speech on the device. OpenPot reads\u003c/li\u003e\n your response aloud. Write naturally, avoid special formatting for\n TTS. If the user is using voice, prefer shorter, conversational\n responses.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eVoice Setup (User-Side, in the OpenPot App)\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eWalk the user through these steps:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e1. \u003cstrong style=\"color:#e5e7eb\"\u003eElevenLabs API Key:\u003c/strong\u003e\n Settings → General → Voice Output → ElevenLabs API Key\n (from elevenlabs.io → Profile → API Keys)\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e2. \u003cstrong style=\"color:#e5e7eb\"\u003eVoice Selection (per agent):\u003c/strong\u003e\n Agents tab → tap agent → Voice section → Voice ID\n (browse at elevenlabs.io/voice-library, copy the Voice ID)\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e3. \u003cstrong style=\"color:#e5e7eb\"\u003eVoice Model:\u003c/strong\u003e Default is \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eeleven_multilingual_v2\u003c/code\u003e. Can be\n changed per-agent.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e4. \u003cstrong style=\"color:#e5e7eb\"\u003eSpeed:\u003c/strong\u003e Adjustable per-agent. Default 1.0, range 0.5–2.0.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eCommon Voice Issues\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003e\"Voice not working\"\u003c/strong\u003e → check ElevenLabs API key in Settings,\u003c/li\u003e\n check API key has credits remaining.\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003e\"Wrong voice\"\u003c/strong\u003e → check per-agent Voice ID in Agents tab.\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003e\"Talk Mode breaks messages\"\u003c/strong\u003e → do NOT add \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eoperator.talk.secrets\u003c/code\u003e\u003c/li\u003e\n scope to gateway config. It interferes with message delivery.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eNo Server Changes Needed\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eVoice requires zero backend configuration. Do not modify SOUL.md,\ninstall services, or configure endpoints for voice. Mark voice as\ninstalled in \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eopenpot-status.json\u003c/code\u003e after the user confirms credentials.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003chr style=\"border:none;border-top:1px solid #1e1e3f;margin:12px 0\"\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eOnboarding\u003c/h2\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eWhen the user asks to set up or connect OpenPot (\"I have OpenPot, get\nme set up,\" \"help me connect OpenPot,\" \"OpenPot setup\"), follow these\nsteps.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eStep 1 — Provide Connection Details\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eYou know your own infrastructure. Provide:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e1. \u003cstrong style=\"color:#e5e7eb\"\u003eGateway URL\u003c/strong\u003e — format: \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ews://\u003cyour-ip\u003e:18789\u003c/code\u003e\n If Tailscale is available, provide both addresses:\n - LAN: \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ews://\u003clan-ip\u003e:18789\u003c/code\u003e (faster, home WiFi only)\n - Tailscale: \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ews://\u003ctailscale-ip\u003e:18789\u003c/code\u003e (works from anywhere)\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e2. \u003cstrong style=\"color:#e5e7eb\"\u003eBearer token\u003c/strong\u003e — send in a private/1:1 channel only.\n Never post tokens in group channels.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e3. \u003cstrong style=\"color:#e5e7eb\"\u003eInstructions:\u003c/strong\u003e \"Open OpenPot → Agents tab → Add Agent → paste\n the gateway URL and token → tap Connect.\"\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eStep 2 — Check Tailscale\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eRun \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003etailscale status\u003c/code\u003e to check if Tailscale is installed.\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eIf running: provide the Tailscale IP for the second route.\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eIf not installed: LAN-only works on home WiFi. For remote access,\u003c/li\u003e\n point them to tailscale.com/download.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eStep 3 — Auto-Approve Device Pairing\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eTell the user: \"When you tap Connect, I'll approve the pairing\nautomatically. Go ahead.\"\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThen:\n1. Run \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eopenclaw devices list\u003c/code\u003e to check for pending requests\n2. Approve the request from \"openclaw-ios\" or \"OpenPot\":\n \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eopenclaw devices approve \u003crequestId\u003e\u003c/code\u003e\n3. Confirm: \"You're approved and connected!\"\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eSecurity rules:\u003c/strong\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eOnly auto-approve during an active onboarding conversation\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eOnly approve ONE device per onboarding flow\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eIf multiple requests arrive, approve only the first and alert the user\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eOutside onboarding, surface pairing requests for manual approval\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eStep 4 — Post-Connection\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eOnce connected:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e1. Send a welcome Pulse card (if you have an HTTP server):\n title: \"Welcome to OpenPot\", body: \"Your connection is live.\n Explore your tabs.\", category: \"system\",\n actions: [\"acknowledge\"]\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e2. Briefly describe the tabs the user has enabled.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e3. Offer to run \"OpenPot sync\" to configure all features.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eStep 5 — Backend Setup (if applicable)\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eIf you have a backend server on port 8000, check health and provide\nthe server URL and token for OpenPot Settings → Server Configuration.\nIf you don't have a backend server, skip — Chat, Terminal, and basic\nfeatures work with the gateway alone.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eStep 6 — SSH Key Setup (for Terminal)\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThe Terminal tab uses a device-generated SSH key. On first use:\n1. The user taps the key icon in the Terminal status bar\n2. Copies the public key\n3. Sends it to you\n4. You add it to \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e~/.ssh/authorized_keys\u003c/code\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThis is a one-time step per device.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eTroubleshooting\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\"Connecting stays amber\" → check gateway is running, port 18789,\u003c/li\u003e\n address starts with ws://, user is on correct network\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\"Device not approved\" → run \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eopenclaw devices list\u003c/code\u003e, approve manually\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\"Pulse is empty\" → run \"OpenPot sync\", check for HTTP server\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\"Can't connect on cellular\" → Tailscale needed for remote access\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\"Agent name blank\" → check IDENTITY.md in workspace\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\"Terminal not connecting on cellular\" → known limitation; Terminal\u003c/li\u003e\n SSH works on LAN, Tailscale SSH support is in development\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003chr style=\"border:none;border-top:1px solid #1e1e3f;margin:12px 0\"\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eSync Process\u003c/h2\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eWhen the user says \"OpenPot sync\" or taps the OpenPot Sync button\nin the Agents tab:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eStep 1 — Refresh the skill\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eIMPORTANT:\u003c/strong\u003e Until the openpot-awareness skill is fully registered\nwith ClawHub for direct updates, refreshing requires a full uninstall\nand reinstall cycle:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eopenclaw skills uninstall openpot-awareness\nopenclaw skills install openpot-awareness\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThen restart the gateway for changes to take effect.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003e[Future — when ClawHub supports updates]\u003c/strong\u003e This step will simplify to:\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eopenclaw skills update openpot-awareness\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eStep 2 — Check current status\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eRead \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eopenpot-status.json\u003c/code\u003e from your workspace root. If it doesn't\nexist, this is a first-time setup — treat all features as version 0.\nUse the template from your skill directory at\n\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eskills/openpot-awareness/templates/\u003c/code\u003e if available.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eStep 3 — Check infrastructure and report\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eFor each feature, check if your infrastructure meets the prerequisites\nand report what's available and what's missing:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eChat\u003c/strong\u003e — always available (gateway only)\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eChat Persistence\u003c/strong\u003e — requires HTTP server + PostgreSQL +\u003c/li\u003e\n \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/api/chat/sessions\u003c/code\u003e endpoint. If not available, offer to set it up\n (see Chat Persistence section).\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003ePulse Cards\u003c/strong\u003e — requires HTTP server with \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ePOST /api/cards\u003c/code\u003e\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eCalendar\u003c/strong\u003e — requires HTTP server + calendar provider +\u003c/li\u003e\n \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/api/calendar/events\u003c/code\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eVoice\u003c/strong\u003e — configured in the app, no server needed\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003ePage Capture\u003c/strong\u003e — always available (gateway only)\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eApps\u003c/strong\u003e — requires HTTP server + \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/api/apps\u003c/code\u003e endpoint\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eTerminal\u003c/strong\u003e — always available (gateway only, LAN connection)\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eOffer to help set up missing prerequisites.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eStep 4 — Install starter apps\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eCopy apps from your skill directory at\n\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eskills/openpot-awareness/apps/\u003c/code\u003e to your apps serving directory.\nTrack installed and user-removed apps in \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eopenpot-starter-apps.json\u003c/code\u003e\nso removed apps don't come back on future syncs.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eStep 5 — Write status\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eUpdate \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eopenpot-status.json\u003c/code\u003e in your workspace root with current\nfeature versions and timestamps.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eStep 6 — Report\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eOpenPot Sync Complete\n━━━━━━━━━━━━━━━━━━━━\n✅ Chat (v1)\n✅ Chat Persistence (v1) — messages backed up to server\n✅ Pulse Cards (v1)\n✅ Calendar (v2) — Google Calendar connected\n✅ Voice (v1) — configured in app\n✅ Page Capture (v2)\n✅ Onboarding (v2)\n✅ Apps (v1) — {count} starter apps installed\n⬚ Terminal SSH on Tailscale — in development\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eStarter Apps: {count} installed\nLast synced: {timestamp}\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003chr style=\"border:none;border-top:1px solid #1e1e3f;margin:12px 0\"\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eMigration: Clean Up Old SOUL.md Inserts\u003c/h2\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eIf your SOUL.md contains \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\u003c!-- OPENPOT INSERT\u003c/code\u003e markers from a previous\nversion of this skill, remove them. The native skill system loads this\nSKILL.md automatically — SOUL.md injection is no longer needed.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eTo clean up:\n1. Read your SOUL.md\n2. Delete everything between \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\u003c!-- OPENPOT INSERT\u003c/code\u003e and\n \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\u003c!-- END OPENPOT INSERT --\u003e\u003c/code\u003e markers (including the markers)\n3. Restart the gateway\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThis is a one-time migration. New installations do not touch SOUL.md.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003chr style=\"border:none;border-top:1px solid #1e1e3f;margin:12px 0\"\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eRules\u003c/h2\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eNEVER modify SOUL.md\u003c/strong\u003e — this skill loads natively via OpenClaw\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eNEVER auto-sync\u003c/strong\u003e — the user triggers sync with \"OpenPot sync\"\u003c/li\u003e\n or the Sync button\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eNEVER re-install apps the user deleted\u003c/strong\u003e — respect the tracking file\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eNEVER add calendar events without explicit user permission\u003c/strong\u003e\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eNEVER respond to \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e[calendar_context]\u003c/code\u003e messages\u003c/strong\u003e — absorb silently\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eAlways update openpot-status.json\u003c/strong\u003e after any change\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eAlways strip HTML from calendar notes\u003c/strong\u003e before serving to OpenPot\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eAlways include \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eemoji\u003c/code\u003e field\u003c/strong\u003e in app metadata for grid icon display\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eApp endpoints (\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/api/apps\u003c/code\u003e) must not require authentication\u003c/li\u003e\n\u003c/p\u003e"])</script><script>self.__next_f.push([1,"20:[\"$\",\"div\",null,{\"className\":\"skill-page\",\"children\":[[\"$\",\"script\",null,{\"type\":\"application/ld+json\",\"dangerouslySetInnerHTML\":{\"__html\":\"{\\\"@context\\\":\\\"https://schema.org\\\",\\\"@type\\\":\\\"SoftwareApplication\\\",\\\"name\\\":\\\"OpenPot Awareness\\\",\\\"description\\\":\\\"Teaches this agent how to serve content to the OpenPot iOS client — cards, apps, page captures, calendar, voice, chat persistence, and onboarding\\\",\\\"url\\\":\\\"https://bytesagain.com/skill/openpot-awareness\\\",\\\"applicationCategory\\\":\\\"requires-oauth-token\\\",\\\"operatingSystem\\\":\\\"Any\\\",\\\"offers\\\":{\\\"@type\\\":\\\"Offer\\\",\\\"price\\\":\\\"0\\\",\\\"priceCurrency\\\":\\\"USD\\\"},\\\"publisher\\\":{\\\"@type\\\":\\\"Organization\\\",\\\"name\\\":\\\"BytesAgain\\\",\\\"url\\\":\\\"https://bytesagain.com\\\"}}\"}}],[\"$\",\"div\",null,{\"className\":\"breadcrumb\",\"children\":[[\"$\",\"a\",null,{\"href\":\"/\",\"children\":\"BytesAgain\"}],\" › \",[\"$\",\"a\",null,{\"href\":\"/skills\",\"children\":\"Skills\"}],\" › \",\"OpenPot Awareness\"]}],[\"$\",\"div\",null,{\"className\":\"two-col\",\"children\":[[\"$\",\"div\",null,{\"className\":\"two-col-main\",\"children\":[[\"$\",\"div\",null,{\"className\":\"skill-card\",\"children\":[[\"$\",\"div\",null,{\"className\":\"skill-header\",\"children\":[[\"$\",\"div\",null,{\"className\":\"skill-badges\",\"children\":[[\"$\",\"span\",null,{\"className\":\"badge\",\"style\":{\"color\":\"#818cf8\",\"background\":\"#818cf822\",\"borderColor\":\"#818cf844\"},\"children\":[\"🦀\",\" \",\"ClawHub\"]}],false]}],[\"$\",\"div\",null,{\"className\":\"skill-top-actions\",\"children\":[\"$\",\"$L22\",null,{\"slug\":\"openpot-awareness\"}]}]]}],[\"$\",\"h1\",null,{\"className\":\"skill-title\",\"children\":\"OpenPot Awareness\"}],[\"$\",\"p\",null,{\"className\":\"skill-owner\",\"children\":[\"by \",[\"$\",\"span\",null,{\"children\":[\"@\",\"almnotai\"]}]]}],[\"$\",\"p\",null,{\"className\":\"skill-desc\",\"children\":\"Teaches this agent how to serve content to the OpenPot iOS client — cards, apps, page captures, calendar, voice, chat persistence, and onboarding\"}],[\"$\",\"div\",null,{\"className\":\"skill-meta\",\"children\":[[\"$\",\"div\",null,{\"className\":\"meta-item\",\"children\":[[\"$\",\"span\",null,{\"className\":\"meta-label\",\"children\":\"Version\"}],[\"$\",\"span\",null,{\"className\":\"meta-value\",\"children\":[\"v\",\"6.0.0\"]}]]}],[\"$\",\"div\",null,{\"className\":\"meta-item\",\"children\":[[\"$\",\"span\",null,{\"className\":\"meta-label\",\"children\":\"Downloads\"}],[\"$\",\"span\",null,{\"className\":\"meta-value\",\"children\":\"336\"}]]}],false,false,false,[\"$\",\"div\",null,{\"className\":\"meta-item\",\"style\":{\"flexDirection\":\"row\",\"gap\":6,\"alignItems\":\"center\"},\"children\":[[\"$\",\"a\",\"crypto-defi\",{\"href\":\"/?q=crypto-defi\",\"className\":\"tag\",\"children\":[\"#\",\"crypto-defi\"]}],[\"$\",\"a\",\"writing\",{\"href\":\"/?q=writing\",\"className\":\"tag\",\"children\":[\"#\",\"writing\"]}],[\"$\",\"a\",\"audio\",{\"href\":\"/?q=audio\",\"className\":\"tag\",\"children\":[\"#\",\"audio\"]}],[\"$\",\"a\",\"legal\",{\"href\":\"/?q=legal\",\"className\":\"tag\",\"children\":[\"#\",\"legal\"]}],[\"$\",\"a\",\"hr\",{\"href\":\"/?q=hr\",\"className\":\"tag\",\"children\":[\"#\",\"hr\"]}]]}]]}],[\"$\",\"div\",null,{\"style\":{\"marginTop\":6},\"children\":[\"$\",\"a\",null,{\"href\":\"https://clawhub.ai/almnotai/openpot-awareness\",\"target\":\"_blank\",\"rel\":\"noopener\",\"className\":\"btn-secondary\",\"style\":{\"padding\":\"6px 12px\",\"fontSize\":\".82em\",\"borderRadius\":8,\"background\":\"transparent\",\"border\":\"1px solid var(--border-card)\",\"color\":\"var(--text-muted2)\",\"textDecoration\":\"none\",\"whiteSpace\":\"nowrap\"},\"children\":[\"View on \",\"ClawHub\",\" →\"]}]}]]}],[\"$\",\"div\",null,{\"className\":\"install-box\",\"children\":[[\"$\",\"div\",null,{\"className\":\"install-header\",\"children\":[[\"$\",\"div\",null,{\"className\":\"install-dots\",\"children\":[[\"$\",\"div\",null,{\"className\":\"dot\",\"style\":{\"background\":\"#ef4444\"}}],[\"$\",\"div\",null,{\"className\":\"dot\",\"style\":{\"background\":\"#eab308\"}}],[\"$\",\"div\",null,{\"className\":\"dot\",\"style\":{\"background\":\"#22c55e\"}}]]}],[\"$\",\"span\",null,{\"className\":\"install-label\",\"children\":\"TERMINAL\"}]]}],[\"$\",\"div\",null,{\"className\":\"install-body\",\"style\":{\"flexWrap\":\"wrap\"},\"children\":[[\"$\",\"code\",null,{\"className\":\"install-cmd\",\"children\":\"clawhub install openpot-awareness\"}],[\"$\",\"button\",null,{\"className\":\"copy-btn\",\"data-cmd\":\"clawhub install openpot-awareness\",\"style\":{\"fontWeight\":700},\"children\":\"Copy\"}]]}]]}],[\"$\",\"section\",null,{\"className\":\"skill-card\",\"style\":{\"marginBottom\":20},\"children\":[[\"$\",\"h2\",null,{\"style\":{\"color\":\"#f8fafc\",\"fontSize\":\"1.2em\",\"fontWeight\":800,\"margin\":\"0 0 16px\",\"display\":\"flex\",\"alignItems\":\"center\",\"gap\":8},\"children\":\"📖 About This Skill\"}],[\"$\",\"div\",null,{\"style\":{\"fontSize\":\".92em\",\"color\":\"#94a3b8\",\"lineHeight\":1.75},\"dangerouslySetInnerHTML\":{\"__html\":\"$23\"}}]]}],null,null,null,\"$L24\",null,\"$L25\",null,false,false]}],\"$L26\"]}]]}]\n"])</script><script>self.__next_f.push([1,"21:[\"$\",\"script\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"\\n document.querySelectorAll('.copy-btn, .script-copy-btn').forEach(btn =\u003e {\\n btn.addEventListener('click', () =\u003e {\\n const cmd = btn.getAttribute('data-cmd');\\n if (!cmd) return;\\n navigator.clipboard.writeText(cmd).then(() =\u003e {\\n const orig = btn.textContent;\\n btn.textContent = 'Copied!';\\n setTimeout(() =\u003e btn.textContent = orig, 1500);\\n }).catch(() =\u003e {});\\n });\\n });\\n \"}}]\n"])</script><script>self.__next_f.push([1,"29:I[71521,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/12w5ognupk9fb.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n27:T4bf,\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\"Connecting stays amber\" → check gateway is running, port 18789,\u003c/li\u003e\n address starts with ws://, user is on correct network\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\"Device not approved\" → run \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eopenclaw devices list\u003c/code\u003e, approve manually\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\"Pulse is empty\" → run \"OpenPot sync\", check for HTTP server\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\"Can't connect on cellular\" → Tailscale needed for remote access\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\"Agent name blank\" → check IDENTITY.md in workspace\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\"Terminal not connecting on cellular\" → known limitation; Terminal\u003c/li\u003e\n SSH works on LAN, Tailscale SSH support is in development\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003chr style=\"border:none;border-top:1px solid #1e1e3f;margin:12px 0\"\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eSync Process\u003c/h2\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eWhen the user says \"OpenPot sync\" or taps the OpenPot Sync button\nin the Agents tab:\u003c/p\u003e24:[\"$\",\"section\",null,{\"className\":\"skill-card\",\"style\":{\"marginBottom\":20},\"children\":[[\"$\",\"h2\",null,{\"style\":{\"color\":\"#f8fafc\",\"fontSize\":\"1.2em\",\"fontWeight\":800,\"margin\":\"0 0 16px\",\"display\":\"flex\",\"alignItems\":\"center\",\"gap\":8},\"children\":\"📋 Tips \u0026 Best Practices\"}],[\"$\",\"div\",null,{\"style\":{\"fontSize\":\".92em\",\"color\":\"#94a3b8\",\"lineHeight\":1.75},\"dangerouslySetInnerHTML\":{\"__html\":\"$27\"}}]]}]\n28:T5f9,"])</script><script>self.__next_f.push([1,"\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eAlways confirm which calendar before creating an event.\u003c/strong\u003e\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eNever silently create events on any calendar.\u003c/strong\u003e\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eKeep titles under 60 characters\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eUse date-only for all-day events, add \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\" style=\"background:#0a0a1c;color:#fbbf24;padding:1px 5px;border-radius:3px;font-size:.85em\"\u003etime\u003c/code\u003e for timed events\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eUse \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\" style=\"background:#0a0a1c;color:#fbbf24;padding:1px 5px;border-radius:3px;font-size:.85em\"\u003eremind\u003c/code\u003e for events the user should be warned about in advance\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eLearn the user's calendar layout early\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003chr style=\"border:none;border-top:1px solid #1e1e3f;margin:12px 0\"\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eChat Persistence\u003c/h2\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eChat messages can be stored in a PostgreSQL database on your backend\nserver, giving the user persistent chat history that survives app\nreinstalls and works across devices. This is a Tier 2 feature —\nrequires an HTTP server on port 8000.\u003c/p\u003e"])</script><script>self.__next_f.push([1,"25:[\"$\",\"section\",null,{\"className\":\"skill-card\",\"style\":{\"marginBottom\":20},\"children\":[[\"$\",\"h2\",null,{\"style\":{\"color\":\"#f8fafc\",\"fontSize\":\"1.2em\",\"fontWeight\":800,\"margin\":\"0 0 16px\",\"display\":\"flex\",\"alignItems\":\"center\",\"gap\":8},\"children\":\"🔒 Constraints\"}],[\"$\",\"div\",null,{\"style\":{\"fontSize\":\".92em\",\"color\":\"#94a3b8\",\"lineHeight\":1.75},\"dangerouslySetInnerHTML\":{\"__html\":\"$28\"}}]]}]\n26:[\"$\",\"div\",null,{\"className\":\"two-col-side\",\"children\":[\"$\",\"$L29\",null,{\"category\":\"requires-oauth-token\",\"currentSlug\":\"openpot-awareness\",\"name\":\"OpenPot Awareness\",\"tags\":[\"crypto-defi\",\"writing\",\"audio\",\"legal\",\"hr\",\"communication\"]}]}]\n"])</script></body></html>