openclaw/docs/tools/capability-cookbook.md

3.3 KiB

summary read_when title
Cookbook for adding a new shared capability to OpenClaw
Adding a new core capability and plugin seam
Deciding whether code belongs in core, a vendor plugin, or a feature plugin
Wiring a new runtime helper for channels or tools
Capability Cookbook

Capability Cookbook

Use this when OpenClaw needs a new domain such as image generation, video generation, or some future vendor-backed feature area.

The rule:

  • plugin = ownership boundary
  • capability = shared core contract

That means you should not start by wiring a vendor directly into a channel or a tool. Start by defining the capability.

When to create a capability

Create a new capability when all of these are true:

  1. more than one vendor could plausibly implement it
  2. channels, tools, or feature plugins should consume it without caring about the vendor
  3. core needs to own fallback, policy, config, or delivery behavior

If the work is vendor-only and no shared contract exists yet, stop and define the contract first.

The standard sequence

  1. Define the typed core contract.
  2. Add plugin registration for that contract.
  3. Add a shared runtime helper.
  4. Wire one real vendor plugin as proof.
  5. Move feature/channel consumers onto the runtime helper.
  6. Add contract tests.
  7. Document the operator-facing config and ownership model.

What goes where

Core:

  • request/response types
  • provider registry + resolution
  • fallback behavior
  • config schema and labels/help
  • runtime helper surface

Vendor plugin:

  • vendor API calls
  • vendor auth handling
  • vendor-specific request normalization
  • registration of the capability implementation

Feature/channel plugin:

  • calls api.runtime.* or the matching plugin-sdk/*-runtime helper
  • never calls a vendor implementation directly

File checklist

For a new capability, expect to touch these areas:

  • src/<capability>/types.ts
  • src/<capability>/...registry/runtime.ts
  • src/plugins/types.ts
  • src/plugins/registry.ts
  • src/plugins/captured-registration.ts
  • src/plugins/contracts/registry.ts
  • src/plugins/runtime/types-core.ts
  • src/plugins/runtime/index.ts
  • src/plugin-sdk/<capability>.ts
  • src/plugin-sdk/<capability>-runtime.ts
  • one or more extensions/<vendor>/...
  • config/docs/tests

Example: image generation

Image generation follows the standard shape:

  1. core defines ImageGenerationProvider
  2. core exposes registerImageGenerationProvider(...)
  3. core exposes runtime.imageGeneration.generate(...)
  4. the openai and google plugins register vendor-backed implementations
  5. future vendors can register the same contract without changing channels/tools

The config key is separate from vision-analysis routing:

  • agents.defaults.imageModel = analyze images
  • agents.defaults.imageGenerationModel = generate images

Keep those separate so fallback and policy remain explicit.

Review checklist

Before shipping a new capability, verify:

  • no channel/tool imports vendor code directly
  • the runtime helper is the shared path
  • at least one contract test asserts bundled ownership
  • config docs name the new model/config key
  • plugin docs explain the ownership boundary

If a PR skips the capability layer and hardcodes vendor behavior into a channel/tool, send it back and define the contract first.