feat: add Pi Coding Agent backend runtime, update dashboard UI
This commit is contained in:
@@ -5,7 +5,7 @@ How a Discord message becomes an AI response, step by step.
|
||||
## The Big Picture
|
||||
|
||||
```
|
||||
Discord User Aetheel Gateway Claude Code CLI
|
||||
Discord User Aetheel Gateway Backend CLI
|
||||
│ │ │
|
||||
│ @Aetheel what's 2+2? │ │
|
||||
├──────────────────────────────► │ │
|
||||
@@ -195,21 +195,23 @@ Sections with null or empty content are omitted entirely.
|
||||
|
||||
The assembled system prompt is written to a temporary file because it can be thousands of characters — too large for a CLI argument.
|
||||
|
||||
**File:** `src/agent-runtime.ts` → `executeClaude()`
|
||||
**File:** `src/agent-runtime.ts` → `processMessage()`
|
||||
|
||||
```
|
||||
/tmp/aetheel-prompt-1d6c77f1-4a4e-49f8-ae9b-cff6fb47b971.txt
|
||||
```
|
||||
|
||||
This file is deleted after the CLI process completes.
|
||||
This file is deleted after the CLI process completes (used by the Claude backend; other backends may prepend the system prompt directly to the user prompt).
|
||||
|
||||
### Step 8: Spawn Claude CLI
|
||||
### Step 8: Spawn Backend CLI
|
||||
|
||||
The gateway spawns the Claude Code CLI as a child process.
|
||||
The gateway delegates to the configured **Backend Adapter**, which spawns the corresponding CLI as a child process.
|
||||
|
||||
**File:** `src/agent-runtime.ts` → `runClaude()`
|
||||
**File:** `src/backends/{claude,codex,gemini,opencode,pi}-backend.ts` → `spawnCli()`
|
||||
|
||||
The actual command:
|
||||
The backend is selected via the `AGENT_BACKEND` environment variable (default: `claude`). Each backend adapter translates the common interface into the CLI-specific flags.
|
||||
|
||||
#### Example: Claude Code CLI (default)
|
||||
|
||||
```bash
|
||||
claude \
|
||||
@@ -227,15 +229,55 @@ claude \
|
||||
--max-turns 25
|
||||
```
|
||||
|
||||
Key flags:
|
||||
#### Example: Pi CLI
|
||||
|
||||
```bash
|
||||
pi \
|
||||
-p "what's the weather like?" \
|
||||
--mode json \
|
||||
--append-system-prompt "<system prompt text>" \
|
||||
--no-session \
|
||||
--no-extensions --no-skills --no-themes
|
||||
```
|
||||
|
||||
#### Example: Codex CLI
|
||||
|
||||
```bash
|
||||
codex exec \
|
||||
"<system prompt + user prompt>" \
|
||||
--json \
|
||||
--dangerously-bypass-approvals-and-sandbox \
|
||||
--cd /path/to/config
|
||||
```
|
||||
|
||||
#### Example: OpenCode CLI
|
||||
|
||||
```bash
|
||||
opencode run \
|
||||
"<system prompt + user prompt>" \
|
||||
--format json
|
||||
```
|
||||
|
||||
#### Example: Gemini CLI
|
||||
|
||||
```bash
|
||||
gemini \
|
||||
"<system prompt + user prompt>" \
|
||||
--output-format json \
|
||||
--approval-mode yolo
|
||||
```
|
||||
|
||||
Key flags (Claude):
|
||||
- `-p` — Print mode (non-interactive, exits after response)
|
||||
- `--output-format json` — Returns JSON array of message objects
|
||||
- `--dangerously-skip-permissions` — No interactive permission prompts
|
||||
- `--append-system-prompt-file` — Appends our persona/memory to Claude's default prompt
|
||||
- `--allowedTools` — Which tools Claude can use (one flag per tool)
|
||||
- `--allowedTools` — Which tools Claude can use (one flag per tool; Claude-only feature)
|
||||
- `--max-turns` — Prevents runaway agent loops
|
||||
- `--resume SESSION_ID` — Added when resuming an existing conversation
|
||||
|
||||
Other backends use equivalent flags for their CLIs (e.g., Pi uses `--mode json` and `--append-system-prompt`, Codex uses `--json` and `--dangerously-bypass-approvals-and-sandbox`).
|
||||
|
||||
The process runs with `cwd` set to the `config/` directory, so Claude can read/write files there (like `memory.md`).
|
||||
|
||||
`stdin` is set to `"ignore"` to prevent the CLI from waiting for interactive input.
|
||||
@@ -259,9 +301,9 @@ When a session ID exists, `--resume 37336c32-73cb-4cf5-9771-1c8f694398ff` is add
|
||||
|
||||
The CLI returns a JSON array on stdout. The gateway parses it as chunks arrive.
|
||||
|
||||
**File:** `src/agent-runtime.ts` → `runClaude()` stdout handler
|
||||
**File:** `src/backends/{backend}-backend.ts` → `spawnCli()` stdout handler
|
||||
|
||||
Example CLI output:
|
||||
Example CLI output (Claude):
|
||||
|
||||
```json
|
||||
[
|
||||
@@ -380,11 +422,11 @@ Instruction: Save important context to memory.md before shutting down.
|
||||
|
||||
---
|
||||
|
||||
## What Gets Sent to Claude
|
||||
## What Gets Sent to the Backend
|
||||
|
||||
For every event, Claude receives:
|
||||
For every event, the backend CLI receives:
|
||||
|
||||
1. **Default Claude Code system prompt** (built-in, from the CLI)
|
||||
1. **Default system prompt** (built-in from the CLI — varies by backend)
|
||||
2. **Appended system prompt** (from our assembled markdown files):
|
||||
- Identity (who the agent is)
|
||||
- Personality (how it behaves)
|
||||
@@ -394,10 +436,12 @@ For every event, Claude receives:
|
||||
- Tool configuration (API notes)
|
||||
- Preamble about writing to memory.md
|
||||
3. **The prompt text** (user message, heartbeat instruction, or cron instruction)
|
||||
4. **Session history** (if resuming via `--resume`)
|
||||
5. **Allowed tools** (Read, Write, Edit, Glob, Grep, WebSearch, WebFetch)
|
||||
4. **Session history** (if resuming via backend-specific session flags)
|
||||
5. **Allowed tools** (Claude only: Read, Write, Edit, Glob, Grep, WebSearch, WebFetch)
|
||||
|
||||
Claude runs in the `config/` directory, so it can read and write files there — including updating `memory.md` with new facts.
|
||||
> **Note:** How the system prompt is delivered varies by backend. Claude Code uses `--append-system-prompt-file`, Pi uses `--append-system-prompt`, and other backends prepend it to the user prompt.
|
||||
|
||||
The backend CLI runs in the `config/` directory, so the agent can read and write files there — including updating `memory.md` with new facts.
|
||||
|
||||
---
|
||||
|
||||
@@ -408,12 +452,26 @@ src/
|
||||
├── index.ts ← Entry point: creates GatewayCore, registers shutdown handler
|
||||
├── gateway-core.ts ← Orchestrator: wires everything, manages lifecycle
|
||||
├── config.ts ← Reads env vars (DISCORD_BOT_TOKEN, etc.)
|
||||
├── logger.ts ← Pino structured logger with pretty-printing
|
||||
├── discord-bot.ts ← Discord.js wrapper: messages, slash commands, typing
|
||||
├── event-queue.ts ← FIFO queue: all events (message, heartbeat, cron, hook)
|
||||
├── agent-runtime.ts ← Core engine: reads configs, spawns CLI, parses output
|
||||
├── agent-runtime.ts ← Core engine: reads configs, delegates to backend adapter
|
||||
├── backends/ ← Pluggable CLI backend adapters
|
||||
│ ├── types.ts ← BackendAdapter interface & BackendName union type
|
||||
│ ├── registry.ts ← resolveBackendName() & createBackend() factory
|
||||
│ ├── index.ts ← Barrel exports
|
||||
│ ├── claude-backend.ts ← Claude Code CLI adapter
|
||||
│ ├── codex-backend.ts ← OpenAI Codex CLI adapter
|
||||
│ ├── gemini-backend.ts ← Google Gemini CLI adapter
|
||||
│ ├── opencode-backend.ts ← OpenCode CLI adapter
|
||||
│ └── pi-backend.ts ← Pi Coding Agent CLI adapter
|
||||
├── markdown-config-loader.ts ← Reads config/*.md files fresh each event
|
||||
├── system-prompt-assembler.ts ← Concatenates markdown into system prompt with headers
|
||||
├── skills-loader.ts ← Loads skills from config/skills/*/SKILL.md
|
||||
├── session-manager.ts ← Channel → session ID mapping (persisted to JSON)
|
||||
├── message-history.ts ← Per-channel message storage
|
||||
├── conversation-archiver.ts ← Markdown conversation logs
|
||||
├── ipc-watcher.ts ← Polls ipc/outbound/ for proactive messages
|
||||
├── response-formatter.ts ← Splits long text for Discord's 2000 char limit
|
||||
├── error-formatter.ts ← Sanitizes errors (strips keys, paths, stacks)
|
||||
├── heartbeat-scheduler.ts ← setInterval timers from heartbeat.md
|
||||
@@ -421,16 +479,64 @@ src/
|
||||
├── hook-manager.ts ← Lifecycle hooks from agents.md
|
||||
├── bootstrap-manager.ts ← First-run: validates/creates config files
|
||||
├── channel-queue.ts ← Per-channel sequential processing
|
||||
└── shutdown-handler.ts ← SIGTERM/SIGINT → graceful shutdown
|
||||
├── shutdown-handler.ts ← SIGTERM/SIGINT → graceful shutdown
|
||||
├── dashboard-server.ts ← Embedded HTTP server for Mission Control dashboard
|
||||
├── dashboard-dev.ts ← Standalone dashboard dev server (mock data)
|
||||
├── activity-log.ts ← In-memory + file-persisted activity event log
|
||||
└── local-store.ts ← JSON file persistence layer (brain, tasks, content)
|
||||
|
||||
dashboard/ ← Mission Control frontend (served by dashboard-server)
|
||||
├── index.html ← SPA shell with sidebar navigation
|
||||
├── styles.css ← Dark-theme design system
|
||||
└── app.js ← Client-side routing, API calls, rendering
|
||||
|
||||
config/
|
||||
├── identity.md ← Agent name, role, specialization
|
||||
├── soul.md ← Personality, tone, values
|
||||
├── agents.md ← Rules, cron jobs, hooks
|
||||
├── user.md ← Human's info and preferences
|
||||
├── memory.md ← Long-term memory (agent-writable)
|
||||
├── tools.md ← Tool configs and notes
|
||||
├── heartbeat.md ← Proactive check definitions
|
||||
├── boot.md ← Bootstrap parameters (optional)
|
||||
└── sessions.json ← Channel → session ID map (auto-generated)
|
||||
├── CLAUDE.md ← Persona: identity, personality, user context, tools
|
||||
├── agents.md ← Rules, cron jobs, hooks (parsed at startup)
|
||||
├── heartbeat.md ← Heartbeat checks (parsed at startup)
|
||||
├── memory.md ← Long-term memory (agent-writable, auto-created)
|
||||
├── sessions.json ← Channel → session ID map (auto-generated)
|
||||
├── brain.json ← Second Brain facts (auto, managed by dashboard)
|
||||
├── tasks.json ← Productivity tasks (auto, managed by dashboard)
|
||||
├── content-items.json ← Content Intel items (auto, managed by dashboard)
|
||||
├── activity-log.json ← Persisted activity log (auto)
|
||||
├── bot-config.json ← Dashboard bot config (auto)
|
||||
├── messages/ ← Message history (auto)
|
||||
├── conversations/ ← Conversation archives (auto)
|
||||
├── ipc/outbound/ ← Proactive message queue (auto)
|
||||
└── skills/ ← Skill definitions
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dashboard Flow
|
||||
|
||||
The Mission Control dashboard runs as an embedded HTTP server inside the gateway process.
|
||||
|
||||
### Real-Time Activity (SSE)
|
||||
|
||||
```
|
||||
Agent Runtime processes event
|
||||
→ ActivityLog.record() called
|
||||
→ Entry added to in-memory buffer (capped at 200)
|
||||
→ Entry persisted to config/activity-log.json (capped at 2000)
|
||||
→ SSE broadcast to all connected dashboard clients
|
||||
→ Dashboard UI updates live activity feed
|
||||
```
|
||||
|
||||
### Data Persistence Flow
|
||||
|
||||
```
|
||||
Dashboard UI (browser)
|
||||
→ POST /api/brain { content, type, category, tags }
|
||||
→ DashboardServer routes to LocalStores
|
||||
→ JsonStore.append() adds entry with generated ID
|
||||
→ Debounced write to config/brain.json (300ms)
|
||||
→ Response with updated data
|
||||
```
|
||||
|
||||
All persistent stores use the same pattern:
|
||||
- **Read:** Load from JSON file on startup, serve from memory
|
||||
- **Write:** Append/update in memory, debounced flush to disk
|
||||
- **Shutdown:** `flushAll()` called to ensure all pending writes complete
|
||||
|
||||
|
||||
Reference in New Issue
Block a user