# Plugin authors guide (v0.15)

`gbrain` discovers subagent definitions from outside this repo via
`GBRAIN_PLUGIN_PATH`. If you maintain a downstream agent (your OpenClaw
deployment, a workflow host, a private tool) and want to ship custom
subagents alongside it, drop a plugin directory on that env path.

This guide is for plugin authors. The CLI user doesn't need to read it.

## Minimum viable plugin

```
/path/to/my-plugin/
├── gbrain.plugin.json
└── subagents/
    └── my-summarizer.md
```

`gbrain.plugin.json`:

```json
{
  "name": "my-plugin",
  "version": "1.0.0",
  "plugin_version": "gbrain-plugin-v1"
}
```

`subagents/my-summarizer.md`:

```markdown
---
name: my-summarizer
model: claude-sonnet-4-6
allowed_tools:
  - brain_search
  - brain_get_page
---

You are a brain page summarizer. Given a slug, fetch the page and produce
a 3-sentence summary.
```

## Turning it on

```bash
export GBRAIN_PLUGIN_PATH="/path/to/my-plugin"
gbrain jobs work           # worker startup prints the plugin load line
gbrain agent run "summarize meetings/2026-04-20" --subagent-def my-summarizer
```

Multiple plugins: colon-separated, just like `$PATH`.

```bash
export GBRAIN_PLUGIN_PATH="/path/to/plugin-a:/path/to/plugin-b"
```

## Rules (strict by design)

**Path policy.** Absolute paths only. Relative paths, `~`-prefixed paths,
and URL-style paths (`https://`, `file://`) are rejected with a warning.
You control where your plugin lives on disk; `gbrain` doesn't guess.

**Collision policy.** If two plugins ship a subagent with the same `name`,
the one listed FIRST in `GBRAIN_PLUGIN_PATH` wins. The other is dropped
with a warning naming both sources.

**Trust policy.** Plugins ship subagent definitions ONLY in v0.15:

- You **cannot** declare new tools.
- You **cannot** extend the brain tool allow-list.
- You **cannot** override any `agentSafe` or similar flag.
- Your `allowed_tools:` frontmatter field MUST subset the derived brain
  tool registry. Names not in the registry are rejected at plugin load
  time (worker startup), NOT at subagent dispatch time — so a typo in
  your plugin gives you a loud startup error, not a silent "tool never
  fires" at 3am.

v0.16+ may open up plugin-declared tools with a separate contract. Don't
expect it.

## `gbrain.plugin.json`

| field            | type   | required | notes                                                              |
|------------------|--------|----------|--------------------------------------------------------------------|
| `name`           | string | yes      | Human-readable plugin id. Shows up in warnings and collision logs. |
| `version`        | string | yes      | Your plugin's semver. Informational.                               |
| `plugin_version` | string | yes      | Contract lock. Must equal `"gbrain-plugin-v1"` for v0.15.          |
| `subagents`      | string | no       | Subdir name (default `subagents`). Escape-attempts are rejected.   |
| `description`    | string | no       | Shown in a future plugin-listing command.                          |

## Subagent definition files

Plain markdown with YAML frontmatter. The body is the system prompt. The
frontmatter controls runtime behavior.

Recognized frontmatter fields:

| field           | type     | required | notes                                                                                   |
|-----------------|----------|----------|-----------------------------------------------------------------------------------------|
| `name`          | string   | no       | Subagent identifier used as `--subagent-def`. Defaults to the file basename.            |
| `model`         | string   | no       | Anthropic model id. Defaults to the handler default (sonnet).                           |
| `max_turns`     | number   | no       | Cap on assistant turns. Defaults to 20.                                                 |
| `allowed_tools` | string[] | no       | Whitelist of tool names. Must subset the derived brain registry. Rejected on mismatch.  |

Unknown frontmatter fields are preserved but ignored by the handler. v0.16
may consume more of them.

## Caveats that will bite you

1. **Plugin definitions can't change during a run.** The loader reads the
   disk once at worker startup. Editing a subagent def doesn't re-take
   effect until you restart the worker. This is deliberate — live
   reloads would break crash-resumable replay.

2. **`~/.gbrain/audit/subagent-jobs-*.jsonl` is local only.** If your
   worker runs on a different host than the `gbrain agent logs` caller,
   the CLI won't see heartbeats from that worker. v0.16 will unify this;
   for now assume worker + CLI share a filesystem.

3. **Tool calls always run with `ctx.remote = true`.** Even on local CLI
   invocation. Tools that gate on `remote=true` (file_upload's strict
   confinement, put_page's namespace check) will apply. Good default; a
   subagent definition that wants local-filesystem reach beyond the brain
   can't have it.

4. **`put_page` writes are namespace-scoped.** A subagent with id 42 can
   only write under `wiki/agents/42/...`. This is enforced both in the
   tool schema (the slug pattern shown to the model) AND server-side in
   the `put_page` operation (fail-closed if `viaSubagent=true`). Don't
   try to route around it; you'll get `permission_denied`.

## Example: a downstream-OpenClaw plugin

```
~/your-openclaw/
└── gbrain-plugin/
    ├── gbrain.plugin.json
    └── subagents/
        ├── meeting-ingestion.md
        ├── signal-detector.md
        └── daily-task-prep.md
```

`~/your-openclaw/gbrain-plugin/gbrain.plugin.json`:

```json
{
  "name": "your-openclaw",
  "version": "2026.4.20",
  "plugin_version": "gbrain-plugin-v1",
  "description": "Your OpenClaw's personal-brain subagents"
}
```

Environment:

```bash
export GBRAIN_PLUGIN_PATH="$HOME/your-openclaw/gbrain-plugin"
```

Then your OpenClaw calls `gbrain agent run --subagent-def meeting-ingestion
--fanout-by transcript ...` and its definitions load automatically.
