Architecture
This page describes how xopc is structured and how the main pieces fit together.
System Architecture
┌─────────────────────────────────────────────────────────────┐
│ xopc │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ CLI Layer │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ setup │ │ onboard │ │ agent │ │ gateway │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Core │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ AgentService │ │ │
│ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │
│ │ │ │ Prompt │ │ Memory │ │ Skills │ │ │ │
│ │ │ │ Builder │ │ Search │ │ │ │ │ │
│ │ │ └─────────┘ └─────────┘ └─────────┘ │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Providers │ │
│ │ @mariozechner/pi-ai (20+ providers) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
└────────────────────────────┼────────────────────────────────┘
│
┌────────────────────┼────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Telegram │ │ Cron │ │ Gateway │
│ Channel │ │ Scheduler│ │ API │
└──────────┘ └──────────┘ └──────────┘Project Structure
src/
├── agent/ # Core agent logic (pi-agent-core based)
│ ├── service.ts # Main AgentService class
│ ├── memory/ # Curated store, MemoryManager, prefetch; session transcripts live under session/
│ ├── prompt/ # Prompt builder system
│ ├── tools/ # Built-in tools (Typebox schemas)
│ └── progress.ts # Progress feedback system
├── infra/
│ └── bus/ # Message bus primitives (queue, etc.)
├── channels/ # Channel integrations (ChannelPlugin + manager)
│ ├── plugin-types.ts # ChannelPlugin interface & adapters
│ ├── manager.ts # Channel lifecycle manager
│ ├── plugins/
│ │ ├── bundled.ts # Built-in workspace plugins (Telegram)
│ │ ├── registry.ts # Plugin registry / lookup
│ │ └── types.*.ts # Registry type helpers
│ ├── telegram/
│ │ └── index.ts # Re-exports from @xopcai/xopc-extension-telegram (compat)
│ ├── outbound/ # Outbound delivery pipeline
│ ├── security.ts # Access control helpers
│ ├── draft-stream.ts # Streaming message preview
│ └── format.ts # Markdown to HTML formatter
├── extensions/ # Extension runtime (loader, hooks); `sdk/` → @xopcai/xopc/extension-sdk
├── routing/ # Session keys, bindings, route resolution
├── acp/ # Agent Control Protocol (optional multi-runtime bridge)
├── cli/ # CLI commands with self-registration
│ ├── commands/ # Individual command modules
│ ├── registry.ts # Command registration system
│ └── index.ts # CLI entry point
├── config/ # Configuration management (Zod schemas)
├── cron/ # Scheduled tasks
├── gateway/ # HTTP/WebSocket gateway server
├── heartbeat/ # Proactive monitoring
├── providers/ # LLM provider registry (pi-ai wrapper)
├── session/ # Conversation session management
├── types/ # Shared TypeScript types
└── utils/ # Shared utilities
├── logger.ts # Logging barrel → `logger/` (context, log-store, …)
└── helpers.ts # Misc helpers
web/ # Gateway console SPA (React + Vite + Tailwind v4)
└── src/ # App source; production build → dist/gateway/static/root
extensions/
└── telegram/ # Workspace package: Telegram channel (@xopcai/xopc-extension-telegram)State directory & workspace on disk
Runtime data (config, credentials, per-agent sessions, the Markdown workspace used as tool cwd and user content, and per-agent bootstrap persona Markdown under agents/<id>/bootstrap/) lives outside the git repo under the state directory (default ~/.xopc). For a filesystem map (bootstrap, agent home, Markdown workspace), see On-disk layout. For setup, env overrides, and how agents.defaults.workspace relates to default paths, see State directory & workspace layout.
Core Modules
Agent Service (src/agent/service.ts)
AgentService is the core orchestrator responsible for:
- Message Processing - Receive user messages, call LLM, handle tool calls
- Prompt Building - Build system prompt from SOUL.md/USER.md/AGENTS.md/TOOLS.md
- Memory - Session message storage and compaction (
src/session/); curated agent-homememories/, pluggable memory providers, and tools (src/agent/memory/) - Tool Execution - Unified execution of built-in tools + extension tools
- Progress Feedback - Real-time updates for long-running tasks
Prompt Builder (src/agent/prompt/)
Modular prompt building system:
src/agent/prompt/
├── index.ts # PromptBuilder - main builder
├── modes.ts # Prompt modes (full/minimal/none)
├── memory/
│ └── index.ts # memory_search, memory_get tools
└── skills.ts # Skills loading systemPrompt Sections:
| Section | Description |
|---|---|
| Identity | "You are a personal assistant running in xopc" |
| Version | xopc version info |
| Tool Call Style | Tool calling style (verbose/brief/minimal) |
| Safety | Safety principles |
| Memory | memory_search/memory_get usage guide |
| Workspace | Working directory |
| Skills | Skills system |
| Messaging | Message sending |
| Heartbeats | Heartbeat monitoring |
| Runtime | Runtime info |
Built-in Tools (src/agent/tools/)
| Tool | Name | Description |
|---|---|---|
| 📄 Read | read_file | Read file content (truncated to 50KB/500 lines) |
| ✍️ Write | write_file | Create or overwrite file |
| ✏️ Edit | edit_file | Replace text in file |
| 📂 List | list_dir | List directory contents |
| 💻 Shell | shell | Execute shell commands (5min timeout) |
| 🔍 Search | grep | Text search in files |
| 📄 Find | find | Find files by pattern |
| 🔍 Web Search | web_search | Web search via Brave Search |
| 📄 Web Fetch | web_fetch | Fetch web page content |
| 📤 Message | send_message | Send messages to channels |
| 🔍 Memory Search | memory_search | Search memory/*.md snippets in the workspace |
| 📄 Memory Get | memory_get | Read memory snippets |
| 🧠 Curated memory | curated_memory | Edit bounded entries in agent-home memories/ (when enabled) |
| 🔎 Session search | session_search | Search other sessions’ transcripts (when session store is wired) |
Progress Feedback (src/agent/progress.ts)
Real-time progress tracking for long-running tasks:
// Tool execution feedback
manager.onToolStart('read_file', { path: '/file.txt' });
// → e.g. 📖 Reading...
// Heartbeat for tasks > 30s
manager.onHeartbeat(elapsed, stage);
// → e.g. ⏱️ Running for 45sProgress Stages:
| Stage | Emoji | Trigger |
|---|---|---|
| thinking | 🤔 | LLM reasoning |
| searching | 🔍 | web_search, grep |
| reading | 📖 | read_file |
| writing | ✍️ | write_file, edit_file |
| executing | ⚙️ | shell commands |
| analyzing | 📊 | Data analysis |
Session transcripts & compaction (src/session/)
Session message persistence, search index helpers for session_search, and compaction live under src/session/ (on disk: agents/<id>/sessions/, not the Markdown workspace tree).
Curated & pluggable memory (src/agent/memory/)
src/agent/memory/
├── builtin-memory-store.ts # agent home memories/MEMORY.md + USER.md (bounded, §-delimited)
├── manager.ts # MemoryManager — providers, prefetch, sync, onMemoryWrite
├── create-memory-manager.ts # Wires builtin + optional stub from config
├── inject-prefetch.ts # Prefix user message with <memory-context> when configured
└── … # context-fence, providers, plugin discoveryConfigured under agents.defaults.memory (Configuration). When the whole subsystem is disabled, curated tools and external prefetch are off.
Compaction (conversation context) is configured under agents.defaults.compaction / pruning in the same defaults object—not the curated memory files above.
Channel plugins (src/channels/)
Channels are implemented as ChannelPlugin instances. The core ChannelManager loads plugins from bundledChannelPlugins in src/channels/plugins/bundled.ts (Telegram is provided by the workspace package extensions/telegram). Each plugin exposes init / start / outbound delivery and optional adapters (config, security, streaming, gateway, etc.).
Features (Telegram):
- Multi-account support
- Access control (allowlist, group policies)
- Streaming message preview
- Voice messages (STT/TTS)
- Document/file support
Imports (extension or core code):
import { telegramPlugin } from '@xopcai/xopc-extension-telegram';
// Re-exported for stable paths: import { telegramPlugin } from './channels/telegram/index.js';Extension System (src/extensions/)
src/extensions/
├── types.ts # Extension type definitions
├── api.ts # Extension API
├── loader.ts # Extension loader
├── hooks.ts # Hook system
└── index.ts # ExportsHook Lifecycle:
before_agent_start → agent_end → message_received →
before_tool_call → after_tool_call → message_sending → session_endThree-tier Storage:
- Workspace (
workspace/.extensions/) - Project-private - Global (
~/.xopc/extensions/) - User-level shared - Bundled (
xopc/extensions/) - Built-in
Data Flow
Conversation Flow
User (Telegram/Gateway/CLI)
│
▼
┌─────────────────────┐
│ Channel Handler │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ AgentService │
│ ┌───────────────┐ │
│ │ Load Bootstrap │ │ ← SOUL.md, USER.md, TOOLS.md, AGENTS.md
│ └───────┬───────┘ │
│ ▼ │
│ ┌───────────────┐ │
│ │ Build Prompt │ │ ← bootstrap files, curated snapshot, memory_search / memory_get, …
│ └───────┬───────┘ │
│ ▼ │
│ ┌───────────────┐ │
│ │ LLM (pi-ai) │ │
│ └───────┬───────┘ │
│ ▼ │
│ ┌───────────────┐ │
│ │ Execute Tools │ │ ← Tools + Extensions
│ │ + Progress │ │ ← Progress feedback
│ └───────┬───────┘ │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Response │
└──────────┬──────────┘
│
▼
User Reply / Channel ResponseCLI Command Registration
xopc uses self-registration pattern:
// src/cli/commands/mycommand.ts
import { register } from '../registry.js';
function createMyCommand(ctx: CLIContext): Command {
return new Command('mycommand')
.description('My command')
.action(async () => { ... });
}
register({
id: 'mycommand',
factory: createMyCommand,
metadata: { category: 'utility' },
});Tech Stack
| Layer | Technology |
|---|---|
| Runtime | Node.js 22+ |
| Language | TypeScript 5.x |
| LLM SDK | @mariozechner/pi-ai |
| Agent Framework | @mariozechner/pi-agent-core |
| CLI | Commander.js |
| Validation | Zod (config) + TypeBox (tools) |
| Logging | Pino |
| Cron | node-cron |
| HTTP Server | Hono |
| Web UI | React + Vite + Tailwind v4 (gateway console in web/) |
| Testing | Vitest |
Extension Points
Adding New Tools
- Create
src/agent/tools/<name>.ts - Implement
AgentToolinterface with Typebox schema - Export from
src/agent/tools/index.ts - Add to tools array in
AgentService
Adding Hooks
api.registerHook('before_tool_call', async (event, ctx) => {
// Intercept tool calls
return { modified: true };
});Adding channel plugins
- Implement
ChannelPluginin a package or underextensions/<name>/(seesrc/channels/plugin-types.tsanddefineChannelPluginEntryin@xopcai/xopc/extension-sdk). - Export the plugin object and add it to
bundledChannelPluginsinsrc/channels/plugins/bundled.tsif it should ship with the core binary. - Ensure
ChannelManagerstartup loads your plugin (bundled plugins are registered automatically when listed inbundled.ts).