Skip to content

[plugin sdk] Consolidate author surface, lifecycle semantics, and export sprawl #80219

Description

@100yenadmin

OpenClaw Plugin SDK Whole-Surface Architecture Plan

Date: 2026-05-10
Status: Whole-surface audit complete
Confidence: 95%+
Repo: /Volumes/LEXAR/repos/openclaw-plugin-sdk-consolidation

Executive Summary

OpenClaw already has a real plugin-first kernel. The strongest parts are:

  • manifest-first discovery and activation planning
  • registry-based capability registration
  • typed hooks
  • shared channel/provider kernels
  • session workflow state seams

The core problem is not "plugins are the wrong direction." The core problem is
that the current public SDK surface mixes too many classes of API at the same
level:

  • declaration-time registration
  • setup/bootstrap-only discovery
  • live session/workflow control
  • runtime helper facades
  • bundled-only authority seams
  • compatibility and migration debt

So the right cleanup is not to smash everything into one generic API. The right
cleanup is:

  1. classify the whole shipped surface from one source of truth
  2. make lifecycle semantics explicit
  3. make grouped families canonical
  4. freeze and de-emphasize wide barrels and compat shims
  5. internalize bundled-only and semi-internal subsystems where the repo already
    treats them as special

This preserves features while reducing accidental permanent API.

What This Audit Covered

  • the full OpenClawPluginApi shape in src/plugins/types.ts
  • the root openclaw package export map for openclaw/plugin-sdk/*
  • docs and generated-baseline surfaces
  • repo-wide import usage across extensions, tests, docs, and runtime code
  • internal lifecycle enforcement around post-register callability
  • compatibility and plugin-owned/public classifications

This was informed by direct repo inspection plus four independent read-only
subagent passes over:

  • lifecycle and role classification
  • export/publicness drift
  • architectural taxonomy and boundaries
  • real in-repo usage and blast radius

Current Surface Snapshot

Counts

  • OpenClawPluginApi currently exposes 79 top-level fields/methods.
  • Root package.json currently publishes 305 exports total.
  • 302 of those are ./plugin-sdk* entrypoints.
  • 376 non-test src/plugin-sdk/*.ts modules exist in-tree.
  • Rough file-family distribution of those non-test plugin-sdk modules:
    • 131 include runtime
    • 59 are channel-related
    • 40 are provider-related
    • 23 are test-ish/testing/contract/mocks
    • 21 are memory-related
    • 4 are compat/legacy-marked
  • 255 unique plugin-sdk/* subpaths are explicitly mentioned in
    docs/plugins/sdk-subpaths.md.
  • 48 shipped plugin-sdk exports are not catalogued there.
  • 39 shipped plugin-sdk exports had zero in-repo imports in the static scan.

Strongest Stable Usage

The highest-blast-radius shipped subpaths in production-ish extension code are:

  • text-runtime
  • config-types
  • runtime-env
  • plugin-entry
  • provider-model-shared
  • channel-contract
  • channel-message
  • routing
  • provider-auth
  • provider-http

The highest-use consumer families are channel/plugin stacks like:

  • discord
  • telegram
  • slack
  • matrix
  • whatsapp

That means many apparently generic helpers are really carrying mature
channel-stack behavior and should not be casually churned.

Low-Risk Consolidation Signals

  • The new grouped aliases in api.session.*, api.agent.*,
    api.runContext.*, and api.lifecycle.* are basically docs/tests/facade-only
    today, with no meaningful in-repo author adoption yet.
  • openclaw/plugin-sdk/compat is effectively dead in-repo.
  • the bare root openclaw/plugin-sdk import behaves mostly like a migration or
    test surface, not a healthy primary authoring surface
  • many host-workflow/session-control registrars are still mostly fixture and
    contract-level seams, not widely used bundled-plugin author APIs

Those are exactly the conditions that make canonicalization and de-emphasis safe.

Core Diagnosis

Two structural problems explain most of the SDK sprawl.

1. Publication-by-list, not publication-by-policy

The public package contract is mechanically generated from the entrypoint list.
That makes it easy to ship new subpaths, but harder to keep "exported",
"documented", "supported", "compat-only", and "bundled-only" aligned.

Today there are multiple overlapping policy layers:

  • the root package export map
  • plugin-sdk-entrypoints.json
  • plugin-sdk/entrypoints.ts classification arrays
  • docs/plugins/sdk-subpaths.md
  • pluginSdkDocMetadata
  • boundary/contract tests

That is workable, but it is not one authoritative support model.

2. Lifecycle flattening

OpenClawPluginApi currently flattens at least four different kinds of API into
one top-level type:

  • declaration-time registration
  • setup-only/bootstrap discovery
  • live workflow/session control
  • trusted in-process runtime power

That flattening makes the API feel broader and more inconsistent than it needs
to be. It also explains why reviewer discomfort shows up as "the API keeps
expanding" even when some of the seams are individually valid.

Architectural Thesis

The long-term SDK should read as a small number of explicit families:

  • kernel
  • capabilities
  • workflow
  • client-contrib
  • runtime-facades
  • compat
  • bundled-internal
  • memory

The first four are the plugin-first public platform.
The last four need much stronger curation and labeling.

Target Taxonomy

flowchart TD
    A["OpenClaw Plugin SDK Today"] --> B["Flat OpenClawPluginApi"]
    A --> C["302 shipped plugin-sdk subpaths"]

    B --> D["declaration-time registration"]
    B --> E["setup/bootstrap-only seams"]
    B --> F["host workflow and session control"]
    B --> G["trusted runtime escape hatch"]

    H["Target Model"] --> I["kernel"]
    H --> J["capabilities"]
    H --> K["workflow"]
    H --> L["client-contrib"]
    H --> M["runtime-facades"]
    H --> N["compat"]
    H --> O["bundled-internal"]
    H --> P["memory"]

    K --> Q["session.state"]
    K --> R["session.workflow"]
    K --> S["agent.events"]
    K --> T["runContext"]
    K --> U["lifecycle"]

    L --> V["session.controls"]
Loading

Kernel

Keep as the architectural center:

  • manifest/discovery
  • entrypoints
  • activation planning
  • registration modes
  • narrow import discipline

This is the cleanest and healthiest part of the system.

Capabilities

These are the true foundational public primitives:

  • provider registration
  • channel registration
  • tool registration
  • command registration
  • route registration
  • CLI registration
  • service registration
  • discovery registration

These should stay public and canonical.

Workflow

These are real plugin-first app behavior primitives:

  • session extensions
  • next-turn injections
  • run context
  • lifecycle cleanup
  • event subscription/emission
  • scheduler ownership and session workflow helpers

These should stay public, but the grouped family shape should become the main
story.

Client-Contrib

These are useful, but they are not kernel primitives:

  • control UI descriptors
  • typed session actions

They should read as client-extension contracts, not as generic platform verbs.

Runtime-Facades

These should remain narrow and cheap:

  • channel/provider/runtime helper subpaths
  • utility leaves used by bundled and external plugins

They should not become a backdoor to host internals or another broad pseudo-root
SDK.

Compat

These are transitional:

  • compat
  • testing
  • channel-runtime
  • infra-runtime
  • branded compatibility facades
  • legacy reply/runtime shims

They should remain supported where needed, but visibly not be peers of the
kernel.

Bundled-Internal

These are real seams, but they are not healthy third-party platform primitives:

  • trusted tool policy
  • agent tool result middleware
  • Codex app-server extension factories
  • host-owned authority surfaces
  • experimental agent-harness-level seams

They should be labeled and treated as advanced/bundled-native, not casually
public.

Memory

Memory needs special treatment. The exclusive capability idea is good, but much
of the current memory-host SDK family is still a semi-internal bridge dressed as
public SDK. This is the strongest whole-surface internalization candidate.

Whole-Surface Classification

Keep Public and Canonical

  • definePluginEntry(...)
  • capability registrars
  • api.on(...)
  • tool/command/http/gateway/service/CLI/channel registration
  • migration provider registration
  • registerMemoryCapability(...)
  • additive memory supplements
  • manifest-first activation planning model
  • narrow high-value leaves like plugin-entry, config-types, routing,
    channel-contract, channel-message, provider-model-shared,
    provider-auth, provider-http

Keep Public but Reclassify as Families

  • registerSessionExtension
  • enqueueNextTurnInjection
  • registerRuntimeLifecycle
  • registerAgentEventSubscription
  • emitAgentEvent
  • setRunContext / getRunContext / clearRunContext
  • registerSessionSchedulerJob
  • registerSessionAction
  • registerControlUiDescriptor
  • sendSessionAttachment
  • scheduleSessionTurn
  • unscheduleSessionTurnsByTag

These should remain available, but the grouped family model should become
canonical:

  • api.session.state.*
  • api.session.workflow.*
  • api.session.controls.*
  • api.agent.events.*
  • api.runContext.*
  • api.lifecycle.*

Fold or Alias

  • registerHook(...) -> legacy alias to api.on(...)
  • registerNodeCliFeature(...) -> helper over registerCli(...)
  • registerMemoryPromptSection(...) -> alias to registerMemoryCapability(...)
  • registerMemoryFlushPlan(...) -> alias to registerMemoryCapability(...)
  • registerMemoryRuntime(...) -> alias to registerMemoryCapability(...)
  • flat host-workflow/control/run-context methods -> compatibility aliases over
    grouped family entrypoints

Freeze and De-Emphasize

Do not keep growing these as primary design targets:

  • bare root openclaw/plugin-sdk
  • compat
  • testing
  • channel-runtime
  • infra-runtime
  • config-runtime
  • wide umbrellas like core, reply-runtime, media-runtime, text-runtime,
    agent-runtime, plugin-runtime

These can stay shipped, but should be treated as supported umbrellas or compat,
not as the best place for new API design.

Advanced / Bundled-Only / Internalize

Strong internalization or advanced-only candidates:

  • runtime
  • registerTrustedToolPolicy(...)
  • registerAgentToolResultMiddleware(...)
  • registerCodexAppServerExtensionFactory(...)
  • registerAgentHarness(...)
  • registerContextEngine(...)
  • registerCompactionProvider(...)
  • registerDetachedTaskRuntime(...)
  • session attachment and scheduled-turn authority seams, insofar as they depend
    on host-owned trust/runtime lanes
  • the broad memory-core-host-* and memory-host-* entrypoint families until
    they are truly package-owned and independently defensible as public SDK

Deprecate Harder

  • openclaw/plugin-sdk/compat
  • bare openclaw/plugin-sdk root import
  • branded facades that are explicitly transitional
  • wide reply/channel compat bridges where the repo already prefers narrower
    leaves

Lifecycle Model

This needs to become explicit in code, docs, and tests.

Proposed Lifecycle Classes

  • declaration
    • registration-only, valid only during register(api)
  • setup_only
    • early discovery/bootstrap-only seams
  • late_call_live
    • may be called after register() closes through the guarded API
  • active_loaded_live
    • live only when a plugin is loaded/active and the host runtime is present
  • bundled_internal
    • trusted or authority-bearing seam not intended as ordinary third-party API

Important Current Inconsistencies

  • activate(api) is not a true second lifecycle phase; loader effectively
    aliases it to register
  • only sendSessionAttachment, scheduleSessionTurn, and
    unscheduleSessionTurnsByTag are explicitly late-callable today
  • emitAgentEvent and run-context methods look like runtime handles, but they
    are not generally treated as late-callable by the guarded loader path
  • registerConfigMigration and registerAutoEnableProbe sit on the main API
    but are only meaningful in setup-only mode
  • registerCli and registerChannel have special wiring characteristics
  • some concerns exist as both entry-definition fields and runtime API methods,
    which blurs declaration-only vs registrar semantics

This is why lifecycle metadata should be authoritative and loader-enforced.

Safe Start Order

Phase 0: Governance, not churn

  1. Add one authoritative SDK surface manifest.
    Each shipped export and each OpenClawPluginApi method should declare:

    • status: stable, supported_umbrella, compat, plugin_owned_public,
      experimental, bundled_internal, or internal_only
    • lifecycle class
    • docs class/category
    • owner, if plugin-owned or bundled-only
  2. Generate these from that manifest:

    • root package exports
    • docs catalog
    • API baseline
    • boundary report
    • deprecation report
  3. Freeze new broad barrels and new top-level live verbs.

This is the highest-value first step because it reduces future API sprawl
without breaking any current features.

Phase 1: Canonicalize the public story

  1. Make grouped host-workflow families canonical in docs and examples.
  2. Keep flat methods as compatibility aliases.
  3. Make api.on(...) the canonical hook registration story.
  4. Reclassify registerNodeCliFeature(...) as a CLI helper, not a peer
    primitive.
  5. Collapse the deprecated memory trio into registerMemoryCapability(...).

This is low-risk because repo adoption of the grouped families is still minimal.

Phase 2: Make lifecycle semantics real

  1. Replace the current tiny post-register allowlist with authoritative method
    lifecycle metadata.
  2. Split setup-only surfaces from ordinary registration semantics in docs and
    policy.
  3. Clarify which surfaces are:
    • declarative
    • retained runtime handles
    • bundled-only authority seams

Phase 3: Export-surface cleanup

  1. Review the undocumented exported subpaths first.
  2. Decide one of:
    • document as intentional
    • classify as compat
    • internalize

Priority review set:

  • root plugin-sdk
  • compat
  • config-runtime
  • channel-runtime
  • sandbox
  • runtime-doctor
  • agent-harness-runtime
  • provider-stream-shared
  • channel-secret-basic-runtime
  1. Internalize anything useful in-tree but not truly intended for external
    plugin authors, using the same pattern the repo already uses for QA-only
    surfaces.

Phase 4: Subsystem cleanup

  1. Memory-host surface review and likely shrink/internalization
  2. bundled facade review
  3. channel/reply/runtime bridge cleanup
  4. follow-on rationalization of wide umbrellas

What This Means for #80219 and #80229

#80219

#80219 should become the umbrella whole-surface issue, not just a note about
the four active PR seams. The current issue direction is good, but it should be
reframed as the architecture plan for:

  • whole-surface classification
  • lifecycle cleanup
  • public vs compat vs bundled-only policy
  • export and docs generation policy

#80229

#80229 should be framed as a valid Phase 1 slice, not as "the" Plugin SDK
cleanup.

Its best architectural value is:

  • grouped host-workflow aliases
  • narrow late-callability metadata
  • proof-backed session/workflow/media seams

That is real progress, but it only addresses one part of the whole SDK surface.

Explicit Non-Goals

  • do not collapse the SDK into one generic API
  • do not widen generic runtime.llm.complete(...) into the structured
    media-understanding seam
  • do not remove compatibility shims before policy classification exists
  • do not rewrite high-blast-radius channel/provider helpers casually
  • do not treat bundled-only trust surfaces as healthy third-party primitives

Bottom Line

The highest-confidence architecture plan is:

  • keep manifest-first kernel and capability registration as the center
  • keep workflow/session seams, but group them and label them clearly
  • stop treating every exported helper as equal public platform API
  • separate stable public primitives from compat, bundled-only, and semi-internal
    lanes
  • use one authoritative surface manifest to govern exports, docs, baselines, and
    lifecycle policy

In one sentence:

OpenClaw should expand through fewer, better-grouped, better-labeled plugin
primitives, not through more flat top-level verbs and more mechanically
published SDK surface.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal backlog priority with limited blast radius.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:needs-security-reviewClawSweeper marked this issue as needing security-sensitive review.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.impact:securitySecurity boundary, credential, authz, sandbox, or sensitive-data risk.issue-rating: 🌊 off-meta tidepoolIssue quality rating does not apply to this item.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions