docs: typography hygiene + drop one in-body H1 across 5 pages

Replaced 98 typography characters (curly quotes, apostrophes, em/en
dashes, non-breaking hyphens) with ASCII equivalents per
docs/CLAUDE.md heading and content hygiene rules.

- docs/plugins/sdk-migration.md: 20 chars
- docs/help/testing.md: 20 chars
- docs/automation/tasks.md: 20 chars
- docs/plugins/sdk-channel-plugins.md: 19 chars
- docs/channels/yuanbao.md: 19 chars; removed the duplicate '# Yuanbao'
  H1 (Mintlify renders title from frontmatter).
This commit is contained in:
Vincent Koc
2026-05-05 19:45:44 -07:00
parent 8489d0eb68
commit 4395f1dd66
5 changed files with 90 additions and 92 deletions

View File

@@ -14,7 +14,7 @@ Looking for scheduling? See [Automation and tasks](/automation) for choosing the
Background tasks track work that runs **outside your main conversation session**: ACP runs, subagent spawns, isolated cron job executions, and CLI-initiated operations.
Tasks do **not** replace sessions, cron jobs, or heartbeats they are the **activity ledger** that records what detached work happened, when, and whether it succeeded.
Tasks do **not** replace sessions, cron jobs, or heartbeats - they are the **activity ledger** that records what detached work happened, when, and whether it succeeded.
<Note>
Not every agent run creates a task. Heartbeat turns and normal interactive chat do not. All cron executions, ACP spawns, subagent spawns, and CLI agent commands do.
@@ -22,7 +22,7 @@ Not every agent run creates a task. Heartbeat turns and normal interactive chat
## TL;DR
- Tasks are **records**, not schedulers cron and heartbeat decide _when_ work runs, tasks track _what happened_.
- Tasks are **records**, not schedulers - cron and heartbeat decide _when_ work runs, tasks track _what happened_.
- ACP, subagents, all cron jobs, and CLI operations create tasks. Heartbeat turns do not.
- Each task moves through `queued → running → terminal` (succeeded, failed, timed_out, cancelled, or lost).
- Cron tasks stay live while the cron runtime still owns the job; if the
@@ -100,7 +100,7 @@ Not every agent run creates a task. Heartbeat turns and normal interactive chat
<AccordionGroup>
<Accordion title="Notify defaults for cron and media">
Main-session cron tasks use `silent` notify policy by default they create records for tracking but do not generate notifications. Isolated cron tasks also default to `silent` but are more visible because they run in their own session.
Main-session cron tasks use `silent` notify policy by default - they create records for tracking but do not generate notifications. Isolated cron tasks also default to `silent` but are more visible because they run in their own session.
Session-backed `music_generate` and `video_generate` runs also use `silent` notify policy. They still create task records, but completion is handed back to the original agent session as an internal wake so the agent can write the follow-up message and attach the finished media itself. Group/channel completions follow the normal visible-reply policy, so the agent uses the message tool when source delivery requires it. If the completion agent fails to produce message-tool delivery evidence in a tool-only route, OpenClaw sends the completion fallback directly to the original channel instead of leaving the media private.
@@ -109,7 +109,7 @@ Not every agent run creates a task. Heartbeat turns and normal interactive chat
While a session-backed `video_generate` task is still active, the tool also acts as a guardrail: repeated `video_generate` calls in that same session return the active task status instead of starting a second concurrent generation. Use `action: "status"` when you want an explicit progress/status lookup from the agent side.
</Accordion>
<Accordion title="What does not create tasks">
- Heartbeat turns main-session; see [Heartbeat](/gateway/heartbeat)
- Heartbeat turns - main-session; see [Heartbeat](/gateway/heartbeat)
- Normal interactive chat turns
- Direct `/command` responses
@@ -140,7 +140,7 @@ stateDiagram-v2
| `cancelled` | Stopped by the operator via `openclaw tasks cancel` |
| `lost` | The runtime lost authoritative backing state after a 5-minute grace period |
Transitions happen automatically when the associated agent run ends, the task status updates to match.
Transitions happen automatically - when the associated agent run ends, the task status updates to match.
Agent run completion is authoritative for active task records. A successful detached run finalizes as `succeeded`, ordinary run errors finalize as `failed`, and timeout or abort outcomes finalize as `timed_out`. If an operator already cancelled the task, or the runtime already recorded a stronger terminal state such as `failed`, `timed_out`, or `lost`, a later success signal does not downgrade that terminal status.
@@ -161,12 +161,12 @@ Agent run completion is authoritative for active task records. A successful deta
When a task reaches a terminal state, OpenClaw notifies you. There are two delivery paths:
**Direct delivery** if the task has a channel target (the `requesterOrigin`), the completion message goes straight to that channel (Telegram, Discord, Slack, etc.). For subagent completions, OpenClaw also preserves bound thread/topic routing when available and can fill a missing `to` / account from the requester session's stored route (`lastChannel` / `lastTo` / `lastAccountId`) before giving up on direct delivery.
**Direct delivery** - if the task has a channel target (the `requesterOrigin`), the completion message goes straight to that channel (Telegram, Discord, Slack, etc.). For subagent completions, OpenClaw also preserves bound thread/topic routing when available and can fill a missing `to` / account from the requester session's stored route (`lastChannel` / `lastTo` / `lastAccountId`) before giving up on direct delivery.
**Session-queued delivery** if direct delivery fails or no origin is set, the update is queued as a system event in the requester's session and surfaces on the next heartbeat.
**Session-queued delivery** - if direct delivery fails or no origin is set, the update is queued as a system event in the requester's session and surfaces on the next heartbeat.
<Tip>
Task completion triggers an immediate heartbeat wake so you see the result quickly you do not have to wait for the next scheduled heartbeat tick.
Task completion triggers an immediate heartbeat wake so you see the result quickly - you do not have to wait for the next scheduled heartbeat tick.
</Tip>
That means the usual workflow is push-based: start detached work once, then let the runtime wake or notify you on completion. Poll task state only when you need debugging, intervention, or an explicit audit.
@@ -177,7 +177,7 @@ Control how much you hear about each task:
| Policy | What is delivered |
| --------------------- | ----------------------------------------------------------------------- |
| `done_only` (default) | Only terminal state (succeeded, failed, etc.) **this is the default** |
| `done_only` (default) | Only terminal state (succeeded, failed, etc.) - **this is the default** |
| `state_changes` | Every state transition and progress update |
| `silent` | Nothing at all |
@@ -290,9 +290,9 @@ Tasks: 3 queued · 2 running · 1 issues
The summary reports:
- **active** count of `queued` + `running`
- **failures** count of `failed` + `timed_out` + `lost`
- **byRuntime** breakdown by `acp`, `subagent`, `cron`, `cli`
- **active** - count of `queued` + `running`
- **failures** - count of `failed` + `timed_out` + `lost`
- **byRuntime** - breakdown by `acp`, `subagent`, `cron`, `cli`
Both `/status` and the `session_status` tool use a cleanup-aware task snapshot: active tasks are preferred, stale completed rows are hidden, and recent failures only surface when no active work remains. This keeps the status card focused on what matters right now.
@@ -343,13 +343,13 @@ A sweeper runs every **60 seconds** and handles four things:
</Accordion>
<Accordion title="Tasks and cron">
A cron job **definition** lives in `~/.openclaw/cron/jobs.json`; runtime execution state lives beside it in `~/.openclaw/cron/jobs-state.json`. **Every** cron execution creates a task record both main-session and isolated. Main-session cron tasks default to `silent` notify policy so they track without generating notifications.
A cron job **definition** lives in `~/.openclaw/cron/jobs.json`; runtime execution state lives beside it in `~/.openclaw/cron/jobs-state.json`. **Every** cron execution creates a task record - both main-session and isolated. Main-session cron tasks default to `silent` notify policy so they track without generating notifications.
See [Cron Jobs](/automation/cron-jobs).
</Accordion>
<Accordion title="Tasks and heartbeat">
Heartbeat runs are main-session turns they do not create task records. When a task completes, it can trigger a heartbeat wake so you see the result promptly.
Heartbeat runs are main-session turns - they do not create task records. When a task completes, it can trigger a heartbeat wake so you see the result promptly.
See [Heartbeat](/gateway/heartbeat).
@@ -358,14 +358,14 @@ A sweeper runs every **60 seconds** and handles four things:
A task may reference a `childSessionKey` (where work runs) and a `requesterSessionKey` (who started it). Sessions are conversation context; tasks are activity tracking on top of that.
</Accordion>
<Accordion title="Tasks and agent runs">
A task's `runId` links to the agent run doing the work. Agent lifecycle events (start, end, error) automatically update the task status you do not need to manage the lifecycle manually.
A task's `runId` links to the agent run doing the work. Agent lifecycle events (start, end, error) automatically update the task status - you do not need to manage the lifecycle manually.
</Accordion>
</AccordionGroup>
## Related
- [Automation & Tasks](/automation) all automation mechanisms at a glance
- [CLI: Tasks](/cli/tasks) CLI command reference
- [Heartbeat](/gateway/heartbeat) periodic main-session turns
- [Scheduled Tasks](/automation/cron-jobs) scheduling background work
- [Task Flow](/automation/taskflow) flow orchestration above tasks
- [Automation & Tasks](/automation) - all automation mechanisms at a glance
- [CLI: Tasks](/cli/tasks) - CLI command reference
- [Heartbeat](/gateway/heartbeat) - periodic main-session turns
- [Scheduled Tasks](/automation/cron-jobs) - scheduling background work
- [Task Flow](/automation/taskflow) - flow orchestration above tasks

View File

@@ -6,8 +6,6 @@ read_when:
title: Yuanbao
---
# Yuanbao
Tencent Yuanbao is Tencent's AI assistant platform. The OpenClaw channel plugin
connects Yuanbao bots to OpenClaw over WebSocket so they can interact with users
through direct messages and group chats.
@@ -53,10 +51,10 @@ Follow the prompts to enter your App ID and App Secret.
Configure `dmPolicy` to control who can DM the bot:
- `"pairing"` unknown users receive a pairing code; approve via CLI
- `"allowlist"` only users listed in `allowFrom` can chat
- `"open"` allow all users (default)
- `"disabled"` disable all DMs
- `"pairing"` - unknown users receive a pairing code; approve via CLI
- `"allowlist"` - only users listed in `allowFrom` can chat
- `"open"` - allow all users (default)
- `"disabled"` - disable all DMs
**Approve a pairing request:**
@@ -69,8 +67,8 @@ openclaw pairing approve yuanbao <CODE>
**Mention requirement** (`channels.yuanbao.requireMention`):
- `true` require @mention (default)
- `false` respond without @mention
- `true` - require @mention (default)
- `false` - respond without @mention
Replying to the bot's message in a group chat is treated as an implicit mention.
@@ -228,9 +226,9 @@ Replying to the bot's message in a group chat is treated as an implicit mention.
### Message limits
- `maxChars` single message max character count (default: `3000` chars)
- `mediaMaxMb` media upload/download limit (default: `20` MB)
- `overflowPolicy` behavior when message exceeds limit: `"split"` (default) or `"stop"`
- `maxChars` - single message max character count (default: `3000` chars)
- `mediaMaxMb` - media upload/download limit (default: `20` MB)
- `overflowPolicy` - behavior when message exceeds limit: `"split"` (default) or `"stop"`
### Streaming
@@ -358,13 +356,13 @@ Full configuration: [Gateway configuration](/gateway/configuration)
| ------------------------------------------ | ------------------------------------------------- | -------------------------------------- |
| `channels.yuanbao.enabled` | Enable/disable the channel | `true` |
| `channels.yuanbao.defaultAccount` | Default account for outbound routing | `default` |
| `channels.yuanbao.accounts.<id>.appKey` | App Key (used for signing and ticket generation) | |
| `channels.yuanbao.accounts.<id>.appSecret` | App Secret (used for signing) | |
| `channels.yuanbao.accounts.<id>.token` | Pre-signed token (skips automatic ticket signing) | |
| `channels.yuanbao.accounts.<id>.name` | Account display name | |
| `channels.yuanbao.accounts.<id>.appKey` | App Key (used for signing and ticket generation) | - |
| `channels.yuanbao.accounts.<id>.appSecret` | App Secret (used for signing) | - |
| `channels.yuanbao.accounts.<id>.token` | Pre-signed token (skips automatic ticket signing) | - |
| `channels.yuanbao.accounts.<id>.name` | Account display name | - |
| `channels.yuanbao.accounts.<id>.enabled` | Enable/disable a specific account | `true` |
| `channels.yuanbao.dm.policy` | DM policy | `open` |
| `channels.yuanbao.dm.allowFrom` | DM allowlist (user ID list) | |
| `channels.yuanbao.dm.allowFrom` | DM allowlist (user ID list) | - |
| `channels.yuanbao.requireMention` | Require @mention in groups | `true` |
| `channels.yuanbao.overflowPolicy` | Long message handling (`split` or `stop`) | `split` |
| `channels.yuanbao.replyToMode` | Group reply-to strategy (`off`, `first`, `all`) | `first` |
@@ -411,8 +409,8 @@ Full configuration: [Gateway configuration](/gateway/configuration)
## Related
- [Channels Overview](/channels) all supported channels
- [Pairing](/channels/pairing) DM authentication and pairing flow
- [Groups](/channels/groups) group chat behavior and mention gating
- [Channel Routing](/channels/channel-routing) session routing for messages
- [Security](/gateway/security) access model and hardening
- [Channels Overview](/channels) - all supported channels
- [Pairing](/channels/pairing) - DM authentication and pairing flow
- [Groups](/channels/groups) - group chat behavior and mention gating
- [Channel Routing](/channels/channel-routing) - session routing for messages
- [Security](/gateway/security) - access model and hardening

View File

@@ -18,9 +18,9 @@ of Docker runners. This doc is a "how we test" guide:
<Note>
**QA stack (qa-lab, qa-channel, live transport lanes)** is documented separately:
- [QA overview](/concepts/qa-e2e-automation) architecture, command surface, scenario authoring.
- [Matrix QA](/concepts/qa-matrix) reference for `pnpm openclaw qa matrix`.
- [QA channel](/channels/qa-channel) the synthetic transport plugin used by repo-backed scenarios.
- [QA overview](/concepts/qa-e2e-automation) - architecture, command surface, scenario authoring.
- [Matrix QA](/concepts/qa-matrix) - reference for `pnpm openclaw qa matrix`.
- [QA channel](/channels/qa-channel) - the synthetic transport plugin used by repo-backed scenarios.
This page covers running the regular test suites and Docker/Parallels runners. The QA-specific runners section below ([QA-specific runners](#qa-specific-runners)) lists the concrete `qa` invocations and points back at the references above.
</Note>
@@ -301,7 +301,7 @@ gh workflow run package-acceptance.yml --ref main \
- Starts only the local AIMock provider server for direct protocol smoke
testing.
- `pnpm openclaw qa matrix`
- Runs the Matrix live QA lane against a disposable Docker-backed Tuwunel homeserver. Source-checkout only packaged installs do not ship `qa-lab`.
- Runs the Matrix live QA lane against a disposable Docker-backed Tuwunel homeserver. Source-checkout only - packaged installs do not ship `qa-lab`.
- Full CLI, profile/scenario catalog, env vars, and artifact layout: [Matrix QA](/concepts/qa-matrix).
- `pnpm openclaw qa telegram`
- Runs the Telegram live QA lane against a real private group using the driver and SUT bot tokens from env.
@@ -399,7 +399,7 @@ The architecture and scenario-helper names for new channel adapters live in [QA
## Test suites (what runs where)
Think of the suites as increasing realism (and increasing flakiness/cost):
Think of the suites as "increasing realism" (and increasing flakiness/cost):
### Unit / integration (default)
@@ -578,12 +578,12 @@ Think of the suites as “increasing realism” (and increasing flakiness/cost):
- Files: `src/**/*.live.test.ts`, `test/**/*.live.test.ts`, and bundled-plugin live tests under `extensions/`
- Default: **enabled** by `pnpm test:live` (sets `OPENCLAW_LIVE_TEST=1`)
- Scope:
- Does this provider/model actually work _today_ with real creds?
- "Does this provider/model actually work _today_ with real creds?"
- Catch provider format changes, tool-calling quirks, auth issues, and rate limit behavior
- Expectations:
- Not CI-stable by design (real networks, real provider policies, quotas, outages)
- Costs money / uses rate limits
- Prefer running narrowed subsets instead of everything
- Prefer running narrowed subsets instead of "everything"
- Live runs source `~/.profile` to pick up missing API keys.
- By default, live runs still isolate `HOME` and copy config/auth material into a temp test home so unit fixtures cannot mutate your real `~/.openclaw`.
- Set `OPENCLAW_LIVE_USE_REAL_HOME=1` only when you intentionally need live tests to use your real home directory.
@@ -601,13 +601,13 @@ Use this decision table:
- Editing logic/tests: run `pnpm test` (and `pnpm test:coverage` if you changed a lot)
- Touching gateway networking / WS protocol / pairing: add `pnpm test:e2e`
- Debugging my bot is down / provider-specific failures / tool calling: run a narrowed `pnpm test:live`
- Debugging "my bot is down" / provider-specific failures / tool calling: run a narrowed `pnpm test:live`
## Live (network-touching) tests
For the live model matrix, CLI backend smokes, ACP smokes, Codex app-server
harness, and all media-provider live tests (Deepgram, BytePlus, ComfyUI, image,
music, video, media harness) plus credential handling for live runs see
music, video, media harness) - plus credential handling for live runs - see
[Testing live suites](/help/testing-live). For the dedicated update and
plugin validation checklist, see
[Testing updates and plugins](/help/testing-updates-plugins).
@@ -744,19 +744,19 @@ Run full Mintlify anchor validation when you need in-page heading checks too: `p
## Offline regression (CI-safe)
These are real pipeline regressions without real providers:
These are "real pipeline" regressions without real providers:
- Gateway tool calling (mock OpenAI, real gateway + agent loop): `src/gateway/gateway.test.ts` (case: "runs a mock OpenAI tool call end-to-end via gateway agent loop")
- Gateway wizard (WS `wizard.start`/`wizard.next`, writes config + auth enforced): `src/gateway/gateway.test.ts` (case: "runs wizard over ws and writes auth token config")
## Agent reliability evals (skills)
We already have a few CI-safe tests that behave like agent reliability evals:
We already have a few CI-safe tests that behave like "agent reliability evals":
- Mock tool-calling through the real gateway + agent loop (`src/gateway/gateway.test.ts`).
- End-to-end wizard flows that validate session wiring and config effects (`src/gateway/gateway.test.ts`).
Whats still missing for skills (see [Skills](/tools/skills)):
What's still missing for skills (see [Skills](/tools/skills)):
- **Decisioning:** when skills are listed in the prompt, does the agent pick the right skill (or avoid irrelevant ones)?
- **Compliance:** does the agent read `SKILL.md` before use and follow required steps/args?
@@ -829,7 +829,7 @@ Contract tests run in CI and do not require real API keys.
When you fix a provider/model issue discovered in live:
- Add a CI-safe regression if possible (mock/stub provider, or capture the exact request-shape transformation)
- If its inherently live-only (rate limits, auth policies), keep the live test narrow and opt-in via env vars
- If it's inherently live-only (rate limits, auth policies), keep the live test narrow and opt-in via env vars
- Prefer targeting the smallest layer that catches the bug:
- provider request conversion/replay bug → direct models test
- gateway session/history/tool pipeline bug → gateway live smoke or CI-safe gateway mock test

View File

@@ -23,13 +23,13 @@ pairing, reply threading, and outbound messaging.
Channel plugins do not need their own send/edit/react tools. OpenClaw keeps one
shared `message` tool in core. Your plugin owns:
- **Config** account resolution and setup wizard
- **Security** DM policy and allowlists
- **Pairing** DM approval flow
- **Session grammar** how provider-specific conversation ids map to base chats, thread ids, and parent fallbacks
- **Outbound** sending text, media, and polls to the platform
- **Threading** how replies are threaded
- **Heartbeat typing** optional typing/busy signals for heartbeat delivery targets
- **Config** - account resolution and setup wizard
- **Security** - DM policy and allowlists
- **Pairing** - DM approval flow
- **Session grammar** - how provider-specific conversation ids map to base chats, thread ids, and parent fallbacks
- **Outbound** - sending text, media, and polls to the platform
- **Threading** - how replies are threaded
- **Heartbeat typing** - optional typing/busy signals for heartbeat delivery targets
Core owns the shared message tool, prompt wiring, the outer session-key shape,
generic `:thread:` bookkeeping, and dispatch.
@@ -145,11 +145,11 @@ Most channel plugins do not need approval-specific code.
- If a channel needs native approval delivery, keep channel code focused on target normalization plus transport/presentation facts. Use `createChannelExecApprovalProfile`, `createChannelNativeOriginTargetResolver`, `createChannelApproverDmTargetResolver`, and `createApproverRestrictedNativeApprovalCapability` from `openclaw/plugin-sdk/approval-runtime`. Put the channel-specific facts behind `approvalCapability.nativeRuntime`, ideally via `createChannelApprovalNativeRuntimeAdapter(...)` or `createLazyChannelApprovalNativeRuntimeAdapter(...)`, so core can assemble the handler and own request filtering, routing, dedupe, expiry, gateway subscription, and routed-elsewhere notices. `nativeRuntime` is split into a few smaller seams:
- `createChannelNativeOriginTargetResolver` uses the shared channel-route matcher by default for `{ to, accountId, threadId }` targets. Pass `targetsMatch` only when a channel has provider-specific equivalence rules, such as Slack timestamp prefix matching.
- Pass `normalizeTargetForMatch` to `createChannelNativeOriginTargetResolver` when the channel needs to canonicalize provider ids before the default route matcher or a custom `targetsMatch` callback runs, while preserving the original target for delivery. Use `normalizeTarget` only when the resolved delivery target itself should be canonicalized.
- `availability` whether the account is configured and whether a request should be handled
- `presentation` map the shared approval view model into pending/resolved/expired native payloads or final actions
- `transport` prepare targets plus send/update/delete native approval messages
- `interactions` optional bind/unbind/clear-action hooks for native buttons or reactions
- `observe` optional delivery diagnostics hooks
- `availability` - whether the account is configured and whether a request should be handled
- `presentation` - map the shared approval view model into pending/resolved/expired native payloads or final actions
- `transport` - prepare targets plus send/update/delete native approval messages
- `interactions` - optional bind/unbind/clear-action hooks for native buttons or reactions
- `observe` - optional delivery diagnostics hooks
- If the channel needs runtime-owned objects such as a client, token, Bolt app, or webhook receiver, register them through `openclaw/plugin-sdk/channel-runtime-context`. The generic runtime-context registry lets core bootstrap capability-driven handlers from channel startup state without adding approval-specific wrapper glue.
- Reach for the lower-level `createChannelApprovalHandler` or `createChannelNativeApprovalRuntime` only when the capability-driven seam is not expressive enough yet.
- Native approval channels must route both `accountId` and `approvalKind` through those helpers. `accountId` keeps multi-account approval policy scoped to the right bot account, and `approvalKind` keeps exec vs plugin approval behavior available to the channel without hardcoded branches in core.
@@ -424,7 +424,7 @@ should use `resolveInboundMentionDecision({ facts, policy })`.
<Step title="Build the channel plugin object">
The `ChannelPlugin` interface has many optional adapter surfaces. Start with
the minimum `id` and `setup` and add adapters as you need them.
the minimum - `id` and `setup` - and add adapters as you need them.
Create `src/channel.ts`:
@@ -631,7 +631,7 @@ should use `resolveInboundMentionDecision({ facts, policy })`.
const event = parseWebhookPayload(req);
// Your inbound handler dispatches the message to OpenClaw.
// The exact wiring depends on your platform SDK
// The exact wiring depends on your platform SDK -
// see a real example in the bundled Microsoft Teams or Google Chat plugin package.
await handleAcmeChatInbound(api, event);
@@ -742,10 +742,10 @@ surface unless you are maintaining that bundled plugin family directly.
## Next steps
- [Provider Plugins](/plugins/sdk-provider-plugins) if your plugin also provides models
- [SDK Overview](/plugins/sdk-overview) full subpath import reference
- [SDK Testing](/plugins/sdk-testing) test utilities and contract tests
- [Plugin Manifest](/plugins/manifest) full manifest schema
- [Provider Plugins](/plugins/sdk-provider-plugins) - if your plugin also provides models
- [SDK Overview](/plugins/sdk-overview) - full subpath import reference
- [SDK Testing](/plugins/sdk-testing) - test utilities and contract tests
- [Plugin Manifest](/plugins/manifest) - full manifest schema
## Related

View File

@@ -19,18 +19,18 @@ the new architecture, this guide helps you migrate.
The old plugin system provided two wide-open surfaces that let plugins import
anything they needed from a single entry point:
- **`openclaw/plugin-sdk/compat`** a single import that re-exported dozens of
- **`openclaw/plugin-sdk/compat`** - a single import that re-exported dozens of
helpers. It was introduced to keep older hook-based plugins working while the
new plugin architecture was being built.
- **`openclaw/plugin-sdk/infra-runtime`** a broad runtime helper barrel that
- **`openclaw/plugin-sdk/infra-runtime`** - a broad runtime helper barrel that
mixed system events, heartbeat state, delivery queues, fetch/proxy helpers,
file helpers, approval types, and unrelated utilities.
- **`openclaw/plugin-sdk/config-runtime`** a broad config compatibility barrel
- **`openclaw/plugin-sdk/config-runtime`** - a broad config compatibility barrel
that still carries deprecated direct load/write helpers during the migration
window.
- **`openclaw/extension-api`** a bridge that gave plugins direct access to
- **`openclaw/extension-api`** - a bridge that gave plugins direct access to
host-side helpers like the embedded agent runner.
- **`api.registerEmbeddedExtensionFactory(...)`** a removed Pi-only bundled
- **`api.registerEmbeddedExtensionFactory(...)`** - a removed Pi-only bundled
extension hook that could observe embedded-runner events such as
`tool_result`.
@@ -55,9 +55,9 @@ registration behavior.
The old approach caused problems:
- **Slow startup** importing one helper loaded dozens of unrelated modules
- **Circular dependencies** broad re-exports made it easy to create import cycles
- **Unclear API surface** no way to tell which exports were stable vs internal
- **Slow startup** - importing one helper loaded dozens of unrelated modules
- **Circular dependencies** - broad re-exports made it easy to create import cycles
- **Unclear API surface** - no way to tell which exports were stable vs internal
The modern plugin SDK fixes this: each import path (`openclaw/plugin-sdk/\<subpath\>`)
is a small, self-contained module with a clear purpose and documented contract.
@@ -679,7 +679,7 @@ canonical replacement.
`buildCommandsMessagePaginated`, `buildHelpMessage`.
**New (`openclaw/plugin-sdk/command-status`)**: same signatures, same
exports just imported from the narrower subpath. `command-auth`
exports - just imported from the narrower subpath. `command-auth`
re-exports them as compat stubs.
```typescript
@@ -698,7 +698,7 @@ canonical replacement.
`openclaw/plugin-sdk/channel-inbound` or
`openclaw/plugin-sdk/channel-mention-gating`.
**New**: `resolveInboundMentionDecision({ facts, policy })` returns a
**New**: `resolveInboundMentionDecision({ facts, policy })` - returns a
single decision object instead of two split calls.
Downstream channel plugins (Slack, Discord, Matrix, MS Teams) have already
@@ -714,7 +714,7 @@ canonical replacement.
`channelActions*` helpers in `openclaw/plugin-sdk/channel-actions` are
deprecated alongside raw "actions" channel exports. Expose capabilities
through the semantic `presentation` surface instead channel plugins
through the semantic `presentation` surface instead - channel plugins
declare what they render (cards, buttons, selects) rather than which raw
action names they accept.
@@ -756,7 +756,7 @@ canonical replacement.
| `ProviderDiscoveryResult` | `ProviderCatalogResult` |
| `ProviderPluginDiscovery` | `ProviderPluginCatalog` |
Plus the legacy `ProviderCapabilities` static bag provider plugins
Plus the legacy `ProviderCapabilities` static bag - provider plugins
should use explicit provider hooks such as `buildReplayPolicy`,
`normalizeToolSchemas`, and `wrapStreamFn` rather than a static object.
@@ -809,12 +809,12 @@ canonical replacement.
</Accordion>
<Accordion title="Memory plugin registration → registerMemoryCapability">
**Old**: three separate calls
**Old**: three separate calls -
`api.registerMemoryPromptSection(...)`,
`api.registerMemoryFlushPlan(...)`,
`api.registerMemoryRuntime(...)`.
**New**: one call on the memory-state API
**New**: one call on the memory-state API -
`registerMemoryCapability(pluginId, { promptBuilder, flushPlanResolver, runtime })`.
Same slots, single registration call. Additive memory helpers
@@ -906,9 +906,9 @@ This is a temporary escape hatch, not a permanent solution.
## Related
- [Getting Started](/plugins/building-plugins) build your first plugin
- [SDK Overview](/plugins/sdk-overview) full subpath import reference
- [Channel Plugins](/plugins/sdk-channel-plugins) building channel plugins
- [Provider Plugins](/plugins/sdk-provider-plugins) building provider plugins
- [Plugin Internals](/plugins/architecture) architecture deep dive
- [Plugin Manifest](/plugins/manifest) manifest schema reference
- [Getting Started](/plugins/building-plugins) - build your first plugin
- [SDK Overview](/plugins/sdk-overview) - full subpath import reference
- [Channel Plugins](/plugins/sdk-channel-plugins) - building channel plugins
- [Provider Plugins](/plugins/sdk-provider-plugins) - building provider plugins
- [Plugin Internals](/plugins/architecture) - architecture deep dive
- [Plugin Manifest](/plugins/manifest) - manifest schema reference