Discord OMP Plugin Blueprint

Discord OMP Plugin Blueprint

This document specifies the architecture for the Tier 3 Bidirectional Discord-OMP Integration. This system connects the Oh My Pi (OMP) local agent workspace with a persistent Discord bot, allowing developers to monitor agent task queues, receive tool-call alerts, and inject steering commands remotely.

1. Stack Matrix

Component Technology Role
OMP Extension TypeScript, @earendil-works/pi-coding-agent Emits tool_execution_start, turn_end events; registers /discord slash command.
Discord Bot Python 3.11, discord.py, Pydantic, FastAPI Listens to Discord slash commands; provides REST endpoint for OMP payloads.
Database SQLite, aiosqlite Persists user-agent session mappings, thread IDs, and notification preferences.
Transport REST (HTTP POST) & Server-Sent Events (SSE) OMP pushes to FastAPI (REST); Bot streams state back to OMP (SSE/WebSocket).

2. Process Topology

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
graph TD
    subgraph OMP Workspace (Local)
        OMP[OMP Agent Process]
        Ext[discord-notify.ts Extension]
        OMP -->|Trigger Event| Ext
    end

    subgraph External Infrastructure (0rk.de / dv)
        API[FastAPI Webhook Server]
        Bot[Discord.py Bot Task]
        DB[(SQLite DB)]
        
        API <--> Bot
        Bot <--> DB
    end

    subgraph Discord
        Channel[Discord Channel/Thread]
    end

    Ext -->|HTTP POST JSON| API
    API -->|Async Task| Bot
    Bot -->|Discord API| Channel
    Channel -->|Slash Command /reply| Bot
    Bot -->|REST Polling / SSE| Ext

3. Sequence Flows

3.1. Agent Event Notification

  1. OMP fires tool_execution_start for bash.
  2. Extension intercepts via pi.on("tool_execution_start", ...)
  3. Extension evaluates policy (e.g., is command destructive?).
  4. Extension posts JSON payload to FastAPI /webhook/omp-event.
  5. FastAPI saves to SQLite and triggers discord.py loop to send an Embed to a designated Discord thread.

3.2. Remote Steering Injection

  1. User types /steer "Stop current task and yield" in Discord.
  2. Discord API calls bot webhook.
  3. Bot inserts steering command into pending_commands SQLite table.
  4. Extension polls /api/commands?session_id=X (or receives via SSE).
  5. Extension executes pi.sendMessage({ deliverAs: "steer", content: ... }).

4. Data Contracts & Interfaces

4.1. TypeScript Interfaces (OMP Extension)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
interface DiscordWebhookPayload {
  session_id: string;
  event_type: "tool_call" | "turn_end" | "session_start";
  tool_name?: string;
  content: string;
  metadata: Record<string, unknown>;
  timestamp: string;
}

interface PendingCommand {
  command_id: string;
  action: "steer" | "followUp" | "abort";
  content: string;
}

4.2. Pydantic Models (Python Backend)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from pydantic import BaseModel
from typing import Optional, Dict, Any

class OMPEvent(BaseModel):
    session_id: str
    event_type: str
    tool_name: Optional[str] = None
    content: str
    metadata: Dict[str, Any] = {}
    timestamp: str

class SteeringCommand(BaseModel):
    session_id: str
    action: str
    content: str

4.3. SQLite Schema

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
CREATE TABLE agent_sessions (
    session_id TEXT PRIMARY KEY,
    discord_thread_id INTEGER,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE pending_commands (
    command_id TEXT PRIMARY KEY,
    session_id TEXT,
    action TEXT,
    content TEXT,
    status TEXT DEFAULT 'pending', -- 'pending' | 'delivered'
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY(session_id) REFERENCES agent_sessions(session_id)
);

5. REST Specification

Endpoint Method Payload Response Description
/api/webhook/event POST OMPEvent 200 OK Receive agent event.
/api/sessions/{id}/link POST {thread_id: int} 200 OK Link OMP session to Discord thread.
/api/commands/poll GET ?session_id=str [PendingCommand] OMP extension checks for queued steer commands.
/api/commands/{id}/ack POST {status: "delivered"} 200 OK Mark steer command as delivered to OMP.

6. Discord Embed Templates

6.1. Tool Call Alert Embed

  • Color: Orange (#FFA500)
  • Title: ๐Ÿ› ๏ธ Tool Execution: bash
  • Description: Command execution started in /home/dev
  • Fields:
    • Input: bash\nrm -rf ./*\n
    • Session: session-xyz-123
  • Footer: OMP Agent System | Timestamp

6.2. Turn End Summary Embed

  • Color: Green (#00FF00)
  • Title: โœ… Turn Completed
  • Description: Agent has finished processing the current prompt.
  • Fields:
    • Tokens Used: 1245
    • Pending Messages: False
  • Footer: OMP Agent System | Timestamp

7. Failure Modes & Observability

  • API Unreachable: Extension catches ECONNREFUSED on webhook POST. It implements a circuit breaker, logs to pi.logger.error(), and continues execution without blocking the agent.
  • Discord Rate Limiting: discord.py handles HTTP 429 transparently. If queues fill, events are dropped (fire-and-forget) to prevent memory leaks in the FastAPI backend.
  • Lost Steering Commands: Steer commands remain pending in SQLite. If OMP crashes, old commands may be injected upon restart. Extension should purge stale commands (older than 1 hour) on startup.
  • Observability: FastAPI uses standard logging. SQLite tracks all dispatched commands for auditing.

8. Decision Records

  1. Why Polling over WebSocket? While SSE/WS is lower latency, REST polling every 5 seconds is simpler to deploy and completely stateless. Polling avoids managing persistent connection lifecycles within the OMP Extension API.
  2. Why Python/FastAPI for Backend? discord.py is the most mature framework for Discord interactions. Bundling FastAPI directly into the bot process avoids the need for Redis pub/sub.
  3. Why SQLite? Lightweight, single-file, zero-dependency. Perfectly fits the scale of a single-developer workspace.