Skip to main content

Plugins

Plugins package governed hook integrations without forking core config. A plugin is a self-contained bundle with:

  • an agentxchain-plugin.json manifest
  • hook executables or HTTP endpoints
  • optional plugin configuration validated at install and upgrade time

Installed plugins are copied into .agentxchain/plugins/, merged into the governed hook config, and then executed by the same hook runner and tamper protections as operator-defined hooks.

Plugin manifest

Every plugin contains an agentxchain-plugin.json manifest at its root:

agentxchain-plugin.json
{
"schema_version": "0.1",
"name": "@agentxchain/plugin-slack-notify",
"version": "0.1.0",
"description": "Posts governed lifecycle notifications to Slack.",
"hooks": {
"after_acceptance": [
{
"name": "slack_notify_acceptance",
"type": "process",
"command": ["node", "./hooks/after-acceptance.js"],
"timeout_ms": 5000,
"mode": "advisory"
}
],
"before_gate": [
{
"name": "slack_notify_gate",
"type": "process",
"command": ["node", "./hooks/before-gate.js"],
"timeout_ms": 5000,
"mode": "advisory"
}
]
},
"config_schema": {
"type": "object",
"properties": {
"webhook_env": {
"type": "string",
"default": "AGENTXCHAIN_SLACK_WEBHOOK_URL"
},
"mention": {
"type": "string"
}
}
}
}

Manifest fields

FieldTypeRequiredDescription
schema_versionstringYesMust be "0.1"
namestringYesPackage-style identifier. Scoped names such as @agentxchain/plugin-slack-notify are supported
versionstringYesSemver version string
descriptionstringNoShort description shown in plugin list output
hooksobjectYesMap of hook phase to hook definition array
config_schemaobjectNoJSON Schema used to validate plugin config during install and upgrade

Hook phases

Plugins register against hook phases, not free-form lifecycle event names:

PhaseWhen it firesBlocking allowed
before_assignmentBefore a turn is assigned to a roleYes
after_dispatchAfter a turn is dispatched to an adapterYes
before_validationBefore a result is validatedYes
after_validationAfter validation completesYes
before_acceptanceBefore a turn is acceptedYes
after_acceptanceAfter acceptance commitsNo
before_gateBefore human gate approval executesYes
on_escalationWhen the run escalatesNo

Hook definition fields

All hooks require:

FieldTypeRequiredDescription
namestringYesUnique hook name within its phase. Must match ^[a-z0-9_-]+$
typestringYesprocess or http
timeout_msintegerYesTimeout in milliseconds, between 100 and 30000
modestringYesblocking or advisory. after_acceptance and on_escalation cannot be blocking
envobjectNoExtra environment variables injected into the hook

process hooks also require command, which must be a non-empty argv array. Relative command tokens such as ./hooks/after-acceptance.js are rewritten during install so they still resolve from the governed project root.

http hooks also require:

FieldTypeRequiredDescription
urlstringYesHTTP or HTTPS endpoint
methodstringYesMust be POST
headersobjectNoHeader values support ${VAR} interpolation from the process environment plus hook.env

Installing plugins

AgentXchain ships with built-in plugins that can be installed by short name:

agentxchain plugin install slack-notify
agentxchain plugin install json-report
agentxchain plugin install github-issues

To see all available built-in plugins:

agentxchain plugin list-available

Other sources

Plugins can also be installed from local directories, archives, or npm packages:

# Local directory
agentxchain plugin install ./path/to/my-plugin

# Archive (.tgz or .tar.gz)
agentxchain plugin install ./downloads/plugin-slack-notify-0.1.0.tar.gz

# npm package
agentxchain plugin install @agentxchain/plugin-slack-notify

The CLI resolves built-in short names first, then local directories, then falls back to npm pack <spec> for tarball-based install.

Install flags

FlagDescription
--config <json>Inline JSON plugin config validated against config_schema
--config-file <path>Read plugin config JSON from a file
-j, --jsonOutput as JSON

--config and --config-file are mutually exclusive.

What happens on install

  1. Manifest validation: the manifest is parsed and validated.
  2. Config validation: if the plugin declares config_schema, the supplied config is validated and stored under plugins.<name>.config.
  3. Duplicate and collision protection: if the plugin name is already installed, install fails. If any hook name collides with an existing hook name in the same phase, install also fails. There is no force override for install.
  4. Command path rewriting: relative process-hook command paths are rewritten to the installed location under .agentxchain/plugins/....
  5. Registration: the plugin is recorded in agentxchain.json under plugins and its hook definitions are merged into the root hooks config.
agentxchain.json (after install)
{
"hooks": {
"after_acceptance": [
{
"name": "slack_notify_acceptance",
"type": "process",
"command": [
"node",
".agentxchain/plugins/agentxchain--plugin-slack-notify--deadbeef/hooks/after-acceptance.js"
],
"timeout_ms": 5000,
"mode": "advisory",
"env": {
"AGENTXCHAIN_PLUGIN_NAME": "@agentxchain/plugin-slack-notify",
"AGENTXCHAIN_PLUGIN_VERSION": "0.1.0",
"AGENTXCHAIN_PLUGIN_CONFIG": "{\"webhook_env\":\"AGENTXCHAIN_SLACK_WEBHOOK_URL\"}"
}
}
]
},
"plugins": {
"@agentxchain/plugin-slack-notify": {
"schema_version": "0.1",
"name": "@agentxchain/plugin-slack-notify",
"version": "0.1.0",
"install_path": ".agentxchain/plugins/agentxchain--plugin-slack-notify--deadbeef",
"source": {
"type": "local_path",
"spec": "./plugins/plugin-slack-notify"
},
"hooks": {
"after_acceptance": ["slack_notify_acceptance"],
"before_gate": ["slack_notify_gate"]
},
"config": {
"webhook_env": "AGENTXCHAIN_SLACK_WEBHOOK_URL"
}
}
}
}

Listing plugins

agentxchain plugin list
FlagDescription
-j, --jsonOutput as JSON

Text output:

Installed plugins: 1
@agentxchain/[email protected]
Path: .agentxchain/plugins/agentxchain--plugin-slack-notify--deadbeef
Source: local_path (./plugins/plugin-slack-notify)
Present: yes
Hooks: after_acceptance: slack_notify_acceptance | before_gate: slack_notify_gate

With --json:

{
"plugins": [
{
"name": "@agentxchain/plugin-slack-notify",
"version": "0.1.0",
"description": "Posts governed lifecycle notifications to a Slack incoming webhook.",
"install_path": ".agentxchain/plugins/agentxchain--plugin-slack-notify--deadbeef",
"source": {
"type": "local_path",
"spec": "./plugins/plugin-slack-notify"
},
"hooks": {
"after_acceptance": ["slack_notify_acceptance"],
"before_gate": ["slack_notify_gate"],
"on_escalation": ["slack_notify_escalation"]
},
"installed": true
}
]
}

Upgrading plugins

agentxchain plugin upgrade @agentxchain/plugin-slack-notify

The optional [source] positional argument specifies a local directory, archive, or npm package to upgrade from. If omitted, the plugin is upgraded from its recorded original source.

FlagDescription
--config <json>Inline JSON plugin config validated against config_schema
--config-file <path>Read plugin config JSON from a file
-j, --jsonOutput as JSON

Upgrade performs an atomic swap:

  1. The replacement source is resolved and validated.
  2. Existing plugin hooks are removed from the in-memory config, then the replacement hooks are merged and validated.
  3. The old install directory is renamed to a rollback path under .agentxchain/plugins/...rollback-<timestamp>.
  4. The new directory is moved into place and agentxchain.json is committed.
  5. If the config write or swap fails, the previous install is restored.

To upgrade from a specific source:

agentxchain plugin upgrade @agentxchain/plugin-slack-notify ./downloads/plugin-slack-notify-0.2.0.tar.gz

Removing plugins

agentxchain plugin remove @agentxchain/plugin-slack-notify
FlagDescription
-j, --jsonOutput as JSON

Removal deletes the plugin metadata entry, strips only that plugin's recorded hook names from agentxchain.json, and removes the installed directory from .agentxchain/plugins/.

Authoring a plugin

A minimal process-hook plugin looks like this:

my-plugin/
agentxchain-plugin.json
hooks/
after-acceptance.js

Process hook contract

Process hooks receive the full hook payload as JSON on stdin. The runner also injects environment variables:

VariableDescription
AGENTXCHAIN_HOOK_PHASECurrent hook phase
AGENTXCHAIN_HOOK_NAMEHook name from the manifest
AGENTXCHAIN_RUN_IDCurrent run ID when available
AGENTXCHAIN_PROJECT_ROOTGoverned project root
AGENTXCHAIN_PLUGIN_NAMEPlugin manifest name
AGENTXCHAIN_PLUGIN_VERSIONPlugin manifest version
AGENTXCHAIN_PLUGIN_CONFIGValidated plugin config as a JSON string when config exists

The hook may print a JSON verdict to stdout:

{
"verdict": "allow",
"message": "Slack notification sent",
"annotations": [
{ "key": "slack_channel", "value": "#ops" }
]
}

Valid verdicts are allow, warn, and block. Advisory hooks that return block are downgraded to a warning by the orchestrator. after_acceptance annotations are written to .agentxchain/hook-annotations.jsonl.

hooks/after-acceptance.js
import { readFileSync } from 'node:fs';

const payload = JSON.parse(readFileSync(0, 'utf8'));
const pluginConfig = process.env.AGENTXCHAIN_PLUGIN_CONFIG
? JSON.parse(process.env.AGENTXCHAIN_PLUGIN_CONFIG)
: {};

process.stdout.write(JSON.stringify({
verdict: 'allow',
message: `Handled ${payload.hook_phase} for ${process.env.AGENTXCHAIN_PLUGIN_NAME}`,
annotations: pluginConfig.mention
? [{ key: 'mention', value: pluginConfig.mention }]
: [],
}));

HTTP hooks

HTTP hooks use the same phase system and verdict contract as process hooks. They send the JSON hook envelope as the HTTP request body and may return a JSON verdict in the response body.

Configuration

{
"name": "notify_webhook",
"type": "http",
"url": "https://my-service.example.com/hooks/after-acceptance",
"method": "POST",
"timeout_ms": 15000,
"mode": "advisory",
"env": {
"HOOK_TOKEN": "secret-token"
},
"headers": {
"Authorization": "Bearer ${HOOK_TOKEN}"
}
}

Behavior

  • Method: POST only. Other methods are rejected at manifest validation time.
  • Body: the full hook envelope is sent as JSON in the request body.
  • Header interpolation: header values support ${VAR} interpolation from the current process environment plus any strings declared in hook.env.
  • Response handling: non-2xx responses are treated as hook failures. There is no built-in retry loop.
  • Verdict surface: HTTP hooks can return the same verdict JSON as process hooks.

Failure modes

FailureImpactRecovery
Process hook exits non-zeroCommand fails. Blocking phases stop the workflow; advisory phases are logged and surfacedFix the hook and rerun the blocked operator step
Process or HTTP hook times outHook is killed with SIGTERM and the step fails or warns based on phase and modeIncrease timeout_ms or shorten the hook
HTTP hook returns non-2xxTreated as a hook failureFix the endpoint or credentials
Manifest validation fails on installInstall is rejectedFix the manifest and retry
Config schema validation failsInstall or upgrade is rejectedProvide config that satisfies config_schema
Duplicate plugin nameInstall is rejectedRemove or upgrade the existing plugin
Duplicate hook name in the same phaseInstall or upgrade is rejected with no partial config mutationRename the hook or remove the conflict
Partial install failure after copy but before config writeStaged plugin files are deleted and config remains unchangedFix the failing hook path or config issue and retry
Upgrade commit failurePrevious install directory and config are restoredFix the failure and rerun plugin upgrade
Hook tampers with protected orchestrator filesThe run fails closed and the protected files are restoredRemove the illegal mutation and rerun the blocked step

Built-in packages

Dedicated package guides:

@agentxchain/plugin-slack-notify

Posts advisory lifecycle notifications to a Slack incoming webhook.

Hook phases: after_acceptance, before_gate, on_escalation

agentxchain plugin install slack-notify

Config:

{
"plugins": {
"@agentxchain/plugin-slack-notify": {
"config": {
"webhook_env": "AGENTXCHAIN_SLACK_WEBHOOK_URL",
"mention": "@here"
}
}
}
}

If mention is not configured, the plugin falls back to AGENTXCHAIN_SLACK_MENTION at runtime. If webhook_env is not configured, it looks for AGENTXCHAIN_SLACK_WEBHOOK_URL first and then SLACK_WEBHOOK_URL.

@agentxchain/plugin-json-report

Writes structured lifecycle report artifacts into .agentxchain/reports/.

Hook phases: after_acceptance, before_gate, on_escalation

agentxchain plugin install json-report

Config:

{
"plugins": {
"@agentxchain/plugin-json-report": {
"config": {
"report_dir": ".agentxchain/reports"
}
}
}
}

report_dir must stay inside the governed project root. The plugin rejects paths that escape the repo and otherwise writes timestamped reports plus latest.json and latest-<hook_phase>.json into the configured directory.

@agentxchain/plugin-github-issues

Mirrors governed run status into one configured GitHub issue with one plugin-owned comment per run.

Hook phases: after_acceptance, on_escalation

agentxchain plugin install github-issues

Config:

{
"plugins": {
"@agentxchain/plugin-github-issues": {
"config": {
"repo": "owner/name",
"issue_number": 42,
"token_env": "GITHUB_TOKEN",
"label_prefix": "agentxchain"
}
}
}
}

This package intentionally does not close issues or set approval-pending labels. The current hook surface has no post-gate callback, so those states would be guesses rather than governed truth.

The built-in packages live in the repo plugins/ directory and can be installed from a local path or published tarball/package spec.