Orchestro plugins are local Python files that register hook handlers into the run and tool lifecycle.
- What A Plugin Is
- Where Plugins Live
- Plugin File Shape
- Available Hooks
- Error Handling
- Example Plugin
- Operational Notes
A plugin is a Python file loaded from the Orchestro data directory. It can:
- register hook handlers
- inspect or annotate lifecycle context
- abort or modify a flow through hook return values
The current implementation lives in src/orchestro/plugins.py.
By default, plugins live under:
.orchestro/plugins/
If ORCHESTRO_HOME is set, the plugin directory becomes:
$ORCHESTRO_HOME/plugins/
Each plugin is a .py file. Files starting with _ are ignored.
Expected pieces:
- optional
METADATA - optional
register(hooks)function
METADATA should be a PluginMetadata instance. register(hooks) receives the shared HookRunner.
Defined hooks:
pre_runpost_runpre_toolpost_toolon_failureon_plan_step
Handlers can return:
CONTINUEHookResult("abort", ...)HookResult("modify", data={...})
modify updates the shared context dict passed through the hook runner.
Plugin errors do not currently crash the whole app by default. They are captured into:
PluginManager.load_errorsHookRunner.last_errors
You can inspect them with:
orchestro pluginsGET /plugins
This means you should treat plugin code as production code anyway. Silent behavior changes are worse than obvious failures.
from __future__ import annotations
from orchestro.plugins import CONTINUE, HookResult, PluginMetadata
METADATA = PluginMetadata(
name="safety-tag",
version="0.1.0",
description="Annotates run context with a local tag.",
)
def _pre_run(context: dict) -> HookResult:
context["local_tag"] = "daily-driver"
return CONTINUE
def _on_failure(context: dict) -> HookResult:
context["plugin_failure_seen"] = True
return CONTINUE
def register(hooks) -> None:
hooks.on("pre_run", _pre_run, plugin_name="safety-tag")
hooks.on("on_failure", _on_failure, plugin_name="safety-tag")Place that in .orchestro/plugins/safety_tag.py, then run:
orchestro plugins
curl http://127.0.0.1:8765/plugins- Keep plugin side effects explicit and local.
- Avoid long-running network work in hooks unless you want that latency in the core run path.
- Prefer annotation and validation over hidden mutation.
- If a plugin changes operator-visible behavior, add a note in Examples or the relevant feature doc.
For related integration surfaces, see MCP, Shell Mode, and API Reference.