agent-workflow 0.7.8 includes the first version of its trusted in-process plugin host. The boundary exists to keep optional capabilities modular; it is not a sandbox and installing or enabling a plugin does not grant workflow, permission, review, or acceptance authority.
Plugins are Python distributions advertising the agent_workflow.plugins entry-point group. Discovery reads package metadata only. A candidate module is imported only when its entry-point name appears in configuration:
[plugins]
enabled = ["agent-workflow-spec"]Every configured plugin is required to be installed, uniquely discoverable, and compatible with the current plugin API. A failure stops command registration before a parser or registry is exposed. Use the global recovery option to start the core product without importing configured plugins:
agent-workflow --no-plugins plugins list
agent-workflow --no-plugins doctoragent-workflow plugins list --json reports discovered distribution metadata, configured enablement, load state, and suppression state.
An entry point exports either a PluginDescriptor or a zero-argument callable returning one. Commands are declared without mutating a core global registry:
from agent_workflow.plugin_api import (
PluginCommand,
PluginDescriptor,
PluginPackageResource,
)
def configure(parser):
parser.add_argument("spec")
def execute(args, context):
return {"spec": args.spec, "state_root": str(context.settings.state_root)}
def plugin():
return PluginDescriptor(
name="agent-workflow-spec",
version="0.1.0",
commands=(
PluginCommand(
name="spec",
summary="author and compile implementation specifications",
configure=configure,
execute=execute,
),
),
resources=("agent-workflow-spec://capabilities",),
package_resources=(
PluginPackageResource(
kind="schema",
identifier="agent-workflow-spec/spec/v1",
package="agent_workflow_spec",
path="schemas/spec-v1.json",
sha256="<lowercase SHA-256 of installed bytes>",
),
PluginPackageResource(
kind="asset",
identifier="agent-workflow-spec/templates/v1",
package="agent_workflow_spec",
path="templates/default.md",
sha256="<lowercase SHA-256 of installed bytes>",
),
),
)The host stages all enabled descriptors, checks API versions and duplicate plugin/command/schema/asset/resource identifiers, resolves declared package files through importlib.resources, verifies normalized relative paths and exact SHA-256 digests, and commits one immutable registry only after the complete set passes. Plugin-owned top-level commands and validated package-resource provenance are included in the parser-derived command catalog and orchestrator command cards. Consumers read activated bytes through PluginRegistry.read_package_resource(kind, identifier); arbitrary host paths are never accepted.
Version 1 supports:
- import-free candidate discovery;
- explicit configured enablement;
- strict API compatibility and collision checks;
- atomic command registration;
- top-level plugin command groups;
- schema, asset, and resource identifiers reserved in the registry;
- digest-bound schema and asset files resolved from installed packages;
- traversal, missing-file, collision, and tamper failures before registry activation;
- read-only activated bytes addressed by exact logical identifier;
- installed-distribution and package-resource provenance in command catalogs;
- a
--no-pluginsrecovery path.
PLUG-001 implementation is complete and awaits independent MOD-GATE-1 review. A general hook framework remains deferred until multiple real plugins require ordered one-to-many hooks. Package-resource activation does not parse schemas, execute templates, or grant authority; feature code must still route all authority-bearing work through core services.