Compact reference for Sapphire's own use when building plugins. For simple tool creation (tool_save/tool_load), see the TOOLMAKER doc — this covers full plugin development.
When creating or modifying plugins:
- Plugin = folder in
plugins/{name}/withplugin.jsonmanifest plugin.jsonrequiresnamefield, everything else optional- Hooks = Python functions receiving mutable
HookEventobject - Tools =
TOOLSlist +execute(function_name, arguments, config)returning(str, bool) - Tool schema supports
is_local(bool or"endpoint") andnetwork: trueflags - Voice commands = pre_chat hooks with trigger matching,
bypass_llm: truefor instant response - Routes = custom HTTP endpoints at
/api/plugin/{name}/{path}, handler receives(path_params, body, settings), auth+CSRF+rate-limit enforced by framework - Schedule = cron tasks calling
run(event)handler, event hassystem,config,task,plugin_state - Web settings =
web/index.jsusingregisterPluginSettings(), served at/plugin-web/{name}/ - Plugin scripts =
web/main.jsauto-loaded on app startup, listen forsapphire:tool_startDOM events - State =
plugin_loader.get_plugin_state(name)for persistent key-value storage - System access =
event.metadata.get("system")inpre_chat,post_chat,pre_executehooks prompt_inject,post_execute,pre_ttsdo NOT get system metadata — onlyconfig- System gives access to:
tts(voice/speed/pitch/speak/stop),toggle_stt(),toggle_wakeword(),llm_chat(chat/history/prompt),function_manager(tools/scopes) - Enable/disable live via
PUT /api/webui/plugins/toggle/{name} - All 10 hooks:
post_stt,pre_chat,prompt_inject,post_llm,post_chat,pre_execute,post_execute,pre_tts,post_tts,on_wake post_sttfires only for voice input (after STT transcription, before chat pipeline)post_llmfires after LLM response, before history save + TTS — mutateresponseto filter/translate/stylepost_ttsfires after playback completes or is stopped (daemon thread, observational)on_wakefires when wakeword detected, before recording starts (notification only, must return fast)- Error isolation: exceptions logged and skipped, never crash pipeline
- Signing: ed25519 signatures in
plugin.sig, tampered = always blocked, unsigned = blocked unless sideloading enabled - Settings stored at
user/webui/plugins/{name}.json, read viaGET /api/webui/plugins/{name}/settings - Settings files are in
user/(gitignored) — never tracked - Multi-account tools use ContextVar scopes:
scope_email,scope_bitcoin,scope_knowledge, etc. - Web UI modules available:
plugin-registry.js,plugins-api.js,toast.js,modal.js,danger-confirm.js,fetch.js - CSS variables for theming:
--bg,--text,--border,--trim,--success,--error, etc. - Always guard system access with
hasattr()checks — subsystems may be None if disabled