Plugin System v2

Plugins &
Extensions

Drop a PLUGIN.md directory into ~/.furoshiki/plugins/ and Furoshiki grows new capabilities — scheduled jobs, conversation context, callable tools, and more. All safely isolated from core internals.

Build a plugin API reference →
Getting started

Install Furoshiki from PyPI

Python 3.11+, an OpenRouter API key, and a few CLI steps. No git clone required.

furoshiki onboard furoshiki db migrate furoshiki start

Three extension layers

Specdocs/PLUGIN-SPEC.md (v2+): hook arguments, env vars, permissions.   APIplugin-api.html / docs/PLUGIN-API.md.   How-toplugin-how-to.html / docs/PLUGIN-HOW-TO.md.
Brain
Scheduler
brain.py runs scheduler.py against memory/schedules.json. No OS cron required. Plugin cron hooks merge in via furoshiki plugins reload.
Plugins
Runtime plugins
PLUGIN.md directories under $FUROSHIKI_DATA/plugins/. Seven hook types. All hooks run as isolated subprocesses with scoped API tokens — no direct DB access. Plugins may declare settings_schema for JSON settings, or operators can edit the full PLUGIN.md in Dashboard → Work & extensions → Plugins (manifest knobs, hooks, permissions).
Extensions
Extensions hub
Delegation jobs, repair jobs, and skill proposals tracked in extension_work_items. Dashboard → Extensions + furoshiki extensions ….

Seven ways to extend

Each hook fires at a specific point in Furoshiki's lifecycle. Declare hooks in your PLUGIN.md and provide a Python entry script. Hooks run as subprocesses; the correct --mode and argument paths are passed automatically. Plugins with a settings_schema block get a live settings form in Dashboard → Work & extensions → Plugins — no hand-editing JSON required.

Scheduled
cron
UTC five-field cron expression. Brain merges into schedules.json on reload and runs via run_plugin_scheduled.py. Stdout mind_queue_item lines are mediated.
Conversation
pre_conversation
Fires during session-context assembly. Return {"label","content","priority"} to inject context the model will see.
Conversation
post_conversation
Fires 20 min after a session ends (alongside post-conversation reflection). Fire-and-forget; stdout ignored.
Reflection
pre_reflection
Fires before any reflection LLM run. Return {"label","content"} to add context to the reflection prompt.
Reflection
post_reflection
Fires after a reflection completes. Receives the full reflection output JSON for logging, alerting, or follow-up queuing.
LLM tool
callable
Registered as an OpenRouter tool the model can invoke on demand. Declare name, description, when_to_use, and args_schema. Return any JSON object.
Signals
post_inbound_signals
After conversation-mode classification. Optionally override conversation_mode or append a prompt_addendum.

Plugin execution pipeline

Every hook — scheduled or event-driven — goes through the same isolation path. The core never imports plugin code; the plugin never imports core modules.

Trigger
Brain / listener
scheduler tick or event
calls
Loader
plugin_loader.py
issue token · strip PYTHONPATH
subprocess + token
Plugin
hook.py
no core imports allowed
HTTP · Bearer
Plugin API
/api/v1/plugin/*
validates scope per token
mediated write
Storage
mind_queue / Chroma
longmemory.sqlite

Token lifecycle

1
Token issued
plugin_loader calls issue_plugin_token(name, permissions, timeout_sec) → writes row to plugin_tokens table with TTL = hook timeout + 30 s.
2
Injected into subprocess
FUROSHIKI_PLUGIN_API_TOKEN and FUROSHIKI_PLUGIN_API_URL are set in the subprocess environment. FUROSHIKI_API_TOKEN (admin key) is explicitly removed.
3
Plugin runs
Every API call validates the token against plugin_tokens and checks the declared permission scope. Write endpoints (mind-queue, llm) require the matching permission flag.
4
Stdout mediation
After the subprocess exits, loader scans stdout for {"type":"mind_queue_item",...} NDJSON lines and inserts them into mind_queue if output_mind_queue permission is declared.
5
Token revoked
revoke_plugin_token(token) deletes the row. Expired tokens are also pruned automatically on each new issue.

Isolation model

Plugins are guests. The system is designed so that a buggy or malicious plugin cannot corrupt Furoshiki's state, impersonate the operator, or bypass the voice dispatch gate.

🔒
No core imports
scripts/ is stripped from PYTHONPATH. Plugins cannot import db, import llm, or reach any Furoshiki internal module. Only the Plugin SDK and the plugin's own scripts/ are available.
🎫
Scoped tokens
Each invocation gets a unique Bearer token valid for one hook run. The token carries the declared permissions; the API enforces them. The admin key is never passed to plugin subprocesses.
🛡️
Static pre-screen
LLM-generated plugins are rejected before review if they contain sqlite3.connect(), longmemory.sqlite, FUROSHIKI_DATA path constructions, or from db import.
Plugin-owned SQLite OK
Plugins may use sqlite3 for their own data under $FUROSHIKI_PLUGIN_DATA_DIR. The restriction applies only to Furoshiki's own databases.
📬
Mediated writes
mind_queue inserts go through POST /api/v1/plugin/mind-queue. The loader also mediates mind_queue_item NDJSON in stdout. Direct DB writes are structurally impossible.
🔔
No rogue Telegram
Proactive messages must go through mind_queuevoice_dispatcher. Plugins cannot send Telegram messages directly or bypass the voice dispatch gate.
🎚️
Priority capped
Plugins declare max_priority in their manifest. The operator sets a global cap in operator_settings.json (default: critical). Both caps are enforced; the more restrictive wins. bypass (skips DND, fires pulse immediately) requires explicit operator opt-in.

Plugin notification priority tiers

PriorityDNDOther gatesDispatch latency
bypassIgnoredAll skippedImmediateoutreach_trigger.fire_now() on insert
criticalRespectedAll skipped≤ 5 min (next pulse)
urgentRespectedStandard cooldowns≤ 5 min
highRespectedStandard cooldowns≤ 5 min
normalRespectedStandard cooldownsvaries
lowRespectedStandard cooldownsvaries

bypass and critical items are never expired by topic dedup or the 24-hour background sweep. Use bypass only for genuine emergencies. Declare max_priority: bypass in the manifest and ensure the operator has set plugins.max_priority_cap: bypass.

Observability

Hook and callable activity is written to Logs → Extensions in the operator dashboard (log_store rows with structured context: discovery, hook phases, subprocess invoke start/end, callable match attempts and outcomes). Process logs still include grep-friendly plugin_pipeline step=… lines. Callable manifest discovery is throttled when unchanged so high-frequency callers do not flood the log DB.

Permissions reference

Declare only the permissions your plugin actually needs. The Plugin API validates every write operation against the token's declared scope.

Permission flagUnlocks
read_user_facts: trueSemantic search over ChromaDB user_facts via GET /api/v1/plugin/user-facts
output_mind_queue: trueInsert into mind_queue via API (POST /api/v1/plugin/mind-queue) or mediated stdout. Priority is capped by the plugin's declared max_priority (default high) and the operator global cap (default critical). bypass-priority items trigger an immediate pulse via outreach_trigger.fire_now().
llm: trueLLM calls via POST /api/v1/plugin/llm — budget enforced, source tagged
network: trueOutbound network from the plugin subprocess (declared intent only; not enforced at kernel level)
openrouter_web: trueSets FUROSHIKI_PLUGIN_OPENROUTER_WEB=1 so the LLM endpoint may use OpenRouter :online

All read endpoints (/state, /emotion-history, /recent-memories, /budget-summary) and /log are available to any valid plugin token regardless of declared permissions.

Delegations, repairs & proposals

extension_work_items is the unified SQLite view for all agentic work. Installing a plugin from a proposal is distinct — furoshiki plugins apply moves files into plugins/; the queue tracks progress and phases.

delegation_jobs
research · code · self-mod
repair_jobs
doctor watchdog
skill proposals
self_modification_dispatcher
extension_work_items
Dashboard → Extensions
furoshiki extensions …
furoshiki plugins apply

Quick reference

CommandWhat it does
furoshiki plugins listList installed plugins and their active status
furoshiki plugins show <name>Full manifest + hook list for one plugin
furoshiki plugins apply <proposal>Install an approved plugin proposal into ~/.furoshiki/plugins/
furoshiki plugins enable/disable <name>Toggle active: true/false in PLUGIN.md
furoshiki plugins reloadMerge cron hooks into schedules.json — run after any PLUGIN.md edit
furoshiki plugins test-suiteRun hook-test-suite plugin against all 7 hook types, print pass/fail
furoshiki plugins delete <name> --yesPermanently remove a plugin directory
furoshiki extensions plugins …Same subcommands under the extensions hub alias
furoshiki startStart Brain + Doctor (scheduler + API server live)

Detailed guides