Skip to main content
Charm separates a small stable core from community-owned extensions. Not every extension works the same way — some are runtime Python plugins, some are store contributions, and some are manifest PRs. Use this page to pick the right path before you start building.

Extension paths at a glance

What you want to addRegistrationRuns whereNeeds custom_image on cloud?Needs store deploy?
Custom adapter plugincharm.adapters entry pointAgent containerYesNo
Custom runtime onlyruntime.custom_image in YAMLAgent containerYesNo
Telemetry exportercharm.telemetry entry pointAgent containerTypically No (dynamic install)No
Memory backendcharm.memory entry pointAgent containerTypically No (dynamic install)No
Input form widgetsinterface.ui in YAMLStore UINoTypically No (dynamic ESM install)
Output renderers_charm_render_type in agent outputStore UINoTypically No (dynamic ESM install)
Starter templatesPR to charm-community-plugincharm initNoNo (manifest merge)

Two kinds of “custom” (important)

These names overlap — they are not the same thing:
TermMeaning
runtime.adapter.type: customBuilt-in Charm adapter for plain Python agents (CharmCustomAdapter in core). Good default for charm init projects.
runtime.custom_imageDocker image override for the cloud runner. Required for heavy dependencies, custom system packages, or third-party adapters.
Third-party adapter pluginNew adapter name (e.g. ag2, plugin_test) registered via [project.entry-points."charm.adapters"].
Scenario: you can use type: custom with custom_image (extra deps, no new adapter class).
Scenario: you can use type: ag2 with custom_image (new adapter plugin + image that installs it).
  1. Start from a normal agentcharm init my-agent --template python
  2. Add a pip package with your adapter/telemetry/memory classes and pyproject.toml entry points
  3. Point charm.yaml at your pluginruntime.adapter.type: your_plugin_name
  4. Add to requirements.txt (if lightweight like Telemetry or Memory) OR Build a custom_image (if heavy like Adapters)
  5. Set runtime.custom_image in charm.yaml (if you built a Docker image)
  6. charm validatecharm push → run on the Store
Note: The cloud runner will execute uv pip install -r requirements.txt at runtime. For lightweight plugins, this is all you need. For heavy plugins (like third-party adapters), you must bake them into a custom_image to avoid boot timeouts.

Built-in vs third-party

LayerBuilt-in (no plugin package)Third-party (plugin author)
Python agent logiccharm init + type: customSame
Adapter typecustom, langchain, openclaw, …New name via entry points
Runtime imageOfficial runner-* imagesYour custom_image
Store form widgetstext, textarea, file, select, checkbox, sliderNew widgets → community plugin PR
Rich output UIBuilt-in _charm_render_type valuesNew types → community plugin PR

Cloud prerequisites

For any custom image on production:
  1. Push the image to a registry the runner can pull (Artifact Registry, GHCR, etc.)
  2. Grant the runner service account read access to that registry (if private)
  3. Declare runtime.custom_image in charm.yaml — the runner selects it over default adapter images
See Base Images and Custom Runtimes.

Next steps

For core contributors merging adapters into the official SDK, see Write a Custom Adapter (in-repo process).