Creating a Plugin
Plugins add content processors and agent tools to EchOS without modifying core code. Each plugin is a separate workspace package inplugins/.
Steps
Write the processor
Create Security rules (non-negotiable):
plugins/my-plugin/src/processor.ts — the logic that fetches/transforms external content:- Always use
validateUrl()before fetching any URL - Always use
sanitizeHtml()on external content - Never use
eval(),Function(), orvm - Never log secrets
Create the agent tool
Create New in this example: AI-powered auto-categorization support using the
plugins/my-plugin/src/tool.ts — defines the tool the LLM agent can call:categorizeContent function from @echos/core. When autoCategorize=true, the plugin will automatically extract category, tags, and optionally gist/summary/key points from the content.PluginContext API
Every plugin receives aPluginContext with:
| Property | Type | Description |
|---|---|---|
sqlite | SqliteStorage | Metadata DB (upsert, query, FTS5 search) |
markdown | MarkdownStorage | Markdown file storage (save, read, delete) |
vectorDb | VectorStorage | Vector embeddings (upsert, search) |
generateEmbedding | (text: string) => Promise<number[]> | Generate embedding vectors |
logger | Logger (Pino) | Structured logger |
config | Record<string, unknown> | App config (API keys, etc.) |
AI Categorization
Plugins can use the built-in categorization service from@echos/core:
- Uses
streamSimple+parseStreamingJsonto stream the LLM response progressively - Fires
onProgressas fields resolve: category → tags → gist (full mode) - Handles errors with safe defaults (fallback to ‘uncategorized’)
- Respects content length limits (5000 chars for lightweight, 10000 for full)
- Is safe to call without
onProgress— callers that don’t need streaming omit the last argument
Existing plugins
| Plugin | Package | Description |
|---|---|---|
| YouTube | @echos/plugin-youtube | Transcript extraction + Whisper fallback |
| Article | @echos/plugin-article | Web article extraction via Readability + DOMPurify |
@echos/plugin-twitter | Tweet/thread extraction via FxTwitter API | |
| Image | @echos/plugin-image | Image storage with metadata extraction (Sharp) |
| Resurface | @echos/plugin-resurface | Knowledge resurfacing via spaced repetition and on-this-day discovery |
| Journal | @echos/plugin-journal | Dedicated journaling, AI reflection, and daily prompts |
Twitter Plugin
The Twitter plugin (@echos/plugin-twitter) provides the save_tweet tool for saving tweets and threads from Twitter/X.
Features:
- Save individual tweets with full metadata (text, author, engagement stats, media URLs)
- Automatic thread unrolling — reply chains by the same author are merged into a clean article
- Quote tweet extraction
- Media URLs referenced as markdown links (images and videos)
- Optional AI categorization for automatic tagging
- No API key required — uses the free FxTwitter API (
api.fxtwitter.com)
twitter.com/<user>/status/<id>x.com/<user>/status/<id>mobile.twitter.com/<user>/status/<id>fxtwitter.com/<user>/status/<id>vxtwitter.com/<user>/status/<id>- All formats support query parameters (
?s=20,?t=...)
- Walks up the reply chain via
replying_to_statusto find earlier tweets by the same author - Stops when a different author is reached or the chain exceeds 25 tweets
- Merges thread tweets into a clean article format, stripping self-reply @mentions
- Single tweets (no thread) are saved in blockquote format with engagement stats
Image Plugin
The image plugin (@echos/plugin-image) provides the save_image tool for storing and organizing images in the knowledge base.
Features:
- Download images from URLs or accept base64 data
- Extract metadata: dimensions, format, file size, EXIF
- Store original files in
knowledge/image/{category}/ - Create searchable markdown notes with image references
- Optional AI categorization for automatic tagging
- JPEG, PNG, GIF, WebP, AVIF, TIFF, BMP
- Maximum size: 20MB
processImage):
- Validates image format and size
- Extracts metadata using Sharp library
- Generates content-based filename hash
- Returns structured metadata and buffer
- Original file:
knowledge/image/{category}/{hash}.{ext} - Markdown note:
knowledge/note/{category}/{date}-{slug}.md - Embedded reference:

- Automatic photo handler via
bot.on('message:photo') - Downloads from Telegram API
- Passes URL to save_image tool
- Supports captions for context
Resurface Plugin
The resurface plugin (@echos/plugin-resurface) brings forgotten knowledge back to the surface through spaced repetition and serendipitous discovery. It provides a get_resurfaced agent tool and a daily scheduled job that broadcasts notes via Telegram.
Features:
- Three resurfacing strategies:
forgotten— notes you haven’t seen in 7+ days, oldest first (classic spaced repetition)on_this_day— notes created on the same calendar date in a prior yearmix(default) — blend of both for maximum serendipityrandom— random sampling of un-recently-surfaced notes (supported by both theget_resurfacedtool and scheduler config)
- Tracks a
last_surfacedtimestamp per note in SQLite — never resurfaces the same note twice within 7 days - Daily broadcast job sends 2–3 notes to Telegram with emoji labels (🔮 Resurfaced, 📅 On this day, 🎲 Discovery)
- On-demand access via the
get_resurfacedtool
- “surprise me”
- “what did I save before?”
- “on this day”
- “rediscover something”
- “show me something old”
- “random note”
“Schedule a daily knowledge resurfacing at 9am.”The agent will create a
resurface schedule with cron 0 9 * * *. You can customize it:
| Key | Type | Default | Description |
|---|---|---|---|
mode | 'forgotten' | 'on_this_day' | 'random' | 'mix' | 'mix' | Resurfacing strategy |
limit | number | 3 | Number of notes to broadcast (max 10) |
Journal Plugin
The journal plugin (@echos/plugin-journal) provides a dedicated journaling experience with two agent tools and an optional daily prompt job.
Features:
- Dedicated
journaltool for creating journal/diary entries (replacescreate_note(type="journal")) - AI-powered
reflecttool that synthesizes journal entries over a time period - Optional
journal_promptscheduled job for daily journaling nudges via Telegram
type: 'journal' and status: 'read'. After creating, the agent always calls categorize_note for automatic tagging.
Tool: reflect
- Fetches journal entries within the date range (up to 50 entries)
- Spawns a sub-agent to synthesize patterns, mood trends, key themes, and insights
- Returns a warm, structured reflection with actionable suggestions
- Validates date ranges (max 365 days lookback)
- “reflect on my journal”
- “weekly journal review”
- “how has my week been?”
- “mood summary”
- “look back at my journaling”
“Schedule a daily journal prompt at 9pm.”The agent will create a
journal_prompt schedule with cron 0 21 * * *.
Scheduler config options:
| Key | Type | Default | Description |
|---|---|---|---|
prompt | string | Built-in journaling nudge | Custom prompt text (max 1000 chars) |