diff --git a/docs/automation/tasks.md b/docs/automation/tasks.md index 47c87d77dd6..a3075542590 100644 --- a/docs/automation/tasks.md +++ b/docs/automation/tasks.md @@ -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. 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 - 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. - - 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. -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. 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: - 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). - 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. - 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. ## 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 diff --git a/docs/channels/yuanbao.md b/docs/channels/yuanbao.md index 88bd49bfb6c..9c493a5d065 100644 --- a/docs/channels/yuanbao.md +++ b/docs/channels/yuanbao.md @@ -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 **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..appKey` | App Key (used for signing and ticket generation) | — | -| `channels.yuanbao.accounts..appSecret` | App Secret (used for signing) | — | -| `channels.yuanbao.accounts..token` | Pre-signed token (skips automatic ticket signing) | — | -| `channels.yuanbao.accounts..name` | Account display name | — | +| `channels.yuanbao.accounts..appKey` | App Key (used for signing and ticket generation) | - | +| `channels.yuanbao.accounts..appSecret` | App Secret (used for signing) | - | +| `channels.yuanbao.accounts..token` | Pre-signed token (skips automatic ticket signing) | - | +| `channels.yuanbao.accounts..name` | Account display name | - | | `channels.yuanbao.accounts..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 diff --git a/docs/help/testing.md b/docs/help/testing.md index 9a3d6df6de9..9ef6597fc8f 100644 --- a/docs/help/testing.md +++ b/docs/help/testing.md @@ -18,9 +18,9 @@ of Docker runners. This doc is a "how we test" guide: **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. @@ -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`). -What’s 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 it’s 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 diff --git a/docs/plugins/sdk-channel-plugins.md b/docs/plugins/sdk-channel-plugins.md index e7c4aa61e95..d8a9ed97e12 100644 --- a/docs/plugins/sdk-channel-plugins.md +++ b/docs/plugins/sdk-channel-plugins.md @@ -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 })`. 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 diff --git a/docs/plugins/sdk-migration.md b/docs/plugins/sdk-migration.md index 8ddb9f322a8..7f68c6185ef 100644 --- a/docs/plugins/sdk-migration.md +++ b/docs/plugins/sdk-migration.md @@ -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/\`) 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. - **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